日志管理
问题
Go 项目应该如何记录日志?zap 和 zerolog 的区别是什么?
答案
结构化日志
生产环境用结构化 JSON 日志,而非 fmt.Println:
{"level":"info","ts":"2024-01-01T00:00:00Z","msg":"user created","userID":123,"latency":"2ms"}
zap(Uber)
import "go.uber.org/zap"
// 生产环境
logger, _ := zap.NewProduction()
defer logger.Sync()
logger.Info("user created",
zap.Int("userID", 123),
zap.Duration("latency", time.Millisecond*2),
)
// 带 Sugar 的便捷 API(性能略低)
sugar := logger.Sugar()
sugar.Infow("user created", "userID", 123, "latency", "2ms")
sugar.Infof("user %d created", 123)
zerolog
import "github.com/rs/zerolog/log"
log.Info().
Int("userID", 123).
Dur("latency", time.Millisecond*2).
Msg("user created")
对比
| 维度 | zap | zerolog | slog (标准库) |
|---|---|---|---|
| 性能 | 极高 | 极高(零分配) | 中等 |
| API | 强类型字段 | 链式调用 | slog.Info("msg", "key", val) |
| 生态 | 最流行 | 轻量 | Go 1.21 内置 |
slog(Go 1.21+ 标准库)
import "log/slog"
slog.Info("user created", "userID", 123, "latency", "2ms")
// 自定义 Handler
logger := slog.New(slog.NewJSONHandler(os.Stdout, &slog.HandlerOptions{
Level: slog.LevelInfo,
}))
slog.SetDefault(logger)
选型建议
- 新项目:先用
slog(标准库),性能不够再换 zap - 高性能:zap 或 zerolog
- 已有项目:不需要迁移,保持一致即可
日志最佳实践
| 实践 | 说明 |
|---|---|
| 结构化 | JSON 格式,方便 ELK 采集 |
| 分级 | Debug/Info/Warn/Error |
| RequestID | 中间件注入,方便追踪 |
| 不记录敏感信息 | 密码、Token 等脱敏 |
| 采样 | 高频日志启用采样,避免打爆磁盘 |
常见面试问题
Q1: 为什么不用 log.Println?
答案:
- 无日志级别(Info/Error 混在一起)
- 非结构化(纯文本难以解析)
- 无字段支持(无法按 userID 过滤)
- 不支持采样和异步写入
Q2: zap 为什么快?
答案:
- 零分配:预分配 buffer,避免
fmt.Sprintf的内存分配 - 强类型字段:
zap.Int("key", val)不需要反射 sync.Pool复用 encoder