当前位置: 首页 > article >正文

rust-flexi_logger

flexi_logger 是字节开源的rust日志库。目前有log4rs、env_log 等库,综合比较下来,还是flexi_logger简单容易上手,而且自定义很方便,以及在效率方面感觉也会高,下篇文章我们来测试下。
下面来看下怎么使用
关注 vx golang技术实验室,获取更多好文

一、启动日志的三种方式

引入包

flexi_logger = { version = "0.25.3", features = ["specfile", "compress"] }

通过选择三个选项之一来指定您想要查看的日志输出进行初始化,并start()立即调用:

  • 在环境变量中提供日志规范RUST_LOG

    Logger::try_with_env()?.start()?;
    
  • 以编程方式提供日志规范:

    Logger::try_with_str("info")?.start()?;
    
  • 结合两个选项:

    Logger::try_with_env_or_str("info")?.start()?;
    

之后,您只需使用日志箱中的日志宏即可。然后,那些与日志规范匹配的日志行将被写入默认输出通道 (stderr)。

二、日志输出通道

2.1 写入到文件

assert_eq!(
    FileSpec::default()
        .directory("/a/b/c")//输出的目录
        .basename("foo")//输出的文件名
        .suppress_timestamp()//是否添加时间
        .suffix("bar"),//添加的后缀
        .discriminant("Sample4711A") .//使用FileSpec::discriminant 您可以向日志文件名添加有区别的中缀。
    FileSpec::try_from("/a/b/c/foo.bar").unwrap()//
);
$ ll log_files
total 16
-rw-r--r--  1 xxx  staff  70 Oct 27 16:47 foo.bar //使用suppress_timestamp
-rw-r--r--  1 xxx  staff  70 Oct 27 16:47 foo_2023-10-27_16-47-53.bar
-rw-r--r--  1 xxx  staff  70 Oct 27 17:01 foo_Sample4711A_2023-10-27_17-01-11.bar

如果给定路径的基本名称没有文件名,则会发生恐慌

三、写入模式

默认情况下,每个日志行都直接写入输出,而不进行缓冲。这允许实时查看新的日志行。

您有Logger::write_mode 一些选择来改变这种行为,例如

-

pub enum WriteMode {
    Direct,
    SupportCapture,
    BufferAndFlush,
    BufferAndFlushWith(usize, Duration),
    BufferDontFlush,
    BufferDontFlushWith(usize),
    Async,
    AsyncWith {
        pool_capa: usize,
        message_capa: usize,
        flush_interval: Duration,
    },
}
  • Direct

    不缓冲(默认)。

    每个日志行都直接写入输出,无需缓冲。这允许实时查看新的日志行,并且不需要额外的线程。

  • SupportCapture

​ 不缓冲和支持cargo test捕获。

​ 很像Direct,只是慢一点,并且允许 cargo test捕获日志输出并仅在失败的测试时打印它。

  • BufferAndFlush

BufferAndFlushWith与默认容量 ( DEFAULT_BUFFER_CAPACITY) 和默认间隔 ( )相同DEFAULT_FLUSH_INTERVAL

pub const DEFAULT_BUFFER_CAPACITY: usize = _; // 8_192usize 8k
pub const DEFAULT_FLUSH_INTERVAL: Duration;//1s
  • BufferAndFlushWith(usize, Duration)
元组字段
0: usize
缓冲能力。

1: Duration
冲洗间隔。

以给定的缓冲区容量和刷新间隔进行缓冲和刷新。
和上面的一样,是指指定size和刷新时间
  • BufferDontFlush

BufferDontFlushWith与默认容量相同( DEFAULT_BUFFER_CAPACITY)。达到8k刷新

  • BufferDontFlushWith(usize)

​ 元组字段

0: usize

​ 缓冲能力。

​ 具有给定缓冲区容量的缓冲区,但不刷新。

​ 如果您想最大程度地减少 I/O 工作量并且不想创建额外的线程用于刷新并且不关心日志行是否出现延迟,这可能会很方便。

  • Async

与 相同AsyncWith,所有参数均使用默认值。

  • AsyncWith
Fields
pool_capa: usize
消息缓冲区池的容量。

message_capa: usize
单个消息缓冲区的容量。

flush_interval: Duration
刷新输出的时间间隔。

随着Duration::ZERO冲洗被抑制。

日志行通过无界通道发送到输出线程,该线程执行 I/O,如果log_to_file()选择的话,还执行轮换和清理。

