metrics/
common.rs

1use std::hash::Hasher;
2
3use ahash::AHasher;
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/// Currently uses AHash - <https://github.com/tkaitchuck/aHash>
23///
24/// For any use-case within a `metrics`-owned or adjacent crate, where hashing of a key is required,
25/// this is the hasher that will be used.
26#[derive(Default)]
27pub struct KeyHasher(AHasher);
28
29impl Hasher for KeyHasher {
30    fn finish(&self) -> u64 {
31        self.0.finish()
32    }
33
34    fn write(&mut self, bytes: &[u8]) {
35        self.0.write(bytes)
36    }
37}
38
39/// Value of a gauge operation.
40#[derive(Clone, Debug)]
41pub enum GaugeValue {
42    /// Sets the value of the gauge to this value.
43    Absolute(f64),
44    /// Increments the value of the gauge by this much.
45    Increment(f64),
46    /// Decrements the value of the gauge by this much.
47    Decrement(f64),
48}
49
50impl GaugeValue {
51    /// Updates an input value based on this gauge value.
52    pub fn update_value(&self, input: f64) -> f64 {
53        match self {
54            GaugeValue::Absolute(val) => *val,
55            GaugeValue::Increment(val) => input + val,
56            GaugeValue::Decrement(val) => input - val,
57        }
58    }
59}
60
61/// Units for a given metric.
62///
63/// While metrics do not necessarily need to be tied to a particular unit to be recorded, some
64/// downstream systems natively support defining units and so they can be specified during registration.
65#[derive(Clone, Copy, Debug, Eq, PartialEq)]
66pub enum Unit {
67    /// Count.
68    Count,
69    /// Percentage.
70    Percent,
71    /// Seconds.
72    ///
73    /// One second is equal to 1000 milliseconds.
74    Seconds,
75    /// Milliseconds.
76    ///
77    /// One millisecond is equal to 1000 microseconds.
78    Milliseconds,
79    /// Microseconds.
80    ///
81    /// One microsecond is equal to 1000 nanoseconds.
82    Microseconds,
83    /// Nanoseconds.
84    Nanoseconds,
85    /// Tebibytes.
86    ///
87    /// One tebibyte is equal to 1024 gigibytes.
88    Tebibytes,
89    /// Gigibytes.
90    ///
91    /// One gigibyte is equal to 1024 mebibytes.
92    Gigibytes,
93    /// Mebibytes.
94    ///
95    /// One mebibyte is equal to 1024 kibibytes.
96    Mebibytes,
97    /// Kibibytes.
98    ///
99    /// One kibibyte is equal to 1024 bytes.
100    Kibibytes,
101    /// Bytes.
102    Bytes,
103    /// Terabits per second.
104    ///
105    /// One terabit is equal to 1000 gigabits.
106    TerabitsPerSecond,
107    /// Gigabits per second.
108    ///
109    /// One gigabit is equal to 1000 megabits.
110    GigabitsPerSecond,
111    /// Megabits per second.
112    ///
113    /// One megabit is equal to 1000 kilobits.
114    MegabitsPerSecond,
115    /// Kilobits per second.
116    ///
117    /// One kilobit is equal to 1000 bits.
118    KilobitsPerSecond,
119    /// Bits per second.
120    BitsPerSecond,
121    /// Count per second.
122    CountPerSecond,
123}
124
125impl Unit {
126    /// Gets the string form of this `Unit`.
127    pub fn as_str(&self) -> &'static str {
128        match self {
129            Unit::Count => "count",
130            Unit::Percent => "percent",
131            Unit::Seconds => "seconds",
132            Unit::Milliseconds => "milliseconds",
133            Unit::Microseconds => "microseconds",
134            Unit::Nanoseconds => "nanoseconds",
135            Unit::Tebibytes => "tebibytes",
136            Unit::Gigibytes => "gigibytes",
137            Unit::Mebibytes => "mebibytes",
138            Unit::Kibibytes => "kibibytes",
139            Unit::Bytes => "bytes",
140            Unit::TerabitsPerSecond => "terabits_per_second",
141            Unit::GigabitsPerSecond => "gigabits_per_second",
142            Unit::MegabitsPerSecond => "megabits_per_second",
143            Unit::KilobitsPerSecond => "kilobits_per_second",
144            Unit::BitsPerSecond => "bits_per_second",
145            Unit::CountPerSecond => "count_per_second",
146        }
147    }
148
149    /// Gets the canonical string label for the given unit.
150    ///
151    /// For example, the canonical label for `Seconds` would be `s`, while for `Nanoseconds`,
152    /// it would be `ns`.
153    ///
154    /// Not all units have a meaningful display label and so some may be empty.
155    pub fn as_canonical_label(&self) -> &'static str {
156        match self {
157            Unit::Count => "",
158            Unit::Percent => "%",
159            Unit::Seconds => "s",
160            Unit::Milliseconds => "ms",
161            Unit::Microseconds => "μs",
162            Unit::Nanoseconds => "ns",
163            Unit::Tebibytes => "TiB",
164            Unit::Gigibytes => "GiB",
165            Unit::Mebibytes => "MiB",
166            Unit::Kibibytes => "KiB",
167            Unit::Bytes => "B",
168            Unit::TerabitsPerSecond => "Tbps",
169            Unit::GigabitsPerSecond => "Gbps",
170            Unit::MegabitsPerSecond => "Mbps",
171            Unit::KilobitsPerSecond => "kbps",
172            Unit::BitsPerSecond => "bps",
173            Unit::CountPerSecond => "/s",
174        }
175    }
176
177    /// Converts the string representation of a unit back into `Unit` if possible.
178    ///
179    /// The value passed here should match the output of [`Unit::as_str`].
180    pub fn from_string(s: &str) -> Option<Unit> {
181        match s {
182            "count" => Some(Unit::Count),
183            "percent" => Some(Unit::Percent),
184            "seconds" => Some(Unit::Seconds),
185            "milliseconds" => Some(Unit::Milliseconds),
186            "microseconds" => Some(Unit::Microseconds),
187            "nanoseconds" => Some(Unit::Nanoseconds),
188            "tebibytes" => Some(Unit::Tebibytes),
189            "gigibytes" => Some(Unit::Gigibytes),
190            "mebibytes" => Some(Unit::Mebibytes),
191            "kibibytes" => Some(Unit::Kibibytes),
192            "bytes" => Some(Unit::Bytes),
193            "terabits_per_second" => Some(Unit::TerabitsPerSecond),
194            "gigabits_per_second" => Some(Unit::GigabitsPerSecond),
195            "megabits_per_second" => Some(Unit::MegabitsPerSecond),
196            "kilobits_per_second" => Some(Unit::KilobitsPerSecond),
197            "bits_per_second" => Some(Unit::BitsPerSecond),
198            "count_per_second" => Some(Unit::CountPerSecond),
199            _ => None,
200        }
201    }
202
203    /// Whether or not this unit relates to the measurement of time.
204    pub fn is_time_based(&self) -> bool {
205        matches!(self, Unit::Seconds | Unit::Milliseconds | Unit::Microseconds | Unit::Nanoseconds)
206    }
207
208    /// Whether or not this unit relates to the measurement of data.
209    pub fn is_data_based(&self) -> bool {
210        matches!(
211            self,
212            Unit::Tebibytes
213                | Unit::Gigibytes
214                | Unit::Mebibytes
215                | Unit::Kibibytes
216                | Unit::Bytes
217                | Unit::TerabitsPerSecond
218                | Unit::GigabitsPerSecond
219                | Unit::MegabitsPerSecond
220                | Unit::KilobitsPerSecond
221                | Unit::BitsPerSecond
222        )
223    }
224
225    /// Whether or not this unit relates to the measurement of data rates.
226    pub fn is_data_rate_based(&self) -> bool {
227        matches!(
228            self,
229            Unit::TerabitsPerSecond
230                | Unit::GigabitsPerSecond
231                | Unit::MegabitsPerSecond
232                | Unit::KilobitsPerSecond
233                | Unit::BitsPerSecond
234        )
235    }
236}
237
238/// An object which can be converted into a `f64` representation.
239///
240/// This trait provides a mechanism for existing types, which have a natural representation
241/// as a 64-bit floating-point number, to be transparently passed in when recording a histogram.
242pub trait IntoF64 {
243    /// Converts this object to its `f64` representation.
244    fn into_f64(self) -> f64;
245}
246
247impl IntoF64 for f64 {
248    fn into_f64(self) -> f64 {
249        self
250    }
251}
252
253impl IntoF64 for core::time::Duration {
254    fn into_f64(self) -> f64 {
255        self.as_secs_f64()
256    }
257}
258
259into_f64!(i8, u8, i16, u16, i32, u32, f32);
260
261/// Helper method to allow monomorphization of values passed to the `histogram!` macro.
262#[doc(hidden)]
263pub fn __into_f64<V: IntoF64>(value: V) -> f64 {
264    value.into_f64()
265}
266
267macro_rules! into_f64 {
268    ($($ty:ty),*) => {
269        $(
270            impl IntoF64 for $ty {
271                fn into_f64(self) -> f64 {
272                    f64::from(self)
273                }
274            }
275        )*
276    };
277}
278
279pub(self) use into_f64;
280
281#[cfg(test)]
282mod tests {
283    use std::time::Duration;
284
285    use super::{IntoF64, Unit};
286
287    #[test]
288    fn test_unit_conversions() {
289        let all_variants = vec![
290            Unit::Count,
291            Unit::Percent,
292            Unit::Seconds,
293            Unit::Milliseconds,
294            Unit::Microseconds,
295            Unit::Nanoseconds,
296            Unit::Tebibytes,
297            Unit::Gigibytes,
298            Unit::Mebibytes,
299            Unit::Kibibytes,
300            Unit::Bytes,
301            Unit::TerabitsPerSecond,
302            Unit::GigabitsPerSecond,
303            Unit::MegabitsPerSecond,
304            Unit::KilobitsPerSecond,
305            Unit::BitsPerSecond,
306            Unit::CountPerSecond,
307        ];
308
309        for variant in all_variants {
310            let s = variant.as_str();
311            let parsed = Unit::from_string(s);
312            assert_eq!(Some(variant), parsed);
313        }
314    }
315
316    #[test]
317    fn into_f64() {
318        fn test<T: IntoF64>(val: T) {
319            assert!(!val.into_f64().is_nan());
320        }
321
322        test::<i8>(1);
323        test::<u8>(1);
324        test::<i16>(1);
325        test::<u16>(1);
326        test::<i32>(1);
327        test::<u32>(1);
328        test::<f32>(1.0);
329        test::<f64>(1.0);
330        test::<Duration>(Duration::from_secs(1));
331    }
332}