在用Rust编写实践项目时经常会用到日志库。我用过env_logger和pretty_env_logger这两个日志库,这两个库大体上都能满足我之前的需求,但是在配置log将日志写入文件而不是只是控制台的时候,我遇到了相当多的麻烦。折腾了一圈,找到了log4rs这个库。虽然上手有点复杂,但我非常喜欢以下功能:支持文件(yaml文件,我个人推荐)和代码配置支持写入日志文件并自动重新加载配置文件以更新日志配置!支持为每个模块单独配置日志支持配置日志信息方式log4rs虽然功能很多,可惜项目README太简单了,很多功能需要配合docs.rs/只有log4rs和项目源码可以被发现。下面总结一下log4rs的使用方法。快速启动首先需要添加依赖[dependencies]log="0.4.8"log4rs="0.10.0"log4rs有很多特性,默认是开启的,这里你只需要指定版本即可。然后需要写一个yaml配置文件log4rs.yaml放在项目的根目录下---#log4rs.yaml#查看配置文件变化的时间间隔refresh_rate:30秒#appender负责收集日志到控制台或文件,可配置多个appender:stdout:kind:consolefile:kind:filepath:"log/log.log"encoder:#日志信息patternpattern:"{d}-{m}{n}"#配置全局日志root:level:infoappenders:-stdout-file然后在代码中使用log4rs::init_file方法读取配置文件进行初始化,然后使用info!和日志中的其他宏以在需要的地方输出日志。使用log::info;uselog4rs;fnmain(){log4rs::init_file("log4rs.yaml",Default::default()).unwrap();信息o!("INFO");}cargorun运行项目会在项目下新建一个log/log.log文件,内容如下,控制台也会输出类似的内容。2020-02-03T15:38:02.659851+08:00-INFO概念解释根据docs.rs/log4rs,log4rs主要有以下概念appendersencodersfilersloggersAppendersappender负责将日志发送到控制台或文件。目前可选的console、file和rolling_file三种。console配置appender比较简单:对于长时间运行的程序,rolling_file比较合适,因为它可以配置logrotate,防止日志文件占用过多的硬盘空间。首先说一下常用的配置项appenders:log_file:kind:file#orrolling_filepath:#日志文件路径append:true#追加方式,即每次在已有的日志末尾添加file,默认为trueencoder:kind:pattern对于rolling_file,需要配置额外的logrotatepolicylog_file:kind:rolling_file#...policy:kind:compound#默认值,即使用所有策略triggers:#当文件超过10mb触发rotatekind:sizelimit:10mbroller:#rotatetypekind:delete#directoriginalfile#orusefixed_windowkind:fixed_windowpattern:"compressed-log-{}-.log"#注意,插入索引值至少需要包含"{}"base:0#压缩日志索引值的起始点count:2#maximumnumberofsavedcompressedfilesfixed_window的逻辑有点复杂。当日志文件触发rotate时,log4rs会对文件进行压缩(需要开启gzip特性),然后在pattern中定义文件模式,在index值中插入{}保存。以上面配置为例,文件生成顺序为compressed-log-0-.log->compressed-log-1-.log,当文件再次触发rotate时,log4rs会删除compressed-log-0-。log,然后将compressed-log-1-.log重命名为compressed-log-0-.log,最新的日志压缩文件命名为compressed-log-1-.log。注意,如果压缩文件数量达到最大值,log4rs会一个一个地重命名文件。这时,如果计数很大,可能会导致性能问题。Encodersencoder负责将日志信息转换成合适的格式,比如固定格式的纯文本或者json,每个appender可以指定一个encoder。目前有2种编码器模式和jsonjson编码器负责将日志转换成json格式,配置项只有一种编码器:kind:json输出示例如下{"time":"2016-03-20T14:22:20.644420340-08:00","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的最外层都没有file包含所有日志信息的列表,导致该文件不是有效的json文件。mdc是mappeddiagnosticcontext,即映射诊断环境,用于向日志中添加额外的信息,帮助调试。参见MDC.patternencoder详解,它将日志转换成特定的格式明文编码器:kind:patternpattern:日志信息的pattern可以在log4rs::encoder::pattern中找到,这里简单介绍一下说明.Pattern类似于正则表达式,{}加上一个特定的字符代表想要插入的一些信息,比如{m}代表日志信息,常用字符如下d,数据日期,默认为ISO9601格式,你可以通过{d(%Y-%m-%d%H:%M:%S)}这个我方法更改日期格式l日志级别L,行日志消息行号m,消息日志消息M,模块日志消息模块X,mdc映射诊断环境如果要在日志消息中使用“{}()”字符,需要转义,log4rs可以通过重复字符转义,比如'{{'->'{',或者加'',比如'{'->'{'。Filtersfilter,顾名思义,就是过滤掉一些日志信息和文件好像有问题,我也没看得很清楚,有点...前面的信息配置,因为我们配置的是root,也就是globallogger,除了log4rs中的globallogger,还可以创建额外的logger。记录器基于Rust模块并实现配置继承。例如,假设程序结构如下src├──ask.rs└──main.rs0目录,2fileslog配置文件如下refresh_rate:30secondsappenders:file:kind:filepath:"log/log.log"编码器:kind:patternpattern:"{d}{l}{M}-{m}{n}"root:level:infoappenders:-ask.rs中的文件内容如下uselog::{debug,info};pubfnask(){debug!("insideaskmodule");info!("insideaskmodule");}假设我们在main.rs中使用ask::ask,你会发现没有debug!打印的信息,这是因为ask模块的logger默认使用rootlogger的级别,即info。现在添加一个appender和logger,并将记录器级别设置为Fordebug.appenders:ask:kind:filepath:"log/ask.log"encoder:pattern:"{d}{l}{M}-{m}{n}"loggers:play::ask:#logger名称必须与模块名称相同,如app::request::db,第一个应该是crate名称level:debugappenders:-askadditivity:false重新运行程序,你会发现log/ask.log日志中出现了debug级别。这里额外设置additivity为false,因为log4rs默认也会收集子logger的信息,所以ask模块的log信息会包含在log/log.log和log/ask.log中,以减少冗余另外可以设置additivity为false,这样子模块的日志就不会输出到父模块。综上所述,感觉log4rs的功能还是挺多的。除了yaml格式,还支持json、toml、代码中的xml和配置基本满足我的需求,建议在项目中尝试一下。但我还没有测试过。