Skip to main content

vortex_metrics/
lib.rs

1// SPDX-License-Identifier: Apache-2.0
2// SPDX-FileCopyrightText: Copyright the Vortex contributors
3
4#![deny(missing_docs)]
5
6//! Vortex metrics
7
8pub mod tracing;
9
10use std::borrow::Cow;
11use std::sync::Arc;
12
13use parking_lot::Mutex;
14
15mod counter;
16mod gauge;
17mod histogram;
18mod timer;
19
20pub use counter::*;
21pub use gauge::*;
22pub use histogram::*;
23pub use timer::*;
24
25/// A metric KV label.
26#[derive(Clone, Debug)]
27pub struct Label {
28    key: Cow<'static, str>,
29    value: Cow<'static, str>,
30}
31
32impl std::fmt::Display for Label {
33    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
34        write!(f, "{}={}", self.key, self.value)
35    }
36}
37
38impl<K, V> From<(K, V)> for Label
39where
40    K: Into<Cow<'static, str>>,
41    V: Into<Cow<'static, str>>,
42{
43    fn from(value: (K, V)) -> Self {
44        Label::new(value.0, value.1)
45    }
46}
47
48impl Label {
49    /// Creates a new label
50    pub fn new(key: impl Into<Cow<'static, str>>, value: impl Into<Cow<'static, str>>) -> Self {
51        Self {
52            key: key.into(),
53            value: value.into(),
54        }
55    }
56
57    /// Returns the label's key
58    pub fn key(&self) -> &str {
59        &self.key
60    }
61
62    /// Returns the label's value
63    pub fn value(&self) -> &str {
64        &self.value
65    }
66}
67
68/// A registry for metrics that allows creating and storing metrics with labels.
69///
70/// Both metrics and labels might not be unique, and its up to users to resolve such cases.
71pub trait MetricsRegistry: Send + Sync {
72    /// Create a counter with the given name and labels.
73    fn register_counter(&self, name: Cow<'static, str>, labels: Vec<Label>) -> Counter;
74
75    /// Create a histogram with the given name and labels.
76    fn register_histogram(&self, name: Cow<'static, str>, labels: Vec<Label>) -> Histogram;
77
78    /// Create a timer with the given name and labels.
79    fn register_timer(&self, name: Cow<'static, str>, labels: Vec<Label>) -> Timer;
80
81    /// Create a gauge with the given name and labels.
82    fn register_gauge(&self, name: Cow<'static, str>, labels: Vec<Label>) -> Gauge;
83
84    /// Returns a snapshot of the current metric values stored in the registry.
85    /// Metrics might not be unique.
86    fn snapshot(&self) -> Vec<Metric>;
87}
88
89/// Builder for creating metrics with labels.
90pub struct MetricBuilder<'s> {
91    labels: Vec<Label>,
92    registry: &'s dyn MetricsRegistry,
93}
94
95impl<'r> MetricBuilder<'r> {
96    /// Create a new builder for a metric backed by a [`MetricsRegistry`].
97    pub fn new(registry: &'r dyn MetricsRegistry) -> Self {
98        Self {
99            labels: vec![],
100            registry,
101        }
102    }
103
104    /// Add a label to the metric. Labels might not be unique, and its up to consumers of this data to resolve such cases.
105    pub fn add_label<K, V>(mut self, key: K, value: V) -> Self
106    where
107        K: Into<Cow<'static, str>>,
108        V: Into<Cow<'static, str>>,
109    {
110        self.labels.push(Label::new(key, value));
111        self
112    }
113
114    /// Adds multiple labels to the metric. Labels might not be unique, and its up to consumers of this data to resolve such cases.
115    pub fn add_labels<I, L>(mut self, labels: I) -> Self
116    where
117        I: IntoIterator<Item = L>,
118        L: Into<Label>,
119    {
120        self.labels.extend(labels.into_iter().map(|l| l.into()));
121        self
122    }
123
124    /// Creates a new [`Counter`] with the given name, registering it with the backend.
125    pub fn counter(self, name: impl Into<Cow<'static, str>>) -> Counter {
126        self.registry.register_counter(name.into(), self.labels)
127    }
128
129    /// Creates a new [`Histogram`] with the given name, registering it with the backend.
130    pub fn histogram(self, name: impl Into<Cow<'static, str>>) -> Histogram {
131        self.registry.register_histogram(name.into(), self.labels)
132    }
133
134    /// Creates a new [`Timer`] with the given name, registering it with the backend.
135    pub fn timer(self, name: impl Into<Cow<'static, str>>) -> Timer {
136        self.registry.register_timer(name.into(), self.labels)
137    }
138
139    /// Creates a new [`Gauge`] with the given name, registering it with the backend.
140    pub fn gauge(self, name: impl Into<Cow<'static, str>>) -> Gauge {
141        self.registry.register_gauge(name.into(), self.labels)
142    }
143}
144
145/// Default metrics registry, stores all state in-memory.
146#[derive(Default, Clone)]
147pub struct DefaultMetricsRegistry {
148    inner: Arc<Mutex<Vec<Metric>>>,
149}
150
151#[derive(Clone, Debug)]
152/// The value of a metric.
153pub enum MetricValue {
154    /// Counter value
155    Counter(Counter),
156    /// Histogram value
157    Histogram(Histogram),
158    /// Timer value
159    Timer(Timer),
160    /// Gauge value
161    Gauge(Gauge),
162}
163
164impl From<Counter> for MetricValue {
165    fn from(value: Counter) -> Self {
166        Self::Counter(value)
167    }
168}
169
170impl From<Histogram> for MetricValue {
171    fn from(value: Histogram) -> Self {
172        Self::Histogram(value)
173    }
174}
175
176impl From<Timer> for MetricValue {
177    fn from(value: Timer) -> Self {
178        Self::Timer(value)
179    }
180}
181
182impl From<Gauge> for MetricValue {
183    fn from(value: Gauge) -> Self {
184        Self::Gauge(value)
185    }
186}
187
188/// A stored metric with name, labels, and value.
189#[derive(Clone, Debug)]
190pub struct Metric {
191    name: Cow<'static, str>,
192    labels: Vec<Label>,
193    value: MetricValue,
194}
195
196impl Metric {
197    /// Returns the name of the metric
198    pub fn name(&self) -> &Cow<'static, str> {
199        &self.name
200    }
201
202    /// Returns the labels assigned to this metric
203    pub fn labels(&self) -> &[Label] {
204        &self.labels
205    }
206
207    /// Returns the current value of the metric
208    pub fn value(&self) -> &MetricValue {
209        &self.value
210    }
211}
212
213impl MetricsRegistry for DefaultMetricsRegistry {
214    fn register_counter(&self, name: Cow<'static, str>, labels: Vec<Label>) -> Counter {
215        let counter = Counter::new();
216        let metric = Metric {
217            name,
218            labels,
219            value: counter.clone().into(),
220        };
221        self.inner.lock().push(metric);
222        counter
223    }
224
225    fn register_histogram(&self, name: Cow<'static, str>, labels: Vec<Label>) -> Histogram {
226        let histogram = Histogram::new();
227        let metric = Metric {
228            name,
229            labels,
230            value: histogram.clone().into(),
231        };
232        self.inner.lock().push(metric);
233        histogram
234    }
235
236    fn register_timer(&self, name: Cow<'static, str>, labels: Vec<Label>) -> Timer {
237        let timer = Timer::new();
238        let metric = Metric {
239            name,
240            labels,
241            value: timer.clone().into(),
242        };
243        self.inner.lock().push(metric);
244        timer
245    }
246
247    fn register_gauge(&self, name: Cow<'static, str>, labels: Vec<Label>) -> Gauge {
248        let gauge = Gauge::new();
249        let metric = Metric {
250            name,
251            labels,
252            value: gauge.clone().into(),
253        };
254        self.inner.lock().push(metric);
255        gauge
256    }
257
258    fn snapshot(&self) -> Vec<Metric> {
259        self.inner.lock().clone()
260    }
261}