golang错误处理最佳实践
github.com/pkg/errors

首先需要明确2个概念:

github.com/pkg/errorsgithub.com/pkg/errors原始1

注意,这里有几个重点

  1. 对原始错误做包装
    对原始错误包装时,支持包装“上下文信息”和“当前堆栈”中的任何一个,也可以同时包装进去。
  2. 对包装错误再次包装 对携带有堆栈的包装错误进行再次包装的时候,注意不要再使用WithStack等再次包装堆栈,否则打印的时候相同的堆栈信息会重复打印。
    所以建议对“带有堆栈的包装错误”使用WithMessagef再次包装。

# 一. 常用的错误包装方法

github.com/pkg/errors
errors.Errorf

注意

当前包装位置

项目中推荐的用法:

%+v
%+v

# 二. 创建堆栈错误

创建堆栈错误有2种方法:

  • 使用包装方法包装1个原始错误
    如:
import (
   "testing"
   "fmt"

	stderr "errors"
	"github.com/pkg/errors"
)

......
return errors.WithStack(stderr.New("has err"))  
......
stderr.Newfmt.Errorf"github.com/pkg/errors"
github.com/pkg/errors
import (
   "testing"
   "fmt"

	stderr "errors"
	"github.com/pkg/errors"
)

......
return errors.New("has err")
......
errors.Errorf

建议将当前函数的所有入参以及被调用函数的入参都包装到错误上下文中,以方便排查问题。

# 三. 打印错误

1行

# 四. 示例代码

import (
   "database/sql"
   "fmt"

   "github.com/pkg/errors"
)

func foo() error {
   return errors.Wrap(sql.ErrNoRows, "foo failed")
}

func bar() error {
   return errors.WithMessage(foo(), "bar failed")
}

func main() {
   err := bar()
   if errors.Cause(err) == sql.ErrNoRows {
      fmt.Printf("data not found, %v\n", err)
      fmt.Printf("%+v\n", err)
      return
   }
   if err != nil {
      // unknown error
   }
}
/*Output:
data not found, bar failed: foo failed: sql: no rows in result set
sql: no rows in result set
foo failed
main.foo
    /usr/three/main.go:11
main.bar
    /usr/three/main.go:15
main.main
    /usr/three/main.go:19
runtime.main
    ...
*/