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::<&str>(&opts, &[])
47    }
48
49    fn with_opts_and_label_values<V: AsRef<str>>(opts: &Opts, label_values: &[V]) -> 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<V: AsRef<str>>(&self, opts: &Opts, vals: &[V]) -> 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    #[cfg(feature = "protobuf")]
170    use crate::proto_ext::MessageFieldExt;
171
172    #[test]
173    fn test_gauge() {
174        let opts = Opts::new("test", "test help")
175            .const_label("a", "1")
176            .const_label("b", "2");
177        let gauge = Gauge::with_opts(opts).unwrap();
178        gauge.inc();
179        assert_eq!(gauge.get() as u64, 1);
180        gauge.add(42.0);
181        assert_eq!(gauge.get() as u64, 43);
182        gauge.sub(42.0);
183        assert_eq!(gauge.get() as u64, 1);
184        gauge.dec();
185        assert_eq!(gauge.get() as u64, 0);
186        gauge.set(42.0);
187        assert_eq!(gauge.get() as u64, 42);
188
189        let mut mfs = gauge.collect();
190        assert_eq!(mfs.len(), 1);
191
192        let mf = mfs.pop().unwrap();
193        let m = mf.get_metric().first().unwrap();
194        assert_eq!(m.get_label().len(), 2);
195        assert_eq!(m.get_gauge().get_value() as u64, 42);
196    }
197
198    #[test]
199    fn test_gauge_vec_with_labels() {
200        let vec = GaugeVec::new(
201            Opts::new("test_gauge_vec", "test gauge vec help"),
202            &["l1", "l2"],
203        )
204        .unwrap();
205
206        let mut labels = HashMap::new();
207        labels.insert("l1", "v1");
208        labels.insert("l2", "v2");
209        assert!(vec.remove(&labels).is_err());
210
211        vec.with(&labels).inc();
212        vec.with(&labels).dec();
213        vec.with(&labels).add(42.0);
214        vec.with(&labels).sub(42.0);
215        vec.with(&labels).set(42.0);
216
217        assert!(vec.remove(&labels).is_ok());
218        assert!(vec.remove(&labels).is_err());
219    }
220
221    #[test]
222    fn test_gauge_vec_with_owned_labels() {
223        let vec = GaugeVec::new(
224            Opts::new("test_gauge_vec", "test gauge vec help"),
225            &["l1", "l2"],
226        )
227        .unwrap();
228
229        let mut labels = HashMap::new();
230        labels.insert("l1", "v1");
231        labels.insert("l2", "v2");
232        assert!(vec.remove(&labels).is_err());
233
234        vec.with(&labels).inc();
235        vec.with(&labels).dec();
236        vec.with(&labels).add(42.0);
237        vec.with(&labels).sub(42.0);
238        vec.with(&labels).set(42.0);
239
240        assert!(vec.remove(&labels).is_ok());
241        assert!(vec.remove(&labels).is_err());
242    }
243
244    #[test]
245    fn test_gauge_vec_with_label_values() {
246        let vec = GaugeVec::new(
247            Opts::new("test_gauge_vec", "test gauge vec help"),
248            &["l1", "l2"],
249        )
250        .unwrap();
251
252        assert!(vec.remove_label_values(&["v1", "v2"]).is_err());
253        vec.with_label_values(&["v1", "v2"]).inc();
254        assert!(vec.remove_label_values(&["v1", "v2"]).is_ok());
255
256        vec.with_label_values(&["v1", "v2"]).inc();
257        vec.with_label_values(&["v1", "v2"]).dec();
258        vec.with_label_values(&["v1", "v2"]).add(42.0);
259        vec.with_label_values(&["v1", "v2"]).sub(42.0);
260        vec.with_label_values(&["v1", "v2"]).set(42.0);
261
262        assert!(vec.remove_label_values(&["v1"]).is_err());
263        assert!(vec.remove_label_values(&["v1", "v3"]).is_err());
264    }
265
266    #[test]
267    fn test_gauge_vec_with_owned_label_values() {
268        let vec = GaugeVec::new(
269            Opts::new("test_gauge_vec", "test gauge vec help"),
270            &["l1", "l2"],
271        )
272        .unwrap();
273
274        let v1 = "v1".to_string();
275        let v2 = "v2".to_string();
276        let v3 = "v3".to_string();
277
278        assert!(vec.remove_label_values(&[v1.clone(), v2.clone()]).is_err());
279        vec.with_label_values(&[v1.clone(), v2.clone()]).inc();
280        assert!(vec.remove_label_values(&[v1.clone(), v2.clone()]).is_ok());
281
282        vec.with_label_values(&[v1.clone(), v2.clone()]).inc();
283        vec.with_label_values(&[v1.clone(), v2.clone()]).dec();
284        vec.with_label_values(&[v1.clone(), v2.clone()]).add(42.0);
285        vec.with_label_values(&[v1.clone(), v2.clone()]).sub(42.0);
286        vec.with_label_values(&[v1.clone(), v2.clone()]).set(42.0);
287
288        assert!(vec.remove_label_values(&[v1.clone()]).is_err());
289        assert!(vec.remove_label_values(&[v1.clone(), v3.clone()]).is_err());
290    }
291}