secra-logger

Crates.iosecra-logger
lib.rssecra-logger
version3.0.3
created_at2025-12-29 04:14:21.352312+00
updated_at2026-01-20 09:22:25.466191+00
description一个生产级的 Rust 日志系统库,基于 tracing 生态系统构建,支持结构化 JSON 日志、文件滚动、UTC+8 时区等特性
homepage
repositoryhttps://github.com/BadassFree/secra-logger
max_upload_size
id2009935
size46,834
(BadassFree)

documentation

https://docs.rs/secra-logger

README

Secra Logger

基于 tracing 生态的日志库,主打 自适应模式

  • 库模式(Library):检测到外部已经初始化了全局 subscriber(例如应用已有自己的 tracing_subscriber),本库不再初始化,只提供统一的配置结构与初始化入口(幂等、零侵入)。
  • 应用模式(Application):检测到当前进程还没初始化 subscriber,本库负责初始化 tracing_subscriber,并按配置安装 console/file 输出。

你既可以在“最终应用”里用它来统一初始化,也可以在“公共库/SDK”里用它而不破坏上层应用已有的日志体系。

特性

  • ✅ 基于 tracing / tracing-subscriber
  • 自动检测并选择“库模式 / 应用模式”
  • ✅ 支持控制台输出(human/json,可选颜色、target、文件与行号)
  • ✅ 支持文件输出(human/json,异步 non-blocking writer)
  • ✅ 支持通过 RUST_LOG 控制过滤规则(优先级更高)
  • ✅ 支持 log crate 桥接(tracing_log::LogTracer

安装

Cargo.toml 添加依赖:

[dependencies]
secra-logger = "3"
tracing = "0.1"

说明:Rust 不会因为你依赖了 secra-logger 就自动让你在代码里使用 tracing::info!;如果你要写 tracing 宏,请显式添加 tracing 依赖(如上所示)。

快速开始(最小可运行)

下面示例在“未初始化 subscriber 的应用”中,会走 应用模式 并把日志输出到控制台:

use secra_logger::{LogConfig, Logger};

fn main() -> secra_logger::Result<()> {
    let mut logger = Logger::new(LogConfig::default())?;
    logger.init()?;

    tracing::info!("应用启动");
    tracing::warn!(user_id = 42, "演示结构化字段");
    Ok(())
}

输出到文件(同时保留控制台)

use secra_logger::{LogConfig, LogFormat, Logger, LogRotationConfig, RotationStrategy};
use std::path::PathBuf;

fn main() -> secra_logger::Result<()> {
    let mut cfg = LogConfig::default();

    // 控制台
    cfg.console_output = true;
    cfg.console_format = LogFormat::Human;

    // 文件
    cfg.file_output = true;
    cfg.log_file = Some(PathBuf::from("./logs/app.log"));
    cfg.file_format = LogFormat::Json;
    cfg.rotation = LogRotationConfig {
        strategy: RotationStrategy::Daily,
        max_files: 7,
    };

    let mut logger = Logger::new(cfg)?;
    logger.init()?;

    tracing::info!("这条会输出到 console + file");
    Ok(())
}

注意:当前文件输出使用 tracing-appender 的 rolling appender。文件名基准来自 log_file 的 file_stem(例如 ./logs/app.log 会以 app 为基准名)。

使用教程(详细)

1) 先理解“自适应模式”在你项目里的含义

  • 最终应用(binary/service):通常推荐直接 Logger::new(cfg)?.init()?; 作为启动时的统一入口。
  • 公共库/SDK(被别人依赖):也可以调用 Logger::new(cfg)?.init()?;,但如果上层应用已经初始化了 subscriber,本库会自动退化为 库模式,不会覆盖上层配置。

2) 显式指定模式(可选)

如果你不想依赖自动检测(例如测试里行为要固定),可以显式指定:

use secra_logger::{LogConfig, Logger, LoggingMode};

fn main() -> secra_logger::Result<()> {
    let mut logger = Logger::new_with_mode(LogConfig::default(), LoggingMode::Application);
    logger.init()?;
    tracing::info!("强制 Application 模式");
    Ok(())
}

3) 日志级别与过滤规则(RUST_LOG 优先)

初始化时会创建 EnvFilter

  • 如果设置了环境变量 RUST_LOG:使用 RUST_LOG 的规则(优先级更高)。
  • 否则:使用 LogConfig.level 作为默认 directive(例如 INFO)。

示例:

RUST_LOG=debug cargo run

也可以按模块过滤:

RUST_LOG=my_app=debug,hyper=warn cargo run

4) 结构化日志怎么写

本库不改变 tracing 的用法,直接用 tracing 宏即可:

tracing::info!(
    user_id = 123,
    action = "login",
    duration_ms = 150,
    "用户登录成功"
);

5) 文件轮转策略(重要限制)

配置项在 LogConfig.rotation.strategy

  • RotationStrategy::Daily:按天轮转(真实实现)
  • RotationStrategy::Size(usize)当前会降级为按小时轮转(因为 tracing-appender 没有内建按大小轮转)

同时 LogRotationConfig.max_files 目前是 保留字段

  • tracing-appender 当前不负责自动清理旧文件
  • 该字段用于兼容/未来扩展(不会自动删除旧日志)

6) 初始化幂等与常见坑

  • Logger::init() 是幂等的:同一个 Logger 重复调用不会重复初始化。
  • 应用模式 下内部使用 try_init():如果进程里其它地方已经先 init() 过 subscriber,本次不会覆盖(不会 panic)。
  • 文件不生成常见原因:
    • cfg.file_output = truecfg.log_file = None
    • ./logs 目录不存在或无写权限(请先创建目录或确保权限)
    • 进程提前退出:异步写入需要 WorkerGuard 存活(本库已在 Logger 内部持有;请确保 logger 的生命周期覆盖你的运行期)

