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)。

二、日志输出通道

  • log_to_stderr

    日志写入标准输入

  • Logger::log_to_stdout

    日志写入标准输出。

  • Logger::log_to_file

    日志写入文件。

    FileSpec有关文件名模式的详细信息,请参阅。

    您可以复制到 stdout 和 stderr,并且可以添加其他编写器。

  • Logger::log_to_writer

    日志写入到提供的写入器。

    您可以复制到 stdout 和 stderr,并且可以添加其他编写器。

  • Logger::log_to_file_and_writer

    日志被写入文件,与 一样Logger::log_to_file并且写入替代 LogWriter实现。

    您可以复制到 stdout 和 stderr,并且可以添加其他编写器。

  • 或者Logger::do_not_log

    日志被处理,包括重复,但不写入任何目的地。

    这非常有用,例如,对于在所有日志级别都处于活动状态的情况下运行应用程序测试,并且仍然避免大量日志文件等。此类测试可确保通常不活动的日志调用在激活时不会导致不需要的副作用(请注意,日志调用宏可能会阻止评估非活动日志调用的参数)。

    或者,如果您希望将日志同时发送到 stdout 和 stderr,但不发送到其他地方,则使用此选项并将其与 Logger::duplicate_to_stdoutand结合使用Logger::duplicate_to_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
元组字段
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 采用三个定义其行为的枚举参数:

案例

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.

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

  • 7
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

a...Z

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值