Rust 日志系统实践总结

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

内容简介:文档列表见:(上次更新: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!");
复制代码

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

查看所有标签

猜你喜欢:

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

Iterative Methods for Sparse Linear Systems, Second Edition

Iterative Methods for Sparse Linear Systems, Second Edition

Yousef Saad / Society for Industrial and Applied Mathematics / 2003-04-30 / USD 102.00

Tremendous progress has been made in the scientific and engineering disciplines regarding the use of iterative methods for linear systems. The size and complexity of linear and nonlinear systems arisi......一起来看看 《Iterative Methods for Sparse Linear Systems, Second Edition》 这本书的介绍吧!

图片转BASE64编码
图片转BASE64编码

在线图片转Base64编码工具

URL 编码/解码
URL 编码/解码

URL 编码/解码

RGB CMYK 转换工具
RGB CMYK 转换工具

RGB CMYK 互转工具