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 #[expect( // anonymous lifetimes in `impl Trait` are unstable
560 single_use_lifetimes,
561 reason = "anonymous lifetimes in `impl Trait` are unstable"
562 )]
563 pub fn with_registry<'r>(
564 mut self,
565 registry: impl IntoCow<'r, prometheus::Registry>,
566 ) -> Self {
567 self.storage.prometheus = registry.into_cow().into_owned();
568 self
569 }
570
571 /// Sets the provided [`failure::Strategy`] to be used by the built
572 /// [`Recorder`].
573 ///
574 /// [`prometheus::Registry`] has far more stricter semantics than the ones
575 /// implied by a [`metrics::Recorder`]. That's why incorrect usage of
576 /// [`prometheus`] metrics via [`metrics`] crate will inevitably lead to a
577 /// [`prometheus::Registry`] returning a [`prometheus::Error`] instead of a
578 /// registering the metric. The returned [`prometheus::Error`] can be either
579 /// turned into a panic, or just silently ignored, making the [`Recorder`]
580 /// to return a no-op metric instead (see [`metrics::Counter::noop()`] for
581 /// example).
582 ///
583 /// The default [`failure::Strategy`] is [`PanicInDebugNoOpInRelease`]. See
584 /// [`failure::strategy`] module for other available [`failure::Strategy`]s,
585 /// or provide your own one by implementing the [`failure::Strategy`] trait.
586 ///
587 /// # Example
588 ///
589 /// ```rust
590 /// use metrics_prometheus::failure::strategy;
591 ///
592 /// metrics_prometheus::Recorder::builder()
593 /// .with_failure_strategy(strategy::NoOp)
594 /// .build_and_install();
595 ///
596 /// metrics::counter!("invalid.name").increment(1);
597 ///
598 /// let stats = prometheus::default_registry().gather();
599 /// assert_eq!(stats.len(), 0);
600 /// ```
601 pub fn with_failure_strategy<F>(self, strategy: F) -> Builder<F, L>
602 where
603 F: failure::Strategy,
604 {
605 Builder {
606 storage: self.storage,
607 failure_strategy: strategy,
608 layers: self.layers,
609 }
610 }
611
612 /// Tries to register the provided [`prometheus`] `metric` in the underlying
613 /// [`prometheus::Registry`] in the way making it usable via the created
614 /// [`Recorder`] (and, so, [`metrics`] crate interfaces).
615 ///
616 /// Accepts only the following [`prometheus`] metrics:
617 /// - [`prometheus::IntCounter`], [`prometheus::IntCounterVec`]
618 /// - [`prometheus::Gauge`], [`prometheus::GaugeVec`]
619 /// - [`prometheus::Histogram`], [`prometheus::HistogramVec`]
620 ///
621 /// # Errors
622 ///
623 /// If the underlying [`prometheus::Registry`] fails to register the
624 /// provided `metric`.
625 ///
626 /// # Example
627 ///
628 /// ```rust
629 /// let gauge = prometheus::Gauge::new("value", "help")?;
630 ///
631 /// metrics_prometheus::Recorder::builder()
632 /// .try_with_metric(gauge.clone())?
633 /// .build_and_install();
634 ///
635 /// gauge.inc();
636 ///
637 /// let report = prometheus::TextEncoder::new()
638 /// .encode_to_string(&prometheus::default_registry().gather())?;
639 /// assert_eq!(
640 /// report.trim(),
641 /// r#"
642 /// ## HELP value help
643 /// ## TYPE value gauge
644 /// value 1
645 /// "#
646 /// .trim(),
647 /// );
648 ///
649 /// metrics::gauge!("value").increment(1.0);
650 ///
651 /// let report = prometheus::TextEncoder::new()
652 /// .encode_to_string(&prometheus::default_registry().gather())?;
653 /// assert_eq!(
654 /// report.trim(),
655 /// r#"
656 /// ## HELP value help
657 /// ## TYPE value gauge
658 /// value 2
659 /// "#
660 /// .trim(),
661 /// );
662 /// # Ok::<_, prometheus::Error>(())
663 /// ```
664 pub fn try_with_metric<M>(self, metric: M) -> prometheus::Result<Self>
665 where
666 M: metric::Bundled + prometheus::core::Collector,
667 <M as metric::Bundled>::Bundle:
668 prometheus::core::Collector + Clone + 'static,
669 storage::Mutable: storage::Get<
670 storage::mutable::Collection<<M as metric::Bundled>::Bundle>,
671 >,
672 {
673 self.storage.register_external(metric)?;
674 Ok(self)
675 }
676
677 /// Registers the provided [`prometheus`] `metric` in the underlying
678 /// [`prometheus::Registry`] in the way making it usable via the created
679 /// [`Recorder`] (and, so, [`metrics`] crate interfaces).
680 ///
681 /// Accepts only the following [`prometheus`] metrics:
682 /// - [`prometheus::IntCounter`], [`prometheus::IntCounterVec`]
683 /// - [`prometheus::Gauge`], [`prometheus::GaugeVec`]
684 /// - [`prometheus::Histogram`], [`prometheus::HistogramVec`]
685 ///
686 /// # Panics
687 ///
688 /// If the underlying [`prometheus::Registry`] fails to register the
689 /// provided `metric`.
690 ///
691 /// # Example
692 ///
693 /// ```rust
694 /// let counter = prometheus::IntCounter::new("value", "help")?;
695 ///
696 /// metrics_prometheus::Recorder::builder()
697 /// .with_metric(counter.clone())
698 /// .build_and_install();
699 ///
700 /// counter.inc();
701 ///
702 /// let report = prometheus::TextEncoder::new()
703 /// .encode_to_string(&prometheus::default_registry().gather())?;
704 /// assert_eq!(
705 /// report.trim(),
706 /// r#"
707 /// ## HELP value help
708 /// ## TYPE value counter
709 /// value 1
710 /// "#
711 /// .trim(),
712 /// );
713 ///
714 /// metrics::counter!("value").increment(1);
715 ///
716 /// let report = prometheus::TextEncoder::new()
717 /// .encode_to_string(&prometheus::default_registry().gather())?;
718 /// assert_eq!(
719 /// report.trim(),
720 /// r#"
721 /// ## HELP value help
722 /// ## TYPE value counter
723 /// value 2
724 /// "#
725 /// .trim(),
726 /// );
727 /// # Ok::<_, prometheus::Error>(())
728 /// ```
729 pub fn with_metric<M>(self, metric: M) -> Self
730 where
731 M: metric::Bundled + prometheus::core::Collector,
732 <M as metric::Bundled>::Bundle:
733 prometheus::core::Collector + Clone + 'static,
734 storage::Mutable: storage::Get<
735 storage::mutable::Collection<<M as metric::Bundled>::Bundle>,
736 >,
737 {
738 self.try_with_metric(metric).unwrap_or_else(|e| {
739 panic!("failed to register `prometheus` metric: {e}")
740 })
741 }
742
743 /// Builds a [`Recorder`] out of this [`Builder`] and returns it being
744 /// wrapped into all the provided [`metrics::Layer`]s.
745 ///
746 /// # Usage
747 ///
748 /// Use this method if you want to:
749 /// - either install the built [`Recorder`] with the
750 /// [`metrics::set_global_recorder()`] manually;
751 /// - or to compose the built [`Recorder`] with some other
752 /// [`metrics::Recorder`]s (like being able to write into multiple
753 /// [`prometheus::Registry`]s via [`metrics::layer::Fanout`], for
754 /// example).
755 ///
756 /// Otherwise, consider using the [`build_and_install()`] method instead.
757 ///
758 /// [`build_and_install()`]: Builder::build_and_install
759 /// [`metrics::layer::Fanout`]: metrics_util::layers::Fanout
760 /// [`metrics::Layer`]: Layer
761 pub fn build(self) -> <L as Layer<Recorder<S>>>::Output
762 where
763 S: failure::Strategy,
764 L: Layer<Recorder<S>>,
765 {
766 let Self { storage, failure_strategy, layers } = self;
767 let rec = Recorder {
768 metrics: Arc::new(metrics_util::registry::Registry::new(
769 storage.clone(),
770 )),
771 storage,
772 failure_strategy,
773 };
774 layers.layer(rec)
775 }
776
777 /// Builds a [`FreezableRecorder`] out of this [`Builder`] and returns it
778 /// being wrapped into all the provided [`metrics::Layer`]s.
779 ///
780 /// # Usage
781 ///
782 /// Use this method if you want to:
783 /// - either install the built [`FreezableRecorder`] with the
784 /// [`metrics::set_global_recorder()`] manually;
785 /// - or to compose the built [`FreezableRecorder`] with some other
786 /// [`metrics::Recorder`]s (like being able to write into multiple
787 /// [`prometheus::Registry`]s via [`metrics::layer::Fanout`], for
788 /// example).
789 ///
790 /// Otherwise, consider using the [`build_freezable_and_install()`] method
791 /// instead.
792 ///
793 /// [`build_freezable_and_install()`]: Builder::build_freezable_and_install
794 /// [`metrics::layer::Fanout`]: metrics_util::layers::Fanout
795 /// [`metrics::Layer`]: Layer
796 /// [`FreezableRecorder`]: Freezable
797 pub fn build_freezable(self) -> <L as Layer<freezable::Recorder<S>>>::Output
798 where
799 S: failure::Strategy,
800 L: Layer<freezable::Recorder<S>>,
801 {
802 let Self { storage, failure_strategy, layers } = self;
803 let rec = freezable::Recorder::wrap(Recorder {
804 metrics: Arc::new(metrics_util::registry::Registry::new(
805 storage.clone(),
806 )),
807 storage,
808 failure_strategy,
809 });
810 layers.layer(rec)
811 }
812
813 /// Builds a [`FrozenRecorder`] out of this [`Builder`] and returns it being
814 /// wrapped into all the provided [`metrics::Layer`]s.
815 ///
816 /// # Usage
817 ///
818 /// Use this method if you want to:
819 /// - either install the built [`FrozenRecorder`] with the
820 /// [`metrics::set_global_recorder()`] manually;
821 /// - or to compose the built [`FrozenRecorder`] with some other
822 /// [`metrics::Recorder`]s (like being able to write into multiple
823 /// [`prometheus::Registry`]s via [`metrics::layer::Fanout`], for
824 /// example).
825 ///
826 /// Otherwise, consider using the [`build_frozen_and_install()`] method
827 /// instead.
828 ///
829 /// [`build_frozen_and_install()`]: Builder::build_frozen_and_install
830 /// [`metrics::layer::Fanout`]: metrics_util::layers::Fanout
831 /// [`metrics::Layer`]: Layer
832 /// [`FrozenRecorder`]: Frozen
833 pub fn build_frozen(self) -> <L as Layer<frozen::Recorder<S>>>::Output
834 where
835 S: failure::Strategy,
836 L: Layer<frozen::Recorder<S>>,
837 {
838 let Self { storage, failure_strategy, layers } = self;
839 let rec =
840 frozen::Recorder { storage: (&storage).into(), failure_strategy };
841 layers.layer(rec)
842 }
843
844 /// Builds a [`Recorder`] out of this [`Builder`] and tries to install it
845 /// with the [`metrics::set_global_recorder()`].
846 ///
847 /// # Errors
848 ///
849 /// If the built [`Recorder`] fails to be installed with the
850 /// [`metrics::set_global_recorder()`].
851 ///
852 /// # Example
853 ///
854 /// ```rust
855 /// use metrics_prometheus::{failure::strategy, recorder};
856 /// use metrics_util::layers::FilterLayer;
857 ///
858 /// let custom = prometheus::Registry::new_custom(Some("my".into()), None)?;
859 ///
860 /// let res = metrics_prometheus::Recorder::builder()
861 /// .with_registry(&custom)
862 /// .with_metric(prometheus::IntCounter::new("count", "help")?)
863 /// .with_metric(prometheus::Gauge::new("value", "help")?)
864 /// .with_failure_strategy(strategy::Panic)
865 /// .with_layer(FilterLayer::from_patterns(["ignored"]))
866 /// .try_build_and_install();
867 /// assert!(res.is_ok(), "cannot install `Recorder`: {}", res.unwrap_err());
868 ///
869 /// metrics::counter!("count").increment(1);
870 /// metrics::gauge!("value").increment(3.0);
871 /// metrics::histogram!("histo").record(38.0);
872 /// metrics::histogram!("ignored_histo").record(1.0);
873 ///
874 /// let report =
875 /// prometheus::TextEncoder::new().encode_to_string(&custom.gather())?;
876 /// assert_eq!(
877 /// report.trim(),
878 /// r#"
879 /// ## HELP my_count help
880 /// ## TYPE my_count counter
881 /// my_count 1
882 /// ## HELP my_histo histo
883 /// ## TYPE my_histo histogram
884 /// my_histo_bucket{le="0.005"} 0
885 /// my_histo_bucket{le="0.01"} 0
886 /// my_histo_bucket{le="0.025"} 0
887 /// my_histo_bucket{le="0.05"} 0
888 /// my_histo_bucket{le="0.1"} 0
889 /// my_histo_bucket{le="0.25"} 0
890 /// my_histo_bucket{le="0.5"} 0
891 /// my_histo_bucket{le="1"} 0
892 /// my_histo_bucket{le="2.5"} 0
893 /// my_histo_bucket{le="5"} 0
894 /// my_histo_bucket{le="10"} 0
895 /// my_histo_bucket{le="+Inf"} 1
896 /// my_histo_sum 38
897 /// my_histo_count 1
898 /// ## HELP my_value help
899 /// ## TYPE my_value gauge
900 /// my_value 3
901 /// "#
902 /// .trim(),
903 /// );
904 /// # Ok::<_, prometheus::Error>(())
905 /// ```
906 pub fn try_build_and_install(
907 self,
908 ) -> Result<Recorder<S>, metrics::SetRecorderError<L::Output>>
909 where
910 S: failure::Strategy + Clone,
911 L: Layer<Recorder<S>>,
912 <L as Layer<Recorder<S>>>::Output: metrics::Recorder + Sync + 'static,
913 {
914 let Self { storage, failure_strategy, layers } = self;
915 let rec = Recorder {
916 metrics: Arc::new(metrics_util::registry::Registry::new(
917 storage.clone(),
918 )),
919 storage,
920 failure_strategy,
921 };
922 metrics::set_global_recorder(layers.layer(rec.clone()))?;
923 Ok(rec)
924 }
925
926 /// Builds a [`FreezableRecorder`] out of this [`Builder`] and tries to
927 /// install it with the [`metrics::set_global_recorder()`].
928 ///
929 /// # Errors
930 ///
931 /// If the built [`FreezableRecorder`] fails to be installed with the
932 /// [`metrics::set_global_recorder()`].
933 ///
934 /// # Example
935 ///
936 /// ```rust
937 /// use metrics_prometheus::{failure::strategy, recorder};
938 /// use metrics_util::layers::FilterLayer;
939 ///
940 /// let custom = prometheus::Registry::new_custom(Some("my".into()), None)?;
941 ///
942 /// let res = metrics_prometheus::Recorder::builder()
943 /// .with_registry(&custom)
944 /// .with_metric(prometheus::IntCounter::new("count", "help")?)
945 /// .with_failure_strategy(strategy::Panic)
946 /// .with_layer(FilterLayer::from_patterns(["ignored"]))
947 /// .try_build_freezable_and_install();
948 /// assert!(
949 /// res.is_ok(),
950 /// "cannot install `FreezableRecorder`: {}",
951 /// res.unwrap_err(),
952 /// );
953 ///
954 /// metrics::gauge!("value").increment(3.0);
955 /// metrics::gauge!("ignored_value").increment(1.0);
956 ///
957 /// res.unwrap().freeze();
958 ///
959 /// metrics::counter!("count").increment(1);
960 /// metrics::gauge!("value").increment(4.0);
961 ///
962 /// let report =
963 /// prometheus::TextEncoder::new().encode_to_string(&custom.gather())?;
964 /// assert_eq!(
965 /// report.trim(),
966 /// r#"
967 /// ## HELP my_count help
968 /// ## TYPE my_count counter
969 /// my_count 1
970 /// ## HELP my_value value
971 /// ## TYPE my_value gauge
972 /// my_value 7
973 /// "#
974 /// .trim(),
975 /// );
976 /// # Ok::<_, prometheus::Error>(())
977 /// ```
978 ///
979 /// [`FreezableRecorder`]: Freezable
980 pub fn try_build_freezable_and_install(
981 self,
982 ) -> Result<freezable::Recorder<S>, metrics::SetRecorderError<L::Output>>
983 where
984 S: failure::Strategy + Clone,
985 L: Layer<freezable::Recorder<S>>,
986 <L as Layer<freezable::Recorder<S>>>::Output:
987 metrics::Recorder + Sync + 'static,
988 {
989 let Self { storage, failure_strategy, layers } = self;
990 let rec = freezable::Recorder::wrap(Recorder {
991 metrics: Arc::new(metrics_util::registry::Registry::new(
992 storage.clone(),
993 )),
994 storage,
995 failure_strategy,
996 });
997 metrics::set_global_recorder(layers.layer(rec.clone()))?;
998 Ok(rec)
999 }
1000
1001 /// Builds a [`FrozenRecorder`] out of this [`Builder`] and tries to install
1002 /// it with the [`metrics::set_global_recorder()`].
1003 ///
1004 /// Returns the [`prometheus::Registry`] backing the installed
1005 /// [`FrozenRecorder`], as there is nothing you can configure with the
1006 /// installed [`FrozenRecorder`] itself.
1007 ///
1008 /// # Errors
1009 ///
1010 /// If the built [`FrozenRecorder`] fails to be installed with the
1011 /// [`metrics::set_global_recorder()`].
1012 ///
1013 /// # Example
1014 ///
1015 /// ```rust
1016 /// use metrics_prometheus::{failure::strategy, recorder};
1017 /// use metrics_util::layers::FilterLayer;
1018 ///
1019 /// let custom = prometheus::Registry::new_custom(Some("my".into()), None)?;
1020 ///
1021 /// let res = metrics_prometheus::Recorder::builder()
1022 /// .with_registry(&custom)
1023 /// .with_metric(prometheus::IntCounter::new("count", "help")?)
1024 /// .with_metric(prometheus::Gauge::new("value", "help")?)
1025 /// .with_metric(prometheus::Gauge::new("ignored_value", "help")?)
1026 /// .with_failure_strategy(strategy::Panic)
1027 /// .with_layer(FilterLayer::from_patterns(["ignored"]))
1028 /// .try_build_frozen_and_install();
1029 /// assert!(
1030 /// res.is_ok(),
1031 /// "cannot install `FrozenRecorder`: {}",
1032 /// res.unwrap_err(),
1033 /// );
1034 ///
1035 /// metrics::counter!("count").increment(1);
1036 /// metrics::gauge!("value").increment(3.0);
1037 /// metrics::gauge!("ignored_value").increment(1.0);
1038 ///
1039 /// let report =
1040 /// prometheus::TextEncoder::new().encode_to_string(&custom.gather())?;
1041 /// assert_eq!(
1042 /// report.trim(),
1043 /// r#"
1044 /// ## HELP my_count help
1045 /// ## TYPE my_count counter
1046 /// my_count 1
1047 /// ## HELP my_ignored_value help
1048 /// ## TYPE my_ignored_value gauge
1049 /// my_ignored_value 0
1050 /// ## HELP my_value help
1051 /// ## TYPE my_value gauge
1052 /// my_value 3
1053 /// "#
1054 /// .trim(),
1055 /// );
1056 /// # Ok::<_, prometheus::Error>(())
1057 /// ```
1058 ///
1059 /// [`FrozenRecorder`]: Frozen
1060 pub fn try_build_frozen_and_install(
1061 self,
1062 ) -> Result<prometheus::Registry, metrics::SetRecorderError<L::Output>>
1063 where
1064 S: failure::Strategy + Clone,
1065 L: Layer<frozen::Recorder<S>>,
1066 <L as Layer<frozen::Recorder<S>>>::Output:
1067 metrics::Recorder + Sync + 'static,
1068 {
1069 let Self { storage, failure_strategy, layers } = self;
1070 let rec =
1071 frozen::Recorder { storage: (&storage).into(), failure_strategy };
1072 metrics::set_global_recorder(layers.layer(rec))?;
1073 Ok(storage.prometheus)
1074 }
1075
1076 /// Builds a [`Recorder`] out of this [`Builder`] and installs it with the
1077 /// [`metrics::set_global_recorder()`].
1078 ///
1079 /// # Panics
1080 ///
1081 /// If the built [`Recorder`] fails to be installed with the
1082 /// [`metrics::set_global_recorder()`].
1083 ///
1084 /// # Example
1085 ///
1086 /// ```rust
1087 /// use metrics_prometheus::{failure::strategy, recorder};
1088 /// use metrics_util::layers::FilterLayer;
1089 ///
1090 /// let custom = prometheus::Registry::new_custom(Some("my".into()), None)?;
1091 ///
1092 /// let recorder = metrics_prometheus::Recorder::builder()
1093 /// .with_registry(custom)
1094 /// .with_metric(prometheus::IntCounter::new("count", "help")?)
1095 /// .with_metric(prometheus::Gauge::new("value", "help")?)
1096 /// .with_failure_strategy(strategy::Panic)
1097 /// .with_layer(FilterLayer::from_patterns(["ignored"]))
1098 /// .build_and_install();
1099 ///
1100 /// metrics::counter!("count").increment(1);
1101 /// metrics::gauge!("value").increment(3.0);
1102 /// metrics::histogram!("histo").record(38.0);
1103 /// metrics::histogram!("ignored_histo").record(1.0);
1104 ///
1105 /// let report = prometheus::TextEncoder::new()
1106 /// .encode_to_string(&recorder.registry().gather())?;
1107 /// assert_eq!(
1108 /// report.trim(),
1109 /// r#"
1110 /// ## HELP my_count help
1111 /// ## TYPE my_count counter
1112 /// my_count 1
1113 /// ## HELP my_histo histo
1114 /// ## TYPE my_histo histogram
1115 /// my_histo_bucket{le="0.005"} 0
1116 /// my_histo_bucket{le="0.01"} 0
1117 /// my_histo_bucket{le="0.025"} 0
1118 /// my_histo_bucket{le="0.05"} 0
1119 /// my_histo_bucket{le="0.1"} 0
1120 /// my_histo_bucket{le="0.25"} 0
1121 /// my_histo_bucket{le="0.5"} 0
1122 /// my_histo_bucket{le="1"} 0
1123 /// my_histo_bucket{le="2.5"} 0
1124 /// my_histo_bucket{le="5"} 0
1125 /// my_histo_bucket{le="10"} 0
1126 /// my_histo_bucket{le="+Inf"} 1
1127 /// my_histo_sum 38
1128 /// my_histo_count 1
1129 /// ## HELP my_value help
1130 /// ## TYPE my_value gauge
1131 /// my_value 3
1132 /// "#
1133 /// .trim(),
1134 /// );
1135 /// # Ok::<_, prometheus::Error>(())
1136 /// ```
1137 pub fn build_and_install(self) -> Recorder<S>
1138 where
1139 S: failure::Strategy + Clone,
1140 L: Layer<Recorder<S>>,
1141 <L as Layer<Recorder<S>>>::Output: metrics::Recorder + Sync + 'static,
1142 {
1143 self.try_build_and_install().unwrap_or_else(|e| {
1144 panic!(
1145 "failed to install `metrics_prometheus::Recorder` with \
1146 `metrics::set_global_recorder()`: {e}",
1147 )
1148 })
1149 }
1150
1151 /// Builds a [`FreezableRecorder`] out of this [`Builder`] and installs it
1152 /// with the [`metrics::set_global_recorder()`].
1153 ///
1154 /// # Panics
1155 ///
1156 /// If the built [`FreezableRecorder`] fails to be installed with the
1157 /// [`metrics::set_global_recorder()`].
1158 ///
1159 /// # Example
1160 ///
1161 /// ```rust
1162 /// use metrics_prometheus::{failure::strategy, recorder};
1163 /// use metrics_util::layers::FilterLayer;
1164 ///
1165 /// let custom = prometheus::Registry::new_custom(Some("my".into()), None)?;
1166 ///
1167 /// let recorder = metrics_prometheus::Recorder::builder()
1168 /// .with_registry(&custom)
1169 /// .with_metric(prometheus::IntCounter::new("count", "help")?)
1170 /// .with_failure_strategy(strategy::Panic)
1171 /// .with_layer(FilterLayer::from_patterns(["ignored"]))
1172 /// .build_freezable_and_install();
1173 ///
1174 /// metrics::gauge!("value").increment(3.0);
1175 /// metrics::gauge!("ignored_value").increment(1.0);
1176 ///
1177 /// recorder.freeze();
1178 ///
1179 /// metrics::counter!("count").increment(1);
1180 /// metrics::gauge!("value").increment(4.0);
1181 ///
1182 /// let report =
1183 /// prometheus::TextEncoder::new().encode_to_string(&custom.gather())?;
1184 /// assert_eq!(
1185 /// report.trim(),
1186 /// r#"
1187 /// ## HELP my_count help
1188 /// ## TYPE my_count counter
1189 /// my_count 1
1190 /// ## HELP my_value value
1191 /// ## TYPE my_value gauge
1192 /// my_value 7
1193 /// "#
1194 /// .trim(),
1195 /// );
1196 /// # Ok::<_, prometheus::Error>(())
1197 /// ```
1198 ///
1199 /// [`FreezableRecorder`]: Freezable
1200 pub fn build_freezable_and_install(self) -> freezable::Recorder<S>
1201 where
1202 S: failure::Strategy + Clone,
1203 L: Layer<freezable::Recorder<S>>,
1204 <L as Layer<freezable::Recorder<S>>>::Output:
1205 metrics::Recorder + Sync + 'static,
1206 {
1207 self.try_build_freezable_and_install().unwrap_or_else(|e| {
1208 panic!(
1209 "failed to install `metrics_prometheus::FreezableRecorder` \
1210 with `metrics::set_global_recorder()`: {e}",
1211 )
1212 })
1213 }
1214
1215 /// Builds a [`FrozenRecorder`] out of this [`Builder`] and installs it with
1216 /// the [`metrics::set_global_recorder()`].
1217 ///
1218 /// Returns the [`prometheus::Registry`] backing the installed
1219 /// [`FrozenRecorder`], as there is nothing you can configure with the
1220 /// installed [`FrozenRecorder`] itself.
1221 ///
1222 /// # Panics
1223 ///
1224 /// If the built [`FrozenRecorder`] fails to be installed with the
1225 /// [`metrics::set_global_recorder()`].
1226 ///
1227 /// # Example
1228 ///
1229 /// ```rust
1230 /// use metrics_prometheus::{failure::strategy, recorder};
1231 /// use metrics_util::layers::FilterLayer;
1232 ///
1233 /// let custom = prometheus::Registry::new_custom(Some("my".into()), None)?;
1234 ///
1235 /// metrics_prometheus::Recorder::builder()
1236 /// .with_registry(&custom)
1237 /// .with_metric(prometheus::IntCounter::new("count", "help")?)
1238 /// .with_metric(prometheus::Gauge::new("value", "help")?)
1239 /// .with_metric(prometheus::Gauge::new("ignored_value", "help")?)
1240 /// .with_failure_strategy(strategy::Panic)
1241 /// .with_layer(FilterLayer::from_patterns(["ignored"]))
1242 /// .build_frozen_and_install();
1243 ///
1244 /// metrics::counter!("count").increment(1);
1245 /// metrics::gauge!("value").increment(3.0);
1246 /// metrics::gauge!("ignored_value").increment(1.0);
1247 ///
1248 /// let report =
1249 /// prometheus::TextEncoder::new().encode_to_string(&custom.gather())?;
1250 /// assert_eq!(
1251 /// report.trim(),
1252 /// r#"
1253 /// ## HELP my_count help
1254 /// ## TYPE my_count counter
1255 /// my_count 1
1256 /// ## HELP my_ignored_value help
1257 /// ## TYPE my_ignored_value gauge
1258 /// my_ignored_value 0
1259 /// ## HELP my_value help
1260 /// ## TYPE my_value gauge
1261 /// my_value 3
1262 /// "#
1263 /// .trim(),
1264 /// );
1265 /// # Ok::<_, prometheus::Error>(())
1266 /// ```
1267 ///
1268 /// [`FrozenRecorder`]: Frozen
1269 pub fn build_frozen_and_install(self) -> prometheus::Registry
1270 where
1271 S: failure::Strategy + Clone,
1272 L: Layer<frozen::Recorder<S>>,
1273 <L as Layer<frozen::Recorder<S>>>::Output:
1274 metrics::Recorder + Sync + 'static,
1275 {
1276 self.try_build_frozen_and_install().unwrap_or_else(|e| {
1277 panic!(
1278 "failed to install `metrics_prometheus::FrozenRecorder` with \
1279 `metrics::set_global_recorder()`: {e}",
1280 )
1281 })
1282 }
1283}
1284
1285impl<S, H, T> Builder<S, layer::Stack<H, T>> {
1286 /// Adds the provided [`metrics::Layer`] to wrap the built [`Recorder`] upon
1287 /// its installation with the [`metrics::set_global_recorder()`].
1288 ///
1289 /// # Example
1290 ///
1291 /// ```rust
1292 /// use metrics_util::layers::FilterLayer;
1293 ///
1294 /// metrics_prometheus::Recorder::builder()
1295 /// .with_layer(FilterLayer::from_patterns(["ignored"]))
1296 /// .with_layer(FilterLayer::from_patterns(["skipped"]))
1297 /// .build_and_install();
1298 ///
1299 /// metrics::counter!("ignored_counter").increment(1);
1300 /// metrics::counter!("reported_counter").increment(1);
1301 /// metrics::counter!("skipped_counter").increment(1);
1302 ///
1303 /// let report = prometheus::TextEncoder::new()
1304 /// .encode_to_string(&prometheus::default_registry().gather())?;
1305 /// assert_eq!(
1306 /// report.trim(),
1307 /// r#"
1308 /// ## HELP reported_counter reported_counter
1309 /// ## TYPE reported_counter counter
1310 /// reported_counter 1
1311 /// "#
1312 /// .trim(),
1313 /// );
1314 /// # Ok::<_, prometheus::Error>(())
1315 /// ```
1316 ///
1317 /// [`metrics::Layer`]: Layer
1318 pub fn with_layer<L>(
1319 self,
1320 layer: L,
1321 ) -> Builder<S, layer::Stack<L, layer::Stack<H, T>>>
1322 where
1323 L: Layer<<layer::Stack<H, T> as Layer<Recorder<S>>>::Output>,
1324 layer::Stack<H, T>: Layer<Recorder<S>>,
1325 {
1326 Builder {
1327 storage: self.storage,
1328 failure_strategy: self.failure_strategy,
1329 layers: self.layers.push(layer),
1330 }
1331 }
1332}
1333
1334/// Ad hoc polymorphism for accepting either a reference or an owned function
1335/// argument.
1336pub trait IntoCow<'a, T: ToOwned + ?Sized + 'a> {
1337 /// Wraps this reference (or owned value) into a [`Cow`].
1338 #[must_use]
1339 fn into_cow(self) -> Cow<'a, T>;
1340}
1341
1342impl<'a> IntoCow<'a, Self> for prometheus::Registry {
1343 fn into_cow(self) -> Cow<'a, Self> {
1344 Cow::Owned(self)
1345 }
1346}
1347
1348impl<'a> IntoCow<'a, prometheus::Registry> for &'a prometheus::Registry {
1349 fn into_cow(self) -> Cow<'a, prometheus::Registry> {
1350 Cow::Borrowed(self)
1351 }
1352}