prometheus_client/registry.rs
1//! Metric registry implementation.
2//!
3//! See [`Registry`] for details.
4
5use std::borrow::Cow;
6
7use crate::collector::Collector;
8use crate::encoding::{DescriptorEncoder, EncodeMetric};
9
10/// A metric registry.
11///
12/// First off one registers metrics with the registry via
13/// [`Registry::register`]. Later on the [`Registry`] is passed to an encoder
14/// collecting samples of each metric by iterating all metrics in the
15/// [`Registry`].
16///
17/// [`Registry`] is the core building block, generic over the metric type being
18/// registered. Out of convenience, the generic type parameter is set to use
19/// dynamic dispatching by default to be able to register different types of
20/// metrics (e.g. [`Counter`](crate::metrics::counter::Counter) and
21/// [`Gauge`](crate::metrics::gauge::Gauge)) with the same registry. Advanced
22/// users might want to use their custom types.
23///
24/// ```
25/// # use prometheus_client::encoding::text::encode;
26/// # use prometheus_client::metrics::counter::{Atomic as _, Counter};
27/// # use prometheus_client::metrics::gauge::{Atomic as _, Gauge};
28/// # use prometheus_client::registry::Registry;
29/// #
30/// // Create a metric registry.
31/// let mut registry = Registry::default();
32///
33/// let counter: Counter = Counter::default();
34/// let gauge: Gauge = Gauge::default();
35///
36/// registry.register(
37/// "my_counter",
38/// "This is my counter",
39/// counter.clone(),
40/// );
41/// registry.register(
42/// "my_gauge",
43/// "This is my gauge",
44/// gauge.clone(),
45/// );
46///
47/// # // Encode all metrics in the registry in the text format.
48/// # let mut buffer = String::new();
49/// # encode(&mut buffer, ®istry).unwrap();
50/// #
51/// # let expected = "# HELP my_counter This is my counter.\n".to_owned() +
52/// # "# TYPE my_counter counter\n" +
53/// # "my_counter_total 0\n" +
54/// # "# HELP my_gauge This is my gauge.\n" +
55/// # "# TYPE my_gauge gauge\n" +
56/// # "my_gauge 0\n" +
57/// # "# EOF\n";
58/// # assert_eq!(expected, buffer);
59/// ```
60#[derive(Debug, Default)]
61pub struct Registry {
62 prefix: Option<Prefix>,
63 labels: Vec<(Cow<'static, str>, Cow<'static, str>)>,
64 metrics: Vec<(Descriptor, Box<dyn Metric>)>,
65 collectors: Vec<Box<dyn Collector>>,
66 sub_registries: Vec<Registry>,
67}
68
69impl Registry {
70 /// Creates a new default [`Registry`] with the given prefix.
71 pub fn with_prefix(prefix: impl Into<String>) -> Self {
72 Self {
73 prefix: Some(Prefix(prefix.into())),
74 ..Default::default()
75 }
76 }
77
78 /// Creates a new default [`Registry`] with the given labels.
79 pub fn with_labels(
80 labels: impl Iterator<Item = (Cow<'static, str>, Cow<'static, str>)>,
81 ) -> Self {
82 Self {
83 labels: labels.into_iter().collect(),
84 ..Default::default()
85 }
86 }
87
88 /// Creates a new default [`Registry`] with the given prefix and labels.
89 pub fn with_prefix_and_labels(
90 prefix: impl Into<String>,
91 labels: impl Iterator<Item = (Cow<'static, str>, Cow<'static, str>)>,
92 ) -> Self {
93 Self {
94 prefix: Some(Prefix(prefix.into())),
95 labels: labels.into_iter().collect(),
96 ..Default::default()
97 }
98 }
99
100 /// Register a metric with the [`Registry`].
101 ///
102 /// Note: In the Open Metrics text exposition format some metric types have
103 /// a special suffix, e.g. the
104 /// [`Counter`](crate::metrics::counter::Counter`) metric with `_total`.
105 /// These suffixes are inferred through the metric type and must not be
106 /// appended to the metric name manually by the user.
107 ///
108 /// Note: A full stop punctuation mark (`.`) is automatically added to the
109 /// passed help text.
110 ///
111 /// Use [`Registry::register_with_unit`] whenever a unit for the given
112 /// metric is known.
113 ///
114 /// ```
115 /// # use prometheus_client::metrics::counter::{Atomic as _, Counter};
116 /// # use prometheus_client::registry::{Registry, Unit};
117 /// #
118 /// let mut registry = Registry::default();
119 /// let counter: Counter = Counter::default();
120 ///
121 /// registry.register("my_counter", "This is my counter", counter.clone());
122 /// ```
123 pub fn register<N: Into<String>, H: Into<String>>(
124 &mut self,
125 name: N,
126 help: H,
127 metric: impl Metric,
128 ) {
129 self.priv_register(name, help, metric, None)
130 }
131
132 /// Register a metric with the [`Registry`] specifying the metric's unit.
133 ///
134 /// See [`Registry::register`] for additional documentation.
135 ///
136 /// Note: In the Open Metrics text exposition format units are appended to
137 /// the metric name. This is done automatically. Users must not append the
138 /// unit to the name manually.
139 ///
140 /// ```
141 /// # use prometheus_client::metrics::counter::{Atomic as _, Counter};
142 /// # use prometheus_client::registry::{Registry, Unit};
143 /// #
144 /// let mut registry = Registry::default();
145 /// let counter: Counter = Counter::default();
146 ///
147 /// registry.register_with_unit(
148 /// "my_counter",
149 /// "This is my counter",
150 /// Unit::Seconds,
151 /// counter.clone(),
152 /// );
153 /// ```
154 pub fn register_with_unit<N: Into<String>, H: Into<String>>(
155 &mut self,
156 name: N,
157 help: H,
158 unit: Unit,
159 metric: impl Metric,
160 ) {
161 self.priv_register(name, help, metric, Some(unit))
162 }
163
164 fn priv_register<N: Into<String>, H: Into<String>>(
165 &mut self,
166 name: N,
167 help: H,
168 metric: impl Metric,
169 unit: Option<Unit>,
170 ) {
171 let descriptor = Descriptor::new(name, help, unit);
172 self.metrics.push((descriptor, Box::new(metric)));
173 }
174
175 /// Register a [`Collector`].
176 ///
177 /// ```
178 /// # use prometheus_client::metrics::counter::ConstCounter;
179 /// # use prometheus_client::registry::Registry;
180 /// # use prometheus_client::collector::Collector;
181 /// # use prometheus_client::encoding::{DescriptorEncoder, EncodeMetric};
182 /// #
183 /// #[derive(Debug)]
184 /// struct MyCollector {}
185 ///
186 /// impl Collector for MyCollector {
187 /// fn encode(&self, mut encoder: DescriptorEncoder) -> Result<(), std::fmt::Error> {
188 /// let counter = ConstCounter::new(42u64);
189 /// let metric_encoder = encoder.encode_descriptor(
190 /// "my_counter",
191 /// "some help",
192 /// None,
193 /// counter.metric_type(),
194 /// )?;
195 /// counter.encode(metric_encoder)?;
196 /// Ok(())
197 /// }
198 /// }
199 ///
200 /// let my_collector = Box::new(MyCollector{});
201 ///
202 /// let mut registry = Registry::default();
203 ///
204 /// registry.register_collector(my_collector);
205 /// ```
206 pub fn register_collector(&mut self, collector: Box<dyn Collector>) {
207 self.collectors.push(collector);
208 }
209
210 /// Create a sub-registry to register metrics with a common prefix.
211 ///
212 /// Say you would like to prefix one set of metrics with `subsystem_a` and
213 /// one set of metrics with `subsystem_b`. Instead of prefixing each metric
214 /// with the corresponding subsystem string individually, you can create two
215 /// sub-registries like demonstrated below.
216 ///
217 /// This can be used to pass a prefixed sub-registry down to a subsystem of
218 /// your architecture automatically adding a prefix to each metric the
219 /// subsystem registers.
220 ///
221 /// ```
222 /// # use prometheus_client::metrics::counter::{Atomic as _, Counter};
223 /// # use prometheus_client::registry::{Registry, Unit};
224 /// #
225 /// let mut registry = Registry::default();
226 ///
227 /// let subsystem_a_counter_1: Counter = Counter::default();
228 /// let subsystem_a_counter_2: Counter = Counter::default();
229 ///
230 /// let subsystem_a_registry = registry.sub_registry_with_prefix("subsystem_a");
231 /// registry.register("counter_1", "", subsystem_a_counter_1.clone());
232 /// registry.register("counter_2", "", subsystem_a_counter_2.clone());
233 ///
234 /// let subsystem_b_counter_1: Counter = Counter::default();
235 /// let subsystem_b_counter_2: Counter = Counter::default();
236 ///
237 /// let subsystem_a_registry = registry.sub_registry_with_prefix("subsystem_b");
238 /// registry.register("counter_1", "", subsystem_b_counter_1.clone());
239 /// registry.register("counter_2", "", subsystem_b_counter_2.clone());
240 /// ```
241 ///
242 /// See [`Registry::sub_registry_with_label`] for the same functionality,
243 /// but namespacing with a label instead of a metric name prefix.
244 pub fn sub_registry_with_prefix<P: AsRef<str>>(&mut self, prefix: P) -> &mut Self {
245 let sub_registry = Registry {
246 prefix: Some(Prefix(
247 self.prefix.clone().map(|p| p.0 + "_").unwrap_or_default() + prefix.as_ref(),
248 )),
249 labels: self.labels.clone(),
250 ..Default::default()
251 };
252
253 self.priv_sub_registry(sub_registry)
254 }
255
256 /// Like [`Registry::sub_registry_with_prefix`] but with a label instead.
257 pub fn sub_registry_with_label(
258 &mut self,
259 label: (Cow<'static, str>, Cow<'static, str>),
260 ) -> &mut Self {
261 self.sub_registry_with_labels(std::iter::once(label))
262 }
263
264 /// Like [`Registry::sub_registry_with_prefix`] but with multiple labels instead.
265 pub fn sub_registry_with_labels(
266 &mut self,
267 labels: impl Iterator<Item = (Cow<'static, str>, Cow<'static, str>)>,
268 ) -> &mut Self {
269 let mut new_labels = self.labels.clone();
270 new_labels.extend(labels);
271
272 let sub_registry = Registry {
273 prefix: self.prefix.clone(),
274 labels: new_labels,
275 ..Default::default()
276 };
277
278 self.priv_sub_registry(sub_registry)
279 }
280
281 fn priv_sub_registry(&mut self, sub_registry: Self) -> &mut Self {
282 self.sub_registries.push(sub_registry);
283
284 self.sub_registries
285 .last_mut()
286 .expect("sub_registries not to be empty.")
287 }
288
289 pub(crate) fn encode(&self, encoder: &mut DescriptorEncoder) -> Result<(), std::fmt::Error> {
290 for (descriptor, metric) in self.metrics.iter() {
291 let mut descriptor_encoder =
292 encoder.with_prefix_and_labels(self.prefix.as_ref(), &self.labels);
293 let metric_encoder = descriptor_encoder.encode_descriptor(
294 &descriptor.name,
295 &descriptor.help,
296 descriptor.unit.as_ref(),
297 EncodeMetric::metric_type(metric.as_ref()),
298 )?;
299 metric.encode(metric_encoder)?;
300 }
301
302 for collector in self.collectors.iter() {
303 let descriptor_encoder =
304 encoder.with_prefix_and_labels(self.prefix.as_ref(), &self.labels);
305 collector.encode(descriptor_encoder)?;
306 }
307
308 for registry in self.sub_registries.iter() {
309 registry.encode(encoder)?;
310 }
311
312 Ok(())
313 }
314}
315
316/// Metric prefix
317#[derive(Clone, Debug)]
318pub(crate) struct Prefix(String);
319
320impl Prefix {
321 pub(crate) fn as_str(&self) -> &str {
322 self.0.as_str()
323 }
324}
325
326impl From<String> for Prefix {
327 fn from(s: String) -> Self {
328 Prefix(s)
329 }
330}
331
332/// OpenMetrics metric descriptor.
333#[derive(Debug, Clone)]
334struct Descriptor {
335 name: String,
336 help: String,
337 unit: Option<Unit>,
338}
339
340impl Descriptor {
341 /// Create new [`Descriptor`].
342 fn new<N: Into<String>, H: Into<String>>(name: N, help: H, unit: Option<Unit>) -> Self {
343 Self {
344 name: name.into(),
345 help: help.into() + ".",
346 unit,
347 }
348 }
349}
350
351/// Metric units recommended by Open Metrics.
352///
353/// See [`Unit::Other`] to specify alternative units.
354#[derive(Debug, Clone)]
355#[allow(missing_docs)]
356pub enum Unit {
357 Amperes,
358 Bytes,
359 Celsius,
360 Grams,
361 Joules,
362 Meters,
363 Ratios,
364 Seconds,
365 Volts,
366 Other(String),
367}
368
369impl Unit {
370 /// Returns the given Unit's str representation.
371 pub fn as_str(&self) -> &str {
372 match self {
373 Unit::Amperes => "amperes",
374 Unit::Bytes => "bytes",
375 Unit::Celsius => "celsius",
376 Unit::Grams => "grams",
377 Unit::Joules => "joules",
378 Unit::Meters => "meters",
379 Unit::Ratios => "ratios",
380 Unit::Seconds => "seconds",
381 Unit::Volts => "volts",
382 Unit::Other(other) => other.as_str(),
383 }
384 }
385}
386
387/// Super trait representing an abstract Prometheus metric.
388pub trait Metric: crate::encoding::EncodeMetric + Send + Sync + std::fmt::Debug + 'static {}
389
390impl<T> Metric for T where T: crate::encoding::EncodeMetric + Send + Sync + std::fmt::Debug + 'static
391{}