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
79fn default_true() -> bool {
80    true
81}
82
83impl Default for LoggingConfig {
84    fn default() -> Self {
85        Self {
86            level: LogLevel::Info,
87            format: LogFormat::Pretty,
88            file: None,
89            include_location: true,
90            include_target: true,
91        }
92    }
93}
94
95/// File logging configuration
96#[derive(Debug, Clone, Serialize, Deserialize)]
97pub struct FileLoggingConfig {
98    /// Directory for log files
99    pub directory: PathBuf,
100
101    /// File name prefix
102    #[serde(default = "default_prefix")]
103    pub prefix: String,
104
105    /// Rotation strategy
106    #[serde(default)]
107    pub rotation: RotationStrategy,
108}
109
110fn default_prefix() -> String {
111    "zlayer".to_string()
112}
113
114/// Log file rotation strategy
115#[derive(Debug, Clone, Copy, Serialize, Deserialize, Default, PartialEq, Eq)]
116#[serde(rename_all = "lowercase")]
117pub enum RotationStrategy {
118    /// Rotate daily
119    #[default]
120    Daily,
121    /// Rotate hourly
122    Hourly,
123    /// Never rotate (single file)
124    Never,
125}
126
127/// Metrics configuration
128#[derive(Debug, Clone, Serialize, Deserialize)]
129pub struct MetricsConfig {
130    /// Enable Prometheus metrics
131    #[serde(default = "default_true")]
132    pub enabled: bool,
133
134    /// Metrics endpoint path
135    #[serde(default = "default_metrics_path")]
136    pub path: String,
137
138    /// Port for standalone metrics server (if not using API)
139    #[serde(default)]
140    pub port: Option<u16>,
141}
142
143fn default_metrics_path() -> String {
144    "/metrics".to_string()
145}
146
147impl Default for MetricsConfig {
148    fn default() -> Self {
149        Self {
150            enabled: true,
151            path: default_metrics_path(),
152            port: None,
153        }
154    }
155}
156
157/// Tracing (OpenTelemetry) configuration
158#[derive(Debug, Clone, Serialize, Deserialize)]
159pub struct TracingConfig {
160    /// Enable distributed tracing
161    #[serde(default)]
162    pub enabled: bool,
163
164    /// OTLP endpoint (e.g., "http://localhost:4317")
165    #[serde(default)]
166    pub otlp_endpoint: Option<String>,
167
168    /// Service name for traces
169    #[serde(default = "default_service_name")]
170    pub service_name: String,
171
172    /// Sampling ratio (0.0 to 1.0)
173    #[serde(default = "default_sampling_ratio")]
174    pub sampling_ratio: f64,
175
176    /// Deployment environment (production, staging, development)
177    #[serde(default)]
178    pub environment: Option<String>,
179
180    /// Batch export configuration
181    #[serde(default)]
182    pub batch: BatchConfig,
183
184    /// Use gRPC (true) or HTTP (false) for OTLP
185    #[serde(default = "default_true")]
186    pub use_grpc: bool,
187}
188
189fn default_service_name() -> String {
190    "zlayer".to_string()
191}
192
193fn default_sampling_ratio() -> f64 {
194    1.0
195}
196
197fn default_max_queue_size() -> usize {
198    2048
199}
200
201fn default_scheduled_delay() -> u64 {
202    5000
203}
204
205fn default_max_export_batch_size() -> usize {
206    512
207}
208
209/// Batch export configuration for OpenTelemetry
210#[derive(Debug, Clone, Serialize, Deserialize)]
211pub struct BatchConfig {
212    /// Maximum queue size before dropping spans (default: 2048)
213    #[serde(default = "default_max_queue_size")]
214    pub max_queue_size: usize,
215
216    /// Scheduled delay for batch export in milliseconds (default: 5000)
217    #[serde(default = "default_scheduled_delay")]
218    pub scheduled_delay_ms: u64,
219
220    /// Maximum export batch size (default: 512)
221    #[serde(default = "default_max_export_batch_size")]
222    pub max_export_batch_size: usize,
223}
224
225impl Default for BatchConfig {
226    fn default() -> Self {
227        Self {
228            max_queue_size: default_max_queue_size(),
229            scheduled_delay_ms: default_scheduled_delay(),
230            max_export_batch_size: default_max_export_batch_size(),
231        }
232    }
233}
234
235impl Default for TracingConfig {
236    fn default() -> Self {
237        Self {
238            enabled: false,
239            otlp_endpoint: None,
240            service_name: default_service_name(),
241            sampling_ratio: default_sampling_ratio(),
242            environment: None,
243            batch: BatchConfig::default(),
244            use_grpc: true,
245        }
246    }
247}
248
249impl TracingConfig {
250    /// Load from environment variables with fallback to defaults
251    pub fn from_env() -> Self {
252        Self {
253            enabled: std::env::var("OTEL_TRACES_ENABLED")
254                .map(|v| v == "true" || v == "1")
255                .unwrap_or(false),
256            otlp_endpoint: std::env::var("OTEL_EXPORTER_OTLP_ENDPOINT").ok(),
257            service_name: std::env::var("OTEL_SERVICE_NAME")
258                .unwrap_or_else(|_| "zlayer".to_string()),
259            sampling_ratio: std::env::var("OTEL_TRACES_SAMPLER_ARG")
260                .ok()
261                .and_then(|v| v.parse().ok())
262                .unwrap_or(1.0),
263            environment: std::env::var("DEPLOYMENT_ENVIRONMENT").ok(),
264            batch: BatchConfig::default(),
265            use_grpc: std::env::var("OTEL_EXPORTER_OTLP_PROTOCOL")
266                .map(|v| v != "http/protobuf")
267                .unwrap_or(true),
268        }
269    }
270}
271
272/// Complete observability configuration
273#[derive(Debug, Clone, Serialize, Deserialize, Default)]
274pub struct ObservabilityConfig {
275    /// Logging configuration
276    #[serde(default)]
277    pub logging: LoggingConfig,
278
279    /// Metrics configuration
280    #[serde(default)]
281    pub metrics: MetricsConfig,
282
283    /// Tracing configuration
284    #[serde(default)]
285    pub tracing: TracingConfig,
286}