#log-file #log #log-level #logger #log-messages #log-line #file

build the_logger

一个非常简单但可定制的 Rust 日志记录器

4 个版本

0.5.3 2023 年 12 月 17 日
0.5.2 2023 年 12 月 17 日
0.5.1 2023 年 12 月 17 日
0.5.0 2023 年 12 月 17 日

#219 in 调试

MIT 许可证

56KB
673

Crates.io Crates.io Rust rustc_version the_logger

the_logger

该包旨在使日志记录变得容易,只需两行即可创建日志文件。但它不仅易于使用和启动,而且还可以进行到微秒级的定制。

此日志系统基于 std 库将内容写入文件,并使用 tokio 仅用于全局锁定文件写入器,lazy_static 保持文件写入器的静态引用,chrono 用于获取日志的日期和时间。可选的(将在未来版本中提供),您可以通过创建一个 json 文件并在此设置配置来配置其设置。

特性

  • 非常容易调用和使用。
  • 高度可定制,个性化功能不断逐渐增加。
  • 轻量级,默认功能下只有 3 个依赖项(lazy_static、tokio 和 chrono)。
  • 在代码的任何位置都可以访问,无需通过引用传递。
  • 启动时和运行时均可配置参数。

如何使用

如前所述,基本用法非常简单,默认设置下无需配置即可进行日志记录。调用记录器需要一个异步函数

use the_logger::{log_warning, TheLogger};

async fn init_logger(thread_id: u8) {
    let logger: &TheLogger = TheLogger::instance();
    log_warning!(logger, "This is a warning emitted by thread {}", thread_id);
}

如上例所示,仅需要两行代码即可设置记录器和记录消息。执行日志记录有多种方式,但最简洁的方式是通过宏调用。另一种方法是

use the_logger::TheLogger;

async fn init_logger(thread_id: u8) {
    TheLogger::instance()
        .warning().await
        .log_in_file(
            (file!(), line!(), column!()
        ), "This is another logging example").await;
}

后一种方法将行数减少到 1,但代价是调用变得非常冗长且不太直观,失去了此包的目的。

如第一个示例所示,log_X!() 宏具有对 format!() 宏的内建支持,无需分配额外变量来构建消息。宏的第一个参数需要是记录器元素,其余参数可以使用内建字符串格式化器使用,支持所有 format!() 宏功能

use the_logger::{log_warning, TheLogger};

async fn init_logger(thread_id: u8) {
    let logger: &TheLogger = TheLogger::instance();
    //  This log...
    log_warning!(logger, "This is a log by thread {}", thread_id);
    
    //  ...is equivalent content-wise to this log
    let msg = format!("This is a log by thread {}", thread_id);
    log_warning!(logger, msg);
}

使用此记录器的两种主要方法:通过引用传递实例,或在任何您想调用实例的地方

use the_logger::{log_info, TheLogger};

async fn parent_function() {
    let logger: &TheLogger = TheLogger::instance();
    
    child_function_one(logger).await;
    
    child_function_two();
}

async fn child_function_one(logger: &TheLogger) {
    
    log_info!(
        logger, 
        "This will trigger an Informational log by passing the logger instance as reference"
    );
    
}

async fn child_function_two() {
    let logger: &TheLogger = TheLogger::instance();
    
    log_info!(
        logger, 
        "This will trigger an Informational log by calling an instance of the logger without parameters"
    );
}

这是由于使用了前面提到的tokio的RwLock功能。此方法决定避免在写入日志文件时出现不一致和错误。这样,无论有多少个线程在运行,_logger crate都将确保日志行一次写入一个,并且按照它们被调用的顺序写入。

日志文件

所有生成的文件都将放在使用此crate的项目中,位于logs/文件夹中,名称为:"Log <year>-<month>-<day>.log"

在未来的版本中,您将能够配置名称(日期始终存在以更好地组织文件),保存路径,文件格式,并且您还可以删除超过可配置天数的老文件。

日志级别

有7个日志级别,它们并非都是级别,而是类别。这些是

  • 详细
  • 信息
  • 错误
  • 警告
  • 调试
  • 跟踪
  • 严重

每个都可能在不同的情况下在您的代码中发挥作用,并且它们都存在供您按需使用。

要调用每个级别,您可以简单地使用定义的宏调用

use the_logger::TheLogger;
use the_logger::{log, log_info, log_error, log_warning, log_debug, log_trace, log_critical};

async fn init_logger(thread_id: u8) {
    let logger: &TheLogger = TheLogger::instance();
    
    log!(logger, "This call will trigger a Verbose log");
    log_info!(logger, "This call will trigger an Informational log");
    log_error!(logger, "This call will trigger an Error log");
    log_warning!(logger, "This call will trigger a Warning log");
    log_debug!(logger, "This call will trigger a Debug log");
    log_trace!(logger, "This call will trigger a Trace log");
    log_critical!(logger, "This call will trigger a Critical log");
}

配置

如前所述,此记录器非常可定制。您可以选择显示或隐藏几乎所有的元素

  • 日期中的年份
  • 日期中的月份
  • 日期中的日期
  • 时间中的小时
  • 时间中的分钟
  • 时间中的秒
  • 时间中的毫秒
  • 时间中的微秒
  • 日志级别
  • 日志位置(文件、行和列)

默认配置是显示上述所有元素,除了文件位置选项中的列。

您还可以配置位置和内容文本的最大长度。默认情况下,位置文本内容限制为60个字符,日志内容本身限制为300。

