Skip to main content

metrics/
common.rs

1use std::hash::Hasher;
2
3use rapidhash::fast::RapidHasher;
4
5use crate::cow::Cow;
6
7/// An allocation-optimized string.
8///
9/// `SharedString` uses a custom copy-on-write implementation that is optimized for metric keys,
10/// providing ergonomic sharing of single instances, or slices, of strings and labels. This
11/// copy-on-write implementation is optimized to allow for constant-time construction (using static
12/// values), as well as accepting owned values and values shared through [`Arc<T>`](std::sync::Arc).
13///
14/// End users generally will not need to interact with this type directly, as the top-level macros
15/// (`counter!`, etc), as well as the various conversion implementations
16/// ([`From<T>`](std::convert::From)), generally allow users to pass whichever variant of a value
17/// (static, owned, shared) is best for them.
18pub type SharedString = Cow<'static, str>;
19
20/// Key-specific hashing algorithm.
21///
22/// Deprecated in favor of a no-hash based implementation in `metrics-util::common::KeyHasher`.
23///
24/// This hasher operates in two modes. When only `write_u64` is called — the path taken by
25/// [`Key`][crate::Key]'s [`Hash`][std::hash::Hash] impl, which writes the pre-computed key hash
26/// via `write_u64(self.hash)` — `finish` returns that value verbatim, matching `Hashable::hashable(&key)`
27/// and the no-op `metrics_util::common::KeyHasher`. When arbitrary bytes are written via `write`
28/// (or any of the typed `write_*` methods that route through `write`), this hasher falls back to
29/// hashing with rapidhash - <https://github.com/hoxxep/rapidhash> - preserving the previous
30/// byte-hashing behavior for callers like `metrics_util::DefaultHashable<H>` in `metrics-util 0.19.x`.
31#[deprecated(since = "0.24.4", note = "Use `metrics-util::common::KeyHasher` instead.")]
32pub struct KeyHasher(KeyHasherState);
33
34enum KeyHasherState {
35    Empty,
36    PreHashed(u64),
37    Bytes(RapidHasher<'static>),
38}
39
40#[allow(deprecated)]
41impl std::fmt::Debug for KeyHasher {
42    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
43        f.debug_struct("KeyHasher").finish_non_exhaustive()
44    }
45}
46
47#[allow(deprecated)]
48impl Default for KeyHasher {
49    fn default() -> Self {
50        KeyHasher(KeyHasherState::Empty)
51    }
52}
53
54#[allow(deprecated)]
55impl Hasher for KeyHasher {
56    fn finish(&self) -> u64 {
57        match &self.0 {
58            KeyHasherState::Empty => 0,
59            KeyHasherState::PreHashed(h) => *h,
60            KeyHasherState::Bytes(h) => h.finish(),
61        }
62    }
63
64    fn write(&mut self, bytes: &[u8]) {
65        // Any byte write transitions to byte mode. If a prior `write_u64` had stored a
66        // pre-hashed value, fold it into the byte hasher first so multi-write hashing
67        // remains well-defined and deterministic.
68        let mut hasher = match std::mem::replace(&mut self.0, KeyHasherState::Empty) {
69            KeyHasherState::Empty => RapidHasher::default_const(),
70            KeyHasherState::PreHashed(prior) => {
71                let mut h = RapidHasher::default_const();
72                h.write_u64(prior);
73                h
74            }
75            KeyHasherState::Bytes(h) => h,
76        };
77        hasher.write(bytes);
78        self.0 = KeyHasherState::Bytes(hasher);
79    }
80
81    fn write_u64(&mut self, i: u64) {
82        // The pre-hashed fast path: store the value directly so `finish` returns it verbatim.
83        // If we've already transitioned to byte mode, stay there.
84        self.0 = match std::mem::replace(&mut self.0, KeyHasherState::Empty) {
85            KeyHasherState::Empty | KeyHasherState::PreHashed(_) => KeyHasherState::PreHashed(i),
86            KeyHasherState::Bytes(mut h) => {
87                h.write_u64(i);
88                KeyHasherState::Bytes(h)
89            }
90        };
91    }
92}
93
94/// Value of a gauge operation.
95#[derive(Clone, Debug)]
96pub enum GaugeValue {
97    /// Sets the value of the gauge to this value.
98    Absolute(f64),
99    /// Increments the value of the gauge by this much.
100    Increment(f64),
101    /// Decrements the value of the gauge by this much.
102    Decrement(f64),
103}
104
105impl GaugeValue {
106    /// Updates an input value based on this gauge value.
107    pub fn update_value(&self, input: f64) -> f64 {
108        match self {
109            GaugeValue::Absolute(val) => *val,
110            GaugeValue::Increment(val) => input + val,
111            GaugeValue::Decrement(val) => input - val,
112        }
113    }
114}
115
116/// Units for a given metric.
117///
118/// While metrics do not necessarily need to be tied to a particular unit to be recorded, some
119/// downstream systems natively support defining units and so they can be specified during registration.
120#[derive(Clone, Copy, Debug, Eq, PartialEq, Hash)]
121pub enum Unit {
122    /// Count.
123    Count,
124    /// Percentage.
125    Percent,
126    /// Seconds.
127    ///
128    /// One second is equal to 1000 milliseconds.
129    Seconds,
130    /// Milliseconds.
131    ///
132    /// One millisecond is equal to 1000 microseconds.
133    Milliseconds,
134    /// Microseconds.
135    ///
136    /// One microsecond is equal to 1000 nanoseconds.
137    Microseconds,
138    /// Nanoseconds.
139    Nanoseconds,
140    /// Tebibytes.
141    ///
142    /// One tebibyte is equal to 1024 gibibytes.
143    Tebibytes,
144    /// Gibibytes.
145    ///
146    /// One gibibyte is equal to 1024 mebibytes.
147    Gibibytes,
148    /// Mebibytes.
149    ///
150    /// One mebibyte is equal to 1024 kibibytes.
151    Mebibytes,
152    /// Kibibytes.
153    ///
154    /// One kibibyte is equal to 1024 bytes.
155    Kibibytes,
156    /// Bytes.
157    Bytes,
158    /// Terabits per second.
159    ///
160    /// One terabit is equal to 1000 gigabits.
161    TerabitsPerSecond,
162    /// Gigabits per second.
163    ///
164    /// One gigabit is equal to 1000 megabits.
165    GigabitsPerSecond,
166    /// Megabits per second.
167    ///
168    /// One megabit is equal to 1000 kilobits.
169    MegabitsPerSecond,
170    /// Kilobits per second.
171    ///
172    /// One kilobit is equal to 1000 bits.
173    KilobitsPerSecond,
174    /// Bits per second.
175    BitsPerSecond,
176    /// Count per second.
177    CountPerSecond,
178}
179
180impl Unit {
181    /// Gets the string form of this `Unit`.
182    pub fn as_str(&self) -> &'static str {
183        match self {
184            Unit::Count => "count",
185            Unit::Percent => "percent",
186            Unit::Seconds => "seconds",
187            Unit::Milliseconds => "milliseconds",
188            Unit::Microseconds => "microseconds",
189            Unit::Nanoseconds => "nanoseconds",
190            Unit::Tebibytes => "tebibytes",
191            Unit::Gibibytes => "gibibytes",
192            Unit::Mebibytes => "mebibytes",
193            Unit::Kibibytes => "kibibytes",
194            Unit::Bytes => "bytes",
195            Unit::TerabitsPerSecond => "terabits_per_second",
196            Unit::GigabitsPerSecond => "gigabits_per_second",
197            Unit::MegabitsPerSecond => "megabits_per_second",
198            Unit::KilobitsPerSecond => "kilobits_per_second",
199            Unit::BitsPerSecond => "bits_per_second",
200            Unit::CountPerSecond => "count_per_second",
201        }
202    }
203
204    /// Gets the canonical string label for the given unit.
205    ///
206    /// For example, the canonical label for `Seconds` would be `s`, while for `Nanoseconds`,
207    /// it would be `ns`.
208    ///
209    /// Not all units have a meaningful display label and so some may be empty.
210    pub fn as_canonical_label(&self) -> &'static str {
211        match self {
212            Unit::Count => "",
213            Unit::Percent => "%",
214            Unit::Seconds => "s",
215            Unit::Milliseconds => "ms",
216            Unit::Microseconds => "μs",
217            Unit::Nanoseconds => "ns",
218            Unit::Tebibytes => "TiB",
219            Unit::Gibibytes => "GiB",
220            Unit::Mebibytes => "MiB",
221            Unit::Kibibytes => "KiB",
222            Unit::Bytes => "B",
223            Unit::TerabitsPerSecond => "Tbps",
224            Unit::GigabitsPerSecond => "Gbps",
225            Unit::MegabitsPerSecond => "Mbps",
226            Unit::KilobitsPerSecond => "kbps",
227            Unit::BitsPerSecond => "bps",
228            Unit::CountPerSecond => "/s",
229        }
230    }
231
232    /// Converts the string representation of a unit back into `Unit` if possible.
233    ///
234    /// The value passed here should match the output of [`Unit::as_str`].
235    pub fn from_string(s: &str) -> Option<Unit> {
236        match s {
237            "count" => Some(Unit::Count),
238            "percent" => Some(Unit::Percent),
239            "seconds" => Some(Unit::Seconds),
240            "milliseconds" => Some(Unit::Milliseconds),
241            "microseconds" => Some(Unit::Microseconds),
242            "nanoseconds" => Some(Unit::Nanoseconds),
243            "tebibytes" => Some(Unit::Tebibytes),
244            "gibibytes" => Some(Unit::Gibibytes),
245            "mebibytes" => Some(Unit::Mebibytes),
246            "kibibytes" => Some(Unit::Kibibytes),
247            "bytes" => Some(Unit::Bytes),
248            "terabits_per_second" => Some(Unit::TerabitsPerSecond),
249            "gigabits_per_second" => Some(Unit::GigabitsPerSecond),
250            "megabits_per_second" => Some(Unit::MegabitsPerSecond),
251            "kilobits_per_second" => Some(Unit::KilobitsPerSecond),
252            "bits_per_second" => Some(Unit::BitsPerSecond),
253            "count_per_second" => Some(Unit::CountPerSecond),
254            _ => None,
255        }
256    }
257
258    /// Whether or not this unit relates to the measurement of time.
259    pub fn is_time_based(&self) -> bool {
260        matches!(self, Unit::Seconds | Unit::Milliseconds | Unit::Microseconds | Unit::Nanoseconds)
261    }
262
263    /// Whether or not this unit relates to the measurement of data.
264    pub fn is_data_based(&self) -> bool {
265        matches!(
266            self,
267            Unit::Tebibytes
268                | Unit::Gibibytes
269                | Unit::Mebibytes
270                | Unit::Kibibytes
271                | Unit::Bytes
272                | Unit::TerabitsPerSecond
273                | Unit::GigabitsPerSecond
274                | Unit::MegabitsPerSecond
275                | Unit::KilobitsPerSecond
276                | Unit::BitsPerSecond
277        )
278    }
279
280    /// Whether or not this unit relates to the measurement of data rates.
281    pub fn is_data_rate_based(&self) -> bool {
282        matches!(
283            self,
284            Unit::TerabitsPerSecond
285                | Unit::GigabitsPerSecond
286                | Unit::MegabitsPerSecond
287                | Unit::KilobitsPerSecond
288                | Unit::BitsPerSecond
289        )
290    }
291}
292
293/// An object which can be converted into a `f64` representation.
294///
295/// This trait provides a mechanism for existing types, which have a natural representation
296/// as a 64-bit floating-point number, to be transparently passed in when recording a histogram.
297pub trait IntoF64 {
298    /// Converts this object to its `f64` representation.
299    fn into_f64(self) -> f64;
300}
301
302impl IntoF64 for f64 {
303    fn into_f64(self) -> f64 {
304        self
305    }
306}
307
308impl IntoF64 for core::time::Duration {
309    fn into_f64(self) -> f64 {
310        self.as_secs_f64()
311    }
312}
313
314into_f64!(i8, u8, i16, u16, i32, u32, f32);
315
316/// Helper method to allow monomorphization of values passed to the `histogram!` macro.
317#[doc(hidden)]
318pub fn __into_f64<V: IntoF64>(value: V) -> f64 {
319    value.into_f64()
320}
321
322macro_rules! into_f64 {
323    ($($ty:ty),*) => {
324        $(
325            impl IntoF64 for $ty {
326                fn into_f64(self) -> f64 {
327                    f64::from(self)
328                }
329            }
330        )*
331    };
332}
333
334use into_f64;
335
336#[cfg(test)]
337mod tests {
338    use std::time::Duration;
339
340    use super::{IntoF64, Unit};
341
342    #[test]
343    fn test_unit_conversions() {
344        let all_variants = vec![
345            Unit::Count,
346            Unit::Percent,
347            Unit::Seconds,
348            Unit::Milliseconds,
349            Unit::Microseconds,
350            Unit::Nanoseconds,
351            Unit::Tebibytes,
352            Unit::Gibibytes,
353            Unit::Mebibytes,
354            Unit::Kibibytes,
355            Unit::Bytes,
356            Unit::TerabitsPerSecond,
357            Unit::GigabitsPerSecond,
358            Unit::MegabitsPerSecond,
359            Unit::KilobitsPerSecond,
360            Unit::BitsPerSecond,
361            Unit::CountPerSecond,
362        ];
363
364        for variant in all_variants {
365            let s = variant.as_str();
366            let parsed = Unit::from_string(s);
367            assert_eq!(Some(variant), parsed);
368        }
369    }
370
371    #[test]
372    fn into_f64() {
373        fn test<T: IntoF64>(val: T) {
374            assert!(!val.into_f64().is_nan());
375        }
376
377        test::<i8>(1);
378        test::<u8>(1);
379        test::<i16>(1);
380        test::<u16>(1);
381        test::<i32>(1);
382        test::<u32>(1);
383        test::<f32>(1.0);
384        test::<f64>(1.0);
385        test::<Duration>(Duration::from_secs(1));
386    }
387}