prometheus_client/metrics/
gauge.rs

1//! Module implementing an Open Metrics gauge.
2//!
3//! See [`Gauge`] for details.
4
5use crate::encoding::{EncodeGaugeValue, EncodeMetric, MetricEncoder};
6
7use super::{MetricType, TypedMetric};
8use std::marker::PhantomData;
9use std::sync::atomic::{AtomicI32, AtomicU32, Ordering};
10#[cfg(target_has_atomic = "64")]
11use std::sync::atomic::{AtomicI64, AtomicU64};
12use std::sync::Arc;
13
14/// Open Metrics [`Gauge`] to record current measurements.
15///
16/// Single increasing, decreasing or constant value metric.
17///
18/// [`Gauge`] is generic over the actual data type tracking the [`Gauge`] state
19/// as well as the data type used to interact with the [`Gauge`]. Out of
20/// convenience the generic type parameters are set to use an [`AtomicI64`] as a
21/// storage and [`i64`] on the interface by default.
22///
23/// # Examples
24///
25/// ## Using [`AtomicI64`] as storage and [`i64`] on the interface
26///
27/// ```
28/// # use prometheus_client::metrics::gauge::Gauge;
29/// let gauge: Gauge = Gauge::default();
30/// gauge.set(42);
31/// let _value = gauge.get();
32/// ```
33///
34/// ## Using [`AtomicU64`] as storage and [`f64`] on the interface
35///
36/// ```
37/// # use prometheus_client::metrics::gauge::Gauge;
38/// # use std::sync::atomic::AtomicU64;
39/// let gauge = Gauge::<f64, AtomicU64>::default();
40/// gauge.set(42.0);
41/// let _value: f64 = gauge.get();
42/// ```
43#[cfg(target_has_atomic = "64")]
44#[derive(Debug)]
45pub struct Gauge<N = i64, A = AtomicI64> {
46    value: Arc<A>,
47    phantom: PhantomData<N>,
48}
49
50/// Open Metrics [`Gauge`] to record current measurements.
51#[cfg(not(target_has_atomic = "64"))]
52#[derive(Debug)]
53pub struct Gauge<N = i32, A = AtomicI32> {
54    value: Arc<A>,
55    phantom: PhantomData<N>,
56}
57
58impl<N, A> Clone for Gauge<N, A> {
59    fn clone(&self) -> Self {
60        Self {
61            value: self.value.clone(),
62            phantom: PhantomData,
63        }
64    }
65}
66
67impl<N, A: Default> Default for Gauge<N, A> {
68    fn default() -> Self {
69        Self {
70            value: Arc::new(A::default()),
71            phantom: PhantomData,
72        }
73    }
74}
75
76impl<N, A: Atomic<N>> Gauge<N, A> {
77    /// Increase the [`Gauge`] by 1, returning the previous value.
78    pub fn inc(&self) -> N {
79        self.value.inc()
80    }
81
82    /// Increase the [`Gauge`] by `v`, returning the previous value.
83    pub fn inc_by(&self, v: N) -> N {
84        self.value.inc_by(v)
85    }
86
87    /// Decrease the [`Gauge`] by 1, returning the previous value.
88    pub fn dec(&self) -> N {
89        self.value.dec()
90    }
91
92    /// Decrease the [`Gauge`] by `v`, returning the previous value.
93    pub fn dec_by(&self, v: N) -> N {
94        self.value.dec_by(v)
95    }
96
97    /// Sets the [`Gauge`] to `v`, returning the previous value.
98    pub fn set(&self, v: N) -> N {
99        self.value.set(v)
100    }
101
102    /// Get the current value of the [`Gauge`].
103    pub fn get(&self) -> N {
104        self.value.get()
105    }
106
107    /// Exposes the inner atomic type of the [`Gauge`].
108    ///
109    /// This should only be used for advanced use-cases which are not directly
110    /// supported by the library.
111    pub fn inner(&self) -> &A {
112        &self.value
113    }
114}
115
116/// Atomic operations for a [`Gauge`] value store.
117pub trait Atomic<N> {
118    /// Increase the value by `1`.
119    fn inc(&self) -> N;
120
121    /// Increase the value.
122    fn inc_by(&self, v: N) -> N;
123
124    /// Decrease the value by `1`.
125    fn dec(&self) -> N;
126
127    /// Decrease the value.
128    fn dec_by(&self, v: N) -> N;
129
130    /// Set the value.
131    fn set(&self, v: N) -> N;
132
133    /// Get the value.
134    fn get(&self) -> N;
135}
136
137impl Atomic<i32> for AtomicI32 {
138    fn inc(&self) -> i32 {
139        self.inc_by(1)
140    }
141
142    fn inc_by(&self, v: i32) -> i32 {
143        self.fetch_add(v, Ordering::Relaxed)
144    }
145
146    fn dec(&self) -> i32 {
147        self.dec_by(1)
148    }
149
150    fn dec_by(&self, v: i32) -> i32 {
151        self.fetch_sub(v, Ordering::Relaxed)
152    }
153
154    fn set(&self, v: i32) -> i32 {
155        self.swap(v, Ordering::Relaxed)
156    }
157
158    fn get(&self) -> i32 {
159        self.load(Ordering::Relaxed)
160    }
161}
162
163impl Atomic<u32> for AtomicU32 {
164    fn inc(&self) -> u32 {
165        self.inc_by(1)
166    }
167
168    fn inc_by(&self, v: u32) -> u32 {
169        self.fetch_add(v, Ordering::Relaxed)
170    }
171
172    fn dec(&self) -> u32 {
173        self.dec_by(1)
174    }
175
176    fn dec_by(&self, v: u32) -> u32 {
177        self.fetch_sub(v, Ordering::Relaxed)
178    }
179
180    fn set(&self, v: u32) -> u32 {
181        self.swap(v, Ordering::Relaxed)
182    }
183
184    fn get(&self) -> u32 {
185        self.load(Ordering::Relaxed)
186    }
187}
188
189#[cfg(target_has_atomic = "64")]
190impl Atomic<i64> for AtomicI64 {
191    fn inc(&self) -> i64 {
192        self.inc_by(1)
193    }
194
195    fn inc_by(&self, v: i64) -> i64 {
196        self.fetch_add(v, Ordering::Relaxed)
197    }
198
199    fn dec(&self) -> i64 {
200        self.dec_by(1)
201    }
202
203    fn dec_by(&self, v: i64) -> i64 {
204        self.fetch_sub(v, Ordering::Relaxed)
205    }
206
207    fn set(&self, v: i64) -> i64 {
208        self.swap(v, Ordering::Relaxed)
209    }
210
211    fn get(&self) -> i64 {
212        self.load(Ordering::Relaxed)
213    }
214}
215
216#[cfg(target_has_atomic = "64")]
217impl Atomic<u64> for AtomicU64 {
218    fn inc(&self) -> u64 {
219        self.inc_by(1)
220    }
221
222    fn inc_by(&self, v: u64) -> u64 {
223        self.fetch_add(v, Ordering::Relaxed)
224    }
225
226    fn dec(&self) -> u64 {
227        self.dec_by(1)
228    }
229
230    fn dec_by(&self, v: u64) -> u64 {
231        self.fetch_sub(v, Ordering::Relaxed)
232    }
233
234    fn set(&self, v: u64) -> u64 {
235        self.swap(v, Ordering::Relaxed)
236    }
237
238    fn get(&self) -> u64 {
239        self.load(Ordering::Relaxed)
240    }
241}
242
243#[cfg(target_has_atomic = "64")]
244impl Atomic<f64> for AtomicU64 {
245    fn inc(&self) -> f64 {
246        self.inc_by(1.0)
247    }
248
249    fn inc_by(&self, v: f64) -> f64 {
250        let mut old_u64 = self.load(Ordering::Relaxed);
251        let mut old_f64;
252        loop {
253            old_f64 = f64::from_bits(old_u64);
254            let new = f64::to_bits(old_f64 + v);
255            match self.compare_exchange_weak(old_u64, new, Ordering::Relaxed, Ordering::Relaxed) {
256                Ok(_) => break,
257                Err(x) => old_u64 = x,
258            }
259        }
260
261        old_f64
262    }
263
264    fn dec(&self) -> f64 {
265        self.dec_by(1.0)
266    }
267
268    fn dec_by(&self, v: f64) -> f64 {
269        let mut old_u64 = self.load(Ordering::Relaxed);
270        let mut old_f64;
271        loop {
272            old_f64 = f64::from_bits(old_u64);
273            let new = f64::to_bits(old_f64 - v);
274            match self.compare_exchange_weak(old_u64, new, Ordering::Relaxed, Ordering::Relaxed) {
275                Ok(_) => break,
276                Err(x) => old_u64 = x,
277            }
278        }
279
280        old_f64
281    }
282
283    fn set(&self, v: f64) -> f64 {
284        f64::from_bits(self.swap(f64::to_bits(v), Ordering::Relaxed))
285    }
286
287    fn get(&self) -> f64 {
288        f64::from_bits(self.load(Ordering::Relaxed))
289    }
290}
291
292impl Atomic<f32> for AtomicU32 {
293    fn inc(&self) -> f32 {
294        self.inc_by(1.0)
295    }
296
297    fn inc_by(&self, v: f32) -> f32 {
298        let mut old_u32 = self.load(Ordering::Relaxed);
299        let mut old_f32;
300        loop {
301            old_f32 = f32::from_bits(old_u32);
302            let new = f32::to_bits(old_f32 + v);
303            match self.compare_exchange_weak(old_u32, new, Ordering::Relaxed, Ordering::Relaxed) {
304                Ok(_) => break,
305                Err(x) => old_u32 = x,
306            }
307        }
308
309        old_f32
310    }
311
312    fn dec(&self) -> f32 {
313        self.dec_by(1.0)
314    }
315
316    fn dec_by(&self, v: f32) -> f32 {
317        let mut old_u32 = self.load(Ordering::Relaxed);
318        let mut old_f32;
319        loop {
320            old_f32 = f32::from_bits(old_u32);
321            let new = f32::to_bits(old_f32 - v);
322            match self.compare_exchange_weak(old_u32, new, Ordering::Relaxed, Ordering::Relaxed) {
323                Ok(_) => break,
324                Err(x) => old_u32 = x,
325            }
326        }
327
328        old_f32
329    }
330
331    fn set(&self, v: f32) -> f32 {
332        f32::from_bits(self.swap(f32::to_bits(v), Ordering::Relaxed))
333    }
334
335    fn get(&self) -> f32 {
336        f32::from_bits(self.load(Ordering::Relaxed))
337    }
338}
339
340impl<N, A> TypedMetric for Gauge<N, A> {
341    const TYPE: MetricType = MetricType::Gauge;
342}
343
344impl<N, A> EncodeMetric for Gauge<N, A>
345where
346    N: EncodeGaugeValue,
347    A: Atomic<N>,
348{
349    fn encode(&self, mut encoder: MetricEncoder) -> Result<(), std::fmt::Error> {
350        encoder.encode_gauge(&self.get())
351    }
352    fn metric_type(&self) -> MetricType {
353        Self::TYPE
354    }
355}
356
357/// As a [`Gauge`], but constant, meaning it cannot change once created.
358///
359/// Needed for advanced use-cases, e.g. in combination with [`Collector`](crate::collector::Collector).
360#[derive(Debug, Default)]
361pub struct ConstGauge<N = i64> {
362    value: N,
363}
364
365impl<N> ConstGauge<N> {
366    /// Creates a new [`ConstGauge`].
367    pub fn new(value: N) -> Self {
368        Self { value }
369    }
370}
371
372impl<N> TypedMetric for ConstGauge<N> {
373    const TYPE: MetricType = MetricType::Gauge;
374}
375
376impl<N> EncodeMetric for ConstGauge<N>
377where
378    N: EncodeGaugeValue,
379{
380    fn encode(&self, mut encoder: MetricEncoder) -> Result<(), std::fmt::Error> {
381        encoder.encode_gauge(&self.value)
382    }
383
384    fn metric_type(&self) -> MetricType {
385        Self::TYPE
386    }
387}
388
389#[cfg(test)]
390mod tests {
391    use super::*;
392
393    #[test]
394    fn inc_dec_and_get() {
395        let gauge: Gauge = Gauge::default();
396        assert_eq!(0, gauge.inc());
397        assert_eq!(1, gauge.get());
398
399        assert_eq!(1, gauge.dec());
400        assert_eq!(0, gauge.get());
401
402        assert_eq!(0, gauge.set(10));
403        assert_eq!(10, gauge.get());
404    }
405}