diff --git a/metrics-util/src/layers/router.rs b/metrics-util/src/layers/router.rs index a4e4beba..448ae5da 100644 --- a/metrics-util/src/layers/router.rs +++ b/metrics-util/src/layers/router.rs @@ -191,7 +191,7 @@ mod tests { predicate::{always, eq}, Sequence, }; - use std::borrow::Cow; + use std::{borrow::Cow, sync::Arc}; use super::RouterBuilder; use crate::MetricKindMask; @@ -292,4 +292,49 @@ mod tests { let _ = recorder.register_counter(&all_override, &METADATA); let _ = recorder.register_histogram(&all_override, &METADATA); } + + #[test] + fn test_same_recorder_multiple_routes() { + let default_counter: Key = "default".into(); + let foo_counter: Key = "foo.counter".into(); + let bar_counter: Key = "bar.counter".into(); + + let mut default_mock = MockTestRecorder::new(); + let mut foo_bar_mock = MockTestRecorder::new(); + + let mut seq = Sequence::new(); + + static METADATA: metrics::Metadata = + metrics::Metadata::new(module_path!(), metrics::Level::INFO, Some(module_path!())); + + foo_bar_mock + .expect_register_counter() + .times(1) + .in_sequence(&mut seq) + .with(eq(foo_counter.clone()), always()) + .returning(|_, _| Counter::noop()); + foo_bar_mock + .expect_register_counter() + .times(1) + .in_sequence(&mut seq) + .with(eq(bar_counter.clone()), always()) + .returning(|_, _| Counter::noop()); + default_mock + .expect_register_counter() + .times(1) + .in_sequence(&mut seq) + .with(eq(default_counter.clone()), always()) + .returning(|_, _| Counter::noop()); + + let foo_bar_mock = Arc::new(foo_bar_mock); + + let mut builder = RouterBuilder::from_recorder(default_mock); + builder.add_route(MetricKindMask::COUNTER, "foo", foo_bar_mock.clone()); + builder.add_route(MetricKindMask::COUNTER, "bar", foo_bar_mock); + let recorder = builder.build(); + + let _ = recorder.register_counter(&foo_counter, &METADATA); + let _ = recorder.register_counter(&bar_counter, &METADATA); + let _ = recorder.register_counter(&default_counter, &METADATA); + } } diff --git a/metrics/CHANGELOG.md b/metrics/CHANGELOG.md index d2439316..c3fa5367 100644 --- a/metrics/CHANGELOG.md +++ b/metrics/CHANGELOG.md @@ -11,6 +11,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Added - Added `Debug` derive to numerous types. ([#504](https://github.com/metrics-rs/metrics/pull/504)) +- Blanket implementations of `Recorder` over smart pointer representations (i.e. `Arc where T: Recorder`). ([#512](https://github.com/metrics-rs/metrics/pull/512)) ### Changed diff --git a/metrics/src/recorder/mod.rs b/metrics/src/recorder/mod.rs index 248e32fe..91125dcd 100644 --- a/metrics/src/recorder/mod.rs +++ b/metrics/src/recorder/mod.rs @@ -26,7 +26,7 @@ pub trait Recorder { /// Describes a counter. /// /// Callers may provide the unit or a description of the counter being registered. Whether or - /// not a metric can be reregistered to provide a unit/description, if one was already passed + /// not a metric can be re-registered to provide a unit/description, if one was already passed /// or not, as well as how units/descriptions are used by the underlying recorder, is an /// implementation detail. fn describe_counter(&self, key: KeyName, unit: Option, description: SharedString); @@ -34,7 +34,7 @@ pub trait Recorder { /// Describes a gauge. /// /// Callers may provide the unit or a description of the gauge being registered. Whether or - /// not a metric can be reregistered to provide a unit/description, if one was already passed + /// not a metric can be re-registered to provide a unit/description, if one was already passed /// or not, as well as how units/descriptions are used by the underlying recorder, is an /// implementation detail. fn describe_gauge(&self, key: KeyName, unit: Option, description: SharedString); @@ -42,7 +42,7 @@ pub trait Recorder { /// Describes a histogram. /// /// Callers may provide the unit or a description of the histogram being registered. Whether or - /// not a metric can be reregistered to provide a unit/description, if one was already passed + /// not a metric can be re-registered to provide a unit/description, if one was already passed /// or not, as well as how units/descriptions are used by the underlying recorder, is an /// implementation detail. fn describe_histogram(&self, key: KeyName, unit: Option, description: SharedString); @@ -57,6 +57,72 @@ pub trait Recorder { fn register_histogram(&self, key: &Key, metadata: &Metadata<'_>) -> Histogram; } +// Blanket implementations. +macro_rules! impl_recorder { + ($inner_ty:ident, $ptr_ty:ty) => { + impl<$inner_ty> $crate::Recorder for $ptr_ty + where + $inner_ty: $crate::Recorder + ?Sized, + { + fn describe_counter( + &self, + key: $crate::KeyName, + unit: Option<$crate::Unit>, + description: $crate::SharedString, + ) { + std::ops::Deref::deref(self).describe_counter(key, unit, description) + } + + fn describe_gauge( + &self, + key: $crate::KeyName, + unit: Option<$crate::Unit>, + description: $crate::SharedString, + ) { + std::ops::Deref::deref(self).describe_gauge(key, unit, description) + } + + fn describe_histogram( + &self, + key: $crate::KeyName, + unit: Option<$crate::Unit>, + description: $crate::SharedString, + ) { + std::ops::Deref::deref(self).describe_histogram(key, unit, description) + } + + fn register_counter( + &self, + key: &$crate::Key, + metadata: &$crate::Metadata<'_>, + ) -> $crate::Counter { + std::ops::Deref::deref(self).register_counter(key, metadata) + } + + fn register_gauge( + &self, + key: &$crate::Key, + metadata: &$crate::Metadata<'_>, + ) -> $crate::Gauge { + std::ops::Deref::deref(self).register_gauge(key, metadata) + } + + fn register_histogram( + &self, + key: &$crate::Key, + metadata: &$crate::Metadata<'_>, + ) -> $crate::Histogram { + std::ops::Deref::deref(self).register_histogram(key, metadata) + } + } + }; +} + +impl_recorder!(T, &T); +impl_recorder!(T, &mut T); +impl_recorder!(T, std::boxed::Box); +impl_recorder!(T, std::sync::Arc); + /// Guard for setting a local recorder. /// /// When using a local recorder, we take a reference to the recorder and only hold it for as long as @@ -151,6 +217,8 @@ mod tests { Arc, }; + use crate::NoopRecorder; + use super::{Recorder, RecorderOnceCell}; #[test] @@ -232,4 +300,17 @@ mod tests { drop(second_set_result); assert!(was_dropped.load(Ordering::SeqCst)); } + + #[test] + fn blanket_implementations() { + fn is_recorder(_recorder: T) {} + + let mut local = NoopRecorder; + + is_recorder(NoopRecorder); + is_recorder(Arc::new(NoopRecorder)); + is_recorder(Box::new(NoopRecorder)); + is_recorder(&local); + is_recorder(&mut local); + } }