#logging #log #structured #log-level #error-logging

astrolog

一个易于使用、简洁且灵活的Rust日志系统

1 个不稳定版本

0.1.0 2019年3月1日

#499 in 调试

LGPL-2.1-only

84KB
2K SLoC

Astrolog

Astrolog是一个易于使用、简洁且灵活的Rust日志系统。

Astrolog的主要目的是在应用程序中使用,而不是在crate或库中使用。它注重简洁,而不是最大效率。性能不可忽视,但在开发过程中,易用性具有更高的优先级。

如何使用

Astrolog可以通过两种方式使用:全局日志记录器可以从应用程序的任何地方静态调用,如果您需要具有不同配置的一个或多个实例,则可以分别实例化它们,并将相关的实例传递给应用程序的组件。

全局日志记录器

如果您正在构建一个小型应用程序且/或不想将日志记录器注入到可能使用它的函数或对象中,则此方法特别有用。如果您熟悉默认的rust log 功能或提供日志宏的crate,如 slog,则此方法将看起来很熟悉,但使用静态调用而不是宏。

fn main() {
    astrolog::config(|logger| {
        logger
            .set_global("OS", env::consts::OS)
            .push_handler(ConsoleHandler::new().with_levels_range(Level::Info, Level::Emergency));
    });
  
    normal_function();
    function_with_error(42);
}

fn normal_function() {
    astrolog::debug("A debug message");
}

fn function_with_error(i: i32) {
    astrolog::with("i", i)
        .with("line", line!())
        .with("file", file!())
        .error("An error with some debug info");
}

常规日志记录器

如果您想为应用程序的不同部分使用不同的日志记录器,或者您想通过依赖注入、服务定位器或DIC(或使用单例)传递日志记录器,则此方法很有用。

fn main() {
    let logger1 = Logger::new()
        .with_global("OS", env::consts::OS)
        .with_handler(ConsoleHandler::new().with_levels_range(Level::Trace, Level::Info));

    let logger2 = Logger::new()
        .with_global("OS", env::consts::OS)
        .with_handler(ConsoleHandler::new().with_levels_range(Level::Info, Level::Emergency));

    normal_function(&logger1);
    function_with_parameter(42, &logger2);

}

fn normal_function(logger: &Logger) {
    logger.debug("A debug message");
}

fn function_with_parameter(i: i32, logger: &Logger) {
    logger
        .with("i", i)
        .with("line", line!())
        .with("file", file!())
        .error("An error with some debug info");
}

语法和配置

Astrolog通过允许用户构建日志条目并将它们传递给处理程序来实现。每个日志记录器可以有一个或多个处理程序,每个处理程序可以单独配置以仅接受某些日志级别。

例如,您可能希望有一个 ConsoleHandler(或用于彩色输出的 TermHandler)来处理 tracedebuginfo 级别,同时您还想将所有级别的消息保存到文件中,并将所有级别为 warning 或更高的消息发送到外部日志聚合器。

通过隐式构建模式构建条目,并在调用适当的级别函数时将它们发送到处理程序。

让我们用一个例子来简化它

fn main() {
    logger.info("Some informative message")
}

这将构建条目并立即将其发送到处理程序。

使用 with 将会开始构建条目

fn main() {
    logger.with("some key", 42).info("Some informative message")
}

这将创建一个条目,将其中的键值对("some key"42)存储在其中,并最终将其发送到处理器。

在调用级别方法之前,可以多次调用 with(或 with_multiwith_error)进行链式调用。

可用级别

Astrolog 使用的级别比普通记录器多。以下是按严重程度排序的完整列表

  • 跟踪
  • 性能分析
  • 调试
  • 信息
  • 注意
  • 警告
  • 错误
  • 严重
  • 警报
  • 紧急

Logger 上的函数名称相应地命名(.trace().profile() 等)。还有一个接受 Level 作为第一个参数的 .log() 函数,以便在运行时程序化地决定。

debugemergency 的级别模仿 Unix/Linux 的 syslog 级别。

其他日志系统往往将所有调试、性能分析和跟踪信息混淆到 debug 中,而 Astrolog 建议只将 debug 用于无用的“占位符”消息。

profile 旨在记录性能分析信息(执行时间、函数或循环迭代的调用次数等),而 trace 旨在跟踪程序执行(例如调试时)

这允许更好地过滤调试信息并将它们发送到特定的处理器。例如,您可能希望将 profile 信息发送到 Prometheus 处理器,将 debuginfonotice 发送到 STDOUT,将 trace 发送到文件以便更容易分析,以及将所有 warning 和以上级别的信息发送到 STDERR。

示例

要运行示例,请使用

cargo run --example simple
cargo run --example global
cargo run --example passing
cargo run --example errors
cargo run --example multithread

每个示例展示了使用 Astrolog 的不同方法。

simple 展示了如何创建专用记录器并通过方法调用使用它,有无额外参数。

global 展示了如何通过静态调用配置和使用全局记录器。

passing 展示了如何创建专用记录器并通过引用或 Rc 传递它。

errors 展示了如何将 Rust 错误传递到日志函数并打印错误跟踪。

multithread 展示了如何轻松地在多线程应用程序中使用 Astrolog。

处理器

Astrolog 核心crate提供了一些基本处理器,而其他处理器可以在单独的crate中实现。

