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](https://docs.rs/flexi_logger/0.27.2/flexi_logger/struct.Logger.html#method.log_to_stderr) 日志写入标准输入 - [`Logger::log_to_stdout`](https://docs.rs/flexi_logger/0.27.2/flexi_logger/struct.Logger.html#method.log_to_stdout) 日志写入标准输出。 - [`Logger::log_to_file`](https://docs.rs/flexi_logger/0.27.2/flexi_logger/struct.Logger.html#method.log_to_file) 日志写入文件。 [`FileSpec`](https://docs.rs/flexi_logger/0.27.2/flexi_logger/struct.FileSpec.html)有关文件名模式的详细信息,请参阅。 您可以复制到 stdout 和 stderr,并且可以添加其他编写器。 - [`Logger::log_to_writer`](https://docs.rs/flexi_logger/0.27.2/flexi_logger/struct.Logger.html#method.log_to_writer) 日志写入到提供的写入器。 您可以复制到 stdout 和 stderr,并且可以添加其他编写器。 - [`Logger::log_to_file_and_writer`](https://docs.rs/flexi_logger/0.27.2/flexi_logger/struct.Logger.html#method.log_to_file_and_writer) 日志被写入文件,与 一样[`Logger::log_to_file`](https://docs.rs/flexi_logger/0.27.2/flexi_logger/struct.Logger.html#method.log_to_file),*并且*写入替代 [`LogWriter`](https://docs.rs/flexi_logger/0.27.2/flexi_logger/writers/trait.LogWriter.html)实现。 您可以复制到 stdout 和 stderr,并且可以添加其他编写器。 - 或者[`Logger::do_not_log`](https://docs.rs/flexi_logger/0.27.2/flexi_logger/struct.Logger.html#method.do_not_log) 日志被处理,包括重复,但不写入任何目的地。 这非常有用,例如,对于在所有日志级别都处于活动状态的情况下运行应用程序测试,并且仍然避免大量日志文件等。此类测试可确保通常不活动的日志调用在激活时不会导致不需要的副作用(请注意,日志调用宏可能会阻止评估非活动日志调用的参数)。 或者,如果您希望将日志同时发送到 stdout 和 stderr,但不发送到其他地方,则使用此选项并将其与 [`Logger::duplicate_to_stdout`](https://docs.rs/flexi_logger/0.27.2/flexi_logger/struct.Logger.html#method.duplicate_to_stdout)and结合使用[`Logger::duplicate_to_stderr`](https://docs.rs/flexi_logger/0.27.2/flexi_logger/struct.Logger.html#method.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`](https://docs.rs/flexi_logger/0.27.2/flexi_logger/struct.Logger.html#method.write_mode) 一些选择来改变这种行为,例如 [-](https://docs.rs/flexi_logger/0.27.2/flexi_logger/enum.WriteMode.html) ``` 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`](https://docs.rs/flexi_logger/0.27.2/flexi_logger/constant.DEFAULT_BUFFER_CAPACITY.html)) 和默认间隔 ( )相同[`DEFAULT_FLUSH_INTERVAL`](https://docs.rs/flexi_logger/0.27.2/flexi_logger/constant.DEFAULT_FLUSH_INTERVAL.html)。 ``` pub const DEFAULT_BUFFER_CAPACITY: usize = _; // 8_192usize 8k pub const DEFAULT_FLUSH_INTERVAL: Duration;//1s ``` - BufferAndFlushWith([usize](https://doc.rust-lang.org/nightly/std/primitive.usize.html), [Duration](https://doc.rust-lang.org/nightly/core/time/struct.Duration.html)) ``` 元组字段 0: usize 缓冲能力。 1: Duration 冲洗间隔。 以给定的缓冲区容量和刷新间隔进行缓冲和刷新。 和上面的一样,是指指定size和刷新时间 ``` - BufferDontFlush `BufferDontFlushWith`与默认容量相同( [`DEFAULT_BUFFER_CAPACITY`](https://docs.rs/flexi_logger/0.27.2/flexi_logger/constant.DEFAULT_BUFFER_CAPACITY.html))。达到8k刷新 - BufferDontFlushWith([usize](https://doc.rust-lang.org/nightly/std/primitive.usize.html)) 元组字段 ``` 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`](https://docs.rs/flexi_logger/0.27.2/flexi_logger/enum.WriteMode.html#variant.BufferAndFlush)、 或[`WriteMode::BufferAndFlushWith`](https://docs.rs/flexi_logger/0.27.2/flexi_logger/enum.WriteMode.html#variant.BufferAndFlushWith),您可以减少程序的 I/O 开销,从而提高整体性能,如果大量使用日志记录,这可能是相关的。此外,为了在日志行在输出通道中可见之前保持较短的最大等待时间,会创建一个额外的线程来定期刷新缓冲区。 ``` fn main() -> Result<(), Box> { let _logger = Logger::try_with_str("info")? .log_to_file(FileSpec::default()) .write_mode(WriteMode::BufferAndFlush) .start()?; // ... do all your work ... Ok(()) } ``` - 使用[`WriteMode::Async`](https://docs.rs/flexi_logger/0.27.2/flexi_logger/enum.WriteMode.html#variant.Async) 或 时[`WriteMode::AsyncWith`](https://docs.rs/flexi_logger/0.27.2/flexi_logger/enum.WriteMode.html#variant.AsyncWith),日志通过无界通道从应用程序线程发送到输出线程,该输出线程执行输出(以及轮换和清理,如果适用)。此外,输出被缓冲,并且使用有界消息池来减少分配,并使用刷新来避免长时间延迟。如果使用复制,则消息将同步写入`stdout`或`stderr`。 ``` fn main() -> Result<(), Box> { 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(""), 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 ``` ![](https://upload-images.jianshu.io/upload_images/28338950-8d5bf5f8716d6133.png) ``` ## colored_detailed_format ``` ![](https://upload-images.jianshu.io/upload_images/28338950-857414aefd6097e1.png) ``` ## colored_opt_format ``` ![](https://upload-images.jianshu.io/upload_images/28338950-057d2e4a8872a14b.png) 和上面的是一一对应的 ## 4.4 日志文件截取 无论[`Logger::log_to_file`](https://docs.rs/flexi_logger/0.27.2/flexi_logger/struct.Logger.html#method.log_to_file)是否进行轮换, `flexi_logger`默认情况下都会使用名称中带有时间戳的文件,例如 `foo_2020-11-16_08-37-44.log`(对于名为 的程序`foo`),这些文件对于每个程序启动来说都是非常唯一的。 这样[`FileSpec::suppress_timestamp`](https://docs.rs/flexi_logger/0.27.2/flexi_logger/struct.FileSpec.html#method.suppress_timestamp) 您就可以获得一个简单的固定文件名,例如`foo.log`. 在这种情况下,重新启动程序将截断现有日志文件。 另外使用[`Logger::append`](https://docs.rs/flexi_logger/0.27.2/flexi_logger/struct.Logger.html#method.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`](https://docs.rs/flexi_logger/0.27.2/flexi_logger/struct.Logger.html#method.rotate) 采用三个定义其行为的枚举参数: - [`Criterion`](https://docs.rs/flexi_logger/0.27.2/flexi_logger/enum.Criterion.html) - [`Criterion::Age`](https://docs.rs/flexi_logger/0.27.2/flexi_logger/enum.Criterion.html#variant.Age)当时钟切换到新的一天、小时、分钟或秒时,就会发生旋转 - [`Criterion::Size`](https://docs.rs/flexi_logger/0.27.2/flexi_logger/enum.Criterion.html#variant.Size)当当前日志文件超过指定限制时发生轮转 - [`Criterion::AgeOrSize`](https://docs.rs/flexi_logger/0.27.2/flexi_logger/enum.Criterion.html#variant.AgeOrSize)当达到两个限制中的任何一个时,就会发生旋转 - [`Naming`](https://docs.rs/flexi_logger/0.27.2/flexi_logger/enum.Naming.html) 然后将当前文件重命名 - 与[`Naming::Timestamps`](https://docs.rs/flexi_logger/0.27.2/flexi_logger/enum.Naming.html#variant.Timestamps)类似的东西`foo_r2020-11-16_08-56-52.log` - 与[`Naming::Numbers`](https://docs.rs/flexi_logger/0.27.2/flexi_logger/enum.Naming.html#variant.Numbers)类似的东西`foo_r00000.log` 并`rCURRENT`创建一个新文件。 - [`Cleanup`](https://docs.rs/flexi_logger/0.27.2/flexi_logger/enum.Cleanup.html)定义是否以及如何避免无限期累积日志文件: - 您指定[`Cleanup::KeepLogFiles`](https://docs.rs/flexi_logger/0.27.2/flexi_logger/enum.Cleanup.html#variant.KeepLogFiles)应保留的日志文件的数量;如果有更多,较旧的将被删除 - 您指定[`Cleanup::KeepCompressedFiles`](https://docs.rs/flexi_logger/0.27.2/flexi_logger/enum.Cleanup.html#variant.KeepCompressedFiles)应保留的日志文件的数量,并且这些文件将被额外压缩 - 您可以[`Cleanup::KeepLogAndCompressedFiles`](https://docs.rs/flexi_logger/0.27.2/flexi_logger/enum.Cleanup.html#variant.KeepLogAndCompressedFiles) 指定应按原样保留的日志文件数量以及正在压缩的附加数量 - 如果[`Cleanup::Never`](https://docs.rs/flexi_logger/0.27.2/flexi_logger/enum.Cleanup.html#variant.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`(如果尚不存在): ```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`. 如果无法读取文件,则初始规范仍然有效。 本文由[mdnice](https://mdnice.com/?platform=6)多平台发布

你可能感兴趣的:(rust-flexi_logger)