metrics_util/layers/
fanout.rs
1use std::sync::Arc;
2
3use metrics::{
4 Counter, CounterFn, Gauge, GaugeFn, Histogram, HistogramFn, Key, KeyName, Metadata, Recorder,
5 SharedString, Unit,
6};
7
8struct FanoutCounter {
9 counters: Vec<Counter>,
10}
11
12impl FanoutCounter {
13 pub fn from_counters(counters: Vec<Counter>) -> Self {
14 Self { counters }
15 }
16}
17
18impl CounterFn for FanoutCounter {
19 fn increment(&self, value: u64) {
20 for counter in &self.counters {
21 counter.increment(value);
22 }
23 }
24
25 fn absolute(&self, value: u64) {
26 for counter in &self.counters {
27 counter.absolute(value);
28 }
29 }
30}
31
32impl From<FanoutCounter> for Counter {
33 fn from(counter: FanoutCounter) -> Counter {
34 Counter::from_arc(Arc::new(counter))
35 }
36}
37
38struct FanoutGauge {
39 gauges: Vec<Gauge>,
40}
41
42impl FanoutGauge {
43 pub fn from_gauges(gauges: Vec<Gauge>) -> Self {
44 Self { gauges }
45 }
46}
47
48impl GaugeFn for FanoutGauge {
49 fn increment(&self, value: f64) {
50 for gauge in &self.gauges {
51 gauge.increment(value);
52 }
53 }
54
55 fn decrement(&self, value: f64) {
56 for gauge in &self.gauges {
57 gauge.decrement(value);
58 }
59 }
60
61 fn set(&self, value: f64) {
62 for gauge in &self.gauges {
63 gauge.set(value);
64 }
65 }
66}
67
68impl From<FanoutGauge> for Gauge {
69 fn from(gauge: FanoutGauge) -> Gauge {
70 Gauge::from_arc(Arc::new(gauge))
71 }
72}
73
74struct FanoutHistogram {
75 histograms: Vec<Histogram>,
76}
77
78impl FanoutHistogram {
79 pub fn from_histograms(histograms: Vec<Histogram>) -> Self {
80 Self { histograms }
81 }
82}
83
84impl HistogramFn for FanoutHistogram {
85 fn record(&self, value: f64) {
86 for histogram in &self.histograms {
87 histogram.record(value);
88 }
89 }
90}
91
92impl From<FanoutHistogram> for Histogram {
93 fn from(histogram: FanoutHistogram) -> Histogram {
94 Histogram::from_arc(Arc::new(histogram))
95 }
96}
97
98pub struct Fanout {
100 recorders: Vec<Box<dyn Recorder>>,
101}
102
103impl Recorder for Fanout {
104 fn describe_counter(&self, key_name: KeyName, unit: Option<Unit>, description: SharedString) {
105 for recorder in &self.recorders {
106 recorder.describe_counter(key_name.clone(), unit, description.clone());
107 }
108 }
109
110 fn describe_gauge(&self, key_name: KeyName, unit: Option<Unit>, description: SharedString) {
111 for recorder in &self.recorders {
112 recorder.describe_gauge(key_name.clone(), unit, description.clone());
113 }
114 }
115
116 fn describe_histogram(&self, key_name: KeyName, unit: Option<Unit>, description: SharedString) {
117 for recorder in &self.recorders {
118 recorder.describe_histogram(key_name.clone(), unit, description.clone());
119 }
120 }
121
122 fn register_counter(&self, key: &Key, metadata: &Metadata<'_>) -> Counter {
123 let counters = self
124 .recorders
125 .iter()
126 .map(|recorder| recorder.register_counter(key, metadata))
127 .collect();
128
129 FanoutCounter::from_counters(counters).into()
130 }
131
132 fn register_gauge(&self, key: &Key, metadata: &Metadata<'_>) -> Gauge {
133 let gauges =
134 self.recorders.iter().map(|recorder| recorder.register_gauge(key, metadata)).collect();
135
136 FanoutGauge::from_gauges(gauges).into()
137 }
138
139 fn register_histogram(&self, key: &Key, metadata: &Metadata<'_>) -> Histogram {
140 let histograms = self
141 .recorders
142 .iter()
143 .map(|recorder| recorder.register_histogram(key, metadata))
144 .collect();
145
146 FanoutHistogram::from_histograms(histograms).into()
147 }
148}
149
150#[derive(Default)]
154pub struct FanoutBuilder {
155 recorders: Vec<Box<dyn Recorder>>,
156}
157
158impl FanoutBuilder {
159 pub fn add_recorder<R>(mut self, recorder: R) -> FanoutBuilder
161 where
162 R: Recorder + 'static,
163 {
164 self.recorders.push(Box::new(recorder));
165 self
166 }
167
168 pub fn build(self) -> Fanout {
170 Fanout { recorders: self.recorders }
171 }
172}
173
174#[cfg(test)]
175mod tests {
176 use super::FanoutBuilder;
177 use crate::test_util::*;
178 use metrics::{Counter, Gauge, Histogram, Unit};
179
180 static METADATA: metrics::Metadata =
181 metrics::Metadata::new(module_path!(), metrics::Level::INFO, Some(module_path!()));
182
183 #[test]
184 fn test_basic_functionality() {
185 let operations = vec![
186 RecorderOperation::DescribeCounter(
187 "counter_key".into(),
188 Some(Unit::Count),
189 "counter desc".into(),
190 ),
191 RecorderOperation::DescribeGauge(
192 "gauge_key".into(),
193 Some(Unit::Bytes),
194 "gauge desc".into(),
195 ),
196 RecorderOperation::DescribeHistogram(
197 "histogram_key".into(),
198 Some(Unit::Nanoseconds),
199 "histogram desc".into(),
200 ),
201 RecorderOperation::RegisterCounter("counter_key".into(), Counter::noop(), &METADATA),
202 RecorderOperation::RegisterGauge("gauge_key".into(), Gauge::noop(), &METADATA),
203 RecorderOperation::RegisterHistogram(
204 "histogram_key".into(),
205 Histogram::noop(),
206 &METADATA,
207 ),
208 ];
209
210 let recorder1 = MockBasicRecorder::from_operations(operations.clone());
211 let recorder2 = MockBasicRecorder::from_operations(operations.clone());
212 let fanout =
213 FanoutBuilder::default().add_recorder(recorder1).add_recorder(recorder2).build();
214
215 for operation in operations {
216 operation.apply_to_recorder(&fanout);
217 }
218 }
219}