12 个版本 (6 个重大更新)

0.7.1 2023 年 6 月 8 日
0.6.1 2023 年 5 月 14 日
0.6.0 2019 年 9 月 29 日
0.3.0 2019 年 7 月 20 日

#264 in 并发

Download history 7/week @ 2024-03-09 1/week @ 2024-03-16 62/week @ 2024-03-30 16/week @ 2024-04-06

每月 70 次下载
用于 vxdraw

LGPL-3.0 或更高版本

73KB
1.5K SLoC

build status Latest version Documentation

快速日志记录器

Fast-logger 是一个 Rust 日志记录器,旨在成为最快的(具有最低的调用者延迟时间)日志记录器。它通过不执行动态分配并将格式化数据通过通道传递到另一个线程来实现这一点。这样做的原因是格式化本身是一个昂贵的操作。


lib.rs:

基于日志级别的简单日志记录器

概述

在创建日志记录器时,会创建一个线程并返回一个句柄结构。这个句柄可以被复制并在线程之间共享。它所做的只是持有一些互斥锁和日志记录器的原子引用。实际的日志记录会通过具有大小限制的异步通道推送数据。

日志记录器需要一个 Display 类型来提供,以便日志记录器可以实际打印数据。这样做的原因是它减少了调用者的序列化成本,将数字序列化为字符串和其他格式化工作留给了日志记录器。

兼容模式

野外有许多日志记录器,不同的库可能使用不同的日志记录器。为了允许程序开发者从不同的来源进行日志记录而不必就使用哪个日志记录器达成一致,可以通过 [兼容模式] 接口。日志宏与 [兼容模式] 一起工作。

日志级别

日志记录器提供两个日志级别控制:按上下文和“全局”(注意:日志记录器没有全局变量,一切都是局部于日志记录器对象的)。在记录消息时,会检查全局日志级别,如果当前消息的优先级低于全局日志级别,则不会将任何内容发送到日志记录器。

一旦日志记录器接收到消息,它会检查上下文到日志级别的内部映射,如果消息的日志级别优先级低于上下文日志级别,则将其丢弃。

我们通常使用辅助函数 tracedebuginfowarnerror,它们分别对应日志级别:255、192、128、64、0。

当关闭 debug_assertions 时禁用跟踪。

注意:错误消息具有优先级 0,日志级别始终为无符号,因此错误消息永远不会被过滤。

示例 - 泛型

请注意,通用日志记录需要在运行时进行间接引用,可能会减慢您的程序。尽管如此,通用日志记录非常受欢迎,因为它易于使用。根据您的需求,有两种方式进行通用日志记录。

use fast_logger::{info, Generic, Logger};

fn main() {
    let logger = Logger::<Generic>::spawn("context");
    info!(logger, "Message {}", "More"; "key" => "value", "three" => 3);
}

其他宏包括[trace!]、[debug!]、[warn!]、[error!]和通用的[log!]。

如果您想将其与静态日志记录混合使用,可以这样做:

use fast_logger::{info, Generic, Logger};

enum MyMsg {
    Static(&'static str),
    Dynamic(Generic),
}

impl std::fmt::Display for MyMsg {
    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
        match self {
            MyMsg::Static(string) => write!(f, "{}", string),
            MyMsg::Dynamic(handle) => handle.fmt(f),
        }
    }
}

impl From<Generic> for MyMsg {
    fn from(f: Generic) -> Self {
        MyMsg::Dynamic(f)
    }
}

fn main() {
    // Setup
    let logger = Logger::<MyMsg>::spawn("context");
    info!(logger, "Message {}", "More"; "key" => "value", "three" => 3);
}

静态日志记录示例

以下是一个仅使用静态日志记录的示例,宏对此不起作用,因为这些生成的是[通用]。任何实现了[Into]接口的类型都可以被接受到日志函数中。这是日志记录的一种快速方式,因为它没有使用[Box]。

