prometheus/
vec.rs

1// Copyright 2014 The Prometheus Authors
2// Copyright 2019 TiKV Project Authors. Licensed under Apache-2.0.
3
4use std::collections::HashMap;
5use std::hash::Hasher;
6use std::sync::Arc;
7
8use fnv::FnvHasher;
9use parking_lot::RwLock;
10
11use crate::desc::{Desc, Describer};
12use crate::errors::{Error, Result};
13use crate::metrics::{Collector, Metric};
14use crate::proto::{MetricFamily, MetricType};
15
16/// An interface for building a metric vector.
17pub trait MetricVecBuilder: Send + Sync + Clone {
18    /// The associated Metric collected.
19    type M: Metric;
20    /// The associated describer.
21    type P: Describer + Sync + Send + Clone;
22
23    /// `build` builds a [`Metric`] with option and corresponding label names.
24    fn build(&self, _: &Self::P, _: &[&str]) -> Result<Self::M>;
25}
26
27#[derive(Debug)]
28pub(crate) struct MetricVecCore<T: MetricVecBuilder> {
29    pub children: RwLock<HashMap<u64, T::M>>,
30    pub desc: Desc,
31    pub metric_type: MetricType,
32    pub new_metric: T,
33    pub opts: T::P,
34}
35
36impl<T: MetricVecBuilder> MetricVecCore<T> {
37    pub fn collect(&self) -> MetricFamily {
38        let mut m = MetricFamily::default();
39        m.set_name(self.desc.fq_name.clone());
40        m.set_help(self.desc.help.clone());
41        m.set_field_type(self.metric_type);
42
43        let children = self.children.read();
44        let mut metrics = Vec::with_capacity(children.len());
45        for child in children.values() {
46            metrics.push(child.metric());
47        }
48        m.set_metric(from_vec!(metrics));
49        m
50    }
51
52    pub fn get_metric_with_label_values(&self, vals: &[&str]) -> Result<T::M> {
53        let h = self.hash_label_values(vals)?;
54
55        if let Some(metric) = self.children.read().get(&h).cloned() {
56            return Ok(metric);
57        }
58
59        self.get_or_create_metric(h, vals)
60    }
61
62    pub fn get_metric_with(&self, labels: &HashMap<&str, &str>) -> Result<T::M> {
63        let h = self.hash_labels(labels)?;
64
65        if let Some(metric) = self.children.read().get(&h).cloned() {
66            return Ok(metric);
67        }
68
69        let vals = self.get_label_values(labels)?;
70        self.get_or_create_metric(h, &vals)
71    }
72
73    pub fn delete_label_values(&self, vals: &[&str]) -> Result<()> {
74        let h = self.hash_label_values(vals)?;
75
76        let mut children = self.children.write();
77        if children.remove(&h).is_none() {
78            return Err(Error::Msg(format!("missing label values {:?}", vals)));
79        }
80
81        Ok(())
82    }
83
84    pub fn delete(&self, labels: &HashMap<&str, &str>) -> Result<()> {
85        let h = self.hash_labels(labels)?;
86
87        let mut children = self.children.write();
88        if children.remove(&h).is_none() {
89            return Err(Error::Msg(format!("missing labels {:?}", labels)));
90        }
91
92        Ok(())
93    }
94
95    /// `reset` deletes all metrics in this vector.
96    pub fn reset(&self) {
97        self.children.write().clear();
98    }
99
100    pub(crate) fn hash_label_values(&self, vals: &[&str]) -> Result<u64> {
101        if vals.len() != self.desc.variable_labels.len() {
102            return Err(Error::InconsistentCardinality {
103                expect: self.desc.variable_labels.len(),
104                got: vals.len(),
105            });
106        }
107
108        let mut h = FnvHasher::default();
109        for val in vals {
110            h.write(val.as_bytes());
111        }
112
113        Ok(h.finish())
114    }
115
116    fn hash_labels(&self, labels: &HashMap<&str, &str>) -> Result<u64> {
117        if labels.len() != self.desc.variable_labels.len() {
118            return Err(Error::InconsistentCardinality {
119                expect: self.desc.variable_labels.len(),
120                got: labels.len(),
121            });
122        }
123
124        let mut h = FnvHasher::default();
125        for name in &self.desc.variable_labels {
126            match labels.get(&name.as_ref()) {
127                Some(val) => h.write(val.as_bytes()),
128                None => {
129                    return Err(Error::Msg(format!(
130                        "label name {} missing in label map",
131                        name
132                    )));
133                }
134            }
135        }
136
137        Ok(h.finish())
138    }
139
140    fn get_label_values<'a>(&self, labels: &'a HashMap<&str, &str>) -> Result<Vec<&'a str>> {
141        let mut values = Vec::new();
142        for name in &self.desc.variable_labels {
143            match labels.get(&name.as_ref()) {
144                Some(val) => values.push(*val),
145                None => {
146                    return Err(Error::Msg(format!(
147                        "label name {} missing in label map",
148                        name
149                    )));
150                }
151            }
152        }
153        Ok(values)
154    }
155
156    fn get_or_create_metric(&self, hash: u64, label_values: &[&str]) -> Result<T::M> {
157        let mut children = self.children.write();
158        // Check exist first.
159        if let Some(metric) = children.get(&hash).cloned() {
160            return Ok(metric);
161        }
162
163        let metric = self.new_metric.build(&self.opts, label_values)?;
164        children.insert(hash, metric.clone());
165        Ok(metric)
166    }
167}
168
169/// A [`Collector`] to bundle metrics of the same name that
170/// differ in their label values. It is usually not used directly but as a
171/// building block for implementations of vectors of a given metric
172/// type. [`GaugeVec`](crate::GaugeVec) and [`CounterVec`](crate::CounterVec)
173/// are examples already provided in this package.
174#[derive(Clone)]
175pub struct MetricVec<T: MetricVecBuilder> {
176    pub(crate) v: Arc<MetricVecCore<T>>,
177}
178
179impl<T: MetricVecBuilder> std::fmt::Debug for MetricVec<T> {
180    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
181        write!(f, "MetricVec")
182    }
183}
184
185impl<T: MetricVecBuilder> MetricVec<T> {
186    /// `create` creates a MetricVec with description `desc`, a metric type `metric_type` and
187    /// a MetricVecBuilder `new_metric`.
188    pub fn create(metric_type: MetricType, new_metric: T, opts: T::P) -> Result<MetricVec<T>> {
189        let desc = opts.describe()?;
190        let v = MetricVecCore {
191            children: RwLock::new(HashMap::new()),
192            desc,
193            metric_type,
194            new_metric,
195            opts,
196        };
197
198        Ok(MetricVec { v: Arc::new(v) })
199    }
200
201    /// `get_metric_with_label_values` returns the [`Metric`] for the given slice
202    /// of label values (same order as the VariableLabels in Desc). If that combination of
203    /// label values is accessed for the first time, a new [`Metric`] is created.
204    ///
205    /// It is possible to call this method without using the returned [`Metric`]
206    /// to only create the new [`Metric`] but leave it at its start value (e.g. a
207    /// [`Histogram`](crate::Histogram) without any observations).
208    ///
209    /// Keeping the [`Metric`] for later use is possible (and should be considered
210    /// if performance is critical), but keep in mind that Reset, DeleteLabelValues and Delete can
211    /// be used to delete the [`Metric`] from the MetricVec. In that case, the
212    /// [`Metric`] will still exist, but it will not be exported anymore, even if a
213    /// [`Metric`] with the same label values is created later. See also the
214    /// CounterVec example.
215    ///
216    /// An error is returned if the number of label values is not the same as the
217    /// number of VariableLabels in Desc.
218    ///
219    /// Note that for more than one label value, this method is prone to mistakes
220    /// caused by an incorrect order of arguments. Consider get_metric_with(labels) as
221    /// an alternative to avoid that type of mistake. For higher label numbers, the
222    /// latter has a much more readable (albeit more verbose) syntax, but it comes
223    /// with a performance overhead (for creating and processing the Labels map).
224    pub fn get_metric_with_label_values(&self, vals: &[&str]) -> Result<T::M> {
225        self.v.get_metric_with_label_values(vals)
226    }
227
228    /// `get_metric_with` returns the [`Metric`] for the given Labels map (the
229    /// label names must match those of the VariableLabels in Desc). If that label map is
230    /// accessed for the first time, a new [`Metric`] is created. Implications of
231    /// creating a [`Metric`] without using it and keeping the
232    /// [`Metric`] for later use are the same as for GetMetricWithLabelValues.
233    ///
234    /// An error is returned if the number and names of the Labels are inconsistent
235    /// with those of the VariableLabels in Desc.
236    ///
237    /// This method is used for the same purpose as
238    /// `get_metric_with_label_values`. See there for pros and cons of the two
239    /// methods.
240    pub fn get_metric_with(&self, labels: &HashMap<&str, &str>) -> Result<T::M> {
241        self.v.get_metric_with(labels)
242    }
243
244    /// `with_label_values` works as `get_metric_with_label_values`, but panics if an error
245    /// occurs.
246    ///
247    /// # Examples
248    ///
249    /// ```
250    /// use prometheus::{CounterVec, Opts};
251    /// let vec = CounterVec::new(
252    ///     Opts::new("requests_total", "Number of requests."),
253    ///     &["code", "http_method"]
254    /// ).unwrap();
255    /// vec.with_label_values(&["404", "POST"]).inc()
256    /// ```
257    pub fn with_label_values(&self, vals: &[&str]) -> T::M {
258        self.get_metric_with_label_values(vals).unwrap()
259    }
260
261    /// `with` works as `get_metric_with`, but panics if an error occurs. The method allows
262    /// neat syntax like:
263    ///     httpReqs.with(Labels{"status":"404", "method":"POST"}).inc()
264    pub fn with(&self, labels: &HashMap<&str, &str>) -> T::M {
265        self.get_metric_with(labels).unwrap()
266    }
267
268    /// `remove_label_values` removes the metric where the variable labels are the same
269    /// as those passed in as labels (same order as the VariableLabels in Desc). It
270    /// returns true if a metric was deleted.
271    ///
272    /// It returns an error if the number of label values is not the same as the
273    /// number of VariableLabels in Desc.
274    ///
275    /// Note that for more than one label value, this method is prone to mistakes
276    /// caused by an incorrect order of arguments. Consider delete(labels) as an
277    /// alternative to avoid that type of mistake. For higher label numbers, the
278    /// latter has a much more readable (albeit more verbose) syntax, but it comes
279    /// with a performance overhead (for creating and processing the Labels map).
280    pub fn remove_label_values(&self, vals: &[&str]) -> Result<()> {
281        self.v.delete_label_values(vals)
282    }
283
284    /// `remove` removes the metric where the variable labels are the same as those
285    /// passed in as labels. It returns true if a metric was deleted.
286    ///
287    /// It returns an error if the number and names of the Labels are inconsistent
288    /// with those of the VariableLabels in the Desc of the MetricVec.
289    ///
290    /// This method is used for the same purpose as `delete_label_values`. See
291    /// there for pros and cons of the two methods.
292    pub fn remove(&self, labels: &HashMap<&str, &str>) -> Result<()> {
293        self.v.delete(labels)
294    }
295
296    /// `reset` deletes all metrics in this vector.
297    pub fn reset(&self) {
298        self.v.reset()
299    }
300}
301
302impl<T: MetricVecBuilder> Collector for MetricVec<T> {
303    fn desc(&self) -> Vec<&Desc> {
304        vec![&self.v.desc]
305    }
306
307    fn collect(&self) -> Vec<MetricFamily> {
308        vec![self.v.collect()]
309    }
310}
311
312#[cfg(test)]
313mod tests {
314    use std::collections::HashMap;
315
316    use crate::counter::CounterVec;
317    use crate::gauge::GaugeVec;
318    use crate::metrics::{Metric, Opts};
319
320    #[test]
321    fn test_counter_vec_with_labels() {
322        let vec = CounterVec::new(
323            Opts::new("test_couter_vec", "test counter vec help"),
324            &["l1", "l2"],
325        )
326        .unwrap();
327
328        let mut labels = HashMap::new();
329        labels.insert("l1", "v1");
330        labels.insert("l2", "v2");
331        assert!(vec.remove(&labels).is_err());
332
333        vec.with(&labels).inc();
334        assert!(vec.remove(&labels).is_ok());
335        assert!(vec.remove(&labels).is_err());
336
337        let mut labels2 = HashMap::new();
338        labels2.insert("l1", "v2");
339        labels2.insert("l2", "v1");
340
341        vec.with(&labels).inc();
342        assert!(vec.remove(&labels2).is_err());
343
344        vec.with(&labels).inc();
345
346        let mut labels3 = HashMap::new();
347        labels3.insert("l1", "v1");
348        assert!(vec.remove(&labels3).is_err());
349    }
350
351    #[test]
352    fn test_counter_vec_with_label_values() {
353        let vec = CounterVec::new(
354            Opts::new("test_vec", "test counter vec help"),
355            &["l1", "l2"],
356        )
357        .unwrap();
358
359        assert!(vec.remove_label_values(&["v1", "v2"]).is_err());
360        vec.with_label_values(&["v1", "v2"]).inc();
361        assert!(vec.remove_label_values(&["v1", "v2"]).is_ok());
362
363        vec.with_label_values(&["v1", "v2"]).inc();
364        assert!(vec.remove_label_values(&["v1"]).is_err());
365        assert!(vec.remove_label_values(&["v1", "v3"]).is_err());
366    }
367
368    #[test]
369    fn test_gauge_vec_with_labels() {
370        let vec = GaugeVec::new(
371            Opts::new("test_gauge_vec", "test gauge vec help"),
372            &["l1", "l2"],
373        )
374        .unwrap();
375
376        let mut labels = HashMap::new();
377        labels.insert("l1", "v1");
378        labels.insert("l2", "v2");
379        assert!(vec.remove(&labels).is_err());
380
381        vec.with(&labels).inc();
382        vec.with(&labels).dec();
383        vec.with(&labels).add(42.0);
384        vec.with(&labels).sub(42.0);
385        vec.with(&labels).set(42.0);
386
387        assert!(vec.remove(&labels).is_ok());
388        assert!(vec.remove(&labels).is_err());
389    }
390
391    #[test]
392    fn test_gauge_vec_with_label_values() {
393        let vec = GaugeVec::new(
394            Opts::new("test_gauge_vec", "test gauge vec help"),
395            &["l1", "l2"],
396        )
397        .unwrap();
398
399        assert!(vec.remove_label_values(&["v1", "v2"]).is_err());
400        vec.with_label_values(&["v1", "v2"]).inc();
401        assert!(vec.remove_label_values(&["v1", "v2"]).is_ok());
402
403        vec.with_label_values(&["v1", "v2"]).inc();
404        vec.with_label_values(&["v1", "v2"]).dec();
405        vec.with_label_values(&["v1", "v2"]).add(42.0);
406        vec.with_label_values(&["v1", "v2"]).sub(42.0);
407        vec.with_label_values(&["v1", "v2"]).set(42.0);
408
409        assert!(vec.remove_label_values(&["v1"]).is_err());
410        assert!(vec.remove_label_values(&["v1", "v3"]).is_err());
411    }
412
413    #[test]
414    fn test_vec_get_metric_with() {
415        let vec = CounterVec::new(
416            Opts::new("test_vec", "test counter vec help"),
417            &["b", "c", "a"],
418        )
419        .unwrap();
420
421        // create a new metric that labels are {b" => "c", "c" => "a" "a" => "b"}.
422        let mut labels = HashMap::new();
423        labels.insert("a", "b");
424        labels.insert("b", "c");
425        labels.insert("c", "a");
426        let c = vec.get_metric_with(&labels).unwrap();
427        let m = c.metric();
428        let label_pairs = m.get_label();
429        assert_eq!(label_pairs.len(), labels.len());
430        for lp in label_pairs.iter() {
431            assert_eq!(lp.get_value(), labels[lp.get_name()]);
432        }
433    }
434}