Rust官方出品的log库,一个被低估的基础设施
Rust官方出品的log库,一个被低估的基础设施
如果你写过Rust,大概率用过log。它只有2500多Star,不算多,但翻一下Cargo.toml依赖树,十有八九能找到它。rust-lang官方维护,MIT开源,而且已经在crates.io上被下载了超过数亿次。

它不是一个日志库,是一个日志接口
这一点刚接触的人容易搞混。log本身不输出任何东西,它只定义了一套宏和一个trait:info!()、warn!()、error!()这些大家天天写的宏,加上Log这个trait。
在软件工程里这叫Facade模式。写库的人用log打日志,用库的人自己决定日志输出去哪里。终端、文件、syslog?库代码完全不用改。
举个例子:你在库代码里写了info!("processing item {}", id);,用户A配了env_logger就能在控制台看到,用户B配了log4rs就能输出到文件。你没有强绑任何实现,用户也不会因为用了你的库就被迫安装某个日志框架。
一个生态里有几百上千个库,如果每个库都用不同的日志方案,最终用户会很痛苦。log用一层接口把这个问题解耦了。库作者只需要知道怎么打日志,不用操心最终输出到哪里。

用法就两句话
库作者在Cargo.toml加一行依赖,然后在代码里直接用宏:
[dependencies]
log = "0.4"
use log::{info, warn, error};
info!("service started on port {}", port);
warn!("connection timeout, retrying...");
error!("failed to write to database: {}", e);
应用开发者在启动时初始化一个后端。社区后端覆盖很全,简单场景用env_logger三行代码搞定,复杂场景有log4rs、fern这样的框架来兜底。一个环境变量就能控制日志级别的输出粒度,用起来很顺手。
20多个后端适配器,这才是真壁垒
翻开log的README,能看到一个很长的后端列表:从env_logger到spdlog-rs,从syslog到android_log,一共列了20多个。Windows、Linux、macOS、Android、WebAssembly,几乎所有主流平台都有对应的后端实现。甚至连浏览器里的WASM场景都有console_log,嵌入式场景可以通过defmt的适配器来对接。
这个生态不是一天建成的。log从2014年开始维护,经过近十年积累,社区贡献的适配器覆盖了你能想到的大部分场景。新项目用日志,直接用log的接口,不用操心后端对接,已经有人踩过坑了。
最有意思的是,这个长列表还不是强制捆绑的。log的核心代码很轻量,编译快,依赖少。它自己不做重量级的事情,但给所有重量级的实现留了统一的入口。
结构化日志也可以
启用kv feature后,log支持在日志里附加键值对:
info!(user_id = 42, action = "login"; "user authenticated");
trace!(yak:serde; "Commencing yak shaving");
字段值支持Debug、Display和serde::Serialize三种序列化方式。做日志分析和按字段过滤时,结构化数据比纯文本灵活很多。配合elasticsearch或者loki这类日志存储,可以直接按字段检索,不用写正则去解析。
稳定压倒一切
log要求的最低Rust编译器版本是1.68.0。项目文档里明确说了:提升最低版本的门槛很高,兼容性是优先保证的事项。
对于被上千个项目依赖的基础设施来说,稳定比新功能重要。作为rust-lang官方组织下的项目,维护保障比个人项目可靠得多。实际上,log的API已经很久没有出现break change了,对于基础设施库来说,这是最高评价。
说两句
写库用log打日志,这是Rust社区目前最接近共识的做法。写应用的,先上env_logger跑起来,有需要再换后端,接口不用动。
它的功能说起来简单,但少了它,Rust的日志生态会乱很多。这种基础设施的价值就是这样:用顺了的时候你根本想不起它,直到你用了别的语言,才发现不是每个生态都有这样统一的日志抽象层。

浙公网安备 33010602011771号