Coverage Report

Created: 2025-02-25 06:39

/rust/registry/src/index.crates.io-6f17d22bba15001f/prometheus-client-0.23.1/src/metrics/counter.rs
Line
Count
Source (jump to first uncovered line)
1
//! Module implementing an Open Metrics counter.
2
//!
3
//! See [`Counter`] for details.
4
5
use crate::encoding::{EncodeMetric, MetricEncoder, NoLabelSet};
6
7
use super::{MetricType, TypedMetric};
8
use std::marker::PhantomData;
9
#[cfg(target_has_atomic = "64")]
10
use std::sync::atomic::AtomicU64;
11
use std::sync::atomic::{AtomicU32, Ordering};
12
use std::sync::Arc;
13
14
/// Open Metrics [`Counter`] to measure discrete events.
15
///
16
/// Single monotonically increasing value metric.
17
///
18
/// [`Counter`] is generic over the actual data type tracking the [`Counter`]
19
/// state as well as the data type used to interact with the [`Counter`]. Out of
20
/// convenience the generic type parameters are set to use an [`AtomicU64`] as a
21
/// storage and [`u64`] on the interface by default.
22
///
23
/// # Examples
24
///
25
/// ## Using [`AtomicU64`] as storage and [`u64`] on the interface
26
///
27
/// ```
28
/// # use prometheus_client::metrics::counter::Counter;
29
/// let counter: Counter = Counter::default();
30
/// counter.inc();
31
/// let _value: u64 = counter.get();
32
/// ```
33
///
34
/// ## Using [`AtomicU64`] as storage and [`f64`] on the interface
35
///
36
/// ```
37
/// # use prometheus_client::metrics::counter::Counter;
38
/// # use std::sync::atomic::AtomicU64;
39
/// let counter = Counter::<f64, AtomicU64>::default();
40
/// counter.inc();
41
/// let _value: f64 = counter.get();
42
/// ```
43
#[cfg(target_has_atomic = "64")]
44
#[derive(Debug)]
45
pub struct Counter<N = u64, A = AtomicU64> {
46
    value: Arc<A>,
47
    phantom: PhantomData<N>,
48
}
49
50
/// Open Metrics [`Counter`] to measure discrete events.
51
#[cfg(not(target_has_atomic = "64"))]
52
#[derive(Debug)]
53
pub struct Counter<N = u32, A = AtomicU32> {
54
    value: Arc<A>,
55
    phantom: PhantomData<N>,
56
}
57
58
impl<N, A> Clone for Counter<N, A> {
59
0
    fn clone(&self) -> Self {
60
0
        Self {
61
0
            value: self.value.clone(),
62
0
            phantom: PhantomData,
63
0
        }
64
0
    }
Unexecuted instantiation: <prometheus_client::metrics::counter::Counter as core::clone::Clone>::clone
Unexecuted instantiation: <prometheus_client::metrics::counter::Counter<_, _> as core::clone::Clone>::clone
65
}
66
67
impl<N, A: Default> Default for Counter<N, A> {
68
0
    fn default() -> Self {
69
0
        Counter {
70
0
            value: Arc::new(A::default()),
71
0
            phantom: PhantomData,
72
0
        }
73
0
    }
Unexecuted instantiation: <prometheus_client::metrics::counter::Counter as core::default::Default>::default
Unexecuted instantiation: <prometheus_client::metrics::counter::Counter<_, _> as core::default::Default>::default
74
}
75
76
impl<N, A: Atomic<N>> Counter<N, A> {
77
    /// Increase the [`Counter`] by 1, returning the previous value.
78
0
    pub fn inc(&self) -> N {
79
0
        self.value.inc()
80
0
    }
Unexecuted instantiation: <prometheus_client::metrics::counter::Counter>::inc
Unexecuted instantiation: <prometheus_client::metrics::counter::Counter<_, _>>::inc
81
82
    /// Increase the [`Counter`] by `v`, returning the previous value.
83
0
    pub fn inc_by(&self, v: N) -> N {
84
0
        self.value.inc_by(v)
85
0
    }
Unexecuted instantiation: <prometheus_client::metrics::counter::Counter>::inc_by
Unexecuted instantiation: <prometheus_client::metrics::counter::Counter<_, _>>::inc_by
86
87
    /// Get the current value of the [`Counter`].
88
0
    pub fn get(&self) -> N {
89
0
        self.value.get()
90
0
    }
Unexecuted instantiation: <prometheus_client::metrics::counter::Counter>::get
Unexecuted instantiation: <prometheus_client::metrics::counter::Counter<_, _>>::get
91
92
    /// Exposes the inner atomic type of the [`Counter`].
93
    ///
94
    /// This should only be used for advanced use-cases which are not directly
95
    /// supported by the library.
96
    ///
97
    /// The caller of this function has to uphold the property of an Open
98
    /// Metrics counter namely that the value is monotonically increasing, i.e.
99
    /// either stays the same or increases.
100
0
    pub fn inner(&self) -> &A {
101
0
        &self.value
102
0
    }
103
}
104
105
/// Atomic operations for a [`Counter`] value store.
106
pub trait Atomic<N> {
107
    /// Increase the value by `1`.
108
    fn inc(&self) -> N;
109
110
    /// Increase the value.
111
    fn inc_by(&self, v: N) -> N;
112
113
    /// Get the the value.
114
    fn get(&self) -> N;
115
}
116
117
#[cfg(target_has_atomic = "64")]
118
impl Atomic<u64> for AtomicU64 {
119
0
    fn inc(&self) -> u64 {
120
0
        self.inc_by(1)
121
0
    }
122
123
0
    fn inc_by(&self, v: u64) -> u64 {
124
0
        self.fetch_add(v, Ordering::Relaxed)
125
0
    }
126
127
0
    fn get(&self) -> u64 {
128
0
        self.load(Ordering::Relaxed)
129
0
    }
130
}
131
132
impl Atomic<u32> for AtomicU32 {
133
0
    fn inc(&self) -> u32 {
134
0
        self.inc_by(1)
135
0
    }
136
137
0
    fn inc_by(&self, v: u32) -> u32 {
138
0
        self.fetch_add(v, Ordering::Relaxed)
139
0
    }
140
141
0
    fn get(&self) -> u32 {
142
0
        self.load(Ordering::Relaxed)
143
0
    }
144
}
145
146
#[cfg(target_has_atomic = "64")]
147
impl Atomic<f64> for AtomicU64 {
148
0
    fn inc(&self) -> f64 {
149
0
        self.inc_by(1.0)
150
0
    }
151
152
0
    fn inc_by(&self, v: f64) -> f64 {
153
0
        let mut old_u64 = self.load(Ordering::Relaxed);
154
        let mut old_f64;
155
        loop {
156
0
            old_f64 = f64::from_bits(old_u64);
157
0
            let new = f64::to_bits(old_f64 + v);
158
0
            match self.compare_exchange_weak(old_u64, new, Ordering::Relaxed, Ordering::Relaxed) {
159
0
                Ok(_) => break,
160
0
                Err(x) => old_u64 = x,
161
            }
162
        }
163
164
0
        old_f64
165
0
    }
166
167
0
    fn get(&self) -> f64 {
168
0
        f64::from_bits(self.load(Ordering::Relaxed))
169
0
    }
170
}
171
172
impl Atomic<f32> for AtomicU32 {
173
0
    fn inc(&self) -> f32 {
174
0
        self.inc_by(1.0)
175
0
    }
176
177
0
    fn inc_by(&self, v: f32) -> f32 {
178
0
        let mut old_u32 = self.load(Ordering::Relaxed);
179
        let mut old_f32;
180
        loop {
181
0
            old_f32 = f32::from_bits(old_u32);
182
0
            let new = f32::to_bits(old_f32 + v);
183
0
            match self.compare_exchange_weak(old_u32, new, Ordering::Relaxed, Ordering::Relaxed) {
184
0
                Ok(_) => break,
185
0
                Err(x) => old_u32 = x,
186
            }
187
        }
188
189
0
        old_f32
190
0
    }
191
192
0
    fn get(&self) -> f32 {
193
0
        f32::from_bits(self.load(Ordering::Relaxed))
194
0
    }
195
}
196
197
impl<N, A> TypedMetric for Counter<N, A> {
198
    const TYPE: MetricType = MetricType::Counter;
199
}
200
201
impl<N, A> EncodeMetric for Counter<N, A>
202
where
203
    N: crate::encoding::EncodeCounterValue,
