写代码的时候,谁还没遇到过ref="/tag/46/" style="color:#EB6E00;font-weight:bold;">程序跑着跑着就“静悄悄”挂掉的情况?没有日志,排查问题就像在黑屋子里找开关。在Rust项目里,合理的日志处理能让你一眼看出程序到底干了啥、卡在哪儿。
用env_logger打点基础
Rust生态里,env_logger是个轻量又实用的日志方案。搭配标准库的log宏,几行代码就能把日志输出安排明白。比如你正在写一个文件同步工具,想知道每次扫描目录时的状态,加个日志比打印println!更专业。
use log::{info, warn};
use env_logger;
fn main() {
env_logger::init();
info!("开始扫描用户目录");
if let Err(e) = scan_directory("/home/user/docs") {
warn!("扫描失败:{}", e);
}
}
控制日志级别,按需查看
开发时想看详细信息,上线后又不想被太多日志刷屏?通过环境变量就能灵活控制。启动程序前设置RUST_LOG=info,只看重要信息;调试时换成debug,连每个HTTP请求头都给你记下来。
RUST_LOG=debug cargo run
你在本地测试API服务时,可以打开trace级别观察数据库查询细节,部署到服务器后调成warn,避免磁盘被日志塞满。
结构化日志进阶:用tracing提升可读性
当项目变大,简单的文本日志不够用了。比如你做的后台服务要分析请求延迟,传统日志得手动解析字符串。换成tracing库,可以直接记录字段化的数据,配合tracing-subscriber输出JSON格式,方便收集到ELK或Loki里做分析。
use tracing::{span, Level, event, instrument};
#[instrument]
async fn handle_request(user_id: u32) {
let span = span!(Level::INFO, "request", user_id);
let _enter = span.enter();
event!(Level::INFO, "处理开始");
// 模拟处理逻辑
event!(Level::INFO, duration_ms = 150, "处理完成");
}
这样一条日志会自带user_id和duration_ms字段,运维查问题时过滤起来特别快。
别忘了错误传播时带上上下文
Rust的?操作符虽然方便,但原样抛出错误可能丢失关键路径信息。用anyhow可以轻松附加上下文,比如你在读取配置文件时报错,加上一句“加载用户配置失败”,排查时就知道是哪一步出了问题。
use anyhow::Result;
fn load_config(path: &str) -> Result<Config> {
let data = std::fs::read_to_string(path)?;
Ok(serde_json::from_str(&data)?)
}
// 调用时附加信息
let config = load_config("config.json").context("加载主配置失败")?;
这类小改动在复杂调用链中特别有用,省去一层层翻代码的时间。