1use serde::{Deserialize, Serialize};
4use std::path::PathBuf;
5
6#[derive(Debug, Clone, Copy, Serialize, Deserialize, Default, PartialEq, Eq)]
8#[serde(rename_all = "lowercase")]
9pub enum LogFormat {
10 #[default]
12 Pretty,
13 Json,
15 Compact,
17}
18
19#[derive(Debug, Clone, Copy, Serialize, Deserialize, Default, PartialEq, Eq)]
21#[serde(rename_all = "lowercase")]
22pub enum LogLevel {
23 Trace,
24 Debug,
25 #[default]
26 Info,
27 Warn,
28 Error,
29}
30
31impl From<LogLevel> for tracing::Level {
32 fn from(level: LogLevel) -> Self {
33 match level {
34 LogLevel::Trace => tracing::Level::TRACE,
35 LogLevel::Debug => tracing::Level::DEBUG,
36 LogLevel::Info => tracing::Level::INFO,
37 LogLevel::Warn => tracing::Level::WARN,
38 LogLevel::Error => tracing::Level::ERROR,
39 }
40 }
41}
42
43impl From<LogLevel> for tracing_subscriber::filter::LevelFilter {
44 fn from(level: LogLevel) -> Self {
45 match level {
46 LogLevel::Trace => tracing_subscriber::filter::LevelFilter::TRACE,
47 LogLevel::Debug => tracing_subscriber::filter::LevelFilter::DEBUG,
48 LogLevel::Info => tracing_subscriber::filter::LevelFilter::INFO,
49 LogLevel::Warn => tracing_subscriber::filter::LevelFilter::WARN,
50 LogLevel::Error => tracing_subscriber::filter::LevelFilter::ERROR,
51 }
52 }
53}
54
55#[derive(Debug, Clone, Serialize, Deserialize)]
57pub struct LoggingConfig {
58 #[serde(default)]
60 pub level: LogLevel,
61
62 #[serde(default)]
64 pub format: LogFormat,
65
66 #[serde(default)]
68 pub file: Option<FileLoggingConfig>,
69
70 #[serde(default = "default_true")]
72 pub include_location: bool,
73
74 #[serde(default = "default_true")]
76 pub include_target: bool,
77
78 #[serde(default)]
81 pub filter_directives: Option<String>,
82}
83
84fn default_true() -> bool {
85 true
86}
87
88impl Default for LoggingConfig {
89 fn default() -> Self {
90 Self {
91 level: LogLevel::Info,
92 format: LogFormat::Pretty,
93 file: None,
94 include_location: true,
95 include_target: true,
96 filter_directives: None,
97 }
98 }
99}
100
101#[derive(Debug, Clone, Serialize, Deserialize)]
103pub struct FileLoggingConfig {
104 pub directory: PathBuf,
106
107 #[serde(default = "default_prefix")]
109 pub prefix: String,
110
111 #[serde(default)]
113 pub rotation: RotationStrategy,
114
115 #[serde(default = "default_max_files")]
118 pub max_files: Option<usize>,
119}
120
121#[allow(clippy::unnecessary_wraps)]
122fn default_max_files() -> Option<usize> {
123 Some(7)
124}
125
126fn default_prefix() -> String {
127 "zlayer".to_string()
128}
129
130#[derive(Debug, Clone, Copy, Serialize, Deserialize, Default, PartialEq, Eq)]
132#[serde(rename_all = "lowercase")]
133pub enum RotationStrategy {
134 #[default]
136 Daily,
137 Hourly,
139 Never,
141}
142
143#[derive(Debug, Clone, Serialize, Deserialize)]
145pub struct LogOutputConfig {
146 #[serde(default)]
148 pub destination: LogDestination,
149
150 #[serde(default = "default_max_log_size")]
153 pub max_size_bytes: u64,
154
155 #[serde(default = "default_log_retention_secs")]
157 pub retention_secs: u64,
158}
159
160fn default_max_log_size() -> u64 {
161 100 * 1024 * 1024 }
163
164fn default_log_retention_secs() -> u64 {
165 7 * 24 * 60 * 60 }
167
168impl Default for LogOutputConfig {
169 fn default() -> Self {
170 Self {
171 destination: LogDestination::default(),
172 max_size_bytes: default_max_log_size(),
173 retention_secs: default_log_retention_secs(),
174 }
175 }
176}
177
178#[derive(Debug, Clone, Copy, Serialize, Deserialize, Default, PartialEq, Eq)]
180#[serde(rename_all = "lowercase")]
181pub enum LogDestination {
182 #[default]
184 Disk,
185 Memory,
187}
188
189#[derive(Debug, Clone, Serialize, Deserialize)]
191pub struct MetricsConfig {
192 #[serde(default = "default_true")]
194 pub enabled: bool,
195
196 #[serde(default = "default_metrics_path")]
198 pub path: String,
199
200 #[serde(default)]
202 pub port: Option<u16>,
203}
204
205fn default_metrics_path() -> String {
206 "/metrics".to_string()
207}
208
209impl Default for MetricsConfig {
210 fn default() -> Self {
211 Self {
212 enabled: true,
213 path: default_metrics_path(),
214 port: None,
215 }
216 }
217}
218
219#[derive(Debug, Clone, Serialize, Deserialize)]
221pub struct TracingConfig {
222 #[serde(default)]
224 pub enabled: bool,
225
226 #[serde(default)]
228 pub otlp_endpoint: Option<String>,
229
230 #[serde(default = "default_service_name")]
232 pub service_name: String,
233
234 #[serde(default = "default_sampling_ratio")]
236 pub sampling_ratio: f64,
237
238 #[serde(default)]
240 pub environment: Option<String>,
241
242 #[serde(default)]
244 pub batch: BatchConfig,
245
246 #[serde(default = "default_true")]
248 pub use_grpc: bool,
249}
250
251fn default_service_name() -> String {
252 "zlayer".to_string()
253}
254
255fn default_sampling_ratio() -> f64 {
256 1.0
257}
258
259fn default_max_queue_size() -> usize {
260 2048
261}
262
263fn default_scheduled_delay() -> u64 {
264 5000
265}
266
267fn default_max_export_batch_size() -> usize {
268 512
269}
270
271#[derive(Debug, Clone, Serialize, Deserialize)]
273pub struct BatchConfig {
274 #[serde(default = "default_max_queue_size")]
276 pub max_queue_size: usize,
277
278 #[serde(default = "default_scheduled_delay")]
280 pub scheduled_delay_ms: u64,
281
282 #[serde(default = "default_max_export_batch_size")]
284 pub max_export_batch_size: usize,
285}
286
287impl Default for BatchConfig {
288 fn default() -> Self {
289 Self {
290 max_queue_size: default_max_queue_size(),
291 scheduled_delay_ms: default_scheduled_delay(),
292 max_export_batch_size: default_max_export_batch_size(),
293 }
294 }
295}
296
297impl Default for TracingConfig {
298 fn default() -> Self {
299 Self {
300 enabled: false,
301 otlp_endpoint: None,
302 service_name: default_service_name(),
303 sampling_ratio: default_sampling_ratio(),
304 environment: None,
305 batch: BatchConfig::default(),
306 use_grpc: true,
307 }
308 }
309}
310
311impl TracingConfig {
312 #[must_use]
314 pub fn from_env() -> Self {
315 Self {
316 enabled: std::env::var("OTEL_TRACES_ENABLED")
317 .map(|v| v == "true" || v == "1")
318 .unwrap_or(false),
319 otlp_endpoint: std::env::var("OTEL_EXPORTER_OTLP_ENDPOINT").ok(),
320 service_name: std::env::var("OTEL_SERVICE_NAME")
321 .unwrap_or_else(|_| "zlayer".to_string()),
322 sampling_ratio: std::env::var("OTEL_TRACES_SAMPLER_ARG")
323 .ok()
324 .and_then(|v| v.parse().ok())
325 .unwrap_or(1.0),
326 environment: std::env::var("DEPLOYMENT_ENVIRONMENT").ok(),
327 batch: BatchConfig::default(),
328 use_grpc: std::env::var("OTEL_EXPORTER_OTLP_PROTOCOL")
329 .map(|v| v != "http/protobuf")
330 .unwrap_or(true),
331 }
332 }
333}
334
335#[derive(Debug, Clone, Serialize, Deserialize, Default)]
337pub struct ObservabilityConfig {
338 #[serde(default)]
340 pub logging: LoggingConfig,
341
342 #[serde(default)]
344 pub metrics: MetricsConfig,
345
346 #[serde(default)]
348 pub tracing: TracingConfig,
349}