Skip to main content

camel_api/
metrics.rs

1use std::time::Duration;
2
3/// Trait for collecting metrics from the Camel runtime.
4/// Implementations can integrate with Prometheus, OpenTelemetry, etc.
5pub trait MetricsCollector: Send + Sync {
6    /// Record exchange processing time
7    fn record_exchange_duration(&self, route_id: &str, duration: Duration);
8
9    /// Increment error counter
10    fn increment_errors(&self, route_id: &str, error_type: &str);
11
12    /// Increment exchange counter
13    fn increment_exchanges(&self, route_id: &str);
14
15    /// Update queue depth
16    fn set_queue_depth(&self, route_id: &str, depth: usize);
17
18    /// Record circuit breaker state change
19    fn record_circuit_breaker_change(&self, route_id: &str, from: &str, to: &str);
20}
21
22/// No-op metrics collector for default behavior
23pub struct NoOpMetrics;
24
25impl MetricsCollector for NoOpMetrics {
26    fn record_exchange_duration(&self, _route_id: &str, _duration: Duration) {}
27    fn increment_errors(&self, _route_id: &str, _error_type: &str) {}
28    fn increment_exchanges(&self, _route_id: &str) {}
29    fn set_queue_depth(&self, _route_id: &str, _depth: usize) {}
30    fn record_circuit_breaker_change(&self, _route_id: &str, _from: &str, _to: &str) {}
31}
32
33#[cfg(test)]
34mod tests {
35    use super::*;
36    use std::sync::Arc;
37
38    #[test]
39    fn test_noop_metrics_implements_trait() {
40        let metrics = NoOpMetrics;
41        let metrics_arc: Arc<dyn MetricsCollector> = Arc::new(metrics);
42
43        // All methods should execute without panicking
44        metrics_arc.record_exchange_duration("test-route", Duration::from_millis(100));
45        metrics_arc.increment_errors("test-route", "test-error");
46        metrics_arc.increment_exchanges("test-route");
47        metrics_arc.set_queue_depth("test-route", 5);
48        metrics_arc.record_circuit_breaker_change("test-route", "closed", "open");
49    }
50
51    #[test]
52    fn test_custom_metrics_collector() {
53        struct TestMetrics {
54            exchange_count: std::sync::atomic::AtomicU64,
55        }
56
57        impl MetricsCollector for TestMetrics {
58            fn record_exchange_duration(&self, route_id: &str, duration: Duration) {
59                // In a real implementation, this would record the duration
60                println!("Route {} took {}ms", route_id, duration.as_millis());
61            }
62
63            fn increment_errors(&self, route_id: &str, error_type: &str) {
64                // In a real implementation, this would increment an error counter
65                println!("Route {} had error: {}", route_id, error_type);
66            }
67
68            fn increment_exchanges(&self, route_id: &str) {
69                // In a real implementation, this would increment an exchange counter
70                self.exchange_count
71                    .fetch_add(1, std::sync::atomic::Ordering::Relaxed);
72                println!("Route {} processed exchange", route_id);
73            }
74
75            fn set_queue_depth(&self, route_id: &str, depth: usize) {
76                // In a real implementation, this would update a gauge
77                println!("Route {} queue depth: {}", route_id, depth);
78            }
79
80            fn record_circuit_breaker_change(&self, route_id: &str, from: &str, to: &str) {
81                // In a real implementation, this would record the state change
82                println!("Route {} circuit breaker: {} -> {}", route_id, from, to);
83            }
84        }
85
86        let test_metrics = TestMetrics {
87            exchange_count: std::sync::atomic::AtomicU64::new(0),
88        };
89        let metrics_arc: Arc<dyn MetricsCollector> = Arc::new(test_metrics);
90
91        // Test that all methods work
92        metrics_arc.record_exchange_duration("test-route", Duration::from_millis(100));
93        metrics_arc.increment_errors("test-route", "test-error");
94        metrics_arc.increment_exchanges("test-route");
95        metrics_arc.set_queue_depth("test-route", 5);
96        metrics_arc.record_circuit_breaker_change("test-route", "closed", "open");
97
98        // Note: We can't easily test the counter value without additional accessors
99        // This is just to verify the trait implementation works
100    }
101}