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