Rust应用监控:tracing库与指标收集
监控是生产系统的关键要素。对于以性能和可靠性著称的Rust应用而言,拥有轻量且高效的遥测收集工具至关重要。tracing库已成为Rust生态系统中可观测性的事实标准。它提供结构化日志记录、用于请求追踪的跨度(span),以及与指标收集系统的集成。
什么是tracing?
tracing是一个用于Rust程序仪表化的框架。与传统的日志记录不同,它基于事件(events)和跨度(spans)工作。跨度代表代码执行的时间段(例如,处理HTTP请求),而事件是跨度内的点(例如,“数据库在10毫秒内响应”)。
主要功能:
- 结构化日志记录 — 每个事件包含字段(键值对),而不仅仅是字符串。
- 跨度层级 — 可以将跨度相互嵌套,追踪完整的执行路径。
- 异步支持 — 与tokio和async/await配合良好。
- 订阅者(Subscriber) — 可以连接各种后端:stdout、OpenTelemetry、Prometheus、文件。
安装与基本使用
在Cargo.toml中添加:
[dependencies]tracing = "0.1"tracing-subscriber = "0.3"结构化日志记录的简单示例:
use tracing::{info, warn, error};use tracing_subscriber;
fn main() { // 初始化用于stdout输出的订阅者 tracing_subscriber::fmt::init();
let user_id = 42; let request_duration_ms = 150;
info!(user_id, duration = request_duration_ms, "请求已处理");
// 使用warn级别的日志记录 warn!(user_id, "响应时间超过阈值");
// 使用error级别的日志记录 error!("无法连接到数据库");}输出将包含类似JSON格式的字段,便于在监控系统中解析。
跨度与追踪
跨度允许测量操作执行时间并将事件链接成链:
use tracing::{span, info, Level};use tracing_subscriber;
fn process_request(user_id: u32) { // 创建带上下文的跨度 let span = span!(Level::INFO, "process_request", user_id); let _guard = span.enter(); // 激活跨度
info!("开始处理"); std::thread::sleep(std::time::Duration::from_millis(50)); info!("处理完成");}
fn main() { tracing_subscriber::fmt::init(); process_request(100);}输出中将显示嵌套上下文,表明事件属于process_request跨度。
使用tracing和Prometheus收集指标
对于生产监控,通常需要将指标导出到Prometheus。使用tracing-appender crate写入文件,或使用tracing-opentelemetry与OpenTelemetry集成。然而,最直接的方式是将metrics库与tracing结合使用。
与metrics-exporter-prometheus集成的示例:
[dependencies]metrics = "0.21"metrics-exporter-prometheus = "0.12"tracing = "0.1"tokio = { version = "1", features = ["full"] }收集指标的代码:
use metrics::{counter, gauge, histogram};use metrics_exporter_prometheus::PrometheusBuilder;use std::net::SocketAddr;
#[tokio::main]async fn main() { // 启动Prometheus的HTTP服务器 let addr: SocketAddr = "0.0.0.0:9090".parse().unwrap(); let builder = PrometheusBuilder::new(); builder .wit