1use std::collections::HashMap;
41use std::sync::atomic::{AtomicU64, AtomicUsize, Ordering};
42use std::sync::RwLock;
43
44#[cfg(any(
47 feature = "count",
48 feature = "gauge",
49 feature = "timer",
50 feature = "meter",
51 feature = "histogram"
52))]
53use std::sync::{Arc, OnceLock};
54
55#[cfg(feature = "count")]
56use crate::Counter;
57#[cfg(feature = "gauge")]
58use crate::Gauge;
59#[cfg(feature = "histogram")]
60use crate::Histogram;
61#[cfg(feature = "meter")]
62use crate::RateMeter;
63#[cfg(feature = "timer")]
64use crate::Timer;
65
66#[cfg(any(
67 feature = "count",
68 feature = "gauge",
69 feature = "timer",
70 feature = "meter",
71 feature = "histogram"
72))]
73use crate::{LabelSet, MetricsError, Result};
74
75use crate::{MetricKind, MetricMetadata, Unit};
76
77pub const DEFAULT_CARDINALITY_CAP: usize = 10_000;
79
80#[repr(align(64))]
89pub struct Registry {
90 #[cfg(feature = "count")]
91 counters: RwLock<HashMap<String, Arc<Counter>>>,
92 #[cfg(feature = "gauge")]
93 gauges: RwLock<HashMap<String, Arc<Gauge>>>,
94 #[cfg(feature = "timer")]
95 timers: RwLock<HashMap<String, Arc<Timer>>>,
96 #[cfg(feature = "meter")]
97 rate_meters: RwLock<HashMap<String, Arc<RateMeter>>>,
98
99 #[cfg(feature = "count")]
100 labeled_counters: RwLock<HashMap<(String, LabelSet), Arc<Counter>>>,
101 #[cfg(feature = "gauge")]
102 labeled_gauges: RwLock<HashMap<(String, LabelSet), Arc<Gauge>>>,
103 #[cfg(feature = "timer")]
104 labeled_timers: RwLock<HashMap<(String, LabelSet), Arc<Timer>>>,
105 #[cfg(feature = "meter")]
106 labeled_rate_meters: RwLock<HashMap<(String, LabelSet), Arc<RateMeter>>>,
107 #[cfg(feature = "histogram")]
108 histograms: RwLock<HashMap<(String, LabelSet), Arc<Histogram>>>,
109 #[cfg(feature = "histogram")]
110 histogram_buckets: RwLock<HashMap<String, Vec<f64>>>,
111
112 metadata: RwLock<HashMap<String, MetricMetadata>>,
113
114 cardinality_cap: AtomicUsize,
115 cardinality_count: AtomicUsize,
116 cardinality_overflows: AtomicU64,
117}
118
119impl Registry {
120 pub fn new() -> Self {
123 Self {
124 #[cfg(feature = "count")]
125 counters: RwLock::new(HashMap::new()),
126 #[cfg(feature = "gauge")]
127 gauges: RwLock::new(HashMap::new()),
128 #[cfg(feature = "timer")]
129 timers: RwLock::new(HashMap::new()),
130 #[cfg(feature = "meter")]
131 rate_meters: RwLock::new(HashMap::new()),
132
133 #[cfg(feature = "count")]
134 labeled_counters: RwLock::new(HashMap::new()),
135 #[cfg(feature = "gauge")]
136 labeled_gauges: RwLock::new(HashMap::new()),
137 #[cfg(feature = "timer")]
138 labeled_timers: RwLock::new(HashMap::new()),
139 #[cfg(feature = "meter")]
140 labeled_rate_meters: RwLock::new(HashMap::new()),
141 #[cfg(feature = "histogram")]
142 histograms: RwLock::new(HashMap::new()),
143 #[cfg(feature = "histogram")]
144 histogram_buckets: RwLock::new(HashMap::new()),
145
146 metadata: RwLock::new(HashMap::new()),
147
148 cardinality_cap: AtomicUsize::new(DEFAULT_CARDINALITY_CAP),
149 cardinality_count: AtomicUsize::new(0),
150 cardinality_overflows: AtomicU64::new(0),
151 }
152 }
153
154 #[inline]
162 pub fn set_cardinality_cap(&self, cap: usize) {
163 self.cardinality_cap.store(cap, Ordering::Relaxed);
164 }
165
166 #[must_use]
168 #[inline]
169 pub fn cardinality_cap(&self) -> usize {
170 self.cardinality_cap.load(Ordering::Relaxed)
171 }
172
173 #[must_use]
176 #[inline]
177 pub fn cardinality_count(&self) -> usize {
178 self.cardinality_count.load(Ordering::Relaxed)
179 }
180
181 #[must_use]
184 #[inline]
185 pub fn cardinality_overflows(&self) -> u64 {
186 self.cardinality_overflows.load(Ordering::Relaxed)
187 }
188
189 #[cfg(any(
192 feature = "count",
193 feature = "gauge",
194 feature = "timer",
195 feature = "meter",
196 feature = "histogram"
197 ))]
198 fn try_acquire_slot(&self) -> bool {
199 let cap = self.cardinality_cap();
200 loop {
201 let current = self.cardinality_count.load(Ordering::Relaxed);
202 if current >= cap {
203 self.cardinality_overflows.fetch_add(1, Ordering::Relaxed);
204 return false;
205 }
206 if self
207 .cardinality_count
208 .compare_exchange_weak(current, current + 1, Ordering::Relaxed, Ordering::Relaxed)
209 .is_ok()
210 {
211 return true;
212 }
213 }
214 }
215
216 pub fn describe(&self, name: &str, metadata: MetricMetadata) {
224 self.metadata
225 .write()
226 .unwrap_or_else(|e| e.into_inner())
227 .insert(name.to_string(), metadata);
228 }
229
230 pub fn describe_counter(
232 &self,
233 name: &str,
234 help: impl Into<std::borrow::Cow<'static, str>>,
235 unit: Unit,
236 ) {
237 self.describe(name, MetricMetadata::new(MetricKind::Counter, help, unit));
238 }
239
240 pub fn describe_gauge(
242 &self,
243 name: &str,
244 help: impl Into<std::borrow::Cow<'static, str>>,
245 unit: Unit,
246 ) {
247 self.describe(name, MetricMetadata::new(MetricKind::Gauge, help, unit));
248 }
249
250 pub fn describe_timer(
252 &self,
253 name: &str,
254 help: impl Into<std::borrow::Cow<'static, str>>,
255 unit: Unit,
256 ) {
257 self.describe(name, MetricMetadata::new(MetricKind::Timer, help, unit));
258 }
259
260 pub fn describe_rate(
262 &self,
263 name: &str,
264 help: impl Into<std::borrow::Cow<'static, str>>,
265 unit: Unit,
266 ) {
267 self.describe(name, MetricMetadata::new(MetricKind::Rate, help, unit));
268 }
269
270 pub fn describe_histogram(
272 &self,
273 name: &str,
274 help: impl Into<std::borrow::Cow<'static, str>>,
275 unit: Unit,
276 ) {
277 self.describe(name, MetricMetadata::new(MetricKind::Histogram, help, unit));
278 }
279
280 #[must_use]
282 pub fn metadata(&self, name: &str) -> Option<MetricMetadata> {
283 self.metadata
284 .read()
285 .unwrap_or_else(|e| e.into_inner())
286 .get(name)
287 .cloned()
288 }
289
290 #[cfg(feature = "count")]
298 pub fn get_or_create_counter(&self, name: &str) -> Arc<Counter> {
299 if let Ok(map) = self.counters.read() {
300 if let Some(c) = map.get(name) {
301 return c.clone();
302 }
303 }
304 let mut map = self.counters.write().unwrap_or_else(|e| e.into_inner());
305 map.entry(name.to_string())
306 .or_insert_with(|| Arc::new(Counter::new()))
307 .clone()
308 }
309
310 #[cfg(feature = "count")]
313 pub fn get_or_create_counter_with(&self, name: &str, labels: &LabelSet) -> Arc<Counter> {
314 if labels.is_empty() {
315 return self.get_or_create_counter(name);
316 }
317 match self.try_get_or_create_counter_with(name, labels) {
318 Ok(c) => c,
319 Err(_) => counter_overflow_sink().clone(),
320 }
321 }
322
323 #[cfg(feature = "count")]
326 pub fn try_get_or_create_counter_with(
327 &self,
328 name: &str,
329 labels: &LabelSet,
330 ) -> Result<Arc<Counter>> {
331 if labels.is_empty() {
332 return Ok(self.get_or_create_counter(name));
333 }
334 if let Ok(map) = self.labeled_counters.read() {
335 if let Some(c) = map.get(&(name.to_string(), labels.clone())) {
336 return Ok(c.clone());
337 }
338 }
339 let mut map = self
340 .labeled_counters
341 .write()
342 .unwrap_or_else(|e| e.into_inner());
343 let key = (name.to_string(), labels.clone());
344 if let Some(c) = map.get(&key) {
345 return Ok(c.clone());
346 }
347 if !self.try_acquire_slot() {
348 return Err(MetricsError::CardinalityExceeded);
349 }
350 let c = Arc::new(Counter::new());
351 map.insert(key, c.clone());
352 Ok(c)
353 }
354
355 #[cfg(feature = "gauge")]
361 pub fn get_or_create_gauge(&self, name: &str) -> Arc<Gauge> {
362 if let Ok(map) = self.gauges.read() {
363 if let Some(g) = map.get(name) {
364 return g.clone();
365 }
366 }
367 let mut map = self.gauges.write().unwrap_or_else(|e| e.into_inner());
368 map.entry(name.to_string())
369 .or_insert_with(|| Arc::new(Gauge::new()))
370 .clone()
371 }
372
373 #[cfg(feature = "gauge")]
375 pub fn get_or_create_gauge_with(&self, name: &str, labels: &LabelSet) -> Arc<Gauge> {
376 if labels.is_empty() {
377 return self.get_or_create_gauge(name);
378 }
379 match self.try_get_or_create_gauge_with(name, labels) {
380 Ok(g) => g,
381 Err(_) => gauge_overflow_sink().clone(),
382 }
383 }
384
385 #[cfg(feature = "gauge")]
388 pub fn try_get_or_create_gauge_with(
389 &self,
390 name: &str,
391 labels: &LabelSet,
392 ) -> Result<Arc<Gauge>> {
393 if labels.is_empty() {
394 return Ok(self.get_or_create_gauge(name));
395 }
396 if let Ok(map) = self.labeled_gauges.read() {
397 if let Some(g) = map.get(&(name.to_string(), labels.clone())) {
398 return Ok(g.clone());
399 }
400 }
401 let mut map = self
402 .labeled_gauges
403 .write()
404 .unwrap_or_else(|e| e.into_inner());
405 let key = (name.to_string(), labels.clone());
406 if let Some(g) = map.get(&key) {
407 return Ok(g.clone());
408 }
409 if !self.try_acquire_slot() {
410 return Err(MetricsError::CardinalityExceeded);
411 }
412 let g = Arc::new(Gauge::new());
413 map.insert(key, g.clone());
414 Ok(g)
415 }
416
417 #[cfg(feature = "timer")]
423 pub fn get_or_create_timer(&self, name: &str) -> Arc<Timer> {
424 if let Ok(map) = self.timers.read() {
425 if let Some(t) = map.get(name) {
426 return t.clone();
427 }
428 }
429 let mut map = self.timers.write().unwrap_or_else(|e| e.into_inner());
430 map.entry(name.to_string())
431 .or_insert_with(|| Arc::new(Timer::new()))
432 .clone()
433 }
434
435 #[cfg(feature = "timer")]
437 pub fn get_or_create_timer_with(&self, name: &str, labels: &LabelSet) -> Arc<Timer> {
438 if labels.is_empty() {
439 return self.get_or_create_timer(name);
440 }
441 match self.try_get_or_create_timer_with(name, labels) {
442 Ok(t) => t,
443 Err(_) => timer_overflow_sink().clone(),
444 }
445 }
446
447 #[cfg(feature = "timer")]
450 pub fn try_get_or_create_timer_with(
451 &self,
452 name: &str,
453 labels: &LabelSet,
454 ) -> Result<Arc<Timer>> {
455 if labels.is_empty() {
456 return Ok(self.get_or_create_timer(name));
457 }
458 if let Ok(map) = self.labeled_timers.read() {
459 if let Some(t) = map.get(&(name.to_string(), labels.clone())) {
460 return Ok(t.clone());
461 }
462 }
463 let mut map = self
464 .labeled_timers
465 .write()
466 .unwrap_or_else(|e| e.into_inner());
467 let key = (name.to_string(), labels.clone());
468 if let Some(t) = map.get(&key) {
469 return Ok(t.clone());
470 }
471 if !self.try_acquire_slot() {
472 return Err(MetricsError::CardinalityExceeded);
473 }
474 let t = Arc::new(Timer::new());
475 map.insert(key, t.clone());
476 Ok(t)
477 }
478
479 #[cfg(feature = "meter")]
485 pub fn get_or_create_rate_meter(&self, name: &str) -> Arc<RateMeter> {
486 if let Ok(map) = self.rate_meters.read() {
487 if let Some(r) = map.get(name) {
488 return r.clone();
489 }
490 }
491 let mut map = self.rate_meters.write().unwrap_or_else(|e| e.into_inner());
492 map.entry(name.to_string())
493 .or_insert_with(|| Arc::new(RateMeter::new()))
494 .clone()
495 }
496
497 #[cfg(feature = "meter")]
500 pub fn get_or_create_rate_meter_with(&self, name: &str, labels: &LabelSet) -> Arc<RateMeter> {
501 if labels.is_empty() {
502 return self.get_or_create_rate_meter(name);
503 }
504 match self.try_get_or_create_rate_meter_with(name, labels) {
505 Ok(r) => r,
506 Err(_) => rate_meter_overflow_sink().clone(),
507 }
508 }
509
510 #[cfg(feature = "meter")]
513 pub fn try_get_or_create_rate_meter_with(
514 &self,
515 name: &str,
516 labels: &LabelSet,
517 ) -> Result<Arc<RateMeter>> {
518 if labels.is_empty() {
519 return Ok(self.get_or_create_rate_meter(name));
520 }
521 if let Ok(map) = self.labeled_rate_meters.read() {
522 if let Some(r) = map.get(&(name.to_string(), labels.clone())) {
523 return Ok(r.clone());
524 }
525 }
526 let mut map = self
527 .labeled_rate_meters
528 .write()
529 .unwrap_or_else(|e| e.into_inner());
530 let key = (name.to_string(), labels.clone());
531 if let Some(r) = map.get(&key) {
532 return Ok(r.clone());
533 }
534 if !self.try_acquire_slot() {
535 return Err(MetricsError::CardinalityExceeded);
536 }
537 let r = Arc::new(RateMeter::new());
538 map.insert(key, r.clone());
539 Ok(r)
540 }
541
542 #[cfg(feature = "histogram")]
553 pub fn configure_histogram(&self, name: &str, buckets: impl IntoIterator<Item = f64>) {
554 let buckets: Vec<f64> = buckets.into_iter().collect();
555 self.histogram_buckets
556 .write()
557 .unwrap_or_else(|e| e.into_inner())
558 .insert(name.to_string(), buckets);
559 }
560
561 #[cfg(feature = "histogram")]
567 pub fn get_or_create_histogram(&self, name: &str) -> Arc<Histogram> {
568 self.get_or_create_histogram_with(name, &LabelSet::EMPTY)
571 }
572
573 #[cfg(feature = "histogram")]
576 pub fn get_or_create_histogram_with(&self, name: &str, labels: &LabelSet) -> Arc<Histogram> {
577 match self.try_get_or_create_histogram_with(name, labels) {
578 Ok(h) => h,
579 Err(_) => histogram_overflow_sink().clone(),
580 }
581 }
582
583 #[cfg(feature = "histogram")]
586 pub fn try_get_or_create_histogram_with(
587 &self,
588 name: &str,
589 labels: &LabelSet,
590 ) -> Result<Arc<Histogram>> {
591 if let Ok(map) = self.histograms.read() {
592 if let Some(h) = map.get(&(name.to_string(), labels.clone())) {
593 return Ok(h.clone());
594 }
595 }
596 if !labels.is_empty() && !self.try_acquire_slot() {
599 return Err(MetricsError::CardinalityExceeded);
600 }
601 let mut map = self.histograms.write().unwrap_or_else(|e| e.into_inner());
602 let key = (name.to_string(), labels.clone());
603 if let Some(h) = map.get(&key) {
604 return Ok(h.clone());
605 }
606 let buckets = self
609 .histogram_buckets
610 .read()
611 .unwrap_or_else(|e| e.into_inner())
612 .get(name)
613 .cloned();
614 let h = Arc::new(match buckets {
615 Some(b) => Histogram::with_buckets(b),
616 None => Histogram::default_seconds(),
617 });
618 map.insert(key, h.clone());
619 Ok(h)
620 }
621
622 #[cfg(feature = "count")]
628 pub fn counter_names(&self) -> Vec<String> {
629 self.counters
630 .read()
631 .unwrap_or_else(|e| e.into_inner())
632 .keys()
633 .cloned()
634 .collect()
635 }
636
637 #[cfg(feature = "gauge")]
639 pub fn gauge_names(&self) -> Vec<String> {
640 self.gauges
641 .read()
642 .unwrap_or_else(|e| e.into_inner())
643 .keys()
644 .cloned()
645 .collect()
646 }
647
648 #[cfg(feature = "timer")]
650 pub fn timer_names(&self) -> Vec<String> {
651 self.timers
652 .read()
653 .unwrap_or_else(|e| e.into_inner())
654 .keys()
655 .cloned()
656 .collect()
657 }
658
659 #[cfg(feature = "meter")]
661 pub fn rate_meter_names(&self) -> Vec<String> {
662 self.rate_meters
663 .read()
664 .unwrap_or_else(|e| e.into_inner())
665 .keys()
666 .cloned()
667 .collect()
668 }
669
670 #[cfg(feature = "histogram")]
673 pub fn histogram_names(&self) -> Vec<String> {
674 let mut names: Vec<String> = self
675 .histograms
676 .read()
677 .unwrap_or_else(|e| e.into_inner())
678 .keys()
679 .map(|(n, _)| n.clone())
680 .collect();
681 names.sort();
682 names.dedup();
683 names
684 }
685
686 pub fn metric_count(&self) -> usize {
689 #[allow(unused_mut)]
690 let mut total = 0;
691 #[cfg(feature = "count")]
692 {
693 total += self
694 .counters
695 .read()
696 .unwrap_or_else(|e| e.into_inner())
697 .len();
698 total += self
699 .labeled_counters
700 .read()
701 .unwrap_or_else(|e| e.into_inner())
702 .len();
703 }
704 #[cfg(feature = "gauge")]
705 {
706 total += self.gauges.read().unwrap_or_else(|e| e.into_inner()).len();
707 total += self
708 .labeled_gauges
709 .read()
710 .unwrap_or_else(|e| e.into_inner())
711 .len();
712 }
713 #[cfg(feature = "timer")]
714 {
715 total += self.timers.read().unwrap_or_else(|e| e.into_inner()).len();
716 total += self
717 .labeled_timers
718 .read()
719 .unwrap_or_else(|e| e.into_inner())
720 .len();
721 }
722 #[cfg(feature = "meter")]
723 {
724 total += self
725 .rate_meters
726 .read()
727 .unwrap_or_else(|e| e.into_inner())
728 .len();
729 total += self
730 .labeled_rate_meters
731 .read()
732 .unwrap_or_else(|e| e.into_inner())
733 .len();
734 }
735 #[cfg(feature = "histogram")]
736 {
737 total += self
738 .histograms
739 .read()
740 .unwrap_or_else(|e| e.into_inner())
741 .len();
742 }
743 total
744 }
745
746 pub fn clear(&self) {
750 #[cfg(feature = "count")]
751 {
752 self.counters
753 .write()
754 .unwrap_or_else(|e| e.into_inner())
755 .clear();
756 self.labeled_counters
757 .write()
758 .unwrap_or_else(|e| e.into_inner())
759 .clear();
760 }
761 #[cfg(feature = "gauge")]
762 {
763 self.gauges
764 .write()
765 .unwrap_or_else(|e| e.into_inner())
766 .clear();
767 self.labeled_gauges
768 .write()
769 .unwrap_or_else(|e| e.into_inner())
770 .clear();
771 }
772 #[cfg(feature = "timer")]
773 {
774 self.timers
775 .write()
776 .unwrap_or_else(|e| e.into_inner())
777 .clear();
778 self.labeled_timers
779 .write()
780 .unwrap_or_else(|e| e.into_inner())
781 .clear();
782 }
783 #[cfg(feature = "meter")]
784 {
785 self.rate_meters
786 .write()
787 .unwrap_or_else(|e| e.into_inner())
788 .clear();
789 self.labeled_rate_meters
790 .write()
791 .unwrap_or_else(|e| e.into_inner())
792 .clear();
793 }
794 #[cfg(feature = "histogram")]
795 {
796 self.histograms
797 .write()
798 .unwrap_or_else(|e| e.into_inner())
799 .clear();
800 self.histogram_buckets
801 .write()
802 .unwrap_or_else(|e| e.into_inner())
803 .clear();
804 }
805 self.metadata
806 .write()
807 .unwrap_or_else(|e| e.into_inner())
808 .clear();
809 self.cardinality_count.store(0, Ordering::Relaxed);
810 }
812
813 #[cfg(feature = "count")]
820 pub fn counter_entries(&self) -> Vec<(String, LabelSet, Arc<Counter>)> {
821 let mut out = Vec::new();
822 for (name, c) in self
823 .counters
824 .read()
825 .unwrap_or_else(|e| e.into_inner())
826 .iter()
827 {
828 out.push((name.clone(), LabelSet::EMPTY, c.clone()));
829 }
830 for ((name, labels), c) in self
831 .labeled_counters
832 .read()
833 .unwrap_or_else(|e| e.into_inner())
834 .iter()
835 {
836 out.push((name.clone(), labels.clone(), c.clone()));
837 }
838 out
839 }
840
841 #[cfg(feature = "gauge")]
844 pub fn gauge_entries(&self) -> Vec<(String, LabelSet, Arc<Gauge>)> {
845 let mut out = Vec::new();
846 for (name, g) in self.gauges.read().unwrap_or_else(|e| e.into_inner()).iter() {
847 out.push((name.clone(), LabelSet::EMPTY, g.clone()));
848 }
849 for ((name, labels), g) in self
850 .labeled_gauges
851 .read()
852 .unwrap_or_else(|e| e.into_inner())
853 .iter()
854 {
855 out.push((name.clone(), labels.clone(), g.clone()));
856 }
857 out
858 }
859
860 #[cfg(feature = "timer")]
863 pub fn timer_entries(&self) -> Vec<(String, LabelSet, Arc<Timer>)> {
864 let mut out = Vec::new();
865 for (name, t) in self.timers.read().unwrap_or_else(|e| e.into_inner()).iter() {
866 out.push((name.clone(), LabelSet::EMPTY, t.clone()));
867 }
868 for ((name, labels), t) in self
869 .labeled_timers
870 .read()
871 .unwrap_or_else(|e| e.into_inner())
872 .iter()
873 {
874 out.push((name.clone(), labels.clone(), t.clone()));
875 }
876 out
877 }
878
879 #[cfg(feature = "meter")]
882 pub fn rate_meter_entries(&self) -> Vec<(String, LabelSet, Arc<RateMeter>)> {
883 let mut out = Vec::new();
884 for (name, r) in self
885 .rate_meters
886 .read()
887 .unwrap_or_else(|e| e.into_inner())
888 .iter()
889 {
890 out.push((name.clone(), LabelSet::EMPTY, r.clone()));
891 }
892 for ((name, labels), r) in self
893 .labeled_rate_meters
894 .read()
895 .unwrap_or_else(|e| e.into_inner())
896 .iter()
897 {
898 out.push((name.clone(), labels.clone(), r.clone()));
899 }
900 out
901 }
902
903 #[cfg(feature = "histogram")]
906 pub fn histogram_entries(&self) -> Vec<(String, LabelSet, Arc<Histogram>)> {
907 self.histograms
908 .read()
909 .unwrap_or_else(|e| e.into_inner())
910 .iter()
911 .map(|((n, l), h)| (n.clone(), l.clone(), h.clone()))
912 .collect()
913 }
914}
915
916impl Default for Registry {
917 fn default() -> Self {
918 Self::new()
919 }
920}
921
922#[cfg(feature = "count")]
932fn counter_overflow_sink() -> &'static Arc<Counter> {
933 static SINK: OnceLock<Arc<Counter>> = OnceLock::new();
934 SINK.get_or_init(|| Arc::new(Counter::new()))
935}
936
937#[cfg(feature = "gauge")]
938fn gauge_overflow_sink() -> &'static Arc<Gauge> {
939 static SINK: OnceLock<Arc<Gauge>> = OnceLock::new();
940 SINK.get_or_init(|| Arc::new(Gauge::new()))
941}
942
943#[cfg(feature = "timer")]
944fn timer_overflow_sink() -> &'static Arc<Timer> {
945 static SINK: OnceLock<Arc<Timer>> = OnceLock::new();
946 SINK.get_or_init(|| Arc::new(Timer::new()))
947}
948
949#[cfg(feature = "meter")]
950fn rate_meter_overflow_sink() -> &'static Arc<RateMeter> {
951 static SINK: OnceLock<Arc<RateMeter>> = OnceLock::new();
952 SINK.get_or_init(|| Arc::new(RateMeter::new()))
953}
954
955#[cfg(feature = "histogram")]
956fn histogram_overflow_sink() -> &'static Arc<Histogram> {
957 static SINK: OnceLock<Arc<Histogram>> = OnceLock::new();
958 SINK.get_or_init(|| Arc::new(Histogram::default_seconds()))
959}
960
961#[cfg(test)]
965#[cfg(all(feature = "count", feature = "gauge", feature = "timer"))]
966mod tests {
967 use super::*;
968 use std::sync::Arc;
969 use std::thread;
970
971 #[test]
972 fn test_counter_registration() {
973 let registry = Registry::new();
974 let c1 = registry.get_or_create_counter("requests");
975 let c2 = registry.get_or_create_counter("requests");
976 assert!(Arc::ptr_eq(&c1, &c2));
977 }
978
979 #[test]
980 fn test_gauge_registration() {
981 let registry = Registry::new();
982 let g1 = registry.get_or_create_gauge("cpu_usage");
983 let g2 = registry.get_or_create_gauge("cpu_usage");
984 assert!(Arc::ptr_eq(&g1, &g2));
985 }
986
987 #[test]
988 fn test_timer_registration() {
989 let registry = Registry::new();
990 let t1 = registry.get_or_create_timer("db_query");
991 let t2 = registry.get_or_create_timer("db_query");
992 assert!(Arc::ptr_eq(&t1, &t2));
993 }
994
995 #[test]
996 #[cfg(feature = "meter")]
997 fn test_rate_meter_registration() {
998 let registry = Registry::new();
999 let r1 = registry.get_or_create_rate_meter("api_calls");
1000 let r2 = registry.get_or_create_rate_meter("api_calls");
1001 assert!(Arc::ptr_eq(&r1, &r2));
1002 }
1003
1004 #[test]
1005 #[cfg(feature = "meter")]
1006 fn test_mixed_metrics() {
1007 let registry = Registry::new();
1008 let _ = registry.get_or_create_counter("a");
1009 let _ = registry.get_or_create_gauge("b");
1010 let _ = registry.get_or_create_timer("c");
1011 let _ = registry.get_or_create_rate_meter("d");
1012 assert_eq!(registry.metric_count(), 4);
1013 }
1014
1015 #[test]
1016 fn test_concurrent_access() {
1017 let registry = Arc::new(Registry::new());
1018 let mut handles = vec![];
1019 for _ in 0..10 {
1020 let r = registry.clone();
1021 handles.push(thread::spawn(move || {
1022 let c = r.get_or_create_counter("concurrent_test");
1023 c.inc();
1024 }));
1025 }
1026 for h in handles {
1027 h.join().unwrap();
1028 }
1029 assert_eq!(registry.get_or_create_counter("concurrent_test").get(), 10);
1030 }
1031
1032 #[test]
1033 fn test_clear() {
1034 let registry = Registry::new();
1035 let _ = registry.get_or_create_counter("a");
1036 let _ = registry.get_or_create_gauge("b");
1037 assert_eq!(registry.metric_count(), 2);
1038 registry.clear();
1039 assert_eq!(registry.metric_count(), 0);
1040 }
1041
1042 #[test]
1043 fn test_metric_names() {
1044 let registry = Registry::new();
1045 let _ = registry.get_or_create_counter("requests");
1046 let _ = registry.get_or_create_counter("errors");
1047 let _ = registry.get_or_create_gauge("cpu");
1048 assert_eq!(registry.counter_names().len(), 2);
1049 assert_eq!(registry.gauge_names().len(), 1);
1050 }
1051
1052 #[test]
1053 #[cfg(feature = "meter")]
1054 fn test_duplicate_names_across_types_are_independent() {
1055 let registry = Registry::new();
1056 let c = registry.get_or_create_counter("x");
1057 let g = registry.get_or_create_gauge("x");
1058 let t = registry.get_or_create_timer("x");
1059 let r = registry.get_or_create_rate_meter("x");
1060 let addrs = [
1061 Arc::as_ptr(&c) as usize,
1062 Arc::as_ptr(&g) as usize,
1063 Arc::as_ptr(&t) as usize,
1064 Arc::as_ptr(&r) as usize,
1065 ];
1066 for i in 0..addrs.len() {
1067 for j in (i + 1)..addrs.len() {
1068 assert_ne!(addrs[i], addrs[j]);
1069 }
1070 }
1071 }
1072
1073 #[test]
1074 fn test_clear_then_recreate_returns_new_instances() {
1075 let registry = Registry::new();
1076 let c_before = registry.get_or_create_counter("requests");
1077 registry.clear();
1078 let c_after = registry.get_or_create_counter("requests");
1079 assert!(!Arc::ptr_eq(&c_before, &c_after));
1080 }
1081
1082 #[test]
1083 fn test_concurrent_duplicate_registration_singleton_per_name() {
1084 let registry = Arc::new(Registry::new());
1085 let mut handles = vec![];
1086 for _ in 0..16 {
1087 let r = registry.clone();
1088 handles.push(thread::spawn(move || r.get_or_create_timer("dup")));
1089 }
1090 let first = registry.get_or_create_timer("dup");
1091 for h in handles {
1092 let t = h.join().unwrap();
1093 assert!(Arc::ptr_eq(&first, &t));
1094 }
1095 }
1096
1097 #[test]
1100 fn labeled_counter_distinct_from_unlabeled() {
1101 let r = Registry::new();
1102 let plain = r.get_or_create_counter("hits");
1103 let labels = LabelSet::from([("region", "us")]);
1104 let labeled = r.get_or_create_counter_with("hits", &labels);
1105 assert!(!Arc::ptr_eq(&plain, &labeled));
1106 plain.inc();
1107 labeled.add(5);
1108 assert_eq!(plain.get(), 1);
1109 assert_eq!(labeled.get(), 5);
1110 assert_eq!(r.cardinality_count(), 1);
1111 }
1112
1113 #[test]
1114 fn empty_labelset_routes_to_unlabeled_fast_path() {
1115 let r = Registry::new();
1116 let plain = r.get_or_create_counter("x");
1117 let same = r.get_or_create_counter_with("x", &LabelSet::EMPTY);
1118 assert!(Arc::ptr_eq(&plain, &same));
1119 assert_eq!(r.cardinality_count(), 0);
1120 }
1121
1122 #[test]
1123 fn cardinality_cap_routes_overflows_to_sink() {
1124 let r = Registry::new();
1125 r.set_cardinality_cap(2);
1126 let l1 = LabelSet::from([("k", "1")]);
1127 let l2 = LabelSet::from([("k", "2")]);
1128 let l3 = LabelSet::from([("k", "3")]);
1129 let _ = r.get_or_create_counter_with("c", &l1);
1130 let _ = r.get_or_create_counter_with("c", &l2);
1131 let over = r.get_or_create_counter_with("c", &l3);
1133 let sink = counter_overflow_sink();
1134 assert!(Arc::ptr_eq(&over, sink));
1135 assert_eq!(r.cardinality_count(), 2);
1136 assert!(r.cardinality_overflows() >= 1);
1137 }
1138
1139 #[test]
1140 fn try_cardinality_cap_returns_error() {
1141 let r = Registry::new();
1142 r.set_cardinality_cap(1);
1143 let _ = r
1144 .try_get_or_create_counter_with("c", &LabelSet::from([("k", "1")]))
1145 .unwrap();
1146 let err = r
1147 .try_get_or_create_counter_with("c", &LabelSet::from([("k", "2")]))
1148 .unwrap_err();
1149 assert_eq!(err, MetricsError::CardinalityExceeded);
1150 }
1151
1152 #[test]
1153 fn metadata_roundtrip() {
1154 let r = Registry::new();
1155 r.describe_counter("requests", "Total HTTP requests", Unit::Custom("requests"));
1156 let meta = r.metadata("requests").unwrap();
1157 assert_eq!(meta.kind, MetricKind::Counter);
1158 assert_eq!(meta.help.as_ref(), "Total HTTP requests");
1159 assert_eq!(meta.unit, Unit::Custom("requests"));
1160 }
1161
1162 #[test]
1163 #[cfg(feature = "histogram")]
1164 fn histogram_uses_configured_buckets() {
1165 let r = Registry::new();
1166 r.configure_histogram("latency", [0.1, 0.5, 1.0]);
1167 let h = r.get_or_create_histogram("latency");
1168 let snap = h.snapshot();
1170 assert_eq!(snap.buckets.len(), 4);
1171 }
1172
1173 #[test]
1176 fn labeled_gauge_distinct_from_unlabeled_and_caps() {
1177 let r = Registry::new();
1178 let plain = r.get_or_create_gauge("temp");
1179 let labels = LabelSet::from([("zone", "a")]);
1180 let labeled = r.get_or_create_gauge_with("temp", &labels);
1181 assert!(!Arc::ptr_eq(&plain, &labeled));
1182 plain.set(1.0);
1183 labeled.set(2.0);
1184 assert_eq!(plain.get(), 1.0);
1185 assert_eq!(labeled.get(), 2.0);
1186
1187 let same = r.get_or_create_gauge_with("temp", &LabelSet::EMPTY);
1189 assert!(Arc::ptr_eq(&plain, &same));
1190
1191 r.set_cardinality_cap(1);
1193 assert!(r
1194 .try_get_or_create_gauge_with("temp", &LabelSet::from([("zone", "b")]))
1195 .is_err());
1196 let _ = r.get_or_create_gauge_with("temp", &LabelSet::from([("zone", "c")]));
1198 assert!(r.cardinality_overflows() >= 1);
1199 }
1200
1201 #[test]
1202 fn labeled_timer_distinct_from_unlabeled_and_caps() {
1203 let r = Registry::new();
1204 let plain = r.get_or_create_timer("rpc");
1205 let labeled = r.get_or_create_timer_with("rpc", &LabelSet::from([("op", "send")]));
1206 assert!(!Arc::ptr_eq(&plain, &labeled));
1207 plain.record(std::time::Duration::from_micros(50));
1208 labeled.record(std::time::Duration::from_micros(100));
1209 assert_eq!(plain.count(), 1);
1210 assert_eq!(labeled.count(), 1);
1211 let same = r.get_or_create_timer_with("rpc", &LabelSet::EMPTY);
1212 assert!(Arc::ptr_eq(&plain, &same));
1213
1214 r.set_cardinality_cap(1);
1215 assert!(r
1216 .try_get_or_create_timer_with("rpc", &LabelSet::from([("op", "recv")]))
1217 .is_err());
1218 let _ = r.get_or_create_timer_with("rpc", &LabelSet::from([("op", "ack")]));
1219 assert!(r.cardinality_overflows() >= 1);
1220 }
1221
1222 #[test]
1223 #[cfg(feature = "meter")]
1224 fn labeled_rate_meter_distinct_from_unlabeled_and_caps() {
1225 let r = Registry::new();
1226 let plain = r.get_or_create_rate_meter("qps");
1227 let labeled = r.get_or_create_rate_meter_with("qps", &LabelSet::from([("tier", "1")]));
1228 assert!(!Arc::ptr_eq(&plain, &labeled));
1229 plain.tick_n(3);
1230 labeled.tick_n(7);
1231 assert_eq!(plain.total(), 3);
1232 assert_eq!(labeled.total(), 7);
1233 let same = r.get_or_create_rate_meter_with("qps", &LabelSet::EMPTY);
1234 assert!(Arc::ptr_eq(&plain, &same));
1235
1236 r.set_cardinality_cap(1);
1237 assert!(r
1238 .try_get_or_create_rate_meter_with("qps", &LabelSet::from([("tier", "2")]))
1239 .is_err());
1240 let _ = r.get_or_create_rate_meter_with("qps", &LabelSet::from([("tier", "3")]));
1241 assert!(r.cardinality_overflows() >= 1);
1242 }
1243
1244 #[test]
1245 #[cfg(feature = "histogram")]
1246 fn labeled_histogram_caps_and_observes() {
1247 let r = Registry::new();
1248 r.configure_histogram("latency", [0.01, 0.1, 1.0]);
1249 let h = r.get_or_create_histogram_with("latency", &LabelSet::from([("op", "a")]));
1250 h.observe(0.005);
1251 assert_eq!(h.count(), 1);
1252
1253 r.set_cardinality_cap(1);
1254 let h2 = r.get_or_create_histogram_with("latency", &LabelSet::from([("op", "a")]));
1256 assert!(Arc::ptr_eq(&h, &h2));
1257
1258 let err = r
1260 .try_get_or_create_histogram_with("latency", &LabelSet::from([("op", "b")]))
1261 .unwrap_err();
1262 assert_eq!(err, MetricsError::CardinalityExceeded);
1263 let _sink = r.get_or_create_histogram_with("latency", &LabelSet::from([("op", "c")]));
1264 assert!(r.cardinality_overflows() >= 1);
1265 }
1266
1267 #[test]
1268 fn describe_shorthands_cover_every_kind() {
1269 let r = Registry::new();
1270 r.describe_counter("c", "counter help", Unit::Custom("1"));
1271 r.describe_gauge("g", "gauge help", Unit::Bytes);
1272 r.describe_timer("t", "timer help", Unit::Seconds);
1273 #[cfg(feature = "meter")]
1274 r.describe_rate("rt", "rate help", Unit::Custom("ops"));
1275 #[cfg(feature = "histogram")]
1276 r.describe_histogram("h", "histogram help", Unit::Milliseconds);
1277
1278 assert_eq!(r.metadata("c").unwrap().kind, MetricKind::Counter);
1279 assert_eq!(r.metadata("g").unwrap().kind, MetricKind::Gauge);
1280 assert_eq!(r.metadata("t").unwrap().kind, MetricKind::Timer);
1281 #[cfg(feature = "meter")]
1282 assert_eq!(r.metadata("rt").unwrap().kind, MetricKind::Rate);
1283 #[cfg(feature = "histogram")]
1284 assert_eq!(r.metadata("h").unwrap().kind, MetricKind::Histogram);
1285
1286 r.describe_counter("c", "new help", Unit::None);
1288 assert_eq!(r.metadata("c").unwrap().help.as_ref(), "new help");
1289 }
1290
1291 #[test]
1292 fn snapshot_accessors_include_labeled_entries() {
1293 let r = Registry::new();
1294 r.get_or_create_counter("c1").inc();
1295 r.get_or_create_counter_with("c2", &LabelSet::from([("k", "v")]))
1296 .inc();
1297 r.get_or_create_gauge("g1").set(1.0);
1298 r.get_or_create_gauge_with("g2", &LabelSet::from([("k", "v")]))
1299 .set(2.0);
1300 r.get_or_create_timer("t1")
1301 .record(std::time::Duration::from_micros(1));
1302 r.get_or_create_timer_with("t2", &LabelSet::from([("k", "v")]))
1303 .record(std::time::Duration::from_micros(2));
1304
1305 assert_eq!(r.counter_entries().len(), 2);
1306 assert_eq!(r.gauge_entries().len(), 2);
1307 assert_eq!(r.timer_entries().len(), 2);
1308
1309 #[cfg(feature = "meter")]
1310 {
1311 r.get_or_create_rate_meter("r1").tick();
1312 r.get_or_create_rate_meter_with("r2", &LabelSet::from([("k", "v")]))
1313 .tick();
1314 assert_eq!(r.rate_meter_entries().len(), 2);
1315 }
1316
1317 #[cfg(feature = "histogram")]
1318 {
1319 r.get_or_create_histogram("h1").observe(0.1);
1320 r.get_or_create_histogram_with("h2", &LabelSet::from([("k", "v")]))
1321 .observe(0.1);
1322 assert_eq!(r.histogram_entries().len(), 2);
1323 let names = r.histogram_names();
1324 assert!(names.contains(&"h1".to_string()));
1325 assert!(names.contains(&"h2".to_string()));
1326 }
1327 }
1328
1329 #[test]
1330 fn cardinality_count_is_reset_by_clear_but_overflows_are_monotonic() {
1331 let r = Registry::new();
1332 r.set_cardinality_cap(2);
1333 let _ = r.get_or_create_counter_with("c", &LabelSet::from([("k", "1")]));
1334 let _ = r.get_or_create_counter_with("c", &LabelSet::from([("k", "2")]));
1335 let _ = r.get_or_create_counter_with("c", &LabelSet::from([("k", "3")])); assert_eq!(r.cardinality_count(), 2);
1337 assert!(r.cardinality_overflows() >= 1);
1338
1339 let prior_overflows = r.cardinality_overflows();
1340 r.clear();
1341 assert_eq!(r.cardinality_count(), 0);
1342 assert_eq!(r.cardinality_overflows(), prior_overflows);
1344 }
1345
1346 #[test]
1347 fn cap_settings_round_trip() {
1348 let r = Registry::new();
1349 let default_cap = r.cardinality_cap();
1350 assert_eq!(default_cap, DEFAULT_CARDINALITY_CAP);
1351 r.set_cardinality_cap(42);
1352 assert_eq!(r.cardinality_cap(), 42);
1353 let _ = r.get_or_create_counter_with("c", &LabelSet::from([("k", "v")]));
1355 r.set_cardinality_cap(0);
1356 assert_eq!(r.cardinality_cap(), 0);
1357 assert_eq!(r.cardinality_count(), 1);
1358 let _ = r.get_or_create_counter_with("c", &LabelSet::from([("k", "v2")]));
1360 assert!(r.cardinality_overflows() >= 1);
1361 }
1362}