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