答案是:Go中应使用结构化错误替代字符串错误,通过定义含Code、Message、Details、Err的BizError实现可维护的错误体系,结合错误码常量、工厂函数、HTTP映射中间件和结构化日志,提升诊断效率与协作体验。
在 Go 语言中,业务错误不应只是 errors.New("xxx") 或 fmt.Errorf("xxx") 这样的字符串错误。真正可维护、可诊断、可扩展的错误体系,需要结构化封装——核心是定义一个带业务上下文、错误码、原始原因、日志友好字段的自定义 error struct。
推荐一个轻量但足够表达业务语义的结构:
errors.Is/errors.As)实现 Error() 方法返回 Message,同时实现 Unwrap() 返回 Err,即可无缝接入 Go 1.13+ 错误链生态。
避免散落的 magic number 和重复构造。集中定义错误码常量,并提供工厂函数:
user.ErrNotFound =
2001、order.ErrInvalidAmount = 3005
user.NewNotFoundErr(uid) *BizError,内部自动填充 Code/Message/Detailsuser.NewNotFoundErr(uid).Wrap(err),保留原始错误栈
不要在 handler 里手动 switch code。建议用中间件或统一响应封装:
ErrorResponse 结构,含 code(HTTP 状态码)、biz_code(业务码)、message、trace_idResolveBizError(err error) *ErrorResponse 函数,根据 BizError.Code 映射 HTTP 状态码(如 404→404,1001→404,500x→500)return handleError(ctx, err),无需每个地方判断结构化错误天然适配 JSON 日志。关键点:
log.Error("biz failed", "err", bizErr, "details", bizErr.Details)
BizError 中加入 TraceID string 字段,从 context 注入,打通全链路追踪Details 中自动脱敏,或提供 Redact() 方法基本上就这些。不复杂但容易忽略的是:坚持用结构体代替字符串错误、所有错误创建走工厂、HTTP 响应逻辑收口。做下来,排查效率和协作体验会明显提升。