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在度量收集方面至少有两个生态系统
- 一个是基于
prometheus
crate,专注于将度量数据发送到Prometheus(或其替代品,如VictoriaMetrics)。它提供了大量的Prometheus特定功能,并对度量数据进行严格验证,以满足Prometheus使用的格式。 - 另一个是基于
metrics
crate,更为通用,针对更广泛的范围,而不仅仅是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::IntCounterVec
metrics::Gauge
:prometheus::Gauge
+prometheus::GaugeVec
metrics::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