stratus/infra/metrics/
metrics_types.rs

1use std::borrow::Cow;
2use std::time::Duration;
3
4use metrics::Label;
5use metrics::describe_counter;
6use metrics::describe_gauge;
7use metrics::describe_histogram;
8
9pub type HistogramInt = u32;
10pub type Sum = u64;
11pub type Count = u64;
12
13// -----------------------------------------------------------------------------
14// Labels
15// -----------------------------------------------------------------------------
16
17/// Label value indicating a value is present.
18pub const LABEL_PRESENT: &str = "present";
19
20/// Label value indicating a value is missing.
21pub const LABEL_MISSING: &str = "missing";
22
23/// Label value indicating a value is unknown.
24pub const LABEL_UNKNOWN: &str = "unknown";
25
26/// Label value indicating an error happened.
27pub const LABEL_ERROR: &str = "error";
28
29// -----------------------------------------------------------------------------
30// Metric
31// -----------------------------------------------------------------------------
32
33/// Metric definition.
34pub(super) struct Metric {
35    pub(super) kind: &'static str,
36    pub(super) name: &'static str,
37    pub(super) description: &'static str,
38}
39
40impl Metric {
41    /// Register description with the provider.
42    pub(super) fn register_description(&self) {
43        match self.kind {
44            "counter" => describe_counter!(self.name, self.description),
45            "histogram_duration" | "histogram_counter" => describe_histogram!(self.name, self.description),
46            "gauge" => describe_gauge!(self.name, self.description),
47            _ => {}
48        }
49    }
50}
51
52// -----------------------------------------------------------------------------
53// MetricLabelValue
54// -----------------------------------------------------------------------------
55
56/// Representation of a metric label value.
57///
58/// It exists to improve two aspects `metrics` crate does not cover:
59/// * Conversion from several types to a label value.
60/// * Handling of optional values.
61pub enum MetricLabelValue {
62    /// Label has a value and should be recorded.
63    Some(String),
64    /// Label does not have a value and should be ignored.
65    None,
66}
67
68impl From<Option<Cow<'static, str>>> for MetricLabelValue {
69    fn from(value: Option<Cow<'static, str>>) -> Self {
70        match value {
71            Some(str) => Self::Some(str.into_owned()),
72            None => Self::None,
73        }
74    }
75}
76
77impl From<&String> for MetricLabelValue {
78    fn from(value: &String) -> Self {
79        Self::Some(value.to_owned())
80    }
81}
82
83impl From<&str> for MetricLabelValue {
84    fn from(value: &str) -> Self {
85        Self::Some(value.to_owned())
86    }
87}
88
89impl From<Option<&str>> for MetricLabelValue {
90    fn from(value: Option<&str>) -> Self {
91        match value {
92            Some(value) => Self::Some(value.to_owned()),
93            None => Self::None,
94        }
95    }
96}
97
98impl From<String> for MetricLabelValue {
99    fn from(value: String) -> Self {
100        Self::Some(value)
101    }
102}
103
104impl From<bool> for MetricLabelValue {
105    fn from(value: bool) -> Self {
106        Self::Some(value.to_string())
107    }
108}
109
110impl From<i32> for MetricLabelValue {
111    fn from(value: i32) -> Self {
112        Self::Some(value.to_string())
113    }
114}
115
116/// Converts a list of label keys-value pairs to `metrics::Label`. Labels with missing values are filtered out.
117pub(super) fn into_labels(labels: Vec<(&'static str, MetricLabelValue)>) -> Vec<Label> {
118    labels
119        .into_iter()
120        .filter_map(|(key, value)| match value {
121            MetricLabelValue::Some(value) => Some((key, value)),
122            MetricLabelValue::None => None,
123        })
124        .map(|(key, value)| Label::new(key, value))
125        .collect()
126}
127
128// -----------------------------------------------------------------------------
129// Timed
130// -----------------------------------------------------------------------------
131#[cfg(feature = "metrics")]
132/// Measures how long the provided function takes to execute.
133///
134/// Returns a wrapper that allows to using it to record metrics if the `metrics` feature is enabled.
135pub fn timed<F, T>(f: F) -> Timed<T>
136where
137    F: FnOnce() -> T,
138{
139    let start = crate::infra::metrics::now();
140    let result = f();
141    Timed {
142        elapsed: start.elapsed(),
143        result,
144    }
145}
146
147#[cfg(not(feature = "metrics"))]
148/// Executes the provided function
149pub fn timed<F, T>(f: F) -> Timed<T>
150where
151    F: FnOnce() -> T,
152{
153    let result = f();
154    Timed {
155        elapsed: Duration::default(),
156        result,
157    }
158}
159
160pub struct Timed<T> {
161    pub elapsed: Duration,
162    pub result: T,
163}
164
165impl<T> Timed<T> {
166    #[cfg(feature = "metrics")]
167    #[inline(always)]
168    /// Applies the provided function to the current metrified execution.
169    pub fn with<F>(self, f: F) -> T
170    where
171        F: FnOnce(&Timed<T>),
172    {
173        f(&self);
174        self.result
175    }
176
177    #[cfg(not(feature = "metrics"))]
178    #[inline(always)]
179    /// Do nothing because the `metrics` function is disabled.
180    pub fn with<F>(self, _: F) -> T
181    where
182        F: FnOnce(&Timed<T>),
183    {
184        self.result
185    }
186}