【Rust】使用日志记录利器flexi_logger

Flexi_logger简介

flexi_logger​是一个功能强大且灵活的日志记录库,用于Rust语言的应用程序。它提供了丰富的配置选项和功能,适用于各种日志记录需求,从简单的控制台输出到复杂的文件日志管理。以下是对flexi_logger​的一些关键功能和特性的简介:

主要功能

  • 多种日志目标:支持将日志输出到控制台、文件或同时输出到多个目标
  • 日志文件滚动:支持基于文件大小或时间的日志文件滚动,可以按大小、时间(每天、每小时等)进行切分
  • 日志文件清理:可以配置保留的日志文件数量,自动删除过期的日志文件
  • 日志格式自定义:支持自定义日志格式,可以包含时间戳、日志级别、目标模块等信息
  • 日志级别过滤 :支持设置不同的日志级别(如infowarnerror等),只记录满足级别要求的日志消息
  • 异步日志:支持异步写日志,提高性能
  • 日志复制:可以将日志复制到标准输出(stdout)或标准错误(stderr)

使用示例

创建项目

plaintext 复制代码
cargo new rs_demo

Cargo.toml

toml 复制代码
[package]
name = "rs_demo"
version = "0.1.0"
edition = "2021"

[dependencies]
log = "0.4"
flexi_logger = "0.22"
time = "0.3"

main.rs

rust 复制代码
use log::{trace, debug, info, warn, error};
use flexi_logger::{Duplicate, FileSpec, Logger, WriteMode, Criterion, Naming, Cleanup, detailed_format};

// 定义一个结构体 `Rectangle`
struct Rectangle {
    width: u32,
    height: u32,
}

// 为 `Rectangle` 实现方法
impl Rectangle {
    // 关联函数(类似于静态方法)
    fn new(width: u32, height: u32) -> Rectangle {
        Rectangle { width, height }
    }

    // 实例方法
    fn area(&self) -> u32 {
        self.width * self.height
    }

    // 实例方法
    fn perimeter(&self) -> u32 {
        2 * (self.width + self.height)
    }

    // 修改实例的方法
    fn set_width(&mut self, width: u32) {
        self.width = width;
    }
}

fn main() {
    // 初始化日志记录,配置输出到文件,设置文件大小限制和滚动日志
    let logger = Logger::try_with_str("trace")
        .unwrap()
        .log_to_file(
            FileSpec::default()
                .directory("logs") // 设置日志文件目录
                .basename("app")   // 设置日志文件前缀
                .suffix("log")     // 设置日志文件后缀
        )
        .rotate(
            Criterion::Size(10_000_000), // 设置日志文件大小限制为 10 MB
            Naming::Numbers,             // 使用数字序号进行文件命名
            Cleanup::KeepLogFiles(3),    // 保留最近的 3 个日志文件
        )
        .write_mode(WriteMode::BufferAndFlush) // 设置日志写入模式
        .duplicate_to_stderr(Duplicate::Warn)  // 将警告级别的日志复制到标准错误输出
        .format_for_files(detailed_format)     // 使用详细格式,包含时间戳
        .start()
        .unwrap();

    // 记录不同级别的日志消息
    trace!("This is a trace message.");
    debug!("This is a debug message.");
    info!("This is an info message.");
    warn!("This is a warning message.");
    error!("This is an error message.");

    // 使用关联函数创建一个 `Rectangle`
    let mut rect = Rectangle::new(30, 50);

    // 调用实例方法
    info!("The area of the rectangle is {} square pixels.", rect.area());
    info!("The perimeter of the rectangle is {} pixels.", rect.perimeter());

    // 修改实例的字段
    rect.set_width(40);
    info!("After resizing, the area of the rectangle is {} square pixels.", rect.area());

    // 强制刷新日志缓冲区
    logger.flush();
}

这里的配置包括:

  • 日志文件目录:日志文件将被保存到logs目录中
  • 日志文件前缀:日志文件将以app作为前缀
  • 日志文件后缀:日志文件将以.log作为后缀
  • 日志文件滚动策略:当日志文件大小达到10 MB时,日志文件将滚动,新的日志文件将使用时间戳命名
  • 日志文件保留策略:只保留最近的3个日志文件
  • 日志写入模式:使用缓冲并在写入时刷新
  • 日志复制到标准错误输出:将警告级别的日志消息复制到标准错误输出
  • 日志格式:使用详细格式,包含时间戳

