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::counter!(
"count", "whose" => "mine", "kind" => "owned",
).increment(1);
metrics::counter!(
"count", "whose" => "mine", "kind" => "ref",
).increment(1);
metrics::counter!(
"count", "kind" => "owned", "whose" => "dummy",
).increment(1);
// 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::gauge!("new").increment(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::Strategy
s, 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::counter!("count", "kind" => "owned").increment(1);
recoder.freeze();
// This panics, as such labeling is not allowed by `prometheus` crate.
metrics::counter!("count", "whose" => "mine").increment(1);
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::counter!("value").increment(1);
sourcepub fn try_register_metric<M>(&self, metric: M) -> Result<()>
pub fn try_register_metric<M>(&self, metric: M) -> Result<()>
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::IntCounterVec
prometheus::Gauge
,prometheus::GaugeVec
prometheus::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::counter!(
"value", "whose" => "mine", "kind" => "owned",
).increment(1);
metrics::counter!(
"value", "whose" => "mine", "kind" => "ref",
).increment(1);
metrics::counter!(
"value", "kind" => "owned", "whose" => "foreign",
).increment(1);
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)
pub fn register_metric<M>(&self, metric: M)
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::IntCounterVec
prometheus::Gauge
,prometheus::GaugeVec
prometheus::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::gauge!(
"value", "whose" => "mine", "kind" => "owned",
).increment(2.0);
metrics::gauge!(
"value","whose" => "mine", "kind" => "ref",
).decrement(2.0);
metrics::gauge!(
"value", "kind" => "owned", "whose" => "foreign",
).increment(2.0);
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::counter!("count").increment(1);
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::counter!("count").increment(1);
// This is no-op.
metrics::counter!("new").increment(1);
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(),
);