metrics_util/layers/
prefix.rs

1use crate::layers::Layer;
2use metrics::{Counter, Gauge, Histogram, Key, KeyName, Metadata, Recorder, SharedString, Unit};
3
4/// Applies a prefix to every metric key.
5///
6/// Keys will be prefixed in the format of `<prefix>.<remaining>`.
7#[derive(Debug)]
8pub struct Prefix<R> {
9    prefix: SharedString,
10    inner: R,
11}
12
13impl<R> Prefix<R> {
14    fn prefix_key(&self, key: &Key) -> Key {
15        let mut new_name = String::with_capacity(self.prefix.len() + 1 + key.name().len());
16        new_name.push_str(self.prefix.as_ref());
17        new_name.push('.');
18        new_name.push_str(key.name());
19
20        Key::from_parts(new_name, key.labels())
21    }
22
23    fn prefix_key_name(&self, key_name: KeyName) -> KeyName {
24        let mut new_name = String::with_capacity(self.prefix.len() + 1 + key_name.as_str().len());
25        new_name.push_str(self.prefix.as_ref());
26        new_name.push('.');
27        new_name.push_str(key_name.as_str());
28
29        KeyName::from(new_name)
30    }
31}
32
33impl<R: Recorder> Recorder for Prefix<R> {
34    fn describe_counter(&self, key_name: KeyName, unit: Option<Unit>, description: SharedString) {
35        let new_key_name = self.prefix_key_name(key_name);
36        self.inner.describe_counter(new_key_name, unit, description)
37    }
38
39    fn describe_gauge(&self, key_name: KeyName, unit: Option<Unit>, description: SharedString) {
40        let new_key_name = self.prefix_key_name(key_name);
41        self.inner.describe_gauge(new_key_name, unit, description)
42    }
43
44    fn describe_histogram(&self, key_name: KeyName, unit: Option<Unit>, description: SharedString) {
45        let new_key_name = self.prefix_key_name(key_name);
46        self.inner.describe_histogram(new_key_name, unit, description)
47    }
48
49    fn register_counter(&self, key: &Key, metadata: &Metadata<'_>) -> Counter {
50        let new_key = self.prefix_key(key);
51        self.inner.register_counter(&new_key, metadata)
52    }
53
54    fn register_gauge(&self, key: &Key, metadata: &Metadata<'_>) -> Gauge {
55        let new_key = self.prefix_key(key);
56        self.inner.register_gauge(&new_key, metadata)
57    }
58
59    fn register_histogram(&self, key: &Key, metadata: &Metadata<'_>) -> Histogram {
60        let new_key = self.prefix_key(key);
61        self.inner.register_histogram(&new_key, metadata)
62    }
63}
64
65/// A layer for applying a prefix to every metric key.
66///
67/// More information on the behavior of the layer can be found in [`Prefix`].
68#[derive(Debug)]
69pub struct PrefixLayer(&'static str);
70
71impl PrefixLayer {
72    /// Creates a new `PrefixLayer` based on the given prefix.
73    pub fn new<S: Into<String>>(prefix: S) -> PrefixLayer {
74        PrefixLayer(Box::leak(prefix.into().into_boxed_str()))
75    }
76}
77
78impl<R> Layer<R> for PrefixLayer {
79    type Output = Prefix<R>;
80
81    fn layer(&self, inner: R) -> Self::Output {
82        Prefix { prefix: self.0.into(), inner }
83    }
84}
85
86#[cfg(test)]
87mod tests {
88    use super::{Prefix, PrefixLayer};
89    use crate::layers::Layer;
90    use crate::test_util::*;
91    use metrics::{Counter, Gauge, Histogram, Key, KeyName, Unit};
92
93    static METADATA: metrics::Metadata =
94        metrics::Metadata::new(module_path!(), metrics::Level::INFO, Some(module_path!()));
95
96    #[test]
97    fn test_basic_functionality() {
98        let inputs = vec![
99            RecorderOperation::DescribeCounter(
100                "counter_key".into(),
101                Some(Unit::Count),
102                "counter desc".into(),
103            ),
104            RecorderOperation::DescribeGauge(
105                "gauge_key".into(),
106                Some(Unit::Bytes),
107                "gauge desc".into(),
108            ),
109            RecorderOperation::DescribeHistogram(
110                "histogram_key".into(),
111                Some(Unit::Nanoseconds),
112                "histogram desc".into(),
113            ),
114            RecorderOperation::RegisterCounter("counter_key".into(), Counter::noop(), &METADATA),
115            RecorderOperation::RegisterGauge("gauge_key".into(), Gauge::noop(), &METADATA),
116            RecorderOperation::RegisterHistogram(
117                "histogram_key".into(),
118                Histogram::noop(),
119                &METADATA,
120            ),
121        ];
122
123        let expectations = vec![
124            RecorderOperation::DescribeCounter(
125                "testing.counter_key".into(),
126                Some(Unit::Count),
127                "counter desc".into(),
128            ),
129            RecorderOperation::DescribeGauge(
130                "testing.gauge_key".into(),
131                Some(Unit::Bytes),
132                "gauge desc".into(),
133            ),
134            RecorderOperation::DescribeHistogram(
135                "testing.histogram_key".into(),
136                Some(Unit::Nanoseconds),
137                "histogram desc".into(),
138            ),
139            RecorderOperation::RegisterCounter(
140                "testing.counter_key".into(),
141                Counter::noop(),
142                &METADATA,
143            ),
144            RecorderOperation::RegisterGauge("testing.gauge_key".into(), Gauge::noop(), &METADATA),
145            RecorderOperation::RegisterHistogram(
146                "testing.histogram_key".into(),
147                Histogram::noop(),
148                &METADATA,
149            ),
150        ];
151
152        let recorder = MockBasicRecorder::from_operations(expectations);
153        let prefix = PrefixLayer::new("testing");
154        let prefix = prefix.layer(recorder);
155
156        for operation in inputs {
157            operation.apply_to_recorder(&prefix);
158        }
159    }
160
161    #[test]
162    fn test_key_vs_key_name() {
163        let prefix = Prefix { prefix: "foobar".into(), inner: () };
164
165        let key_name = KeyName::from("my_key");
166        let key = Key::from_name(key_name.clone());
167
168        let prefixed_key = prefix.prefix_key(&key);
169        let prefixed_key_name = prefix.prefix_key_name(key_name);
170
171        assert_eq!(
172            prefixed_key.name(),
173            prefixed_key_name.as_str(),
174            "prefixed key and prefixed key name should match"
175        );
176    }
177}