rust_serv/metrics/
collector.rs1use std::sync::Arc;
4use std::collections::HashMap;
5
6use super::counter::Counter;
7use super::gauge::Gauge;
8use super::histogram::Histogram;
9
10pub 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 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 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 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 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 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 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 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 pub fn all_counters(&self) -> Vec<Arc<Counter>> {
82 let counters = self.counters.read().unwrap();
83 counters.values().cloned().collect()
84 }
85
86 pub fn all_gauges(&self) -> Vec<Arc<Gauge>> {
88 let gauges = self.gauges.read().unwrap();
89 gauges.values().cloned().collect()
90 }
91
92 pub fn all_histograms(&self) -> Vec<Arc<Histogram>> {
94 let histograms = self.histograms.read().unwrap();
95 histograms.values().cloned().collect()
96 }
97
98 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 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 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 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}