在Rust的log库中,不同的日志宏可以记录不同级别的日志消息。这些日志级别分别是:

  • trace!:最详细的日志信息,通常用于跟踪程序的细节流程
  • debug!:用于调试时的信息,比 trace! 略少,但仍然非常详细
  • info!:一般信息,不含调试细节,适合普通运行时的信息
  • warn!:警告信息,表明某些事情可能会出问题,但程序可以继续运行
  • error!:错误信息,表明发生了严重的问题,程序可能无法继续运行

日志文件数据:

rust 复制代码
[2024-07-17 21:08:07.689886 +08:00] TRACE [rs_demo] src/main.rs:55: This is a trace message.
[2024-07-17 21:08:07.691157 +08:00] DEBUG [rs_demo] src/main.rs:56: This is a debug message.
[2024-07-17 21:08:07.691170 +08:00] INFO [rs_demo] src/main.rs:57: This is an info message.
[2024-07-17 21:08:07.697314 +08:00] WARN [rs_demo] src/main.rs:58: This is a warning message.
[2024-07-17 21:08:07.707785 +08:00] ERROR [rs_demo] src/main.rs:59: This is an error message.
[2024-07-17 21:08:07.707804 +08:00] INFO [rs_demo] src/main.rs:65: The area of the rectangle is 1500 square pixels.
[2024-07-17 21:08:07.707809 +08:00] INFO [rs_demo] src/main.rs:66: The perimeter of the rectangle is 160 pixels.
[2024-07-17 21:08:07.707814 +08:00] INFO [rs_demo] src/main.rs:70: After resizing, the area of the rectangle is 2000 square pixels.

自定义日志的输出格式

使用format_for_files函数可以用于自定义日志的输出格式。定义一个名为custom_format的函数,并将其传递给format_for_files以使用自定义格式记录日志

custom_format中:

  • w:一个实现了Write trait的可写对象,日志消息将被写入这个对象
  • record:一个Record对象,包含了日志记录的详细信息
rust 复制代码
use log::{info};
use flexi_logger::{DeferredNow, Duplicate, FileSpec, Logger, Record, WriteMode, Criterion, Naming, Cleanup};
use std::io::Write;
use time::format_description::well_known::Rfc3339;
use time::OffsetDateTime;

// 定义一个结构体 `Rectangle`
struct Rectangle {
    width: u32,
    height: u32,
}

// 为 `Rectangle` 实现方法
impl Rectangle {
    // 关联函数(类似于静态方法)
    fn new(width: u32, height: u32) -> Rectangle {
        Rectangle { width, height }
    }

    // 实例方法
    fn area(&self) -> u32 {
        self.width * self.height
    }

    // 实例方法
    fn perimeter(&self) -> u32 {
        2 * (self.width + self.height)
    }

    // 修改实例的方法
    fn set_width(&mut self, width: u32) {
        self.width = width;
    }
}

fn custom_format(
    w: &mut dyn Write,
    _now: &mut DeferredNow,
    record: &Record,
) -> std::io::Result<()> {
    let now = OffsetDateTime::now_utc().format(&Rfc3339).unwrap();
    write!(
        w,
        "{} [{}] - {} - {}",
        now,
        record.level(),
        record.target(),
        record.args()
    )
}

fn main() {
    // 初始化日志记录,配置输出到文件,设置文件大小限制和滚动日志
    let logger = Logger::try_with_str("info")
        .unwrap()
        .log_to_file(
            FileSpec::default()
                .directory("logs") // 设置日志文件目录
                .basename("app")   // 设置日志文件前缀
                .suffix("log")     // 设置日志文件后缀
        )
        .rotate(
            Criterion::Size(10_000_000), // 设置日志文件大小限制为 10 MB
            Naming::Numbers,             // 使用数字序号进行文件命名
            Cleanup::KeepLogFiles(3),    // 保留最近的 3 个日志文件
        )
        .write_mode(WriteMode::BufferAndFlush) // 设置日志写入模式
        .duplicate_to_stderr(Duplicate::Warn)  // 将警告级别的日志复制到标准错误输出
        .format_for_files(custom_format)       // 使用自定义格式
        .start()
        .unwrap();

    // 记录不同级别的日志消息
    info!("This is an info message.");

    // 使用关联函数创建一个 `Rectangle`
    let mut rect = Rectangle::new(30, 50);

    // 调用实例方法
    info!("The area of the rectangle is {} square pixels.", rect.area());
    info!("The perimeter of the rectangle is {} pixels.", rect.perimeter());

    // 修改实例的字段
    rect.set_width(40);
    info!("After resizing, the area of the rectangle is {} square pixels.", rect.area());

    // 强制刷新日志缓冲区
    logger.flush();
}
rust 复制代码
2024-07-17T12:28:34.701362Z [INFO] - rs_demo - This is an info message.
2024-07-17T12:28:34.7024068Z [INFO] - rs_demo - The area of the rectangle is 1500 square pixels.
2024-07-17T12:28:34.7024153Z [INFO] - rs_demo - The perimeter of the rectangle is 160 pixels.
2024-07-17T12:28:34.7024181Z [INFO] - rs_demo - After resizing, the area of the rectangle is 2000 square pixels.

