1use std::{
8 collections::HashMap,
9 fmt::Debug,
10 hash::Hash,
11 sync::{atomic::Ordering, Arc, Mutex},
12};
13
14use crate::{
15 kind::MetricKind,
16 registry::{AtomicStorage, Registry},
17 CompositeKey,
18};
19
20use indexmap::IndexMap;
21use metrics::{
22 Counter, Gauge, Histogram, Key, KeyName, Metadata, Recorder, SetRecorderError, SharedString,
23 Unit,
24};
25use ordered_float::OrderedFloat;
26
27#[derive(Clone, Debug, Eq, Hash, PartialEq, PartialOrd, Ord)]
29struct CompositeKeyName(MetricKind, KeyName);
30
31impl CompositeKeyName {
32 pub const fn new(kind: MetricKind, key_name: KeyName) -> CompositeKeyName {
34 CompositeKeyName(kind, key_name)
35 }
36}
37
38#[derive(Debug)]
40pub struct Snapshot(Vec<(CompositeKey, Option<Unit>, Option<SharedString>, DebugValue)>);
41
42impl Snapshot {
43 #[allow(clippy::mutable_key_type)]
45 pub fn into_hashmap(
46 self,
47 ) -> HashMap<CompositeKey, (Option<Unit>, Option<SharedString>, DebugValue)> {
48 self.0
49 .into_iter()
50 .map(|(k, unit, desc, value)| (k, (unit, desc, value)))
51 .collect::<HashMap<_, _>>()
52 }
53
54 pub fn into_vec(self) -> Vec<(CompositeKey, Option<Unit>, Option<SharedString>, DebugValue)> {
56 self.0
57 }
58}
59
60#[derive(Debug, PartialEq, Eq, Hash)]
62pub enum DebugValue {
63 Counter(u64),
65 Gauge(OrderedFloat<f64>),
67 Histogram(Vec<OrderedFloat<f64>>),
69}
70
71#[derive(Debug)]
72struct Inner {
73 registry: Registry<Key, AtomicStorage>,
74 seen: Mutex<IndexMap<CompositeKey, ()>>,
75 metadata: Mutex<IndexMap<CompositeKeyName, (Option<Unit>, SharedString)>>,
76}
77
78impl Inner {
79 fn new() -> Self {
80 Self {
81 registry: Registry::atomic(),
82 seen: Mutex::new(IndexMap::new()),
83 metadata: Mutex::new(IndexMap::new()),
84 }
85 }
86}
87
88#[derive(Clone, Debug)]
90pub struct Snapshotter {
91 inner: Arc<Inner>,
92}
93
94impl Snapshotter {
95 pub fn snapshot(&self) -> Snapshot {
97 let mut snapshot = Vec::new();
98
99 let counters = self.inner.registry.get_counter_handles();
100 let gauges = self.inner.registry.get_gauge_handles();
101 let histograms = self.inner.registry.get_histogram_handles();
102
103 let seen = self.inner.seen.lock().expect("seen lock poisoned").clone();
104 let metadata = self.inner.metadata.lock().expect("metadata lock poisoned").clone();
105
106 for (ck, _) in seen.into_iter() {
107 let value = match ck.kind() {
108 MetricKind::Counter => {
109 counters.get(ck.key()).map(|c| DebugValue::Counter(c.swap(0, Ordering::SeqCst)))
110 }
111 MetricKind::Gauge => gauges.get(ck.key()).map(|g| {
112 let value = f64::from_bits(g.swap(0, Ordering::SeqCst));
113 DebugValue::Gauge(value.into())
114 }),
115 MetricKind::Histogram => histograms.get(ck.key()).map(|h| {
116 let mut values = Vec::new();
117 h.clear_with(|xs| values.extend(xs.iter().map(|f| OrderedFloat::from(*f))));
118 DebugValue::Histogram(values)
119 }),
120 };
121
122 let ckn = CompositeKeyName::new(ck.kind(), ck.key().name().to_string().into());
123 let (unit, desc) = metadata
124 .get(&ckn)
125 .map(|(u, d)| (u.to_owned(), Some(d.to_owned())))
126 .unwrap_or_else(|| (None, None));
127
128 if let Some(value) = value {
131 snapshot.push((ck, unit, desc, value));
132 }
133 }
134
135 Snapshot(snapshot)
136 }
137}
138
139#[derive(Debug)]
144pub struct DebuggingRecorder {
145 inner: Arc<Inner>,
146}
147
148impl DebuggingRecorder {
149 pub fn new() -> DebuggingRecorder {
151 DebuggingRecorder { inner: Arc::new(Inner::new()) }
152 }
153
154 pub fn snapshotter(&self) -> Snapshotter {
156 Snapshotter { inner: Arc::clone(&self.inner) }
157 }
158
159 fn describe_metric(&self, rkey: CompositeKeyName, unit: Option<Unit>, desc: SharedString) {
160 let mut metadata = self.inner.metadata.lock().expect("metadata lock poisoned");
161 let (uentry, dentry) = metadata.entry(rkey).or_insert((None, desc.to_owned()));
162 if unit.is_some() {
163 *uentry = unit;
164 }
165 *dentry = desc;
166 }
167
168 fn track_metric(&self, ckey: CompositeKey) {
169 let mut seen = self.inner.seen.lock().expect("seen lock poisoned");
170 seen.insert(ckey, ());
171 }
172
173 pub fn install(self) -> Result<(), SetRecorderError<Self>> {
175 metrics::set_global_recorder(self)
176 }
177}
178
179impl Recorder for DebuggingRecorder {
180 fn describe_counter(&self, key: KeyName, unit: Option<Unit>, description: SharedString) {
181 let ckey = CompositeKeyName::new(MetricKind::Counter, key);
182 self.describe_metric(ckey, unit, description);
183 }
184
185 fn describe_gauge(&self, key: KeyName, unit: Option<Unit>, description: SharedString) {
186 let ckey = CompositeKeyName::new(MetricKind::Gauge, key);
187 self.describe_metric(ckey, unit, description);
188 }
189
190 fn describe_histogram(&self, key: KeyName, unit: Option<Unit>, description: SharedString) {
191 let ckey = CompositeKeyName::new(MetricKind::Histogram, key);
192 self.describe_metric(ckey, unit, description);
193 }
194
195 fn register_counter(&self, key: &Key, _metadata: &Metadata<'_>) -> Counter {
196 let key = key.to_retained();
197 let ckey = CompositeKey::new(MetricKind::Counter, key.clone());
198 self.track_metric(ckey);
199
200 self.inner.registry.get_or_create_counter(&key, |c| Counter::from_arc(c.clone()))
201 }
202
203 fn register_gauge(&self, key: &Key, _metadata: &Metadata<'_>) -> Gauge {
204 let key = key.to_retained();
205 let ckey = CompositeKey::new(MetricKind::Gauge, key.clone());
206 self.track_metric(ckey);
207
208 self.inner.registry.get_or_create_gauge(&key, |g| Gauge::from_arc(g.clone()))
209 }
210
211 fn register_histogram(&self, key: &Key, _metadata: &Metadata<'_>) -> Histogram {
212 let key = key.to_retained();
213 let ckey = CompositeKey::new(MetricKind::Histogram, key.clone());
214 self.track_metric(ckey);
215
216 self.inner.registry.get_or_create_histogram(&key, |h| Histogram::from_arc(h.clone()))
217 }
218}
219
220impl Default for DebuggingRecorder {
221 fn default() -> Self {
222 DebuggingRecorder::new()
223 }
224}
225
226#[cfg(test)]
227mod tests {
228 use super::*;
229 use metrics::Label;
230
231 #[test]
232 fn test_debugging_recorder() {
233 let recorder = DebuggingRecorder::default();
234 let snapshotter = recorder.snapshotter();
235 metrics::with_local_recorder(&recorder, move || {
236 for _ in 1..3 {
240 let counter = metrics::counter!("test.counter", "counter.key" => "counter.value");
241 let gauge_incr = metrics::gauge!("test.gauge.incr", "gauge.key" => "gauge.value");
242 let gauge_set = metrics::gauge!("test.gauge.set", "gauge.key" => "gauge.value");
243 let histogram =
244 metrics::histogram!("test.histogram", "histogram.key" => "histogram.value");
245
246 counter.increment(123);
247 gauge_incr.increment(456);
248 gauge_set.set(654);
249 histogram.record(789);
250
251 let metrics = snapshotter.snapshot().into_vec();
252
253 assert_eq!(
254 metrics,
255 vec![
256 (
257 CompositeKey::new(
258 MetricKind::Counter,
259 Key::from_parts(
260 "test.counter",
261 vec![Label::new("counter.key", "counter.value")]
262 )
263 ),
264 None,
265 None,
266 DebugValue::Counter(123)
267 ),
268 (
269 CompositeKey::new(
270 MetricKind::Gauge,
271 Key::from_parts(
272 "test.gauge.incr",
273 vec![Label::new("gauge.key", "gauge.value")]
274 )
275 ),
276 None,
277 None,
278 DebugValue::Gauge(OrderedFloat(456.0))
279 ),
280 (
281 CompositeKey::new(
282 MetricKind::Gauge,
283 Key::from_parts(
284 "test.gauge.set",
285 vec![Label::new("gauge.key", "gauge.value")]
286 )
287 ),
288 None,
289 None,
290 DebugValue::Gauge(OrderedFloat(654.0))
291 ),
292 (
293 CompositeKey::new(
294 MetricKind::Histogram,
295 Key::from_parts(
296 "test.histogram",
297 vec![Label::new("histogram.key", "histogram.value")]
298 )
299 ),
300 None,
301 None,
302 DebugValue::Histogram(vec![OrderedFloat(789.0)])
303 )
304 ]
305 );
306 }
307 });
308 }
309}