metriken 0.1.1

A fast and lightweight metrics library
Documentation
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
// Copyright 2021 Twitter, Inc.
// Licensed under the Apache License, Version 2.0
// http://www.apache.org/licenses/LICENSE-2.0

//! Easily registered distributed metrics.
//!
//! More docs todo...
//!
//! # Creating a Metric
//! Registering a metric is straightforward. All that's needed is to declare a
//! static within the [`metric`] macro. By default, the metric will have the
//! name of the path to the static variable you used to declare it but this can
//! be overridden by passing the `name` parameter to the macro.
//!
//! ```
//! # // This should remain in sync with the example below.
//! use metriken::*;
//! /// A counter metric named "<crate name>::COUNTER_A"
//! #[metric]
//! static COUNTER_A: Counter = Counter::new();
//!
//! /// A counter metric named "my.metric.name"
//! #[metric(name = "my.metric.name")]
//! static COUNTER_B: Counter = Counter::new();
//! #
//! # let metrics = metrics();
//! # // Metrics may be in any arbitrary order
//! # let mut names: Vec<_> = metrics.iter().map(|metric| metric.name()).collect();
//! # names.sort();
//! #
//! # assert_eq!(names.len(), 2);
//! # assert_eq!(names[0], "my.metric.name");
//! # assert_eq!(names[1], concat!(module_path!(), "::", "COUNTER_A"));
//! ```
//!
//! # Accessing Metrics
//! All metrics registered via the [`metric`] macro can be accessed by calling
//! the [`metrics`] function. This will return an instance of the [`Metric`]
//! struct which allows you to access all staticly and dynamically registered
//! metrics.
//!
//! Suppose we have the metrics declared in the example above.
//! ```
//! # // This should remain in sync with the example above.
//! # use metriken::*;
//! # /// A counter metric named "<crate name>::COUNTER_A"
//! # #[metric]
//! # static COUNTER_A: Counter = Counter::new();
//! #
//! # /// A counter metric named "my.metric.name"
//! # #[metric(name = "my.metric.name")]
//! # static COUNTER_B: Counter = Counter::new();
//! #
//! let metrics = metrics();
//! // Metrics may be in any arbitrary order
//! let mut names: Vec<_> = metrics.iter().map(|metric| metric.name()).collect();
//! names.sort();
//!
//! assert_eq!(names.len(), 2);
//! assert_eq!(names[0], "my.metric.name");
//! assert_eq!(names[1], concat!(module_path!(), "::", "COUNTER_A"));
//! ```
//!
//! # How it Works
//! Behind the scenes, this crate uses the [`linkme`] crate to create a
//! distributed slice containing a [`MetricEntry`] instance for each metric that
//! is registered via the [`metric`] attribute.

use parking_lot::RwLockReadGuard;
use std::any::Any;
use std::borrow::Cow;

mod counter;
mod gauge;
mod heatmap;
mod lazy;

extern crate self as metriken;

pub mod dynmetrics;

pub use crate::counter::Counter;
pub use crate::dynmetrics::{DynBoxedMetric, DynPinnedMetric};
pub use crate::gauge::Gauge;
pub use crate::heatmap::Heatmap;
pub use crate::lazy::{Lazy, Relaxed};

pub use metriken_derive::metric;

pub extern crate clocksource as time;

#[doc(hidden)]
pub use metriken_derive::to_lowercase;

#[doc(hidden)]
pub mod export {
    pub extern crate linkme;
    pub use clocksource::{Duration, Nanoseconds};

    #[linkme::distributed_slice]
    pub static METRICS: [crate::MetricEntry] = [..];
}

#[macro_export]
#[rustfmt::skip]
macro_rules! counter {
    ($name:ident) => {
        #[$crate::metric(
            name = $crate::to_lowercase!($name),
            crate = $crate
        )]
        pub static $name: $crate::Counter = $crate::Counter::new();
    };
    ($name:ident, $description:tt) => {
        #[$crate::metric(
            name = $crate::to_lowercase!($name),
            description = $description,
            crate = $crate
        )]
        pub static $name: $crate::Counter = $crate::Counter::new();
    };
}