验证日志文件大小和保留日志文件个数

下面通过一个无限循环不断地写入日志消息,从而测试日志文件的滚动和清理功能。这种设置在实际应用中可能用于持续记录应用程序的运行状态或调试信息

rust 复制代码
use log::info;
use flexi_logger::{Logger, FileSpec, Criterion, Naming, Cleanup, WriteMode, Duplicate, detailed_format};
use std::thread;
use std::time::Duration;

fn main() {
    // 初始化日志记录,配置输出到文件,设置文件大小限制和滚动日志
    let _logger = Logger::try_with_str("info")
        .unwrap()
        .log_to_file(
            FileSpec::default()
                .directory("logs") // 设置日志文件目录
                .basename("app") // 设置日志文件前缀
                .suffix("log")     // 设置日志文件后缀
        )
        .rotate(
            Criterion::Size(5_000), // 设置日志文件大小限制为 5 KB
            Naming::Timestamps,         // 使用时间戳进行文件命名
            Cleanup::KeepLogFiles(3),   // 保留最近的 3 个日志文件
        )
        .write_mode(WriteMode::BufferAndFlush) // 设置日志写入模式
        .duplicate_to_stderr(Duplicate::Warn)  // 将警告级别的日志复制到标准错误输出
        .format_for_files(detailed_format)     // 使用详细格式,包含时间戳
        .start()
        .unwrap();

    // 无限循环,不断写入日志
    loop {
        info!("This is an info message.");
      
        // 模拟一些工作,等待一段时间
        thread::sleep(Duration::from_secs(1));
    }
}

运行程序数分钟,查看日志文件夹,文件夹目录结构如下,"rCURRENT.log"代表当前程序正在写入的日志文件,剩余的三个日志文件为程序保留的三个日志文件,日志文件都含有时间戳,并且不会随着程序运行的时间延长而新增文件个数

rust 复制代码
├─logs
│      app_r2024-07-17_20-44-24.log
│      app_r2024-07-17_20-45-19.log
│      app_r2024-07-17_20-46-15.log
│      app_rCURRENT.log

日志输出到控制台和文件

使用duplicate_to_stdout(Duplicate::All)方法,将所有级别的日志复制到标准输出(控制台)

rust 复制代码
.duplicate_to_stdout(Duplicate::All)   // 将所有级别的日志复制到标准输出

这样,控制台能看到所有输出到日志文件的日志信息

按时间切分日志文件

Criterion::Age表示按时间切分日志文件,flexi_logger::Age::Day指定了按每天切分日志文件。这样,每天会生成一个新的日志文件

rust 复制代码
Criterion::Age(flexi_logger::Age::Day), // 设置日志文件按日切分
相关推荐
阿祖zu6 分钟前
芝士就是力量!开源私有化部署与 GitHub 双向同步的个人知识笔记 App
前端·后端·ios
yiqiefeimeng9 分钟前
Rust还是Go?编程语言的未来之争
rust·go·编程语言·技术社区·未来之争
不才不才不不才16 分钟前
Spring 源码系列(16): doDispatch 全流程——一次请求的主干链路
java·后端·spring
IT_陈寒23 分钟前
SpringBoot自动配置失效?这个隐藏配置坑了我一整晚
前端·人工智能·后端
搜狐技术产品小编202337 分钟前
告别“黑盒”与误判:如何用“多智能体对抗辩论”重构内容安全审核系统
后端·多agent
DantyWei1 小时前
controller注册及调用时机
后端
edwarddamon1 小时前
Spring Cloud 配置热更新与 Bean 代理机制梳理
java·后端
码事漫谈2 小时前
我用 Seed Evolving 做了个 AI 小说写作工具
后端
Raas1002 小时前
MAIGateway,魔芋企业级AI网关的FinAPI成本竞争力设计
java·后端·网关·api网关·魔芋ai·finapi·mai gateway
DantyWei2 小时前
kube-controller-manager的leader选举流程和策略
后端