Skip to main content

rust_serv/metrics/
collector.rs

1//! Central metrics collector
2
3use std::sync::Arc;
4use std::collections::HashMap;
5
6use super::counter::Counter;
7use super::gauge::Gauge;
8use super::histogram::Histogram;
9
10/// Central metrics collector that manages all metrics
11pub struct MetricsCollector {
12    counters: RwLock<HashMap<String, Arc<Counter>>>,
13    gauges: RwLock<HashMap<String, Arc<Gauge>>>,
14    histograms: RwLock<HashMap<String, Arc<Histogram>>>,
15}
16
17use std::sync::RwLock;
18
19impl MetricsCollector {
20    /// Create a new metrics collector
21    pub fn new() -> Self {
22        Self {
23            counters: RwLock::new(HashMap::new()),
24            gauges: RwLock::new(HashMap::new()),
25            histograms: RwLock::new(HashMap::new()),
26        }
27    }
28
29    /// Create a new counter
30    pub fn create_counter(&self, name: impl Into<String>, help: impl Into<String>) -> Arc<Counter> {
31        let counter = Arc::new(Counter::new(name.into(), help.into()));
32        let name = counter.name().to_string();
33        
34        let mut counters = self.counters.write().unwrap();
35        counters.insert(name, Arc::clone(&counter));
36        
37        counter
38    }
39
40    /// Create a new gauge
41    pub fn create_gauge(&self, name: impl Into<String>, help: impl Into<String>) -> Arc<Gauge> {
42        let gauge = Arc::new(Gauge::new(name.into(), help.into()));
43        let name = gauge.name().to_string();
44        
45        let mut gauges = self.gauges.write().unwrap();
46        gauges.insert(name, Arc::clone(&gauge));
47        
48        gauge
49    }
50
51    /// Create a new histogram
52    pub fn create_histogram(&self, name: impl Into<String>, help: impl Into<String>) -> Arc<Histogram> {
53        let histogram = Arc::new(Histogram::new(name.into(), help.into()));
54        let name = histogram.name().to_string();
55        
56        let mut histograms = self.histograms.write().unwrap();
57        histograms.insert(name, Arc::clone(&histogram));
58        
59        histogram
60    }
61
62    /// Get a counter by name
63    pub fn get_counter(&self, name: &str) -> Option<Arc<Counter>> {
64        let counters = self.counters.read().unwrap();
65        counters.get(name).cloned()
66    }
67
68    /// Get a gauge by name
69    pub fn get_gauge(&self, name: &str) -> Option<Arc<Gauge>> {
70        let gauges = self.gauges.read().unwrap();
71        gauges.get(name).cloned()
72    }
73
74    /// Get a histogram by name
75    pub fn get_histogram(&self, name: &str) -> Option<Arc<Histogram>> {
76        let histograms = self.histograms.read().unwrap();
77        histograms.get(name).cloned()
78    }
79
80    /// Get all counters
81    pub fn all_counters(&self) -> Vec<Arc<Counter>> {
82        let counters = self.counters.read().unwrap();
83        counters.values().cloned().collect()
84    }
85
86    /// Get all gauges
87    pub fn all_gauges(&self) -> Vec<Arc<Gauge>> {
88        let gauges = self.gauges.read().unwrap();
89        gauges.values().cloned().collect()
90    }
91
92    /// Get all histograms
93    pub fn all_histograms(&self) -> Vec<Arc<Histogram>> {
94        let histograms = self.histograms.read().unwrap();
95        histograms.values().cloned().collect()
96    }
97
98    /// Remove a counter by name
99    pub fn remove_counter(&self, name: &str) -> bool {
100        let mut counters = self.counters.write().unwrap();
101        counters.remove(name).is_some()
102    }
103
104    /// Remove a gauge by name
105    pub fn remove_gauge(&self, name: &str) -> bool {
106        let mut gauges = self.gauges.write().unwrap();
107        gauges.remove(name).is_some()
108    }
109
110    /// Remove a histogram by name
111    pub fn remove_histogram(&self, name: &str) -> bool {
112        let mut histograms = self.histograms.write().unwrap();
113        histograms.remove(name).is_some()
114    }
115
116    /// Clear all metrics
117    pub fn clear(&self) {
118        let mut counters = self.counters.write().unwrap();
119        let mut gauges = self.gauges.write().unwrap();
120        let mut histograms = self.histograms.write().unwrap();
121        
122        counters.clear();
123        gauges.clear();
124        histograms.clear();
125    }
126}
127
128impl Default for MetricsCollector {
129    fn default() -> Self {
130        Self::new()
131    }
132}
133
134#[cfg(test)]
135mod tests {
136    use super::*;
137
138    #[test]
139    fn test_collector_creation() {
140        let collector = MetricsCollector::new();
141        assert!(collector.all_counters().is_empty());
142        assert!(collector.all_gauges().is_empty());
143        assert!(collector.all_histograms().is_empty());
144    }
145
146    #[test]
147    fn test_create_counter() {
148        let collector = MetricsCollector::new();
149        let counter = collector.create_counter("requests_total", "Total requests");
150        
151        counter.inc();
152        assert_eq!(counter.get(), 1);
153        assert!(collector.get_counter("requests_total").is_some());
154    }
155
156    #[test]
157    fn test_create_gauge() {
158        let collector = MetricsCollector::new();
159        let gauge = collector.create_gauge("active_connections", "Active connections");
160        
161        gauge.set(10);
162        assert_eq!(gauge.get(), 10);
163        assert!(collector.get_gauge("active_connections").is_some());
164    }
165
166    #[test]
167    fn test_create_histogram() {
168        let collector = MetricsCollector::new();
169        let hist = collector.create_histogram("request_duration", "Request duration");
170        
171        hist.observe(0.1);
172        assert_eq!(hist.count(), 1);
173        assert!(collector.get_histogram("request_duration").is_some());
174    }
175
176    #[test]
177    fn test_get_nonexistent() {
178        let collector = MetricsCollector::new();
179        assert!(collector.get_counter("nonexistent").is_none());
180        assert!(collector.get_gauge("nonexistent").is_none());
181        assert!(collector.get_histogram("nonexistent").is_none());
182    }
183
184    #[test]
185    fn test_all_counters() {
186        let collector = MetricsCollector::new();
187        collector.create_counter("counter1", "Counter 1");
188        collector.create_counter("counter2", "Counter 2");
189        
190        let counters = collector.all_counters();
191        assert_eq!(counters.len(), 2);
192    }
193
194    #[test]
195    fn test_all_gauges() {
196        let collector = MetricsCollector::new();
197        collector.create_gauge("gauge1", "Gauge 1");
198        collector.create_gauge("gauge2", "Gauge 2");
199        collector.create_gauge("gauge3", "Gauge 3");
200        
201        let gauges = collector.all_gauges();
202        assert_eq!(gauges.len(), 3);
203    }
204
205    #[test]
206    fn test_all_histograms() {
207        let collector = MetricsCollector::new();
208        collector.create_histogram("hist1", "Histogram 1");
209        
210        let histograms = collector.all_histograms();
211        assert_eq!(histograms.len(), 1);
212    }
213
214    #[test]
215    fn test_remove_counter() {
216        let collector = MetricsCollector::new();
217        collector.create_counter("test_counter", "Test");
218        
219        assert!(collector.get_counter("test_counter").is_some());
220        assert!(collector.remove_counter("test_counter"));
221        assert!(collector.get_counter("test_counter").is_none());
222    }
223
224    #[test]
225    fn test_remove_nonexistent() {
226        let collector = MetricsCollector::new();
227        assert!(!collector.remove_counter("nonexistent"));
228    }
229
230    #[test]
231    fn test_clear() {
232        let collector = MetricsCollector::new();
233        collector.create_counter("counter1", "Counter 1");
234        collector.create_gauge("gauge1", "Gauge 1");
235        collector.create_histogram("hist1", "Histogram 1");
236        
237        collector.clear();
238        
239        assert!(collector.all_counters().is_empty());
240        assert!(collector.all_gauges().is_empty());
241        assert!(collector.all_histograms().is_empty());
242    }
243
244    #[test]
245    fn test_shared_counter() {
246        let collector = MetricsCollector::new();
247        let counter = collector.create_counter("test", "Test");
248        
249        let counter2 = collector.get_counter("test").unwrap();
250        
251        counter.inc();
252        counter2.inc();
253        
254        assert_eq!(counter.get(), 2);
255        assert_eq!(counter2.get(), 2);
256    }
257
258    #[test]
259    fn test_default() {
260        let collector = MetricsCollector::default();
261        assert!(collector.all_counters().is_empty());
262    }
263}