204
    A: Atomic<N>,
205
{
206
0
    fn encode(&self, mut encoder: MetricEncoder) -> Result<(), std::fmt::Error> {
207
0
        encoder.encode_counter::<NoLabelSet, _, u64>(&self.get(), None)
208
0
    }
Unexecuted instantiation: <prometheus_client::metrics::counter::Counter as prometheus_client::encoding::EncodeMetric>::encode
Unexecuted instantiation: <prometheus_client::metrics::counter::Counter<_, _> as prometheus_client::encoding::EncodeMetric>::encode
209
210
0
    fn metric_type(&self) -> MetricType {
211
0
        Self::TYPE
212
0
    }
Unexecuted instantiation: <prometheus_client::metrics::counter::Counter as prometheus_client::encoding::EncodeMetric>::metric_type
Unexecuted instantiation: <prometheus_client::metrics::counter::Counter<_, _> as prometheus_client::encoding::EncodeMetric>::metric_type
213
}
214
215
/// As a [`Counter`], but constant, meaning it cannot change once created.
216
///
217
/// Needed for advanced use-cases, e.g. in combination with [`Collector`](crate::collector::Collector).
218
#[derive(Debug, Default)]
219
pub struct ConstCounter<N = u64> {
220
    value: N,
221
}
222
223
impl<N> ConstCounter<N> {
224
    /// Creates a new [`ConstCounter`].
225
0
    pub fn new(value: N) -> Self {
226
0
        Self { value }
227
0
    }
228
}
229
230
impl<N> TypedMetric for ConstCounter<N> {
231
    const TYPE: MetricType = MetricType::Counter;
232
}
233
234
impl<N> EncodeMetric for ConstCounter<N>
235
where
236
    N: crate::encoding::EncodeCounterValue,
