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 cache;
14mod counter;
15mod distribution;
16mod gauge;
17mod gauge_i64;
18mod histogram;
19
20pub use counter::{DynamicCounter, DynamicCounterSeries};
21pub use distribution::{DynamicDistribution, DynamicDistributionSeries};
22pub use gauge::{DynamicGauge, DynamicGaugeSeries};
23pub use gauge_i64::{DynamicGaugeI64, DynamicGaugeI64Series};
24pub use histogram::{DynamicHistogram, DynamicHistogramSeries, DynamicHistogramSeriesView};
25
26use std::collections::BTreeMap;
27use std::sync::atomic::AtomicUsize;
28#[cfg(feature = "eviction")]
29use std::sync::atomic::{AtomicU32, Ordering};
30
31pub(crate) use crate::thread_id::thread_id;
32
33#[cfg(feature = "eviction")]
34static EVICTION_CYCLE: AtomicU32 = AtomicU32::new(0);
35
36/// Get the current eviction cycle.
37#[cfg(feature = "eviction")]
38#[inline]
39pub fn current_cycle() -> u32 {
40    EVICTION_CYCLE.load(Ordering::Relaxed)
41}
42
43/// Advance the eviction cycle by 1 and return the new value.
44///
45/// Call this from your exporter task before calling `evict_stale()` on metrics.
46#[cfg(feature = "eviction")]
47#[inline]
48pub fn advance_cycle() -> u32 {
49    EVICTION_CYCLE.fetch_add(1, Ordering::Relaxed) + 1
50}
51
52/// Canonicalized runtime label set.
53///
54/// Labels are deduplicated by key (last value wins) and sorted by key to ensure
55/// stable identity regardless of input order.
56#[derive(Clone, Debug, Eq, PartialEq, Hash)]
57pub struct DynamicLabelSet {
58    labels: Vec<(String, String)>,
59}
60
61impl DynamicLabelSet {
62    /// Build a canonical label set from borrowed key/value pairs.
63    pub fn from_pairs(labels: &[(&str, &str)]) -> Self {
64        let mut map = BTreeMap::new();
65        for (k, v) in labels {
66            map.insert((*k).to_string(), (*v).to_string());
67        }
68        Self {
69            labels: map.into_iter().collect(),
70        }
71    }
72
73    /// Build from already-canonicalized owned pairs.
74    ///
75    /// The input must already be sorted by key and deduplicated.
76    #[doc(hidden)]
77    pub fn from_canonical_pairs(labels: &[(String, String)]) -> Self {
78        Self {
79            labels: labels.to_vec(),
80        }
81    }
82
83    /// Returns labels as ordered `(key, value)` pairs.
84    pub fn pairs(&self) -> &[(String, String)] {
85        &self.labels
86    }
87}
88
89// Shared ID generators
90pub(crate) static COUNTER_IDS: AtomicUsize = AtomicUsize::new(1);
91pub(crate) static GAUGE_IDS: AtomicUsize = AtomicUsize::new(1);
92pub(crate) static HISTOGRAM_IDS: AtomicUsize = AtomicUsize::new(1);
93pub(crate) static DISTRIBUTION_IDS: AtomicUsize = AtomicUsize::new(1);