快速开始
go的error,首先没有stack trace,所以使用起来很不方便,而且自己去实现一个又没有必要,本身实现起来很简单!
所以这里介绍一个包 "github.com/juju/errors" 十分小巧的,十分的好用,业务中学会使用会节省很多的代码量。
下面是个简单的例子,假如我们的func每次都去向外抛出异常,对于代码跟踪来说说实话很不方便的,所以依靠这个日志很好的定位问题!
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)
}
输出:
/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,那么会带来很多方便的功能,就是很好的定位异常。
比如我举个例子:
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)
}
输出
/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 是很重要的!!
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方法!
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
}
输出:
/Users/dong/go/code/github/errors-pck/errors/main.go:11: find err
errors.Annotate(error,msg)
如果我们向外抛出的时候,还想记录一些信息,那么很棒,这个方法就是!!! 是不是很nice!
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
}
输出:
/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到多少!
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)
一些类型判断的函数,比如我异常类型又多个,需要判断,所以就需要这个。
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
}
不怕传递空
func main() {
request := errors.NewBadRequest(nil, "111")
fmt.Println(errors.IsBadRequest(request)) // 不会异常
}
// /Users/dong/go/code/github/errors-pck/errors/main.go:64: 111