1. 添加 slog_logger.go 实现基于 Go 1.21+ slog 的结构化日志 2. 支持 trace_id、request_id、tenant_id 等标准字段注入 3. 添加日志标准化重构方案文档 推荐使用 Go 内置 log/slog,无需第三方依赖。 Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
4.0 KiB
4.0 KiB
日志标准化重构方案
问题分析
当前状态
- 已实现结构化日志接口 (
internal/pkg/logging/logger.go) - 支持 JSON 格式输出,带 trace_id、span_id、request_id 等标准字段
- 内置敏感字段脱敏
问题
大量代码仍使用标准库 log:
log.Printf("[AUDIT_ERROR] failed to emit audit event: %v", err)
缺陷:
- 非结构化,难以解析和搜索
- 缺少 trace_id,无法关联请求链路
- 缺少标准化字段,grep 难度大
- 无法输出到统一日志收集系统
优化方案
1. 创建 log.Logger 适配器
将标准库 log 重定向到结构化日志:
// logadapter 包:将标准 log 重定向到结构化日志
package logadapter
import (
"log"
"os"
"sync"
)
var (
defaultLogger Logger
mu sync.Mutex
)
// SetLogger 设置全局日志实例
func SetLogger(l Logger) {
mu.Lock()
defer mu.Unlock()
defaultLogger = l
}
// Printf 实现 log.Logger 接口
func (l *stdLoggerAdapter) Printf(format string, v ...interface{}) {
if defaultLogger != nil {
defaultLogger.Info(fmt.Sprintf(format, v...), nil)
}
}
// init 拦截标准库 log
func init() {
log.SetOutput(&stdLoggerAdapter{})
log.SetFlags(0) // 禁用标准库时间戳
}
2. 使用 slog 作为后端(推荐)
Go 1.21+ 内置 log/slog,直接使用:
import "log/slog"
func main() {
// 初始化 slog JSON handler
handler := slog.NewJSONHandler(os.Stdout, &slog.HandlerOptions{
Level: slog.LevelInfo,
})
slog.SetDefault(slog.New(handler))
// 使用 slog
slog.Info("server started", "addr", ":8080")
slog.Error("connection failed", "err", err, "peer", "10.0.0.1")
}
3. 统一字段命名
| 字段 | 标准化名称 | 说明 |
|---|---|---|
| 租户ID | tenant_id |
|
| 用户ID | user_id |
|
| 请求ID | request_id |
HTTP 请求追踪 |
| Trace ID | trace_id |
W3C Trace Context |
| 操作类型 | operation |
如 withdraw, create |
| 耗时 | duration_ms |
毫秒 |
| 状态码 | status_code |
HTTP 状态码 |
| 错误码 | error_code |
业务错误码 |
4. 实施步骤
Phase 1: 基础设施
- 使用
log/slog替代自定义实现 - 配置 JSON handler 输出到 stdout
- 设置默认字段(service_name, version)
Phase 2: HTTP 层改造
- 中间件注入 trace_id、request_id
- Request/Response 日志
- 慢请求日志
Phase 3: Domain 层改造
- 业务操作日志(withdraw, create)
- 错误日志包含 error_code
- 审计事件日志
Phase 4: 清理
- 移除所有
log.Printf - 添加 log level 配置
- 支持日志采样
5. 日志格式规范
JSON 日志条目:
{
"time": "2026-04-13T10:15:30.123Z",
"level": "INFO",
"service": "supply-api",
"trace_id": "abc123",
"request_id": "req-456",
"tenant_id": 1001,
"operation": "withdraw",
"duration_ms": 45,
"status_code": 200,
"message": "withdraw completed"
}
错误日志:
{
"time": "2026-04-13T10:15:30.123Z",
"level": "ERROR",
"service": "supply-api",
"error_code": "SUP_SET_4001",
"error": "withdraw amount exceeds available balance",
"tenant_id": 1001,
"operation": "withdraw",
"stack_trace": "..."
}
6. 迁移检查清单
- 移除
log.Printf替换为结构化日志 - 所有日志包含
trace_id(从 context 提取) - 错误日志包含
error_code - 敏感字段自动脱敏
- 配置 log level 支持动态调整
- Benchmark 测试日志性能影响 < 5%
推荐方案
使用 Go 1.21+ 内置 log/slog,无需引入第三方依赖。
// 推荐配置
handler := slog.NewJSONHandler(os.Stdout, &slog.HandlerOptions{
Level: slog.LevelInfo,
AddSource: false,
ReplaceAttr: func(groups []string, a slog.Attr) slog.Attr {
if a.Key == slog.TimeKey {
a.Value = slog.StringValue(a.Value.Time().Format(time.RFC3339Nano))
}
return a
},
})
slog.SetDefault(slog.New(handler))