有两种配置此记录器的方法:在启动时和在运行时(将来将通过json文件添加配置)。要在启动时进行配置

use the_logger::{TheLogger, TheLoggerConfig};

async fn config_logger() {
    let logger_config = TheLoggerConfig::default()
        .hide_millisecs()               //  This will hide both the milliseconds and the microseconds
        .hide_level()                   //  This will hide the log level
        .hide_location()                //  This will hide the file name, line and column
        .location_content_length(100)   //  This will set the maximum location content text length to 100
        .log_content_length(1000);      //  This will set the maximum log content text length to 1000
    //  Feel free to explore the documentation for more configuration options
    
    let logger: &TheLogger = TheLogger::instance().config_logger(logger_config).await;
    //  The logger is now configured with the options set above
}

此配置方法仅建议在启动时使用,因为将TheLoggerConfig结构实例发送到.config_logger()将覆盖当前配置的内容。对于运行时配置,有更有效的方法

use the_logger::{TheLogger, TheLoggerConfig};

async fn config_logger() {
    //  Logger instanced at start
    let logger: &TheLogger = TheLogger::instance();
    
    //  Logger configured on the go
    logger.hide_millisecs().await
        .hide_level().await
        .hide_location().await
        .location_content_length(100).await
        .log_content_length(1000).await;
    
    //  The configuration applied is the same as in the example above
}

日志内容示例

在以下示例中,除了位置内容长度设置为20个字符外,所有参数都保留为其默认值

2023-12-17 01:51:10.677106	[VERBOSE]	@src\main.rs: 37    Log from thread 10
2023-12-17 01:51:10.677219	[WARNING]	@src\main.rs: 37    Log from thread 17
2023-12-17 01:51:10.677343	[TRACE]		@src\main.rs: 34    Log from thread 2
2023-12-17 01:51:10.702689	[CRITICAL]	@src\main.rs: 28    Log from thread 0
2023-12-17 01:51:10.810655	[ERROR]		@src\main.rs: 31    Log from thread 1
2023-12-17 01:51:11.367695	[INFO]		@src\main.rs: 34    Log from thread 2
2023-12-17 01:51:11.538744	[DEBUG]		@src\main.rs: 40    Log from thread 4

上一行日志是从大约350行由30个线程生成的日志中选出的。它们不是作为一组提取的,因为有些线程的睡眠时间比其他线程长,导致重复,因此我不得不选择其中的一些行。

以下是一个具有所有默认设置的日志行示例

2023-12-17 01:55:43.144632	[WARNING]	@src\main.rs: 43                                            Log from thread 26

您可以看到隐藏某些元素以及配置位置内容长度和/或日志文本内容长度是有用的。为了提高可读性,我们将从现在开始使用20个字符的位置内容长度。现在,如果我们去掉日志级别,我们得到

2023-12-17 01:58:57.874425		@src\main.rs: 39    Log from thread 24

接下来,我们还将去掉毫秒,这将自动去掉微秒

2023-12-17 01:59:57		@src\main.rs: 37    Log from thread 2

但是,您可能需要按时间点跟踪您的程序或服务,此时日志日期可能没有用,只显示完整的时间戳、级别、位置和内容

02:01:52.509640	[CRITICAL]	@src\main.rs: 32    Log from thread 0

您可能还有一个只有单个文件的小程序,错误信息非常详细,您可以配置它以获取

02:02:54.830615	[INFO]		Log from thread 16

如果您只想获取日志级别和内容,因为您的错误信息已经包含了日期、时间和位置?您也可以这样做

[CRITICAL]	Log from thread 14

最后,如果您只想隐藏所有内容,除了日志文本内容

Log from thread 25

唯一不能隐藏的参数是日志文本内容,因为它是日志的基本要求。

一些测试和结论

禁用元素记录到文件的能力带来了两个主要好处:由于需要写入文件的数据量减少,程序执行速度更快,与数据量相关的是文件大小。

为了测试文件大小,我在全配置(默认参数)和最小配置(仅日志文本内容)下运行了一个包含100个线程的程序,持续了2分钟,文件大小如下

  • 全配置大约为1116KB
  • 最小配置大约为177KB

这意味着通过仅保留所需的最小数据,文件大小减少了约84%。当然,这么多线程在写入文件时引入了瓶颈,因为它们必须等待锁释放,但大小比例在不同测试中保持不变。为了确保这一点,我将线程数量减少到30,时间相同,结果如下

  • 全配置大约为792KB
  • 最小配置大约为124KB

这给出了相同的全大小/最小大小比率。

未来功能和错误报告

正如我在整个readme文件中提到的,我有一些改进和功能的想法,包括

  • 通过在一个文件夹中创建一个json文件来配置,尚未确定
  • 配置日志文件路径
  • 配置日志文件名
  • 配置一些分隔符
  • 配置日志级别封装 -> [ERROR] vs {ERROR} vs (ERROR) vs ERROR vs |ERROR|等

如果您有任何建议、错误报告或其他类型的反馈,我将在下一节中提供一些联系方式。

总结

这就是对这个crate的总结,希望您觉得它有用,如果您有任何问题、建议或报告,请随时联系我:

电子邮件:nacho.ponce25@gmail.com

Telegram:@tommyHellraiser

或在GitHub存储库中提交一个问题: https://github.com/tommyHellraiser/the_logger

感谢您阅读,祝您一切顺利!

依赖项

~3–4.5MB
~71K SLoC