Skip to main content

forge_runtime/observability/
config.rs

1use std::time::Duration;
2
3use serde::{Deserialize, Serialize};
4
5use forge_core::LogLevel;
6
7/// Observability configuration.
8#[derive(Debug, Clone, Serialize, Deserialize)]
9pub struct ObservabilityConfig {
10    /// Whether observability is enabled.
11    pub enabled: bool,
12    /// Separate database URL for observability (optional).
13    pub database_url: Option<String>,
14    /// Connection pool size.
15    pub pool_size: u32,
16    /// Connection pool timeout.
17    pub pool_timeout: Duration,
18    /// Metrics configuration.
19    pub metrics: MetricsConfig,
20    /// Logs configuration.
21    pub logs: LogsConfig,
22    /// Traces configuration.
23    pub traces: TracesConfig,
24    /// Export configuration.
25    pub export: ExportConfig,
26}
27
28impl Default for ObservabilityConfig {
29    fn default() -> Self {
30        Self {
31            enabled: true,
32            database_url: None,
33            pool_size: 10,
34            pool_timeout: Duration::from_secs(5),
35            metrics: MetricsConfig::default(),
36            logs: LogsConfig::default(),
37            traces: TracesConfig::default(),
38            export: ExportConfig::default(),
39        }
40    }
41}
42
43/// Metrics configuration.
44#[derive(Debug, Clone, Serialize, Deserialize)]
45pub struct MetricsConfig {
46    /// Collection interval.
47    pub interval: Duration,
48    /// Raw data retention.
49    pub raw_retention: Duration,
50    /// 1-minute aggregate retention.
51    pub downsampled_1m: Duration,
52    /// 5-minute aggregate retention.
53    pub downsampled_5m: Duration,
54    /// 1-hour aggregate retention.
55    pub downsampled_1h: Duration,
56    /// In-memory buffer size before flush.
57    pub buffer_size: usize,
58    /// Flush interval.
59    pub flush_interval: Duration,
60}
61
62impl Default for MetricsConfig {
63    fn default() -> Self {
64        Self {
65            interval: Duration::from_secs(10),
66            raw_retention: Duration::from_secs(3600), // 1 hour
67            downsampled_1m: Duration::from_secs(86400), // 24 hours
68            downsampled_5m: Duration::from_secs(604800), // 7 days
69            downsampled_1h: Duration::from_secs(7776000), // 90 days
70            buffer_size: 10000,
71            flush_interval: Duration::from_secs(10),
72        }
73    }
74}
75
76/// Logs configuration.
77#[derive(Debug, Clone, Serialize, Deserialize)]
78pub struct LogsConfig {
79    /// Minimum log level.
80    pub level: LogLevel,
81    /// Retention duration.
82    pub retention: Duration,
83    /// Slow query threshold.
84    pub slow_query_threshold: Duration,
85    /// Async writes.
86    pub async_writes: bool,
87    /// Buffer size.
88    pub buffer_size: usize,
89}
90
91impl Default for LogsConfig {
92    fn default() -> Self {
93        Self {
94            level: LogLevel::Info,
95            retention: Duration::from_secs(604800), // 7 days
96            slow_query_threshold: Duration::from_millis(100),
97            async_writes: true,
98            buffer_size: 5000,
99        }
100    }
101}
102
103/// Traces configuration.
104#[derive(Debug, Clone, Serialize, Deserialize)]
105pub struct TracesConfig {
106    /// Sample rate (0.0 to 1.0).
107    pub sample_rate: f64,
108    /// Retention duration.
109    pub retention: Duration,
110    /// Always trace errors.
111    pub always_trace_errors: bool,
112}
113
114impl Default for TracesConfig {
115    fn default() -> Self {
116        Self {
117            sample_rate: 1.0,
118            retention: Duration::from_secs(86400), // 24 hours
119            always_trace_errors: true,
120        }
121    }
122}
123
124/// Export configuration.
125#[derive(Debug, Clone, Serialize, Deserialize)]
126pub struct ExportConfig {
127    /// Export destinations.
128    pub destinations: Vec<ExportDestination>,
129    /// OTLP configuration.
130    pub otlp: Option<OtlpConfig>,
131    /// Prometheus configuration.
132    pub prometheus: Option<PrometheusConfig>,
133}
134
135impl Default for ExportConfig {
136    fn default() -> Self {
137        Self {
138            destinations: vec![ExportDestination::Postgres],
139            otlp: None,
140            prometheus: None,
141        }
142    }
143}
144
145/// Export destination.
146#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
147#[serde(rename_all = "lowercase")]
148pub enum ExportDestination {
149    /// PostgreSQL storage.
150    Postgres,
151    /// OpenTelemetry Protocol.
152    Otlp,
153    /// Prometheus metrics endpoint.
154    Prometheus,
155}
156
157/// OTLP export configuration.
158#[derive(Debug, Clone, Serialize, Deserialize)]
159pub struct OtlpConfig {
160    /// OTLP endpoint.
161    pub endpoint: String,
162    /// Protocol (grpc or http/protobuf).
163    pub protocol: String,
164    /// Authentication headers.
165    pub headers: std::collections::HashMap<String, String>,
166    /// Export metrics.
167    pub metrics: bool,
168    /// Export logs.
169    pub logs: bool,
170    /// Export traces.
171    pub traces: bool,
172    /// Trace sample rate for export.
173    pub trace_sample_rate: f64,
174    /// Always export errors.
175    pub always_export_errors: bool,
176}
177
178impl Default for OtlpConfig {
179    fn default() -> Self {
180        Self {
181            endpoint: "http://localhost:4317".to_string(),
182            protocol: "grpc".to_string(),
183            headers: std::collections::HashMap::new(),
184            metrics: true,
185            logs: true,
186            traces: true,
187            trace_sample_rate: 1.0,
188            always_export_errors: true,
189        }
190    }
191}
192
193/// Prometheus export configuration.
194#[derive(Debug, Clone, Serialize, Deserialize)]
195pub struct PrometheusConfig {
196    /// Whether enabled.
197    pub enabled: bool,
198    /// Metrics endpoint path.
199    pub path: String,
200}
201
202impl Default for PrometheusConfig {
203    fn default() -> Self {
204        Self {
205            enabled: false,
206            path: "/metrics".to_string(),
207        }
208    }
209}
210
211#[cfg(test)]
212mod tests {
213    use super::*;
214
215    #[test]
216    fn test_observability_config_default() {
217        let config = ObservabilityConfig::default();
218        assert!(config.enabled);
219        assert!(config.database_url.is_none());
220        assert_eq!(config.pool_size, 10);
221    }
222
223    #[test]
224    fn test_metrics_config_default() {
225        let config = MetricsConfig::default();
226        assert_eq!(config.interval, Duration::from_secs(10));
227        assert_eq!(config.buffer_size, 10000);
228    }
229
230    #[test]
231    fn test_logs_config_default() {
232        let config = LogsConfig::default();
233        assert_eq!(config.level, LogLevel::Info);
234        assert!(config.async_writes);
235    }
236
237    #[test]
238    fn test_traces_config_default() {
239        let config = TracesConfig::default();
240        assert_eq!(config.sample_rate, 1.0);
241        assert!(config.always_trace_errors);
242    }
243
244    #[test]
245    fn test_export_config_default() {
246        let config = ExportConfig::default();
247        assert_eq!(config.destinations, vec![ExportDestination::Postgres]);
248    }
249}