1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165
//! Immutable storage of [`metric::Describable`].
use std::{collections::HashMap, sync::Arc};
use sealed::sealed;
use crate::{metric, Metric};
use super::KeyName;
/// Collection of [`Describable`] [`metric::Bundle`]s, stored in an immutable
/// [`Storage`].
///
/// [`Describable`]: metric::Describable
pub type Collection<M> = HashMap<KeyName, metric::Describable<M>>;
/// Snapshot of a [`mutable::Storage`], that is not capable of registering
/// metrics in a [`prometheus::Registry`] on the fly, but still allowing to
/// change their [`help` description] on the fly, once they were registered in
/// the [`mutable::Storage`] before snapshot.
///
/// In comparison with a [`metrics::Registry`], this immutable [`Storage`]
/// provides much less overhead of accessing an already registered metric (just
/// a simple [`HashMap`] lookup), however, is not capable of registering new
/// metrics on the fly.
///
/// [`metrics::Registry`]: metrics_util::registry::Registry
/// [`mutable::Storage`]: super::Mutable
/// [`help` description]: prometheus::proto::MetricFamily::get_help
#[derive(Debug)]
pub struct Storage {
/// [`Collection`] of [`prometheus::IntCounter`] metrics registered in this
/// immutable [`Storage`].
counters: Collection<metric::PrometheusIntCounter>,
/// [`Collection`] of [`prometheus::Gauge`] metrics registered in this
/// immutable [`Storage`].
gauges: Collection<metric::PrometheusGauge>,
/// [`Collection`] of [`prometheus::Histogram`] metrics registered in this
/// immutable [`Storage`].
histograms: Collection<metric::PrometheusHistogram>,
}
#[sealed]
impl super::Get<Collection<metric::PrometheusIntCounter>> for Storage {
fn collection(&self) -> &Collection<metric::PrometheusIntCounter> {
&self.counters
}
}
#[sealed]
impl super::Get<Collection<metric::PrometheusGauge>> for Storage {
fn collection(&self) -> &Collection<metric::PrometheusGauge> {
&self.gauges
}
}
#[sealed]
impl super::Get<Collection<metric::PrometheusHistogram>> for Storage {
fn collection(&self) -> &Collection<metric::PrometheusHistogram> {
&self.histograms
}
}
impl Storage {
/// Changes the [`help` description] of the [`prometheus`] `M`etric
/// identified by its `name`. No-op if this immutable [`Storage`] doesn't
/// contain it.
///
/// Accepts only the following [`prometheus`] `M`etrics:
/// - [`prometheus::IntCounter`], [`prometheus::IntCounterVec`]
/// - [`prometheus::Gauge`], [`prometheus::GaugeVec`]
/// - [`prometheus::Histogram`], [`prometheus::HistogramVec`]
///
/// Intended to be used in [`metrics::Recorder::describe_counter()`],
/// [`metrics::Recorder::describe_gauge()`] and
/// [`metrics::Recorder::describe_histogram()`] implementations.
///
/// [`help` description]: prometheus::proto::MetricFamily::get_help
pub fn describe<M>(&self, name: &str, description: String)
where
M: metric::Bundled,
<M as metric::Bundled>::Bundle: metric::Bundle,
Self: super::Get<Collection<<M as metric::Bundled>::Bundle>>,
{
use super::Get as _;
if let Some(bundle) = self.collection().get(name) {
bundle.description.store(Arc::new(description));
};
}
/// Returns a [`prometheus`] `M`etric stored in this immutable [`Storage`]
/// and identified by the provided [`metrics::Key`].
///
/// Accepts only the following [`prometheus`] metrics:
/// - [`prometheus::IntCounter`]
/// - [`prometheus::Gauge`]
/// - [`prometheus::Histogram`]
///
/// Intended to be used in [`metrics::Recorder::register_counter()`],
/// [`metrics::Recorder::register_gauge()`] and
/// [`metrics::Recorder::register_histogram()`] implementations.
///
/// # Errors
///
/// If the identified [`prometheus`] `M`etric doesn't comply with the
/// labeling of the provided [`metrics::Key`].
pub fn get_metric<M>(
&self,
key: &metrics::Key,
) -> Option<Result<Metric<M>, prometheus::Error>>
where
M: metric::Bundled,
<M as metric::Bundled>::Bundle: metric::Bundle<Single = M>,
Self: super::Get<Collection<<M as metric::Bundled>::Bundle>>,
{
use super::Get as _;
use metric::Bundle as _;
self.collection().get(key.name()).map(|bundle| {
bundle.metric.get_single_metric(key).map(Metric::wrap)
})
}
}
// PANIC: `RwLock` usage is fully panic-safe inside, so the `impl` is
// infallible, in fact.
#[allow(clippy::fallible_impl_from)]
impl From<&super::mutable::Storage> for Storage {
/// Creates a new immutable [`Storage`] by [draining] the referred
/// [`mutable::Storage`] and leaving it empty.
///
/// [`mutable::Storage`]: super::mutable::Storage
/// [draining]: HashMap::drain
fn from(mutable: &super::mutable::Storage) -> Self {
// PANIC: `RwLock` usage is fully panic-safe here.
#![allow(clippy::unwrap_used)]
Self {
counters: mutable
.counters
.write()
.unwrap()
.drain()
.filter_map(|(name, bundle)| Some((name, bundle.transpose()?)))
.collect(),
gauges: mutable
.gauges
.write()
.unwrap()
.drain()
.filter_map(|(name, bundle)| Some((name, bundle.transpose()?)))
.collect(),
histograms: mutable
.histograms
.write()
.unwrap()
.drain()
.filter_map(|(name, bundle)| Some((name, bundle.transpose()?)))
.collect(),
}
}
}