prometheus/
gauge.rs
1use 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#[derive(Debug)]
17pub struct GenericGauge<P: Atomic> {
18 v: Arc<Value<P>>,
19}
20
21pub type Gauge = GenericGauge<AtomicF64>;
24
25pub 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 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 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 #[inline]
56 pub fn set(&self, v: P::T) {
57 self.v.set(v);
58 }
59
60 #[inline]
62 pub fn inc(&self) {
63 self.v.inc();
64 }
65
66 #[inline]
68 pub fn dec(&self) {
69 self.v.dec();
70 }
71
72 #[inline]
75 pub fn add(&self, v: P::T) {
76 self.v.inc_by(v);
77 }
78
79 #[inline]
82 pub fn sub(&self, v: P::T) {
83 self.v.dec_by(v);
84 }
85
86 #[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
137pub type GenericGaugeVec<P> = MetricVec<GaugeVecBuilder<P>>;
139
140pub type GaugeVec = GenericGaugeVec<AtomicF64>;
145
146pub type IntGaugeVec = GenericGaugeVec<AtomicI64>;
149
150impl<P: Atomic> GenericGaugeVec<P> {
151 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}