docs(readme): change readme
This commit is contained in:
390
README.md
390
README.md
@@ -1,164 +1,171 @@
|
|||||||
```mermaid
|
# 🚀 Rust Logger
|
||||||
classDiagram
|
|
||||||
class LogLevel {
|
|
||||||
<<enumeration>>
|
|
||||||
DEBUG
|
|
||||||
INFO
|
|
||||||
WARNING
|
|
||||||
ERROR
|
|
||||||
FATAL
|
|
||||||
}
|
|
||||||
|
|
||||||
class LogRecord {
|
**Rust Logger** 是一个专为微服务架构设计的高性能、异步、可扩展的 Rust 日志库。
|
||||||
+DateTime timestamp
|
|
||||||
+LogLevel level
|
|
||||||
+String message
|
|
||||||
+String target
|
|
||||||
}
|
|
||||||
|
|
||||||
class LogOutput {
|
它基于 `Tokio` 运行时,支持多输出源(控制台、文件、PostgreSQL),内置分布式追踪(TraceID)支持,并提供自动化的日志轮转与过期清理机制。
|
||||||
<<interface>>
|
|
||||||
+write(record: LogRecord) Future
|
|
||||||
}
|
|
||||||
|
|
||||||
class ConsoleOutput {
|
## ✨ 核心特性
|
||||||
+write(record: LogRecord)
|
|
||||||
}
|
|
||||||
|
|
||||||
class PostgresOutput {
|
* **⚡ 全异步非阻塞**:使用 `tokio` 和 `channel` 异步写入,确保业务主线程不受 I/O 阻塞。
|
||||||
-PgPool pool
|
* **🗄️ PostgreSQL 集成**:
|
||||||
+new(pool: PgPool)
|
* 支持 **自动按月分表**(Auto Partitioning),无需人工干预。
|
||||||
+write(record: LogRecord)
|
* 支持 `service_name` 区分多服务。
|
||||||
}
|
* 结构化存储,便于 SQL 审计与分析。
|
||||||
|
* **📄 文件日志管理**:
|
||||||
|
* 支持 **按天 + 按大小 (100MB)** 自动轮转切割。
|
||||||
|
* 支持基于文件名的智能过期删除。
|
||||||
|
* **🔍 分布式追踪 (TraceID)**:
|
||||||
|
* 基于 `Task Local` 的无侵入式上下文传递。
|
||||||
|
* 全链路日志关联,轻松定位并发请求问题。
|
||||||
|
* **🧹 自动清理 (Retention)**:
|
||||||
|
* 后台协程自动清理过期数据库分区和旧日志文件。
|
||||||
|
|
||||||
class FileOutput {
|
## 📂 项目结构
|
||||||
-File file
|
|
||||||
+write(record: LogRecord)
|
|
||||||
}
|
|
||||||
|
|
||||||
class LoggerConfig {
|
|
||||||
+LogLevel min_level
|
|
||||||
+Vec~Box~LogOutput~~ outputs
|
|
||||||
}
|
|
||||||
|
|
||||||
class Logger {
|
|
||||||
-Sender~LogRecord~ tx
|
|
||||||
-LogLevel min_level
|
|
||||||
+init(config: LoggerConfig) Arc~Logger~
|
|
||||||
+log(level, message, target)
|
|
||||||
-run_background_task(rx, outputs)
|
|
||||||
}
|
|
||||||
|
|
||||||
%% Relationships
|
|
||||||
LogOutput <|.. ConsoleOutput : Implements
|
|
||||||
LogOutput <|.. PostgresOutput : Implements
|
|
||||||
LogOutput <|.. FileOutput : Implements
|
|
||||||
|
|
||||||
Logger ..> LogRecord : Creates
|
|
||||||
Logger o-- LoggerConfig : Uses
|
|
||||||
LoggerConfig o-- LogOutput : Aggregates (0..*)
|
|
||||||
LogRecord -- LogLevel : Has
|
|
||||||
```
|
|
||||||
|
|
||||||
```mermaid
|
|
||||||
classDiagram
|
|
||||||
%% 接口定义:强调 Send + Sync 约束
|
|
||||||
class LogOutput {
|
|
||||||
<<interface>>
|
|
||||||
<<Send + Sync>>
|
|
||||||
+write(record: LogRecord) Future
|
|
||||||
}
|
|
||||||
|
|
||||||
%% 具体实现
|
|
||||||
class PostgresOutput {
|
|
||||||
-PgPool pool
|
|
||||||
+write(record)
|
|
||||||
}
|
|
||||||
class ConsoleOutput {
|
|
||||||
+write(record)
|
|
||||||
}
|
|
||||||
|
|
||||||
%% 业务线程持有的 Logger (Producer)
|
|
||||||
class Logger {
|
|
||||||
<<Thread-Safe>>
|
|
||||||
<<Shared via Arc>>
|
|
||||||
-mpsc::Sender~LogRecord~ tx
|
|
||||||
-LogLevel min_level
|
|
||||||
+log(level, msg)
|
|
||||||
}
|
|
||||||
|
|
||||||
%% 后台异步任务 (Consumer)
|
|
||||||
class BackgroundWorker {
|
|
||||||
<<Active Object>>
|
|
||||||
<<Running in tokio::spawn>>
|
|
||||||
-mpsc::Receiver~LogRecord~ rx
|
|
||||||
-Vec~Box~LogOutput~~ outputs
|
|
||||||
+run()
|
|
||||||
}
|
|
||||||
|
|
||||||
%% 数据包
|
|
||||||
class LogRecord {
|
|
||||||
<<Immutable>>
|
|
||||||
+timestamp
|
|
||||||
+level
|
|
||||||
+message
|
|
||||||
}
|
|
||||||
|
|
||||||
%% 关系描述
|
|
||||||
LogOutput <|.. PostgresOutput
|
|
||||||
LogOutput <|.. ConsoleOutput
|
|
||||||
|
|
||||||
%% 关键的线程安全机制:MPSC Channel
|
|
||||||
Logger "1" o-- "1" `mpsc::Sender` : Owns
|
|
||||||
BackgroundWorker "1" o-- "1" `mpsc::Receiver` : Owns
|
|
||||||
|
|
||||||
%% 逻辑流
|
|
||||||
ClientThread ..> Logger : 1. Calls log() (Non-blocking)
|
|
||||||
Logger ..> `mpsc::Sender` : 2. Sends Record
|
|
||||||
`mpsc::Sender` ..> `mpsc::Receiver` : 3. Channel Transfer (Thread-Safe)
|
|
||||||
`mpsc::Receiver` ..> BackgroundWorker : 4. Receives Record
|
|
||||||
BackgroundWorker --> LogOutput : 5. Serialized Writes
|
|
||||||
```
|
|
||||||
|
|
||||||
```text
|
```text
|
||||||
rust_logger/
|
rust_logger/
|
||||||
├── Cargo.toml
|
├── Cargo.toml # 项目依赖配置
|
||||||
|
├── tests/ # 集成测试
|
||||||
├── src/
|
├── src/
|
||||||
│ ├── lib.rs # 库入口,定义宏
|
│ ├── lib.rs # 库入口 (导出宏与全局实例)
|
||||||
│ ├── model.rs # 定义 LogLevel, LogRecord
|
│ ├── core.rs # 核心引擎 (Channel 管理与后台协程)
|
||||||
│ ├── outputs/ # 输出模块
|
│ ├── model.rs # 数据模型 (LogRecord, LogLevel)
|
||||||
│ │ ├── mod.rs # Trait 定义
|
│ ├── context.rs # 链路追踪上下文 (Task Local)
|
||||||
│ │ ├── console.rs # 控制台输出实现
|
│ ├── cleaner.rs # 自动清理任务 (Retention Policy)
|
||||||
│ │ ├── file.rs # 文件输出实现
|
│ └── outputs/ # 输出策略模块
|
||||||
│ │ └── postgres.rs # 数据库输出实现
|
│ ├── mod.rs # LogOutput Trait 定义
|
||||||
│ ├── core.rs # Logger 核心逻辑 (Channel, Spawn)
|
│ ├── console.rs # 控制台输出
|
||||||
│ └── cleaner.rs # 清理输出协程
|
│ ├── file.rs # 文件输出 (含轮转逻辑)
|
||||||
|
│ └── postgres.rs # 数据库输出 (含自动分表逻辑)
|
||||||
```
|
```
|
||||||
|
|
||||||
|
## 📦 安装说明
|
||||||
|
|
||||||
为了展示 **TraceID** 的真正威力,我们将模拟一个 **高并发的 Web 服务场景**。
|
由于本项目托管于内部 Kellnr 仓库,请按照以下步骤配置。
|
||||||
|
|
||||||
在这个场景中,如果不使用 TraceID,多个用户的日志会像“洗牌”一样混在一起,根本无法区分谁是谁。使用了 TraceID 后,我们就能像“抓线头”一样把一次请求的完整路径提出来。
|
### 1. 配置 Registry 源
|
||||||
|
在项目根目录或全局的 `.cargo/config.toml` 中添加:
|
||||||
|
|
||||||
---
|
```toml
|
||||||
|
[registries.kellnr]
|
||||||
|
index = "sparse+https://kellnr.shay7sev.site/api/v1/crates/"
|
||||||
|
```
|
||||||
|
|
||||||
### 第一步:准备工作 (不可跳过)
|
### 2. 添加依赖
|
||||||
|
在你的 `Cargo.toml` 中添加:
|
||||||
|
|
||||||
因为我们在 `LogRecord` 中增加了 `trace_id`,你必须更新数据库表结构,否则程序运行会报错。
|
```toml
|
||||||
|
[dependencies]
|
||||||
|
# 指定 registry 为私有源
|
||||||
|
rust_logger = { version = "0.1.0", registry = "kellnr" }
|
||||||
|
tokio = { version = "1", features = ["full"] }
|
||||||
|
sqlx = { version = "0.7", features = ["postgres", "runtime-tokio-native-tls"] }
|
||||||
|
```
|
||||||
|
|
||||||
**请在 PostgreSQL 中执行:**
|
## 🛠️ 快速开始
|
||||||
|
|
||||||
|
### 1. 数据库准备
|
||||||
|
在使用 PostgreSQL 输出前,请先创建基础表结构:
|
||||||
|
|
||||||
```sql
|
```sql
|
||||||
-- 1. 增加字段
|
CREATE TABLE app_logs (
|
||||||
ALTER TABLE app_logs ADD COLUMN trace_id VARCHAR(64);
|
id BIGSERIAL,
|
||||||
|
service_name VARCHAR(50) NOT NULL,
|
||||||
|
log_level VARCHAR(10) NOT NULL,
|
||||||
|
message TEXT NOT NULL,
|
||||||
|
module VARCHAR(100),
|
||||||
|
trace_id VARCHAR(64), -- 支持 TraceID
|
||||||
|
created_at TIMESTAMPTZ NOT NULL
|
||||||
|
) PARTITION BY RANGE (created_at);
|
||||||
|
|
||||||
-- 2. 建立索引 (查询全靠它)
|
-- 建立索引
|
||||||
|
CREATE INDEX idx_logs_service_time ON app_logs(service_name, created_at);
|
||||||
CREATE INDEX idx_logs_trace_id ON app_logs(trace_id);
|
CREATE INDEX idx_logs_trace_id ON app_logs(trace_id);
|
||||||
```
|
```
|
||||||
|
|
||||||
---
|
### 2. 初始化 Logger
|
||||||
|
|
||||||
### 第二步:编写业务代码 (`order-service/src/main.rs`)
|
```rust
|
||||||
|
use rust_logger::{
|
||||||
|
Logger, LoggerConfig, LogLevel, LogCleaner,
|
||||||
|
outputs::{ConsoleOutput, PostgresOutput, FileOutput},
|
||||||
|
set_global_logger, log_info
|
||||||
|
};
|
||||||
|
use sqlx::postgres::PgPoolOptions;
|
||||||
|
|
||||||
|
#[tokio::main]
|
||||||
|
async fn main() -> anyhow::Result<()> {
|
||||||
|
// 1. 连接数据库
|
||||||
|
let pool = PgPoolOptions::new().connect("postgres://...").await?;
|
||||||
|
|
||||||
|
// 2. 初始化文件输出
|
||||||
|
let file_output = FileOutput::new("logs", "my-service").await?;
|
||||||
|
|
||||||
|
// 3. 配置 Logger
|
||||||
|
let config = LoggerConfig {
|
||||||
|
min_level: LogLevel::INFO,
|
||||||
|
outputs: vec![
|
||||||
|
Box::new(ConsoleOutput),
|
||||||
|
Box::new(PostgresOutput::new(pool.clone())),
|
||||||
|
Box::new(file_output),
|
||||||
|
],
|
||||||
|
};
|
||||||
|
|
||||||
|
// 4. 启动全局 Logger (传入当前服务名)
|
||||||
|
let logger = Logger::init("my-service", config);
|
||||||
|
set_global_logger(logger);
|
||||||
|
|
||||||
|
// 5. (可选) 启动自动清理任务:保留 7 天日志
|
||||||
|
LogCleaner::new(7)
|
||||||
|
.with_db_cleanup(pool)
|
||||||
|
.with_file_cleanup("logs", "my-service")
|
||||||
|
.start();
|
||||||
|
|
||||||
|
// 6. 打印日志
|
||||||
|
log_info!("System started successfully!");
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## 🔍 使用 TraceID 进行链路追踪
|
||||||
|
|
||||||
|
在处理 HTTP 请求或复杂业务时,建议使用 `with_trace_id` 包裹逻辑。
|
||||||
|
|
||||||
|
```rust
|
||||||
|
use rust_logger::context::with_trace_id;
|
||||||
|
use rust_logger::{log_info, log_error};
|
||||||
|
use uuid::Uuid;
|
||||||
|
|
||||||
|
async fn handle_request() {
|
||||||
|
let trace_id = Uuid::new_v4().to_string();
|
||||||
|
|
||||||
|
// 在此闭包内的所有日志都会自动带上 trace_id
|
||||||
|
with_trace_id(trace_id, async {
|
||||||
|
log_info!("收到请求");
|
||||||
|
|
||||||
|
process_payment().await;
|
||||||
|
|
||||||
|
log_info!("请求处理完成");
|
||||||
|
}).await;
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn process_payment() {
|
||||||
|
// 无需手动传递 ID,自动获取上下文
|
||||||
|
log_info!("正在扣款...");
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
**日志输出示例:**
|
||||||
|
```text
|
||||||
|
2026-01-23 10:00:01 [INFO] [a1b2-c3d4...] - 收到请求
|
||||||
|
2026-01-23 10:00:02 [INFO] [a1b2-c3d4...] - 正在扣款...
|
||||||
|
2026-01-23 10:00:03 [INFO] [a1b2-c3d4...] - 请求处理完成
|
||||||
|
```
|
||||||
|
|
||||||
|
<details>
|
||||||
|
|
||||||
|
### 实例:编写业务代码 (`order-service/src/main.rs`)
|
||||||
|
|
||||||
我们需要引入 `uuid` 来生成唯一的 ID。
|
我们需要引入 `uuid` 来生成唯一的 ID。
|
||||||
|
|
||||||
@@ -266,9 +273,7 @@ async fn payment_service(user: &str, amount: i32) {
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
---
|
### 测试:运行与证明
|
||||||
|
|
||||||
### 第三步:运行结果与证明
|
|
||||||
|
|
||||||
运行代码后,我们来看看 **为什么 TraceID 是分布式的救命稻草**。
|
运行代码后,我们来看看 **为什么 TraceID 是分布式的救命稻草**。
|
||||||
|
|
||||||
@@ -304,7 +309,7 @@ async fn payment_service(user: &str, amount: i32) {
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
### 第四步:如何追查问题 (Audit)
|
### 调试:如何追查问题 (Audit)
|
||||||
|
|
||||||
当生产环境出现问题时,正确的排查姿势如下:
|
当生产环境出现问题时,正确的排查姿势如下:
|
||||||
|
|
||||||
@@ -344,8 +349,107 @@ ORDER BY created_at ASC;
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
### 总结 TraceID 的三大好处
|
### 总结:TraceID 的三大好处
|
||||||
|
|
||||||
1. **解耦并发**:在高并发系统中(每秒几千个请求),TraceID 是唯一能把属于同一个请求的日志“聚合”在一起的键。
|
1. **解耦并发**:在高并发系统中(每秒几千个请求),TraceID 是唯一能把属于同一个请求的日志“聚合”在一起的键。
|
||||||
2. **跨越边界**:虽然本例只演示了一个服务,但 TraceID 通常会通过 HTTP Header 传给下游服务(Payment Service, Stock Service)。这样你在 Kibana/Grafana 里搜一个 TraceID,能看到跨越 5 个微服务的完整调用链。
|
2. **跨越边界**:虽然本例只演示了一个服务,但 TraceID 通常会通过 HTTP Header 传给下游服务(Payment Service, Stock Service)。这样你在 Kibana/Grafana 里搜一个 TraceID,能看到跨越 5 个微服务的完整调用链。
|
||||||
3. **无侵入开发**:你的开发人员不需要在写 `payment_service` 时手动传递 `trace_id` 参数,他们只需要写 `log_info!`,库会自动完成剩下的工作。
|
3. **无侵入开发**:你的开发人员不需要在写 `payment_service` 时手动传递 `trace_id` 参数,他们只需要写 `log_info!`,库会自动完成剩下的工作。
|
||||||
|
|
||||||
|
</details>
|
||||||
|
|
||||||
|
## ⚙️ 高级配置
|
||||||
|
|
||||||
|
### 数据库分区策略
|
||||||
|
* 库会自动检测当前月份,若 `app_logs_YYYY_MM` 表不存在,则自动创建。
|
||||||
|
* 无需 DBA 手动维护未来的分区表。
|
||||||
|
|
||||||
|
### 文件轮转策略
|
||||||
|
* **按大小**:单文件超过 100MB 自动重命名归档。
|
||||||
|
* **按日期**:跨天后自动创建新日期的文件。
|
||||||
|
* **文件命名**:`{service_name}-{yyyy-mm-dd}.log`
|
||||||
|
|
||||||
|
## 🤝 贡献指南
|
||||||
|
|
||||||
|
1. Clone 本仓库。
|
||||||
|
2. 在 `tests/` 目录下编写集成测试。
|
||||||
|
3. 提交 Pull Request 前请运行 `cargo test` 确保所有测试通过。
|
||||||
|
|
||||||
|
<details>
|
||||||
|
<summary>👉 点击查看系统架构 UML 图</summary>
|
||||||
|
|
||||||
|
## 🏗️ 架构设计
|
||||||
|
|
||||||
|
本库采用 **异步事件驱动 (Async Event-driven)** 架构,基于 **Active Object** 模式设计。
|
||||||
|
业务线程仅负责将日志通过 `MPSC Channel` 发送,由独立的后台协程负责实际的 I/O 写入,从而实现 **Zero-Blocking**(零阻塞)的业务性能。
|
||||||
|
|
||||||
|
```mermaid
|
||||||
|
classDiagram
|
||||||
|
class LogLevel {
|
||||||
|
<<enumeration>>
|
||||||
|
DEBUG
|
||||||
|
INFO
|
||||||
|
WARNING
|
||||||
|
ERROR
|
||||||
|
FATAL
|
||||||
|
}
|
||||||
|
|
||||||
|
class LogRecord {
|
||||||
|
+String service_name
|
||||||
|
+DateTime timestamp
|
||||||
|
+LogLevel level
|
||||||
|
+String message
|
||||||
|
+String module
|
||||||
|
+Option~String~ trace_id %% 使用 ~ 代替 < >
|
||||||
|
}
|
||||||
|
%% 接口定义:强调 Send + Sync 约束
|
||||||
|
class LogOutput {
|
||||||
|
<<interface>>
|
||||||
|
<<Send + Sync>>
|
||||||
|
+write(record: LogRecord) Future
|
||||||
|
}
|
||||||
|
|
||||||
|
%% 具体实现
|
||||||
|
class PostgresOutput {
|
||||||
|
-PgPool pool
|
||||||
|
+write(record)
|
||||||
|
}
|
||||||
|
class ConsoleOutput {
|
||||||
|
+write(record)
|
||||||
|
}
|
||||||
|
|
||||||
|
%% 业务线程持有的 Logger (Producer)
|
||||||
|
class Logger {
|
||||||
|
<<Thread-Safe>>
|
||||||
|
<<Shared via Arc>>
|
||||||
|
-mpsc::Sender~LogRecord~ tx
|
||||||
|
-LogLevel min_level
|
||||||
|
+log(level, msg)
|
||||||
|
}
|
||||||
|
|
||||||
|
%% 后台异步任务 (Consumer)
|
||||||
|
class BackgroundWorker {
|
||||||
|
<<Active Object>>
|
||||||
|
<<Running in tokio::spawn>>
|
||||||
|
-mpsc::Receiver~LogRecord~ rx
|
||||||
|
-Vec~Box~LogOutput~~ outputs
|
||||||
|
+run()
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
%% 关系描述
|
||||||
|
LogOutput <|.. PostgresOutput
|
||||||
|
LogOutput <|.. ConsoleOutput
|
||||||
|
|
||||||
|
%% 关键的线程安全机制:MPSC Channel
|
||||||
|
Logger "1" o-- "1" `mpsc::Sender` : Owns
|
||||||
|
BackgroundWorker "1" o-- "1" `mpsc::Receiver` : Owns
|
||||||
|
|
||||||
|
%% 逻辑流
|
||||||
|
ClientThread ..> Logger : 1. Calls log() (Non-blocking)
|
||||||
|
Logger ..> `mpsc::Sender` : 2. Sends Record
|
||||||
|
`mpsc::Sender` ..> `mpsc::Receiver` : 3. Channel Transfer (Thread-Safe)
|
||||||
|
`mpsc::Receiver` ..> BackgroundWorker : 4. Receives Record
|
||||||
|
BackgroundWorker --> LogOutput : 5. Serialized Writes
|
||||||
|
```
|
||||||
|
|
||||||
|
</details>
|
||||||
Reference in New Issue
Block a user