prometheus/
counter.rs

1// Copyright 2014 The Prometheus Authors
2// Copyright 2019 TiKV Project Authors. Licensed under Apache-2.0.
3
4use std::cell::RefCell;
5use std::collections::HashMap;
6use std::marker::PhantomData;
7use std::sync::Arc;
8
9use crate::atomic64::{Atomic, AtomicF64, AtomicU64, Number};
10use crate::desc::Desc;
11use crate::errors::Result;
12use crate::metrics::{Collector, LocalMetric, Metric, Opts};
13use crate::proto;
14use crate::value::{Value, ValueType};
15use crate::vec::{MetricVec, MetricVecBuilder};
16
17/// The underlying implementation for [`Counter`] and [`IntCounter`].
18#[derive(Debug)]
19pub struct GenericCounter<P: Atomic> {
20    v: Arc<Value<P>>,
21}
22
23/// A [`Metric`] represents a single numerical value that only ever goes up.
24pub type Counter = GenericCounter<AtomicF64>;
25
26/// The integer version of [`Counter`]. Provides better performance if metric values
27/// are all positive integers (natural numbers).
28pub type IntCounter = GenericCounter<AtomicU64>;
29
30impl<P: Atomic> Clone for GenericCounter<P> {
31    fn clone(&self) -> Self {
32        Self {
33            v: Arc::clone(&self.v),
34        }
35    }
36}
37
38impl<P: Atomic> GenericCounter<P> {
39    /// Create a [`GenericCounter`] with the `name` and `help` arguments.
40    pub fn new<S1: Into<String>, S2: Into<String>>(name: S1, help: S2) -> Result<Self> {
41        let opts = Opts::new(name, help);
42        Self::with_opts(opts)
43    }
44
45    /// Create a [`GenericCounter`] with the `opts` options.
46    pub fn with_opts(opts: Opts) -> Result<Self> {
47        Self::with_opts_and_label_values(&opts, &[])
48    }
49
50    fn with_opts_and_label_values(opts: &Opts, label_values: &[&str]) -> Result<Self> {
51        let v = Value::new(opts, ValueType::Counter, P::T::from_i64(0), label_values)?;
52        Ok(Self { v: Arc::new(v) })
53    }
54
55    /// Increase the given value to the counter.
56    ///
57    /// # Panics
58    ///
59    /// Panics in debug build if the value is < 0.
60    #[inline]
61    pub fn inc_by(&self, v: P::T) {
62        debug_assert!(v >= P::T::from_i64(0));
63        self.v.inc_by(v);
64    }
65
66    /// Increase the counter by 1.
67    #[inline]
68    pub fn inc(&self) {
69        self.v.inc();
70    }
71
72    /// Return the counter value.
73    #[inline]
74    pub fn get(&self) -> P::T {
75        self.v.get()
76    }
77
78    /// Restart the counter, resetting its value back to 0.
79    #[inline]
80    pub fn reset(&self) {
81        self.v.set(P::T::from_i64(0))
82    }
83
84    /// Return a [`GenericLocalCounter`] for single thread usage.
85    pub fn local(&self) -> GenericLocalCounter<P> {
86        GenericLocalCounter::new(self.clone())
87    }
88}
89
90impl<P: Atomic> Collector for GenericCounter<P> {
91    fn desc(&self) -> Vec<&Desc> {
92        vec![&self.v.desc]
93    }
94
95    fn collect(&self) -> Vec<proto::MetricFamily> {
96        vec![self.v.collect()]
97    }
98}
99
100impl<P: Atomic> Metric for GenericCounter<P> {
101    fn metric(&self) -> proto::Metric {
102        self.v.metric()
103    }
104}
105
106#[derive(Debug)]
107pub struct CounterVecBuilder<P: Atomic> {
108    _phantom: PhantomData<P>,
109}
110
111impl<P: Atomic> CounterVecBuilder<P> {
112    pub fn new() -> Self {
113        Self {
114            _phantom: PhantomData,
115        }
116    }
117}
118
119impl<P: Atomic> Clone for CounterVecBuilder<P> {
120    fn clone(&self) -> Self {
121        Self::new()
122    }
123}
124
125impl<P: Atomic> MetricVecBuilder for CounterVecBuilder<P> {
126    type M = GenericCounter<P>;
127    type P = Opts;
128
129    fn build(&self, opts: &Opts, vals: &[&str]) -> Result<Self::M> {
130        Self::M::with_opts_and_label_values(opts, vals)
131    }
132}
133
134/// The underlying implementation for [`CounterVec`] and [`IntCounterVec`].
135pub type GenericCounterVec<P> = MetricVec<CounterVecBuilder<P>>;
136
137/// A [`Collector`] that bundles a set of [`Counter`]s that all share
138/// the same [`Desc`], but have different values for their variable labels. This is
139/// used if you want to count the same thing partitioned by various dimensions
140/// (e.g. number of HTTP requests, partitioned by response code and method).
141pub type CounterVec = GenericCounterVec<AtomicF64>;
142
143/// The integer version of [`CounterVec`]. Provides better performance if metric
144/// are all positive integers (natural numbers).
145pub type IntCounterVec = GenericCounterVec<AtomicU64>;
146
147impl<P: Atomic> GenericCounterVec<P> {
148    /// Create a new [`GenericCounterVec`] based on the provided
149    /// [`Opts`] and partitioned by the given label names. At least one label name must be
150    /// provided.
151    pub fn new(opts: Opts, label_names: &[&str]) -> Result<Self> {
152        let variable_names = label_names.iter().map(|s| (*s).to_owned()).collect();
153        let opts = opts.variable_labels(variable_names);
154        let metric_vec =
155            MetricVec::create(proto::MetricType::COUNTER, CounterVecBuilder::new(), opts)?;
156
157        Ok(metric_vec as Self)
158    }
159
160    /// Return a [`GenericLocalCounterVec`] for single thread usage.
161    pub fn local(&self) -> GenericLocalCounterVec<P> {
162        GenericLocalCounterVec::new(self.clone())
163    }
164}
165
166/// The underlying implementation for [`LocalCounter`]
167/// and [`LocalIntCounter`].
168#[derive(Debug)]
169pub struct GenericLocalCounter<P: Atomic> {
170    counter: GenericCounter<P>,
171    val: RefCell<P::T>,
172}
173
174/// For auto_flush::AFLocalCounter to use to make type inference possible
175pub trait CounterWithValueType {
176    ///the exact type which implements Atomic
177    type ValueType: Atomic;
178}
179
180impl<P: Atomic> CounterWithValueType for GenericLocalCounter<P> {
181    type ValueType = P;
182}
183
184/// An unsync [`Counter`].
185pub type LocalCounter = GenericLocalCounter<AtomicF64>;
186
187/// The integer version of [`LocalCounter`]. Provides better performance
188/// are all positive integers (natural numbers).
189pub type LocalIntCounter = GenericLocalCounter<AtomicU64>;
190
191impl<P: Atomic> GenericLocalCounter<P> {
192    fn new(counter: GenericCounter<P>) -> Self {
193        Self {
194            counter,
195            val: RefCell::new(P::T::from_i64(0)),
196        }
197    }
198
199    /// Increase the given value to the local counter.
200    ///
201    /// # Panics
202    ///
203    /// Panics in debug build if the value is < 0.
204    #[inline]
205    pub fn inc_by(&self, v: P::T) {
206        debug_assert!(v >= P::T::from_i64(0));
207        *self.val.borrow_mut() += v;
208    }
209
210    /// Increase the local counter by 1.
211    #[inline]
212    pub fn inc(&self) {
213        *self.val.borrow_mut() += P::T::from_i64(1);
214    }
215
216    /// Return the local counter value.
217    #[inline]
218    pub fn get(&self) -> P::T {
219        *self.val.borrow()
220    }
221
222    /// Restart the counter, resetting its value back to 0.
223    #[inline]
224    pub fn reset(&self) {
225        *self.val.borrow_mut() = P::T::from_i64(0);
226    }
227
228    /// Flush the local metrics to the [`Counter`].
229    #[inline]
230    pub fn flush(&self) {
231        if *self.val.borrow() == P::T::from_i64(0) {
232            return;
233        }
234        self.counter.inc_by(*self.val.borrow());
235        *self.val.borrow_mut() = P::T::from_i64(0);
236    }
237}
238
239impl<P: Atomic> LocalMetric for GenericLocalCounter<P> {
240    /// Flush the local metrics to the [`Counter`].
241    #[inline]
242    fn flush(&self) {
243        GenericLocalCounter::flush(self);
244    }
245}
246
247impl<P: Atomic> Clone for GenericLocalCounter<P> {
248    fn clone(&self) -> Self {
249        Self::new(self.counter.clone())
250    }
251}
252
253/// The underlying implementation for [`LocalCounterVec`]
254/// and [`LocalIntCounterVec`].
255pub struct GenericLocalCounterVec<P: Atomic> {
256    vec: GenericCounterVec<P>,
257    local: HashMap<u64, GenericLocalCounter<P>>,
258}
259
260impl<P: Atomic> std::fmt::Debug for GenericLocalCounterVec<P> {
261    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
262        write!(
263            f,
264            "GenericLocalCounterVec ({} locals)",
265            self.local.keys().len()
266        )
267    }
268}
269
270/// An unsync [`CounterVec`].
271pub type LocalCounterVec = GenericLocalCounterVec<AtomicF64>;
272
273/// The integer version of [`LocalCounterVec`].
274/// Provides better performance if metric values are all positive
275/// integers (natural numbers).
276pub type LocalIntCounterVec = GenericLocalCounterVec<AtomicU64>;
277
278impl<P: Atomic> GenericLocalCounterVec<P> {
279    fn new(vec: GenericCounterVec<P>) -> Self {
280        let local = HashMap::with_capacity(vec.v.children.read().len());
281        Self { vec, local }
282    }
283
284    /// Get a [`GenericLocalCounter`] by label values.
285    /// See more [MetricVec::with_label_values].
286    pub fn with_label_values<'a>(&'a mut self, vals: &[&str]) -> &'a mut GenericLocalCounter<P> {
287        let hash = self.vec.v.hash_label_values(vals).unwrap();
288        let vec = &self.vec;
289        self.local
290            .entry(hash)
291            .or_insert_with(|| vec.with_label_values(vals).local())
292    }
293
294    /// Remove a [`GenericLocalCounter`] by label values.
295    /// See more [MetricVec::remove_label_values].
296    pub fn remove_label_values(&mut self, vals: &[&str]) -> Result<()> {
297        let hash = self.vec.v.hash_label_values(vals)?;
298        self.local.remove(&hash);
299        self.vec.v.delete_label_values(vals)
300    }
301
302    /// Flush the local metrics to the [`CounterVec`] metric.
303    pub fn flush(&self) {
304        for h in self.local.values() {
305            h.flush();
306        }
307    }
308}
309
310impl<P: Atomic> LocalMetric for GenericLocalCounterVec<P> {
311    /// Flush the local metrics to the [`CounterVec`] metric.
312    fn flush(&self) {
313        GenericLocalCounterVec::flush(self);
314    }
315}
316
317impl<P: Atomic> Clone for GenericLocalCounterVec<P> {
318    fn clone(&self) -> Self {
319        Self::new(self.vec.clone())
320    }
321}
322
323#[cfg(test)]
324mod tests {
325    use std::collections::HashMap;
326    use std::f64::EPSILON;
327
328    use super::*;
329    use crate::metrics::{Collector, Opts};
330
331    #[test]
332    fn test_counter() {
333        let opts = Opts::new("test", "test help")
334            .const_label("a", "1")
335            .const_label("b", "2");
336        let counter = Counter::with_opts(opts).unwrap();
337        counter.inc();
338        assert_eq!(counter.get() as u64, 1);
339        counter.inc_by(42.0);
340        assert_eq!(counter.get() as u64, 43);
341
342        let mut mfs = counter.collect();
343        assert_eq!(mfs.len(), 1);
344
345        let mf = mfs.pop().unwrap();
346        let m = mf.get_metric().get(0).unwrap();
347        assert_eq!(m.get_label().len(), 2);
348        assert_eq!(m.get_counter().get_value() as u64, 43);
349
350        counter.reset();
351        assert_eq!(counter.get() as u64, 0);
352    }
353
354    #[test]
355    fn test_int_counter() {
356        let counter = IntCounter::new("foo", "bar").unwrap();
357        counter.inc();
358        assert_eq!(counter.get(), 1);
359        counter.inc_by(11);
360        assert_eq!(counter.get(), 12);
361
362        let mut mfs = counter.collect();
363        assert_eq!(mfs.len(), 1);
364
365        let mf = mfs.pop().unwrap();
366        let m = mf.get_metric().get(0).unwrap();
367        assert_eq!(m.get_label().len(), 0);
368        assert_eq!(m.get_counter().get_value() as u64, 12);
369
370        counter.reset();
371        assert_eq!(counter.get() as u64, 0);
372    }
373
374    #[test]
375    fn test_local_counter() {
376        let counter = Counter::new("counter", "counter helper").unwrap();
377        let local_counter1 = counter.local();
378        let local_counter2 = counter.local();
379
380        local_counter1.inc();
381        local_counter2.inc();
382        assert_eq!(local_counter1.get() as u64, 1);
383        assert_eq!(local_counter2.get() as u64, 1);
384        assert_eq!(counter.get() as u64, 0);
385        local_counter1.flush();
386        assert_eq!(local_counter1.get() as u64, 0);
387        assert_eq!(counter.get() as u64, 1);
388        local_counter2.flush();
389        assert_eq!(counter.get() as u64, 2);
390
391        local_counter1.reset();
392        local_counter2.reset();
393        counter.reset();
394        assert_eq!(counter.get() as u64, 0);
395        local_counter1.flush();
396        assert_eq!(counter.get() as u64, 0);
397        local_counter2.flush();
398        assert_eq!(counter.get() as u64, 0);
399    }
400
401    #[test]
402    fn test_int_local_counter() {
403        let counter = IntCounter::new("foo", "bar").unwrap();
404        let local_counter = counter.local();
405
406        local_counter.inc();
407        assert_eq!(local_counter.get(), 1);
408        assert_eq!(counter.get(), 0);
409
410        local_counter.inc_by(5);
411        local_counter.flush();
412        assert_eq!(local_counter.get(), 0);
413        assert_eq!(counter.get(), 6);
414
415        local_counter.reset();
416        counter.reset();
417        assert_eq!(counter.get() as u64, 0);
418        local_counter.flush();
419        assert_eq!(counter.get() as u64, 0);
420    }
421
422    #[test]
423    fn test_counter_vec_with_labels() {
424        let vec = CounterVec::new(
425            Opts::new("test_couter_vec", "test counter vec help"),
426            &["l1", "l2"],
427        )
428        .unwrap();
429
430        let mut labels = HashMap::new();
431        labels.insert("l1", "v1");
432        labels.insert("l2", "v2");
433        assert!(vec.remove(&labels).is_err());
434
435        vec.with(&labels).inc();
436        assert!(vec.remove(&labels).is_ok());
437        assert!(vec.remove(&labels).is_err());
438
439        let mut labels2 = HashMap::new();
440        labels2.insert("l1", "v2");
441        labels2.insert("l2", "v1");
442
443        vec.with(&labels).inc();
444        assert!(vec.remove(&labels2).is_err());
445
446        vec.with(&labels).inc();
447
448        let mut labels3 = HashMap::new();
449        labels3.insert("l1", "v1");
450        assert!(vec.remove(&labels3).is_err());
451    }
452
453    #[test]
454    fn test_int_counter_vec() {
455        let vec = IntCounterVec::new(Opts::new("foo", "bar"), &["l1", "l2"]).unwrap();
456
457        vec.with_label_values(&["v1", "v3"]).inc();
458        assert_eq!(vec.with_label_values(&["v1", "v3"]).get(), 1);
459
460        vec.with_label_values(&["v1", "v2"]).inc_by(12);
461        assert_eq!(vec.with_label_values(&["v1", "v3"]).get(), 1);
462        assert_eq!(vec.with_label_values(&["v1", "v2"]).get(), 12);
463
464        vec.with_label_values(&["v4", "v2"]).inc_by(2);
465        assert_eq!(vec.with_label_values(&["v1", "v3"]).get(), 1);
466        assert_eq!(vec.with_label_values(&["v1", "v2"]).get(), 12);
467        assert_eq!(vec.with_label_values(&["v4", "v2"]).get(), 2);
468
469        vec.with_label_values(&["v1", "v3"]).inc_by(5);
470        assert_eq!(vec.with_label_values(&["v1", "v3"]).get(), 6);
471        assert_eq!(vec.with_label_values(&["v1", "v2"]).get(), 12);
472        assert_eq!(vec.with_label_values(&["v4", "v2"]).get(), 2);
473    }
474
475    #[test]
476    fn test_counter_vec_with_label_values() {
477        let vec = CounterVec::new(
478            Opts::new("test_vec", "test counter vec help"),
479            &["l1", "l2"],
480        )
481        .unwrap();
482
483        assert!(vec.remove_label_values(&["v1", "v2"]).is_err());
484        vec.with_label_values(&["v1", "v2"]).inc();
485        assert!(vec.remove_label_values(&["v1", "v2"]).is_ok());
486
487        vec.with_label_values(&["v1", "v2"]).inc();
488        assert!(vec.remove_label_values(&["v1"]).is_err());
489        assert!(vec.remove_label_values(&["v1", "v3"]).is_err());
490    }
491
492    #[test]
493    fn test_counter_vec_local() {
494        let vec = CounterVec::new(
495            Opts::new("test_vec_local", "test counter vec help"),
496            &["l1", "l2"],
497        )
498        .unwrap();
499        let mut local_vec_1 = vec.local();
500        let mut local_vec_2 = local_vec_1.clone();
501
502        assert!(local_vec_1.remove_label_values(&["v1", "v2"]).is_err());
503
504        local_vec_1.with_label_values(&["v1", "v2"]).inc_by(23.0);
505        assert!((local_vec_1.with_label_values(&["v1", "v2"]).get() - 23.0) <= EPSILON);
506        assert!((vec.with_label_values(&["v1", "v2"]).get() - 0.0) <= EPSILON);
507
508        local_vec_1.flush();
509        assert!((local_vec_1.with_label_values(&["v1", "v2"]).get() - 0.0) <= EPSILON);
510        assert!((vec.with_label_values(&["v1", "v2"]).get() - 23.0) <= EPSILON);
511
512        local_vec_1.flush();
513        assert!((local_vec_1.with_label_values(&["v1", "v2"]).get() - 0.0) <= EPSILON);
514        assert!((vec.with_label_values(&["v1", "v2"]).get() - 23.0) <= EPSILON);
515
516        local_vec_1.with_label_values(&["v1", "v2"]).inc_by(11.0);
517        assert!((local_vec_1.with_label_values(&["v1", "v2"]).get() - 11.0) <= EPSILON);
518        assert!((vec.with_label_values(&["v1", "v2"]).get() - 23.0) <= EPSILON);
519
520        local_vec_1.flush();
521        assert!((local_vec_1.with_label_values(&["v1", "v2"]).get() - 0.0) <= EPSILON);
522        assert!((vec.with_label_values(&["v1", "v2"]).get() - 34.0) <= EPSILON);
523
524        // When calling `remove_label_values`, it is "flushed" immediately.
525        assert!(local_vec_1.remove_label_values(&["v1", "v2"]).is_ok());
526        assert!((local_vec_1.with_label_values(&["v1", "v2"]).get() - 0.0) <= EPSILON);
527        assert!((vec.with_label_values(&["v1", "v2"]).get() - 0.0) <= EPSILON);
528
529        local_vec_1.with_label_values(&["v1", "v2"]).inc();
530        assert!(local_vec_1.remove_label_values(&["v1"]).is_err());
531        assert!(local_vec_1.remove_label_values(&["v1", "v3"]).is_err());
532
533        local_vec_1.with_label_values(&["v1", "v2"]).inc_by(13.0);
534        assert!((local_vec_1.with_label_values(&["v1", "v2"]).get() - 14.0) <= EPSILON);
535        assert!((vec.with_label_values(&["v1", "v2"]).get() - 0.0) <= EPSILON);
536
537        local_vec_2.with_label_values(&["v1", "v2"]).inc_by(7.0);
538        assert!((local_vec_2.with_label_values(&["v1", "v2"]).get() - 7.0) <= EPSILON);
539
540        local_vec_1.flush();
541        local_vec_2.flush();
542        assert!((vec.with_label_values(&["v1", "v2"]).get() - 21.0) <= EPSILON);
543
544        local_vec_1.flush();
545        local_vec_2.flush();
546        assert!((vec.with_label_values(&["v1", "v2"]).get() - 21.0) <= EPSILON);
547    }
548
549    #[test]
550    fn test_int_counter_vec_local() {
551        let vec = IntCounterVec::new(Opts::new("foo", "bar"), &["l1", "l2"]).unwrap();
552        let mut local_vec_1 = vec.local();
553        assert!(local_vec_1.remove_label_values(&["v1", "v2"]).is_err());
554
555        local_vec_1.with_label_values(&["v1", "v2"]).inc_by(23);
556        assert_eq!(local_vec_1.with_label_values(&["v1", "v2"]).get(), 23);
557        assert_eq!(vec.with_label_values(&["v1", "v2"]).get(), 0);
558
559        local_vec_1.flush();
560        assert_eq!(local_vec_1.with_label_values(&["v1", "v2"]).get(), 0);
561        assert_eq!(vec.with_label_values(&["v1", "v2"]).get(), 23);
562
563        local_vec_1.flush();
564        assert_eq!(local_vec_1.with_label_values(&["v1", "v2"]).get(), 0);
565        assert_eq!(vec.with_label_values(&["v1", "v2"]).get(), 23);
566
567        local_vec_1.with_label_values(&["v1", "v2"]).inc_by(11);
568        assert_eq!(local_vec_1.with_label_values(&["v1", "v2"]).get(), 11);
569        assert_eq!(vec.with_label_values(&["v1", "v2"]).get(), 23);
570
571        local_vec_1.flush();
572        assert_eq!(local_vec_1.with_label_values(&["v1", "v2"]).get(), 0);
573        assert_eq!(vec.with_label_values(&["v1", "v2"]).get(), 34);
574    }
575
576    #[cfg(debug_assertions)]
577    #[test]
578    #[should_panic(expected = "assertion failed")]
579    fn test_counter_negative_inc() {
580        let counter = Counter::new("foo", "bar").unwrap();
581        counter.inc_by(-42.0);
582    }
583
584    #[cfg(debug_assertions)]
585    #[test]
586    #[should_panic(expected = "assertion failed")]
587    fn test_local_counter_negative_inc() {
588        let counter = Counter::new("foo", "bar").unwrap();
589        let local = counter.local();
590        local.inc_by(-42.0);
591    }
592}