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::{BuildHasher, 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::nohash::BuildNoHashHasher;
15use crate::proto::{MetricFamily, MetricType};
16
17/// An interface for building a metric vector.
18pub trait MetricVecBuilder: Send + Sync + Clone {
19    /// The associated Metric collected.
20    type M: Metric;
21    /// The associated describer.
22    type P: Describer + Sync + Send + Clone;
23
24    /// `build` builds a [`Metric`] with option and corresponding label names.
25    fn build<V: AsRef<str>>(&self, _: &Self::P, _: &[V]) -> Result<Self::M>;
26}
27
28#[derive(Debug)]
29pub(crate) struct MetricVecCore<T: MetricVecBuilder> {
30    // the key is pre-hashed, and so we use a no-hash hasher to avoid hashing again.
31    pub children: RwLock<HashMap<u64, T::M, BuildNoHashHasher>>,
32    pub desc: Desc,
33    pub metric_type: MetricType,
34    pub new_metric: T,
35    pub opts: T::P,
36}
37
38impl<T: MetricVecBuilder> MetricVecCore<T> {
39    pub fn collect(&self) -> MetricFamily {
40        let mut m = MetricFamily::default();
41        m.set_name(self.desc.fq_name.clone());
42        m.set_help(self.desc.help.clone());
43        m.set_field_type(self.metric_type);
44
45        let children = self.children.read();
46        let mut metrics = Vec::with_capacity(children.len());
47        for child in children.values() {
48            metrics.push(child.metric());
49        }
50        m.set_metric(metrics);
51        m
52    }
53
54    pub fn get_metric_with_label_values<V>(&self, vals: &[V]) -> Result<T::M>
55    where
56        V: AsRef<str> + std::fmt::Debug,
57    {
58        let h = self.hash_label_values(vals)?;
59
60        if let Some(metric) = self.children.read().get(&h).cloned() {
61            return Ok(metric);
62        }
63
64        self.get_or_create_metric(h, vals)
65    }
66
67    pub fn get_metric_with<V, S: BuildHasher>(&self, labels: &HashMap<&str, V, S>) -> Result<T::M>
68    where
69        V: AsRef<str> + std::fmt::Debug,
70    {
71        let h = self.hash_labels(labels)?;
72
73        if let Some(metric) = self.children.read().get(&h).cloned() {
74            return Ok(metric);
75        }
76
77        let vals = self.get_label_values(labels)?;
78        self.get_or_create_metric(h, &vals)
79    }
80
81    pub fn delete_label_values<V>(&self, vals: &[V]) -> Result<()>
82    where
83        V: AsRef<str> + std::fmt::Debug,
84    {
85        let h = self.hash_label_values(vals)?;
86
87        let mut children = self.children.write();
88        if children.remove(&h).is_none() {
89            return Err(Error::Msg(format!("missing label values {:?}", vals)));
90        }
91
92        Ok(())
93    }
94
95    pub fn delete<V, S: BuildHasher>(&self, labels: &HashMap<&str, V, S>) -> Result<()>
96    where
97        V: AsRef<str> + std::fmt::Debug,
98    {
99        let h = self.hash_labels(labels)?;
100
101        let mut children = self.children.write();
102        if children.remove(&h).is_none() {
103            return Err(Error::Msg(format!("missing labels {:?}", labels)));
104        }
105
106        Ok(())
107    }
108
109    /// `reset` deletes all metrics in this vector.
110    pub fn reset(&self) {
111        self.children.write().clear();
112    }
113
114    pub(crate) fn hash_label_values<V>(&self, vals: &[V]) -> Result<u64>
115    where
116        V: AsRef<str> + std::fmt::Debug,
117    {
118        if vals.len() != self.desc.variable_labels.len() {
119            return Err(Error::InconsistentCardinality {
120                expect: self.desc.variable_labels.len(),
121                got: vals.len(),
122            });
123        }
124
125        let mut h = FnvHasher::default();
126        for val in vals {
127            h.write(val.as_ref().as_bytes());
128        }
129
130        Ok(h.finish())
131    }
132
133    fn hash_labels<V, S: BuildHasher>(&self, labels: &HashMap<&str, V, S>) -> Result<u64>
134    where
135        V: AsRef<str> + std::fmt::Debug,
136    {
137        if labels.len() != self.desc.variable_labels.len() {
138            return Err(Error::InconsistentCardinality {
139                expect: self.desc.variable_labels.len(),
140                got: labels.len(),
141            });
142        }
143
144        let mut h = FnvHasher::default();
145        for name in &self.desc.variable_labels {
146            match labels.get(&name.as_ref()) {
147                Some(val) => h.write(val.as_ref().as_bytes()),
148                None => {
149                    return Err(Error::Msg(format!(
150                        "label name {} missing in label map",
151                        name
152                    )));
153                }
154            }
155        }
156
157        Ok(h.finish())
158    }
159
160    fn get_label_values<'a, V, S: BuildHasher>(
161        &'a self,
162        labels: &'a HashMap<&str, V, S>,
163    ) -> Result<Vec<&'a str>>
164    where
165        V: AsRef<str> + std::fmt::Debug,
166    {
167        let mut values = Vec::new();
168        for name in &self.desc.variable_labels {
169            match labels.get(&name.as_ref()) {
170                Some(val) => values.push(val.as_ref()),
171                None => {
172                    return Err(Error::Msg(format!(
173                        "label name {} missing in label map",
174                        name
175                    )));
176                }
177            }
178        }
179        Ok(values)
180    }
181
182    fn get_or_create_metric<V>(&self, hash: u64, label_values: &[V]) -> Result<T::M>
183    where
184        V: AsRef<str> + std::fmt::Debug,
185    {
186        let mut children = self.children.write();
187        // Check exist first.
188        if let Some(metric) = children.get(&hash).cloned() {
189            return Ok(metric);
190        }
191
192        let metric = self.new_metric.build(&self.opts, label_values)?;
193        children.insert(hash, metric.clone());
194        Ok(metric)
195    }
196}
197
198/// A [`Collector`] to bundle metrics of the same name that
199/// differ in their label values. It is usually not used directly but as a
200/// building block for implementations of vectors of a given metric
201/// type. [`GaugeVec`](crate::GaugeVec) and [`CounterVec`](crate::CounterVec)
202/// are examples already provided in this package.
203#[derive(Clone)]
204pub struct MetricVec<T: MetricVecBuilder> {
205    pub(crate) v: Arc<MetricVecCore<T>>,
206}
207
208impl<T: MetricVecBuilder> std::fmt::Debug for MetricVec<T> {
209    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
210        write!(f, "MetricVec")
211    }
212}
213
214impl<T: MetricVecBuilder> MetricVec<T> {
215    /// `create` creates a MetricVec with description `desc`, a metric type `metric_type` and
216    /// a MetricVecBuilder `new_metric`.
217    pub fn create(metric_type: MetricType, new_metric: T, opts: T::P) -> Result<MetricVec<T>> {
218        let desc = opts.describe()?;
219        let v = MetricVecCore {
220            children: RwLock::new(HashMap::default()),
221            desc,
222            metric_type,
223            new_metric,
224            opts,
225        };
226
227        Ok(MetricVec { v: Arc::new(v) })
228    }
229
230    /// `get_metric_with_label_values` returns the [`Metric`] for the given slice
231    /// of label values (same order as the VariableLabels in Desc). If that combination of
232    /// label values is accessed for the first time, a new [`Metric`] is created.
233    ///
234    /// It is possible to call this method without using the returned [`Metric`]
235    /// to only create the new [`Metric`] but leave it at its start value (e.g. a
236    /// [`Histogram`](crate::Histogram) without any observations).
237    ///
238    /// Keeping the [`Metric`] for later use is possible (and should be considered
239    /// if performance is critical), but keep in mind that Reset, DeleteLabelValues and Delete can
240    /// be used to delete the [`Metric`] from the MetricVec. In that case, the
241    /// [`Metric`] will still exist, but it will not be exported anymore, even if a
242    /// [`Metric`] with the same label values is created later. See also the
243    /// CounterVec example.
244    ///
245    /// An error is returned if the number of label values is not the same as the
246    /// number of VariableLabels in Desc.
247    ///
248    /// Note that for more than one label value, this method is prone to mistakes
249    /// caused by an incorrect order of arguments. Consider get_metric_with(labels) as
250    /// an alternative to avoid that type of mistake. For higher label numbers, the
251    /// latter has a much more readable (albeit more verbose) syntax, but it comes
252    /// with a performance overhead (for creating and processing the Labels map).
253    pub fn get_metric_with_label_values<V>(&self, vals: &[V]) -> Result<T::M>
254    where
255        V: AsRef<str> + std::fmt::Debug,
256    {
257        self.v.get_metric_with_label_values(vals)
258    }
259
260    /// `get_metric_with` returns the [`Metric`] for the given Labels map (the
261    /// label names must match those of the VariableLabels in Desc). If that label map is
262    /// accessed for the first time, a new [`Metric`] is created. Implications of
263    /// creating a [`Metric`] without using it and keeping the
264    /// [`Metric`] for later use are the same as for GetMetricWithLabelValues.
265    ///
266    /// An error is returned if the number and names of the Labels are inconsistent
267    /// with those of the VariableLabels in Desc.
268    ///
269    /// This method is used for the same purpose as
270    /// `get_metric_with_label_values`. See there for pros and cons of the two
271    /// methods.
272    pub fn get_metric_with<V, S: BuildHasher>(&self, labels: &HashMap<&str, V, S>) -> Result<T::M>
273    where
274        V: AsRef<str> + std::fmt::Debug,
275    {
276        self.v.get_metric_with(labels)
277    }
278
279    /// `with_label_values` works as `get_metric_with_label_values`, but panics if an error
280    /// occurs.
281    ///
282    /// # Examples
283    ///
284    /// ```
285    /// use prometheus::{CounterVec, Opts};
286    /// let vec = CounterVec::new(
287    ///     Opts::new("requests_total", "Number of requests."),
288    ///     &["code", "http_method"]
289    /// ).unwrap();
290    /// vec.with_label_values(&["404", "POST"]).inc()
291    /// ```
292    pub fn with_label_values<V>(&self, vals: &[V]) -> T::M
293    where
294        V: AsRef<str> + std::fmt::Debug,
295    {
296        self.get_metric_with_label_values(vals).unwrap()
297    }
298
299    /// `with` works as `get_metric_with`, but panics if an error occurs. The method allows
300    /// neat syntax like:
301    ///     httpReqs.with(Labels{"status":"404", "method":"POST"}).inc()
302    pub fn with<V, S: BuildHasher>(&self, labels: &HashMap<&str, V, S>) -> T::M
303    where
304        V: AsRef<str> + std::fmt::Debug,
305    {
306        self.get_metric_with(labels).unwrap()
307    }
308
309    /// `remove_label_values` removes the metric where the variable labels are the same
310    /// as those passed in as labels (same order as the VariableLabels in Desc). It
311    /// returns true if a metric was deleted.
312    ///
313    /// It returns an error if the number of label values is not the same as the
314    /// number of VariableLabels in Desc.
315    ///
316    /// Note that for more than one label value, this method is prone to mistakes
317    /// caused by an incorrect order of arguments. Consider delete(labels) as an
318    /// alternative to avoid that type of mistake. For higher label numbers, the
319    /// latter has a much more readable (albeit more verbose) syntax, but it comes
320    /// with a performance overhead (for creating and processing the Labels map).
321    pub fn remove_label_values<V>(&self, vals: &[V]) -> Result<()>
322    where
323        V: AsRef<str> + std::fmt::Debug,
324    {
325        self.v.delete_label_values(vals)
326    }
327
328    /// `remove` removes the metric where the variable labels are the same as those
329    /// passed in as labels. It returns true if a metric was deleted.
330    ///
331    /// It returns an error if the number and names of the Labels are inconsistent
332    /// with those of the VariableLabels in the Desc of the MetricVec.
333    ///
334    /// This method is used for the same purpose as `delete_label_values`. See
335    /// there for pros and cons of the two methods.
336    pub fn remove<V, S: BuildHasher>(&self, labels: &HashMap<&str, V, S>) -> Result<()>
337    where
338        V: AsRef<str> + std::fmt::Debug,
339    {
340        self.v.delete(labels)
341    }
342
343    /// `reset` deletes all metrics in this vector.
344    pub fn reset(&self) {
345        self.v.reset()
346    }
347}
348
349impl<T: MetricVecBuilder> Collector for MetricVec<T> {
350    fn desc(&self) -> Vec<&Desc> {
351        vec![&self.v.desc]
352    }
353
354    fn collect(&self) -> Vec<MetricFamily> {
355        vec![self.v.collect()]
356    }
357}
358
359#[cfg(test)]
360mod tests {
361    use std::collections::HashMap;
362
363    use crate::counter::CounterVec;
364    use crate::gauge::GaugeVec;
365    use crate::metrics::{Metric, Opts};
366
367    #[test]
368    fn test_counter_vec_with_labels() {
369        let vec = CounterVec::new(
370            Opts::new("test_couter_vec", "test counter vec help"),
371            &["l1", "l2"],
372        )
373        .unwrap();
374
375        let mut labels = HashMap::new();
376        labels.insert("l1", "v1");
377        labels.insert("l2", "v2");
378        assert!(vec.remove(&labels).is_err());
379
380        vec.with(&labels).inc();
381        assert!(vec.remove(&labels).is_ok());
382        assert!(vec.remove(&labels).is_err());
383
384        let mut labels2 = HashMap::new();
385        labels2.insert("l1", "v2");
386        labels2.insert("l2", "v1");
387
388        vec.with(&labels).inc();
389        assert!(vec.remove(&labels2).is_err());
390
391        vec.with(&labels).inc();
392
393        let mut labels3 = HashMap::new();
394        labels3.insert("l1", "v1");
395        assert!(vec.remove(&labels3).is_err());
396    }
397
398    #[test]
399    fn test_counter_vec_with_owned_labels() {
400        let vec = CounterVec::new(
401            Opts::new("test_couter_vec", "test counter vec help"),
402            &["l1", "l2"],
403        )
404        .unwrap();
405
406        let v1 = "v1".to_string();
407        let v2 = "v2".to_string();
408
409        let mut labels = HashMap::new();
410        labels.insert("l1", v1.clone());
411        labels.insert("l2", v2.clone());
412        assert!(vec.remove(&labels).is_err());
413
414        vec.with(&labels).inc();
415        assert!(vec.remove(&labels).is_ok());
416        assert!(vec.remove(&labels).is_err());
417
418        let mut labels2 = HashMap::new();
419        labels2.insert("l1", v2.clone());
420        labels2.insert("l2", v1.clone());
421
422        vec.with(&labels).inc();
423        assert!(vec.remove(&labels2).is_err());
424
425        vec.with(&labels).inc();
426
427        let mut labels3 = HashMap::new();
428        labels3.insert("l1", v1.clone());
429        assert!(vec.remove(&labels3).is_err());
430    }
431
432    #[test]
433    fn test_counter_vec_with_label_values() {
434        let vec = CounterVec::new(
435            Opts::new("test_vec", "test counter vec help"),
436            &["l1", "l2"],
437        )
438        .unwrap();
439
440        assert!(vec.remove_label_values(&["v1", "v2"]).is_err());
441        vec.with_label_values(&["v1", "v2"]).inc();
442        assert!(vec.remove_label_values(&["v1", "v2"]).is_ok());
443
444        vec.with_label_values(&["v1", "v2"]).inc();
445        assert!(vec.remove_label_values(&["v1"]).is_err());
446        assert!(vec.remove_label_values(&["v1", "v3"]).is_err());
447    }
448
449    #[test]
450    fn test_counter_vec_with_owned_label_values() {
451        let vec = CounterVec::new(
452            Opts::new("test_vec", "test counter vec help"),
453            &["l1", "l2"],
454        )
455        .unwrap();
456
457        let v1 = "v1".to_string();
458        let v2 = "v2".to_string();
459        let v3 = "v3".to_string();
460
461        assert!(vec.remove_label_values(&[v1.clone(), v2.clone()]).is_err());
462        vec.with_label_values(&[v1.clone(), v2.clone()]).inc();
463        assert!(vec.remove_label_values(&[v1.clone(), v2.clone()]).is_ok());
464
465        vec.with_label_values(&[v1.clone(), v2.clone()]).inc();
466        assert!(vec.remove_label_values(&[v1.clone()]).is_err());
467        assert!(vec.remove_label_values(&[v1.clone(), v3.clone()]).is_err());
468    }
469
470    #[test]
471    fn test_gauge_vec_with_labels() {
472        let vec = GaugeVec::new(
473            Opts::new("test_gauge_vec", "test gauge vec help"),
474            &["l1", "l2"],
475        )
476        .unwrap();
477
478        let mut labels = HashMap::new();
479        labels.insert("l1", "v1");
480        labels.insert("l2", "v2");
481        assert!(vec.remove(&labels).is_err());
482
483        vec.with(&labels).inc();
484        vec.with(&labels).dec();
485        vec.with(&labels).add(42.0);
486        vec.with(&labels).sub(42.0);
487        vec.with(&labels).set(42.0);
488
489        assert!(vec.remove(&labels).is_ok());
490        assert!(vec.remove(&labels).is_err());
491    }
492
493    #[test]
494    fn test_gauge_vec_with_owned_labels() {
495        let vec = GaugeVec::new(
496            Opts::new("test_gauge_vec", "test gauge vec help"),
497            &["l1", "l2"],
498        )
499        .unwrap();
500
501        let v1 = "v1".to_string();
502        let v2 = "v2".to_string();
503
504        let mut labels = HashMap::new();
505        labels.insert("l1", v1.clone());
506        labels.insert("l2", v2.clone());
507        assert!(vec.remove(&labels).is_err());
508
509        vec.with(&labels).inc();
510        vec.with(&labels).dec();
511        vec.with(&labels).add(42.0);
512        vec.with(&labels).sub(42.0);
513        vec.with(&labels).set(42.0);
514
515        assert!(vec.remove(&labels).is_ok());
516        assert!(vec.remove(&labels).is_err());
517    }
518
519    #[test]
520    fn test_gauge_vec_with_label_values() {
521        let vec = GaugeVec::new(
522            Opts::new("test_gauge_vec", "test gauge vec help"),
523            &["l1", "l2"],
524        )
525        .unwrap();
526
527        assert!(vec.remove_label_values(&["v1", "v2"]).is_err());
528        vec.with_label_values(&["v1", "v2"]).inc();
529        assert!(vec.remove_label_values(&["v1", "v2"]).is_ok());
530
531        vec.with_label_values(&["v1", "v2"]).inc();
532        vec.with_label_values(&["v1", "v2"]).dec();
533        vec.with_label_values(&["v1", "v2"]).add(42.0);
534        vec.with_label_values(&["v1", "v2"]).sub(42.0);
535        vec.with_label_values(&["v1", "v2"]).set(42.0);
536
537        assert!(vec.remove_label_values(&["v1"]).is_err());
538        assert!(vec.remove_label_values(&["v1", "v3"]).is_err());
539    }
540
541    #[test]
542    fn test_gauge_vec_with_owned_label_values() {
543        let vec = GaugeVec::new(
544            Opts::new("test_gauge_vec", "test gauge vec help"),
545            &["l1", "l2"],
546        )
547        .unwrap();
548
549        let v1 = "v1".to_string();
550        let v2 = "v2".to_string();
551        let v3 = "v3".to_string();
552
553        assert!(vec.remove_label_values(&[v1.clone(), v2.clone()]).is_err());
554        vec.with_label_values(&[v1.clone(), v2.clone()]).inc();
555        assert!(vec.remove_label_values(&[v1.clone(), v2.clone()]).is_ok());
556
557        vec.with_label_values(&[v1.clone(), v2.clone()]).inc();
558        vec.with_label_values(&[v1.clone(), v2.clone()]).dec();
559        vec.with_label_values(&[v1.clone(), v2.clone()]).add(42.0);
560        vec.with_label_values(&[v1.clone(), v2.clone()]).sub(42.0);
561        vec.with_label_values(&[v1.clone(), v2.clone()]).set(42.0);
562
563        assert!(vec.remove_label_values(&[v1.clone()]).is_err());
564        assert!(vec.remove_label_values(&[v1.clone(), v3.clone()]).is_err());
565    }
566
567    #[test]
568    fn test_vec_get_metric_with() {
569        let vec = CounterVec::new(
570            Opts::new("test_vec", "test counter vec help"),
571            &["b", "c", "a"],
572        )
573        .unwrap();
574
575        // create a new metric that labels are {b" => "c", "c" => "a" "a" => "b"}.
576        let mut labels = HashMap::new();
577        labels.insert("a", "b");
578        labels.insert("b", "c");
579        labels.insert("c", "a");
580        let c = vec.get_metric_with(&labels).unwrap();
581        let m = c.metric();
582        let label_pairs = m.get_label();
583        assert_eq!(label_pairs.len(), labels.len());
584        for lp in label_pairs.iter() {
585            assert_eq!(lp.value(), labels[lp.name()]);
586        }
587    }
588}