Rust 日志系统实践总结

栏目: 编程语言 · Rust · 发布时间: 5年前

内容简介:文档列表见:(上次更新:2018-11-22)基于log、env_logger、fern等的使用总结,详细配置建议参考官方说明。给Cargo.toml文件加上如下配置,log基本为Rust项目日志需求的标配库,env_logger提供了具体实现,类似策略模式:log定义操作,env_logger实现具体行为,很方便切换另一个实现了log所定义接口的库,比如

文档列表见: Rust 移动端跨平台复杂图形渲染项目开发系列总结(目录)

(上次更新:2018-11-22)基于log、env_logger、fern等的使用总结,详细配置建议参考官方说明。

给工程添加第三方日志库依赖

给Cargo.toml文件加上如下配置,log基本为Rust项目日志需求的标配库,env_logger提供了具体实现,类似策略模式:log定义操作,env_logger实现具体行为,很方便切换另一个实现了log所定义接口的库,比如 daboross/fern

[dependencies]
log = "0.4.0"
env_logger = "0.6.0"
复制代码

env_log配置

下面描述我们项目对env_log所作的配置。

配置输出时间为本地时间

env_logger默认用0时区,而北京是东8区,每次日志输出都少8小时,时间没对上不方便分析日志。0时区举个例子:

[2018-11-18T02:00:08Z INFO  webgpu-native::registry] env_logger initialized.
复制代码

下面给出env_logger输出本地时间的示例代码,参考了 DCjanus/nabu ,他用flexi_logger,略调整即可用于env_logger。 加上更多自定义信息的关键是修改 writeln! 宏。

#[macro_use]
extern crate log;
extern crate chrono;
extern crate env_logger;

fn init_log() {
    use chrono::Local;
    use std::io::Write;

    let env = env_logger::Env::default().filter_or(env_logger::DEFAULT_FILTER_ENV, "trace");
    env_logger::Builder::from_env(env)
        .format(|buf, record| {
            writeln!(
                buf,
                "{} {} [{}] {}",
                Local::now().format("%Y-%m-%d %H:%M:%S"),
                record.level(),
                record.module_path().unwrap_or("<unnamed>"),
                &record.args()
            )
        })
        .init();

    info!("env_logger initialized.");
}
复制代码

以上代码需在fn main()开始或lazy_static开始,否则刚开始部分日志不受新配置影响。放在lazy_static的日志配置需要 手动 激活,比如

// 定义
lazy_static! {
    pub(crate) static ref HUB: Hub = {
        init_log();
        Hub::default()
    };
}
// 在某个入口函数中先访问HUB,“强迫”它执行lazy_static代码块
fn entry_point() {
    &*HUB; // “强迫”执行lazy_static代码块
    
    HUB.some_method(); // 里面的info!()等可正常输出到文件或控制台
}
复制代码

以东8区为例,执行显示:

// 修改前
[2018-11-18T02:00:08Z INFO  webgpu-native::registry] env_logger initialized.
// 修改后
2018-11-18 09:27:43 INFO [webgpu-native::registry] env_logger initialized.
复制代码

日志添加行号

writeln!(
    buf,
    "{} {} [{}:{}] {}",
    Local::now().format("%Y-%m-%d %H:%M:%S"),
    record.level(),
    record.module_path().unwrap_or("<unnamed>"),
    record.line().unwrap_or(0),
    &record.args()
)
复制代码

执行显示:

2018-11-18 10:38:41 INFO [webgpu-native::registry:87] env_logger initialized.
复制代码

日志添加文件名

writeln!(
    buf,
    "{} {} [{}:{}:{}] {}",
    Local::now().format("%Y-%m-%d %H:%M:%S"),
    record.level(),
    record.module_path().unwrap_or("<unnamed>"),
    record.line().unwrap_or(0),
    &record.args()
)
复制代码

执行显示:

2018-11-18 10:38:48 INFO [webgpu-native::registry:webgpu-native/src/registry.rs:87] env_logger initialized.
复制代码

过滤日志级别

env_logger::Env::default().filter_or(env_logger::DEFAULT_FILTER_ENV, "trace");
复制代码

filter_or() 可配置如下内置值过滤不同级别的日志,其实我们也可自行添加filter tag:

  • "trace"
  • "info"
  • "debug"
  • "warn"
  • "error"

也可以在程序执行时传递命令行参数进行过滤:

$ RUST_LOG=info ./main
[2018-11-03T06:09:06Z INFO  default] starting up
复制代码

动态过滤信息

在复杂项目中往往存在多个模块,作为其中一个模块的开发者,为了定位自己负责模块的问题,过滤掉其他模块的日志是很常见的需求,由于整体项目通常使用同一个日志库,逐行注释其他模块的日志输出显然是不可理的行为。另外,虽然控制台可以做过滤处理,多条件的过滤规则编写起来也有难度,而且可能日志查看系统不支持这种操作。其实,我们可以给前面一直在修改的 format() 加上过滤逻辑,比如:

format(|buf, record| {
    // special format for debug messages coming from our own crate.
    if record.level() > log::LevelFilter::Info && record.target() == "my_module" {
        write!(...)
    } else if /* some condition */ {
        write!(...)
    } else if /* some condition 2*/ {
        write!(...)
    } else {
        write!(...)
    }
}
复制代码

过滤逻辑的实现可参考 fern/cmd-program.rs

fern,env_logger的另一个选择

daboross/fern

Simple, efficient logging for Rust

fern配置起来更直观(所下所示), 目前我还没测试它与env_logger的性能差异

// Configure logger at runtime
fern::Dispatch::new()
    // Perform allocation-free log formatting
    .format(|out, message, record| {
        out.finish(format_args!(
            "{}[{}][{}] {}",
            chrono::Local::now().format("[%Y-%m-%d][%H:%M:%S]"),
            record.target(),
            record.level(),
            message
        ))
    })
    // Add blanket level filter -
    .level(log::LevelFilter::Debug)
    // - and per-module overrides
    .level_for("hyper", log::LevelFilter::Info)
    // Output to stdout, files, and other Dispatch configurations
    .chain(std::io::stdout())
    .chain(fern::log_file("output.log")?)
    // Apply globally
    .apply()?;

// and log using log crate macros!
info!("helllo, world!");
复制代码

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持 码农网

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

SQL进阶教程

SQL进阶教程

[ 日] MICK / 吴炎昌 / 人民邮电出版社 / 2017-11 / 79.00元

本书是《SQL基础教程》作者MICK为志在向中级进阶的数据库工程师编写的一本SQL技能提升指南。全书可分为两部分,第一部分介绍了SQL语言不同寻常的使用技巧,带领读者从SQL常见技术,比如CASE表达式、自连接、HAVING子句、外连接、关联子查询、EXISTS……去探索新发现。这部分不仅穿插讲解了这些技巧背后的逻辑和相关知识,而且辅以丰富的示例程序,旨在帮助读者提升编程水平;第二部分着重介绍关系......一起来看看 《SQL进阶教程》 这本书的介绍吧!

在线进制转换器
在线进制转换器

各进制数互转换器

XML、JSON 在线转换
XML、JSON 在线转换

在线XML、JSON转换工具

Markdown 在线编辑器
Markdown 在线编辑器

Markdown 在线编辑器