Zerolog不是导入即用的日志库,必须在main第一行调用SetGlobalLevel设级别,提前配置LevelFieldName和TimeFieldFormat,用.With().Timestamp().Logger()注入时间戳,且只输出到stdout/stderr。

Zerolog 不是导入就能用的日志库——不提前设全局级别、不手动注入时间戳、不统一字段命名规范,日志大概率在生产环境静默丢失,或被 Loki/Fluent-bit 当作非法 JSON 丢弃。
zerolog.SetGlobalLevel 必须在第一条日志前调用
常见错误是把 zerolog.SetGlobalLevel(zerolog.InfoLevel) 放在 main() 末尾、HTTP handler 里,甚至配置加载之后。此时全局 logger 已按默认 zerolog.TraceLevel 初始化完毕,再设 ErrorLevel 也拦不住 info 日志输出,更不会让已存在的 logger 实例降级。
- 必须放在
import之后、任何log.Info()或log.Error()调用之前,通常就是func main()的第一行 - 级别只能用常量:
zerolog.DebugLevel、zerolog.WarnLevel,不是字符串"debug" - 开发环境建议设为
zerolog.DebugLevel,生产环境至少设为zerolog.InfoLevel - 别指望运行时热切换——
SetGlobalLevel对已有 logger 实例无效,真要调试得靠环境变量 + 重启
每条日志必须带 level 和 time 字段,否则 Loki 查不到
zerolog 默认 JSON 输出里没有 level 字段,log.Warn() 只是构造器标记;也不自动加 time 字段,导致日志无法按时间排序、告警规则失效。
- 提前设置:
zerolog.LevelFieldName = "level",再配zerolog.LevelFieldMarshalFunc = strings.ToLower让 level 值小写 -
zerolog.TimeFieldFormat = time.RFC3339必须在创建 logger 前设好,否则时间戳是纳秒整数,Loki 解析失败 - 唯一可靠方式是用
.With().Timestamp().Logger()注入时间字段;.Timestamp()单独调用无效 - 禁止在
.Msg()里拼"time: " + time.Now().String()——结构化崩解,字段变字符串,Loki 无法当 label 提取
Err(err) 要真正展开堆栈,得满足两个硬条件
只写 .Str("err", err.Error()) 或 .Msg("failed: " + err.Error()),等于主动放弃可观测性:嵌套错误丢失、无文件行号、Grafana 点不开堆栈。
立即学习“go语言免费学习笔记(深入)”;
- 必须用
.Err(err),它会递归展开 error 链(包括fmt.Errorf("wrap: %w", err)中的 cause) - error 必须实现
Unwrap()或StackTrace(),比如用github.com/pkg/errors或go.opentelemetry.io/otel/codes包裹过的错误 - 启用 stack 字段:在初始化 logger 前加
zerolog.ErrorStackMarshaler = func(err error) interface{} { return stack.Trace().TrimRuntime() }(需引入github.com/rs/zerolog/pkg/errors) -
.Err(nil)会生成"error":{}空对象,不是字符串,不必担心 panic
容器环境只写 os.Stdout / os.Stderr,别碰本地文件
Kubernetes 日志采集链路只认标准输出流。一旦写 /var/log/app.log,等于主动退出可观测体系——目录可能只读、重启即失、无轮转、无锁、无 close 控制。
- 禁止
os.OpenFile("/var/log/app.log", os.O_CREATE|...),哪怕加了defer f.Close()也不行 - 开发时可用
zerolog.ConsoleWriter{Out: os.Stderr}看彩色文本,但上线务必关掉——ConsoleWriter 输出带颜色/缩进,会被 fluent-bit 当作非法 JSON 丢弃 - 静态字段如
service_name、env、version必须在入口统一注入:.Str("service_name", os.Getenv("SERVICE_NAME")),禁止业务代码里重复写 - 字段名必须是顶层 key:
.Fields(map[string]interface{}{"req": req})会把整个结构体转成字符串,Loki 无法索引req.user_id
最易被忽略的是:zerolog 没有“默认安全配置”——缺一不可的四件事(全局级别、level 字段、time 字段、输出目标)少做任何一项,线上日志就可能无声无息地消失。它不报错、不警告,只是 quietly drop。


















