Crates.io | metrics-prometheus |
lib.rs | metrics-prometheus |
version | 0.8.0 |
source | src |
created_at | 2020-04-11 14:34:16.244444 |
updated_at | 2024-10-21 10:57:45.709093 |
description | `prometheus` backend for `metrics` crate. |
homepage | https://github.com/instrumentisto/metrics-prometheus-rs |
repository | https://github.com/instrumentisto/metrics-prometheus-rs |
max_upload_size | |
id | 228674 |
size | 153,729 |
metrics
+ prometheus
= ❤️prometheus
backend for metrics
crate.
Rust has at least two ecosystems regarding metrics collection:
prometheus
crate, focusing on delivering metrics to Prometheus (or its drop-in replacements like VictoriaMetrics). It provides a lot of Prometheus-specific capabilities and validates metrics strictly to meet the format used by Prometheus.metrics
crate, being more generic and targeting a wider scope, rather than Prometheus only. It provides a convenient and ergonomic facade, allowing to work with metrics in the very similar way we do work with logs and traces via log
/tracing
ecosystems (and even supports tracing::Span
s for metrics labels).As the result, some crates use prometheus
crate for providing their metrics, and another crates do use metrics
crate for that. Furthermore, prometheus
and metrics
crates are designed quite differently, making their composition a non-trivial task. This crate aims to mitigate this gap, allowing to combine both prometheus
and metrics
ecosystems in a single project.
If you're not obligated to deal with prometheus
crate directly or via third-party crates which do use it, consider the metrics-exporter-prometheus
crate, which provides a simple Prometheus backend for metrics
facade, without bringing in the whole prometheus
crate's machinery.
This crate provides a metrics::Recorder
implementation, allowing to work with a prometheus::Registry
via metrics
facade.
It comes in 3 flavours, allowing to choose the smallest performance overhead depending on a use case:
Recorder
, allowing to create new metrics via metrics
facade anytime, without limits. Provides the same overhead of accessing an already registered metric as a metrics::Registry
does: read
-lock on a sharded HashMap
plus Arc
cloning.FrozenRecorder
, unable to create new metrics via metrics
facade at all (just no-op in such case). Provides the smallest overhead of accessing an already registered metric: just a regular HashMap
lookup plus Arc
cloning.FreezableRecorder
, acting the same way as the Recorder
at first, but being able to .freeze()
and so, becoming a FrozenRecorder
at the end. The overhead of accessing an already registered metric is the same as Recorder
and FrozenRecorder
provide, plus AtomicBool
loading to check whether it has been .freeze()
d.Not any prometheus
metric is supported, because metrics
crate implies only few of them. This is how the metrics
crate's metrics are mapped onto prometheus
ones:
metrics::Counter
: prometheus::IntCounter
+ prometheus::IntCounterVec
metrics::Gauge
: prometheus::Gauge
+ prometheus::GaugeVec
metrics::Histogram
: prometheus::Histogram
+ prometheus::HistogramVec
prometheus::MetricVec
types are used whenever any labels are specified via metrics
facade.
To satisfy the metrics::Recorder
's requirement of allowing changing metrics description anytime after its registration (prometheus
crate doesn't imply and allow that), the Describable
wrapper is used, allowing to arc-swap
the description.
// By default `prometheus::default_registry()` is used.
let recorder = metrics_prometheus::install();
// 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(),
);
// Metrics can be described anytime after being registered in
// `prometheus::Registry`.
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(),
);
// Description can be changed multiple times and anytime.
metrics::describe_counter!("count", "Another description.");
// Even before a metric is registered in `prometheus::Registry`.
metrics::describe_counter!("another", "Yet another counter.");
metrics::counter!("another").increment(1);
let report = prometheus::TextEncoder::new()
.encode_to_string(&recorder.registry().gather())?;
assert_eq!(
report.trim(),
r#"
## HELP another Yet another counter.
## TYPE another counter
another 1
## HELP count Another description.
## 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(),
);
# Ok::<_, prometheus::Error>(())
Since prometheus
crate validates the metrics format very strictly, not everything, expressed via metrics
facade, may be put into a prometheus::Registry
, ending up with a prometheus::Error
being emitted.
Metric names cannot be namespaced with dots (and should follow Prometheus format).
metrics_prometheus::install();
// panics: 'queries.count' is not a valid metric name
metrics::counter!("queries.count").increment(1);
The same metric should use always the same set of labels:
metrics_prometheus::install();
metrics::counter!("count").increment(1);
// panics: Inconsistent label cardinality, expect 0 label values, but got 1
metrics::counter!("count", "whose" => "mine").increment(1);
metrics_prometheus::install();
metrics::counter!("count", "kind" => "owned").increment(1);
// panics: label name kind missing in label map
metrics::counter!("count", "whose" => "mine").increment(1);
metrics_prometheus::install();
metrics::counter!("count", "kind" => "owned").increment(1);
// panics: Inconsistent label cardinality, expect 1 label values, but got 2
metrics::counter!("count", "kind" => "ref", "whose" => "mine").increment(1);
The same name cannot be used for different types of metrics:
metrics_prometheus::install();
metrics::counter!("count").increment(1);
// panics: Duplicate metrics collector registration attempted
metrics::gauge!("count").increment(1.0);
Any metric registered in a prometheus::Registry
directly, without using metrics
or this crate interfaces, is not usable via metrics
facade and will cause a prometheus::Error
.
metrics_prometheus::install();
prometheus::default_registry()
.register(Box::new(prometheus::Gauge::new("value", "help")?))?;
// panics: Duplicate metrics collector registration attempted
metrics::gauge!("value").increment(4.5);
# Ok::<_, prometheus::Error>(())
metrics::Unit
s are not supported, as Prometheus has no notion of ones. Specifying them via metrics
macros will be no-op.
prometheus::Error
handlingSince metrics::Recorder
doesn't expose any errors in its API, the emitted prometheus::Error
s can be either turned into a panic, or just silently ignored, returning a no-op metric instead (see metrics::Counter::noop()
for example).
This can be tuned by providing a failure::Strategy
when building a Recorder
.
use metrics_prometheus::failure::strategy;
metrics_prometheus::Recorder::builder()
.with_failure_strategy(strategy::NoOp)
.build_and_install();
// `prometheus::Error` is ignored inside.
metrics::counter!("invalid.name").increment(1);
let stats = prometheus::default_registry().gather();
assert_eq!(stats.len(), 0);
The default failure::Strategy
is PanicInDebugNoOpInRelease
. See failure::strategy
module for other available failure::Strategy
s,
or provide your own one by implementing the failure::Strategy
trait.
Copyright © 2022-2024 Instrumentisto Team, https://github.com/instrumentisto
Licensed under either of Apache License, Version 2.0 or MIT license at your option.
Unless you explicitly state otherwise, any contribution intentionally submitted for inclusion in this crate by you, as defined in the Apache-2.0 license, shall be dual licensed as above, without any additional terms or conditions.