forge_core/config/
observability.rs

1use serde::{Deserialize, Serialize};
2
3/// Observability configuration.
4#[derive(Debug, Clone, Serialize, Deserialize)]
5pub struct ObservabilityConfig {
6    /// Enable metrics collection.
7    #[serde(default = "default_true")]
8    pub metrics_enabled: bool,
9
10    /// Enable logging.
11    #[serde(default = "default_true")]
12    pub logging_enabled: bool,
13
14    /// Enable distributed tracing.
15    #[serde(default = "default_true")]
16    pub tracing_enabled: bool,
17
18    /// Enable built-in dashboard.
19    #[serde(default = "default_true")]
20    pub dashboard_enabled: bool,
21
22    /// Logging configuration.
23    #[serde(default)]
24    pub logging: LoggingConfig,
25
26    /// Metrics configuration.
27    #[serde(default)]
28    pub metrics: MetricsConfig,
29
30    /// Tracing configuration.
31    #[serde(default)]
32    pub tracing: TracingConfig,
33
34    /// Data retention configuration.
35    #[serde(default)]
36    pub retention: RetentionConfig,
37}
38
39impl Default for ObservabilityConfig {
40    fn default() -> Self {
41        Self {
42            metrics_enabled: true,
43            logging_enabled: true,
44            tracing_enabled: true,
45            dashboard_enabled: true,
46            logging: LoggingConfig::default(),
47            metrics: MetricsConfig::default(),
48            tracing: TracingConfig::default(),
49            retention: RetentionConfig::default(),
50        }
51    }
52}
53
54fn default_true() -> bool {
55    true
56}
57
58/// Logging configuration.
59#[derive(Debug, Clone, Serialize, Deserialize)]
60pub struct LoggingConfig {
61    /// Log level.
62    #[serde(default = "default_log_level")]
63    pub level: String,
64
65    /// Slow query threshold in milliseconds.
66    #[serde(default = "default_slow_query_threshold")]
67    pub slow_query_threshold_ms: u64,
68
69    /// Whether to output JSON format.
70    #[serde(default)]
71    pub json_format: bool,
72}
73
74impl Default for LoggingConfig {
75    fn default() -> Self {
76        Self {
77            level: default_log_level(),
78            slow_query_threshold_ms: default_slow_query_threshold(),
79            json_format: false,
80        }
81    }
82}
83
84fn default_log_level() -> String {
85    "info".to_string()
86}
87
88fn default_slow_query_threshold() -> u64 {
89    100
90}
91
92/// Metrics configuration.
93#[derive(Debug, Clone, Serialize, Deserialize)]
94pub struct MetricsConfig {
95    /// Flush interval in seconds.
96    #[serde(default = "default_flush_interval")]
97    pub flush_interval_secs: u64,
98
99    /// Enable Prometheus endpoint.
100    #[serde(default)]
101    pub prometheus_enabled: bool,
102
103    /// Prometheus endpoint path.
104    #[serde(default = "default_prometheus_path")]
105    pub prometheus_path: String,
106}
107
108impl Default for MetricsConfig {
109    fn default() -> Self {
110        Self {
111            flush_interval_secs: default_flush_interval(),
112            prometheus_enabled: false,
113            prometheus_path: default_prometheus_path(),
114        }
115    }
116}
117
118fn default_flush_interval() -> u64 {
119    10
120}
121
122fn default_prometheus_path() -> String {
123    "/metrics".to_string()
124}
125
126/// Tracing configuration.
127#[derive(Debug, Clone, Serialize, Deserialize)]
128pub struct TracingConfig {
129    /// Sample rate (0.0 to 1.0).
130    #[serde(default = "default_sample_rate")]
131    pub sample_rate: f64,
132
133    /// OTLP endpoint for exporting traces.
134    pub otlp_endpoint: Option<String>,
135}
136
137impl Default for TracingConfig {
138    fn default() -> Self {
139        Self {
140            sample_rate: default_sample_rate(),
141            otlp_endpoint: None,
142        }
143    }
144}
145
146fn default_sample_rate() -> f64 {
147    1.0
148}
149
150/// Data retention configuration.
151#[derive(Debug, Clone, Serialize, Deserialize)]
152pub struct RetentionConfig {
153    /// Metrics retention in days.
154    #[serde(default = "default_metrics_retention")]
155    pub metrics_days: u32,
156
157    /// Logs retention in days.
158    #[serde(default = "default_logs_retention")]
159    pub logs_days: u32,
160
161    /// Traces retention in days.
162    #[serde(default = "default_traces_retention")]
163    pub traces_days: u32,
164
165    /// Completed jobs retention in days.
166    #[serde(default = "default_jobs_retention")]
167    pub completed_jobs_days: u32,
168}
169
170impl Default for RetentionConfig {
171    fn default() -> Self {
172        Self {
173            metrics_days: default_metrics_retention(),
174            logs_days: default_logs_retention(),
175            traces_days: default_traces_retention(),
176            completed_jobs_days: default_jobs_retention(),
177        }
178    }
179}
180
181fn default_metrics_retention() -> u32 {
182    30
183}
184
185fn default_logs_retention() -> u32 {
186    7
187}
188
189fn default_traces_retention() -> u32 {
190    7
191}
192
193fn default_jobs_retention() -> u32 {
194    7
195}
196
197#[cfg(test)]
198mod tests {
199    use super::*;
200
201    #[test]
202    fn test_default_observability_config() {
203        let config = ObservabilityConfig::default();
204        assert!(config.metrics_enabled);
205        assert!(config.logging_enabled);
206        assert!(config.tracing_enabled);
207        assert_eq!(config.logging.level, "info");
208    }
209
210    #[test]
211    fn test_parse_observability_config() {
212        let toml = r#"
213            metrics_enabled = true
214            logging_enabled = true
215            dashboard_enabled = false
216
217            [logging]
218            level = "debug"
219            slow_query_threshold_ms = 50
220
221            [metrics]
222            flush_interval_secs = 5
223            prometheus_enabled = true
224        "#;
225
226        let config: ObservabilityConfig = toml::from_str(toml).unwrap();
227        assert!(!config.dashboard_enabled);
228        assert_eq!(config.logging.level, "debug");
229        assert!(config.metrics.prometheus_enabled);
230    }
231}