use fast_logger::Logger;

// You need to define your own message type
enum MyMessageEnum {
    SimpleMessage(&'static str)
}

// It needs to implement std::fmt::Display
impl std::fmt::Display for MyMessageEnum {
    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
        match self {
            MyMessageEnum::SimpleMessage(string) => write!(f, "{}", string),
        }
    }
}

fn main() {
    // Setup
    let logger = Logger::<MyMessageEnum>::spawn("ctx");

    // Actual logging
    logger.info(MyMessageEnum::SimpleMessage("Hello world!"));

    // Various logging levels
    logger.trace(MyMessageEnum::SimpleMessage("Hello world!"));
    logger.debug(MyMessageEnum::SimpleMessage("Hello world!"));
    logger.info(MyMessageEnum::SimpleMessage("Hello world!"));
    logger.warn(MyMessageEnum::SimpleMessage("Hello world!"));
    logger.error(MyMessageEnum::SimpleMessage("Hello world!"));
}

带有日志级别的示例

以下是一个设置特定上下文日志级别的示例。

use fast_logger::Logger;

// You need to define your own message type
enum MyMessageEnum {
    SimpleMessage(&'static str)
}

// It needs to implement std::fmt::Display
impl std::fmt::Display for MyMessageEnum {
    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
        match self {
            MyMessageEnum::SimpleMessage(string) => write!(f, "{}", string),
        }
    }
}

fn main() {
    // Setup
    let logger = Logger::<MyMessageEnum>::spawn("ctx");

    // Set the log level of `ctx` to 70, this filters
    // All future log levels 71-255 out.
    assert!(logger.set_context_specific_log_level("ctx", 70));

    // This gets printed, because `warn` logs at level 64 <= 70
    logger.warn(MyMessageEnum::SimpleMessage("1"));

    // This gets printed, because 50 <= 70
    logger.log(50, MyMessageEnum::SimpleMessage("2"));

    // This does not get printed, because !(80 <= 70)
    logger.log(80, MyMessageEnum::SimpleMessage("3"));

    // This gets printed, because the context is different
    logger.clone_with_context("ctx*").log(128, MyMessageEnum::SimpleMessage("4"));
}

仅使用字符串的示例

如果您真的不关心调用者方面的格式化开销,可以直接使用[String]作为消息类型。

use fast_logger::Logger;

fn main() {
    // Setup
    let logger = Logger::<String>::spawn("ctx");

    // Set the log level of `ctx` to 70, this filters
    // All future log levels 71-255 out.
    assert!(logger.set_context_specific_log_level("ctx", 70));

    // This gets printed, because `warn` logs at level 64 <= 70
    logger.warn(format!("1"));

    // This gets printed, because 50 <= 70
    logger.log(50, format!("2"));

    // This does not get printed, because !(80 <= 70)
    logger.log(80, format!("3"));

    // This gets printed, because the context is different
    logger.clone_with_context("ctx*").log(128, format!("4"));
}

非复制数据

非复制数据可能难以传递给日志记录器,为了缓解这个问题,宏中内置了一个克隆指令。

use fast_logger::{info, Generic, InDebug, Logger};

#[derive(Clone, Debug)]
struct MyStruct();

fn main() {
    let logger = Logger::<Generic>::spawn("context");
    let my_struct = MyStruct();
    info!(logger, "Message {}", "More"; "key" => InDebug(&my_struct); clone
    my_struct);
    info!(logger, "Message {}", "More"; "key" => InDebug(&my_struct));
}

嵌套日志记录

有时需要嵌套日志上下文,以下是如何实现的方法。

use fast_logger::{info, Generic, Logger};

fn main() {
    let logger = Logger::<Generic>::spawn("context");
    let submodule = logger.clone_add_context("submodule");
    let mut something = submodule.clone_add_context("something");
    info!(something, "This message appears in the context: context-submodule-something");
}

依赖关系

~1–11MB
~75K SLoC