prometheus/
gauge.rs

1// Copyright 2014 The Prometheus Authors
2// Copyright 2019 TiKV Project Authors. Licensed under Apache-2.0.
3
4use std::marker::PhantomData;
5use std::sync::Arc;
6
7use crate::atomic64::{Atomic, AtomicF64, AtomicI64, Number};
8use crate::desc::Desc;
9use crate::errors::Result;
10use crate::metrics::{Collector, Metric, Opts};
11use crate::proto;
12use crate::value::{Value, ValueType};
13use crate::vec::{MetricVec, MetricVecBuilder};
14
15/// The underlying implementation for [`Gauge`] and [`IntGauge`].
16#[derive(Debug)]
17pub struct GenericGauge<P: Atomic> {
18    v: Arc<Value<P>>,
19}
20
21/// A [`Metric`] represents a single numerical value that can arbitrarily go up
22/// and down.
23pub type Gauge = GenericGauge<AtomicF64>;
24
25/// The integer version of [`Gauge`]. Provides better performance if metric values are
26/// all integers.
27pub type IntGauge = GenericGauge<AtomicI64>;
28
29impl<P: Atomic> Clone for GenericGauge<P> {
30    fn clone(&self) -> Self {
31        Self {
32            v: Arc::clone(&self.v),
33        }
34    }
35}
36
37impl<P: Atomic> GenericGauge<P> {
38    /// Create a [`GenericGauge`] with the `name` and `help` arguments.
39    pub fn new<S1: Into<String>, S2: Into<String>>(name: S1, help: S2) -> Result<Self> {
40        let opts = Opts::new(name, help);
41        Self::with_opts(opts)
42    }
43
44    /// Create a [`GenericGauge`] with the `opts` options.
45    pub fn with_opts(opts: Opts) -> Result<Self> {
46        Self::with_opts_and_label_values(&opts, &[])
47    }
48
49    fn with_opts_and_label_values(opts: &Opts, label_values: &[&str]) -> Result<Self> {
50        let v = Value::new(opts, ValueType::Gauge, P::T::from_i64(0), label_values)?;
51        Ok(Self { v: Arc::new(v) })
52    }
53
54    /// Set the gauge to an arbitrary value.
55    #[inline]
56    pub fn set(&self, v: P::T) {
57        self.v.set(v);
58    }
59
60    /// Increase the gauge by 1.
61    #[inline]
62    pub fn inc(&self) {
63        self.v.inc();
64    }
65
66    /// Decrease the gauge by 1.
67    #[inline]
68    pub fn dec(&self) {
69        self.v.dec();
70    }
71
72    /// Add the given value to the gauge. (The value can be
73    /// negative, resulting in a decrement of the gauge.)
74    #[inline]
75    pub fn add(&self, v: P::T) {
76        self.v.inc_by(v);
77    }
78
79    /// Subtract the given value from the gauge. (The value can be
80    /// negative, resulting in an increment of the gauge.)
81    #[inline]
82    pub fn sub(&self, v: P::T) {
83        self.v.dec_by(v);
84    }
85
86    /// Return the gauge value.
87    #[inline]
88    pub fn get(&self) -> P::T {
89        self.v.get()
90    }
91}
92
93impl<P: Atomic> Collector for GenericGauge<P> {
94    fn desc(&self) -> Vec<&Desc> {
95        vec![&self.v.desc]
96    }
97
98    fn collect(&self) -> Vec<proto::MetricFamily> {
99        vec![self.v.collect()]
100    }
101}
102
103impl<P: Atomic> Metric for GenericGauge<P> {
104    fn metric(&self) -> proto::Metric {
105        self.v.metric()
106    }
107}
108
109#[derive(Debug)]
110pub struct GaugeVecBuilder<P: Atomic> {
111    _phantom: PhantomData<P>,
112}
113
114impl<P: Atomic> GaugeVecBuilder<P> {
115    pub fn new() -> Self {
116        Self {
117            _phantom: PhantomData,
118        }
119    }
120}
121
122impl<P: Atomic> Clone for GaugeVecBuilder<P> {
123    fn clone(&self) -> Self {
124        Self::new()
125    }
126}
127
128impl<P: Atomic> MetricVecBuilder for GaugeVecBuilder<P> {
129    type M = GenericGauge<P>;
130    type P = Opts;
131
132    fn build(&self, opts: &Opts, vals: &[&str]) -> Result<Self::M> {
133        Self::M::with_opts_and_label_values(opts, vals)
134    }
135}
136
137/// The underlying implementation for [`GaugeVec`] and [`IntGaugeVec`].
138pub type GenericGaugeVec<P> = MetricVec<GaugeVecBuilder<P>>;
139
140/// A [`Collector`] that bundles a set of [`Gauge`]s that all share
141/// the same [`Desc`], but have different values for their variable labels. This is
142/// used if you want to count the same thing partitioned by various dimensions
143/// (e.g. number of operations queued, partitioned by user and operation type).
144pub type GaugeVec = GenericGaugeVec<AtomicF64>;
145
146/// The integer version of [`GaugeVec`]. Provides better performance if metric values
147/// are all integers.
148pub type IntGaugeVec = GenericGaugeVec<AtomicI64>;
149
150impl<P: Atomic> GenericGaugeVec<P> {
151    /// Create a new [`GenericGaugeVec`] based on the provided
152    /// [`Opts`] and partitioned by the given label names. At least one label name must
153    /// be provided.
154    pub fn new(opts: Opts, label_names: &[&str]) -> Result<Self> {
155        let variable_names = label_names.iter().map(|s| (*s).to_owned()).collect();
156        let opts = opts.variable_labels(variable_names);
157        let metric_vec = MetricVec::create(proto::MetricType::GAUGE, GaugeVecBuilder::new(), opts)?;
158
159        Ok(metric_vec as Self)
160    }
161}
162
163#[cfg(test)]
164mod tests {
165    use std::collections::HashMap;
166
167    use super::*;
168    use crate::metrics::{Collector, Opts};
169
170    #[test]
171    fn test_gauge() {
172        let opts = Opts::new("test", "test help")
173            .const_label("a", "1")
174            .const_label("b", "2");
175        let gauge = Gauge::with_opts(opts).unwrap();
176        gauge.inc();
177        assert_eq!(gauge.get() as u64, 1);
178        gauge.add(42.0);
179        assert_eq!(gauge.get() as u64, 43);
180        gauge.sub(42.0);
181        assert_eq!(gauge.get() as u64, 1);
182        gauge.dec();
183        assert_eq!(gauge.get() as u64, 0);
184        gauge.set(42.0);
185        assert_eq!(gauge.get() as u64, 42);
186
187        let mut mfs = gauge.collect();
188        assert_eq!(mfs.len(), 1);
189
190        let mf = mfs.pop().unwrap();
191        let m = mf.get_metric().get(0).unwrap();
192        assert_eq!(m.get_label().len(), 2);
193        assert_eq!(m.get_gauge().get_value() as u64, 42);
194    }
195
196    #[test]
197    fn test_gauge_vec_with_labels() {
198        let vec = GaugeVec::new(
199            Opts::new("test_gauge_vec", "test gauge vec help"),
200            &["l1", "l2"],
201        )
202        .unwrap();
203
204        let mut labels = HashMap::new();
205        labels.insert("l1", "v1");
206        labels.insert("l2", "v2");
207        assert!(vec.remove(&labels).is_err());
208
209        vec.with(&labels).inc();
210        vec.with(&labels).dec();
211        vec.with(&labels).add(42.0);
212        vec.with(&labels).sub(42.0);
213        vec.with(&labels).set(42.0);
214
215        assert!(vec.remove(&labels).is_ok());
216        assert!(vec.remove(&labels).is_err());
217    }
218
219    #[test]
220    fn test_gauge_vec_with_label_values() {
221        let vec = GaugeVec::new(
222            Opts::new("test_gauge_vec", "test gauge vec help"),
223            &["l1", "l2"],
224        )
225        .unwrap();
226
227        assert!(vec.remove_label_values(&["v1", "v2"]).is_err());
228        vec.with_label_values(&["v1", "v2"]).inc();
229        assert!(vec.remove_label_values(&["v1", "v2"]).is_ok());
230
231        vec.with_label_values(&["v1", "v2"]).inc();
232        vec.with_label_values(&["v1", "v2"]).dec();
233        vec.with_label_values(&["v1", "v2"]).add(42.0);
234        vec.with_label_values(&["v1", "v2"]).sub(42.0);
235        vec.with_label_values(&["v1", "v2"]).set(42.0);
236
237        assert!(vec.remove_label_values(&["v1"]).is_err());
238        assert!(vec.remove_label_values(&["v1", "v3"]).is_err());
239    }
240}