Golang自定义error类型_实现error接口添加更多错误信息

2次阅读

go自定义Error必须实现error()方法,否则if err != nil不生效;还需实现unwrap()和is()以支持errors.as()和errors.is(),并注意json序列化字段导出与标签。

Golang自定义error类型_实现error接口添加更多错误信息

Go里自定义error类型必须实现Error()方法

Go的error接口就一个方法:Error() String。只要你的结构体实现了它,就是合法的error。别想绕过这个——不实现它,哪怕字段再丰富,if err != nil也永远进不去分支。

常见错误是只加了字段、忘了写Error(),或者返回空字符串,结果日志里打印出来是<nil></nil>或空白,排查时一头雾水。

  • 必须返回有意义的字符串,通常拼接关键字段(比如codemessagetraceID
  • 不要在Error()里做耗时操作(如调用fmt.Sprintf拼接大量上下文),影响错误路径性能
  • 如果需要携带原始错误,用Unwrap()方法(Go 1.13+),否则上层调用errors.Is()errors.As()会失败
type AppError struct {     Code    int     Message string     TraceID string     Err     error // 原始底层错误 } func (e *AppError) Error() string {     return fmt.Sprintf("code=%d msg=%s trace=%s", e.Code, e.Message, e.TraceID) } func (e *AppError) Unwrap() error { return e.Err }

信息的error要用fmt.Errorf%werrors.Join

单纯实现Error()没法自动捕获调用栈。想让panic或日志里看到出错位置,得靠fmt.Errorf%w动词包装,或者用errors.Join组合多个错误。

典型坑:自己写了Error()方法,又手动调用fmt.Errorf("wrap: %v", err)但没加%w,导致errors.Is()失效,且堆栈断在包装处,不是原始出错点。

立即学习go语言免费学习笔记(深入)”;

  • fmt.Errorf("failed to read: %w", ioErr) → 保留原始堆栈和可判定性
  • fmt.Errorf("failed to read: %v", ioErr) → 堆栈丢失,errors.Is(ioErr)返回false
  • 多个错误合并用errors.Join(err1, err2),比拼接字符串更利于下游解析

区分业务错误和系统错误:用errors.Is判断而不是==

自定义error类型一旦涉及多实例(比如不同CodeAppError),就不能用==比较。因为每次&AppError{Code: 404}都是新地址,err == someErr永远为false

正确方式是给错误类型定义导出的变量(哨兵错误),或用errors.Is()配合Is()方法。

  • 定义哨兵:var ErrNotFound = &AppError{Code: 404, Message: "not found"}
  • 实现Is(target error) bool方法,支持按Code或语义匹配
  • 调用方统一用errors.Is(err, ErrNotFound)errors.Is(err, context.Canceled)
func (e *AppError) Is(target error) bool {     if t, ok := target.(*AppError); ok {         return e.Code == t.Code     }     return false }

JSON序列化自定义error时字段名要对齐json:标签

很多服务把error结构体直接json.Marshal返回给前端,这时字段名大小写、是否导出、json:标签都直接影响输出。常见问题:字段全小写没加json:,结果序列化出来是空对象{};或者用了json:"-" 却忘了去掉敏感字段(比如Err里的内部错误详情)。

  • 所有要暴露的字段必须首字母大写(导出)
  • 显式加json:标签控制键名,避免意外驼峰或下划线
  • 敏感字段(如原始Err、调试用Stack)加上json:"-"或设为小写不导出
  • 测试一下json.Marshal(&AppError{Code: 500})输出是否符合API文档约定

最常被忽略的是Unwrap()Is()这两个方法——它们不写,errors.As()就抽不出你的自定义类型,errors.Is()也判不准。不是“能跑就行”,而是“不用它们,下游根本没法可靠处理”。

text=ZqhQzanResources