1#![doc = include_str!("../README.md")]
2
3mod counter;
4mod histogram;
5
6use crate::access::get_metrics;
7pub use counter::{Counter, CounterOps};
9pub use histogram::{Histogram, HistogramOps};
10use serde::{Deserialize, Serialize};
11use serde_with::serde_as;
12use std::collections::HashMap;
13use std::sync::atomic::{AtomicPtr, Ordering};
14
15pub type Id = u64;
17pub type Tag<'a> = (&'a str, &'a str);
19pub type Tags<'a> = &'a [Tag<'a>];
21
22pub const fn empty_tags() -> Tags<'static> {
24 &[]
25}
26
27pub trait Metrics {
29 fn name(&self) -> &'static str;
30
31 fn new_counter(&mut self, name: &str, tags: Tags) -> Id;
32
33 fn delete_counter(&mut self, id: Id);
34
35 fn increment_counter_by(&mut self, id: Id, delta: u64);
36
37 fn increment_counter(&mut self, id: Id) {
38 self.increment_counter_by(id, 1)
39 }
40
41 fn new_histogram(&mut self, name: &str, tags: Tags) -> Id;
42
43 fn delete_histogram(&mut self, id: Id);
44
45 fn record(&mut self, id: Id, value: u64);
46}
47
48trait IntoHandle {
49 fn into_handle(self) -> MetricsHandle;
50}
51
52impl<T: Metrics + Sized> IntoHandle for T {
53 fn into_handle(self) -> MetricsHandle {
54 let name = self.name();
55 let ptr = Box::into_raw(Box::new(self)) as *mut _;
56
57 let vtable = MetricsVTable {
58 new_counter: new_counter_raw::<Self>,
59 delete_counter: delete_counter_raw::<Self>,
60 increment_counter: increment_counter_raw::<Self>,
61 increment_counter_by: increment_counter_by_raw::<Self>,
62 new_histogram: new_histogram_raw::<Self>,
63 delete_histogram: delete_histogram_raw::<Self>,
64 record: record_raw::<Self>,
65 };
66 MetricsHandle { ptr, vtable, name }
67 }
68}
69
70#[inline]
71fn new_counter_raw<T: Metrics>(ptr: *mut u8, name: &str, tags: Tags) -> Id {
72 let metrics = unsafe { &mut *(ptr as *mut T) };
73 metrics.new_counter(name, tags)
74}
75
76#[inline]
77fn delete_counter_raw<T: Metrics>(ptr: *mut u8, id: Id) {
78 let metrics = unsafe { &mut *(ptr as *mut T) };
79 metrics.delete_counter(id)
80}
81
82#[inline]
83fn increment_counter_by_raw<T: Metrics>(ptr: *mut u8, id: Id, delta: u64) {
84 let metrics = unsafe { &mut *(ptr as *mut T) };
85 metrics.increment_counter_by(id, delta)
86}
87
88#[inline]
89fn increment_counter_raw<T: Metrics>(ptr: *mut u8, id: Id) {
90 increment_counter_by_raw::<T>(ptr, id, 1)
91}
92
93#[inline]
94fn new_histogram_raw<T: Metrics>(ptr: *mut u8, name: &str, tags: Tags) -> Id {
95 let metrics = unsafe { &mut *(ptr as *mut T) };
96 metrics.new_histogram(name, tags)
97}
98
99#[inline]
100fn delete_histogram_raw<T: Metrics>(ptr: *mut u8, id: Id) {
101 let metrics = unsafe { &mut *(ptr as *mut T) };
102 metrics.delete_histogram(id)
103}
104
105#[inline]
106fn record_raw<T: Metrics>(ptr: *mut u8, id: Id, value: u64) {
107 let metrics = unsafe { &mut *(ptr as *mut T) };
108 metrics.record(id, value)
109}
110
111#[serde_as]
113#[derive(Serialize, Deserialize, Debug, Clone)]
114#[serde(rename_all = "snake_case")]
115#[serde(tag = "type")]
116pub enum PreAllocatedMetric {
117 Counter {
118 name: String,
119 id: Id,
120 #[serde_as(as = "HashMap<_, _>")]
121 #[serde(default)]
122 tags: Vec<(String, String)>,
123 },
124 Histogram {
125 name: String,
126 id: Id,
127 #[serde_as(as = "HashMap<_, _>")]
128 #[serde(default)]
129 tags: Vec<(String, String)>,
130 },
131}
132
133impl PreAllocatedMetric {
134 pub fn counter(name: &str, id: Id, tags: &[Tag]) -> Self {
135 PreAllocatedMetric::Counter {
136 name: name.to_owned(),
137 id,
138 tags: tags.iter().map(|tag| (tag.0.to_owned(), tag.1.to_owned())).collect(),
139 }
140 }
141
142 pub fn histogram(name: &str, id: Id, tags: &[Tag]) -> Self {
143 PreAllocatedMetric::Histogram {
144 name: name.to_owned(),
145 id,
146 tags: tags.iter().map(|tag| (tag.0.to_owned(), tag.1.to_owned())).collect(),
147 }
148 }
149}
150
151struct NoOpMetrics;
153
154impl Metrics for NoOpMetrics {
155 fn name(&self) -> &'static str {
156 "no-op"
157 }
158
159 fn new_counter(&mut self, _name: &str, _tags: Tags) -> Id {
160 Id::default()
161 }
162
163 fn delete_counter(&mut self, _id: Id) {
164 }
166
167 fn increment_counter_by(&mut self, _id: Id, _delta: u64) {
168 }
170
171 fn new_histogram(&mut self, _name: &str, _tags: Tags) -> Id {
172 Id::default()
173 }
174
175 fn delete_histogram(&mut self, _id: Id) {
176 }
178
179 fn record(&mut self, _id: Id, _value: u64) {
180 }
182}
183
184const NO_OP_METRICS: NoOpMetrics = NoOpMetrics;
185
186const NO_OP_METRICS_VTABLE: MetricsVTable = MetricsVTable {
187 new_counter: new_counter_raw::<NoOpMetrics>,
188 delete_counter: delete_counter_raw::<NoOpMetrics>,
189 increment_counter: increment_counter_raw::<NoOpMetrics>,
190 increment_counter_by: increment_counter_by_raw::<NoOpMetrics>,
191 new_histogram: new_histogram_raw::<NoOpMetrics>,
192 delete_histogram: delete_histogram_raw::<NoOpMetrics>,
193 record: record_raw::<NoOpMetrics>,
194};
195
196const NO_OP_METRICS_HANDLE: MetricsHandle = MetricsHandle {
197 ptr: &NO_OP_METRICS as *const NoOpMetrics as *mut u8,
198 vtable: NO_OP_METRICS_VTABLE,
199 name: "no-op",
200};
201
202struct MetricsHolder {
203 handle: AtomicRef<MetricsHandle>,
204}
205
206static mut METRICS: MetricsHolder = MetricsHolder {
208 handle: AtomicRef::new(&NO_OP_METRICS_HANDLE),
209};
210
211pub fn set_metrics(metrics: impl Metrics) {
214 #[allow(static_mut_refs)]
215 unsafe { &mut METRICS }
216 .handle
217 .set(Box::leak(Box::new(metrics.into_handle())), Ordering::SeqCst);
218}
219
220pub fn get_metrics_backend_name() -> &'static str {
222 get_metrics().name
223}
224
225struct MetricsVTable {
226 new_counter: fn(*mut u8, &str, Tags) -> Id,
227 delete_counter: fn(*mut u8, Id),
228 increment_counter: fn(*mut u8, Id),
229 increment_counter_by: fn(*mut u8, Id, u64),
230 new_histogram: fn(*mut u8, &str, Tags) -> Id,
231 delete_histogram: fn(*mut u8, Id),
232 record: fn(*mut u8, Id, u64),
233}
234
235pub struct MetricsHandle {
237 ptr: *mut u8,
238 vtable: MetricsVTable,
239 name: &'static str,
240}
241
242impl MetricsHandle {
243 #[inline]
244 fn new_counter(&mut self, name: &str, tags: Tags) -> Id {
245 (self.vtable.new_counter)(self.ptr, name, tags)
246 }
247
248 #[inline]
249 fn delete_counter(&mut self, id: Id) {
250 (self.vtable.delete_counter)(self.ptr, id)
251 }
252
253 #[inline]
254 fn increment_counter_by(&mut self, id: Id, delta: u64) {
255 (self.vtable.increment_counter_by)(self.ptr, id, delta)
256 }
257
258 #[inline]
259 fn increment_counter(&mut self, id: Id) {
260 (self.vtable.increment_counter)(self.ptr, id)
261 }
262
263 #[inline]
264 fn new_histogram(&mut self, name: &str, tags: Tags) -> Id {
265 (self.vtable.new_histogram)(self.ptr, name, tags)
266 }
267
268 #[inline]
269 fn delete_histogram(&mut self, id: Id) {
270 (self.vtable.delete_histogram)(self.ptr, id)
271 }
272
273 #[inline]
274 fn record(&mut self, id: Id, value: u64) {
275 (self.vtable.record)(self.ptr, id, value)
276 }
277}
278
279struct AtomicRef<T> {
280 ptr: AtomicPtr<T>,
281}
282
283impl<T> AtomicRef<T> {
284 pub const fn new(data: &T) -> Self {
285 Self {
286 ptr: AtomicPtr::new(data as *const T as *mut T),
287 }
288 }
289
290 #[inline]
291 pub fn get(&self, order: Ordering) -> &T {
292 unsafe { &*self.ptr.load(order) }
293 }
294
295 #[inline]
296 pub fn get_mut(&mut self, order: Ordering) -> &mut T {
297 unsafe { &mut *self.ptr.load(order) }
298 }
299
300 #[inline]
301 pub fn set(&self, new_ref: &T, order: Ordering) {
302 self.ptr.store(new_ref as *const T as *mut T, order);
303 }
304}
305
306mod access {
307 use crate::{MetricsHandle, METRICS};
308 use std::sync::atomic::Ordering;
309
310 #[allow(static_mut_refs)]
311 pub fn get_metrics_mut() -> &'static mut MetricsHandle {
312 unsafe { &mut METRICS }.handle.get_mut(Ordering::Acquire)
313 }
314
315 #[allow(static_mut_refs)]
316 pub fn get_metrics() -> &'static MetricsHandle {
317 unsafe { &METRICS }.handle.get(Ordering::Acquire)
318 }
319}