Git URL:

1
git://www.github.com/juju/errors.git

quick start

下面是个简单的例子,假如我们的func每次都去向外抛出异常,对于代码跟踪来说说实话很不方便的,所以依靠这个日志很好的定位问题!

1
2
3
4
5
6
7
8
func main() {
err := errors.New("1")
err1 := errors.Trace(err)
err2 := errors.Trace(err1)
err3 := errors.Trace(err2)
stack := errors.ErrorStack(err3)
fmt.Println(stack)
}

输出:

1
2
3
4
/Users/dong/go/code/github/errors-pck/errors/main.go:9: 1
/Users/dong/go/code/github/errors-pck/errors/main.go:10:
/Users/dong/go/code/github/errors-pck/errors/main.go:11:
/Users/dong/go/code/github/errors-pck/errors/main.go:12:

如果在业务中,我们可能靠打印日志的问题,那么就会造成异常日志难以追踪,必须依赖trace_id,还需要依赖上下文传递,所以这里我们假如使用这个error,那么会带来很多方便的功能,就是很好的定位异常。

比如我举个例子:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
import (
"context"
"fmt"
"github.com/juju/errors"
)

// dao
func findUserNameById(ctx context.Context, id uint64) (string, error) {
if id == 0 {
return "", errors.Errorf("not fond user , user_id=%d", id)
}
return "success", nil
}

// service
func GetUserInfo(ctx context.Context, id uint64) (interface{}, error) {
name, err := findUserNameById(ctx, id)
if err != nil {
return nil, errors.Annotate(err, "GetUserInfo error")
}
return map[string]interface{}{
"name": name,
}, nil
}

// controller
func main() {
info, err := GetUserInfo(context.Background(), 0)
if err != nil {
errs := errors.ErrorStack(err)
// todo log
fmt.Println(errs)
return
}
fmt.Println(info)
}

输出

1
2
/Users/dong/go/code/github/errors-pck/errors/main.go:11: not fond user , user_id=0
/Users/dong/go/code/github/errors-pck/errors/main.go:19: GetUserInfo error

所以很轻松的携带了信息,同时很好的记录的异常。如果我们业务去记录大量的日志,对于io的影响很大。

其实对于磁盘来说,单次io的消耗>调用Caller的消耗,同时省去了trace_id 的烦恼!!!!!!

API 介绍

errors.Errorf(format,arg...) / errors.New(msg)

我相信,业务异常中,大部分都是需要打印参数信息的,所以errorf 是很重要的!!

1
2
3
4
5
func Errorf(format string, args ...interface{}) error {
err := &Err{message: fmt.Sprintf(format, args...)}
err.SetLocation(1) // 记录堆栈信息,这里堆栈信息可以通过runtime.Caller(skip) 函数来拿,性能很高,如果skip值越小损耗性能越小
return err
}

errors.Trace(error)

如果你想函数传递过程中记录你这个error抛出的位置,记得调用trace方法!

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
func findUserNameById(ctx context.Context, id uint64) (string, error) {
if id == 0 {
return "", errors.New("find err")
}
return "success", nil
}

func GetUserInfo(ctx context.Context, id uint64) (interface{}, error) {
name, err := findUserNameById(ctx, id)
if err != nil {
return nil, err // 如果把errors.Trace(err)改成err,那么只会打印一层堆栈信息
}
return map[string]interface{}{
"name": name,
}, nil
}

输出:

1
/Users/dong/go/code/github/errors-pck/errors/main.go:11: find err

errors.Annotate(error,msg)

如果我们向外抛出的时候,还想记录一些信息,那么很棒,这个方法就是!!! 是不是很nice!

1
2
3
4
5
6
7
8
9
func GetUserInfo(ctx context.Context, id uint64) (interface{}, error) {
name, err := findUserNameById(ctx, id)
if err != nil {
return nil, errors.Annotate(err, "我要记录一下")
}
return map[string]interface{}{
"name": name,
}, nil
}

输出:

1
2
3
/Users/dong/go/code/github/errors-pck/errors/main.go:11: not fond user , user_id=0
/Users/dong/go/code/github/errors-pck/errors/main.go:19: 我要记录一下

Wrapf(error1, error2, format, args) , Wrap(parent, new) error

wraper 的方式,我感觉使用场景并不多,所以并没有care到多少!

1
2
3
4
5
6
7
8
9
10
11
12
func GetUserInfo(ctx context.Context, id uint64) (interface{}, error) {
name, err := findUserNameById(ctx, id)
if err != nil {
return nil, errors.Wrapf(err, errors.New("GetUserInfo"), "GetUserInfo")
}
return map[string]interface{}{
"name": name,
}, nil
}

/Users/dong/go/code/github/errors-pck/errors/main.go:23: not fond user , user_id=0
/Users/dong/go/code/github/errors-pck/errors/main.go:31: GetUserInfo: GetUserInfo

errors.NewBadRequest(err,msg)errors.IsBadRequest(err)

​ 一些类型判断的函数,比如我异常类型又多个,需要判断,所以就需要这个。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
func main() {
request := errors.NewBadRequest(errors.Errorf("err"), "111")
fmt.Println(errors.IsBadRequest(request))
}
// true

type badRequest struct {
Err
}

func NewBadRequest(err error, msg string) error {
return &badRequest{wrap(err, msg, "")}
}

func IsBadRequest(err error) bool {
err = Cause(err) // 找真正的error,cause我不理解这个函数的地位!
_, ok := err.(*badRequest)
return ok
}

不怕传递空

1
2
3
4
5
6
func main() {
request := errors.NewBadRequest(nil, "111")
fmt.Println(errors.IsBadRequest(request)) // 不会异常
}
// /Users/dong/go/code/github/errors-pck/errors/main.go:64: 111