Skip to main content

metriken_core/
lib.rs

1//! Easily registered distributed metrics.
2//!
3//! You should usually be using the [`metriken`] crate instead. This crate
4//! contains the core distributed slice used by [`metriken`] so that multiple
5//! major versions of [`metriken`] can coexist.
6//!
7//! [`metriken`]: https://docs.rs/metriken
8
9use std::any::Any;
10use std::borrow::Cow;
11
12/// A helper macro for marking imports as being used.
13///
14/// This is meant to be used for when a reference is made to an item from a doc
15/// comment but that item isn't actually used for code anywhere.
16macro_rules! used_in_docs {
17    ($($item:ident),* $(,)?) => {
18        const _: () = {
19            #[allow(unused_imports)]
20            mod _docs {
21                $( use super::$item; )*
22            }
23        };
24    };
25}
26
27pub mod dynmetrics;
28mod formatter;
29mod metadata;
30mod metrics;
31mod null;
32mod provide;
33mod traits;
34mod window;
35mod wrapper;
36
37pub use crate::formatter::{default_formatter, Format};
38pub use crate::metadata::{Metadata, MetadataIter};
39pub use crate::metrics::{metrics, DynMetricsIter, Metrics, MetricsIter};
40pub use crate::provide::{request_ref, request_value, Request};
41pub use crate::traits::{
42    CounterGroupMetric, GaugeGroupMetric, HistogramGroupMetric, HistogramMetric,
43};
44pub use crate::window::Window;
45
46/// Global interface to a metric.
47///
48/// Most use of metrics should use the directly declared constants.
49pub trait Metric: Send + Sync + 'static {
50    /// Indicate whether this metric has been set up.
51    ///
52    /// Generally, if this returns `false` then the other methods on this
53    /// trait should return `None`.
54    fn is_enabled(&self) -> bool {
55        self.as_any().is_some()
56    }
57
58    /// Get the current metric as an [`Any`] instance. This is meant to allow
59    /// custom processing for known metric types.
60    ///
61    /// [`Any`]: std::any::Any
62    fn as_any(&self) -> Option<&dyn Any>;
63
64    /// Get the value of the current metric, should it be enabled.
65    ///
66    /// # Note to Implementors
67    /// If your metric's value does not correspond to one of the variants of
68    /// [`Value`] then return [`Value::Other`] and metric consumers can use
69    /// [`as_any`](crate::Metric::as_any) to specifically handle your metric.
70    fn value(&self) -> Option<Value<'_>>;
71
72    /// Get this metric's acquisition window, if one has been recorded.
73    ///
74    /// The acquisition window is the interval over which the metric's value
75    /// was read. Default: `None` — most metrics do not record a window. The
76    /// windowed scalar wrappers (`WindowedLazyCounter`, `WindowedLazyGauge`)
77    /// and the base `RwLockHistogram` override this to return the window
78    /// recorded by `set_with_window`.
79    fn load_window(&self) -> Option<Window> {
80        None
81    }
82
83    /// Get this metric's value and its acquisition window as a torn-safe pair.
84    ///
85    /// Consumers that need a self-consistent `(value, window)` pair (such as
86    /// exposition) must call this instead of pairing separate `value()` and
87    /// `load_window()` reads, which can tear under a concurrent
88    /// `set_with_window`. Default: `(self.value(), None)`. The windowed scalar
89    /// wrappers (`WindowedLazyCounter`, `WindowedLazyGauge`) and the base
90    /// `RwLockHistogram` override this to read both the value and the window
91    /// under a single acquisition of their window lock, so the pair is never
92    /// torn.
93    fn value_with_window(&self) -> (Option<Value<'_>>, Option<Window>) {
94        (self.value(), None)
95    }
96
97    /// Provides type based access to context.
98    ///
99    /// This can be used in conjunction with [`Request::provide_value`] and
100    /// [`Request::provide_ref`] to extract references to member variables from
101    /// `dyn Metric` trait objects.
102    ///
103    /// If you want to read provided types from a metric see
104    /// [`MetricEntry::request_value`] and [`MetricEntry::request_ref`].
105    fn provide<'a>(&'a self, request: &mut Request<'a>) {
106        // Silence the unused variable warning.
107        let _ = request;
108    }
109}
110
111/// The value of a metric.
112///
113/// See [`Metric::value`].
114#[non_exhaustive]
115pub enum Value<'a> {
116    /// A counter value.
117    Counter(u64),
118
119    /// A gauge value.
120    Gauge(i64),
121
122    /// A histogram metric that can produce snapshots.
123    Histogram(&'a dyn HistogramMetric),
124
125    /// A group of counter metrics with per-entry metadata.
126    CounterGroup(&'a dyn CounterGroupMetric),
127
128    /// A group of gauge metrics with per-entry metadata.
129    GaugeGroup(&'a dyn GaugeGroupMetric),
130
131    /// A group of histogram metrics with per-entry metadata.
132    HistogramGroup(&'a dyn HistogramGroupMetric),
133
134    /// The value of the metric could not be represented using the other `Value`
135    /// variants.
136    ///
137    /// Use [`Metric::as_any`] to specifically handle the type of this metric.
138    Other(&'a dyn Any),
139}
140
141/// A statically declared metric entry.
142pub struct MetricEntry {
143    metric: *const dyn Metric,
144    name: Cow<'static, str>,
145    description: Option<Cow<'static, str>>,
146    module: Cow<'static, str>,
147}
148
149impl MetricEntry {
150    /// Get a reference to the metric that this entry corresponds to.
151    pub fn metric(&self) -> &dyn Metric {
152        unsafe { &*self.metric }
153    }
154
155    /// Get the name of this metric.
156    pub fn name(&self) -> &str {
157        &self.name
158    }
159
160    /// Get the module path where this metric was defined (`module_path!()` at
161    /// the `#[metric]` definition site).
162    pub fn module(&self) -> &str {
163        &self.module
164    }
165
166    /// Get the description of this metric.
167    pub fn description(&self) -> Option<&str> {
168        self.description.as_deref()
169    }
170
171    /// Access the [`Metadata`] associated with this metrics entry.
172    pub fn metadata(&self) -> &Metadata {
173        static EMPTY: Metadata = Metadata::default_const();
174        self.request_ref::<Metadata>().unwrap_or(&EMPTY)
175    }
176
177    /// Format the metric into a string with the given format.
178    pub fn formatted(&self, format: Format) -> String {
179        let formatter = self
180            .request_value::<crate::wrapper::FormattingFn>()
181            .map(|func| func.0)
182            .unwrap_or(crate::default_formatter);
183
184        formatter(self, format)
185    }
186
187    /// Checks whether `metric` is the metric for this entry.
188    ///
189    /// This checks both the type id and the address. Note that it may have
190    /// false positives if `metric` is a ZST since multiple ZSTs may share
191    /// the same address.
192    pub fn is(&self, metric: &dyn Metric) -> bool {
193        if self.metric().type_id() != metric.type_id() {
194            return false;
195        }
196
197        let a = self.metric() as *const _ as *const ();
198        let b = metric as *const _ as *const ();
199        a == b
200    }
201
202    /// Request a value of type `T` from the metric.
203    ///
204    /// This will succeed if the metric's [`provide`] implementation called
205    /// [`Request::provide_value`] with a value of type `T`.
206    ///
207    /// [`provide`]: Metric::provide
208    pub fn request_value<T>(&self) -> Option<T>
209    where
210        T: 'static,
211    {
212        crate::request_value(self.metric())
213    }
214
215    /// Request a reference of type `T` from the metric.
216    ///
217    /// This will succeed if the metric's [`provide`] implementation called
218    /// [`Request::provide_ref`] with a value of type `T`.
219    ///
220    /// [`provide`]: Metric::provide
221    pub fn request_ref<T>(&self) -> Option<&T>
222    where
223        T: ?Sized + 'static,
224    {
225        crate::request_ref(self.metric())
226    }
227}
228
229unsafe impl Send for MetricEntry {}
230unsafe impl Sync for MetricEntry {}
231
232impl std::ops::Deref for MetricEntry {
233    type Target = dyn Metric;
234
235    #[inline]
236    fn deref(&self) -> &Self::Target {
237        self.metric()
238    }
239}
240
241impl std::fmt::Debug for MetricEntry {
242    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
243        f.debug_struct("MetricEntry")
244            .field("name", &self.name())
245            .field("metric", &"<dyn Metric>")
246            .finish()
247    }
248}
249
250/// Implementation detail exports for use by the `#[metric]`
251#[doc(hidden)]
252pub mod export {
253    use crate::{Metadata, Metric};
254
255    pub extern crate linkme;
256    pub extern crate phf;
257
258    pub use crate::wrapper::*;
259
260    #[linkme::distributed_slice]
261    pub static METRICS: [crate::MetricEntry] = [..];
262
263    pub const fn entry_v1(
264        metric: &'static dyn Metric,
265        name: &'static str,
266        description: Option<&'static str>,
267        module: &'static str,
268    ) -> crate::MetricEntry {
269        use std::borrow::Cow;
270
271        crate::MetricEntry {
272            metric,
273            name: Cow::Borrowed(name),
274            description: match description {
275                Some(desc) => Some(Cow::Borrowed(desc)),
276                None => None,
277            },
278            module: Cow::Borrowed(module),
279        }
280    }
281
282    pub const fn metadata(metadata: &'static phf::Map<&'static str, &'static str>) -> Metadata {
283        Metadata::new_static(metadata)
284    }
285}
286
287/// Declare a new metric.
288#[macro_export]
289macro_rules! declare_metric_v1 {
290    {
291        metric: $metric:expr,
292        name: $name:expr,
293        description: $description:expr,
294        module: $module:expr,
295        metadata: { $( $key:expr => $value:expr ),* $(,)? },
296        formatter: $formatter:expr $(,)?
297    } => {
298        const _: () = {
299            use $crate::export::phf;
300
301            static __METADATA_MAP: $crate::export::phf::Map<&'static str, &'static str> =
302                $crate::export::phf::phf_map! { $( $key => $value, )* };
303            static __METADATA: $crate::Metadata = $crate::export::metadata(&__METADATA_MAP);
304
305            // We use this to inject some provided values into metric itself
306            // without having to use up extra memory storing anything.
307            struct MetricProvider;
308
309            impl $crate::export::InjectedProvider for MetricProvider {
310                fn provide(request: &mut $crate::Request<'_>) {
311                    request
312                        .provide_ref(&__METADATA)
313                        .provide_value($crate::export::FormattingFn($formatter));
314                }
315            }
316
317            #[$crate::export::linkme::distributed_slice($crate::export::METRICS)]
318            #[linkme(crate = $crate::export::linkme)]
319            static __ENTRY: $crate::MetricEntry = $crate::export::entry_v1(
320                $crate::export::MetricWrapper::<_, MetricProvider>::from_ref(&$metric),
321                $name,
322                $description,
323                $module,
324            );
325        };
326    }
327}