马上注册,结交更多好友,享用更多功能,让你轻松玩转社区。
您需要 登录 才可以下载或查看,没有账号?立即注册
×
tracing 是 Rust 生态中用于布局化日记纪录与分布式追踪的今世化框架,由 Tokio 团队开发维护,扩展了传统日记功能,提供了关于时序性和因果关系的额外信息。
添加依赖
- cargo add tracing # 核心框架
- cargo add tracing-subscriber --features env-filter,fmt # 日志订阅与处理
- cargo add tracing-appender # 文件输出与轮转
复制代码 常用特性分析:
- env-filter:通过环境变量设置日记过滤规则
- fmt:提供格式化输出(文本 / JSON)
- json:支持 JSON 格式输出
- chrono:使用更友爱的时间格式
初始化 Subscriber
Subscriber 是 tracing 的日记处置惩罚器,负责网络和输出日记数据。你可以把它明白为日记的"输出目的地"。常见的 Subscriber 包罗将日记写入标准输出、生存到文件、发送到日记网络体系如 ELK Stack 或 Jaeger。
Subscriber 负责格式化 Span 和 Event 数据,并决定哪些日记应该被纪录(根据日记级别过滤)。
示例,默认初始化:- use tracing::Level;
- use tracing_subscriber;
- fn main() {
- // 初始化 Subscriber,输出到 stdout,
- // 从RUST_LOG 环境变量读取日志过滤级别,如未设置,默认为 ERROR 级别
- tracing_subscriber::fmt::init();
- }
复制代码 示例,纪录日记到文件,按日轮转,同时也输出到控制台。- use tracing_subscriber::{fmt, layer::SubscriberExt, util::SubscriberInitExt, EnvFilter};
- fn main() {
- let _guard = init_logging()
- // 测试记录
- tracing::info!("系统启动,开始记录日志到文件...");
- for i in 0..5 {
- tracing::debug!(id = i, "正在处理任务");
- }
- }
- fn init_logging() -> tracing_appender::non_blocking::WorkerGuard {
- // 1. 设置日志文件存放目录和文件名前缀
- let directory = "logs";
- let file_name_prefix = "app.log";
- // 2. 配置每日滚动 (Daily Rotation)
- let file_appender = tracing_appender::rolling::daily(directory, file_name_prefix);
- // 3. 构造非阻塞写入器 (Non-blocking writer)
- // guard 必须在 main 中持有,如果它被丢弃,日志缓冲区的内容可能无法写入文件
- let (non_blocking_writer, guard) = tracing_appender::non_blocking(file_appender);
- // 4. 将文件输出与控制台输出结合
- tracing_subscriber::registry()
- // 过滤器:从环境变量 RUST_LOG 读取,默认为 info
- .with(EnvFilter::try_from_default_env().unwrap_or_else(|_| EnvFilter::new("info")))
- // 终端输出层
- .with(fmt::layer().with_writer(std::io::stdout))
- // 文件输出层(这里可以设置是否需要 ANSI 颜色,存文件建议关掉)
- .with(
- fmt::layer()
- .with_ansi(false)
- .with_writer(non_blocking_writer)
- )
- .init();
- return guard;
- }
复制代码 纪录Event
Event 代表一个具体的时间点变乱。使用 event! 宏纪录变乱。- use tracing::{event, Level};
- event!(Level::INFO, "something has happened!");
复制代码 tracing提供了一组与event!雷同但已指定了Level 参数的宏,与 log 库中的宏同名:- trace!("跟踪信息:变量 x = {}", x);
- debug!("调试信息:计算结果 = {}", result);
- info!("普通信息:处理请求 {}", request_id);
- warn!("警告信息:缓存命中率较低");
- error!("错误信息:数据库连接失败");
复制代码 布局化日记(Structured Logging)
tracing 的强大之处在于你可以像写 JSON 一样纪录变量,而不是拼接字符串。这对于后期在 ELK 或 Datadog 中检索极其方便。- let user = "Alice";
- let user_id = 42;
- // 推荐做法:使用字段名
- info!(user = %user, id = ?user_id, "用户登录成功");
- // 注释:
- // % 表示调用 Display 格式化
- // ? 表示调用 Debug 格式化
复制代码 创建Span
在异步代码中,相干的变乱和日记行会相互肴杂,使得追踪逻辑流程变得困难。为此,tracing 引入了 span 的概念。与代表一个时间点的日记行差别,span 代表一个有开始和竣事的时间段。span 内的 Event将关联到此 span,使得变乱可以追溯到其产生的上下文环境。- use tracing::{info, span, Level};
- use tracing_subscriber;
- fn main() {
- // 1. 初始化默认订阅者
- tracing_subscriber::fmt()
- .with_max_level(Level::INFO)
- .init();
- // 3. 创建并进入一个 Span
- let main_span = span!(Level::INFO, "my_span");
- let _enter = main_span.enter(); // 进入 Span,在此之后的日志都会带上该 Span 的信息
- info!("这条日志是在 Span 内部产生的");
- } // _enter 离开作用域,Span 自动关闭
复制代码 #[instrument]
#[instrument] 是 tracing 提供的一个过程宏,可以主动为函数创建 Span。使用这个宏,你无需手动编写 Span 的创建和进入代码。- use tracing::{info, instrument};
- #[instrument] // 自动创建一个名为 "calculate" 的 Span,并记录 a 和 b 的值
- async fn calculate(a: u32, b: u32) -> u32 {
- info!("计算中...");
- a + b
- }
- #[tokio::main]
- async fn main() {
- tracing_subscriber::fmt::init();
- calculate(10, 20).await;
- }
复制代码 #[instrument] 常用参数:
- level:设置 span 级别(默认 INFO)
- name:自界说 span 名称
- skip/skip_all:跳过某些参数纪录
- fields:添加额外布局化字段
- ret:纪录函数返回值
免责声明:如果侵犯了您的权益,请联系站长及时删除侵权内容,谢谢合作!qidao123.com:ToB企服之家,中国第一个企服评测及软件市场,开放入驻,技术点评得现金. |