zap.NewProduction() 默认不输出Debug日志且以JSON格式输出到os.Stderr,无颜色和换行,易被误判为无输出;调试应改用zap.NewDevelopment()或显式设置日志等级。

zap.NewProduction() 为什么打不出日志到控制台?
默认不输出——zap.NewProduction() 内部用的是 os.Stderr,但会过滤掉 DebugLevel 及以下日志,且默认编码为 JSON,没有颜色、没有换行美化,直接看就是一整行,容易误判“没输出”。
- 调试时改用
zap.NewDevelopment():自动启用彩色、带文件行号、允许Debug()输出 - 若坚持用
NewProduction(),记得显式设置等级:zap.LevelEnablerFunc(func(lvl zapcore.Level) bool { return lvl >= zapcore.InfoLevel }) - JSON 日志里字段名固定(如
"level"、"ts"),别指望它直接打印fmt.Printf那种格式
logrus.Fields 和 zap.SugaredLogger 的字段写法差异
结构化字段不是自由拼字符串,两边语法不兼容,混用会导致字段丢失或 panic。
-
logrus.WithFields(logrus.Fields{"user_id": 123, "action": "login"})→ 字段是 map[string]interface{} -
zap.Sugar().Infow("user login", "user_id", 123, "action", "login")→ 必须成对出现(key, value),数量必须为偶数,否则 panic - zap 原生
zap.Logger更严格:logger.Info("user login", zap.Int("user_id", 123), zap.String("action", "login")),类型必须匹配(zap.Int不能传 string) - logrus 默认支持嵌套结构体序列化;zap 需手动用
zap.Any(),且深度过大会被截断(默认 10 层)
日志异步写入时,进程退出前日志丢失怎么办?
zap 和 logrus 默认都是同步刷盘,加了 zap.AddSync 或 logrus.SetOutput 到文件后,若用 os.Exit(0) 或 panic 后未等待,缓冲区日志就丢了。
使用 uber-go/dig 在 Golang 中实现依赖注入——基于反射容器,提供 Provide/Invoke、dig.In/dig.Out 参数与结果对象,支持命名依赖等
- zap:调用
logger.Sync()或sugar.Sync()强制刷新,建议在defer或main结尾处显式调用 - logrus:没有内置
Sync(),需自己 wrapio.Writer加缓冲并提供 flush 方法,或改用logrus.NewEntry().Logger+Close()(v1.9+ 支持) - 更稳妥的做法是避免用
os.Exit,改用return让 defer 生效;或者用信号监听(如os.Interrupt)触发Sync()后再退出
多 goroutine 写同一个日志实例是否线程安全?
zap.Logger 和 logrus.Logger 都是并发安全的,但要注意初始化后的复用方式。
- zap:全局单例
zap.L()或自定义 logger 实例可直接被所有 goroutine 调用,无需加锁 - logrus:同样线程安全,但
logrus.SetLevel()、logrus.SetFormatter()是全局生效,运行时修改会影响所有地方 - 常见坑:在 handler 里临时改
logrus.Entry的字段(如.WithField("req_id", ...)),没问题;但若在中间件里反复调用logrus.SetOutput(...),会导致竞态和 fd 泄漏 - 生产环境建议初始化一次 logger,通过
With()(logrus)或With()/Named()(zap)派生子 logger,而不是共享可变状态
结构化日志真正难的不是怎么打,而是字段命名要不要统一、错误码要不要标准化、trace_id 怎么透传、日志采样策略怎么配——这些不在库 API 里,得靠团队约定和中间件兜底。
golang免费学习笔记(深入):立即使用
在学习笔记中,你将探索golang的核心概念和高级技巧!










