prometheus_http_client/
labels.rs

1//! Helpers for extracting common labels from metrics before presenting them
2use std::{
3    collections::BTreeMap,
4    fmt::{Debug, Display},
5};
6
7/// A set of labels extracted by ExtractLabels
8pub type Labels = BTreeMap<String, String>;
9
10/// Common labels extracted from a very generic sequence of metric labels (key value pairs)
11pub struct ExtractLabels {
12    /// The metric name (from __name__ label)
13    pub name: String,
14    /// Labels that are common to all metrics in the set
15    pub common_labels: Labels,
16    /// Labels specific to each metric (excluding common labels)
17    pub specific_labels: Vec<Labels>,
18}
19
20impl ExtractLabels {
21    /// Extract common and specific labels from an iterator of metric label sets.
22    ///
23    /// Labels in `skip_labels` are excluded from both common and specific labels.
24    pub fn new<'a, I, KV, K, V>(src: I, skip_labels: &[String]) -> Self
25    where
26        I: Iterator<Item = &'a KV>,
27        KV: Clone + Debug + 'a,
28        K: Display + 'a,
29        V: Display + 'a,
30        &'a KV: IntoIterator<Item = (&'a K, &'a V)>,
31    {
32        // Common labels needs to be String -> Option<String>, because when we find a conflict,
33        // we need to poison that key (by putting None)
34        let mut common_labels = BTreeMap::<String, Option<String>>::default();
35        let mut specific_labels: Vec<Labels> = src
36            .map(|kv| {
37                let labels: Labels = kv
38                    .into_iter()
39                    .map(|(k, v)| {
40                        let k = k.to_string();
41                        let v = v.to_string();
42                        if let Some(existing_val) = common_labels.get_mut(&k) {
43                            // If the existing value is None, we already eliminated this label,
44                            // so don't add it back.
45                            if let Some(common_val) = existing_val
46                                && common_val != &v
47                            {
48                                *existing_val = None;
49                            }
50                        } else {
51                            common_labels.insert(k.clone(), Some(v.clone()));
52                        }
53                        (k, v)
54                    })
55                    .collect();
56                labels
57            })
58            .collect();
59
60        let mut common_labels = common_labels
61            .into_iter()
62            .filter_map(|(k, maybe_v)| maybe_v.map(|v| (k, v)))
63            .collect::<Labels>();
64
65        for sl in skip_labels {
66            common_labels.remove(sl);
67        }
68        for sp in &mut specific_labels {
69            for sl in skip_labels {
70                sp.remove(sl);
71            }
72            for cl in common_labels.keys() {
73                sp.remove(cl);
74            }
75        }
76        let name = common_labels.remove("__name__").unwrap_or_default();
77
78        ExtractLabels {
79            name,
80            common_labels,
81            specific_labels,
82        }
83    }
84}