#[macro_export]
#[rustfmt::skip]
macro_rules! gauge {
    ($name:ident) => {
        #[$crate::metric(
            name = $crate::to_lowercase!($name),
            crate = $crate
        )]
        pub static $name: $crate::Gauge = $crate::Gauge::new();
    };
    ($name:ident, $description:tt) => {
        #[$crate::metric(
            name = $crate::to_lowercase!($name),
            description = $description,
            crate = $crate
        )]
        pub static $name: $crate::Gauge = $crate::Gauge::new();
    };
}

#[macro_export]
#[rustfmt::skip]
macro_rules! heatmap {
    ($name:ident, $max:expr) => {
        #[$crate::metric(
            name = $crate::to_lowercase!($name),
            crate = $crate
        )]
        pub static $name: $crate::Relaxed<$crate::Heatmap> = $crate::Relaxed::new(|| {
            $crate::Heatmap::builder()
                .maximum_value($max as _)
                .min_resolution(1)
                .min_resolution_range(1024)
                .span($crate::export::Duration::<$crate::export::Nanoseconds<u64>>::from_secs(60))
                .resolution($crate::export::Duration::<$crate::export::Nanoseconds<u64>>::from_secs(1))
                .build()
                .expect("bad heatmap configuration")
        });
    };
    ($name:ident, $max:expr, $description:tt) => {
        #[$crate::metric(
            name = $crate::to_lowercase!($name),
            description = $description,
            crate = $crate
        )]
        pub static $name: $crate::Relaxed<$crate::Heatmap> = $crate::Relaxed::new(|| {
            $crate::Heatmap::builder()
                .maximum_value($max as _)
                .min_resolution(1)
                .min_resolution_range(1024)
                .span($crate::export::Duration::<$crate::export::Nanoseconds<u64>>::from_secs(60))
                .resolution($crate::export::Duration::<$crate::export::Nanoseconds<u64>>::from_secs(1))
                .build()
                .expect("bad heatmap configuration")
        });
    };
}

/// Global interface to a metric.
///
/// Most use of metrics should use the directly declared constants.
pub trait Metric: Send + Sync + 'static {
    /// Indicate whether this metric has been set up.
    ///
    /// Generally, if this returns `false` then the other methods on this
    /// trait should return `None`.
    fn is_enabled(&self) -> bool {
        self.as_any().is_some()
    }

    /// Get the current metric as an [`Any`] instance. This is meant to allow
    /// custom processing for known metric types.
    ///
    /// [`Any`]: std::any::Any
    fn as_any(&self) -> Option<&dyn Any>;
}

/// A statically declared metric entry.
pub struct MetricEntry {
    metric: MetricWrapper,
    name: Cow<'static, str>,
    namespace: Option<&'static str>,
    description: Option<&'static str>,
}

impl MetricEntry {
    #[doc(hidden)]
    pub const fn _new_const(
        metric: MetricWrapper,
        name: &'static str,
        namespace: &'static str,
        description: &'static str,
    ) -> Self {
        let namespace = if namespace.is_empty() {
            None
        } else {
            Some(namespace)
        };
        let description = if description.is_empty() {
            None
        } else {
            Some(description)
        };
        Self {
            metric,
            name: Cow::Borrowed(name),
            namespace,
            description,
        }
    }

    /// Create a new metric entry with the provided metric and name.
    pub fn new(metric: &'static dyn Metric, name: Cow<'static, str>) -> Self {
        // SAFETY: The lifetimes here are static.
        unsafe { Self::new_unchecked(metric, name) }
    }

    /// Create a new metric entry with the provided metric and name.
    ///
    /// # Safety
    /// This method is only safe to call if
    /// - `metric` points to a valid `dyn Metric` instance, and,
    /// - the metric instance outlives this `MetricEntry`.
    pub unsafe fn new_unchecked(metric: *const dyn Metric, name: Cow<'static, str>) -> Self {
        Self {
            metric: MetricWrapper(metric),
            name,
            namespace: None,
            description: None,
        }
    }

    /// Get a reference to the metric that this entry corresponds to.
    pub fn metric(&self) -> &dyn Metric {
        unsafe { &*self.metric.0 }
    }

    /// Get the name of this metric.
    pub fn name(&self) -> &str {
        &*self.name
    }

    /// Get the namespace of this metric.
    pub fn namespace(&self) -> Option<&str> {
        self.namespace
    }

