1use std::collections::HashMap;
9use std::sync::{Arc, RwLock};
10
11#[cfg(feature = "count")]
12use crate::Counter;
13#[cfg(feature = "gauge")]
14use crate::Gauge;
15#[cfg(feature = "meter")]
16use crate::RateMeter;
17#[cfg(feature = "timer")]
18use crate::Timer;
19
20#[repr(align(64))]
30pub struct Registry {
31 #[cfg(feature = "count")]
32 counters: RwLock<HashMap<String, Arc<Counter>>>,
33 #[cfg(feature = "gauge")]
34 gauges: RwLock<HashMap<String, Arc<Gauge>>>,
35 #[cfg(feature = "timer")]
36 timers: RwLock<HashMap<String, Arc<Timer>>>,
37 #[cfg(feature = "meter")]
38 rate_meters: RwLock<HashMap<String, Arc<RateMeter>>>,
39}
40
41impl Registry {
42 pub fn new() -> Self {
44 Self {
45 #[cfg(feature = "count")]
46 counters: RwLock::new(HashMap::new()),
47 #[cfg(feature = "gauge")]
48 gauges: RwLock::new(HashMap::new()),
49 #[cfg(feature = "timer")]
50 timers: RwLock::new(HashMap::new()),
51 #[cfg(feature = "meter")]
52 rate_meters: RwLock::new(HashMap::new()),
53 }
54 }
55
56 #[cfg(feature = "count")]
60 pub fn get_or_create_counter(&self, name: &str) -> Arc<Counter> {
61 if let Ok(counters) = self.counters.read() {
63 if let Some(counter) = counters.get(name) {
64 return counter.clone();
65 }
66 }
67
68 let mut counters = self.counters.write().unwrap_or_else(|e| e.into_inner());
70 counters
71 .entry(name.to_string())
72 .or_insert_with(|| Arc::new(Counter::new()))
73 .clone()
74 }
75
76 #[cfg(feature = "gauge")]
80 pub fn get_or_create_gauge(&self, name: &str) -> Arc<Gauge> {
81 if let Ok(gauges) = self.gauges.read() {
83 if let Some(gauge) = gauges.get(name) {
84 return gauge.clone();
85 }
86 }
87
88 let mut gauges = self.gauges.write().unwrap_or_else(|e| e.into_inner());
90 gauges
91 .entry(name.to_string())
92 .or_insert_with(|| Arc::new(Gauge::new()))
93 .clone()
94 }
95
96 #[cfg(feature = "timer")]
100 pub fn get_or_create_timer(&self, name: &str) -> Arc<Timer> {
101 if let Ok(timers) = self.timers.read() {
103 if let Some(timer) = timers.get(name) {
104 return timer.clone();
105 }
106 }
107
108 let mut timers = self.timers.write().unwrap_or_else(|e| e.into_inner());
110 timers
111 .entry(name.to_string())
112 .or_insert_with(|| Arc::new(Timer::new()))
113 .clone()
114 }
115
116 #[cfg(feature = "meter")]
120 pub fn get_or_create_rate_meter(&self, name: &str) -> Arc<RateMeter> {
121 if let Ok(rate_meters) = self.rate_meters.read() {
123 if let Some(rate_meter) = rate_meters.get(name) {
124 return rate_meter.clone();
125 }
126 }
127
128 let mut rate_meters = self.rate_meters.write().unwrap_or_else(|e| e.into_inner());
130 rate_meters
131 .entry(name.to_string())
132 .or_insert_with(|| Arc::new(RateMeter::new()))
133 .clone()
134 }
135
136 #[cfg(feature = "count")]
138 pub fn counter_names(&self) -> Vec<String> {
139 self.counters
140 .read()
141 .unwrap_or_else(|e| e.into_inner())
142 .keys()
143 .cloned()
144 .collect()
145 }
146
147 #[cfg(feature = "gauge")]
149 pub fn gauge_names(&self) -> Vec<String> {
150 self.gauges
151 .read()
152 .unwrap_or_else(|e| e.into_inner())
153 .keys()
154 .cloned()
155 .collect()
156 }
157
158 #[cfg(feature = "timer")]
160 pub fn timer_names(&self) -> Vec<String> {
161 self.timers
162 .read()
163 .unwrap_or_else(|e| e.into_inner())
164 .keys()
165 .cloned()
166 .collect()
167 }
168
169 #[cfg(feature = "meter")]
171 pub fn rate_meter_names(&self) -> Vec<String> {
172 self.rate_meters
173 .read()
174 .unwrap_or_else(|e| e.into_inner())
175 .keys()
176 .cloned()
177 .collect()
178 }
179
180 pub fn metric_count(&self) -> usize {
182 #[allow(unused_mut)]
183 let mut total = 0;
184 #[cfg(feature = "count")]
185 {
186 total += self
187 .counters
188 .read()
189 .unwrap_or_else(|e| e.into_inner())
190 .len();
191 }
192 #[cfg(feature = "gauge")]
193 {
194 total += self.gauges.read().unwrap_or_else(|e| e.into_inner()).len();
195 }
196 #[cfg(feature = "timer")]
197 {
198 total += self.timers.read().unwrap_or_else(|e| e.into_inner()).len();
199 }
200 #[cfg(feature = "meter")]
201 {
202 total += self
203 .rate_meters
204 .read()
205 .unwrap_or_else(|e| e.into_inner())
206 .len();
207 }
208 total
209 }
210
211 pub fn clear(&self) {
213 #[cfg(feature = "count")]
214 self.counters
215 .write()
216 .unwrap_or_else(|e| e.into_inner())
217 .clear();
218 #[cfg(feature = "gauge")]
219 self.gauges
220 .write()
221 .unwrap_or_else(|e| e.into_inner())
222 .clear();
223 #[cfg(feature = "timer")]
224 self.timers
225 .write()
226 .unwrap_or_else(|e| e.into_inner())
227 .clear();
228 #[cfg(feature = "meter")]
229 self.rate_meters
230 .write()
231 .unwrap_or_else(|e| e.into_inner())
232 .clear();
233 }
234}
235
236impl Default for Registry {
237 fn default() -> Self {
238 Self::new()
239 }
240}
241
242#[cfg(test)]
246#[cfg(all(feature = "count", feature = "gauge", feature = "timer"))]
248mod tests {
249 use super::*;
250 use std::sync::Arc;
251 use std::thread;
252 #[test]
253 fn test_counter_registration() {
254 let registry = Registry::new();
255
256 let counter1 = registry.get_or_create_counter("requests");
257 let counter2 = registry.get_or_create_counter("requests");
258
259 assert!(Arc::ptr_eq(&counter1, &counter2));
261 assert_eq!(registry.metric_count(), 1);
262 }
263
264 #[test]
265 fn test_gauge_registration() {
266 let registry = Registry::new();
267
268 let gauge1 = registry.get_or_create_gauge("cpu_usage");
269 let gauge2 = registry.get_or_create_gauge("cpu_usage");
270
271 assert!(Arc::ptr_eq(&gauge1, &gauge2));
273 assert_eq!(registry.metric_count(), 1);
274 }
275
276 #[test]
277 fn test_timer_registration() {
278 let registry = Registry::new();
279
280 let timer1 = registry.get_or_create_timer("db_query");
281 let timer2 = registry.get_or_create_timer("db_query");
282
283 assert!(Arc::ptr_eq(&timer1, &timer2));
285 assert_eq!(registry.metric_count(), 1);
286 }
287
288 #[test]
289 #[cfg(feature = "meter")]
290 fn test_rate_meter_registration() {
291 let registry = Registry::new();
292
293 let meter1 = registry.get_or_create_rate_meter("api_calls");
294 let meter2 = registry.get_or_create_rate_meter("api_calls");
295
296 assert!(Arc::ptr_eq(&meter1, &meter2));
298 assert_eq!(registry.metric_count(), 1);
299 }
300
301 #[test]
302 #[cfg(feature = "meter")]
303 fn test_mixed_metrics() {
304 let registry = Registry::new();
305
306 let _counter = registry.get_or_create_counter("requests");
307 let _gauge = registry.get_or_create_gauge("cpu_usage");
308 let _timer = registry.get_or_create_timer("db_query");
309 let _meter = registry.get_or_create_rate_meter("api_calls");
310
311 assert_eq!(registry.metric_count(), 4);
312 assert_eq!(registry.counter_names().len(), 1);
313 assert_eq!(registry.gauge_names().len(), 1);
314 assert_eq!(registry.timer_names().len(), 1);
315 assert_eq!(registry.rate_meter_names().len(), 1);
316 }
317
318 #[test]
319 fn test_concurrent_access() {
320 let registry = Arc::new(Registry::new());
321 let mut handles = vec![];
322
323 for _i in 0..10 {
325 let registry = registry.clone();
326 let handle = thread::spawn(move || {
327 let counter = registry.get_or_create_counter("concurrent_test");
328 counter.inc();
329 counter.get()
330 });
331 handles.push(handle);
332 }
333
334 for handle in handles {
336 handle.join().unwrap();
337 }
338
339 assert_eq!(registry.metric_count(), 1);
341 let counter = registry.get_or_create_counter("concurrent_test");
342 assert_eq!(counter.get(), 10);
343 }
344
345 #[test]
346 fn test_clear() {
347 let registry = Registry::new();
348
349 let _counter = registry.get_or_create_counter("requests");
350 let _gauge = registry.get_or_create_gauge("cpu_usage");
351
352 assert_eq!(registry.metric_count(), 2);
353
354 registry.clear();
355 assert_eq!(registry.metric_count(), 0);
356 }
357
358 #[test]
359 fn test_metric_names() {
360 let registry = Registry::new();
361
362 let _counter1 = registry.get_or_create_counter("requests");
363 let _counter2 = registry.get_or_create_counter("errors");
364 let _gauge1 = registry.get_or_create_gauge("cpu_usage");
365
366 let counter_names = registry.counter_names();
367 let gauge_names = registry.gauge_names();
368
369 assert_eq!(counter_names.len(), 2);
370 assert_eq!(gauge_names.len(), 1);
371 assert!(counter_names.contains(&"requests".to_string()));
372 assert!(counter_names.contains(&"errors".to_string()));
373 assert!(gauge_names.contains(&"cpu_usage".to_string()));
374 }
375
376 #[test]
377 #[cfg(feature = "meter")]
378 fn test_duplicate_names_across_types_are_independent() {
379 let registry = Registry::new();
380
381 let c = registry.get_or_create_counter("same_name");
382 let g = registry.get_or_create_gauge("same_name");
383 let t = registry.get_or_create_timer("same_name");
384 let r = registry.get_or_create_rate_meter("same_name");
385
386 assert_eq!(registry.metric_count(), 4);
388
389 let c_addr = Arc::as_ptr(&c) as usize;
391 let g_addr = Arc::as_ptr(&g) as usize;
392 let t_addr = Arc::as_ptr(&t) as usize;
393 let r_addr = Arc::as_ptr(&r) as usize;
394
395 assert_ne!(c_addr, g_addr);
396 assert_ne!(c_addr, t_addr);
397 assert_ne!(c_addr, r_addr);
398 assert_ne!(g_addr, t_addr);
399 assert_ne!(g_addr, r_addr);
400 assert_ne!(t_addr, r_addr);
401 }
402
403 #[test]
404 fn test_clear_then_recreate_returns_new_instances() {
405 let registry = Registry::new();
406
407 let counter_before = registry.get_or_create_counter("requests");
408 let gauge_before = registry.get_or_create_gauge("cpu");
409 assert_eq!(registry.metric_count(), 2);
410
411 registry.clear();
413 assert_eq!(registry.metric_count(), 0);
414
415 let counter_after = registry.get_or_create_counter("requests");
416 let gauge_after = registry.get_or_create_gauge("cpu");
417
418 assert!(!Arc::ptr_eq(&counter_before, &counter_after));
420 assert!(!Arc::ptr_eq(&gauge_before, &gauge_after));
421 }
422
423 #[test]
424 fn test_concurrent_duplicate_registration_singleton_per_name() {
425 let registry = Arc::new(Registry::new());
426 let mut handles = vec![];
427
428 for _ in 0..16 {
429 let r = registry.clone();
430 handles.push(thread::spawn(move || r.get_or_create_timer("dup")));
431 }
432
433 let first = registry.get_or_create_timer("dup");
434 for h in handles {
435 let timer = h.join().unwrap();
436 assert!(Arc::ptr_eq(&first, &timer));
437 }
438 assert_eq!(registry.metric_count(), 1);
439 }
440}