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}