Struct metrics_prometheus::FreezableRecorder
source · pub struct FreezableRecorder<FailureStrategy = PanicInDebugNoOpInRelease> { /* private fields */ }Expand description
metrics::Recorder being essential a usual Recorder, which is able to
become a Frozen one at some point after creation.
This FreezableRecorder is capable of registering metrics in its
prometheus::Registry on the fly, but only before being .freeze()d.
By default, the prometheus::default_registry() is used.
Example
let recorder = metrics_prometheus::install_freezable();
// Either use `metrics` crate interfaces.
metrics::increment_counter!("count", "whose" => "mine", "kind" => "owned");
metrics::increment_counter!("count", "whose" => "mine", "kind" => "ref");
metrics::increment_counter!("count", "kind" => "owned", "whose" => "dummy");
// Or construct and provide `prometheus` metrics directly.
recorder.register_metric(prometheus::Gauge::new("value", "help")?);
let report = prometheus::TextEncoder::new()
.encode_to_string(&prometheus::default_registry().gather())?;
assert_eq!(
report.trim(),
r#"
# HELP count count
# TYPE count counter
count{kind="owned",whose="dummy"} 1
count{kind="owned",whose="mine"} 1
count{kind="ref",whose="mine"} 1
# HELP value help
# TYPE value gauge
value 0
"#
.trim(),
);
recorder.freeze();
// However, you cannot register new metrics after freezing.
// This is just no-op.
metrics::increment_gauge!("new", 2.0);
let report = prometheus::TextEncoder::new()
.encode_to_string(&prometheus::default_registry().gather())?;
assert_eq!(
report.trim(),
r#"
# HELP count count
# TYPE count counter
count{kind="owned",whose="dummy"} 1
count{kind="owned",whose="mine"} 1
count{kind="ref",whose="mine"} 1
# HELP value help
# TYPE value gauge
value 0
"#
.trim(),
);
// Luckily, metrics still can be described anytime after being registered,
// even after freezing.
metrics::describe_counter!("count", "Example of counter.");
metrics::describe_gauge!("value", "Example of gauge.");
let report = prometheus::TextEncoder::new()
.encode_to_string(&recorder.registry().gather())?;
assert_eq!(
report.trim(),
r#"
# HELP count Example of counter.
# TYPE count counter
count{kind="owned",whose="dummy"} 1
count{kind="owned",whose="mine"} 1
count{kind="ref",whose="mine"} 1
# HELP value Example of gauge.
# TYPE value gauge
value 0
"#
.trim(),
);Performance
This FreezableRecorder provides the same overhead of accessing an
already registered metric as a usual Recorder or a FrozenRecorder,
depending on whether it has been .freeze()d, plus an AtomicBool
loading to check whether it has been actually .freeze()d.
So, before .freeze() it’s: AtomicBool loading plus read-lock on
a sharded HashMap plus Arc cloning.
And after .freeze(): AtomicBool loading plus regular HashMap
lookup plus Arc cloning.
Errors
prometheus::Registry has far more stricter semantics than the ones
implied by a metrics::Recorder. That’s why incorrect usage of
prometheus metrics via metrics crate will inevitably lead to a
prometheus::Registry returning a prometheus::Error instead of
registering the metric. The returned prometheus::Error can be either
turned into a panic, or just silently ignored, making this
FreezableRecorder to return a no-op metric instead (see
metrics::Counter::noop() for example).
The desired behavior can be specified with a failure::Strategy
implementation of this FreezableRecorder. By default a
PanicInDebugNoOpInRelease failure::Strategy is used. See
failure::strategy module for other available failure::Strategys, or
provide your own one by implementing the failure::Strategy trait.
use metrics_prometheus::failure::strategy;
let recoder = metrics_prometheus::Recorder::builder()
.with_failure_strategy(strategy::Panic)
.build_freezable_and_install();
metrics::increment_counter!("count", "kind" => "owned");
recoder.freeze();
// This panics, as such labeling is not allowed by `prometheus` crate.
metrics::increment_counter!("count", "whose" => "mine");Implementations§
source§impl Recorder
impl Recorder
sourcepub fn builder() -> Builder
pub fn builder() -> Builder
Starts building a new FreezableRecorder on top of the
prometheus::default_registry().
source§impl<S> Recorder<S>
impl<S> Recorder<S>
sourcepub const fn registry(&self) -> &Registry
pub const fn registry(&self) -> &Registry
Returns the underlying prometheus::Registry backing this
FreezableRecorder.
Warning
Any prometheus metrics, registered directly in the returned
prometheus::Registry, cannot be used via this metrics::Recorder
(and, so, metrics crate interfaces), and trying to use them will
inevitably cause a prometheus::Error being emitted.
use metrics_prometheus::failure::strategy;
let recorder = metrics_prometheus::Recorder::builder()
.with_failure_strategy(strategy::Panic)
.build_freezable_and_install();
let counter = prometheus::IntCounter::new("value", "help")?;
recorder.registry().register(Box::new(counter))?;
// panics: Duplicate metrics collector registration attempted
metrics::increment_counter!("value");sourcepub fn try_register_metric<M>(&self, metric: M) -> Result<()>where
M: Bundled + Collector,
<M as Bundled>::Bundle: Collector + Clone + 'static,
Mutable: Get<Collection<<M as Bundled>::Bundle>>,
pub fn try_register_metric<M>(&self, metric: M) -> Result<()>where M: Bundled + Collector, <M as Bundled>::Bundle: Collector + Clone + 'static, Mutable: Get<Collection<<M as Bundled>::Bundle>>,
Tries to register the provided prometheus metric in the underlying
prometheus::Registry in the way making it usable via this
FreezableRecorder (and, so, metrics crate interfaces).
No-op, if this FreezableRecorder has been .freeze()d.
Accepts only the following prometheus metrics:
prometheus::IntCounter,prometheus::IntCounterVecprometheus::Gauge,prometheus::GaugeVecprometheus::Histogram,prometheus::HistogramVec
Errors
If the underlying prometheus::Registry fails to register the
provided metric.
Example
let recorder = metrics_prometheus::install_freezable();
let counter = prometheus::IntCounterVec::new(
prometheus::opts!("value", "help"),
&["whose", "kind"],
)?;
recorder.try_register_metric(counter.clone())?;
counter.with_label_values(&["mine", "owned"]).inc();
counter.with_label_values(&["foreign", "ref"]).inc_by(2);
counter.with_label_values(&["foreign", "owned"]).inc_by(3);
let report = prometheus::TextEncoder::new()
.encode_to_string(&prometheus::default_registry().gather())?;
assert_eq!(
report.trim(),
r#"
# HELP value help
# TYPE value counter
value{kind="owned",whose="foreign"} 3
value{kind="owned",whose="mine"} 1
value{kind="ref",whose="foreign"} 2
"#
.trim(),
);
recorder.freeze();
// No-op, as the `Recorder` has been frozen.
recorder.try_register_metric(prometheus::Gauge::new("new", "help")?)?;
metrics::increment_counter!(
"value", "whose" => "mine", "kind" => "owned",
);
metrics::increment_counter!(
"value", "whose" => "mine", "kind" => "ref",
);
metrics::increment_counter!(
"value", "kind" => "owned", "whose" => "foreign",
);
let report = prometheus::TextEncoder::new()
.encode_to_string(&recorder.registry().gather())?;
assert_eq!(
report.trim(),
r#"
# HELP value help
# TYPE value counter
value{kind="owned",whose="foreign"} 4
value{kind="owned",whose="mine"} 2
value{kind="ref",whose="foreign"} 2
value{kind="ref",whose="mine"} 1
"#
.trim(),
);sourcepub fn register_metric<M>(&self, metric: M)where
M: Bundled + Collector,
<M as Bundled>::Bundle: Collector + Clone + 'static,
Mutable: Get<Collection<<M as Bundled>::Bundle>>,
pub fn register_metric<M>(&self, metric: M)where M: Bundled + Collector, <M as Bundled>::Bundle: Collector + Clone + 'static, Mutable: Get<Collection<<M as Bundled>::Bundle>>,
Registers the provided prometheus metric in the underlying
prometheus::Registry in the way making it usable via this
FreezableRecorder (and, so, metrics crate interfaces).
No-op, if this FreezableRecorder has been .freeze()d.
Accepts only the following prometheus metrics:
prometheus::IntCounter,prometheus::IntCounterVecprometheus::Gauge,prometheus::GaugeVecprometheus::Histogram,prometheus::HistogramVec
Panics
If the underlying prometheus::Registry fails to register the
provided metric.
Example
let recorder = metrics_prometheus::install_freezable();
let gauge = prometheus::GaugeVec::new(
prometheus::opts!("value", "help"),
&["whose", "kind"],
)?;
recorder.register_metric(gauge.clone());
gauge.with_label_values(&["mine", "owned"]).inc();
gauge.with_label_values(&["foreign", "ref"]).set(2.0);
gauge.with_label_values(&["foreign", "owned"]).set(3.0);
let report = prometheus::TextEncoder::new()
.encode_to_string(&prometheus::default_registry().gather())?;
assert_eq!(
report.trim(),
r#"
# HELP value help
# TYPE value gauge
value{kind="owned",whose="foreign"} 3
value{kind="owned",whose="mine"} 1
value{kind="ref",whose="foreign"} 2
"#
.trim(),
);
recorder.freeze();
// No-op, as the `Recorder` has been frozen.
recorder.register_metric(prometheus::Gauge::new("new", "help")?);
metrics::increment_gauge!(
"value", 2.0, "whose" => "mine", "kind" => "owned",
);
metrics::decrement_gauge!(
"value", 2.0, "whose" => "mine", "kind" => "ref",
);
metrics::increment_gauge!(
"value", 2.0, "kind" => "owned", "whose" => "foreign",
);
let report = prometheus::TextEncoder::new()
.encode_to_string(&recorder.registry().gather())?;
assert_eq!(
report.trim(),
r#"
# HELP value help
# TYPE value gauge
value{kind="owned",whose="foreign"} 5
value{kind="owned",whose="mine"} 3
value{kind="ref",whose="foreign"} 2
value{kind="ref",whose="mine"} -2
"#
.trim(),
);sourcepub fn freeze(&self)where
S: Clone,
pub fn freeze(&self)where S: Clone,
Freezes this FreezableRecorder, making it unable to register new
prometheus metrics in the benefit of providing faster access to the
already registered ones.
No-op, if this FreezableRecorder has been .freeze()d already.
Performance
This FreezableRecorder provides the same overhead of accessing an
already registered metric as a usual Recorder or a
FrozenRecorder, depending on whether it has been .freeze()d,
plus an AtomicBool loading to check whether it has been actually
.freeze()d.
So, before .freeze() it’s: AtomicBool loading plus [read-lock]
on a sharded HashMap plus Arc cloning.
And after .freeze(): AtomicBool loading plus regular HashMap
lookup plus Arc cloning.
Example
let recorder = metrics_prometheus::install_freezable();
metrics::increment_counter!("count");
let report = prometheus::TextEncoder::new()
.encode_to_string(&recorder.registry().gather())?;
assert_eq!(
report.trim(),
r#"
# HELP count count
# TYPE count counter
count 1
"#
.trim(),
);
recorder.freeze();
metrics::increment_counter!("count");
// This is no-op.
metrics::increment_counter!("new");
let report = prometheus::TextEncoder::new()
.encode_to_string(&recorder.registry().gather())?;
assert_eq!(
report.trim(),
r#"
# HELP count count
# TYPE count counter
count 2
"#
.trim(),
);