Skip to main content

metrics_procession/
recorder.rs

1use std::sync::{Arc, Mutex, MutexGuard};
2
3use metrics::{CounterFn, GaugeFn, HistogramFn, Recorder};
4
5use crate::{
6    event::{Entry, Op},
7    procession::Procession,
8};
9
10#[derive(Debug, Clone, Default)]
11pub struct ProcessionRecorder(Arc<Mutex<Procession>>);
12
13impl ProcessionRecorder {
14    pub fn lock(&self) -> MutexGuard<Procession> {
15        self.0.lock().unwrap_or_else(|e| e.into_inner())
16    }
17    pub fn memory_size(&self) -> usize {
18        self.0.lock().unwrap().memory_size()
19    }
20}
21
22impl Recorder for ProcessionRecorder {
23    fn describe_counter(
24        &self,
25        _: metrics::KeyName,
26        _: Option<metrics::Unit>,
27        _: metrics::SharedString,
28    ) {
29    }
30
31    fn describe_gauge(
32        &self,
33        _: metrics::KeyName,
34        _: Option<metrics::Unit>,
35        _: metrics::SharedString,
36    ) {
37    }
38
39    fn describe_histogram(
40        &self,
41        _: metrics::KeyName,
42        _: Option<metrics::Unit>,
43        _: metrics::SharedString,
44    ) {
45    }
46
47    fn register_counter(&self, key: &metrics::Key, _: &metrics::Metadata<'_>) -> metrics::Counter {
48        let label = self.0.lock().unwrap().ensure_label(key);
49        metrics::Counter::from_arc(Arc::new(Counter(label, self.clone())))
50    }
51
52    fn register_gauge(&self, key: &metrics::Key, _: &metrics::Metadata<'_>) -> metrics::Gauge {
53        let label = self.0.lock().unwrap().ensure_label(key);
54        metrics::Gauge::from_arc(Arc::new(Gauge(label, self.clone())))
55    }
56
57    fn register_histogram(
58        &self,
59        key: &metrics::Key,
60        _: &metrics::Metadata<'_>,
61    ) -> metrics::Histogram {
62        let label = self.0.lock().unwrap().ensure_label(key);
63        metrics::Histogram::from_arc(Arc::new(Histo(label, self.clone())))
64    }
65}
66
67struct Counter(u16, ProcessionRecorder);
68
69impl CounterFn for Counter {
70    fn increment(&self, value: u64) {
71        self.insert(value, Op::Add);
72    }
73
74    fn absolute(&self, value: u64) {
75        self.insert(value, Op::Set);
76    }
77}
78
79impl Counter {
80    pub fn insert(&self, value: u64, op: Op) {
81        let Ok(value) = u32::try_from(value) else {
82            eprintln!("value has exceeded a u32, skipping event");
83            return;
84        };
85        self.1
86            .0
87            .lock()
88            .unwrap()
89            .insert_entry(Entry::Counter { value, op }, self.0);
90    }
91}
92
93struct Gauge(u16, ProcessionRecorder);
94
95impl GaugeFn for Gauge {
96    fn increment(&self, value: f64) {
97        self.1.0.lock().unwrap().insert_entry(
98            Entry::Gauge {
99                value: value as f32,
100                op: Op::Add,
101            },
102            self.0,
103        );
104    }
105
106    fn decrement(&self, value: f64) {
107        self.1.0.lock().unwrap().insert_entry(
108            Entry::Gauge {
109                value: value as f32,
110                op: Op::Sub,
111            },
112            self.0,
113        );
114    }
115
116    fn set(&self, value: f64) {
117        self.1.0.lock().unwrap().insert_entry(
118            Entry::Gauge {
119                value: value as f32,
120                op: Op::Set,
121            },
122            self.0,
123        );
124    }
125}
126
127struct Histo(u16, ProcessionRecorder);
128
129impl HistogramFn for Histo {
130    fn record(&self, value: f64) {
131        self.1.0.lock().unwrap().insert_entry(
132            Entry::Histogram {
133                value: value as f32,
134            },
135            self.0,
136        );
137    }
138}
139
140#[cfg(test)]
141mod tests {
142    use super::*;
143
144    #[test]
145    fn install_and_emit() {
146        let recorder = ProcessionRecorder::default();
147        metrics::with_local_recorder(&recorder, || {
148            let ct = metrics::counter!("one_counter");
149            let ct2 = metrics::counter!("with_label", "label" => "this-one");
150            let g = metrics::gauge!("one_gauge");
151            let g2 = metrics::gauge!("with_labels", "label1" => "value1", "label2" => "value2");
152            let h = metrics::histogram!("one_histo");
153            let h2 = metrics::histogram!("with_label", "tid" => format!("{:?}", std::thread::current().id()));
154
155            for i in 0..1000 {
156                ct.increment(1);
157                ct2.increment(2);
158                g.set(i as f64);
159                g2.set((i / 2) as f64);
160                h.record(i as f64);
161                h2.record((i / 2) as f64);
162            }
163        });
164    }
165}