zerolog 默认仅输出 tracelevel 及以上日志,infolevel 被静默丢弃;需在首次 log 调用前设置全局级别、时间格式、level 字段名、错误堆栈序列化器,并为自定义 logger 显式指定 level。

为什么 log.Info().Msg("x") 什么都没输出?
因为 zerolog 默认只放行 TraceLevel 及以上日志,而 InfoLevel 被静默丢弃——不报错、不警告、不提示,看起来就像没调用一样。
- 必须在**任何
log.Xxx()调用前**执行zerolog.SetGlobalLevel(zerolog.InfoLevel) - 如果用了自定义 logger(比如
log.Logger = zerolog.New(...).With().Logger()),它**不响应**后续的SetGlobalLevel变更,得在构造时显式传.Level(zerolog.InfoLevel) - 开发时误开
ConsoleWriter,上线却忘了切回os.Stdout:彩色格式会被 fluent-bit / Loki 当作非法 JSON 丢弃
怎么让 Err(err) 真正展开堆栈?
.Err(err) 不是万能的。它只对实现了 Unwrap() 或 StackTrace() 的错误类型生效(如 github.com/pkg/errors、go.opentelemetry.io/otel/codes 包裹过的错误),且需手动启用堆栈序列化。
- 初始化 logger 前加:
zerolog.ErrorStackMarshaler = func(err error) interface{} { return stack.Trace().TrimRuntime() }(需引入github.com/rs/zerolog/pkg/errors) - 禁止在
.Msg()里拼接错误文本,例如.Msg("failed: " + err.Error())—— 这会让error字段变成空对象,结构化日志崩解 - 别用
.Str("err", err.Error())替代.Err(err):丢失嵌套错误链、无文件行号、Grafana 里点不开堆栈
容器环境里哪些写法必须禁用?
Kubernetes 日志采集链路只认 os.Stdout 和 os.Stderr。任何偏离这个路径的写法,等于主动退出可观测体系。
- 禁止写本地文件:
os.OpenFile("/var/log/app.log", ...)—— 容器内目录可能只读、不存在、重启即失,且无轮转、无锁、无 close 控制 - 禁止硬编码 service 字段到业务代码里;应统一在入口初始化:
log.Logger = zerolog.New(os.Stdout).With().Str("service_name", os.Getenv("SERVICE_NAME")).Str("env", os.Getenv("ENV")).Timestamp().Logger() - 字段名必须是顶层 key:
.Fields(map[string]interface{}{"req": req})会把整个req当字符串塞进一个字段,Loki 无法索引其内部字段
时间戳、level 字段为什么没出现?
zerolog 不是“配置完就能用”的日志库。它默认不输出 level 字段、不带时间戳、不校验字段类型,直接照抄示例大概率导致日志查不到、级别被静默丢弃、线上 panic 无声无息。
- 必须显式设置:
zerolog.TimeFieldFormat = time.RFC3339(或zerolog.TimeFormatUnix) - 必须手动注入时间戳:
zerolog.New(w).With().Timestamp().Logger(),否则time字段不会出现 - 必须提前设置 level 字段名:
zerolog.LevelFieldName = "level",否则.Level(zerolog.InfoLevel)生效但字段名是空或默认值 - 静态字段(如
service_name、env)必须在 logger 初始化时通过.With().Str(...).Logger()注入,不能靠运行时补
最容易被忽略的是:所有这些设置(SetGlobalLevel、TimeFieldFormat、LevelFieldName、ErrorStackMarshaler)都必须在第一次 log.Xxx() 调用前完成,顺序错一步,整条链就断了。
golang免费学习笔记(深入):立即使用
在学习笔记中,你将探索golang的核心概念和高级技巧!











