

新闻资讯
技术学院Go 中 error 构造方式决定日志上下文可追溯性:应使用链式包装(%w)、结构化错误类型或自定义 error 实现 Unwrap/LogValue,避免 errors.New 覆盖原始错误,确保日志能提取错误码、堆栈、TraceID 等关键信息。
直接用 errors.New("xxx") 或 fmt.Errorf("xxx") 生成的 error 缺乏结构化字段,导致日志中无法自动提取错误码、请求 ID、堆栈等关键信息。工程中应优先使用带字段的错误类型(如 pkg/errors 已弃用,推荐 github.com/cockroachdb/errors 或原生 errors.Join/fmt.Errorf("%w") 链式包装)。
fmt.Errorf("failed to parse config: %w", err) 保留原始错误链,日志框架(如 zerolog)可通过 errors.Unwrap 逐层提取errors.New 覆盖原始错误,否则堆栈和根本原因丢失ErrorCode、TraceID),定义自定义 error 类型并实现 Error() 和 Unwrap() 方法zerolog 默认不展开 error 的 stack trace,即使调用了 .Stack(),也仅记录当前 goroutine 的调用栈,而非 error 自身携带的 stack(比如由 github.com/cockroachdb/errors 包裹的)。必须显式调用 .Err(err).Stack() 并配合启用 stack capture。
log := zerolog.New(os.Stderr).With().Timestamp().Stack().Logger()
log.Error().Err(err).Msg("handler failed") —— 注意 .Err() 必须在 .Stack() 后调用才有效github.com/cockroachdb/errors,需额外调用 errors.Detail(err) 获取带堆栈的字符串,再手动注入日志字段全局 panic 捕获(如 http.Server.ErrorLog 或中间件中的 recover())拿到的是裸 interface{},不是 error 类型,且原始 request context(含 trace ID、user ID)已不可达。

X-Request-ID)中提取 trace 信息log.Error().Interface("panic", r).Msg("recovered"),而应转换为 error:if p := recover(); p != nil {
var err error
if e, ok := p.(error); ok {
err = e
} else {
err = fmt.Errorf("%v", p)
}
log.Error().Err(err).Str("panic_type", fmt.Sprintf("%T", p)).Msg("panic recovered")
}LevelFatal 或打标 "panic": true 字段,便于告警过滤slog(标准库)默认不解析 error 链,zap 则通过 zap.Error() 自动展开;但两者对自定义 error 的字段提取逻辑不同,混用时易漏关键信息。
slog 记录 error 推荐用 slog.Any("err", err),而非 slog.String("err", err.Error()),前者会触发 LogValue() 方法(若 error 实现了该接口)zap 中用 zap.NamedError("err", err) 可保留 error 名称(如 *json.SyntaxError),比 zap.Error(err) 更利于分类fmt.Errorf("mod: %w", err),避免在某一层转成字符串再包一次,否则 error 链断裂