使用缓冲输出来减少开销,并使用有界消息池来减少分配。日志输出按照给定的时间间隔定期刷新。

  • 使用WriteMode::BufferAndFlush、 或WriteMode::BufferAndFlushWith,您可以减少程序的 I/O 开销,从而提高整体性能,如果大量使用日志记录,这可能是相关的。此外,为了在日志行在输出通道中可见之前保持较短的最大等待时间,会创建一个额外的线程来定期刷新缓冲区。

    fn main() -> Result<(), Box<dyn std::error::Error>> {
        let _logger = Logger::try_with_str("info")?
           .log_to_file(FileSpec::default())
           .write_mode(WriteMode::BufferAndFlush)
           .start()?;
        // ... do all your work ...
        Ok(())
    }
    
  • 使用WriteMode::Async 或 时WriteMode::AsyncWith,日志通过无界通道从应用程序线程发送到输出线程,该输出线程执行输出(以及轮换和清理,如果适用)。此外,输出被缓冲,并且使用有界消息池来减少分配,并使用刷新来避免长时间延迟。如果使用复制,则消息将同步写入stdoutstderr

    fn main() -> Result<(), Box<dyn std::error::Error>> {
        let _logger = Logger::try_with_str("info")?
           .log_to_file(FileSpec::default())
           .write_mode(WriteMode::Async)
           .start()?;
        // ... do all your work ...
        Ok(())
    }
    

四、日志格式

4.1 自定义输出格式

use flexi_logger::{ DeferredNow, Record};

fn custom_format(
    w: &mut dyn std::io::Write,
    now: &mut DeferredNow,
    record: &Record,
) -> Result<(), std::io::Error> {
    write!(
        w,
        "[{}] [{}] {}: {}",
        now.now().format("%Y-%m-%d %H:%M:%S"),
        record.level(),
        record.module_path().unwrap_or("<unnamed>"),
        record.args()
    )
}

   let _logger = Logger::try_with_str("debug, my::critical::module=trace").unwrap()
        .log_to_file(
            FileSpec::default().
                directory("./log_files").
                basename("foo").
                // suppress_timestamp().
                suffix("bar").
                discriminant("Sample4711A")
        )
        .duplicate_to_stderr(Duplicate::Debug)// print warnings and errors also to the console
        .write_mode(WriteMode::BufferAndFlush).
        format(custom_format)//定义的输出格式
            .start().

        unwrap();

[2023-10-27 17:32:08] [DEBUG] my_test: s
[2023-10-27 17:32:08] [INFO] my_test: s
[2023-10-27 17:32:08] [WARN] my_test: s
[2023-10-27 17:32:08] [ERROR] my_test: s

        

4. 2 使用默认的日志格式

## opt_format
[2023-10-27 17:34:23.438382 +08:00] DEBUG [src/main.rs:50] s
[2023-10-27 17:34:23.439057 +08:00] INFO [src/main.rs:51] s
[2023-10-27 17:34:23.439078 +08:00] WARN [src/main.rs:52] s
[2023-10-27 17:34:23.439091 +08:00] ERROR [src/main.rs:53] s

## default_format
DEBUG [my_test] s
INFO [my_test] s
WARN [my_test] s
ERROR [my_test] s

## detailed_format
[2023-10-27 17:36:12.719699 +08:00] DEBUG [my_test] src/main.rs:50: s
[2023-10-27 17:36:12.719948 +08:00] INFO [my_test] src/main.rs:51: s
[2023-10-27 17:36:12.719964 +08:00] WARN [my_test] src/main.rs:52: s
[2023-10-27 17:36:12.719978 +08:00] ERROR [my_test] src/main.rs:53: s

## with_thread
[2023-10-27 17:36:41.542709 +08:00] T[main] DEBUG [src/main.rs:50] s
[2023-10-27 17:36:41.542968 +08:00] T[main] INFO [src/main.rs:51] s
[2023-10-27 17:36:41.542984 +08:00] T[main] WARN [src/main.rs:52] s
[2023-10-27 17:36:41.542997 +08:00] T[main] ERROR [src/main.rs:53] s

4.2 默认的彩色输出

## colored_default_format

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

## colored_detailed_format

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

## colored_opt_format

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

和上面的是一一对应的

4.4 日志文件截取

无论Logger::log_to_file是否进行轮换, flexi_logger默认情况下都会使用名称中带有时间戳的文件,例如 foo_2020-11-16_08-37-44.log(对于名为 的程序foo),这些文件对于每个程序启动来说都是非常唯一的。

这样FileSpec::suppress_timestamp 您就可以获得一个简单的固定文件名,例如foo.log.

在这种情况下,重新启动程序将截断现有日志文件。

另外使用Logger::append 将每次新运行的日志附加到现有文件中。

Logger::try_with_str("info")? // Write all error, warn, and info messages
    // use a simple filename without a timestamp
    .log_to_file(
        FileSpec::default().suppress_timestamp()
    )
    // do not truncate the log file when the program is restarted
    .append()
    .start()?;

通过轮换,日志始终会写入带有中缀的文件中rCURRENT,例如foo_rCURRENT.log

