hotmic/data/
mod.rs

1use std::{
2    fmt::{self, Display},
3    hash::Hash,
4};
5
6pub mod counter;
7pub mod gauge;
8pub mod histogram;
9pub mod snapshot;
10
11pub(crate) use self::{counter::Counter, gauge::Gauge, histogram::Histogram, snapshot::Snapshot};
12
13/// A measurement.
14///
15/// Samples are the decoupled way of submitting data into the sink.
16#[derive(Debug)]
17pub(crate) enum Sample<T> {
18    /// A counter delta.
19    ///
20    /// The value is added directly to the existing counter, and so negative deltas will decrease
21    /// the counter, and positive deltas will increase the counter.
22    Count(T, i64),
23
24    /// A single value, also known as a gauge.
25    ///
26    /// Values operate in last-write-wins mode.
27    ///
28    /// Values themselves cannot be incremented or decremented, so you must hold them externally
29    /// before sending them.
30    Gauge(T, u64),
31
32    /// A timed sample.
33    ///
34    /// Includes the start and end times, as well as a count field.
35    ///
36    /// The count field can represent amounts integral to the event, such as the number of bytes
37    /// processed in the given time delta.
38    TimingHistogram(T, u64, u64, u64),
39
40    /// A single value measured over time.
41    ///
42    /// Unlike a gauge, where the value is only ever measured at a point in time, value histogram
43    /// measure values over time, and their distribution.  This is nearly identical to timing
44    /// histograms, since the end result is just a single number, but we don't spice it up with
45    /// special unit labels or anything.
46    ValueHistogram(T, u64),
47}
48
49/// An integer scoped metric key.
50#[derive(Clone, Hash, PartialEq, Eq, Debug)]
51pub(crate) struct ScopedKey<T: Clone + Eq + Hash + Display>(u64, T);
52
53impl<T: Clone + Eq + Hash + Display> ScopedKey<T> {
54    pub(crate) fn id(&self) -> u64 { self.0 }
55
56    pub(crate) fn into_string_scoped(self, scope: String) -> StringScopedKey<T> { StringScopedKey(scope, self.1) }
57}
58
59/// A string scoped metric key.
60#[derive(Clone, Hash, PartialEq, Eq, Debug)]
61pub(crate) struct StringScopedKey<T: Clone + Eq + Hash + Display>(String, T);
62
63impl<T: Clone + Hash + Eq + Display> Display for StringScopedKey<T> {
64    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
65        if self.0.is_empty() {
66            write!(f, "{}", self.1)
67        } else {
68            write!(f, "{}.{}", self.0, self.1)
69        }
70    }
71}
72
73impl<T: Clone + Eq + Hash + Display> Sample<T> {
74    pub(crate) fn into_scoped(self, scope_id: u64) -> Sample<ScopedKey<T>> {
75        match self {
76            Sample::Count(key, value) => Sample::Count(ScopedKey(scope_id, key), value),
77            Sample::Gauge(key, value) => Sample::Gauge(ScopedKey(scope_id, key), value),
78            Sample::TimingHistogram(key, start, end, count) => {
79                Sample::TimingHistogram(ScopedKey(scope_id, key), start, end, count)
80            },
81            Sample::ValueHistogram(key, count) => Sample::ValueHistogram(ScopedKey(scope_id, key), count),
82        }
83    }
84}
85
86/// A labeled percentile.
87///
88/// This represents a floating-point value from 0 to 100, with a string label to be used for
89/// displaying the given percentile.
90#[derive(Derivative, Debug, Clone)]
91#[derivative(Hash, PartialEq)]
92pub struct Percentile {
93    label: String,
94
95    #[derivative(Hash = "ignore")]
96    #[derivative(PartialEq = "ignore")]
97    value: f64,
98}
99
100impl Percentile {
101    /// Gets the standardized label for this percentile value.
102    ///
103    /// This follows the convention of `pXXX`, where `xxx` represents the percentage.  For example,
104    /// for the 99th percentile, you would get `p99`.  For the 99.9th percentile, you would get `p999`.
105    pub fn label(&self) -> &str { self.label.as_str() }
106
107    /// Gets the actual percentile value.
108    pub fn percentile(&self) -> f64 { self.value }
109
110    /// Gets the percentile value as a quantile.
111    pub fn as_quantile(&self) -> f64 { self.value / 100.0 }
112}
113
114impl Eq for Percentile {}
115
116impl From<f64> for Percentile {
117    fn from(p: f64) -> Self {
118        // Force our value between +0.0 and +100.0.
119        let clamped = p.max(0.0);
120        let clamped = clamped.min(100.0);
121
122        let raw_label = format!("{}", clamped);
123        let label = match raw_label.as_str() {
124            "0" => "min".to_string(),
125            "100" => "max".to_string(),
126            _ => {
127                let raw = format!("p{}", clamped);
128                raw.replace(".", "")
129            },
130        };
131
132        Percentile { label, value: clamped }
133    }
134}