secra-logger 3.0.3

一个生产级的 Rust 日志系统库,基于 tracing 生态系统构建,支持结构化 JSON 日志、文件滚动、UTC+8 时区等特性
Documentation
# 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` 添加依赖:

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

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

## 快速开始(最小可运行)

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

```rust
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(())
}
```

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

```rust
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) 显式指定模式(可选)

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

```rust
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`)。

示例:

```bash
RUST_LOG=debug cargo run
```

也可以按模块过滤:

```bash
RUST_LOG=my_app=debug,hyper=warn cargo run
```

### 4) 结构化日志怎么写

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

```rust
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 = true``cfg.log_file = None`
  - `./logs` 目录不存在或无写权限(请先创建目录或确保权限)
  - 进程提前退出:异步写入需要 `WorkerGuard` 存活(本库已在 `Logger` 内部持有;请确保 `logger` 的生命周期覆盖你的运行期)

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

#### 仅控制台(开发环境)

```rust
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(())
}
```

#### 仅文件(生产环境常见)

```rust
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 收集)

```rust
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/文件/行号)

```rust
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:

```rust
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!` 这类宏:

```rust
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`

```rust
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`

```rust
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