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::<&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 #[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<V: AsRef<str>>(&self, opts: &Opts, vals: &[V]) -> 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 #[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}