237
{
238
0
    fn encode(&self, mut encoder: MetricEncoder) -> Result<(), std::fmt::Error> {
239
0
        encoder.encode_counter::<NoLabelSet, _, u64>(&self.value, None)
240
0
    }
241
242
0
    fn metric_type(&self) -> MetricType {
243
0
        Self::TYPE
244
0
    }
245
}
246
247
#[cfg(test)]
248
mod tests {
249
    use super::*;
250
    use quickcheck::QuickCheck;
251
252
    #[test]
253
    fn inc_and_get() {
254
        let counter: Counter = Counter::default();
255
        assert_eq!(0, counter.inc());
256
        assert_eq!(1, counter.get());
257
    }
258
259
    #[cfg(target_has_atomic = "64")]
260
    #[test]
261
    fn f64_stored_in_atomic_u64() {
262
        fn prop(fs: Vec<f64>) {
263
            let fs: Vec<f64> = fs
264
                .into_iter()
265
                // Map infinite, subnormal and NaN to 0.0.
266
                .map(|f| if f.is_normal() { f } else { 0.0 })
267
                .collect();
268
            let sum = fs.iter().sum();
269
            let counter = Counter::<f64, AtomicU64>::default();
270
            for f in fs {
271
                counter.inc_by(f);
272
            }
273
            assert_eq!(counter.get(), sum)
274
        }
275
276
        QuickCheck::new().tests(10).quickcheck(prop as fn(_))
277
    }
278
}