9个版本 (重大更改)
| 0.7.0 | 2024年5月30日 |
|---|---|
| 0.6.0 | 2023年12月25日 |
| 0.5.0 | 2023年9月6日 |
| 0.4.1 | 2023年4月25日 |
| 0.1.0 | 2020年4月11日 |
#44 在 调试
每月下载 6,940 次
在 3 个crate中使用(通过metrics-dashboard)
140KB
1.5K SLoC

metrics + prometheus = ❤️
prometheus后端用于metrics crate.
动机
Rust在度量收集方面至少有两个生态系统
- 一个是基于
prometheuscrate,专注于将度量数据发送到Prometheus(或其替代品,如VictoriaMetrics)。它提供了大量的Prometheus特定功能,并对度量数据进行严格验证,以满足Prometheus使用的格式。 - 另一个是基于
metricscrate,更为通用,针对更广泛的范围,而不仅仅是Prometheus。它提供了一个方便且易于使用的封装,允许以与通过log/tracing生态系统(甚至支持tracing::Span作为度量标签)处理日志和跟踪非常相似的方式与度量数据一起工作。
结果,一些crate使用prometheus crate来提供它们的指标,而另一些crate则使用metrics crate。此外,prometheus和metrics crate的设计差异很大,使得它们的组合成为一个非平凡的挑战。这个crate旨在弥合这一差距,允许在一个项目中结合prometheus和metrics生态系统。
替代方案
如果你没有义务直接处理prometheus crate或通过使用它的第三方crate,考虑使用metrics-exporter-prometheus crate,它为metrics界面提供了简单的Prometheus后端,而不引入整个prometheus crate的机制。
概述
这个crate提供了一个metrics::Recorder实现,允许通过metrics界面使用prometheus::Registry。
它有3种口味,允许根据使用情况选择最小的性能开销
- 常规
Recorder,允许在任何时候通过metrics界面创建新的指标,没有限制。提供与访问已注册指标相同的开销,如metrics::Registry所做的:read-lock在分片HashMap上加上Arc克隆。 FrozenRecorder,无法通过metrics界面创建新的指标(在这种情况下只是无操作)。提供访问已注册指标的最小开销:只是一个常规的HashMap查找加上Arc克隆。FreezableRecorder,最初的行为与Recorder相同,但能够.freeze(),最终变为FrozenRecorder。访问已注册指标的额外开销与Recorder和FrozenRecorder提供的一样,另外还有使用AtomicBool加载来检查是否已经.freeze()。
不支持任何prometheus指标,因为metrics包仅包含其中的一些。这是metrics包的指标如何映射到prometheus指标的方式。
metrics::Counter:prometheus::IntCounter+prometheus::IntCounterVecmetrics::Gauge:prometheus::Gauge+prometheus::GaugeVecmetrics::Histogram:prometheus::Histogram+prometheus::HistogramVec
当通过metrics外观指定任何标签时,使用prometheus::MetricVec类型。
为了满足metrics::Recorder的要求,在注册后可以随时更改指标描述(prometheus包不暗示并允许这样做),使用Describable包装器,允许arc-swap描述。
// 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>(())
限制
由于 prometheus 包对指标格式进行了非常严格的验证,并非通过 metrics 门面表示的所有内容都可以放入 prometheus::Registry 中,否则会导致发出 prometheus::Error。
-
指标名称不能使用点进行命名空间(并且应遵循 Prometheus 格式)。
metrics_prometheus::install(); // panics: 'queries.count' is not a valid metric name metrics::counter!("queries.count").increment(1); -
相同的指标应始终使用相同的标签集。
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); -
相同的名称不能用于不同类型的指标。
metrics_prometheus::install(); metrics::counter!("count").increment(1); // panics: Duplicate metrics collector registration attempted metrics::gauge!("count").increment(1.0); -
直接在
prometheus::Registry中注册的任何指标,而不使用metrics或此包接口,将无法通过metrics门面使用,并会导致发出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,因为 Prometheus 没有这个概念。通过metrics宏指定它们将不会产生任何效果。
prometheus::Error 处理
由于 metrics::Recorder 在其 API 中没有暴露任何错误,发出的 prometheus::Error 可以被转换为 panic,或者简单地静默忽略,返回一个无操作的指标(例如,参见 metrics::Counter::noop())。
这可以通过在构建 Recorder 时提供一个 failure::Strategy 来调整。
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);
默认的 failure::Strategy 是 PanicInDebugNoOpInRelease。有关其他可用的 failure::Strategy,请参阅 failure::strategy 模块,或通过实现 failure::Strategy 特性来提供自己的一个。
许可协议
版权所有 © 2022-2024 Instrumentisto 团队,https://github.com/instrumentisto
根据您的选择,该项目受Apache License, Version 2.0或MIT许可的许可。
除非您明确表示,否则您根据Apache-2.0许可提交给本项目以包含在内的任何贡献,都将按照上述方式双重许可,不附加任何额外条款或条件。
依赖项
约3–9MB
约74K SLoC