Skip to main content

fast_telemetry/metric/dynamic/
mod.rs

1//! Runtime-labeled metrics for dynamic dimensions.
2//!
3//! Use these types when label values are not known at compile time
4//! (e.g. `endpoint_uuid`, `org_id`, `user_id`).
5//!
6//! Each type provides:
7//! - Thread-local caching for hot paths
8//! - Sharded index for concurrent access
9//! - Label canonicalization (order-independent)
10//! - Series handles for zero-lookup repeated access
11//! - Access-timestamp eviction for bounded cardinality
12
13mod counter;
14mod distribution;
15mod gauge;
16mod gauge_i64;
17mod histogram;
18
19pub use counter::{DynamicCounter, DynamicCounterSeries};
20pub use distribution::{DynamicDistribution, DynamicDistributionSeries};
21pub use gauge::{DynamicGauge, DynamicGaugeSeries};
22pub use gauge_i64::{DynamicGaugeI64, DynamicGaugeI64Series};
23pub use histogram::{DynamicHistogram, DynamicHistogramSeries, DynamicHistogramSeriesView};
24
25use std::collections::BTreeMap;
26use std::sync::atomic::AtomicUsize;
27#[cfg(feature = "eviction")]
28use std::sync::atomic::{AtomicU32, Ordering};
29
30pub(crate) use crate::thread_id::thread_id;
31
32#[cfg(feature = "eviction")]
33static EVICTION_CYCLE: AtomicU32 = AtomicU32::new(0);
34
35/// Get the current eviction cycle.
36#[cfg(feature = "eviction")]
37#[inline]
38pub fn current_cycle() -> u32 {
39    EVICTION_CYCLE.load(Ordering::Relaxed)
40}
41
42/// Advance the eviction cycle by 1 and return the new value.
43///
44/// Call this from your exporter task before calling `evict_stale()` on metrics.
45#[cfg(feature = "eviction")]
46#[inline]
47pub fn advance_cycle() -> u32 {
48    EVICTION_CYCLE.fetch_add(1, Ordering::Relaxed) + 1
49}
50
51/// Canonicalized runtime label set.
52///
53/// Labels are deduplicated by key (last value wins) and sorted by key to ensure
54/// stable identity regardless of input order.
55#[derive(Clone, Debug, Eq, PartialEq, Hash)]
56pub struct DynamicLabelSet {
57    labels: Vec<(String, String)>,
58}
59
60impl DynamicLabelSet {
61    /// Build a canonical label set from borrowed key/value pairs.
62    pub fn from_pairs(labels: &[(&str, &str)]) -> Self {
63        let mut map = BTreeMap::new();
64        for (k, v) in labels {
65            map.insert((*k).to_string(), (*v).to_string());
66        }
67        Self {
68            labels: map.into_iter().collect(),
69        }
70    }
71
72    /// Build from already-canonicalized owned pairs.
73    ///
74    /// The input must already be sorted by key and deduplicated.
75    #[doc(hidden)]
76    pub fn from_canonical_pairs(labels: &[(String, String)]) -> Self {
77        Self {
78            labels: labels.to_vec(),
79        }
80    }
81
82    /// Returns labels as ordered `(key, value)` pairs.
83    pub fn pairs(&self) -> &[(String, String)] {
84        &self.labels
85    }
86}
87
88// Shared ID generators
89pub(crate) static COUNTER_IDS: AtomicUsize = AtomicUsize::new(1);
90pub(crate) static GAUGE_IDS: AtomicUsize = AtomicUsize::new(1);
91pub(crate) static HISTOGRAM_IDS: AtomicUsize = AtomicUsize::new(1);
92pub(crate) static DISTRIBUTION_IDS: AtomicUsize = AtomicUsize::new(1);