zerolog 1.35.0 日志还在,error 字段却消失了
启用 Stack 且自定义 ErrorStackMarshaler 对无栈错误返回 nil 时,zerolog 1.35.0 会提前结束 Err,连错误文本也漏掉。1.35.1 恢复了后续错误序列化;核对普通错误与带栈错误两条路径。
升级到 zerolog 1.35.0 后,同一条错误日志仍有 level 和 message,却没有 error 字段。如果应用启用了 .Stack(),并且设置过自定义 zerolog.ErrorStackMarshaler,先检查这个函数对“没有调用栈的错误”返回什么。
触发条件是:传给 Err 的错误本身非 nil,但栈编码函数返回 nil。1.35.0 会在这里提前返回,错误文本也随之被跳过。只在日志平台搜索 error 字段,可能因此漏掉已经写出的失败事件。
缺少调用栈,不应顺带删掉错误
例如,应用已有的栈编码函数只认识某种带栈错误。普通 errors.New 或其他库返回的错误没有那种栈结构,编码函数便返回 nil。下面是相关调用形状,logger 和非 nil 的 err 都来自应用:
logger.Error().Stack().Err(err).Msg("save failed")
在 1.35.0 中,Err 先调用栈编码函数。结果为 nil 时直接返回当前事件,后面的 AnErr(ErrorFieldName, err) 没有机会执行。消息还能写出,因为事件本身没有被取消;漏掉的是错误序列化这一步。
这与真正的 Err(nil) 不同。nil 错误原本就不添加错误字段;也不能只看字段名是否叫 error,应用可能通过 ErrorFieldName 改过名称。若自定义 ErrorMarshalFunc 本身选择不输出某种错误,还应单独检查那个函数的契约。
1.35.1 保留无栈错误的文本
正式 v1.35.1 去掉了 nil 分支的提前返回:没有栈内容时继续执行 AnErr,由正常的错误编码函数决定字段内容。采用这一修订版本或已确认包含同项修正的兼容版本,并核对最终构建实际解析到的模块版本。
v1.35.1 的 go.mod 要求 Go 1.23。旧工具链项目应先确认应用及构建环境满足这个要求,不只修改依赖版本号。
暂时不能更新时,可以在已明确不需要栈、或确定该错误没有可用栈的局部调用上不启用 Stack:
logger.Error().Err(err).Msg("save failed")
这会失去该调用的栈输出能力,适合边界明确的临时分支。若 Stack 来自 logger 的公共上下文,仅删掉调用处一个 .Stack() 不一定关闭它;应检查 logger 的创建位置,避免以为已经绕开条件。
不要把公共栈编码函数一律改成返回错误字符串。那会改变所有带栈日志的字段形状,下游查询也可能受影响。应用确实需要自定义兼容处理时,应保留原有栈结构,并明确区分“无栈”与“编码失败”。
同时回查漏字段期间的事件
验证时至少覆盖普通非 nil 错误、原来支持的带栈错误和 nil 错误。普通错误应保留文本,带栈错误应同时保留原栈字段,nil 错误仍遵循原来的省略约定。还应查看原始 JSON,而不只检查采集平台是否接受该行。
修复只影响之后写出的记录,不能补回旧日志中已经缺失的错误文本。排查故障窗口时,可沿保留下来的 message、请求标识与业务失败记录交叉定位;不要把没有 error 字段直接算成没有发生错误。


