开发环境必须用zap.newdevelopment(),它默认启用consoleencoder、颜色、换行、完整字段名和caller;线上必须用zap.newproduction(),启用jsonencoder、字段压缩、采样,二者混用会导致日志爆炸或调试困难。

zap 不是“怎么记录”的问题,而是“别用错方式记录”的问题。直接上 zap.NewDevelopment() 或 zap.NewProduction() 能跑,但线上日志爆炸、调试时看不懂、性能掉 3 倍——全因初始化和字段写法错了。
开发环境该用 zap.NewDevelopment() 还是自定义 Config?
必须用 zap.NewDevelopment(),别自己 new 一个 Config 然后设 Encoding: "console"。
-
zap.NewDevelopment()不只是换 encoder,它还默认开启caller(文件+行号)、禁用采样、保留完整字段名("msg"不是"m")、自动换行+颜色——这些是人眼调试必需的 - 手动 Config 设
Encoding: "console"但没配EncoderConfig.EncodeLevel和EncodeTime,结果输出一堆纳秒整数、无颜色、字段缩写,等于白搭 - 更危险的是:有人把
Encoding: "json"和ConsoleEncoder混搭,导致日志既不是人可读也不是机器可解析
生产环境为什么不能用 zap.NewDevelopment()?
不是“不推荐”,是会直接拖垮服务。
- 单条日志体积大 3–5 倍:带完整
caller、不压缩字段、无采样,高频接口下每秒多写几 MB 日志 - inode 耗尽常见原因:
df -i显示 100%,八成是zap.NewDevelopment()被打到线上,生成海量小文件(如按请求 ID 分日志) - CPU 占用异常升高:每次日志都调用
runtime.Caller(),在 QPS > 1k 的服务里,这部分开销能占到日志总耗时 40% 以上 -
zap.NewProduction()默认禁用caller,如真要保留(比如排查第三方 SDK),得显式加zap.AddCaller(),而不是切回 Development
zap.String() 和 zap.Stringer() 到底怎么选?
传 nil 指针时行为完全不同,panic 就发生在这儿。
-
zap.String("key", nil)安全:输出"key":"" -
zap.Stringer("key", (*MyType)(nil))危险:调用(*MyType).String()时 panic,错误信息是invalid memory address or nil pointer dereference - 常见误用:HTTP 查询参数判空后转成
*string,再塞进zap.Stringer—— 应该统一走zap.String;真要格式化复杂对象,先判空,再用zap.Object()或实现MarshalLogObject()
Info 和 Debug 日志的字段构造,为什么影响性能?
字段构造器(如 zap.Int)本身很快,但你写的表达式不一定。
-
logger.Debug("slow", zap.String("sql", fmt.Sprintf("SELECT * FROM t WHERE id=%d", id))):每次调用都执行fmt.Sprintf,不管日志级别是否开启 —— Debug 关闭时也白干 - 正确写法:
logger.Debug("slow", zap.Int("id", id), zap.String("table", "t")),Zap 内部按需格式化,零分配 - 高频路径(如中间件、DB hook)慎用
logger.Debug:即使字段构造安全,Debug 级别默认关闭,但zap.String调用仍发生;不确定要不要记,改用logger.Check(zap.DebugLevel, "msg").Write()显式判断
真正容易被忽略的,是 logger.Sync() 的覆盖范围 —— 它只刷当前 logger 及其所有子 logger(比如 logger.With(zap.String("req_id", id)) 创建的)的缓冲区。如果主流程没调用或 defer 错位置,最后几条日志就永远卡在内存里。
golang免费学习笔记(深入):立即使用
在学习笔记中,你将探索golang的核心概念和高级技巧!