    /// Get the description of this metric.
    pub fn description(&self) -> Option<&str> {
        self.description
    }
}

unsafe impl Send for MetricEntry {}
unsafe impl Sync for MetricEntry {}

impl std::ops::Deref for MetricEntry {
    type Target = dyn Metric;

    #[inline]
    fn deref(&self) -> &Self::Target {
        self.metric()
    }
}

impl std::fmt::Debug for MetricEntry {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        f.debug_struct("MetricEntry")
            .field("name", &self.name())
            .field("metric", &"<dyn Metric>")
            .finish()
    }
}

/// You can't use `dyn <trait>s` directly in const methods for now but a wrapper
/// is fine. This wrapper is a work around to allow us to use const constructors
/// for the MetricEntry struct.
#[doc(hidden)]
pub struct MetricWrapper(pub *const dyn Metric);

/// The list of all metrics registered via the either [`metric`] attribute or by
/// using the types within the [`dynmetrics`] module.
///
/// Names within metrics are not guaranteed to be unique and no aggregation of
/// metrics with the same name is done.
pub fn metrics() -> Metrics {
    Metrics {
        dyn_metrics: crate::dynmetrics::get_registry(),
    }
}

/// Provides access to all registered metrics both static and dynamic.
///
/// **IMPORTANT:** Note that while any instance of this struct is live
/// attempting to register or unregister any dynamic metrics will block.
/// If this is done on the same thread as is currently working with an instance
/// of `Metrics` then it will cause a deadlock. If your application will be
/// registering and unregistering dynamic metrics then you should avoid holding
/// on to `Metrics` instances for long periods of time.
///
/// `Metrics` instances can be created via the [`metrics`] function.
pub struct Metrics {
    dyn_metrics: RwLockReadGuard<'static, dynmetrics::DynMetricsRegistry>,
}

impl Metrics {
    /// A list containing all metrics that were registered via the [`metric`]
    /// attribute macro.
    pub fn static_metrics(&self) -> &'static [MetricEntry] {
        &*crate::export::METRICS
    }

    /// A list containing all metrics that were dynamically registered.
    pub fn dynamic_metrics(&self) -> &[MetricEntry] {
        self.dyn_metrics.metrics()
    }

    pub fn iter(&self) -> <&Self as IntoIterator>::IntoIter {
        self.into_iter()
    }
}

impl<'a> IntoIterator for &'a Metrics {
    type Item = &'a MetricEntry;

    type IntoIter =
        std::iter::Chain<std::slice::Iter<'a, MetricEntry>, std::slice::Iter<'a, MetricEntry>>;

    fn into_iter(self) -> Self::IntoIter {
        self.static_metrics()
            .iter()
            .chain(self.dynamic_metrics().iter())
    }
}

/// The type of the static generated by `#[metric]`.
///
/// This exports the name of the generated metric so that other code
/// can use it.
pub struct MetricInstance<M> {
    // The generated code by the #[metric] attribute needs to access this
    // directly so it needs to be public.
    #[doc(hidden)]
    pub metric: M,
    name: &'static str,
    description: Option<&'static str>,
}

impl<M> MetricInstance<M> {
    #[doc(hidden)]
    pub const fn new(metric: M, name: &'static str, description: &'static str) -> Self {
        let description = if description.is_empty() {
            None
        } else {
            Some(description)
        };
        Self {
            metric,
            name,
            description,
        }
    }

    /// Get the name of this metric.
    pub const fn name(&self) -> &'static str {
        self.name
    }

    /// Get the description of this metric.
    pub const fn description(&self) -> Option<&'static str> {
        self.description
    }
}

impl<M> std::ops::Deref for MetricInstance<M> {
    type Target = M;

    #[inline]
    fn deref(&self) -> &Self::Target {
        &self.metric
    }
}

impl<M> std::ops::DerefMut for MetricInstance<M> {
    #[inline]
    fn deref_mut(&mut self) -> &mut Self::Target {
        &mut self.metric
    }
}

impl<M> AsRef<M> for MetricInstance<M> {
    #[inline]
    fn as_ref(&self) -> &M {
        &self.metric
    }
}

impl<M> AsMut<M> for MetricInstance<M> {
    #[inline]
    fn as_mut(&mut self) -> &mut M {
        &mut self.metric
    }
}