应选用 zap 而非 logrus 或 zerolog:它性能最高、字段语义清晰、生产验证充分,支持 development/production 双模式,避免反射与 fmt.sprintf,强制类型化字段(如 zap.string、zap.int),禁用 zap.any 和敏感信息明文,推荐子 logger 传递上下文字段,并统一团队字段命名规范。

Go 语言原生 log 包不支持结构化日志——它只输出字符串,没法直接解析字段、加 trace_id、对接 ELK 或 Loki。真要结构化,得换库,而且得选对。
用 zap 而不是 logrus 或 zerolog(尤其线上服务)
zap 是目前 Go 生态中性能最高、字段语义最清晰、生产环境验证最充分的结构化日志库。它的 Logger 分为 Development 和 Production 两种构建模式,底层避免反射和 fmt.Sprintf,字段写入是预分配 + 无锁缓冲。
-
logrus默认用fmt.Sprintf拼接,高并发下易成性能瓶颈;字段类型检查弱,log.WithField("id", nil)会 panic -
zerolog零分配设计激进,但 JSON 序列化逻辑耦合深,自定义 Encoder(比如输出 Protobuf 日志)成本高 -
zap的Sugar模式兼容旧习惯(类似log.Printf),Logger模式强制字段键值对,且所有字段必须显式声明类型:zap.String("path", r.URL.Path)、zap.Int("status", w.Status())
初始化时必须区分开发与生产环境
同一份代码,在本地调试和上线后日志行为应不同:开发要可读,生产要可解析、低开销。
- 开发用
zap.NewDevelopment():自动加时间、调用文件行号、颜色高亮,输出到os.Stderr - 生产用
zap.NewProduction():时间用 RFC3339Nano、无颜色、JSON 格式、自动加caller(可关)、日志级别大写("level":"INFO") - 别手动 new
zap.Logger并传一堆EncoderConfig——除非你明确需要定制时间格式或字段名(比如把"level"改成"severity"适配 Google Cloud Logging)
字段命名和类型不能随便写
结构化日志的价值在于后续能被统一查询、聚合、告警。字段名混乱或类型错乱,会导致 Loki 查不到 status=500,或 ES mapping 冲突报错。
- HTTP 请求日志固定字段建议:
zap.String("method", r.Method)、zap.String("path", r.URL.Path)、zap.Int("status", statusCode)、zap.Float64("latency_ms", dur.Seconds()*1000) - 避免用
zap.Any("req", r)—— 它会触发反射序列化,且内容不可控(可能含指针、chan、func) - 敏感字段如
password、token绝对不要打日志;必须记录 token 类型时,只记前 4 位:zap.String("token_prefix", token[:4]) - 错误对象统一用
zap.Error(err),它会自动展开err.Error()和fmt.Sprintf("%+v", err)(如果实现了StackTrace())
中间件里怎么安全传 context 日志字段
HTTP handler 中想给整条请求链路加上 request_id、user_id,不能每个 log.Info 都重复写一遍字段。
- 用
logger.With(zap.String("request_id", rid), zap.String("user_id", uid))创建子 logger,传入 context 或作为 handler 参数 - 别用
context.WithValue塞 logger——这违反 context 设计原则(只传取消/超时/截止时间),且容易泄漏 - 子 logger 是轻量对象,无锁复制,可放心在 goroutine 里用;但注意别长期持有(比如缓存到 map 里),避免内存泄露
- 如果用了 OpenTelemetry,优先用
otel.GetTracerProvider().Tracer(...).Start(ctx, "...")提取 trace_id,并通过zap.String("trace_id", traceID.String())注入日志
结构化日志最难的不是选库或写字段,而是团队共识:字段名要不要下划线(user_id)还是驼峰(userId)、时间字段叫 ts 还是 timestamp、错误堆栈要不要默认开启。这些细节一旦线上跑起来,改起来就牵一发而动全身。
golang免费学习笔记(深入):立即使用
在学习笔记中,你将探索golang的核心概念和高级技巧!











