1
0
Files
nex/openspec/specs/module-logging/spec.md
lanyuanxiaoyao 280099b89c refactor: 后端日志系统重构
- 新增模块化日志器(pkg/logger/module.go)
- 新增 GORM 日志适配器
- 统一日志入口,移除所有 zap.L() 全局 logger 调用
- 字段标准化
- 启动阶段使用结构化日志
- 更新所有相关测试
2026-04-23 18:37:51 +08:00

141 lines
4.4 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
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.
# Module Logging
## Purpose
定义模块化日志器规范,支持模块标识和 Context 传播,实现日志来源快速定位和请求追踪链完整性。
## Requirements
### Requirement: 模块标识注入
系统 SHALL 支持通过构造函数注入带模块标识的 logger。
#### Scenario: 构造函数注入模块 logger
- **WHEN** 创建需要记录日志的组件
- **THEN** 构造函数 SHALL 接受 `*zap.Logger` 参数
- **THEN** SHALL 使用 `logger.Named("module.name")` 绑定模块标识
- **THEN** SHALL 将 logger 存储在结构体字段中
#### Scenario: 模块标识格式
- **WHEN** 绑定模块标识
- **THEN** 单一职责包 SHALL 使用包名(如 `database`
- **THEN** 多实体包 SHALL 使用 `包名.实体名`(如 `handler.proxy`
- **THEN** 子包 SHALL 使用 `包名.子包名`(如 `handler.middleware`
#### Scenario: 模块标识输出
- **WHEN** 记录日志
- **THEN** Console 格式 SHALL 输出为 `[module.name]`
- **THEN** JSON 格式 SHALL 包含 `"logger":"module.name"` 字段
### Requirement: 禁止全局 logger
系统 SHALL 禁止在业务代码中使用全局 logger。
#### Scenario: 移除 zap.L() 调用
- **WHEN** 重构现有代码
- **THEN** SHALL 移除所有 `zap.L()` 调用
- **THEN** SHALL 通过构造函数注入 logger
- **THEN** 允许仅在测试代码中使用 `zap.L()``zap.NewNop()`
#### Scenario: 移除 zap.L() fallback
- **WHEN** 构造函数 logger 参数为 nil
- **THEN** SHALL NOT 使用 `zap.L()` 作为默认值
- **THEN** 调用方 SHALL 必须传入有效的 logger
### Requirement: Request ID 传播
系统 SHALL 通过 Context 传播 request_id。
#### Scenario: Context 注入 request_id
- **WHEN** 中间件生成 request_id
- **THEN** SHALL 存储到 `context.Context`
- **THEN** SHALL 使用类型安全的 context key
#### Scenario: 从 Context 提取 request_id
- **WHEN** 业务层需要记录日志
- **THEN** SHALL 从 `gin.Context` 提取 request_id
- **THEN** SHALL 使用 `logger.With(zap.String("request_id", id))` 创建带 request_id 的 logger
- **THEN** 日志 SHALL 自动包含 request_id 字段
#### Scenario: Request ID 辅助函数
- **WHEN** 使用 request_id
- **THEN** `pkg/logger` SHALL 提供 `RequestIDFromContext(ctx)` 辅助函数
- **THEN** 辅助函数 SHALL 返回 `zap.Field` 类型
### Requirement: 单行输出
系统 SHALL 保证所有日志单行输出。
#### Scenario: Console 单行输出
- **WHEN** 记录日志到 stdout
- **THEN** SHALL 单行输出
- **THEN** 字段之间 SHALL 使用空格分隔
#### Scenario: JSON 单行输出
- **WHEN** 记录日志到文件
- **THEN** SHALL 单行紧凑输出
- **THEN** SHALL NOT 使用美化缩进
#### Scenario: 多行数据保留
- **WHEN** 日志数据本身包含多行如堆栈跟踪、SQL 换行)
- **THEN** SHALL 保留原始多行格式
### Requirement: 模块命名规范
系统 SHALL 遵循模块命名规范。
#### Scenario: Handler 层命名
- **WHEN** 创建 handler 层 logger
- **THEN** ProxyHandler SHALL 使用 `handler.proxy`
- **THEN** ProviderHandler SHALL 使用 `handler.provider`
- **THEN** ModelHandler SHALL 使用 `handler.model`
- **THEN** StatsHandler SHALL 使用 `handler.stats`
- **THEN** Middleware SHALL 使用 `handler.middleware`
#### Scenario: Provider 层命名
- **WHEN** 创建 provider 层 logger
- **THEN** Client SHALL 使用 `provider.client`
#### Scenario: Conversion 层命名
- **WHEN** 创建 conversion 层 logger
- **THEN** Engine SHALL 使用 `conversion.engine`
- **THEN** OpenAI Adapter SHALL 使用 `conversion.openai`
- **THEN** Anthropic Adapter SHALL 使用 `conversion.anthropic`
#### Scenario: Service 层命名
- **WHEN** 创建 service 层 logger
- **THEN** RoutingCache SHALL 使用 `service.routing_cache`
- **THEN** StatsBuffer SHALL 使用 `service.stats_buffer`
- **THEN** ProviderService SHALL 使用 `service.provider`
- **THEN** ModelService SHALL 使用 `service.model`
- **THEN** RoutingService SHALL 使用 `service.routing`
- **THEN** StatsService SHALL 使用 `service.stats`
#### Scenario: Repository 层命名
- **WHEN** 创建 repository 层 logger
- **THEN** ProviderRepository SHALL 使用 `repository.provider`
- **THEN** ModelRepository SHALL 使用 `repository.model`
- **THEN** StatsRepository SHALL 使用 `repository.stats`
#### Scenario: Infrastructure 层命名
- **WHEN** 创建基础设施层 logger
- **THEN** Database SHALL 使用 `database`
- **THEN** Config SHALL 使用 `config`