Skip to content

Commit

Permalink
Add blanket implementations of Recorder for some pointer-like types (
Browse files Browse the repository at this point in the history
…#512)

Co-authored-by: david-perez <[email protected]>
  • Loading branch information
tobz and david-perez authored Sep 21, 2024
1 parent 600873b commit 6e1e7f9
Show file tree
Hide file tree
Showing 3 changed files with 131 additions and 4 deletions.
47 changes: 46 additions & 1 deletion metrics-util/src/layers/router.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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);
}
}
1 change: 1 addition & 0 deletions metrics/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -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<T> where T: Recorder`). ([#512](https://github.com/metrics-rs/metrics/pull/512))

### Changed

Expand Down
87 changes: 84 additions & 3 deletions metrics/src/recorder/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -26,23 +26,23 @@ 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<Unit>, description: SharedString);

/// 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<Unit>, description: SharedString);

/// 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<Unit>, description: SharedString);
Expand All @@ -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<T>);
impl_recorder!(T, std::sync::Arc<T>);

/// 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
Expand Down Expand Up @@ -151,6 +217,8 @@ mod tests {
Arc,
};

use crate::NoopRecorder;

use super::{Recorder, RecorderOnceCell};

#[test]
Expand Down Expand Up @@ -232,4 +300,17 @@ mod tests {
drop(second_set_result);
assert!(was_dropped.load(Ordering::SeqCst));
}

#[test]
fn blanket_implementations() {
fn is_recorder<T: 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);
}
}

0 comments on commit 6e1e7f9

Please sign in to comment.