错误处理:gerror 创建/包装/Is/Equal 与错误码思路

引言

Go 原生错误只是 error 接口,缺少堆栈与分类信息。GoFrame 的 gerror 在错误上叠加了堆栈、错误码等能力,gcode 提供错误码定义,二者是框架错误处理的基石。

概念

  • 创建错误:gerror.New("...") / gerror.Newf(...),错误自带调用堆栈。
  • 包装错误:gerror.Wrap(err, "...") 保留原始错误并追加一层描述,便于逐层上报。
  • 取根因:gerror.Cause(err) 返回链条最底层的原始错误。
  • 比较错误:gerror.Is(err, target) 沿包装链查找是否包含目标错误;gerror.Equal(err1, err2) 判断两个错误是否相等(比较规则以官方文档 gerror 章节为准)。
  • 错误码:gcode.New(code, message) 定义业务错误码,gerror.NewCode(code, ...) 创建带码错误;框架自身的错误也都带 gcode 错误码。

代码示例

创建与包装:

package main

import (
	"fmt"

	"github.com/gogf/gf/v2/errors/gcode"
	"github.com/gogf/gf/v2/errors/gerror"
)

// 定义业务错误码(建议避开框架内置码段,自行规划编号)
var ErrUserNotFound = gerror.NewCode(gcode.New(10001, "用户不存在"))

func findUser(id int) error {
	if id <= 0 {
		return gerror.New("非法用户 ID")
	}
	return nil
}

func main() {
	err := findUser(0)
	// 包装:保留原错误与堆栈,同时追加上下文
	wrapped := gerror.Wrap(err, "查询用户失败")
	fmt.Println(wrapped) // 打印整条错误链
	fmt.Println(gerror.Stack(wrapped))

	// 取出根因
	fmt.Println(gerror.Cause(wrapped))

	// 与带码错误比较
	err2 := ErrUserNotFound
	fmt.Println(gerror.Is(err2, ErrUserNotFound))
	fmt.Println(gerror.Equal(err2, ErrUserNotFound))
}

在 Web/服务层把错误码带出去,是"统一错误返回"的基础:

func handler() error {
	_, err := findUser(-1)
	if err != nil {
		// 转成带业务错误码的错误,交给统一返回处理
		return gerror.WrapCode(gcode.New(10001, "用户不存在"), err, "查询用户")
	}
	return nil
}

注意点

  • Wrap 而非直接 fmt.Errorf("%v: %w", ...)%w 方式包装,能同时保留 gerror 的堆栈与错误码。
  • gerror.Isgerror.Equal 的判断维度(错误码、消息、链上查找)以官方文档为准,涉及跨包比较时先确认比较对象。
  • 自定义业务错误码建议统一编号段管理(如 1xxxxx 业务、9xxxxx 系统),便于日志与告警归类。
  • 框架内组件返回的错误大多实现了 gerror 接口,可直接用 gerror.Cause 提取底层原因。

小结

gerror 负责"错误长什么样"(堆栈、链、码),gcode 负责"错误怎么分类"。养成"底层 Wrap、上层判断、出口带码"的习惯,错误处理会清晰很多。完整方法清单以官方文档 goframe.org 的 gerror 章节为准。

笔记加载中…