7) 常用配置模板(直接复制)

仅控制台(开发环境)

use secra_logger::{LogConfig, LogFormat, Logger};

fn main() -> secra_logger::Result<()> {
    let mut cfg = LogConfig::default();
    cfg.console_output = true;
    cfg.console_format = LogFormat::Human;
    cfg.console_colors = true;
    cfg.file_output = false;

    let mut logger = Logger::new(cfg)?;
    logger.init()?;
    tracing::debug!("仅控制台输出");
    Ok(())
}

仅文件(生产环境常见)

use secra_logger::{LogConfig, LogFormat, Logger, LogRotationConfig, RotationStrategy};
use std::path::PathBuf;

fn main() -> secra_logger::Result<()> {
    let mut cfg = LogConfig::default();
    cfg.console_output = false;
    cfg.file_output = true;
    cfg.log_file = Some(PathBuf::from("./logs/app.log"));
    cfg.file_format = LogFormat::Json;
    cfg.rotation = LogRotationConfig {
        strategy: RotationStrategy::Daily,
        max_files: 7,
    };

    let mut logger = Logger::new(cfg)?;
    logger.init()?;
    tracing::info!("仅文件输出(JSON)");
    Ok(())
}

控制台 JSON(便于容器 stdout 收集)

use secra_logger::{LogConfig, LogFormat, Logger};

fn main() -> secra_logger::Result<()> {
    let mut cfg = LogConfig::default();
    cfg.console_output = true;
    cfg.console_format = LogFormat::Json;
    cfg.console_colors = false; // JSON 通常不需要颜色
    cfg.file_output = false;

    let mut logger = Logger::new(cfg)?;
    logger.init()?;
    tracing::info!(service = "api", "stdout JSON");
    Ok(())
}

精简控制台元信息(不显示 target/文件/行号)

use secra_logger::{LogConfig, Logger};

fn main() -> secra_logger::Result<()> {
    let mut cfg = LogConfig::default();
    cfg.console_show_target = false;
    cfg.console_show_file = false;
    cfg.console_show_line = false;

    let mut logger = Logger::new(cfg)?;
    logger.init()?;
    tracing::info!("更干净的控制台输出");
    Ok(())
}

8) 与现有 tracing_subscriber 共存(库/框架集成)

如果你的应用已经自己初始化了 subscriber(比如为了加上其它 layer),本库会自动检测并进入 库模式,不会覆盖你已有的全局 subscriber:

use secra_logger::{LogConfig, Logger};
use tracing_subscriber::util::SubscriberInitExt;

fn main() -> secra_logger::Result<()> {
    // 应用自己的 subscriber
    tracing_subscriber::fmt().with_env_filter("info").finish().init();

    // secra-logger 将检测到已有 subscriber -> Library 模式(不会覆盖)
    let mut logger = Logger::new(LogConfig::default())?;
    logger.init()?;

    tracing::info!("仍然由应用自己的 subscriber 负责输出");
    Ok(())
}

9) 使用 log crate 宏(兼容老代码)

本库在初始化时会尝试安装 tracing_log::LogTracer(把 log 事件桥接到 tracing)。如果你的项目使用 log::info! 这类宏:

use secra_logger::{LogConfig, Logger};

fn main() -> secra_logger::Result<()> {
    let mut logger = Logger::new(LogConfig::default())?;
    logger.init()?;

    log::info!("通过 log 宏输出(会被桥接到 tracing)");
    Ok(())
}

配置参考(对照 src/config.rs

LogConfig

pub struct LogConfig {
    pub level: tracing::Level,

    pub console_output: bool,
    pub console_format: LogFormat,
    pub console_colors: bool,
    pub console_show_target: bool,
    pub console_show_file: bool,
    pub console_show_line: bool,

    pub file_output: bool,
    pub log_file: Option<std::path::PathBuf>,
    pub file_format: LogFormat,
    pub rotation: LogRotationConfig,
}

字段含义:

  • level:仅在未设置 RUST_LOG 时生效,作为默认过滤级别(默认 INFO
  • console_output:是否启用控制台输出(默认 true
  • console_format:控制台格式
    • LogFormat::Human:人类可读
    • LogFormat::Json:JSON
  • console_colors:控制台 ANSI 颜色(默认 true
  • console_show_target / file / line:控制台是否展示 target/文件名/行号(默认都为 true
  • file_output:是否启用文件输出(默认 false
  • log_file:文件输出路径(推荐类似 ./logs/app.log)。注意它用于确定输出目录与文件基准名(stem)
  • file_format:文件格式(默认 Json
  • rotation:轮转配置(见下)

LogRotationConfig / RotationStrategy

pub struct LogRotationConfig {
    pub strategy: RotationStrategy,
    pub max_files: usize,
}

pub enum RotationStrategy {
    Daily,
    Size(usize),
}

API 速览

  • Logger::new(config: LogConfig) -> Result<Logger>:创建(自动检测模式)
  • Logger::new_with_mode(config: LogConfig, mode: LoggingMode) -> Logger:创建(强制模式)
  • Logger::init(&mut self) -> Result<()>:初始化(幂等)
  • SubscriberDetector::has_subscriber() -> bool:是否已设置全局 dispatcher
  • SubscriberDetector::detect_mode() -> LoggingMode:检测模式(带缓存)

许可证

MIT License

Commit count: 0

cargo fmt