1use crate::layers::Layer;
2use metrics::{Counter, Gauge, Histogram, Key, KeyName, Metadata, Recorder, SharedString, Unit};
3
4#[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#[derive(Debug)]
69pub struct PrefixLayer(&'static str);
70
71impl PrefixLayer {
72 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}