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::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::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

source

pub fn builder() -> Builder

Starts building a new FreezableRecorder on top of the prometheus::default_registry().

source§

impl<S> Recorder<S>

source

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);
source

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:

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(),
);
source

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:

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(),
);
source

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(),
);

Trait Implementations§

source§

impl<FailureStrategy: Clone> Clone for Recorder<FailureStrategy>

source§

fn clone(&self) -> Recorder<FailureStrategy>

Returns a copy of the value. Read more
1.0.0 · source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
source§

impl<FailureStrategy: Debug> Debug for Recorder<FailureStrategy>

source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
source§

impl<S> Recorder for Recorder<S>

source§

fn describe_counter( &self, name: KeyName, unit: Option<Unit>, description: SharedString )

Describes a counter. Read more
source§

fn describe_gauge( &self, name: KeyName, unit: Option<Unit>, description: SharedString )

Describes a gauge. Read more
source§

fn describe_histogram( &self, name: KeyName, unit: Option<Unit>, description: SharedString )

Describes a histogram. Read more
source§

fn register_counter(&self, key: &Key, meta: &Metadata<'_>) -> Counter

Registers a counter.
source§

fn register_gauge(&self, key: &Key, meta: &Metadata<'_>) -> Gauge

Registers a gauge.
source§

fn register_histogram(&self, key: &Key, meta: &Metadata<'_>) -> Histogram

Registers a histogram.

Auto Trait Implementations§

§

impl<FailureStrategy = PanicInDebugNoOpInRelease> !RefUnwindSafe for Recorder<FailureStrategy>

§

impl<FailureStrategy> Send for Recorder<FailureStrategy>
where FailureStrategy: Send + Sync,

§

impl<FailureStrategy> Sync for Recorder<FailureStrategy>
where FailureStrategy: Send + Sync,

§

impl<FailureStrategy> Unpin for Recorder<FailureStrategy>
where FailureStrategy: Unpin,

§

impl<FailureStrategy = PanicInDebugNoOpInRelease> !UnwindSafe for Recorder<FailureStrategy>

Blanket Implementations§

source§

impl<T> Any for T
where T: 'static + ?Sized,

source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
source§

impl<T> Borrow<T> for T
where T: ?Sized,

source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
source§

impl<T> From<T> for T

source§

fn from(t: T) -> T

Returns the argument unchanged.

source§

impl<T, U> Into<U> for T
where U: From<T>,

source§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

§

impl<T> Pointable for T

§

const ALIGN: usize = _

The alignment of pointer.
§

type Init = T

The type for initializers.
§

unsafe fn init(init: <T as Pointable>::Init) -> usize

Initializes a with the given initializer. Read more
§

unsafe fn deref<'a>(ptr: usize) -> &'a T

Dereferences the given pointer. Read more
§

unsafe fn deref_mut<'a>(ptr: usize) -> &'a mut T

Mutably dereferences the given pointer. Read more
§

unsafe fn drop(ptr: usize)

Drops the object pointed to by the given pointer. Read more
source§

impl<T> ToOwned for T
where T: Clone,

§

type Owned = T

The resulting type after obtaining ownership.
source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
source§

impl<T, U> TryFrom<U> for T
where U: Into<T>,

§

type Error = Infallible

The type returned in the event of a conversion error.
source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
source§

impl<T, U> TryInto<U> for T
where U: TryFrom<T>,

§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.