iroh_metrics/
metrics.rs

1//! This module defines the individual metric types.
2//!
3//! If the `metrics` feature is enabled, they contain metric types based on atomics
4//! which can be modified without needing mutable access.
5//!
6//! If the `metrics` feature is disabled, all operations defined on these types are noops,
7//! and the structs don't collect actual data.
8
9use std::any::Any;
10#[cfg(feature = "metrics")]
11use std::sync::atomic::{AtomicI64, AtomicU64, Ordering};
12
13use serde::{Deserialize, Serialize};
14
15/// The types of metrics supported by this crate.
16#[derive(Debug, Clone, Copy, Eq, PartialEq, Serialize, Deserialize)]
17#[non_exhaustive]
18pub enum MetricType {
19    /// A [`Counter`].
20    Counter,
21    /// A [`Gauge`].
22    Gauge,
23}
24
25impl MetricType {
26    /// Returns the given metric type's str representation.
27    pub fn as_str(&self) -> &str {
28        match self {
29            MetricType::Counter => "counter",
30            MetricType::Gauge => "gauge",
31        }
32    }
33}
34
35/// The value of an individual metric item.
36#[derive(Debug, Clone, Copy, Eq, PartialEq, Serialize, Deserialize)]
37#[non_exhaustive]
38pub enum MetricValue {
39    /// A [`Counter`] value.
40    Counter(u64),
41    /// A [`Gauge`] value.
42    Gauge(i64),
43}
44
45impl MetricValue {
46    /// Returns the value as [`f32`].
47    pub fn to_f32(&self) -> f32 {
48        match self {
49            MetricValue::Counter(value) => *value as f32,
50            MetricValue::Gauge(value) => *value as f32,
51        }
52    }
53
54    /// Returns the [`MetricType`] for this metric value.
55    pub fn r#type(&self) -> MetricType {
56        match self {
57            MetricValue::Counter(_) => MetricType::Counter,
58            MetricValue::Gauge(_) => MetricType::Gauge,
59        }
60    }
61}
62
63impl Metric for MetricValue {
64    fn r#type(&self) -> MetricType {
65        self.r#type()
66    }
67
68    fn value(&self) -> MetricValue {
69        *self
70    }
71
72    fn as_any(&self) -> &dyn Any {
73        self
74    }
75}
76
77/// Trait for metric items.
78pub trait Metric: std::fmt::Debug {
79    /// Returns the type of this metric.
80    fn r#type(&self) -> MetricType;
81
82    /// Returns the current value of this metric.
83    fn value(&self) -> MetricValue;
84
85    /// Casts this metric to [`Any`] for downcasting to concrete types.
86    fn as_any(&self) -> &dyn Any;
87}
88
89/// OpenMetrics [`Counter`] to measure discrete events.
90///
91/// Single monotonically increasing value metric.
92#[derive(Debug, Default, Serialize, Deserialize)]
93pub struct Counter {
94    /// The counter value.
95    #[cfg(feature = "metrics")]
96    pub(crate) value: AtomicU64,
97}
98
99impl Metric for Counter {
100    fn value(&self) -> MetricValue {
101        MetricValue::Counter(self.get())
102    }
103
104    fn r#type(&self) -> MetricType {
105        MetricType::Counter
106    }
107
108    fn as_any(&self) -> &dyn Any {
109        self
110    }
111}
112
113impl Counter {
114    /// Constructs a new counter, based on the given `help`.
115    pub fn new() -> Self {
116        Self::default()
117    }
118
119    /// Increases the [`Counter`] by 1, returning the previous value.
120    pub fn inc(&self) -> u64 {
121        #[cfg(feature = "metrics")]
122        {
123            self.value.fetch_add(1, Ordering::Relaxed)
124        }
125        #[cfg(not(feature = "metrics"))]
126        0
127    }
128
129    /// Increases the [`Counter`] by `u64`, returning the previous value.
130    pub fn inc_by(&self, v: u64) -> u64 {
131        #[cfg(feature = "metrics")]
132        {
133            self.value.fetch_add(v, Ordering::Relaxed)
134        }
135        #[cfg(not(feature = "metrics"))]
136        {
137            let _ = v;
138            0
139        }
140    }
141
142    /// Sets the [`Counter`] value, returning the previous value.
143    ///
144    /// Warning: this is not default behavior for a counter that should always be monotonically increasing.
145    pub fn set(&self, v: u64) -> u64 {
146        #[cfg(feature = "metrics")]
147        {
148            self.value.swap(v, Ordering::Relaxed)
149        }
150        #[cfg(not(feature = "metrics"))]
151        {
152            let _ = v;
153            0
154        }
155    }
156
157    /// Returns the current value of the [`Counter`].
158    pub fn get(&self) -> u64 {
159        #[cfg(feature = "metrics")]
160        {
161            self.value.load(Ordering::Relaxed)
162        }
163        #[cfg(not(feature = "metrics"))]
164        0
165    }
166}
167
168/// OpenMetrics [`Gauge`].
169#[derive(Debug, Default, Serialize, Deserialize)]
170pub struct Gauge {
171    /// The gauge value.
172    #[cfg(feature = "metrics")]
173    pub(crate) value: AtomicI64,
174}
175
176impl Metric for Gauge {
177    fn r#type(&self) -> MetricType {
178        MetricType::Gauge
179    }
180
181    fn value(&self) -> MetricValue {
182        MetricValue::Gauge(self.get())
183    }
184
185    fn as_any(&self) -> &dyn Any {
186        self
187    }
188}
189
190impl Gauge {
191    /// Constructs a new gauge, based on the given `help`.
192    pub fn new() -> Self {
193        Self::default()
194    }
195
196    /// Increases the [`Gauge`] by 1, returning the previous value.
197    pub fn inc(&self) -> i64 {
198        #[cfg(feature = "metrics")]
199        {
200            self.value.fetch_add(1, Ordering::Relaxed)
201        }
202        #[cfg(not(feature = "metrics"))]
203        0
204    }
205
206    /// Increases the [`Gauge`] by `i64`, returning the previous value.
207    pub fn inc_by(&self, v: i64) -> i64 {
208        #[cfg(feature = "metrics")]
209        {
210            self.value.fetch_add(v, Ordering::Relaxed)
211        }
212        #[cfg(not(feature = "metrics"))]
213        {
214            let _ = v;
215            0
216        }
217    }
218
219    /// Decreases the [`Gauge`] by 1, returning the previous value.
220    pub fn dec(&self) -> i64 {
221        #[cfg(feature = "metrics")]
222        {
223            self.value.fetch_sub(1, Ordering::Relaxed)
224        }
225        #[cfg(not(feature = "metrics"))]
226        0
227    }
228
229    /// Decreases the [`Gauge`] by `i64`, returning the previous value.
230    pub fn dec_by(&self, v: i64) -> i64 {
231        #[cfg(feature = "metrics")]
232        {
233            self.value.fetch_sub(v, Ordering::Relaxed)
234        }
235        #[cfg(not(feature = "metrics"))]
236        {
237            let _ = v;
238            0
239        }
240    }
241
242    /// Sets the [`Gauge`] to `v`, returning the previous value.
243    pub fn set(&self, v: i64) -> i64 {
244        #[cfg(feature = "metrics")]
245        {
246            self.value.swap(v, Ordering::Relaxed)
247        }
248        #[cfg(not(feature = "metrics"))]
249        {
250            let _ = v;
251            0
252        }
253    }
254
255    /// Returns the [`Gauge`] value.
256    pub fn get(&self) -> i64 {
257        #[cfg(feature = "metrics")]
258        {
259            self.value.load(Ordering::Relaxed)
260        }
261        #[cfg(not(feature = "metrics"))]
262        0
263    }
264}