Rust中的可观测性:构建可靠系统的追踪、指标与日志

面向初学者的在线Python训练器

轻松学习Python,无需理论负担。在浏览器中直接解决带自动检查的实践任务,无需安装任何东西。

开始课程

为什么可观测性是Rust开发者的超能力



想象一下:您的Rust服务在生产环境中运行,但延迟突然飙升到天际。日志保持沉默,指标显示200 OK,而用户却在抱怨。熟悉吗?这就是经典的“黑盒”。没有Rust中的可观测性,您就是盲目的。



可观测性不仅仅是“日志+指标”。它是一种无需编写新代码就能向系统提出任何问题的能力。在Rust中,由于其强调性能和可靠性,可观测性成为生产级应用程序的必备要素。



在本文中,我们将剖析Rust中的可观测性的三大支柱:追踪、指标和日志。您将获得可直接集成到项目中的、使用tracing、OpenTelemetry和Prometheus的现成代码示例。



Rust中的追踪:请求在系统中的旅程



追踪是一个请求的生命故事。您获得的不是零散的日志,而是一条关联的路径:从API入口到数据库查询,再到客户端响应。对于Rust,最好的工具是来自Tokio社区的tracing crate。



在Rust应用程序中配置tracing



从基本配置开始。在Cargo.toml中添加:



[dependencies]tracing = "0.1"tracing-subscriber = "0.3"tracing-opentelemetry = "0.23"opentelemetry = "0.22"opentelemetry-otlp = "0.15"


现在在main.rs中初始化追踪器:



use tracing_subscriber::{fmt, prelude::*, EnvFilter};use opentelemetry::{global, KeyValue};use opentelemetry_otlp::WithExportConfig;

fn init_tracer() -> Result<(), Box<dyn std::error::Error>> { global::set_text_map_propagator(opentelemetry_jaeger::Propagator::new()); let tracer = opentelemetry_otlp::new_pipeline() .tracing() .with_exporter( opentelemetry_otlp::new_exporter() .tonic() .with_endpoint("http://localhost:4317") ) .with_trace_config( opentelemetry::sdk::trace::config() .with_resource(opentelemetry::sdk::Resource::new(vec![ KeyValue::new("service.name", "rust-observability-demo") ])) ) .install_simple()?;

// 将tracing与OpenTelemetry关联 let telemetry = tracing_opentelemetry::layer().with_tracer(tracer); let subscriber = fmt::Subscriber::builder() .with_env_filter(EnvFilter::from_default_env()) .finish() .with(telemetry);

tracing::subscriber::set_global_default(subscriber)?; Ok(())}


创建span和trace



现在将追踪添加到业务逻辑中:



use tracing::{span, Span, Level, instrument};

#[instrument]async fn handle_request(user_id: u64) -> Result<String, Box<dyn std::error::Error>> { let user_data = get_user_data(user_id).await?; let processed = process_data(&user_data).await?; Ok(processed)}

#[instrument(skip(db_pool))]async fn get_user_data(user_id: u64) -> Result<User, Box<dyn std::error::Error>> { // 模拟数据库查询 let span = Span::current(); span.record("db.query", "SELECT * FROM users WHERE id = $1"); tokio::time::sleep(std::time::Duration::from_millis(50)).await; Ok(User { id: user_id, name: "Alice".into() })}

#[instrument]async fn process_data(user: &User) -> Result<String, Box<dyn std::error::Error>> { let result = format!("Processed user: {}", user.name); tracing::info!(user_id = user.id, "Data processed successfully"); Ok(result)}


#[instrument]属性会自动为函数创建一个span,并记录所有参数。在Jaege

博客

书籍推荐