Files
Inbox/Go项目实战/03_基础设施/02_日志/05_目录结构与职责.md
2025-12-11 07:24:36 +08:00

77 lines
8.7 KiB
Markdown
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

---
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 标准库接口。
这个结构完全穷尽了我们在前几轮讨论中确定的技术决策。如果确认无误,我们将在下一步生成具体的代码实现。