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}