Coverage Report

Created: 2024-12-17 06:15

/rust/registry/src/index.crates.io-6f17d22bba15001f/prometheus-client-0.22.3/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};
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
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
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
    }
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
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<N, A> TypedMetric for Counter<N, A> {
173
    const TYPE: MetricType = MetricType::Counter;
174
}
175
176
impl<N, A> EncodeMetric for Counter<N, A>
177
where
178
    N: crate::encoding::EncodeCounterValue,
179
    A: Atomic<N>,
180
{
181
0
    fn encode(&self, mut encoder: MetricEncoder) -> Result<(), std::fmt::Error> {
182
0
        encoder.encode_counter::<(), _, u64>(&self.get(), None)
183
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
Unexecuted instantiation: <prometheus_client::metrics::counter::Counter as prometheus_client::encoding::EncodeMetric>::encode
184
185
0
    fn metric_type(&self) -> MetricType {
186
0
        Self::TYPE
187
0
    }
188
}
189
190
/// As a [`Counter`], but constant, meaning it cannot change once created.
191
///
192
/// Needed for advanced use-cases, e.g. in combination with [`Collector`](crate::collector::Collector).
193
#[derive(Debug, Default)]
194
pub struct ConstCounter<N = u64> {
195
    value: N,
196
}
197
198
impl<N> ConstCounter<N> {
199
    /// Creates a new [`ConstCounter`].
200
0
    pub fn new(value: N) -> Self {
201
0
        Self { value }
202
0
    }
Unexecuted instantiation: <prometheus_client::metrics::counter::ConstCounter<_>>::new
Unexecuted instantiation: <prometheus_client::metrics::counter::ConstCounter<f64>>::new
203
}
204
205
impl<N> TypedMetric for ConstCounter<N> {
206
    const TYPE: MetricType = MetricType::Counter;
207
}
208
209
impl<N> EncodeMetric for ConstCounter<N>
210
where
211
    N: crate::encoding::EncodeCounterValue,
212
{
213
0
    fn encode(&self, mut encoder: MetricEncoder) -> Result<(), std::fmt::Error> {
214
0
        encoder.encode_counter::<(), _, u64>(&self.value, None)
215
0
    }
Unexecuted instantiation: <prometheus_client::metrics::counter::ConstCounter<_> as prometheus_client::encoding::EncodeMetric>::encode
Unexecuted instantiation: <prometheus_client::metrics::counter::ConstCounter<f64> as prometheus_client::encoding::EncodeMetric>::encode
216
217
0
    fn metric_type(&self) -> MetricType {
218
0
        Self::TYPE
219
0
    }
220
}
221
222
#[cfg(test)]
223
mod tests {
224
    use super::*;
225
    use quickcheck::QuickCheck;
226
227
    #[test]
228
    fn inc_and_get() {
229
        let counter: Counter = Counter::default();
230
        assert_eq!(0, counter.inc());
231
        assert_eq!(1, counter.get());
232
    }
233
234
    #[cfg(target_has_atomic = "64")]
235
    #[test]
236
    fn f64_stored_in_atomic_u64() {
237
        fn prop(fs: Vec<f64>) {
238
            let fs: Vec<f64> = fs
239
                .into_iter()
240
                // Map infinite, subnormal and NaN to 0.0.
241
                .map(|f| if f.is_normal() { f } else { 0.0 })
242
                .collect();
243
            let sum = fs.iter().sum();
244
            let counter = Counter::<f64, AtomicU64>::default();
245
            for f in fs {
246
                counter.inc_by(f);
247
            }
248
            assert_eq!(counter.get(), sum)
249
        }
250
251
        QuickCheck::new().tests(10).quickcheck(prop as fn(_))
252
    }
253
}