1use std::{
31 collections::HashMap,
32 sync::{Arc, OnceLock, RwLock},
33};
34
35#[doc(hidden)]
39pub use prometheus;
40pub use prometheus::{
42 DEFAULT_BUCKETS, Encoder, Error, HistogramOpts, Opts, PROTOBUF_FORMAT, ProtobufEncoder, Result,
43 TextEncoder, exponential_buckets, gather, histogram_opts, linear_buckets, opts, proto,
44};
45use tracing::warn;
46
47pub mod core {
59 use std::mem::ManuallyDrop;
60
61 pub use prometheus::core::{
62 Atomic, AtomicF64, AtomicI64, AtomicU64, Collector, Desc, Describer, Metric,
63 MetricVecBuilder, Number,
64 };
65
66 macro_rules! impl_generic_metric_traits {
67 ($T:ident) => {
68 impl<P: Atomic> Clone for $T<P> {
69 fn clone(&self) -> Self {
70 Self(self.0.clone())
71 }
72 }
73
74 impl<P: Atomic> std::fmt::Debug for $T<P> {
75 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
76 write!(f, "{}", stringify!($T))?;
77 if self.0.is_none() {
78 write!(f, "(disabled)")?;
79 }
80 Ok(())
81 }
82 }
83
84 impl<P: Atomic> prometheus::core::Collector for $T<P> {
85 fn desc(&self) -> Vec<&Desc> {
86 self.0
87 .as_ref()
88 .map(|inner| inner.desc())
89 .unwrap_or_default()
90 }
91
92 fn collect(&self) -> Vec<prometheus::proto::MetricFamily> {
93 self.0
94 .as_ref()
95 .map(|inner| inner.collect())
96 .unwrap_or_default()
97 }
98 }
99 };
100 }
101
102 macro_rules! impl_metric_traits {
103 ($T:ident) => {
104 impl Clone for $T {
105 fn clone(&self) -> Self {
106 Self(self.0.clone())
107 }
108 }
109
110 impl std::fmt::Debug for $T {
111 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
112 write!(f, "{}", stringify!($T))?;
113 if self.0.is_none() {
114 write!(f, "(disabled)")?;
115 }
116 Ok(())
117 }
118 }
119
120 impl prometheus::core::Collector for $T {
121 fn desc(&self) -> Vec<&Desc> {
122 self.0
123 .as_ref()
124 .map(|inner| inner.desc())
125 .unwrap_or_default()
126 }
127
128 fn collect(&self) -> Vec<prometheus::proto::MetricFamily> {
129 self.0
130 .as_ref()
131 .map(|inner| inner.collect())
132 .unwrap_or_default()
133 }
134 }
135 };
136 }
137
138 macro_rules! impl_generic_metric_vec {
139 ($T:ident, $M:ident) => {
140 impl<P: Atomic> $T<P> {
141 pub fn new_some(inner: prometheus::core::$T<P>) -> Self {
142 Self(Some(inner))
143 }
144
145 pub fn new_none() -> Self {
146 Self(None)
147 }
148
149 #[inline]
150 pub fn with_label_values<V>(&self, vals: &[V]) -> $M<P>
151 where
152 V: AsRef<str> + std::fmt::Debug,
153 {
154 $M::<P>(self.0.as_ref().map(|inner| inner.with_label_values(vals)))
155 }
156
157 #[inline]
158 pub fn remove_label_values<V>(&self, vals: &[V]) -> super::Result<()>
159 where
160 V: AsRef<str> + std::fmt::Debug,
161 {
162 self.0
163 .as_ref()
164 .map(|inner| inner.remove_label_values(vals))
165 .unwrap_or(Ok(()))
166 }
167
168 #[inline]
169 pub fn get_metric_with<V, S: std::hash::BuildHasher>(
170 &self,
171 labels: &std::collections::HashMap<&str, V, S>,
172 ) -> super::Result<$M<P>>
173 where
174 V: AsRef<str> + std::fmt::Debug,
175 {
176 self.0
177 .as_ref()
178 .map(|inner| inner.get_metric_with(labels).map($M::<P>::new_some))
179 .unwrap_or(Ok($M::<P>::new_none()))
180 }
181
182 #[inline]
183 pub fn get_metric_with_label_values<V>(&self, vals: &[V]) -> super::Result<$M<P>>
184 where
185 V: AsRef<str> + std::fmt::Debug,
186 {
187 self.0
188 .as_ref()
189 .map(|inner| {
190 inner
191 .get_metric_with_label_values(vals)
192 .map($M::<P>::new_some)
193 })
194 .unwrap_or(Ok($M::<P>::new_none()))
195 }
196
197 #[inline]
198 pub fn reset(&self) {
199 if let Some(v) = &self.0 {
200 v.reset();
201 }
202 }
203 }
204 };
205 }
206
207 pub struct GenericCounter<P: Atomic>(Option<prometheus::core::GenericCounter<P>>);
208
209 impl<P: Atomic> GenericCounter<P> {
210 pub fn new_some(inner: prometheus::core::GenericCounter<P>) -> Self {
211 Self(Some(inner))
212 }
213
214 pub fn new_none() -> Self {
215 Self(None)
216 }
217
218 pub fn new(name: &str, help: &str) -> prometheus::Result<Self> {
219 prometheus::core::GenericCounter::new(name, help).map(Self::new_some)
220 }
221
222 pub fn with_opts(opts: super::Opts) -> super::Result<Self> {
223 prometheus::core::GenericCounter::with_opts(opts).map(Self::new_some)
224 }
225
226 #[inline]
227 pub fn get(&self) -> P::T {
228 self.0
229 .as_ref()
230 .map(|inner| inner.get())
231 .unwrap_or(<P::T>::from_i64(0))
232 }
233
234 #[inline]
235 pub fn inc(&self) {
236 if let Some(inner) = &self.0 {
237 inner.inc();
238 }
239 }
240
241 #[inline]
242 pub fn inc_by(&self, v: <P as Atomic>::T) {
243 if let Some(inner) = &self.0 {
244 inner.inc_by(v);
245 }
246 }
247
248 #[inline]
249 pub fn reset(&self) {
250 if let Some(inner) = &self.0 {
251 inner.reset();
252 }
253 }
254 }
255
256 impl_generic_metric_traits!(GenericCounter);
257
258 pub struct GenericGauge<P: Atomic>(Option<prometheus::core::GenericGauge<P>>);
259
260 impl<P: Atomic> GenericGauge<P> {
261 pub fn new_some(inner: prometheus::core::GenericGauge<P>) -> Self {
262 Self(Some(inner))
263 }
264
265 pub fn new_none() -> Self {
266 Self(None)
267 }
268
269 pub fn new(name: &str, help: &str) -> super::Result<Self> {
270 prometheus::core::GenericGauge::new(name, help).map(Self::new_some)
271 }
272
273 pub fn with_opts(opts: super::Opts) -> super::Result<Self> {
274 prometheus::core::GenericGauge::with_opts(opts).map(Self::new_some)
275 }
276
277 #[inline]
278 pub fn get(&self) -> P::T {
279 self.0
280 .as_ref()
281 .map(|inner| inner.get())
282 .unwrap_or(<P::T>::from_i64(0))
283 }
284
285 #[inline]
286 pub fn set(&self, v: P::T) {
287 if let Some(inner) = &self.0 {
288 inner.set(v);
289 }
290 }
291
292 #[inline]
293 pub fn inc(&self) {
294 if let Some(inner) = &self.0 {
295 inner.inc();
296 }
297 }
298
299 #[inline]
300 pub fn dec(&self) {
301 if let Some(inner) = &self.0 {
302 inner.dec();
303 }
304 }
305
306 #[inline]
307 pub fn add(&self, v: P::T) {
308 if let Some(inner) = &self.0 {
309 inner.add(v);
310 }
311 }
312
313 #[inline]
314 pub fn sub(&self, v: P::T) {
315 if let Some(inner) = &self.0 {
316 inner.sub(v);
317 }
318 }
319 }
320
321 impl_generic_metric_traits!(GenericGauge);
322
323 pub struct GenericCounterVec<P: Atomic>(Option<prometheus::core::GenericCounterVec<P>>);
324
325 impl_generic_metric_traits!(GenericCounterVec);
326 impl_generic_metric_vec!(GenericCounterVec, GenericCounter);
327
328 pub struct GenericGaugeVec<P: Atomic>(Option<prometheus::core::GenericGaugeVec<P>>);
329
330 impl_generic_metric_traits!(GenericGaugeVec);
331 impl_generic_metric_vec!(GenericGaugeVec, GenericGauge);
332
333 pub struct Histogram(Option<prometheus::Histogram>);
334
335 impl_metric_traits!(Histogram);
336
337 impl Histogram {
338 pub fn new_some(inner: prometheus::Histogram) -> Self {
339 Self(Some(inner))
340 }
341
342 pub fn new_none() -> Self {
343 Self(None)
344 }
345
346 pub fn with_opts(opts: prometheus::HistogramOpts) -> prometheus::Result<Self> {
347 prometheus::Histogram::with_opts(opts).map(|h| Self(Some(h)))
348 }
349
350 #[inline]
351 pub fn observe(&self, v: f64) {
352 if let Some(h) = &self.0 {
353 h.observe(v);
354 }
355 }
356
357 #[inline]
358 pub fn start_timer(&self) -> HistogramTimer {
359 HistogramTimer(self.0.as_ref().map(|h| h.start_timer()))
360 }
361
362 #[inline]
363 pub fn get_sample_count(&self) -> u64 {
364 self.0.as_ref().map_or(0, |h| h.get_sample_count())
365 }
366
367 #[inline]
368 pub fn get_sample_sum(&self) -> f64 {
369 self.0.as_ref().map_or(0.0, |h| h.get_sample_sum())
370 }
371 }
372
373 pub struct HistogramTimer(Option<prometheus::HistogramTimer>);
374
375 impl std::fmt::Debug for HistogramTimer {
376 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
377 write!(f, "HistogramTimer")?;
378 if self.0.is_none() {
379 write!(f, "(disabled)")?;
380 }
381 Ok(())
382 }
383 }
384
385 impl Drop for HistogramTimer {
386 fn drop(&mut self) {
387 drop(self.0.take());
389 }
390 }
391
392 impl HistogramTimer {
393 #[inline]
396 pub fn stop_and_record(self) -> f64 {
397 let mut wrapper = ManuallyDrop::new(self);
400 wrapper
401 .0
402 .take()
403 .map(|t| t.stop_and_record())
404 .unwrap_or_default()
405 }
406
407 #[inline]
410 pub fn observe_duration(self) {
411 let _ = self.stop_and_record();
412 }
413
414 #[inline]
416 pub fn stop_and_discard(self) -> f64 {
417 let mut wrapper = ManuallyDrop::new(self);
418 wrapper
419 .0
420 .take()
421 .map(|t| t.stop_and_discard())
422 .unwrap_or_default()
423 }
424 }
425
426 pub struct HistogramVec(Option<prometheus::HistogramVec>);
427
428 impl_metric_traits!(HistogramVec);
429
430 impl HistogramVec {
431 pub fn new_some(inner: prometheus::HistogramVec) -> Self {
432 Self(Some(inner))
433 }
434
435 pub fn new_none() -> Self {
436 Self(None)
437 }
438
439 #[inline]
440 pub fn with_label_values(&self, vals: &[&str]) -> Histogram {
441 Histogram(self.0.as_ref().map(|v| v.with_label_values(vals)))
442 }
443
444 #[inline]
445 pub fn remove_label_values(&self, vals: &[&str]) -> prometheus::Result<()> {
446 match &self.0 {
447 Some(v) => v.remove_label_values(vals),
448 None => Ok(()),
449 }
450 }
451 }
452}
453
454pub type Counter = core::GenericCounter<prometheus::core::AtomicF64>;
455pub type IntCounter = core::GenericCounter<prometheus::core::AtomicU64>;
456pub type Gauge = core::GenericGauge<prometheus::core::AtomicF64>;
457pub type IntGauge = core::GenericGauge<prometheus::core::AtomicI64>;
458
459pub type CounterVec = core::GenericCounterVec<prometheus::core::AtomicF64>;
460pub type IntCounterVec = core::GenericCounterVec<prometheus::core::AtomicU64>;
461pub type GaugeVec = core::GenericGaugeVec<prometheus::core::AtomicF64>;
462pub type IntGaugeVec = core::GenericGaugeVec<prometheus::core::AtomicI64>;
463
464pub use core::{Histogram, HistogramTimer, HistogramVec};
465
466#[derive(Debug, Default, Clone, Copy, PartialEq, Eq, serde::Deserialize, serde::Serialize)]
472#[serde(rename_all = "lowercase")]
473pub enum MetricLevel {
474 Off,
477 Warn,
478 Info,
479 #[default]
481 Debug,
482 Trace,
483}
484
485impl MetricLevel {
486 pub(crate) const fn verbosity(self) -> u8 {
487 match self {
488 Self::Off => 0,
489 Self::Warn => 1,
490 Self::Info => 2,
491 Self::Debug => 3,
492 Self::Trace => 4,
493 }
494 }
495}
496
497const DEFAULT_THRESHOLD: u8 = MetricLevel::Trace.verbosity();
500
501#[derive(Clone)]
502struct FilterDirective {
503 pattern: String,
505 threshold: u8,
508}
509
510#[derive(Default)]
515pub struct Filter {
516 directives: Vec<FilterDirective>,
517}
518
519fn parse_directive(part: &str) -> Option<FilterDirective> {
522 let part = part.trim();
523 if part.is_empty() {
524 return None;
525 }
526 let (pattern, value) = if let Some(eq) = part.rfind('=') {
527 (part[..eq].trim().to_owned(), part[eq + 1..].trim())
528 } else {
529 (String::new(), part)
530 };
531 let threshold = match value {
532 "off" => 0,
533 "warn" => 1,
534 "info" => 2,
535 "debug" => 3,
536 "trace" => 4,
537 other => {
538 warn!(
539 "dropping prometheus filter directive {part:?}: invalid level {other:?}, \
540 expected one of off/warn/info/debug/trace"
541 );
542 return None;
543 }
544 };
545 Some(FilterDirective { pattern, threshold })
546}
547
548fn threshold_for(directives: &[FilterDirective], name: &str, module: &str) -> u8 {
557 let mut threshold = DEFAULT_THRESHOLD;
558 for dir in directives {
559 if dir.pattern.is_empty()
560 || name.starts_with(dir.pattern.as_str())
561 || module.starts_with(dir.pattern.as_str())
562 || module.contains(&format!("::{}", dir.pattern))
563 {
564 threshold = dir.threshold;
565 }
566 }
567 threshold
568}
569
570impl Filter {
571 pub fn parse(s: &str) -> Self {
574 let directives = s.split(',').filter_map(parse_directive).collect();
575 Self { directives }
576 }
577
578 #[inline]
581 pub fn is_exposed(&self, name: &str, module: &str, level: MetricLevel) -> bool {
582 threshold_for(&self.directives, name, module) >= level.verbosity()
583 }
584
585 pub fn resolve(fallback: Option<&str>) -> Self {
589 let env = std::env::var("METRICS_FILTER").ok();
590 match (fallback, env.as_deref()) {
591 (Some(f), Some(e)) => Self::parse(&format!("{f},{e}")),
592 (Some(f), None) => Self::parse(f),
593 (None, Some(e)) => Self::parse(e),
594 (None, None) => Self::default(),
595 }
596 }
597}
598
599#[derive(Clone)]
611pub struct Registry {
612 inner: prometheus::Registry,
613 filter: Arc<Filter>,
614 prefix: Option<String>,
617 registered: Arc<RwLock<HashMap<String, (String, MetricLevel)>>>,
620}
621
622impl Registry {
623 pub fn new() -> Self {
626 Self {
627 inner: prometheus::Registry::new(),
628 filter: Arc::new(Filter::resolve(None)),
629 prefix: None,
630 registered: Arc::new(RwLock::new(HashMap::new())),
631 }
632 }
633
634 pub fn new_custom(
636 prefix: Option<String>,
637 labels: Option<std::collections::HashMap<String, String>>,
638 filter: Option<Arc<Filter>>,
639 ) -> prometheus::Result<Self> {
640 Ok(Self {
641 inner: prometheus::Registry::new_custom(prefix.clone(), labels)?,
642 filter: filter.unwrap_or_else(|| Arc::new(Filter::resolve(None))),
643 prefix,
644 registered: Arc::new(RwLock::new(HashMap::new())),
645 })
646 }
647
648 #[inline]
651 pub fn filter(&self) -> Arc<Filter> {
652 self.filter.clone()
653 }
654
655 #[inline]
659 pub fn record(&self, name: &str, module: &str, level: MetricLevel) {
660 let exposed_name = match &self.prefix {
661 Some(prefix) => format!("{prefix}_{name}"),
662 None => name.to_owned(),
663 };
664 self.registered
665 .write()
666 .unwrap()
667 .insert(exposed_name, (module.to_owned(), level));
668 }
669
670 #[inline]
673 pub fn inner(&self) -> &prometheus::Registry {
674 &self.inner
675 }
676
677 pub fn register(&self, c: Box<dyn prometheus::core::Collector>) -> prometheus::Result<()> {
678 self.inner.register(c)
679 }
680
681 pub fn unregister(&self, c: Box<dyn prometheus::core::Collector>) -> prometheus::Result<()> {
682 self.inner.unregister(c)
683 }
684
685 pub fn gather(&self) -> Vec<prometheus::proto::MetricFamily> {
689 let registered = self.registered.read().unwrap();
690 self.inner
691 .gather()
692 .into_iter()
693 .filter(|family| {
694 registered.get(family.name()).is_none_or(|(module, level)| {
695 self.filter.is_exposed(family.name(), module, *level)
696 })
697 })
698 .collect()
699 }
700}
701
702impl Default for Registry {
703 fn default() -> Self {
704 Self::new()
705 }
706}
707
708impl std::fmt::Debug for Registry {
709 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
710 f.debug_struct("Registry").finish_non_exhaustive()
711 }
712}
713
714fn default_filter() -> &'static Arc<Filter> {
717 static INSTANCE: OnceLock<Arc<Filter>> = OnceLock::new();
718 INSTANCE.get_or_init(|| Arc::new(Filter::resolve(None)))
719}
720
721pub fn default_registry() -> &'static Registry {
725 use std::sync::OnceLock;
726 static INSTANCE: OnceLock<Registry> = OnceLock::new();
727 INSTANCE.get_or_init(|| Registry {
728 inner: prometheus::default_registry().clone(),
729 filter: default_filter().clone(),
730 prefix: None,
731 registered: Arc::new(RwLock::new(HashMap::new())),
732 })
733}
734
735#[macro_export]
754macro_rules! register_int_counter_with_registry {
755 ($name:expr, $help:expr, $registry:expr $(,)?) => {
756 $crate::register_int_counter_with_registry!(
757 $name, $help, $registry; $crate::MetricLevel::Debug
758 )
759 };
760 ($name:expr, $help:expr, $registry:expr ; $level:expr $(,)?) => {{
761 let _n = $name;
762 let name: &str = &*_n;
763 let module: &str = module_path!();
764 ($registry).record(name, module, $level);
765 $crate::prometheus::register_int_counter_with_registry!(
766 name,
767 $help,
768 ($registry).inner()
769 )
770 .map($crate::core::GenericCounter::new_some)
771 }};
772}
773
774#[macro_export]
776macro_rules! register_int_counter_vec_with_registry {
777 ($name:expr, $help:expr, $labels:expr, $registry:expr $(,)?) => {
778 $crate::register_int_counter_vec_with_registry!(
779 $name, $help, $labels, $registry; $crate::MetricLevel::Debug
780 )
781 };
782 ($name:expr, $help:expr, $labels:expr, $registry:expr ; $level:expr $(,)?) => {{
783 let _n = $name;
784 let name: &str = &*_n;
785 let module: &str = module_path!();
786 ($registry).record(name, module, $level);
787 $crate::prometheus::register_int_counter_vec_with_registry!(
788 name,
789 $help,
790 $labels,
791 ($registry).inner()
792 )
793 .map($crate::IntCounterVec::new_some)
794 }};
795}
796
797#[macro_export]
799macro_rules! register_int_gauge_with_registry {
800 ($name:expr, $help:expr, $registry:expr $(,)?) => {
801 $crate::register_int_gauge_with_registry!(
802 $name, $help, $registry; $crate::MetricLevel::Debug
803 )
804 };
805 ($name:expr, $help:expr, $registry:expr ; $level:expr $(,)?) => {{
806 let _n = $name;
807 let name: &str = &*_n;
808 let module: &str = module_path!();
809 ($registry).record(name, module, $level);
810 $crate::prometheus::register_int_gauge_with_registry!(name, $help, ($registry).inner())
811 .map($crate::core::GenericGauge::new_some)
812 }};
813}
814
815#[macro_export]
817macro_rules! register_int_gauge_vec_with_registry {
818 ($name:expr, $help:expr, $labels:expr, $registry:expr $(,)?) => {
819 $crate::register_int_gauge_vec_with_registry!(
820 $name, $help, $labels, $registry; $crate::MetricLevel::Debug
821 )
822 };
823 ($name:expr, $help:expr, $labels:expr, $registry:expr ; $level:expr $(,)?) => {{
824 let _n = $name;
825 let name: &str = &*_n;
826 let module: &str = module_path!();
827 ($registry).record(name, module, $level);
828 $crate::prometheus::register_int_gauge_vec_with_registry!(
829 name,
830 $help,
831 $labels,
832 ($registry).inner()
833 )
834 .map($crate::IntGaugeVec::new_some)
835 }};
836}
837
838#[macro_export]
841macro_rules! register_histogram_with_registry {
842 ($name:expr, $help:expr, $registry:expr $(,)?) => {
843 $crate::register_histogram_with_registry!($name, $help, $registry; $crate::MetricLevel::Debug)
844 };
845 ($name:expr, $help:expr, $buckets:expr, $registry:expr $(,)?) => {
846 $crate::register_histogram_with_registry!(
847 $name, $help, $buckets, $registry; $crate::MetricLevel::Debug
848 )
849 };
850 ($name:expr, $help:expr, $registry:expr ; $level:expr $(,)?) => {{
851 let _n = $name;
852 let name: &str = &*_n;
853 let module: &str = module_path!();
854 ($registry).record(name, module, $level);
855 $crate::prometheus::register_histogram_with_registry!(name, $help, ($registry).inner())
856 .map($crate::Histogram::new_some)
857 }};
858 ($name:expr, $help:expr, $buckets:expr, $registry:expr ; $level:expr $(,)?) => {{
859 let _n = $name;
860 let name: &str = &*_n;
861 let module: &str = module_path!();
862 ($registry).record(name, module, $level);
863 $crate::prometheus::register_histogram_with_registry!(
864 name,
865 $help,
866 $buckets,
867 ($registry).inner()
868 )
869 .map($crate::Histogram::new_some)
870 }};
871}
872
873#[macro_export]
876macro_rules! register_histogram_vec_with_registry {
877 ($name:expr, $help:expr, $labels:expr, $registry:expr $(,)?) => {
878 $crate::register_histogram_vec_with_registry!(
879 $name, $help, $labels, $registry; $crate::MetricLevel::Debug
880 )
881 };
882 ($name:expr, $help:expr, $labels:expr, $buckets:expr, $registry:expr $(,)?) => {
883 $crate::register_histogram_vec_with_registry!(
884 $name, $help, $labels, $buckets, $registry; $crate::MetricLevel::Debug
885 )
886 };
887 ($name:expr, $help:expr, $labels:expr, $registry:expr ; $level:expr $(,)?) => {{
888 let _n = $name;
889 let name: &str = &*_n;
890 let module: &str = module_path!();
891 ($registry).record(name, module, $level);
892 $crate::prometheus::register_histogram_vec_with_registry!(
893 name,
894 $help,
895 $labels,
896 ($registry).inner()
897 )
898 .map($crate::HistogramVec::new_some)
899 }};
900 ($name:expr, $help:expr, $labels:expr, $buckets:expr, $registry:expr ; $level:expr $(,)?) => {{
901 let _n = $name;
902 let name: &str = &*_n;
903 let module: &str = module_path!();
904 ($registry).record(name, module, $level);
905 $crate::prometheus::register_histogram_vec_with_registry!(
906 name,
907 $help,
908 $labels,
909 $buckets,
910 ($registry).inner()
911 )
912 .map($crate::HistogramVec::new_some)
913 }};
914}
915
916#[macro_export]
918macro_rules! register_gauge_vec_with_registry {
919 ($name:expr, $help:expr, $labels:expr, $registry:expr $(,)?) => {
920 $crate::register_gauge_vec_with_registry!(
921 $name, $help, $labels, $registry; $crate::MetricLevel::Debug
922 )
923 };
924 ($name:expr, $help:expr, $labels:expr, $registry:expr ; $level:expr $(,)?) => {{
925 let _n = $name;
926 let name: &str = &*_n;
927 let module: &str = module_path!();
928 ($registry).record(name, module, $level);
929 $crate::prometheus::register_gauge_vec_with_registry!(
930 name,
931 $help,
932 $labels,
933 ($registry).inner()
934 )
935 .map($crate::core::GenericGaugeVec::new_some)
936 }};
937}
938
939#[macro_export]
941macro_rules! register_gauge_with_registry {
942 ($name:expr, $help:expr, $registry:expr $(,)?) => {
943 $crate::register_gauge_with_registry!($name, $help, $registry; $crate::MetricLevel::Debug)
944 };
945 ($name:expr, $help:expr, $registry:expr ; $level:expr $(,)?) => {{
946 let _n = $name;
947 let name: &str = &*_n;
948 let module: &str = module_path!();
949 ($registry).record(name, module, $level);
950 $crate::prometheus::register_gauge_with_registry!(name, $help, ($registry).inner())
951 .map($crate::core::GenericGauge::new_some)
952 }};
953}
954
955#[macro_export]
957macro_rules! register_counter {
958 ($name:expr, $help:expr $(,)?) => {
959 $crate::register_counter!($name, $help; $crate::MetricLevel::Debug)
960 };
961 ($name:expr, $help:expr ; $level:expr $(,)?) => {{
962 let _n = $name;
963 let name: &str = &*_n;
964 let module: &str = module_path!();
965 $crate::default_registry().record(name, module, $level);
966 $crate::prometheus::register_counter!(name, $help)
967 .map($crate::core::GenericCounter::new_some)
968 }};
969}
970
971#[macro_export]
973macro_rules! register_counter_vec_with_registry {
974 ($name:expr, $help:expr, $labels:expr, $registry:expr $(,)?) => {{
975 let _n = $name;
976 let name: &str = &*_n;
977 let module: &str = module_path!();
978 ($registry).record(name, module, $crate::MetricLevel::Debug);
979 $crate::prometheus::register_counter_vec_with_registry!(
980 name,
981 $help,
982 $labels,
983 ($registry).inner()
984 )
985 .map($crate::core::GenericCounterVec::new_some)
986 }};
987}
988
989#[macro_export]
991macro_rules! register_counter_vec {
992 ($name:expr, $help:expr, $labels:expr $(,)?) => {
993 $crate::register_counter_vec!($name, $help, $labels; $crate::MetricLevel::Debug)
994 };
995 ($name:expr, $help:expr, $labels:expr ; $level:expr $(,)?) => {{
996 let _n = $name;
997 let name: &str = &*_n;
998 let module: &str = module_path!();
999 $crate::default_registry().record(name, module, $level);
1000 $crate::prometheus::register_counter_vec!(name, $help, $labels)
1001 .map($crate::core::GenericCounterVec::new_some)
1002 }};
1003}
1004
1005#[macro_export]
1008macro_rules! register_histogram_vec {
1009 ($opts:expr, $labels:expr $(,)?) => {
1010 $crate::register_histogram_vec!($opts, $labels; $crate::MetricLevel::Debug)
1011 };
1012 ($name:expr, $help:expr, $labels:expr $(,)?) => {
1013 $crate::register_histogram_vec!($name, $help, $labels; $crate::MetricLevel::Debug)
1014 };
1015 ($name:expr, $help:expr, $labels:expr, $buckets:expr $(,)?) => {
1016 $crate::register_histogram_vec!(
1017 $name, $help, $labels, $buckets; $crate::MetricLevel::Debug
1018 )
1019 };
1020 ($opts:expr, $labels:expr ; $level:expr $(,)?) => {{
1021 let opts = $opts;
1022 let name: &str = &opts.common_opts.name;
1023 let module: &str = module_path!();
1024 $crate::default_registry().record(name, module, $level);
1025 $crate::prometheus::register_histogram_vec!(opts, $labels)
1026 .map($crate::HistogramVec::new_some)
1027 }};
1028 ($name:expr, $help:expr, $labels:expr ; $level:expr $(,)?) => {{
1029 let _n = $name;
1030 let name: &str = &*_n;
1031 let module: &str = module_path!();
1032 $crate::default_registry().record(name, module, $level);
1033 $crate::prometheus::register_histogram_vec!(name, $help, $labels)
1034 .map($crate::HistogramVec::new_some)
1035 }};
1036 ($name:expr, $help:expr, $labels:expr, $buckets:expr ; $level:expr $(,)?) => {{
1037 let _n = $name;
1038 let name: &str = &*_n;
1039 let module: &str = module_path!();
1040 $crate::default_registry().record(name, module, $level);
1041 $crate::prometheus::register_histogram_vec!(name, $help, $labels, $buckets)
1042 .map($crate::HistogramVec::new_some)
1043 }};
1044}
1045
1046#[cfg(test)]
1047mod tests {
1048 use super::MetricLevel::Debug;
1049
1050 #[test]
1051 fn filter_matches_metric_or_module_name_prefix() {
1052 let filter = super::Filter::parse("authority=off");
1055 assert!(filter.is_exposed("some_authority", "iota_core::checkpoints", Debug));
1056 assert!(!filter.is_exposed("authority", "iota_core::checkpoints", Debug));
1057 assert!(!filter.is_exposed("authority_aggregator", "iota_core::checkpoints", Debug));
1058 assert!(filter.is_exposed("certs_total", "iota_core::some_authority", Debug));
1059 assert!(!filter.is_exposed("certs_total", "iota_core::authority", Debug));
1060 assert!(!filter.is_exposed("certs_total", "iota_core::authority_aggregator", Debug));
1061
1062 let filter = super::Filter::parse("authority=off,authority_aggregator=trace");
1064 assert!(!filter.is_exposed("authority", "iota_core::checkpoints", Debug));
1065 assert!(filter.is_exposed("authority_aggregator", "iota_core::checkpoints", Debug));
1066 assert!(!filter.is_exposed("certs_total", "iota_core::authority", Debug));
1067 assert!(filter.is_exposed("certs_total", "iota_core::authority_aggregator", Debug));
1068
1069 let filter = super::Filter::parse("off,authority_aggregator=trace");
1071 assert!(!filter.is_exposed("some_authority", "iota_core::checkpoints", Debug));
1072 assert!(!filter.is_exposed("authority", "iota_core::checkpoints", Debug));
1073 assert!(filter.is_exposed("authority_aggregator", "iota_core::checkpoints", Debug));
1074 assert!(!filter.is_exposed("certs_total", "iota_core::some_authority", Debug));
1075 assert!(!filter.is_exposed("certs_total", "iota_core::authority", Debug));
1076 assert!(filter.is_exposed("certs_total", "iota_core::authority_aggregator", Debug));
1077
1078 let filter = super::Filter::parse("authority_aggregator=off");
1080 assert!(filter.is_exposed("authority", "iota_core::checkpoints", Debug));
1081 assert!(!filter.is_exposed("authority_aggregator", "iota_core::checkpoints", Debug));
1082 assert!(filter.is_exposed("certs_total", "iota_core::authority", Debug));
1083 assert!(!filter.is_exposed("certs_total", "iota_core::authority_aggregator", Debug));
1084 }
1085
1086 #[test]
1087 fn unmatched_metrics_are_exposed() {
1088 use super::MetricLevel::{Info, Trace, Warn};
1089 for filter in [
1092 super::Filter::parse(""),
1093 super::Filter::default(),
1094 super::Filter::parse(",,"),
1096 ] {
1097 assert!(filter.is_exposed("anything", "any::module", Warn));
1098 assert!(filter.is_exposed("anything", "any::module", Info));
1099 assert!(filter.is_exposed("anything", "any::module", Debug));
1100 assert!(filter.is_exposed("anything", "any::module", Trace));
1101 }
1102 }
1103
1104 #[test]
1105 fn rejects_boolean_and_numeric_aliases() {
1106 use super::MetricLevel::Trace;
1107 for alias in ["on", "true", "1", "false", "0"] {
1111 let filter = super::Filter::parse(&format!("authority={alias}"));
1112 assert!(
1113 filter.is_exposed("authority", "m", Trace),
1114 "{alias} should be dropped as invalid, leaving the default"
1115 );
1116 }
1117 assert!(
1119 !super::Filter::parse("authority=off").is_exposed("authority", "m", Debug),
1120 "off should disable"
1121 );
1122 }
1123
1124 #[test]
1125 fn invalid_directives_are_dropped() {
1126 use super::MetricLevel::Trace;
1127 assert!(super::Filter::parse("authority=maybe").is_exposed("authority", "m", Trace));
1130 assert!(super::Filter::parse("authority").is_exposed("authority", "m", Trace));
1133 let filter = super::Filter::parse("authority=off,bogus=nope");
1135 assert!(!filter.is_exposed("authority", "m", Debug));
1136 }
1137
1138 #[test]
1139 fn matches_module_path_prefix() {
1140 let filter = super::Filter::parse("iota_core=off");
1143 assert!(!filter.is_exposed("certs_total", "iota_core::authority", Debug));
1144 assert!(filter.is_exposed("certs_total", "starfish::core", Debug));
1145 }
1146
1147 #[test]
1148 fn global_trace_default() {
1149 assert!(super::Filter::parse("off,trace").is_exposed("authority", "m", Debug));
1151 let filter = super::Filter::parse("trace,authority=off");
1153 assert!(filter.is_exposed("certs_total", "m", Debug));
1154 assert!(!filter.is_exposed("authority", "m", Debug));
1155 }
1156
1157 #[test]
1158 fn whitespace_is_trimmed() {
1159 let filter = super::Filter::parse(" authority = off , authority_aggregator = trace ");
1160 assert!(!filter.is_exposed("authority", "m", Debug));
1161 assert!(filter.is_exposed("authority_aggregator", "m", Debug));
1162 }
1163
1164 #[test]
1165 fn resolve_applies_fallback() {
1166 use super::{Arc, Filter, MetricLevel, Registry};
1167
1168 if std::env::var_os("METRICS_FILTER").is_some() {
1171 return;
1172 }
1173
1174 assert!(Filter::resolve(None).is_exposed("anything", "m", MetricLevel::Trace));
1176 assert!(Filter::resolve(None).is_exposed("anything", "m", Debug));
1177
1178 let filter = Arc::new(Filter::resolve(Some("off,authority=trace")));
1180 assert!(filter.is_exposed("authority", "m", MetricLevel::Debug));
1181 assert!(!filter.is_exposed("consensus", "m", MetricLevel::Debug));
1182
1183 let registry = Registry::new_custom(None, None, Some(filter.clone())).unwrap();
1185 let shared = Registry::new_custom(None, None, Some(filter)).unwrap();
1186 assert!(std::sync::Arc::ptr_eq(®istry.filter(), &shared.filter()));
1187 }
1188
1189 #[test]
1190 fn level_thresholds() {
1191 use super::MetricLevel::{Debug, Info, Trace, Warn};
1192 let f = super::Filter::parse("authority=warn");
1194 assert!(f.is_exposed("x", "iota_core::authority", Warn));
1195 assert!(!f.is_exposed("x", "iota_core::authority", Info));
1196 assert!(!f.is_exposed("x", "iota_core::authority", Debug));
1197 let f = super::Filter::parse("authority=info");
1199 assert!(f.is_exposed("x", "iota_core::authority", Warn));
1200 assert!(f.is_exposed("x", "iota_core::authority", Info));
1201 assert!(!f.is_exposed("x", "iota_core::authority", Debug));
1202 let f = super::Filter::parse("authority=debug");
1204 assert!(f.is_exposed("x", "iota_core::authority", Debug));
1205 assert!(!f.is_exposed("x", "iota_core::authority", Trace));
1206 let f = super::Filter::parse("authority=trace");
1208 assert!(f.is_exposed("x", "iota_core::authority", Trace));
1209 assert!(!super::Filter::parse("authority=off").is_exposed(
1211 "x",
1212 "iota_core::authority",
1213 Warn
1214 ));
1215 assert!(super::Filter::parse("").is_exposed("x", "m", Info));
1217 assert!(super::Filter::parse("").is_exposed("x", "m", Trace));
1218 }
1219}
1220
1221#[cfg(test)]
1222mod gather_filter_tests {
1223 use super::{Filter, MetricLevel, Registry};
1224
1225 fn registry(filter: &str) -> Registry {
1226 Registry::new_custom(None, None, Some(std::sync::Arc::new(Filter::parse(filter)))).unwrap()
1227 }
1228
1229 fn gathered_names(registry: &Registry) -> Vec<String> {
1230 let mut names: Vec<_> = registry
1231 .gather()
1232 .iter()
1233 .map(|f| f.name().to_owned())
1234 .collect();
1235 names.sort();
1236 names
1237 }
1238
1239 #[test]
1240 fn gather_applies_level_thresholds_by_module() {
1241 let reg = registry("gather_filter_tests=warn");
1243 crate::register_int_gauge_with_registry!("g_warn", "h", ® MetricLevel::Warn).unwrap();
1244 let g_debug = crate::register_int_gauge_with_registry!("g_debug", "h", ®).unwrap();
1245 g_debug.set(7);
1246
1247 assert_eq!(gathered_names(®), ["g_warn"]);
1250 assert_eq!(g_debug.get(), 7);
1251 }
1252
1253 #[test]
1254 fn off_directive_hides_but_still_registers() {
1255 let reg = registry("g_hidden=off");
1256 let g = crate::register_int_gauge_with_registry!("g_hidden", "h", ®).unwrap();
1257 assert_eq!(format!("{g:?}"), "GenericGauge");
1260 g.set(9);
1261 assert_eq!(g.get(), 9);
1262 assert_eq!(gathered_names(®), Vec::<String>::new());
1263 }
1264
1265 #[test]
1266 fn prefixed_registry_records_exposed_family_names() {
1267 let exposed = Registry::new_custom(
1268 Some("consensus".to_owned()),
1269 None,
1270 Some(std::sync::Arc::new(Filter::parse(""))),
1271 )
1272 .unwrap();
1273 crate::register_int_gauge_with_registry!("g", "h", &exposed; MetricLevel::Warn).unwrap();
1274 assert_eq!(gathered_names(&exposed), ["consensus_g"]);
1275
1276 let hidden = Registry::new_custom(
1279 Some("consensus".to_owned()),
1280 None,
1281 Some(std::sync::Arc::new(Filter::parse(
1282 "gather_filter_tests=off",
1283 ))),
1284 )
1285 .unwrap();
1286 crate::register_int_gauge_with_registry!("g", "h", &hidden; MetricLevel::Warn).unwrap();
1287 assert_eq!(gathered_names(&hidden), Vec::<String>::new());
1288 }
1289
1290 #[test]
1291 fn directly_registered_collectors_bypass_filter() {
1292 let reg = registry("off");
1293 crate::register_int_gauge_with_registry!("g_macro", "h", ®).unwrap();
1294 let gauge = prometheus::IntGauge::new("g_direct", "h").unwrap();
1295 reg.register(Box::new(gauge)).unwrap();
1296
1297 assert_eq!(gathered_names(®), ["g_direct"]);
1300 }
1301}
1302
1303#[cfg(test)]
1304mod level_macro_tests {
1305 use super::{IntGauge, MetricLevel, Registry};
1306
1307 fn registry(filter: &str) -> Registry {
1308 Registry::new_custom(
1309 None,
1310 None,
1311 Some(std::sync::Arc::new(super::Filter::parse(filter))),
1312 )
1313 .unwrap()
1314 }
1315
1316 #[test]
1317 fn hidden_metrics_still_register_and_collect() {
1318 let reg = registry("g_default=warn");
1321 let g: IntGauge = crate::register_int_gauge_with_registry!("g_default", "h", ®).unwrap();
1322 assert_eq!(format!("{g:?}"), "GenericGauge");
1325 g.set(42);
1326 assert_eq!(g.get(), 42);
1327 assert!(reg.gather().is_empty());
1328
1329 let reg = registry("g_off=off");
1331 let g: IntGauge = crate::register_int_gauge_with_registry!(
1332 "g_off", "h", ® MetricLevel::Warn
1333 )
1334 .unwrap();
1335 assert_eq!(format!("{g:?}"), "GenericGauge");
1336 assert!(reg.gather().is_empty());
1337 }
1338}