metrics_prometheus/recorder/mod.rs
1//! [`metrics::Recorder`] implementations.
2
3pub mod freezable;
4pub mod frozen;
5pub mod layer;
6
7use std::{borrow::Cow, fmt, sync::Arc};
8
9pub use metrics_util::layers::Layer;
10
11pub use self::{freezable::Recorder as Freezable, frozen::Recorder as Frozen};
12use crate::{
13 failure::{self, strategy::PanicInDebugNoOpInRelease},
14 metric, storage,
15};
16
17/// [`metrics::Recorder`] registering metrics in a [`prometheus::Registry`] and
18/// powered by a [`metrics::Registry`] built on top of a [`storage::Mutable`].
19///
20/// This [`Recorder`] is capable of registering metrics in its
21/// [`prometheus::Registry`] on the fly. By default, the
22/// [`prometheus::default_registry()`] is used.
23///
24/// # Example
25///
26/// ```rust
27/// let recorder = metrics_prometheus::install();
28///
29/// // Either use `metrics` crate interfaces.
30/// metrics::counter!(
31/// "count", "whose" => "mine", "kind" => "owned",
32/// ).increment(1);
33/// metrics::counter!(
34/// "count", "whose" => "mine", "kind" => "ref",
35/// ).increment(1);
36/// metrics::counter!(
37/// "count", "kind" => "owned", "whose" => "dummy",
38/// ).increment(1);
39///
40/// // Or construct and provide `prometheus` metrics directly.
41/// recorder.register_metric(prometheus::Gauge::new("value", "help")?);
42///
43/// let report = prometheus::TextEncoder::new()
44/// .encode_to_string(&prometheus::default_registry().gather())?;
45/// assert_eq!(
46/// report.trim(),
47/// r#"
48/// ## HELP count count
49/// ## TYPE count counter
50/// count{kind="owned",whose="dummy"} 1
51/// count{kind="owned",whose="mine"} 1
52/// count{kind="ref",whose="mine"} 1
53/// ## HELP value help
54/// ## TYPE value gauge
55/// value 0
56/// "#
57/// .trim(),
58/// );
59///
60/// // Metrics can be described anytime after being registered in
61/// // `prometheus::Registry`.
62/// metrics::describe_counter!("count", "Example of counter.");
63/// metrics::describe_gauge!("value", "Example of gauge.");
64///
65/// let report = prometheus::TextEncoder::new()
66/// .encode_to_string(&recorder.registry().gather())?;
67/// assert_eq!(
68/// report.trim(),
69/// r#"
70/// ## HELP count Example of counter.
71/// ## TYPE count counter
72/// count{kind="owned",whose="dummy"} 1
73/// count{kind="owned",whose="mine"} 1
74/// count{kind="ref",whose="mine"} 1
75/// ## HELP value Example of gauge.
76/// ## TYPE value gauge
77/// value 0
78/// "#
79/// .trim(),
80/// );
81///
82/// // Description can be changed multiple times and anytime:
83/// metrics::describe_counter!("count", "Another description.");
84///
85/// // Even before a metric is registered in `prometheus::Registry`.
86/// metrics::describe_counter!("another", "Yet another counter.");
87/// metrics::counter!("another").increment(1);
88///
89/// let report = prometheus::TextEncoder::new()
90/// .encode_to_string(&recorder.registry().gather())?;
91/// assert_eq!(
92/// report.trim(),
93/// r#"
94/// ## HELP another Yet another counter.
95/// ## TYPE another counter
96/// another 1
97/// ## HELP count Another description.
98/// ## TYPE count counter
99/// count{kind="owned",whose="dummy"} 1
100/// count{kind="owned",whose="mine"} 1
101/// count{kind="ref",whose="mine"} 1
102/// ## HELP value Example of gauge.
103/// ## TYPE value gauge
104/// value 0
105/// "#
106/// .trim(),
107/// );
108/// # Ok::<_, prometheus::Error>(())
109/// ```
110///
111/// # Performance
112///
113/// This [`Recorder`] provides the same overhead of accessing an already
114/// registered metric as a [`metrics::Registry`] does: [`read`-lock] on a
115/// sharded [`HashMap`] plus [`Arc`] cloning.
116///
117/// # Errors
118///
119/// [`prometheus::Registry`] has far more stricter semantics than the ones
120/// implied by a [`metrics::Recorder`]. That's why incorrect usage of
121/// [`prometheus`] metrics via [`metrics`] crate will inevitably lead to a
122/// [`prometheus::Registry`] returning a [`prometheus::Error`] instead of
123/// registering the metric. The returned [`prometheus::Error`] can be either
124/// turned into a panic, or just silently ignored, making this [`Recorder`] to
125/// return a no-op metric instead (see [`metrics::Counter::noop()`] for
126/// example).
127///
128/// The desired behavior can be specified with a [`failure::Strategy`]
129/// implementation of this [`Recorder`]. By default a
130/// [`PanicInDebugNoOpInRelease`] [`failure::Strategy`] is used. See
131/// [`failure::strategy`] module for other available [`failure::Strategy`]s, or
132/// provide your own one by implementing the [`failure::Strategy`] trait.
133///
134/// ```rust,should_panic
135/// use metrics_prometheus::failure::strategy;
136///
137/// metrics_prometheus::Recorder::builder()
138/// .with_failure_strategy(strategy::Panic)
139/// .build_and_install();
140///
141/// metrics::counter!("count", "kind" => "owned").increment(1);
142/// // This panics, as such labeling is not allowed by `prometheus` crate.
143/// metrics::counter!("count", "whose" => "mine").increment(1);
144/// ```
145///
146/// [`HashMap`]: std::collections::HashMap
147/// [`metrics::Registry`]: metrics_util::registry::Registry
148/// [`read`-lock]: std::sync::RwLock::read()
149#[derive(Clone)]
150pub struct Recorder<FailureStrategy = PanicInDebugNoOpInRelease> {
151 /// [`metrics::Registry`] providing performant access to the stored metrics.
152 ///
153 /// [`metrics::Registry`]: metrics_util::registry::Registry
154 metrics:
155 Arc<metrics_util::registry::Registry<metrics::Key, storage::Mutable>>,
156
157 /// [`storage::Mutable`] backing the [`metrics::Registry`] and registering
158 /// metrics in its [`prometheus::Registry`].
159 ///
160 /// [`metrics::Registry`]: metrics_util::registry::Registry
161 storage: storage::Mutable,
162
163 /// [`failure::Strategy`] to apply when a [`prometheus::Error`] is
164 /// encountered inside [`metrics::Recorder`] methods.
165 failure_strategy: FailureStrategy,
166}
167
168// TODO: Make a PR with `Debug` impl for `metrics_util::registry::Registry`.
169impl<S: fmt::Debug> fmt::Debug for Recorder<S> {
170 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
171 f.debug_struct("Recorder")
172 .field("storage", &self.storage)
173 .field("failure_strategy", &self.failure_strategy)
174 .finish_non_exhaustive()
175 }
176}
177
178impl Recorder {
179 /// Starts building a new [`Recorder`] on top of the
180 /// [`prometheus::default_registry()`].
181 pub fn builder() -> Builder {
182 Builder {
183 storage: storage::Mutable::default(),
184 failure_strategy: PanicInDebugNoOpInRelease,
185 layers: layer::Stack::identity(),
186 }
187 }
188}
189
190impl<S> Recorder<S> {
191 /// Returns the underlying [`prometheus::Registry`] backing this
192 /// [`Recorder`].
193 ///
194 /// # Warning
195 ///
196 /// Any [`prometheus`] metrics, registered directly in the returned
197 /// [`prometheus::Registry`], cannot be used via this [`metrics::Recorder`]
198 /// (and, so, [`metrics`] crate interfaces), and trying to use them will
199 /// inevitably cause a [`prometheus::Error`] being emitted.
200 ///
201 /// ```rust,should_panic
202 /// use metrics_prometheus::failure::strategy;
203 ///
204 /// let recorder = metrics_prometheus::Recorder::builder()
205 /// .with_failure_strategy(strategy::Panic)
206 /// .build_and_install();
207 ///
208 /// let counter = prometheus::IntCounter::new("value", "help")?;
209 /// recorder.registry().register(Box::new(counter))?;
210 ///
211 /// // panics: Duplicate metrics collector registration attempted
212 /// metrics::counter!("value").increment(1);
213 /// # Ok::<_, prometheus::Error>(())
214 /// ```
215 #[must_use]
216 pub const fn registry(&self) -> &prometheus::Registry {
217 &self.storage.prometheus
218 }
219
220 /// Tries to register the provided [`prometheus`] `metric` in the underlying
221 /// [`prometheus::Registry`] in the way making it usable via this
222 /// [`Recorder`] (and, so, [`metrics`] crate interfaces).
223 ///
224 /// Accepts only the following [`prometheus`] metrics:
225 /// - [`prometheus::IntCounter`], [`prometheus::IntCounterVec`]
226 /// - [`prometheus::Gauge`], [`prometheus::GaugeVec`]
227 /// - [`prometheus::Histogram`], [`prometheus::HistogramVec`]
228 ///
229 /// # Errors
230 ///
231 /// If the underlying [`prometheus::Registry`] fails to register the
232 /// provided `metric`.
233 ///
234 /// # Example
235 ///
236 /// ```rust
237 /// let recorder = metrics_prometheus::install();
238 ///
239 /// let counter = prometheus::IntCounterVec::new(
240 /// prometheus::opts!("value", "help"),
241 /// &["whose", "kind"],
242 /// )?;
243 ///
244 /// recorder.try_register_metric(counter.clone())?;
245 ///
246 /// counter.with_label_values(&["mine", "owned"]).inc();
247 /// counter.with_label_values(&["foreign", "ref"]).inc_by(2);
248 /// counter.with_label_values(&["foreign", "owned"]).inc_by(3);
249 ///
250 /// let report = prometheus::TextEncoder::new()
251 /// .encode_to_string(&prometheus::default_registry().gather())?;
252 /// assert_eq!(
253 /// report.trim(),
254 /// r#"
255 /// ## HELP value help
256 /// ## TYPE value counter
257 /// value{kind="owned",whose="foreign"} 3
258 /// value{kind="owned",whose="mine"} 1
259 /// value{kind="ref",whose="foreign"} 2
260 /// "#
261 /// .trim(),
262 /// );
263 ///
264 /// metrics::counter!(
265 /// "value", "whose" => "mine", "kind" => "owned",
266 /// ).increment(1);
267 /// metrics::counter!(
268 /// "value", "whose" => "mine", "kind" => "ref",
269 /// ).increment(1);
270 /// metrics::counter!(
271 /// "value", "kind" => "owned", "whose" => "foreign",
272 /// ).increment(1);
273 ///
274 /// let report = prometheus::TextEncoder::new()
275 /// .encode_to_string(&recorder.registry().gather())?;
276 /// assert_eq!(
277 /// report.trim(),
278 /// r#"
279 /// ## HELP value help
280 /// ## TYPE value counter
281 /// value{kind="owned",whose="foreign"} 4
282 /// value{kind="owned",whose="mine"} 2
283 /// value{kind="ref",whose="foreign"} 2
284 /// value{kind="ref",whose="mine"} 1
285 /// "#
286 /// .trim(),
287 /// );
288 /// # Ok::<_, prometheus::Error>(())
289 /// ```
290 pub fn try_register_metric<M>(&self, metric: M) -> prometheus::Result<()>
291 where
292 M: metric::Bundled + prometheus::core::Collector,
293 <M as metric::Bundled>::Bundle:
294 prometheus::core::Collector + Clone + 'static,
295 storage::Mutable: storage::Get<
296 storage::mutable::Collection<<M as metric::Bundled>::Bundle>,
297 >,
298 {
299 self.storage.register_external(metric)
300 }
301
302 /// Registers the provided [`prometheus`] `metric` in the underlying
303 /// [`prometheus::Registry`] in the way making it usable via this
304 /// [`Recorder`] (and, so, [`metrics`] crate interfaces).
305 ///
306 /// Accepts only the following [`prometheus`] metrics:
307 /// - [`prometheus::IntCounter`], [`prometheus::IntCounterVec`]
308 /// - [`prometheus::Gauge`], [`prometheus::GaugeVec`]
309 /// - [`prometheus::Histogram`], [`prometheus::HistogramVec`]
310 ///
311 /// # Panics
312 ///
313 /// If the underlying [`prometheus::Registry`] fails to register the
314 /// provided `metric`.
315 ///
316 /// # Example
317 ///
318 /// ```rust
319 /// let recorder = metrics_prometheus::install();
320 ///
321 /// let gauge = prometheus::GaugeVec::new(
322 /// prometheus::opts!("value", "help"),
323 /// &["whose", "kind"],
324 /// )?;
325 ///
326 /// recorder.register_metric(gauge.clone());
327 ///
328 /// gauge.with_label_values(&["mine", "owned"]).inc();
329 /// gauge.with_label_values(&["foreign", "ref"]).set(2.0);
330 /// gauge.with_label_values(&["foreign", "owned"]).set(3.0);
331 ///
332 /// let report = prometheus::TextEncoder::new()
333 /// .encode_to_string(&prometheus::default_registry().gather())?;
334 /// assert_eq!(
335 /// report.trim(),
336 /// r#"
337 /// ## HELP value help
338 /// ## TYPE value gauge
339 /// value{kind="owned",whose="foreign"} 3
340 /// value{kind="owned",whose="mine"} 1
341 /// value{kind="ref",whose="foreign"} 2
342 /// "#
343 /// .trim(),
344 /// );
345 ///
346 /// metrics::gauge!(
347 /// "value", "whose" => "mine", "kind" => "owned",
348 /// ).increment(2.0);
349 /// metrics::gauge!(
350 /// "value", "whose" => "mine", "kind" => "ref",
351 /// ).decrement(2.0);
352 /// metrics::gauge!(
353 /// "value", "kind" => "owned", "whose" => "foreign",
354 /// ).increment(2.0);
355 ///
356 /// let report = prometheus::TextEncoder::new()
357 /// .encode_to_string(&prometheus::default_registry().gather())?;
358 /// assert_eq!(
359 /// report.trim(),
360 /// r#"
361 /// ## HELP value help
362 /// ## TYPE value gauge
363 /// value{kind="owned",whose="foreign"} 5
364 /// value{kind="owned",whose="mine"} 3
365 /// value{kind="ref",whose="foreign"} 2
366 /// value{kind="ref",whose="mine"} -2
367 /// "#
368 /// .trim(),
369 /// );
370 /// # Ok::<_, prometheus::Error>(())
371 /// ```
372 pub fn register_metric<M>(&self, metric: M)
373 where
374 M: metric::Bundled + prometheus::core::Collector,
375 <M as metric::Bundled>::Bundle:
376 prometheus::core::Collector + Clone + 'static,
377 storage::Mutable: storage::Get<
378 storage::mutable::Collection<<M as metric::Bundled>::Bundle>,
379 >,
380 {
381 self.try_register_metric(metric).unwrap_or_else(|e| {
382 panic!("failed to register `prometheus` metric: {e}")
383 });
384 }
385}
386
387#[warn(clippy::missing_trait_methods)]
388impl<S> metrics::Recorder for Recorder<S>
389where
390 S: failure::Strategy,
391{
392 fn describe_counter(
393 &self,
394 key: metrics::KeyName,
395 _: Option<metrics::Unit>,
396 description: metrics::SharedString,
397 ) {
398 self.storage.describe::<prometheus::IntCounter>(
399 key.as_str(),
400 description.into_owned(),
401 );
402 }
403
404 fn describe_gauge(
405 &self,
406 key: metrics::KeyName,
407 _: Option<metrics::Unit>,
408 description: metrics::SharedString,
409 ) {
410 self.storage.describe::<prometheus::Gauge>(
411 key.as_str(),
412 description.into_owned(),
413 );
414 }
415
416 fn describe_histogram(
417 &self,
418 key: metrics::KeyName,
419 _: Option<metrics::Unit>,
420 description: metrics::SharedString,
421 ) {
422 self.storage.describe::<prometheus::Histogram>(
423 key.as_str(),
424 description.into_owned(),
425 );
426 }
427
428 fn register_counter(
429 &self,
430 key: &metrics::Key,
431 _: &metrics::Metadata<'_>,
432 ) -> metrics::Counter {
433 self.metrics
434 .get_or_create_counter(key, |counter| {
435 counter.as_ref().map(|c| Arc::clone(c).into()).or_else(|e| {
436 match self.failure_strategy.decide(e) {
437 failure::Action::NoOp => Ok(metrics::Counter::noop()),
438 // PANIC: We cannot panic inside this closure, because
439 // this may lead to poisoning `RwLock`s inside
440 // `metrics_util::registry::Registry`.
441 failure::Action::Panic => Err(e.to_string()),
442 }
443 })
444 })
445 .unwrap_or_else(|e| {
446 panic!(
447 "failed to register `prometheus::IntCounter` metric: {e}"
448 )
449 })
450 }
451
452 fn register_gauge(
453 &self,
454 key: &metrics::Key,
455 _: &metrics::Metadata<'_>,
456 ) -> metrics::Gauge {
457 self.metrics
458 .get_or_create_gauge(key, |gauge| {
459 gauge.as_ref().map(|c| Arc::clone(c).into()).or_else(|e| {
460 match self.failure_strategy.decide(e) {
461 failure::Action::NoOp => Ok(metrics::Gauge::noop()),
462 // PANIC: We cannot panic inside this closure, because
463 // this may lead to poisoning `RwLock`s inside
464 // `metrics_util::registry::Registry`.
465 failure::Action::Panic => Err(e.to_string()),
466 }
467 })
468 })
469 .unwrap_or_else(|e| {
470 panic!("failed to register `prometheus::Gauge` metric: {e}")
471 })
472 }
473
474 fn register_histogram(
475 &self,
476 key: &metrics::Key,
477 _: &metrics::Metadata<'_>,
478 ) -> metrics::Histogram {
479 self.metrics
480 .get_or_create_histogram(key, |histogram| {
481 histogram.as_ref().map(|c| Arc::clone(c).into()).or_else(|e| {
482 match self.failure_strategy.decide(e) {
483 failure::Action::NoOp => Ok(metrics::Histogram::noop()),
484 // PANIC: We cannot panic inside this closure, because
485 // this may lead to poisoning `RwLock`s inside
486 // `metrics_util::registry::Registry`.
487 failure::Action::Panic => Err(e.to_string()),
488 }
489 })
490 })
491 .unwrap_or_else(|e| {
492 panic!("failed to register `prometheus::Histogram` metric: {e}")
493 })
494 }
495}
496
497/// Builder for building a [`Recorder`].
498#[derive(Debug)]
499#[must_use]
500pub struct Builder<
501 FailureStrategy = PanicInDebugNoOpInRelease,
502 Layers = layer::Stack,
503> {
504 /// [`storage::Mutable`] registering metrics in its
505 /// [`prometheus::Registry`].
506 storage: storage::Mutable,
507
508 /// [`failure::Strategy`] of the built [`Recorder`] to apply when a
509 /// [`prometheus::Error`] is encountered inside its [`metrics::Recorder`]
510 /// methods.
511 failure_strategy: FailureStrategy,
512
513 /// [`metrics::Layer`]s to wrap the built [`Recorder`] with upon its
514 /// installation with the [`metrics::set_global_recorder()`].
515 ///
516 /// [`metrics::Layer`]: Layer
517 layers: Layers,
518}
519
520impl<S, L> Builder<S, L> {
521 /// Sets the provided [`prometheus::Registry`] to be used by the built
522 /// [`Recorder`].
523 ///
524 /// When not specified, the [`prometheus::default_registry()`] is used by
525 /// default.
526 ///
527 /// # Warning
528 ///
529 /// Any [`prometheus`] metrics, already registered in the provided
530 /// [`prometheus::Registry`], cannot be used via the built
531 /// [`metrics::Recorder`] (and, so, [`metrics`] crate interfaces), and
532 /// trying to use them will inevitably cause a [`prometheus::Error`] being
533 /// emitted.
534 ///
535 /// # Example
536 ///
537 /// ```rust
538 /// let custom = prometheus::Registry::new_custom(Some("my".into()), None)?;
539 ///
540 /// metrics_prometheus::Recorder::builder()
541 /// .with_registry(&custom)
542 /// .build_and_install();
543 ///
544 /// metrics::counter!("count").increment(1);
545 ///
546 /// let report =
547 /// prometheus::TextEncoder::new().encode_to_string(&custom.gather())?;
548 /// assert_eq!(
549 /// report.trim(),
550 /// r#"
551 /// ## HELP my_count count
552 /// ## TYPE my_count counter
553 /// my_count 1
554 /// "#
555 /// .trim(),
556 /// );
557 /// # Ok::<_, prometheus::Error>(())
558 /// ```
559 pub fn with_registry<'r>(
560 mut self,
561 registry: impl IntoCow<'r, prometheus::Registry>,
562 ) -> Self {
563 self.storage.prometheus = registry.into_cow().into_owned();
564 self
565 }
566
567 /// Sets the provided [`failure::Strategy`] to be used by the built
568 /// [`Recorder`].
569 ///
570 /// [`prometheus::Registry`] has far more stricter semantics than the ones
571 /// implied by a [`metrics::Recorder`]. That's why incorrect usage of
572 /// [`prometheus`] metrics via [`metrics`] crate will inevitably lead to a
573 /// [`prometheus::Registry`] returning a [`prometheus::Error`] instead of a
574 /// registering the metric. The returned [`prometheus::Error`] can be either
575 /// turned into a panic, or just silently ignored, making the [`Recorder`]
576 /// to return a no-op metric instead (see [`metrics::Counter::noop()`] for
577 /// example).
578 ///
579 /// The default [`failure::Strategy`] is [`PanicInDebugNoOpInRelease`]. See
580 /// [`failure::strategy`] module for other available [`failure::Strategy`]s,
581 /// or provide your own one by implementing the [`failure::Strategy`] trait.
582 ///
583 /// # Example
584 ///
585 /// ```rust
586 /// use metrics_prometheus::failure::strategy;
587 ///
588 /// metrics_prometheus::Recorder::builder()
589 /// .with_failure_strategy(strategy::NoOp)
590 /// .build_and_install();
591 ///
592 /// metrics::counter!("invalid.name").increment(1);
593 ///
594 /// let stats = prometheus::default_registry().gather();
595 /// assert_eq!(stats.len(), 0);
596 /// ```
597 pub fn with_failure_strategy<F>(self, strategy: F) -> Builder<F, L>
598 where
599 F: failure::Strategy,
600 {
601 Builder {
602 storage: self.storage,
603 failure_strategy: strategy,
604 layers: self.layers,
605 }
606 }
607
608 /// Tries to register the provided [`prometheus`] `metric` in the underlying
609 /// [`prometheus::Registry`] in the way making it usable via the created
610 /// [`Recorder`] (and, so, [`metrics`] crate interfaces).
611 ///
612 /// Accepts only the following [`prometheus`] metrics:
613 /// - [`prometheus::IntCounter`], [`prometheus::IntCounterVec`]
614 /// - [`prometheus::Gauge`], [`prometheus::GaugeVec`]
615 /// - [`prometheus::Histogram`], [`prometheus::HistogramVec`]
616 ///
617 /// # Errors
618 ///
619 /// If the underlying [`prometheus::Registry`] fails to register the
620 /// provided `metric`.
621 ///
622 /// # Example
623 ///
624 /// ```rust
625 /// let gauge = prometheus::Gauge::new("value", "help")?;
626 ///
627 /// metrics_prometheus::Recorder::builder()
628 /// .try_with_metric(gauge.clone())?
629 /// .build_and_install();
630 ///
631 /// gauge.inc();
632 ///
633 /// let report = prometheus::TextEncoder::new()
634 /// .encode_to_string(&prometheus::default_registry().gather())?;
635 /// assert_eq!(
636 /// report.trim(),
637 /// r#"
638 /// ## HELP value help
639 /// ## TYPE value gauge
640 /// value 1
641 /// "#
642 /// .trim(),
643 /// );
644 ///
645 /// metrics::gauge!("value").increment(1.0);
646 ///
647 /// let report = prometheus::TextEncoder::new()
648 /// .encode_to_string(&prometheus::default_registry().gather())?;
649 /// assert_eq!(
650 /// report.trim(),
651 /// r#"
652 /// ## HELP value help
653 /// ## TYPE value gauge
654 /// value 2
655 /// "#
656 /// .trim(),
657 /// );
658 /// # Ok::<_, prometheus::Error>(())
659 /// ```
660 pub fn try_with_metric<M>(self, metric: M) -> prometheus::Result<Self>
661 where
662 M: metric::Bundled + prometheus::core::Collector,
663 <M as metric::Bundled>::Bundle:
664 prometheus::core::Collector + Clone + 'static,
665 storage::Mutable: storage::Get<
666 storage::mutable::Collection<<M as metric::Bundled>::Bundle>,
667 >,
668 {
669 self.storage.register_external(metric)?;
670 Ok(self)
671 }
672
673 /// Registers the provided [`prometheus`] `metric` in the underlying
674 /// [`prometheus::Registry`] in the way making it usable via the created
675 /// [`Recorder`] (and, so, [`metrics`] crate interfaces).
676 ///
677 /// Accepts only the following [`prometheus`] metrics:
678 /// - [`prometheus::IntCounter`], [`prometheus::IntCounterVec`]
679 /// - [`prometheus::Gauge`], [`prometheus::GaugeVec`]
680 /// - [`prometheus::Histogram`], [`prometheus::HistogramVec`]
681 ///
682 /// # Panics
683 ///
684 /// If the underlying [`prometheus::Registry`] fails to register the
685 /// provided `metric`.
686 ///
687 /// # Example
688 ///
689 /// ```rust
690 /// let counter = prometheus::IntCounter::new("value", "help")?;
691 ///
692 /// metrics_prometheus::Recorder::builder()
693 /// .with_metric(counter.clone())
694 /// .build_and_install();
695 ///
696 /// counter.inc();
697 ///
698 /// let report = prometheus::TextEncoder::new()
699 /// .encode_to_string(&prometheus::default_registry().gather())?;
700 /// assert_eq!(
701 /// report.trim(),
702 /// r#"
703 /// ## HELP value help
704 /// ## TYPE value counter
705 /// value 1
706 /// "#
707 /// .trim(),
708 /// );
709 ///
710 /// metrics::counter!("value").increment(1);
711 ///
712 /// let report = prometheus::TextEncoder::new()
713 /// .encode_to_string(&prometheus::default_registry().gather())?;
714 /// assert_eq!(
715 /// report.trim(),
716 /// r#"
717 /// ## HELP value help
718 /// ## TYPE value counter
719 /// value 2
720 /// "#
721 /// .trim(),
722 /// );
723 /// # Ok::<_, prometheus::Error>(())
724 /// ```
725 pub fn with_metric<M>(self, metric: M) -> Self
726 where
727 M: metric::Bundled + prometheus::core::Collector,
728 <M as metric::Bundled>::Bundle:
729 prometheus::core::Collector + Clone + 'static,
730 storage::Mutable: storage::Get<
731 storage::mutable::Collection<<M as metric::Bundled>::Bundle>,
732 >,
733 {
734 self.try_with_metric(metric).unwrap_or_else(|e| {
735 panic!("failed to register `prometheus` metric: {e}")
736 })
737 }
738
739 /// Builds a [`Recorder`] out of this [`Builder`] and returns it being
740 /// wrapped into all the provided [`metrics::Layer`]s.
741 ///
742 /// # Usage
743 ///
744 /// Use this method if you want to:
745 /// - either install the built [`Recorder`] with the
746 /// [`metrics::set_global_recorder()`] manually;
747 /// - or to compose the built [`Recorder`] with some other
748 /// [`metrics::Recorder`]s (like being able to write into multiple
749 /// [`prometheus::Registry`]s via [`metrics::layer::Fanout`], for
750 /// example).
751 ///
752 /// Otherwise, consider using the [`build_and_install()`] method instead.
753 ///
754 /// [`build_and_install()`]: Builder::build_and_install
755 /// [`metrics::layer::Fanout`]: metrics_util::layers::Fanout
756 /// [`metrics::Layer`]: Layer
757 pub fn build(self) -> <L as Layer<Recorder<S>>>::Output
758 where
759 S: failure::Strategy,
760 L: Layer<Recorder<S>>,
761 {
762 let Self { storage, failure_strategy, layers } = self;
763 let rec = Recorder {
764 metrics: Arc::new(metrics_util::registry::Registry::new(
765 storage.clone(),
766 )),
767 storage,
768 failure_strategy,
769 };
770 layers.layer(rec)
771 }
772
773 /// Builds a [`FreezableRecorder`] out of this [`Builder`] and returns it
774 /// being wrapped into all the provided [`metrics::Layer`]s.
775 ///
776 /// # Usage
777 ///
778 /// Use this method if you want to:
779 /// - either install the built [`FreezableRecorder`] with the
780 /// [`metrics::set_global_recorder()`] manually;
781 /// - or to compose the built [`FreezableRecorder`] with some other
782 /// [`metrics::Recorder`]s (like being able to write into multiple
783 /// [`prometheus::Registry`]s via [`metrics::layer::Fanout`], for
784 /// example).
785 ///
786 /// Otherwise, consider using the [`build_freezable_and_install()`] method
787 /// instead.
788 ///
789 /// [`build_freezable_and_install()`]: Builder::build_freezable_and_install
790 /// [`metrics::layer::Fanout`]: metrics_util::layers::Fanout
791 /// [`metrics::Layer`]: Layer
792 /// [`FreezableRecorder`]: Freezable
793 pub fn build_freezable(self) -> <L as Layer<freezable::Recorder<S>>>::Output
794 where
795 S: failure::Strategy,
796 L: Layer<freezable::Recorder<S>>,
797 {
798 let Self { storage, failure_strategy, layers } = self;
799 let rec = freezable::Recorder::wrap(Recorder {
800 metrics: Arc::new(metrics_util::registry::Registry::new(
801 storage.clone(),
802 )),
803 storage,
804 failure_strategy,
805 });
806 layers.layer(rec)
807 }
808
809 /// Builds a [`FrozenRecorder`] out of this [`Builder`] and returns it being
810 /// wrapped into all the provided [`metrics::Layer`]s.
811 ///
812 /// # Usage
813 ///
814 /// Use this method if you want to:
815 /// - either install the built [`FrozenRecorder`] with the
816 /// [`metrics::set_global_recorder()`] manually;
817 /// - or to compose the built [`FrozenRecorder`] with some other
818 /// [`metrics::Recorder`]s (like being able to write into multiple
819 /// [`prometheus::Registry`]s via [`metrics::layer::Fanout`], for
820 /// example).
821 ///
822 /// Otherwise, consider using the [`build_frozen_and_install()`] method
823 /// instead.
824 ///
825 /// [`build_frozen_and_install()`]: Builder::build_frozen_and_install
826 /// [`metrics::layer::Fanout`]: metrics_util::layers::Fanout
827 /// [`metrics::Layer`]: Layer
828 /// [`FrozenRecorder`]: Frozen
829 pub fn build_frozen(self) -> <L as Layer<frozen::Recorder<S>>>::Output
830 where
831 S: failure::Strategy,
832 L: Layer<frozen::Recorder<S>>,
833 {
834 let Self { storage, failure_strategy, layers } = self;
835 let rec =
836 frozen::Recorder { storage: (&storage).into(), failure_strategy };
837 layers.layer(rec)
838 }
839
840 /// Builds a [`Recorder`] out of this [`Builder`] and tries to install it
841 /// with the [`metrics::set_global_recorder()`].
842 ///
843 /// # Errors
844 ///
845 /// If the built [`Recorder`] fails to be installed with the
846 /// [`metrics::set_global_recorder()`].
847 ///
848 /// # Example
849 ///
850 /// ```rust
851 /// use metrics_prometheus::{failure::strategy, recorder};
852 /// use metrics_util::layers::FilterLayer;
853 ///
854 /// let custom = prometheus::Registry::new_custom(Some("my".into()), None)?;
855 ///
856 /// let res = metrics_prometheus::Recorder::builder()
857 /// .with_registry(&custom)
858 /// .with_metric(prometheus::IntCounter::new("count", "help")?)
859 /// .with_metric(prometheus::Gauge::new("value", "help")?)
860 /// .with_failure_strategy(strategy::Panic)
861 /// .with_layer(FilterLayer::from_patterns(["ignored"]))
862 /// .try_build_and_install();
863 /// assert!(res.is_ok(), "cannot install `Recorder`: {}", res.unwrap_err());
864 ///
865 /// metrics::counter!("count").increment(1);
866 /// metrics::gauge!("value").increment(3.0);
867 /// metrics::histogram!("histo").record(38.0);
868 /// metrics::histogram!("ignored_histo").record(1.0);
869 ///
870 /// let report =
871 /// prometheus::TextEncoder::new().encode_to_string(&custom.gather())?;
872 /// assert_eq!(
873 /// report.trim(),
874 /// r#"
875 /// ## HELP my_count help
876 /// ## TYPE my_count counter
877 /// my_count 1
878 /// ## HELP my_histo histo
879 /// ## TYPE my_histo histogram
880 /// my_histo_bucket{le="0.005"} 0
881 /// my_histo_bucket{le="0.01"} 0
882 /// my_histo_bucket{le="0.025"} 0
883 /// my_histo_bucket{le="0.05"} 0
884 /// my_histo_bucket{le="0.1"} 0
885 /// my_histo_bucket{le="0.25"} 0
886 /// my_histo_bucket{le="0.5"} 0
887 /// my_histo_bucket{le="1"} 0
888 /// my_histo_bucket{le="2.5"} 0
889 /// my_histo_bucket{le="5"} 0
890 /// my_histo_bucket{le="10"} 0
891 /// my_histo_bucket{le="+Inf"} 1
892 /// my_histo_sum 38
893 /// my_histo_count 1
894 /// ## HELP my_value help
895 /// ## TYPE my_value gauge
896 /// my_value 3
897 /// "#
898 /// .trim(),
899 /// );
900 /// # Ok::<_, prometheus::Error>(())
901 /// ```
902 pub fn try_build_and_install(
903 self,
904 ) -> Result<Recorder<S>, metrics::SetRecorderError<L::Output>>
905 where
906 S: failure::Strategy + Clone,
907 L: Layer<Recorder<S>>,
908 <L as Layer<Recorder<S>>>::Output: metrics::Recorder + Sync + 'static,
909 {
910 let Self { storage, failure_strategy, layers } = self;
911 let rec = Recorder {
912 metrics: Arc::new(metrics_util::registry::Registry::new(
913 storage.clone(),
914 )),
915 storage,
916 failure_strategy,
917 };
918 metrics::set_global_recorder(layers.layer(rec.clone()))?;
919 Ok(rec)
920 }
921
922 /// Builds a [`FreezableRecorder`] out of this [`Builder`] and tries to
923 /// install it with the [`metrics::set_global_recorder()`].
924 ///
925 /// # Errors
926 ///
927 /// If the built [`FreezableRecorder`] fails to be installed with the
928 /// [`metrics::set_global_recorder()`].
929 ///
930 /// # Example
931 ///
932 /// ```rust
933 /// use metrics_prometheus::{failure::strategy, recorder};
934 /// use metrics_util::layers::FilterLayer;
935 ///
936 /// let custom = prometheus::Registry::new_custom(Some("my".into()), None)?;
937 ///
938 /// let res = metrics_prometheus::Recorder::builder()
939 /// .with_registry(&custom)
940 /// .with_metric(prometheus::IntCounter::new("count", "help")?)
941 /// .with_failure_strategy(strategy::Panic)
942 /// .with_layer(FilterLayer::from_patterns(["ignored"]))
943 /// .try_build_freezable_and_install();
944 /// assert!(
945 /// res.is_ok(),
946 /// "cannot install `FreezableRecorder`: {}",
947 /// res.unwrap_err(),
948 /// );
949 ///
950 /// metrics::gauge!("value").increment(3.0);
951 /// metrics::gauge!("ignored_value").increment(1.0);
952 ///
953 /// res.unwrap().freeze();
954 ///
955 /// metrics::counter!("count").increment(1);
956 /// metrics::gauge!("value").increment(4.0);
957 ///
958 /// let report =
959 /// prometheus::TextEncoder::new().encode_to_string(&custom.gather())?;
960 /// assert_eq!(
961 /// report.trim(),
962 /// r#"
963 /// ## HELP my_count help
964 /// ## TYPE my_count counter
965 /// my_count 1
966 /// ## HELP my_value value
967 /// ## TYPE my_value gauge
968 /// my_value 7
969 /// "#
970 /// .trim(),
971 /// );
972 /// # Ok::<_, prometheus::Error>(())
973 /// ```
974 ///
975 /// [`FreezableRecorder`]: Freezable
976 pub fn try_build_freezable_and_install(
977 self,
978 ) -> Result<freezable::Recorder<S>, metrics::SetRecorderError<L::Output>>
979 where
980 S: failure::Strategy + Clone,
981 L: Layer<freezable::Recorder<S>>,
982 <L as Layer<freezable::Recorder<S>>>::Output:
983 metrics::Recorder + Sync + 'static,
984 {
985 let Self { storage, failure_strategy, layers } = self;
986 let rec = freezable::Recorder::wrap(Recorder {
987 metrics: Arc::new(metrics_util::registry::Registry::new(
988 storage.clone(),
989 )),
990 storage,
991 failure_strategy,
992 });
993 metrics::set_global_recorder(layers.layer(rec.clone()))?;
994 Ok(rec)
995 }
996
997 /// Builds a [`FrozenRecorder`] out of this [`Builder`] and tries to install
998 /// it with the [`metrics::set_global_recorder()`].
999 ///
1000 /// Returns the [`prometheus::Registry`] backing the installed
1001 /// [`FrozenRecorder`], as there is nothing you can configure with the
1002 /// installed [`FrozenRecorder`] itself.
1003 ///
1004 /// # Errors
1005 ///
1006 /// If the built [`FrozenRecorder`] fails to be installed with the
1007 /// [`metrics::set_global_recorder()`].
1008 ///
1009 /// # Example
1010 ///
1011 /// ```rust
1012 /// use metrics_prometheus::{failure::strategy, recorder};
1013 /// use metrics_util::layers::FilterLayer;
1014 ///
1015 /// let custom = prometheus::Registry::new_custom(Some("my".into()), None)?;
1016 ///
1017 /// let res = metrics_prometheus::Recorder::builder()
1018 /// .with_registry(&custom)
1019 /// .with_metric(prometheus::IntCounter::new("count", "help")?)
1020 /// .with_metric(prometheus::Gauge::new("value", "help")?)
1021 /// .with_metric(prometheus::Gauge::new("ignored_value", "help")?)
1022 /// .with_failure_strategy(strategy::Panic)
1023 /// .with_layer(FilterLayer::from_patterns(["ignored"]))
1024 /// .try_build_frozen_and_install();
1025 /// assert!(
1026 /// res.is_ok(),
1027 /// "cannot install `FrozenRecorder`: {}",
1028 /// res.unwrap_err(),
1029 /// );
1030 ///
1031 /// metrics::counter!("count").increment(1);
1032 /// metrics::gauge!("value").increment(3.0);
1033 /// metrics::gauge!("ignored_value").increment(1.0);
1034 ///
1035 /// let report =
1036 /// prometheus::TextEncoder::new().encode_to_string(&custom.gather())?;
1037 /// assert_eq!(
1038 /// report.trim(),
1039 /// r#"
1040 /// ## HELP my_count help
1041 /// ## TYPE my_count counter
1042 /// my_count 1
1043 /// ## HELP my_ignored_value help
1044 /// ## TYPE my_ignored_value gauge
1045 /// my_ignored_value 0
1046 /// ## HELP my_value help
1047 /// ## TYPE my_value gauge
1048 /// my_value 3
1049 /// "#
1050 /// .trim(),
1051 /// );
1052 /// # Ok::<_, prometheus::Error>(())
1053 /// ```
1054 ///
1055 /// [`FrozenRecorder`]: Frozen
1056 pub fn try_build_frozen_and_install(
1057 self,
1058 ) -> Result<prometheus::Registry, metrics::SetRecorderError<L::Output>>
1059 where
1060 S: failure::Strategy + Clone,
1061 L: Layer<frozen::Recorder<S>>,
1062 <L as Layer<frozen::Recorder<S>>>::Output:
1063 metrics::Recorder + Sync + 'static,
1064 {
1065 let Self { storage, failure_strategy, layers } = self;
1066 let rec =
1067 frozen::Recorder { storage: (&storage).into(), failure_strategy };
1068 metrics::set_global_recorder(layers.layer(rec))?;
1069 Ok(storage.prometheus)
1070 }
1071
1072 /// Builds a [`Recorder`] out of this [`Builder`] and installs it with the
1073 /// [`metrics::set_global_recorder()`].
1074 ///
1075 /// # Panics
1076 ///
1077 /// If the built [`Recorder`] fails to be installed with the
1078 /// [`metrics::set_global_recorder()`].
1079 ///
1080 /// # Example
1081 ///
1082 /// ```rust
1083 /// use metrics_prometheus::{failure::strategy, recorder};
1084 /// use metrics_util::layers::FilterLayer;
1085 ///
1086 /// let custom = prometheus::Registry::new_custom(Some("my".into()), None)?;
1087 ///
1088 /// let recorder = metrics_prometheus::Recorder::builder()
1089 /// .with_registry(custom)
1090 /// .with_metric(prometheus::IntCounter::new("count", "help")?)
1091 /// .with_metric(prometheus::Gauge::new("value", "help")?)
1092 /// .with_failure_strategy(strategy::Panic)
1093 /// .with_layer(FilterLayer::from_patterns(["ignored"]))
1094 /// .build_and_install();
1095 ///
1096 /// metrics::counter!("count").increment(1);
1097 /// metrics::gauge!("value").increment(3.0);
1098 /// metrics::histogram!("histo").record(38.0);
1099 /// metrics::histogram!("ignored_histo").record(1.0);
1100 ///
1101 /// let report = prometheus::TextEncoder::new()
1102 /// .encode_to_string(&recorder.registry().gather())?;
1103 /// assert_eq!(
1104 /// report.trim(),
1105 /// r#"
1106 /// ## HELP my_count help
1107 /// ## TYPE my_count counter
1108 /// my_count 1
1109 /// ## HELP my_histo histo
1110 /// ## TYPE my_histo histogram
1111 /// my_histo_bucket{le="0.005"} 0
1112 /// my_histo_bucket{le="0.01"} 0
1113 /// my_histo_bucket{le="0.025"} 0
1114 /// my_histo_bucket{le="0.05"} 0
1115 /// my_histo_bucket{le="0.1"} 0
1116 /// my_histo_bucket{le="0.25"} 0
1117 /// my_histo_bucket{le="0.5"} 0
1118 /// my_histo_bucket{le="1"} 0
1119 /// my_histo_bucket{le="2.5"} 0
1120 /// my_histo_bucket{le="5"} 0
1121 /// my_histo_bucket{le="10"} 0
1122 /// my_histo_bucket{le="+Inf"} 1
1123 /// my_histo_sum 38
1124 /// my_histo_count 1
1125 /// ## HELP my_value help
1126 /// ## TYPE my_value gauge
1127 /// my_value 3
1128 /// "#
1129 /// .trim(),
1130 /// );
1131 /// # Ok::<_, prometheus::Error>(())
1132 /// ```
1133 pub fn build_and_install(self) -> Recorder<S>
1134 where
1135 S: failure::Strategy + Clone,
1136 L: Layer<Recorder<S>>,
1137 <L as Layer<Recorder<S>>>::Output: metrics::Recorder + Sync + 'static,
1138 {
1139 self.try_build_and_install().unwrap_or_else(|e| {
1140 panic!(
1141 "failed to install `metrics_prometheus::Recorder` with \
1142 `metrics::set_global_recorder()`: {e}",
1143 )
1144 })
1145 }
1146
1147 /// Builds a [`FreezableRecorder`] out of this [`Builder`] and installs it
1148 /// with the [`metrics::set_global_recorder()`].
1149 ///
1150 /// # Panics
1151 ///
1152 /// If the built [`FreezableRecorder`] fails to be installed with the
1153 /// [`metrics::set_global_recorder()`].
1154 ///
1155 /// # Example
1156 ///
1157 /// ```rust
1158 /// use metrics_prometheus::{failure::strategy, recorder};
1159 /// use metrics_util::layers::FilterLayer;
1160 ///
1161 /// let custom = prometheus::Registry::new_custom(Some("my".into()), None)?;
1162 ///
1163 /// let recorder = metrics_prometheus::Recorder::builder()
1164 /// .with_registry(&custom)
1165 /// .with_metric(prometheus::IntCounter::new("count", "help")?)
1166 /// .with_failure_strategy(strategy::Panic)
1167 /// .with_layer(FilterLayer::from_patterns(["ignored"]))
1168 /// .build_freezable_and_install();
1169 ///
1170 /// metrics::gauge!("value").increment(3.0);
1171 /// metrics::gauge!("ignored_value").increment(1.0);
1172 ///
1173 /// recorder.freeze();
1174 ///
1175 /// metrics::counter!("count").increment(1);
1176 /// metrics::gauge!("value").increment(4.0);
1177 ///
1178 /// let report =
1179 /// prometheus::TextEncoder::new().encode_to_string(&custom.gather())?;
1180 /// assert_eq!(
1181 /// report.trim(),
1182 /// r#"
1183 /// ## HELP my_count help
1184 /// ## TYPE my_count counter
1185 /// my_count 1
1186 /// ## HELP my_value value
1187 /// ## TYPE my_value gauge
1188 /// my_value 7
1189 /// "#
1190 /// .trim(),
1191 /// );
1192 /// # Ok::<_, prometheus::Error>(())
1193 /// ```
1194 ///
1195 /// [`FreezableRecorder`]: Freezable
1196 pub fn build_freezable_and_install(self) -> freezable::Recorder<S>
1197 where
1198 S: failure::Strategy + Clone,
1199 L: Layer<freezable::Recorder<S>>,
1200 <L as Layer<freezable::Recorder<S>>>::Output:
1201 metrics::Recorder + Sync + 'static,
1202 {
1203 self.try_build_freezable_and_install().unwrap_or_else(|e| {
1204 panic!(
1205 "failed to install `metrics_prometheus::FreezableRecorder` \
1206 with `metrics::set_global_recorder()`: {e}",
1207 )
1208 })
1209 }
1210
1211 /// Builds a [`FrozenRecorder`] out of this [`Builder`] and installs it with
1212 /// the [`metrics::set_global_recorder()`].
1213 ///
1214 /// Returns the [`prometheus::Registry`] backing the installed
1215 /// [`FrozenRecorder`], as there is nothing you can configure with the
1216 /// installed [`FrozenRecorder`] itself.
1217 ///
1218 /// # Panics
1219 ///
1220 /// If the built [`FrozenRecorder`] fails to be installed with the
1221 /// [`metrics::set_global_recorder()`].
1222 ///
1223 /// # Example
1224 ///
1225 /// ```rust
1226 /// use metrics_prometheus::{failure::strategy, recorder};
1227 /// use metrics_util::layers::FilterLayer;
1228 ///
1229 /// let custom = prometheus::Registry::new_custom(Some("my".into()), None)?;
1230 ///
1231 /// metrics_prometheus::Recorder::builder()
1232 /// .with_registry(&custom)
1233 /// .with_metric(prometheus::IntCounter::new("count", "help")?)
1234 /// .with_metric(prometheus::Gauge::new("value", "help")?)
1235 /// .with_metric(prometheus::Gauge::new("ignored_value", "help")?)
1236 /// .with_failure_strategy(strategy::Panic)
1237 /// .with_layer(FilterLayer::from_patterns(["ignored"]))
1238 /// .build_frozen_and_install();
1239 ///
1240 /// metrics::counter!("count").increment(1);
1241 /// metrics::gauge!("value").increment(3.0);
1242 /// metrics::gauge!("ignored_value").increment(1.0);
1243 ///
1244 /// let report =
1245 /// prometheus::TextEncoder::new().encode_to_string(&custom.gather())?;
1246 /// assert_eq!(
1247 /// report.trim(),
1248 /// r#"
1249 /// ## HELP my_count help
1250 /// ## TYPE my_count counter
1251 /// my_count 1
1252 /// ## HELP my_ignored_value help
1253 /// ## TYPE my_ignored_value gauge
1254 /// my_ignored_value 0
1255 /// ## HELP my_value help
1256 /// ## TYPE my_value gauge
1257 /// my_value 3
1258 /// "#
1259 /// .trim(),
1260 /// );
1261 /// # Ok::<_, prometheus::Error>(())
1262 /// ```
1263 ///
1264 /// [`FrozenRecorder`]: Frozen
1265 pub fn build_frozen_and_install(self) -> prometheus::Registry
1266 where
1267 S: failure::Strategy + Clone,
1268 L: Layer<frozen::Recorder<S>>,
1269 <L as Layer<frozen::Recorder<S>>>::Output:
1270 metrics::Recorder + Sync + 'static,
1271 {
1272 self.try_build_frozen_and_install().unwrap_or_else(|e| {
1273 panic!(
1274 "failed to install `metrics_prometheus::FrozenRecorder` with \
1275 `metrics::set_global_recorder()`: {e}",
1276 )
1277 })
1278 }
1279}
1280
1281impl<S, H, T> Builder<S, layer::Stack<H, T>> {
1282 /// Adds the provided [`metrics::Layer`] to wrap the built [`Recorder`] upon
1283 /// its installation with the [`metrics::set_global_recorder()`].
1284 ///
1285 /// # Example
1286 ///
1287 /// ```rust
1288 /// use metrics_util::layers::FilterLayer;
1289 ///
1290 /// metrics_prometheus::Recorder::builder()
1291 /// .with_layer(FilterLayer::from_patterns(["ignored"]))
1292 /// .with_layer(FilterLayer::from_patterns(["skipped"]))
1293 /// .build_and_install();
1294 ///
1295 /// metrics::counter!("ignored_counter").increment(1);
1296 /// metrics::counter!("reported_counter").increment(1);
1297 /// metrics::counter!("skipped_counter").increment(1);
1298 ///
1299 /// let report = prometheus::TextEncoder::new()
1300 /// .encode_to_string(&prometheus::default_registry().gather())?;
1301 /// assert_eq!(
1302 /// report.trim(),
1303 /// r#"
1304 /// ## HELP reported_counter reported_counter
1305 /// ## TYPE reported_counter counter
1306 /// reported_counter 1
1307 /// "#
1308 /// .trim(),
1309 /// );
1310 /// # Ok::<_, prometheus::Error>(())
1311 /// ```
1312 ///
1313 /// [`metrics::Layer`]: Layer
1314 pub fn with_layer<L>(
1315 self,
1316 layer: L,
1317 ) -> Builder<S, layer::Stack<L, layer::Stack<H, T>>>
1318 where
1319 L: Layer<<layer::Stack<H, T> as Layer<Recorder<S>>>::Output>,
1320 layer::Stack<H, T>: Layer<Recorder<S>>,
1321 {
1322 Builder {
1323 storage: self.storage,
1324 failure_strategy: self.failure_strategy,
1325 layers: self.layers.push(layer),
1326 }
1327 }
1328}
1329
1330/// Ad hoc polymorphism for accepting either a reference or an owned function
1331/// argument.
1332pub trait IntoCow<'a, T: ToOwned + ?Sized + 'a> {
1333 /// Wraps this reference (or owned value) into a [`Cow`].
1334 #[must_use]
1335 fn into_cow(self) -> Cow<'a, T>;
1336}
1337
1338impl<'a> IntoCow<'a, Self> for prometheus::Registry {
1339 fn into_cow(self) -> Cow<'a, Self> {
1340 Cow::Owned(self)
1341 }
1342}
1343
1344impl<'a> IntoCow<'a, prometheus::Registry> for &'a prometheus::Registry {
1345 fn into_cow(self) -> Cow<'a, prometheus::Registry> {
1346 Cow::Borrowed(self)
1347 }
1348}