支持通过文件(yaml文件, 我个人非常推荐)和代码配置
支持写入 log 文件
自动重载配置文件更新 log 配置!
支持为每个模块单独配置 log
支持对 log 信息模式进行配置
添加依赖
[dependencies]
log = “0.4.8”
log4rs = “0.10.0”
配置文件 log4rs.yaml 放在项目根目录下
---
# log4rs.yaml
# 检查配置文件变动的时间间隔
refresh_rate: 30 seconds
# appender 负责将日志收集到控制台或文件, 可配置多个
appenders:
stdout:
kind: console
file:
kind: file
path: "log/log.log"
encoder:
# log 信息模式
pattern: "{d} - {m}{n}"
# 对全局 log 进行配置
root:
level: info
appenders:
- stdout
- file
use log::info;
use log4rs;
fn main() {
log4rs::init_file("log4rs.yaml", Default::default()).unwrap();
info!("INFO");
}
appenders
encoders
filers
loggers
Appenders
appender 负责将 log 发往控制台或文件. 目前一共有三种类型可选 console, file 和 rolling_file.
console 配置相对简单
appenders:
console:
kind: console
target: stdout # 或者 stderr
encoder: # console 同样可以配置 encoder
kind: pattern
file 和 rolling_file 都是将日志收集到文件中, 但对于长时间运行的程序, rolling_file 更合适, 因为它可以配置 log rotate, 避免 log 文件占用太多硬盘空间.
先说二者的共同配置项
appenders:
log_file:
kind: file # 或 rolling_file
path: <log_file_path> # log 文件路径
append: true # 追加模式, 即每次在已有文件末尾添加日志, 默认为 true
encoder:
kind: pattern
对于 rolling_file 需要额外配置 log rotate policy
log_file:
kind: rolling_file
# ...
policy:
kind: compound # 默认值, 即使用所有 policy
trigger: # 当文件超过10mb 时触发 rotate
kind: size
limit: 10mb
roller: # rotate 类型
kind: delete # 直接原有文件
# 或者用 fixed_window
kind: fixed_window
pattern: "compressed-log-{}-.log" # 注意, 需要至少包含 "{}" 用于插入索引值
base: 0 # 压缩日志索引值起点
count: 2 # 最大保存压缩文件数
fixed_window 的逻辑稍微有些复杂, 当 log 文件触发 rotate 时 log4rs 会把该文件压缩(需启用 gzip feature) 然后以 pattern 中定义文件模式并将 {} 插入索引值保存. 以上述配置为例, 文件产生顺序为 compressed-log-0-.log -> compressed-log-1-.log, 当文件再次触发 rotate 时 log4rs 会删除 compressed-log-0-.log, 然后将 compressed-log-1-.log 重命名为 compressed-log-0-.log, 最新的 log 压缩文件命名为 compressed-log-1-.log.
注意, 如果压缩文件数量达到最大时, log4rs 会逐个重命名文件, 此时如果 count 的很大, 有可能会造成性能问题.
Encoders
encoder 负责将 log 信息转换为合适的格式, 如固定格式的平文本或 json, 每个 appender 都可以指定一个 encoder.
目前有2个 encoder pattern 和 json
json encoder 负责将日志转为 json 格式, 配置项只有一个
encoder:
kind: json
输出例子如下
{
"time": "2016-03-20T14:22:20.644420340-08:00",
"message": "the log message",
"module_path": "foo::bar",
"file": "foo/bar/mod.rs",
"line": 100,
"level": "INFO",
"target": "foo::bar",
"thread": "main",
"thread_id": 123,
"mdc": {
"request_id": "123e4567-e89b-12d3-a456-426655440000"
}
}
注意 实际写入时并不会增加缩进, 而且 log4rs 有个 bug, 其输出的 json 文件最外层没有一个包含所有 log 信息的列表, 导致该文件不是有效的 json 文件.
mdc 是 mapped diagnostic context 即映射诊断环境, 用于向 log 中添加额外信息帮助 debug. 详见 MDC.
pattern encoder 则是将 log 转换为特定格式的纯文本
encoder:
kind: pattern
pattern: <log_message_pattern>
log 信息的 pattern 可以在 log4rs::encoder::pattern 找到详细定义, 这里简要说明一下.
pattern 类似于正则表达式, {} 加一个特定字符代表要插入的某个信息, 如 {m} 代表 log 信息, 常见字符如下
d, data 日期, 默认为 ISO 9601 格式, 可以通过 {d(%Y-%m-%d %H:%M:%S)} 这种方式改变日期格式
l log 级别
L, line log消息所在行数
m, message log 消息
M, Module log 消息所在模块
X, mdc 映射诊断环境
如果要在 log 消息中使用 “{}()” 字符, 需要进行转义, log4rs 可以通过重复字符转义, 如 ‘{{’ -> ‘{’, 或者通过添加 ‘’ 进行转义, 如 ‘{’ -> ‘{’.
Filters
filter 顾名思义即为过滤掉某些 log 信息, 文档好像有问题, 我读的也不是很清楚, 略…
Loggers
在之前的信息配置中都没有出现 loggers 的配置, 因为我们配置 root 即全局 logger, 在 log4rs 中除了全局 logger, 还可以创建额外的 logger, logger 以 Rust 的模块为结构, 实现了配置继承.
举个例子, 假设程序结构如下
src
├── ask.rs
└── main.rs
0 directories, 2 files
log 配置文件如下
refresh_rate: 30 seconds
appenders:
file:
kind: file
path: "log/log.log"
encoder:
kind: pattern
pattern: "{d} {l} {M} - {m}{n}"
root:
level: info
appenders:
- file
在 ask.rs 内容如下
use log::{debug, info};
pub fn ask() {
debug!("inside ask module");
info!("inside ask module");
}
假设我们在 main.rs 中使用了 ask::ask 你会发现没有 debug! 打印的信息, 这是因为 ask 模块的 logger 默认使用 root logger 的 level, 即 info.
现在新增一个 appender 和 logger, 并把这个 logger level 设置为 debug.
appenders:
ask:
kind: file
path: "log/ask.log"
encoder:
pattern: "{d} {l} {M} - {m}{n}"
loggers:
play::ask: # logger 名称必须与模块名相同, 如 app::request::db, 第一个应该为 crate 名称
level: debug
appenders:
- ask
additivity: false
重新运行程序, 会发现在 log/ask.log 中出现了 debug 级别的日志. 这里额外设置了 additivity 为 false, 因为 log4rs 默认会把子 logger 的信息也收集起来, 因此会在 log/log.log 和 log/ask.log 中都含有 ask 模块的 log 信息, 为了减少冗余, 可以将 additivity 设置为 false, 这样子模块的 log 就不会输出到父模块上.
总结
跟人感觉 log4rs 的功能挺丰富的, 除了 yaml 格式的, 它还支持 json, toml, xml 和在代码中配置, 基本上都满足了我的需求, 推荐在项目中试一试. 但我还没有对它进行性能测试.