Skip to main content

litellm_rs/config/models/
monitoring.rs

1//! Monitoring configuration
2
3use super::*;
4use serde::{Deserialize, Serialize};
5
6/// Monitoring configuration
7#[derive(Debug, Clone, Serialize, Deserialize, Default)]
8pub struct MonitoringConfig {
9    /// Metrics configuration
10    #[serde(default)]
11    pub metrics: MetricsConfig,
12    /// Tracing configuration
13    #[serde(default)]
14    pub tracing: TracingConfig,
15    /// Health check configuration
16    #[serde(default)]
17    pub health: HealthConfig,
18}
19
20impl MonitoringConfig {
21    /// Merge monitoring configurations
22    pub fn merge(mut self, other: Self) -> Self {
23        self.metrics = self.metrics.merge(other.metrics);
24        self.tracing = self.tracing.merge(other.tracing);
25        self.health = self.health.merge(other.health);
26        self
27    }
28}
29
30/// Metrics configuration
31#[derive(Debug, Clone, Serialize, Deserialize)]
32pub struct MetricsConfig {
33    /// Enable metrics
34    #[serde(default = "default_true")]
35    pub enabled: bool,
36    /// Metrics port
37    #[serde(default = "default_metrics_port")]
38    pub port: u16,
39    /// Metrics path
40    #[serde(default = "default_metrics_path")]
41    pub path: String,
42}
43
44impl Default for MetricsConfig {
45    fn default() -> Self {
46        Self {
47            enabled: true,
48            port: default_metrics_port(),
49            path: default_metrics_path(),
50        }
51    }
52}
53
54impl MetricsConfig {
55    /// Merge metrics configurations
56    pub fn merge(mut self, other: Self) -> Self {
57        if !other.enabled {
58            self.enabled = other.enabled;
59        }
60        if other.port != default_metrics_port() {
61            self.port = other.port;
62        }
63        if other.path != default_metrics_path() {
64            self.path = other.path;
65        }
66        self
67    }
68}
69
70/// Tracing configuration
71#[derive(Debug, Clone, Serialize, Deserialize)]
72pub struct TracingConfig {
73    /// Enable tracing
74    #[serde(default)]
75    pub enabled: bool,
76    /// Tracing endpoint
77    pub endpoint: Option<String>,
78    /// Service name
79    #[serde(default = "default_service_name")]
80    pub service_name: String,
81}
82
83impl Default for TracingConfig {
84    fn default() -> Self {
85        Self {
86            enabled: false,
87            endpoint: None,
88            service_name: default_service_name(),
89        }
90    }
91}
92
93impl TracingConfig {
94    /// Merge tracing configurations
95    pub fn merge(mut self, other: Self) -> Self {
96        if other.enabled {
97            self.enabled = other.enabled;
98        }
99        if other.endpoint.is_some() {
100            self.endpoint = other.endpoint;
101        }
102        if other.service_name != default_service_name() {
103            self.service_name = other.service_name;
104        }
105        self
106    }
107}
108
109/// Health check configuration
110#[derive(Debug, Clone, Serialize, Deserialize)]
111pub struct HealthConfig {
112    /// Health check path
113    #[serde(default = "default_health_path")]
114    pub path: String,
115    /// Enable detailed health checks
116    #[serde(default = "default_true")]
117    pub detailed: bool,
118}
119
120impl Default for HealthConfig {
121    fn default() -> Self {
122        Self {
123            path: default_health_path(),
124            detailed: true,
125        }
126    }
127}
128
129impl HealthConfig {
130    /// Merge health configurations
131    pub fn merge(mut self, other: Self) -> Self {
132        if other.path != default_health_path() {
133            self.path = other.path;
134        }
135        if !other.detailed {
136            self.detailed = other.detailed;
137        }
138        self
139    }
140}
141
142fn default_true() -> bool {
143    true
144}
145
146#[cfg(test)]
147mod tests {
148    use super::*;
149
150    // ==================== MetricsConfig Tests ====================
151
152    #[test]
153    fn test_metrics_config_default() {
154        let config = MetricsConfig::default();
155        assert!(config.enabled);
156        assert_eq!(config.port, 9090);
157        assert_eq!(config.path, "/metrics");
158    }
159
160    #[test]
161    fn test_metrics_config_structure() {
162        let config = MetricsConfig {
163            enabled: false,
164            port: 8080,
165            path: "/prometheus".to_string(),
166        };
167        assert!(!config.enabled);
168        assert_eq!(config.port, 8080);
169        assert_eq!(config.path, "/prometheus");
170    }
171
172    #[test]
173    fn test_metrics_config_serialization() {
174        let config = MetricsConfig {
175            enabled: true,
176            port: 9100,
177            path: "/stats".to_string(),
178        };
179        let json = serde_json::to_value(&config).unwrap();
180        assert_eq!(json["enabled"], true);
181        assert_eq!(json["port"], 9100);
182        assert_eq!(json["path"], "/stats");
183    }
184
185    #[test]
186    fn test_metrics_config_deserialization() {
187        let json = r#"{"enabled": false, "port": 3000, "path": "/api/metrics"}"#;
188        let config: MetricsConfig = serde_json::from_str(json).unwrap();
189        assert!(!config.enabled);
190        assert_eq!(config.port, 3000);
191    }
192
193    #[test]
194    fn test_metrics_config_merge_disabled() {
195        let base = MetricsConfig::default();
196        let other = MetricsConfig {
197            enabled: false,
198            port: 9090,
199            path: "/metrics".to_string(),
200        };
201        let merged = base.merge(other);
202        assert!(!merged.enabled);
203    }
204
205    #[test]
206    fn test_metrics_config_merge_port() {
207        let base = MetricsConfig::default();
208        let other = MetricsConfig {
209            enabled: true,
210            port: 8888,
211            path: "/metrics".to_string(),
212        };
213        let merged = base.merge(other);
214        assert_eq!(merged.port, 8888);
215    }
216
217    #[test]
218    fn test_metrics_config_clone() {
219        let config = MetricsConfig::default();
220        let cloned = config.clone();
221        assert_eq!(config.enabled, cloned.enabled);
222        assert_eq!(config.port, cloned.port);
223    }
224
225    // ==================== TracingConfig Tests ====================
226
227    #[test]
228    fn test_tracing_config_default() {
229        let config = TracingConfig::default();
230        assert!(!config.enabled);
231        assert!(config.endpoint.is_none());
232        assert_eq!(config.service_name, "litellm-rs");
233    }
234
235    #[test]
236    fn test_tracing_config_structure() {
237        let config = TracingConfig {
238            enabled: true,
239            endpoint: Some("http://jaeger:14268".to_string()),
240            service_name: "my-gateway".to_string(),
241        };
242        assert!(config.enabled);
243        assert_eq!(config.endpoint, Some("http://jaeger:14268".to_string()));
244    }
245
246    #[test]
247    fn test_tracing_config_serialization() {
248        let config = TracingConfig {
249            enabled: true,
250            endpoint: Some("http://otel:4317".to_string()),
251            service_name: "api-gateway".to_string(),
252        };
253        let json = serde_json::to_value(&config).unwrap();
254        assert_eq!(json["enabled"], true);
255        assert_eq!(json["endpoint"], "http://otel:4317");
256    }
257
258    #[test]
259    fn test_tracing_config_deserialization() {
260        let json =
261            r#"{"enabled": true, "endpoint": "http://zipkin:9411", "service_name": "tracer"}"#;
262        let config: TracingConfig = serde_json::from_str(json).unwrap();
263        assert!(config.enabled);
264        assert_eq!(config.service_name, "tracer");
265    }
266
267    #[test]
268    fn test_tracing_config_merge_enabled() {
269        let base = TracingConfig::default();
270        let other = TracingConfig {
271            enabled: true,
272            endpoint: None,
273            service_name: "litellm-gateway".to_string(),
274        };
275        let merged = base.merge(other);
276        assert!(merged.enabled);
277    }
278
279    #[test]
280    fn test_tracing_config_merge_endpoint() {
281        let base = TracingConfig::default();
282        let other = TracingConfig {
283            enabled: false,
284            endpoint: Some("http://collector:4317".to_string()),
285            service_name: "litellm-gateway".to_string(),
286        };
287        let merged = base.merge(other);
288        assert_eq!(merged.endpoint, Some("http://collector:4317".to_string()));
289    }
290
291    #[test]
292    fn test_tracing_config_clone() {
293        let config = TracingConfig::default();
294        let cloned = config.clone();
295        assert_eq!(config.enabled, cloned.enabled);
296        assert_eq!(config.service_name, cloned.service_name);
297    }
298
299    // ==================== HealthConfig Tests ====================
300
301    #[test]
302    fn test_health_config_default() {
303        let config = HealthConfig::default();
304        assert_eq!(config.path, "/health");
305        assert!(config.detailed);
306    }
307
308    #[test]
309    fn test_health_config_structure() {
310        let config = HealthConfig {
311            path: "/api/health".to_string(),
312            detailed: false,
313        };
314        assert_eq!(config.path, "/api/health");
315        assert!(!config.detailed);
316    }
317
318    #[test]
319    fn test_health_config_serialization() {
320        let config = HealthConfig {
321            path: "/status".to_string(),
322            detailed: true,
323        };
324        let json = serde_json::to_value(&config).unwrap();
325        assert_eq!(json["path"], "/status");
326        assert_eq!(json["detailed"], true);
327    }
328
329    #[test]
330    fn test_health_config_deserialization() {
331        let json = r#"{"path": "/ready", "detailed": false}"#;
332        let config: HealthConfig = serde_json::from_str(json).unwrap();
333        assert_eq!(config.path, "/ready");
334        assert!(!config.detailed);
335    }
336
337    #[test]
338    fn test_health_config_merge_path() {
339        let base = HealthConfig::default();
340        let other = HealthConfig {
341            path: "/healthz".to_string(),
342            detailed: true,
343        };
344        let merged = base.merge(other);
345        assert_eq!(merged.path, "/healthz");
346    }
347
348    #[test]
349    fn test_health_config_merge_detailed() {
350        let base = HealthConfig::default();
351        let other = HealthConfig {
352            path: "/health".to_string(),
353            detailed: false,
354        };
355        let merged = base.merge(other);
356        assert!(!merged.detailed);
357    }
358
359    #[test]
360    fn test_health_config_clone() {
361        let config = HealthConfig::default();
362        let cloned = config.clone();
363        assert_eq!(config.path, cloned.path);
364        assert_eq!(config.detailed, cloned.detailed);
365    }
366
367    // ==================== MonitoringConfig Tests ====================
368
369    #[test]
370    fn test_monitoring_config_default() {
371        let config = MonitoringConfig::default();
372        assert!(config.metrics.enabled);
373        assert!(!config.tracing.enabled);
374        assert!(config.health.detailed);
375    }
376
377    #[test]
378    fn test_monitoring_config_structure() {
379        let config = MonitoringConfig {
380            metrics: MetricsConfig::default(),
381            tracing: TracingConfig::default(),
382            health: HealthConfig::default(),
383        };
384        assert_eq!(config.metrics.port, 9090);
385    }
386
387    #[test]
388    fn test_monitoring_config_serialization() {
389        let config = MonitoringConfig::default();
390        let json = serde_json::to_value(&config).unwrap();
391        assert!(json["metrics"].is_object());
392        assert!(json["tracing"].is_object());
393        assert!(json["health"].is_object());
394    }
395
396    #[test]
397    fn test_monitoring_config_merge() {
398        let base = MonitoringConfig::default();
399        let other = MonitoringConfig {
400            metrics: MetricsConfig {
401                enabled: false,
402                port: 9090,
403                path: "/metrics".to_string(),
404            },
405            tracing: TracingConfig::default(),
406            health: HealthConfig::default(),
407        };
408        let merged = base.merge(other);
409        assert!(!merged.metrics.enabled);
410    }
411
412    #[test]
413    fn test_monitoring_config_clone() {
414        let config = MonitoringConfig::default();
415        let cloned = config.clone();
416        assert_eq!(config.metrics.enabled, cloned.metrics.enabled);
417    }
418}