1 // Copyright 2014 The Prometheus Authors
2 // Copyright 2019 TiKV Project Authors. Licensed under Apache-2.0.
3 
4 use std::marker::PhantomData;
5 use std::sync::Arc;
6 
7 use crate::atomic64::{Atomic, AtomicF64, AtomicI64, Number};
8 use crate::desc::Desc;
9 use crate::errors::Result;
10 use crate::metrics::{Collector, Metric, Opts};
11 use crate::proto;
12 use crate::value::{Value, ValueType};
13 use crate::vec::{MetricVec, MetricVecBuilder};
14 
15 /// The underlying implementation for [`Gauge`] and [`IntGauge`].
16 #[derive(Debug)]
17 pub 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.
23 pub type Gauge = GenericGauge<AtomicF64>;
24 
25 /// The integer version of [`Gauge`]. Provides better performance if metric values are
26 /// all integers.
27 pub type IntGauge = GenericGauge<AtomicI64>;
28 
29 impl<P: Atomic> Clone for GenericGauge<P> {
clone(&self) -> Self30     fn clone(&self) -> Self {
31         Self {
32             v: Arc::clone(&self.v),
33         }
34     }
35 }
36 
37 impl<P: Atomic> GenericGauge<P> {
38     /// Create a [`GenericGauge`] with the `name` and `help` arguments.
new<S1: Into<String>, S2: Into<String>>(name: S1, help: S2) -> Result<Self>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.
with_opts(opts: Opts) -> Result<Self>45     pub fn with_opts(opts: Opts) -> Result<Self> {
46         Self::with_opts_and_label_values(&opts, &[])
47     }
48 
with_opts_and_label_values(opts: &Opts, label_values: &[&str]) -> Result<Self>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]
set(&self, v: P::T)56     pub fn set(&self, v: P::T) {
57         self.v.set(v);
58     }
59 
60     /// Increase the gauge by 1.
61     #[inline]
inc(&self)62     pub fn inc(&self) {
63         self.v.inc();
64     }
65 
66     /// Decrease the gauge by 1.
67     #[inline]
dec(&self)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]
add(&self, v: P::T)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]
sub(&self, v: P::T)82     pub fn sub(&self, v: P::T) {
83         self.v.dec_by(v);
84     }
85 
86     /// Return the gauge value.
87     #[inline]
get(&self) -> P::T88     pub fn get(&self) -> P::T {
89         self.v.get()
90     }
91 }
92 
93 impl<P: Atomic> Collector for GenericGauge<P> {
desc(&self) -> Vec<&Desc>94     fn desc(&self) -> Vec<&Desc> {
95         vec![&self.v.desc]
96     }
97 
collect(&self) -> Vec<proto::MetricFamily>98     fn collect(&self) -> Vec<proto::MetricFamily> {
99         vec![self.v.collect()]
100     }
101 }
102 
103 impl<P: Atomic> Metric for GenericGauge<P> {
metric(&self) -> proto::Metric104     fn metric(&self) -> proto::Metric {
105         self.v.metric()
106     }
107 }
108 
109 #[derive(Debug)]
110 pub struct GaugeVecBuilder<P: Atomic> {
111     _phantom: PhantomData<P>,
112 }
113 
114 impl<P: Atomic> GaugeVecBuilder<P> {
new() -> Self115     pub fn new() -> Self {
116         Self {
117             _phantom: PhantomData,
118         }
119     }
120 }
121 
122 impl<P: Atomic> Clone for GaugeVecBuilder<P> {
clone(&self) -> Self123     fn clone(&self) -> Self {
124         Self::new()
125     }
126 }
127 
128 impl<P: Atomic> MetricVecBuilder for GaugeVecBuilder<P> {
129     type M = GenericGauge<P>;
130     type P = Opts;
131 
build(&self, opts: &Opts, vals: &[&str]) -> Result<Self::M>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`].
138 pub 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).
144 pub type GaugeVec = GenericGaugeVec<AtomicF64>;
145 
146 /// The integer version of [`GaugeVec`]. Provides better performance if metric values
147 /// are all integers.
148 pub type IntGaugeVec = GenericGaugeVec<AtomicI64>;
149 
150 impl<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.
new(opts: Opts, label_names: &[&str]) -> Result<Self>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)]
164 mod tests {
165     use std::collections::HashMap;
166 
167     use super::*;
168     use crate::metrics::{Collector, Opts};
169 
170     #[test]
test_gauge()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]
test_gauge_vec_with_labels()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]
test_gauge_vec_with_label_values()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 }
241