Skip to main content

zlayer_observability/
config.rs

1//! Configuration types for observability
2
3use serde::{Deserialize, Serialize};
4use std::path::PathBuf;
5
6/// Log output format
7#[derive(Debug, Clone, Copy, Serialize, Deserialize, Default, PartialEq, Eq)]
8#[serde(rename_all = "lowercase")]
9pub enum LogFormat {
10    /// Human-readable pretty format
11    #[default]
12    Pretty,
13    /// JSON format for log aggregation
14    Json,
15    /// Compact format (single line)
16    Compact,
17}
18
19/// Log level
20#[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/// Logging configuration
56#[derive(Debug, Clone, Serialize, Deserialize)]
57pub struct LoggingConfig {
58    /// Log level
59    #[serde(default)]
60    pub level: LogLevel,
61
62    /// Output format
63    #[serde(default)]
64    pub format: LogFormat,
65
66    /// Log to file (optional)
67    #[serde(default)]
68    pub file: Option<FileLoggingConfig>,
69
70    /// Include source code location in logs
71    #[serde(default = "default_true")]
72    pub include_location: bool,
73
74    /// Include target (module path) in logs
75    #[serde(default = "default_true")]
76    pub include_target: bool,
77
78    /// Per-crate tracing filter directives (e.g. "`runtime=info,zlayer_agent=warn,warn`")
79    /// When set, overrides the global `level` for `EnvFilter` construction.
80    #[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/// File logging configuration
102#[derive(Debug, Clone, Serialize, Deserialize)]
103pub struct FileLoggingConfig {
104    /// Directory for log files
105    pub directory: PathBuf,
106
107    /// File name prefix
108    #[serde(default = "default_prefix")]
109    pub prefix: String,
110
111    /// Rotation strategy
112    #[serde(default)]
113    pub rotation: RotationStrategy,
114}
115
116fn default_prefix() -> String {
117    "zlayer".to_string()
118}
119
120/// Log file rotation strategy
121#[derive(Debug, Clone, Copy, Serialize, Deserialize, Default, PartialEq, Eq)]
122#[serde(rename_all = "lowercase")]
123pub enum RotationStrategy {
124    /// Rotate daily
125    #[default]
126    Daily,
127    /// Rotate hourly
128    Hourly,
129    /// Never rotate (single file)
130    Never,
131}
132
133/// Metrics configuration
134#[derive(Debug, Clone, Serialize, Deserialize)]
135pub struct MetricsConfig {
136    /// Enable Prometheus metrics
137    #[serde(default = "default_true")]
138    pub enabled: bool,
139
140    /// Metrics endpoint path
141    #[serde(default = "default_metrics_path")]
142    pub path: String,
143
144    /// Port for standalone metrics server (if not using API)
145    #[serde(default)]
146    pub port: Option<u16>,
147}
148
149fn default_metrics_path() -> String {
150    "/metrics".to_string()
151}
152
153impl Default for MetricsConfig {
154    fn default() -> Self {
155        Self {
156            enabled: true,
157            path: default_metrics_path(),
158            port: None,
159        }
160    }
161}
162
163/// Tracing (OpenTelemetry) configuration
164#[derive(Debug, Clone, Serialize, Deserialize)]
165pub struct TracingConfig {
166    /// Enable distributed tracing
167    #[serde(default)]
168    pub enabled: bool,
169
170    /// OTLP endpoint (e.g., "<http://localhost:4317>")
171    #[serde(default)]
172    pub otlp_endpoint: Option<String>,
173
174    /// Service name for traces
175    #[serde(default = "default_service_name")]
176    pub service_name: String,
177
178    /// Sampling ratio (0.0 to 1.0)
179    #[serde(default = "default_sampling_ratio")]
180    pub sampling_ratio: f64,
181
182    /// Deployment environment (production, staging, development)
183    #[serde(default)]
184    pub environment: Option<String>,
185
186    /// Batch export configuration
187    #[serde(default)]
188    pub batch: BatchConfig,
189
190    /// Use gRPC (true) or HTTP (false) for OTLP
191    #[serde(default = "default_true")]
192    pub use_grpc: bool,
193}
194
195fn default_service_name() -> String {
196    "zlayer".to_string()
197}
198
199fn default_sampling_ratio() -> f64 {
200    1.0
201}
202
203fn default_max_queue_size() -> usize {
204    2048
205}
206
207fn default_scheduled_delay() -> u64 {
208    5000
209}
210
211fn default_max_export_batch_size() -> usize {
212    512
213}
214
215/// Batch export configuration for OpenTelemetry
216#[derive(Debug, Clone, Serialize, Deserialize)]
217pub struct BatchConfig {
218    /// Maximum queue size before dropping spans (default: 2048)
219    #[serde(default = "default_max_queue_size")]
220    pub max_queue_size: usize,
221
222    /// Scheduled delay for batch export in milliseconds (default: 5000)
223    #[serde(default = "default_scheduled_delay")]
224    pub scheduled_delay_ms: u64,
225
226    /// Maximum export batch size (default: 512)
227    #[serde(default = "default_max_export_batch_size")]
228    pub max_export_batch_size: usize,
229}
230
231impl Default for BatchConfig {
232    fn default() -> Self {
233        Self {
234            max_queue_size: default_max_queue_size(),
235            scheduled_delay_ms: default_scheduled_delay(),
236            max_export_batch_size: default_max_export_batch_size(),
237        }
238    }
239}
240
241impl Default for TracingConfig {
242    fn default() -> Self {
243        Self {
244            enabled: false,
245            otlp_endpoint: None,
246            service_name: default_service_name(),
247            sampling_ratio: default_sampling_ratio(),
248            environment: None,
249            batch: BatchConfig::default(),
250            use_grpc: true,
251        }
252    }
253}
254
255impl TracingConfig {
256    /// Load from environment variables with fallback to defaults
257    #[must_use]
258    pub fn from_env() -> Self {
259        Self {
260            enabled: std::env::var("OTEL_TRACES_ENABLED")
261                .map(|v| v == "true" || v == "1")
262                .unwrap_or(false),
263            otlp_endpoint: std::env::var("OTEL_EXPORTER_OTLP_ENDPOINT").ok(),
264            service_name: std::env::var("OTEL_SERVICE_NAME")
265                .unwrap_or_else(|_| "zlayer".to_string()),
266            sampling_ratio: std::env::var("OTEL_TRACES_SAMPLER_ARG")
267                .ok()
268                .and_then(|v| v.parse().ok())
269                .unwrap_or(1.0),
270            environment: std::env::var("DEPLOYMENT_ENVIRONMENT").ok(),
271            batch: BatchConfig::default(),
272            use_grpc: std::env::var("OTEL_EXPORTER_OTLP_PROTOCOL")
273                .map(|v| v != "http/protobuf")
274                .unwrap_or(true),
275        }
276    }
277}
278
279/// Complete observability configuration
280#[derive(Debug, Clone, Serialize, Deserialize, Default)]
281pub struct ObservabilityConfig {
282    /// Logging configuration
283    #[serde(default)]
284    pub logging: LoggingConfig,
285
286    /// Metrics configuration
287    #[serde(default)]
288    pub metrics: MetricsConfig,
289
290    /// Tracing configuration
291    #[serde(default)]
292    pub tracing: TracingConfig,
293}