Logger::rotate 采用三个定义其行为的枚举参数:

  • Criterion

    • Criterion::Age当时钟切换到新的一天、小时、分钟或秒时,就会发生旋转
    • Criterion::Size当当前日志文件超过指定限制时发生轮转
    • Criterion::AgeOrSize当达到两个限制中的任何一个时,就会发生旋转
  • Naming
    然后将当前文件重命名

    • Naming::Timestamps类似的东西foo_r2020-11-16_08-56-52.log
    • Naming::Numbers类似的东西foo_r00000.log

    rCURRENT创建一个新文件。

  • Cleanup定义是否以及如何避免无限期累积日志文件:

    • 您指定Cleanup::KeepLogFiles应保留的日志文件的数量;如果有更多,较旧的将被删除
    • 您指定Cleanup::KeepCompressedFiles应保留的日志文件的数量,并且这些文件将被额外压缩
    • 您可以Cleanup::KeepLogAndCompressedFiles 指定应按原样保留的日志文件数量以及正在压缩的附加数量
    • 如果Cleanup::Never不进行清理,所有文件都会保留。

案例

Logger::try_with_str("info")?      // Write all error, warn, and info messages
    .log_to_file(
        FileSpec::default()
    )
    .rotate(                      // If the program runs long enough,
        Criterion::Age(Age::Day), // - create a new file every day
        Naming::Timestamps,       // - let the rotated files have a timestamp in their name
        Cleanup::KeepLogFiles(7), // - keep at most 7 log files
    )
    .start()?;
    
-rw-r--r--  1 xxx  staff  1508 Oct 27 18:46 foo_Sample4711A_rCURRENT.bar

五、代码方式启动

5.1 程序中设置

    let _logger = Logger::try_with_str("debug, my::critical::module=trace").unwrap()
        .log_to_file(
            FileSpec::default().
                directory("./log_files").
                basename("foo").
                suppress_timestamp().
                suffix("bar").
                discriminant("Sample4711A")
        )
        .duplicate_to_stderr(Duplicate::Debug)// print warnings and errors also to the console
        .write_mode(WriteMode::BufferAndFlush).
        format(colored_opt_format).
        append()
        .rotate(                      // If the program runs long enough,
                                      Criterion::AgeOrSize(Age::Day,10000000000000), // - create a new file every day
                                      Naming::Timestamps,       // - let the rotated files have a timestamp in their name
                                      Cleanup::KeepLogFiles(7), // - keep at most 7 log files
        )
        .start().
        unwrap();

这里我们使用了配置字符串 "debug, my::critical::module=trace",表示全局默认日志级别为 debug,而模块路径为 my::critical::module 的日志级别为 trace

5.2 文件设置

Logger::try_with_str("info").unwrap()
    // ... logger configuration ...
   .start_with_specfile("./server/config/logspec.toml")
   .unwrap();


将创建包含以下内容的文件logspecification.toml(如果尚不存在):

### Optional: Default log level
global_level = 'info'
### Optional: specify a regular expression to suppress all messages that don't match
#global_pattern = 'foo'

### Specific log levels per module are optionally defined in this section
[modules]
#'mod1' = 'warn'
#'mod2' = 'debug'
#'mod2::mod3' = 'trace'

随后,您可以根据需要编辑和修改该文件,在程序运行时,它将立即考虑您的更改。

目前仅支持 toml 文件,因此文件后缀必须是.toml.

如果无法读取文件,则初始规范仍然有效。


http://www.kler.cn/a/147378.html

相关文章:

  • STM32寄存器结构体详解
  • 云计算复习文档
  • QT QLineEdit失去焦点事件问题与解决
  • 学习threejs,使用TWEEN插件实现动画
  • Python期末复习 | 列表、元组、字典、集合与字符串 | 代码演示
  • ssh登陆服务器后支持Tab键命令补全
  • 40.0/jdbc/Java数据连接/jar包运用增删改
  • iOS强引用引起的内存泄漏
  • 基于单片机设计的超声波测距仪(采用HC-SR04模块)
  • 【限时免费】20天拿下华为OD笔试之【双指针】2023Q1A-两数之和绝对值最小【欧弟算法】全网注释最详细分类最全的华为OD真题题解
  • 二十章多线程
  • 短视频获客系统成功分享,与其开发流程与涉及到的技术
  • stream流和方法引用
  • shell脚本正则表达式
  • 有一种浪漫,叫接触Linux
  • Verilog基本语法概述
  • 智慧工地管理系统加快推进工程建设项目全生命周期数字化
  • 抽象类的使用—模板设计模式 Java
  • 网络入门---网络的大致了解
  • JVM GC算法
  • pycharm安装PyQt5及其工具
  • Centos Download
  • Xshell连接VMware虚拟机中的CentOS
  • 【开源】基于Vue.js的海南旅游景点推荐系统的设计和实现
  • Java核心知识点整理大全19-笔记
  • 如何理解2023vivo开发者大会,使用Rust语言编写蓝河操作系统(BlueOS)?