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}