metrics_util/layers/
mod.rs

1//! Layers are composable helpers that can be "layered" on top of an existing `Recorder` to enhance
2//! or alter its behavior as desired, without having to change the recorder implementation itself.
3//!
4//! As well, [`Stack`] can be used to easily compose multiple layers together and provides a
5//! convenience method for installing it as the global recorder, providing a smooth transition from
6//! working directly with installing exporters to installing stacks.
7//!
8//! Here's an example of a layer that filters out all metrics that start with a specific string:
9//!
10//! ```no_run
11//! # use metrics::{Counter, Gauge, Histogram, Key, KeyName, Metadata, Recorder, SharedString, Unit};
12//! # use metrics::NoopRecorder as BasicRecorder;
13//! # use metrics_util::layers::{Layer, Stack, PrefixLayer};
14//! // A simple layer that denies any metrics that have "stairway" or "heaven" in their name.
15//! #[derive(Default, Debug)]
16//! pub struct StairwayDeny<R>(pub(crate) R);
17//!
18//! impl<R> StairwayDeny<R> {
19//!     fn is_invalid_key(&self, key: &str) -> bool {
20//!         key.contains("stairway") || key.contains("heaven")
21//!     }
22//! }
23//!
24//! impl<R: Recorder> Recorder for StairwayDeny<R> {
25//!     fn describe_counter(
26//!         &self,
27//!         key_name: KeyName,
28//!         unit: Option<Unit>,
29//!         description: SharedString,
30//!     ) {
31//!         if self.is_invalid_key(key_name.as_str()) {
32//!             return;
33//!         }
34//!         self.0.describe_counter(key_name, unit, description)
35//!     }
36//!
37//!     fn describe_gauge(&self, key_name: KeyName, unit: Option<Unit>, description: SharedString) {
38//!         if self.is_invalid_key(key_name.as_str()) {
39//!             return;
40//!         }
41//!         self.0.describe_gauge(key_name, unit, description)
42//!     }
43//!
44//!     fn describe_histogram(
45//!         &self,
46//!         key_name: KeyName,
47//!         unit: Option<Unit>,
48//!         description: SharedString,
49//!     ) {
50//!         if self.is_invalid_key(key_name.as_str()) {
51//!             return;
52//!         }
53//!         self.0.describe_histogram(key_name, unit, description)
54//!     }
55//!
56//!     fn register_counter(&self, key: &Key, metadata: &Metadata<'_>) -> Counter {
57//!         if self.is_invalid_key(key.name()) {
58//!             return Counter::noop();
59//!         }
60//!         self.0.register_counter(key, metadata)
61//!     }
62//!
63//!     fn register_gauge(&self, key: &Key, metadata: &Metadata<'_>) -> Gauge {
64//!         if self.is_invalid_key(key.name()) {
65//!             return Gauge::noop();
66//!         }
67//!         self.0.register_gauge(key, metadata)
68//!     }
69//!
70//!     fn register_histogram(&self, key: &Key, metadata: &Metadata<'_>) -> Histogram {
71//!         if self.is_invalid_key(key.name()) {
72//!             return Histogram::noop();
73//!         }
74//!         self.0.register_histogram(key, metadata)
75//!     }
76//! }
77//!
78//! #[derive(Debug, Default)]
79//! pub struct StairwayDenyLayer;
80//!
81//! impl<R> Layer<R> for StairwayDenyLayer {
82//!     type Output = StairwayDeny<R>;
83//!
84//!     fn layer(&self, inner: R) -> Self::Output {
85//!         StairwayDeny(inner)
86//!     }
87//! }
88//!
89//! // Now you can construct an instance of it to use it.  The layer will be wrapped around
90//! // our base recorder, which is a debugging recorder also supplied by `metrics_util`.
91//! # fn main() {
92//! let recorder = BasicRecorder;
93//! let layer = StairwayDenyLayer::default();
94//! let layered = layer.layer(recorder);
95//! metrics::set_global_recorder(layered).expect("failed to install recorder");
96//!
97//! // Working with layers directly is a bit cumbersome, though, so let's use a `Stack`.
98//! let stack = Stack::new(BasicRecorder);
99//! stack.push(StairwayDenyLayer::default()).install().expect("failed to install stack");
100//!
101//! // `Stack` makes it easy to chain layers together, as well.
102//! let stack = Stack::new(BasicRecorder);
103//! stack
104//!     .push(PrefixLayer::new("app_name"))
105//!     .push(StairwayDenyLayer::default())
106//!     .install()
107//!     .expect("failed to install stack");
108//! # }
109//! ```
110use metrics::{Counter, Gauge, Histogram, Key, KeyName, Metadata, Recorder, SharedString, Unit};
111
112use metrics::SetRecorderError;
113
114mod fanout;
115pub use fanout::{Fanout, FanoutBuilder};
116
117#[cfg(feature = "layer-filter")]
118mod filter;
119#[cfg(feature = "layer-filter")]
120pub use filter::{Filter, FilterLayer};
121
122mod prefix;
123pub use prefix::{Prefix, PrefixLayer};
124
125#[cfg(feature = "layer-router")]
126mod router;
127#[cfg(feature = "layer-router")]
128pub use router::{Router, RouterBuilder};
129
130/// Decorates an object by wrapping it within another type.
131pub trait Layer<R> {
132    /// The output type after wrapping.
133    type Output;
134
135    /// Wraps `inner` based on this layer.
136    fn layer(&self, inner: R) -> Self::Output;
137}
138
139/// Builder for composing layers together in a top-down/inside-out order.
140#[derive(Debug)]
141pub struct Stack<R> {
142    inner: R,
143}
144
145impl<R> Stack<R> {
146    /// Creates a new `Stack` around the given object.
147    pub fn new(inner: R) -> Self {
148        Stack { inner }
149    }
150
151    /// Pushes the given layer on to the stack, wrapping the existing stack.
152    pub fn push<L: Layer<R>>(self, layer: L) -> Stack<L::Output> {
153        Stack::new(layer.layer(self.inner))
154    }
155}
156
157impl<R: Recorder + Sync + 'static> Stack<R> {
158    /// Installs this stack as the global recorder.
159    ///
160    /// An error will be returned if there's an issue with installing the stack as the global recorder.
161    pub fn install(self) -> Result<(), SetRecorderError<Self>> {
162        metrics::set_global_recorder(self)
163    }
164}
165
166impl<R: Recorder> Recorder for Stack<R> {
167    fn describe_counter(&self, key_name: KeyName, unit: Option<Unit>, description: SharedString) {
168        self.inner.describe_counter(key_name, unit, description);
169    }
170
171    fn describe_gauge(&self, key_name: KeyName, unit: Option<Unit>, description: SharedString) {
172        self.inner.describe_gauge(key_name, unit, description);
173    }
174
175    fn describe_histogram(&self, key_name: KeyName, unit: Option<Unit>, description: SharedString) {
176        self.inner.describe_histogram(key_name, unit, description);
177    }
178
179    fn register_counter(&self, key: &Key, metadata: &Metadata<'_>) -> Counter {
180        self.inner.register_counter(key, metadata)
181    }
182
183    fn register_gauge(&self, key: &Key, metadata: &Metadata<'_>) -> Gauge {
184        self.inner.register_gauge(key, metadata)
185    }
186
187    fn register_histogram(&self, key: &Key, metadata: &Metadata<'_>) -> Histogram {
188        self.inner.register_histogram(key, metadata)
189    }
190}