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
38pub struct Snapshot(Vec<(CompositeKey, Option<Unit>, Option<SharedString>, DebugValue)>);
40
41impl Snapshot {
42 #[allow(clippy::mutable_key_type)]
44 pub fn into_hashmap(
45 self,
46 ) -> HashMap<CompositeKey, (Option<Unit>, Option<SharedString>, DebugValue)> {
47 self.0
48 .into_iter()
49 .map(|(k, unit, desc, value)| (k, (unit, desc, value)))
50 .collect::<HashMap<_, _>>()
51 }
52
53 pub fn into_vec(self) -> Vec<(CompositeKey, Option<Unit>, Option<SharedString>, DebugValue)> {
55 self.0
56 }
57}
58
59#[derive(Debug, PartialEq, Eq, Hash)]
61pub enum DebugValue {
62 Counter(u64),
64 Gauge(OrderedFloat<f64>),
66 Histogram(Vec<OrderedFloat<f64>>),
68}
69
70struct Inner {
71 registry: Registry<Key, AtomicStorage>,
72 seen: Mutex<IndexMap<CompositeKey, ()>>,
73 metadata: Mutex<IndexMap<CompositeKeyName, (Option<Unit>, SharedString)>>,
74}
75
76impl Inner {
77 fn new() -> Self {
78 Self {
79 registry: Registry::atomic(),
80 seen: Mutex::new(IndexMap::new()),
81 metadata: Mutex::new(IndexMap::new()),
82 }
83 }
84}
85
86#[derive(Clone)]
88pub struct Snapshotter {
89 inner: Arc<Inner>,
90}
91
92impl Snapshotter {
93 pub fn snapshot(&self) -> Snapshot {
95 let mut snapshot = Vec::new();
96
97 let counters = self.inner.registry.get_counter_handles();
98 let gauges = self.inner.registry.get_gauge_handles();
99 let histograms = self.inner.registry.get_histogram_handles();
100
101 let seen = self.inner.seen.lock().expect("seen lock poisoned").clone();
102 let metadata = self.inner.metadata.lock().expect("metadata lock poisoned").clone();
103
104 for (ck, _) in seen.into_iter() {
105 let value = match ck.kind() {
106 MetricKind::Counter => {
107 counters.get(ck.key()).map(|c| DebugValue::Counter(c.load(Ordering::SeqCst)))
108 }
109 MetricKind::Gauge => gauges.get(ck.key()).map(|g| {
110 let value = f64::from_bits(g.load(Ordering::SeqCst));
111 DebugValue::Gauge(value.into())
112 }),
113 MetricKind::Histogram => histograms.get(ck.key()).map(|h| {
114 let mut values = Vec::new();
115 h.clear_with(|xs| values.extend(xs.iter().map(|f| OrderedFloat::from(*f))));
116 DebugValue::Histogram(values)
117 }),
118 };
119
120 let ckn = CompositeKeyName::new(ck.kind(), ck.key().name().to_string().into());
121 let (unit, desc) = metadata
122 .get(&ckn)
123 .map(|(u, d)| (u.to_owned(), Some(d.to_owned())))
124 .unwrap_or_else(|| (None, None));
125
126 if let Some(value) = value {
129 snapshot.push((ck, unit, desc, value));
130 }
131 }
132
133 Snapshot(snapshot)
134 }
135}
136
137pub struct DebuggingRecorder {
142 inner: Arc<Inner>,
143}
144
145impl DebuggingRecorder {
146 pub fn new() -> DebuggingRecorder {
148 DebuggingRecorder { inner: Arc::new(Inner::new()) }
149 }
150
151 pub fn snapshotter(&self) -> Snapshotter {
153 Snapshotter { inner: Arc::clone(&self.inner) }
154 }
155
156 fn describe_metric(&self, rkey: CompositeKeyName, unit: Option<Unit>, desc: SharedString) {
157 let mut metadata = self.inner.metadata.lock().expect("metadata lock poisoned");
158 let (uentry, dentry) = metadata.entry(rkey).or_insert((None, desc.to_owned()));
159 if unit.is_some() {
160 *uentry = unit;
161 }
162 *dentry = desc;
163 }
164
165 fn track_metric(&self, ckey: CompositeKey) {
166 let mut seen = self.inner.seen.lock().expect("seen lock poisoned");
167 seen.insert(ckey, ());
168 }
169
170 pub fn install(self) -> Result<(), SetRecorderError<Self>> {
172 metrics::set_global_recorder(self)
173 }
174}
175
176impl Recorder for DebuggingRecorder {
177 fn describe_counter(&self, key: KeyName, unit: Option<Unit>, description: SharedString) {
178 let ckey = CompositeKeyName::new(MetricKind::Counter, key);
179 self.describe_metric(ckey, unit, description);
180 }
181
182 fn describe_gauge(&self, key: KeyName, unit: Option<Unit>, description: SharedString) {
183 let ckey = CompositeKeyName::new(MetricKind::Gauge, key);
184 self.describe_metric(ckey, unit, description);
185 }
186
187 fn describe_histogram(&self, key: KeyName, unit: Option<Unit>, description: SharedString) {
188 let ckey = CompositeKeyName::new(MetricKind::Histogram, key);
189 self.describe_metric(ckey, unit, description);
190 }
191
192 fn register_counter(&self, key: &Key, _metadata: &Metadata<'_>) -> Counter {
193 let ckey = CompositeKey::new(MetricKind::Counter, key.clone());
194 self.track_metric(ckey);
195
196 self.inner.registry.get_or_create_counter(key, |c| Counter::from_arc(c.clone()))
197 }
198
199 fn register_gauge(&self, key: &Key, _metadata: &Metadata<'_>) -> Gauge {
200 let ckey = CompositeKey::new(MetricKind::Gauge, key.clone());
201 self.track_metric(ckey);
202
203 self.inner.registry.get_or_create_gauge(key, |g| Gauge::from_arc(g.clone()))
204 }
205
206 fn register_histogram(&self, key: &Key, _metadata: &Metadata<'_>) -> Histogram {
207 let ckey = CompositeKey::new(MetricKind::Histogram, key.clone());
208 self.track_metric(ckey);
209
210 self.inner.registry.get_or_create_histogram(key, |h| Histogram::from_arc(h.clone()))
211 }
212}
213
214impl Default for DebuggingRecorder {
215 fn default() -> Self {
216 DebuggingRecorder::new()
217 }
218}