Golang 错误码最佳实践 Errcode

Posted by     "冯旭" on Wednesday, May 3, 2023

基于 Golang int 枚举的错误类型生成工具

代码链接:https://github.com/fengxuway/errcode

errcode 是一个附带错误码的 error 生成工具

源代码基于 https://github.com/golang/tools/tree/master/cmd/stringer ,并在此基础上,增加了根据 error 获取错误码的功能。

安装

go install github.com/fengxuway/errcode@latest

用法

编写自定义类型的 int 枚举,并为每个常量添加注释

type Err int

const (
	ErrNotFound Err = iota + 1 // not found
	ErrBadRequest              // bad request
	ErrUnknown                 // unknown error
)

使用 errcode 命令为 Err类型生成 Error() 等 error 接口方法:

errcode -type=Err --linecomment -codefunc=Code -unknowncode=-1

可在同级目录下生成 *_errcode.go源代码文件,内部定义了 String()Error() 方法,和 Code(error) int 函数,用于获取 error 对应的错误码

部分参数含义:

-type: 指定需要生成错误码的枚举类型,同时指定多个用,分隔
--linecomment: 读取常量行级注释,作为 error message 信息
-codefunc: 指定获取错误码的函数名称,默认为 Code
-unknowncode: 当 Code 函数传入的 error 没有匹配时返回的未知错误码,默认-1

完整示例见 https://github.com/fengxuway/errcode/tree/main/example

效果

生成的代码效果大致为:

// Code generated by "errcode -type=Err,Err2 --linecomment -unknowncode=-1 -codefunc=Code"; DO NOT EDIT.

package example

import "strconv"

func _() {
	// An "invalid array index" compiler error signifies that the constant values have changed.
	// Re-run the errcode command to generate them again.
	var x [1]struct{}
	_ = x[ErrPlanIsEmpty-10]
	_ = x[ErrManualCreateFailed-11]
	_ = x[ErrRPCFailed-12]
	_ = x[ErrStageNotMatched-13]
	_ = x[ErrNoAvailable-14]
	_ = x[ErrUnmarshalFailed-15]
	_ = x[ErrGetFailed-17]
	_ = x[ErrPlanNotSupport-18]
	_ = x[ErrDBFailed-19]
}

const (
	_Err_name_0 = "plan is emptymanual create operation failedrpc request failedstage not matchednot availablejson unmarshal failed"
	_Err_name_1 = "get data failedplan not supportdb operation failed"
)

var (
	_Err_index_0 = [...]uint8{0, 13, 43, 61, 78, 91, 112}
	_Err_index_1 = [...]uint8{0, 15, 31, 50}
)

func (i Err) Error() string {
	return i.String()
}

func (i Err) String() string {
	switch {
	case 10 <= i && i <= 15:
		i -= 10
		return _Err_name_0[_Err_index_0[i]:_Err_index_0[i+1]]
	case 17 <= i && i <= 19:
		i -= 17
		return _Err_name_1[_Err_index_1[i]:_Err_index_1[i+1]]
	default:
		return "Err(" + strconv.FormatInt(int64(i), 10) + ")"
	}
}

var _ecCodeMap = map[string]int{
	ErrPlanIsEmpty.String():        int(ErrPlanIsEmpty),
	ErrManualCreateFailed.String(): int(ErrManualCreateFailed),
	ErrRPCFailed.String():          int(ErrRPCFailed),
	ErrStageNotMatched.String():    int(ErrStageNotMatched),
	ErrNoAvailable.String():        int(ErrNoAvailable),
	ErrUnmarshalFailed.String():    int(ErrUnmarshalFailed),
	ErrGetFailed.String():          int(ErrGetFailed),
	ErrPlanNotSupport.String():     int(ErrPlanNotSupport),
	ErrDBFailed.String():           int(ErrDBFailed),
}

func Code(e error) int {
	v, ok := _ecCodeMap[e.Error()]
	if ok {
		return v
	}
	return -1
}

调用效果

var err error = ErrNotFound

# 打印错误信息
fmt.Println(err.Error())

// 打印错误码
fmt.Println(Code(err))