控制台处理器

控制台处理器简单地将所有消息打印到控制台,每行一个,具有可配置的格式(稍后将在 [格式化程序] 中介绍)。

此处理器可以配置为使用 stdoutstderr 打印消息,但由于 Astrolog 的模块化,可以为不同的日志级别注册两个 ConsoleHandler,以便将消息发送到不同的输出。

此处理器不提供输出着色(有关着色,请参阅 [astrolog-term] crate)。

示例

fn main() {
    let logger = Logger::new()
        .with_handler(ConsoleHandler::new()
              .with_stdout()
              .with_levels_range(Level::Debug, Level::Notice)
        )
        .with_handler(ConsoleHandler::new()
              .with_stderr()
              .with_levels_range(Level::Warning, Level::Emergency)
        );
}

Vec 处理器

此处理程序简单地将所有消息收集到一个 Vec 中,以便稍后处理。例如,它有助于调试新的格式化程序或批量处理消息。

由于Rust的所有权规则,使用它需要在设置时做更多的工作。

fn main() {
    let handler = VecHandler::new();
    let logs_store = handler.get_store();
    
    let logger = Logger::new()
        .with_handler(handler);
        
    logger.info("A new message");
    
    let logs = logs_store.take();
    
    // logs is now a Vec<Record> over which you can iterate
}

空处理程序

此处理程序是Astrolog的 /dev/null。它简单地丢弃接收到的任何消息。

它可以用于在运行时决定不记录任何内容,同时具有最小的处理成本,保持记录器实例就位。

fn main() {
    let logger = Logger::new()
        .with_handler(
            NullHandler::new()
        );
}

格式化程序

格式化程序允许以不同的方式格式化日志记录,具体取决于用户的偏好或处理程序所需的格式。

例如,将日志记录到文件最好使用 LineFormatter,而在网页上打印错误则可能受益于 HtmlFormatter,而通过REST API发送则需要 JsonFormatter

然后,每个记录格式化程序可以使用不同的“子格式化程序”来格式化级别指示器、日期和上下文(与记录相关联的值)。

LineFormatter

这是Astrolog基本crate中提供最简单且最有用的格式化程序。它简单地以模板格式返回单个行中的日志记录信息。模板支持占位符,可以在输出中插入记录或上下文的各个部分。

fn main() {
    let logger = Logger::new()
        .with_handler(ConsoleHandler::new()
              .with_formatter(LineFormatter::new()
                  .with_template("{{ [datetime]+ }}{{ level }}: {{ message }}{{ +context? }}")
              )
        );
}

此示例显示了 ConsoleHandler 的默认配置,因此不需要,但它可以提供如何配置格式化程序及其模板的示例。

支持的占位符有:

  • datetime:插入记录的日期和时间,根据配置的日期时间格式化程序(见下文)
  • level:插入记录的级别,根据配置的级别格式化程序(见下文)
  • message:插入记录的消息
  • context:插入上下文,根据配置的上下文格式化程序(见下文)

任何其他占位符都视为上下文中的变量名称,并支持JSON指针来访问嵌套信息。

所有占位符(除了 message 以避免循环)都可以用于消息本身,以将上下文变量添加到消息中。例如

fn main() {
    logger
        .with("user", json!({"name": "Alex", "login": { "username": "dummy123" } }))
        .debug("Logged in as {{ /user/login/username }}");
}

HtmlFormatter

此格式化程序与 LineFormatter 非常相似,新增了自动转义消息和值中的字符串的功能,以便将 &<> 正确编码为实体并显示在页面上。

JsonFormatter

此格式化程序将记录转换为JSON对象并将其作为字符串返回。

默认情况下,顶级字段名称为 timelevelmessagecontext,但它们可以通过 .set_field_names().with_field_names() 进行自定义。

日志部分格式化程序

日志消息的部分可以用不同的方式进行格式化。有时处理程序需要特定的格式,例如日期,而有时只是个人偏好的问题。

日期格式化

formatter::date::Format 是一个枚举,支持多种预定义的格式,并能够根据 [chrono] 的格式使用自定义格式。

级别格式化

formatter::level::Format 是一个枚举,支持日志级别的长(全称)和短(4字符)表示,可以是小写、大写或标题大小写。

上下文格式化

此模块在核心crate中提供了两种格式化与日志记录关联的上下文的方式:JSON 和 JSON5。

虽然 JSON 在将日志传输到其他服务或应用程序时很有用(甚至必不可少),但 JSON5 在人类可读性方面更好,因此特别适用于控制台、文件和syslog日志(它是默认格式)。

字段命名

一个特殊情况是 formatter::fields::Names,因为它用于定义某些格式器(如 JSON)中的主要字段名称,允许自定义最终表示的外观。

示例

fn main() {
    let logger = Logger::new()
        .with_handler(ConsoleHandler::new()
            .with_formatter(LineFormatter::new()
                .with_date_format(DateFormat::Email)
                .with_level_format(LevelFormat::LowerShort)
            )
        );
}

许可证

作为说明,当使用 Astrolog 作为 Rust crate 时,crate 被视为动态库,因此 LGPL 允许你在封闭源代码项目或具有不同开源许可证的项目中使用它,而任何对 Astrolog 本身的修改都必须在 LGPL 2.1 许可证下发布。

依赖项

~6MB
~121K SLoC