## `rulog` 目录解析
`rulog` 是项目的**日志系统层**,基于 `flexi_logger` + `log` 生态构建,提供控制台输出 + 文件持久化的双通道日志能力,同时封装了日志消息构建器 `LogInfo`。
### 文件结构
```
rulog/
├── mod.rs # 模块导出
├── rulog.rs # 核心日志引擎 RuLog + 全局函数
├── rulog_init.rs # RuLog 的 DI 注册(自动生成)
└── loginfo.rs # 日志消息构建器 LogInfo
```
---
### 核心设计:双通道日志
[rulog.rs](file:///E:/soft/gitee.com/ruwebframe/src/rulog/rulog.rs) 实现了一个**控制台 + 文件**双通道日志系统:
```
日志调用
│
├──→ println!() → 控制台输出(即时可见)
│
└──→ log::info!() → flexi_logger → 文件持久化
```
每条日志**同时**输出到控制台和文件,确保开发调试直观、生产环境可追溯。
---
### 日志格式
文件日志的格式为:
```
[2026-08-01 14:30:25.123] [INFO] module::path:10 - 日志消息内容
```
| `时间戳` | 精确到毫秒 `%Y-%m-%d %H:%M:%S%.3f` |
| `日志级别` | INFO / ERROR / DEBUG |
| `模块路径` | Rust 模块路径 `module_path()` |
| `行号` | 源代码行号 `record.line()` |
| `消息` | 日志内容 |
---
### 文件轮转策略
```rust
Logger::try_with_str("info")
.log_to_file(FileSpec::default()
.directory("./logs") // 日志目录
.basename("app") // 文件名前缀
.suffix(".log") // 后缀
)
.rotate(
Criterion::Size(50_000_000), // 50MB 触发轮转
Naming::Numbers, // 旧文件编号:app_r00001.log
Cleanup::KeepLogFiles(5), // 只保留最近 5 个文件
)
```
| 日志目录 | `./logs/` | 相对路径 |
| 文件名 | `app.log` | 当前日志文件 |
| 轮转大小 | 50 MB | 超过后触发轮转 |
| 轮转命名 | 数字编号 | `app_r00001.log`、`app_r00002.log`... |
| 保留数量 | 5 个 | 超出自动删除旧文件 |
---
### 日志级别
默认级别为 `info`,支持三个级别:
| `INFO` | `info()` / `info2()` / `info!()` | 一般信息 |
| `ERROR` | `error()` / `error2()` | 错误信息 |
| `DEBUG` | `debug()` / `debug2()` | 调试信息 |
---
### API 分类
#### 1. 全局函数(推荐,无需实例)
```rust
// 单参数日志
rulog::info("服务启动成功");
rulog::error("连接数据库失败");
rulog::debug("调试信息");
// 双参数拼接日志(msg1 + " " + msg2)
rulog::info2("key:", "value");
rulog::error2("错误:", "详细原因");
rulog::debug2("变量:", "当前值");
```
#### 2. `info!` 宏(类似 `format!` 语法)
```rust
rulog::info!("用户 {} 登录成功,IP: {}", username, ip);
```
内部实现:
```rust
macro_rules! info {
($($arg:tt)*) => {
println!("{}", format!($($arg)*)); // 控制台
log::info!("{}", format!($($arg)*)); // 文件
};
}
```
#### 3. `RuLog` 实例方法(DI 注入)
```rust
impl RuLog {
pub fn info(&self, msg: &str) { println!("{}", msg); }
}
```
`RuLog` 实现了 `BaseEntitySingle`,以单例模式注册在 DI 容器中。
---
### 初始化机制
使用 `OnceLock` 保证日志系统**全局只初始化一次**:
```rust
static CONFIG: OnceLock<String> = OnceLock::new();
fn get_log_config() -> &'static String {
CONFIG.get_or_init(|| {
// flexi_logger 初始化...
Logger::try_with_str("info").unwrap()...
String::from("init log")
})
}
```
每次调用 `info()` / `error()` / `debug()` 时,内部先调用 `get_log_config()`,首次调用完成初始化,后续调用直接返回,零开销。
---
### `LogInfo` — 日志消息构建器
[loginfo.rs](file:///E:/soft/gitee.com/ruwebframe/src/rulog/loginfo.rs) 提供了一个**可拼接的日志消息构建器**:
```rust
pub struct LogInfo {
content: Vec<u8>, // 字节缓冲区
}
```
| `new()` | 创建空构建器 |
| `new_msg(msg)` | 创建并填充消息 |
| `extend(msg)` | 追加消息(链式调用) |
| `to_string_lossy()` | 转为 `String` |
| `merge_multiple(logs, sep)` | 合并多个 `LogInfo` |
使用示例:
```rust
let log = LogInfo::new_msg("错误代码: ")
.extend("500")
.extend(", 原因: ")
.extend("连接超时");
rulog::info(log.to_string_lossy().as_str());
// 输出: "错误代码: 500, 原因: 连接超时"
```
---
### DI 注册
```rust
// 单例模式
direg::register_singleton(SINGLE_BEAN_NAME, RuLog::new);
```
获取方式:`rulog::find_bean_rulog().unwrap()`
但在实际使用中,更推荐直接调用全局函数(`rulog::info()` 等),无需手动获取实例。
---
### 架构定位
```
┌─────────────────────────────────────────────┐
│ rulog (日志层) │
│ │
│ ┌──────────────────────────────────────┐ │
│ │ RuLog (单例) │ │
│ │ - info() / error() / debug() │ │
│ └──────────────────────────────────────┘ │
│ │
│ ┌──────────────────────────────────────┐ │
│ │ 全局函数(推荐使用) │ │
│ │ - info() / info2() / info!() │ │
│ │ - error() / error2() │ │
│ │ - debug() / debug2() │ │
│ └──────────────┬───────────────────────┘ │
│ │ │
│ ┌──────────────┴───────────────────────┐ │
│ │ 双通道输出 │ │
│ │ ┌──────────┐ ┌────────────────┐ │ │
│ │ │println!()│ │ flexi_logger │ │ │
│ │ │ 控制台 │ │ ./logs/app.log │ │ │
│ │ └──────────┘ │ 轮转/压缩/清理 │ │ │
│ │ └────────────────┘ │ │
│ └──────────────────────────────────────┘ │
│ │
│ ┌──────────────────────────────────────┐ │
│ │ LogInfo (消息构建器) │ │
│ │ - new_msg() / extend() / merge() │ │
│ └──────────────────────────────────────┘ │
└─────────────────────────────────────────────┘
↑ 被所有模块依赖
┌────┴────┬────────┬────────┬──────┐
│ rubase │ rucfg │ rucmd │ ... │
└─────────┴────────┴────────┴──────┘
```
---
### 快速参考
```rust
// 简单日志
rulog::info("服务启动");
// 键值对日志
rulog::info2("port:", "8080");
// 格式化日志
rulog::info!("第 {} 次重试,延迟 {}ms", retry, delay);
// 错误日志
rulog::error2("数据库错误:", err.to_string().as_str());
// 调试日志
rulog::debug2("request_id:", "abc-123");
```
**总结**:`rulog` 是一个轻量但完整的日志系统,通过 `flexi_logger` 实现文件日志的格式化、轮转和清理,同时保留控制台即时输出。全局函数 API 简洁易用,`OnceLock` 保证初始化安全,`LogInfo` 提供灵活的消息拼接能力。它是整个项目最底层的横切关注点,被所有模块依赖。