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}