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

8.7 KiB
Raw Blame History

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() 中初始化一个默认的 NopConsole Logger防止未初始化调用导致 Panic。
Caller 修正
1. 底层 globalLogger 配置 AddCallerSkip(0)
2. 静态代理方法 (InfoError) 内部使用 WithOptions(AddCallerSkip(1))
3. L() 和 WithContext() 返回原生 Logger (Skip 0),确保业务层直接调用时行号正确。
options.go 配置对象 (DTO)
定义 Options 结构体,用于接收 Viper 的配置映射。
配置解耦:只定义 struct不包含逻辑。支持从 config.yamllog 节点自动 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 接口。
可靠性:配置 LumberjackCompress: trueMaxSize: 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)

为了确保你理解“上下文”是如何流动的,以下是逻辑路径:

  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 标准库接口。

这个结构完全穷尽了我们在前几轮讨论中确定的技术决策。如果确认无误,我们将在下一步生成具体的代码实现。