Files
Inbox/Go项目实战/03_基础设施/02_日志/05_目录结构与职责.md

77 lines
8.7 KiB
Markdown
Raw Normal View History

2025-12-11 07:24:36 +08:00
---
tags: []
aliases:
- 目录结构与职责
date created: 星期三, 十二月 10日 2025, 10:45:40 晚上
date modified: 星期三, 十二月 10日 2025, 11:40:48 晚上
---
# 目录结构与职责
## 1. 目录结构设计 (Directory Structure)
该结构旨在实现 **“配置分离”**、**“核心隐藏”** 与 **“上下文桥接”**。
```Plaintext
internal/
├── middleware/ # [Global] 全局中间件层
│ ├── access_log.go # [New] HTTP 请求访问日志 (请求入/出记录, 耗时统计)
│ └── trace.go # [New] 链路追踪 (生成/透传 TraceID -> 注入 Context)
└── pkg/
└── log/ # [Level 0] 全局日志核心包 (基于 Zap)
├── log.go # [Facade] 对外入口 (Init, Global L(), Static Proxies)
├── options.go # [Config] 配置定义 (Level, FilePath, MaxSize)
├── zap.go # [Core] Zap 实例构建 (Encoder, Core, AtomicLevel)
├── writer.go # [IO] 输出源管理 (Lumberjack 轮转, Console/File 双写)
├── context.go # [Bridge] 上下文桥接 (WithContext, TraceID 提取)
└── standard.go # [Schema] 标准字段定义 (Standardized Field Constructors)
```
---
## 2. 文件职责详解 (Responsibilities)
### A. `internal/pkg/log` (核心日志包)
这是一个基础设施包不应依赖任何业务逻辑User, Order 等)。
| **文件名** | **职责描述** | **关键设计点 (Design Decisions)** |
| ----------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| **`log.go`** | **门面 (Facade) 与单例管理**<br>1. 维护私有全局变量 `globalLogger`<br>2. 提供 `Init(opts)` 初始化入口。<br>3. 提供 `L()` 获取底层 `*zap.Logger`<br>4. 提供 `Info/Error` 等静态代理方法。 | **单例兜底**:在 `init()` 中初始化一个默认的 `Nop``Console` Logger防止未初始化调用导致 Panic。<br>**Caller 修正**<br>1. 底层 `globalLogger` 配置 `AddCallerSkip(0)`<br>2. 静态代理方法 (`Info`, `Error`) 内部使用 `WithOptions(AddCallerSkip(1))`<br>3. `L()` 和 `WithContext()` 返回原生 Logger (Skip 0),确保业务层直接调用时行号正确。 |
| **`options.go`** | **配置对象 (DTO)**<br>定义 `Options` 结构体,用于接收 Viper 的配置映射。 | **配置解耦**:只定义 struct不包含逻辑。支持从 `config.yaml``log` 节点自动 Unmarshal。 |
| **`zap.go`** | **核心构建工厂 (Factory)**<br>负责组装 Encoder (JSON/Console)、Writer 和 Level。<br>实现 `New(opts)` 函数。 | **环境隔离**<br>- Dev: ConsoleEncoder + StackTrace (Warn 级)<br>- Prod: JsonEncoder + StackTrace (Panic 级) |
| **`writer.go`** | **IO 输出管理**<br>封装 `lumberjack.Logger`<br>实现 `zapcore.WriteSyncer` 接口。 | **可靠性**:配置 `Lumberjack``Compress: true``MaxSize: 100MB`。实现 Console + File 的 **Tee (双写)** 模式。 |
| **`context.go`** | **上下文装饰器与播种器 (Decorator & Seeder)**<br>1. `WithContext(ctx)`: 提取 TraceID。<br>2. **[New] `StartBackgroundTrace(ctx)`**: 为后台任务生成并注入根 TraceID。 | **零侵入**:仅通过 `zap.With()` 附加字段,返回 **派生 Logger**,不修改全局 Logger线程安全。 |
| **`standard.go`** | **标准化字段与存取器 (Schema & Accessor)**<br>1. 定义**私有** Context Key 类型,防止碰撞。<br>2. 提供 `WithTraceID(ctx, id)``GetTraceID(ctx)` 公开方法。<br>3. 定义标准字段构造器 (如 `zap.String("trace_id", …)`)。 | **规范约束**<br>- 统一使用 snake_case。<br>- 防止拼写错误 (如 `uid` vs `user_id`)。 |
### B. `internal/middleware` (中间件集成)
这是日志模块与 HTTP 框架 (Gin) 结合的触点。
| **文件名** | **职责描述** | **交互逻辑** |
| ------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------ |
| **`trace.go`** | **链路起点**<br>链路追踪 (生成 TraceID -> **注入标准 Context** -> 挂载回 Gin Request) | **上下游打通**:保证 TraceID 在微服务或网关间的透传能力。 |
| **`access_log.go`** | **流量审计**<br>1. 记录 `Start Time`<br>2. 执行 `c.Next()`<br>3. 计算 `Latency`<br>4. 打印结构化日志。 | **字段映射**<br>`path`, `method`, `status`, `client_ip`, `latency`, `user_agent`。**必须使用 `log.WithContext(c)`**。 |
| `recovery.go` | 结构化灾难恢复。<br>1. `defer recover()` 捕获 Panic。<br>2. 获取 Stack Trace。<br>3. **调用 `pkg/log` 记录 JSON 格式的 Error 日志** (包含 `stack` 字段)。<br>4. 返回 500 响应。 | **替代 Gin 默认组件**:必须使用 `gin.New()` 启动,手动注册此中间件,杜绝默认的控制台文本打印。 |
---
## 3. 数据流转图示 (Data Flow)
为了确保你理解“上下文”是如何流动的,以下是逻辑路径:
1. **Request In** -> `middleware/trace.go` -> 生成 `trace_id` -> **Wrap 进 `std.Context`**。 …
2. **`pkg/log/context.go`** -> 从 **`std.Context`** 取出 `trace_id` -> …
3. **`pkg/log/context.go`** -> 从 `gin.Context` 取出 `trace_id` -> 创建带字段的 `zap.Logger`
4. **`pkg/log/zap.go`** -> 序列化为 JSON `{…"trace_id":"xyz"…}`
5. **`pkg/log/writer.go`** -> 写入 `app.log` 文件 (由 Lumberjack 轮转)。
## 4. 依赖关系检查 (Dependency Check)
- `pkg/log` **不依赖** `middleware` (防止循环依赖)。
- `middleware` **依赖** `pkg/log` (调用日志打印)。
- `pkg/log` **仅依赖** `uber-go/zap`, `natefinch/lumberjack`。**严禁依赖** `gin` 或其他 Web 框架。所有 Context 操作均基于 Go 标准库接口。
这个结构完全穷尽了我们在前几轮讨论中确定的技术决策。如果确认无误,我们将在下一步生成具体的代码实现。