# Secra Logger
一个生产级的 Rust 日志系统库,基于 `tracing` 生态系统构建。
## 特性
- ✅ **统一日志入口**:对业务代码透明,只需使用 `tracing::info!` 等宏
- ✅ **结构化日志(JSON)**:所有输出日志为标准 JSON 格式
- ✅ **高可配置性**:日志等级、输出位置、滚动策略可配置
- ✅ **上下文感知**:支持 Span / RequestId / 自定义字段注入
- ✅ **生产可用**:支持文件滚动、异步写入、标准输出
- ✅ **UTC+8 时区**:所有日志时间统一为北京时间
## 快速开始
### 基本使用
```rust
use secra_logger::{init_logging, LogConfig, LogLevel, LogOutput};
use tracing::info;
fn main() {
let config = LogConfig {
level: LogLevel::Info,
output: LogOutput::Stdout,
json: true,
..Default::default()
};
init_logging(config).unwrap();
info!("Hello, world!");
}
```
### 使用环境变量
```rust
use secra_logger::init_from_env;
fn main() {
init_from_env().unwrap();
tracing::info!("Hello, world!");
}
```
设置环境变量:
```bash
export RUST_LOG=info
export LOG_FILE=./logs/app.log
export LOG_DAILY=true
export LOG_JSON=true
```
### 使用 Span 注入上下文
```rust
use tracing::{info, info_span};
let span = info_span!(
"http_request",
request_id = "req-12345",
user_id = "user123",
company_id = "company456"
);
let _enter = span.enter();
info!("处理请求");
```
## 配置选项
### LogConfig
```rust
pub struct LogConfig {
pub level: LogLevel, // 全局默认日志等级
pub modules: HashMap<String, LogLevel>, // 按模块配置日志等级
pub output: LogOutput, // 日志输出配置
pub json: bool, // 是否使用 JSON 格式
pub actix_web: bool, // 是否启用 Actix-Web 集成
pub capture_panic: bool, // 是否捕获 panic 并记录
}
```
### LogOutput
支持三种输出模式:
1. **Stdout**:标准输出
2. **File**:文件输出(支持滚动)
3. **Both**:同时输出到文件和标准输出
```rust
// 文件输出,按天滚动
LogOutput::File {
path: PathBuf::from("./logs/app.log"),
max_size: Some(100 * 1024 * 1024), // 100MB
daily: true,
max_files: Some(7), // 保留 7 天
}
```
## 日志格式
所有日志输出为 JSON 格式,包含以下字段:
```json
{
"timestamp": "2025-01-01T12:00:00.123+08:00",
"level": "INFO",
"target": "my_crate::module",
"message": "something happened",
"span": {
"request_id": "uuid",
"user_id": "optional",
"company_id": "optional"
}
}
```
## 环境变量
- `RUST_LOG`:日志级别(如 `info`, `debug`, `my_crate=debug`)
- `LOG_FILE`:日志文件路径
- `LOG_MAX_SIZE`:最大文件大小(字节)
- `LOG_DAILY`:是否按天滚动(`true` 或 `1`)
- `LOG_MAX_FILES`:最大保留文件数
- `LOG_STDOUT`:是否同时输出到标准输出(`true` 或 `1`)
- `LOG_JSON`:是否使用 JSON 格式(`true` 或 `1`,默认 true)
- `LOG_ACTIX_WEB`:是否启用 Actix-Web 集成(`true` 或 `1`)
## 示例
查看 `examples/` 目录下的示例:
- `basic.rs`:基本使用
- `file_output.rs`:文件输出配置
- `env_config.rs`:环境变量配置
- `file_deletion_test.rs`:文件删除测试(演示运行过程中删除日志文件后的行为)
- `size_rotation.rs`:日志大小滚动示例(演示当日志文件超过指定大小时如何触发滚动)
运行示例:
```bash
cargo run --example basic
cargo run --example file_output
RUST_LOG=debug cargo run --example env_config
cargo run --example file_deletion_test
cargo run --example size_rotation
```
## 模块结构
- **config**:日志配置解析与校验
- **formatter**:JSON 格式化逻辑
- **layer**:tracing Layer 组合
- **time**:UTC+8 时间处理
- **init**:日志系统初始化入口
## 注意事项
1. **初始化只能调用一次**:重复初始化会返回错误
2. **文件滚动**:使用 `tracing-appender` 处理文件滚动,支持按天滚动
3. **Panic 捕获**:默认启用 panic hook,自动记录 panic 信息
4. **时区**:所有日志时间统一为 UTC+8(北京时间)
5. **Doctest 环境**:如果在运行示例时看到"日志系统已被其他代码初始化"的警告,这是因为 doctest 在编译时已经初始化了 subscriber。在实际生产环境中,请确保只初始化一次日志系统,配置会正常生效。
### 解决 Doctest 初始化问题
如果在运行示例时遇到初始化警告,这是因为 doctest 在编译时已经初始化了 subscriber。由于 tracing 的设计限制,无法强制替换已存在的 subscriber。
**解决方案**:
1. **使用 `allow_already_initialized` 选项**(仅用于测试/示例):
```rust
let config = LogConfig {
allow_already_initialized: true, ..Default::default()
};
```
注意:即使设置了此选项,配置也可能不会生效,因为会使用已存在的 subscriber。
2. **在生产环境中**:确保在程序开始时只初始化一次日志系统,这样所有配置都会正常生效。
3. **使用独立的测试二进制文件**:创建独立的测试程序,而不是使用示例,避免 doctest 的影响。
4. **在程序开始时初始化**:确保在程序的最开始就初始化日志系统,避免其他代码先初始化。
## License
MIT