go 错误处理设计思考

前段时间准备对线上一个golang系统服务进行内部开源,对代码里面的错误处理进行了一波优化。

优化的几个原因:

  • 错误处理信息随意,未分类未定义。看到错误日志不能第一时间定位
  • 错误的日志重复,有时候一个错误经过了好几层,每一层都会记录,导致日志混乱
  • 错误处理不统一,使用不统一,管理也不统一

优化的解决办法:

  • 对错误进行分类,统一定义和使用
  • 每一个错误都有冒泡到包的顶层,处理与记日志。使用方只需定义好自己的信息

实施过程

错误分类:函数级,包模块级,系统api级。

函数级别:

还是采用 err != nil 的形式,并且做一个如下的包装。

模块级别

统一返回到对应的goroutine顶层处理

服务对外级别

适当的code和健名的message

底层错误级别

考虑及时panic,暴露有用信息

以下为代码设计:

点击查看代码
package ferrors

import (
        "fmt"
        "golang.org/x/xerrors"
)

//Errors 新的错误处理方式
type Errors struct {
        Code int64
        Msg  string
}

// Error 输出错误信息
func (e Errors) Error() string {
        return fmt.Sprintf("code: %d msg: %s at: %s", e.Code, e.Msg, "错误位置,堆栈信息,可选")
}

// New 创建自定义错误
func New(code int64, str string, arg ...interface{}) *Errors {
        if len(arg) > 0 {
                str = fmt.Sprintf(str, arg...)
        }
        return &Errors{Code: code, Msg: str}
}

// newErr 创建通用错误
func newErr(code int64, err error) *Errors {
        switch err := err.(type) {
        case *Errors:
                return err
        case nil:
                return &Errors{Code: code, Msg: ""}
        default:
                return &Errors{Code: code, Msg: err.Error()}
        }
}
func NewErrNotFound(err error) error {
        return newErr(CodeMkNotFound, err)
}

// ErrorEcho example:使用 error wrapping
func ErrorEcho(err error) string {

        return fmt.Sprintf("the error %w", err)
}

//ErrorDump example: xerrors 打印堆栈信息
func ErrorDump() {
        err := foo1()
        fmt.Printf("%v\n", err)
        fmt.Printf("%+v\n", err)
}

var myError = xerrors.New("myerror")

func foo() error {
        return myError
}
func foo1() error {
        return xerrors.Errorf("foo1 : %w", foo())
}