prometheus/
metrics.rs

1// Copyright 2014 The Prometheus Authors
2// Copyright 2019 TiKV Project Authors. Licensed under Apache-2.0.
3
4use std::cmp::{Eq, Ord, Ordering, PartialOrd};
5use std::collections::HashMap;
6
7use crate::desc::{Desc, Describer};
8use crate::errors::Result;
9use crate::proto::{self, LabelPair};
10use crate::timer;
11use std::cell::Cell;
12
13pub const SEPARATOR_BYTE: u8 = 0xFF;
14
15/// An interface for collecting metrics.
16pub trait Collector: Sync + Send {
17    /// Return descriptors for metrics.
18    fn desc(&self) -> Vec<&Desc>;
19
20    /// Collect metrics.
21    fn collect(&self) -> Vec<proto::MetricFamily>;
22}
23
24/// An interface models a single sample value with its meta data being exported to Prometheus.
25pub trait Metric: Sync + Send + Clone {
26    /// Return the protocol Metric.
27    fn metric(&self) -> proto::Metric;
28}
29
30/// An interface models a Metric only usable in single thread environment.
31pub trait LocalMetric {
32    /// Flush the local metrics to the global one.
33    fn flush(&self);
34}
35
36/// An interface models a LocalMetric with try to flush functions.
37/// Not intend to be implemented by user manually, used in macro generated code.
38pub trait MayFlush: LocalMetric {
39    /// If the LocalMetric is already flushed in last `flush_interval_sec` seconds, then do nothing,
40    /// else flush and update last flush time.
41    fn try_flush(&self, last_flush: &Cell<u64>, flush_interval_millis: u64) {
42        let now = timer::recent_millis();
43        let last_tick = last_flush.get();
44        if now < last_tick + flush_interval_millis {
45            return;
46        }
47        self.flush();
48        last_flush.set(now);
49    }
50
51    /// Open to implementation to fill try_flush parameters
52    fn may_flush(&self);
53}
54
55/// A struct that bundles the options for creating most [`Metric`] types.
56#[derive(Debug, Clone)]
57pub struct Opts {
58    /// namespace, subsystem, and name are components of the fully-qualified
59    /// name of the [`Metric`] (created by joining these components with
60    /// "_"). Only Name is mandatory, the others merely help structuring the
61    /// name. Note that the fully-qualified name of the metric must be a
62    /// valid Prometheus metric name.
63    pub namespace: String,
64    /// namespace, subsystem, and name are components of the fully-qualified
65    /// name of the [`Metric`] (created by joining these components with
66    /// "_"). Only Name is mandatory, the others merely help structuring the
67    /// name. Note that the fully-qualified name of the metric must be a
68    /// valid Prometheus metric name.
69    pub subsystem: String,
70    /// namespace, subsystem, and name are components of the fully-qualified
71    /// name of the [`Metric`] (created by joining these components with
72    /// "_"). Only Name is mandatory, the others merely help structuring the
73    /// name. Note that the fully-qualified name of the metric must be a
74    /// valid Prometheus metric name.
75    pub name: String,
76
77    /// help provides information about this metric. Mandatory!
78    ///
79    /// Metrics with the same fully-qualified name must have the same Help
80    /// string.
81    pub help: String,
82
83    /// const_labels are used to attach fixed labels to this metric. Metrics
84    /// with the same fully-qualified name must have the same label names in
85    /// their ConstLabels.
86    ///
87    /// Note that in most cases, labels have a value that varies during the
88    /// lifetime of a process. Those labels are usually managed with a metric
89    /// vector collector (like CounterVec, GaugeVec). ConstLabels
90    /// serve only special purposes. One is for the special case where the
91    /// value of a label does not change during the lifetime of a process,
92    /// e.g. if the revision of the running binary is put into a
93    /// label. Another, more advanced purpose is if more than one [`Collector`]
94    /// needs to collect Metrics with the same fully-qualified name. In that
95    /// case, those Metrics must differ in the values of their
96    /// ConstLabels. See the [`Collector`] examples.
97    ///
98    /// If the value of a label never changes (not even between binaries),
99    /// that label most likely should not be a label at all (but part of the
100    /// metric name).
101    pub const_labels: HashMap<String, String>,
102
103    /// variable_labels contains names of labels for which the metric maintains
104    /// variable values. Metrics with the same fully-qualified name must have
105    /// the same label names in their variable_labels.
106    ///
107    /// Note that variable_labels is used in `MetricVec`. To create a single
108    /// metric must leave it empty.
109    pub variable_labels: Vec<String>,
110}
111
112impl Opts {
113    /// `new` creates the Opts with the `name` and `help` arguments.
114    pub fn new<S1: Into<String>, S2: Into<String>>(name: S1, help: S2) -> Opts {
115        Opts {
116            namespace: "".to_owned(),
117            subsystem: "".to_owned(),
118            name: name.into(),
119            help: help.into(),
120            const_labels: HashMap::new(),
121            variable_labels: Vec::new(),
122        }
123    }
124
125    /// `namespace` sets the namespace.
126    pub fn namespace<S: Into<String>>(mut self, namespace: S) -> Self {
127        self.namespace = namespace.into();
128        self
129    }
130
131    /// `subsystem` sets the sub system.
132    pub fn subsystem<S: Into<String>>(mut self, subsystem: S) -> Self {
133        self.subsystem = subsystem.into();
134        self
135    }
136
137    /// `const_labels` sets the const labels.
138    pub fn const_labels(mut self, const_labels: HashMap<String, String>) -> Self {
139        self.const_labels = const_labels;
140        self
141    }
142
143    /// `const_label` adds a const label.
144    pub fn const_label<S1: Into<String>, S2: Into<String>>(mut self, name: S1, value: S2) -> Self {
145        self.const_labels.insert(name.into(), value.into());
146        self
147    }
148
149    /// `variable_labels` sets the variable labels.
150    pub fn variable_labels(mut self, variable_labels: Vec<String>) -> Self {
151        self.variable_labels = variable_labels;
152        self
153    }
154
155    /// `variable_label` adds a variable label.
156    pub fn variable_label<S: Into<String>>(mut self, name: S) -> Self {
157        self.variable_labels.push(name.into());
158        self
159    }
160
161    /// `fq_name` returns the fq_name.
162    pub fn fq_name(&self) -> String {
163        build_fq_name(&self.namespace, &self.subsystem, &self.name)
164    }
165}
166
167impl Describer for Opts {
168    fn describe(&self) -> Result<Desc> {
169        Desc::new(
170            self.fq_name(),
171            self.help.clone(),
172            self.variable_labels.clone(),
173            self.const_labels.clone(),
174        )
175    }
176}
177
178impl Ord for LabelPair {
179    fn cmp(&self, other: &LabelPair) -> Ordering {
180        self.get_name().cmp(other.get_name())
181    }
182}
183
184impl Eq for LabelPair {}
185
186impl PartialOrd for LabelPair {
187    fn partial_cmp(&self, other: &LabelPair) -> Option<Ordering> {
188        Some(self.cmp(other))
189    }
190}
191
192/// `build_fq_name` joins the given three name components by "_". Empty name
193/// components are ignored. If the name parameter itself is empty, an empty
194/// string is returned, no matter what. [`Metric`] implementations included in this
195/// library use this function internally to generate the fully-qualified metric
196/// name from the name component in their Opts. Users of the library will only
197/// need this function if they implement their own [`Metric`] or instantiate a Desc
198/// directly.
199fn build_fq_name(namespace: &str, subsystem: &str, name: &str) -> String {
200    if name.is_empty() {
201        return "".to_owned();
202    }
203
204    if !namespace.is_empty() && !subsystem.is_empty() {
205        return format!("{}_{}_{}", namespace, subsystem, name);
206    } else if !namespace.is_empty() {
207        return format!("{}_{}", namespace, name);
208    } else if !subsystem.is_empty() {
209        return format!("{}_{}", subsystem, name);
210    }
211
212    name.to_owned()
213}
214
215#[cfg(test)]
216mod tests {
217    use std::cmp::{Ord, Ordering};
218
219    use super::*;
220    use crate::proto::LabelPair;
221
222    fn new_label_pair(name: &str, value: &str) -> LabelPair {
223        let mut l = LabelPair::default();
224        l.set_name(name.to_owned());
225        l.set_value(value.to_owned());
226        l
227    }
228
229    #[test]
230    fn test_label_cmp() {
231        let tbl = vec![
232            ("k1", "k2", Ordering::Less),
233            ("k1", "k1", Ordering::Equal),
234            ("k1", "k0", Ordering::Greater),
235        ];
236
237        for (l1, l2, order) in tbl {
238            let lhs = new_label_pair(l1, l1);
239            let rhs = new_label_pair(l2, l2);
240            assert_eq!(lhs.cmp(&rhs), order);
241        }
242    }
243
244    #[test]
245    fn test_build_fq_name() {
246        let tbl = vec![
247            ("a", "b", "c", "a_b_c"),
248            ("", "b", "c", "b_c"),
249            ("a", "", "c", "a_c"),
250            ("", "", "c", "c"),
251            ("a", "b", "", ""),
252            ("a", "", "", ""),
253            ("", "b", "", ""),
254            (" ", "", "", ""),
255        ];
256
257        for (namespace, subsystem, name, res) in tbl {
258            assert_eq!(&build_fq_name(namespace, subsystem, name), res);
259        }
260    }
261
262    #[test]
263    fn test_different_generic_types() {
264        Opts::new(format!("{}_{}", "string", "label"), "&str_label");
265    }
266}