Golang github.com/pkg/errors
包使用的正确姿势
Golang 的 error 不会像 Java 那样打印 stackTrack 信息。回溯 err 非常不方便。
之前见过比较蠢的的做法是层层 log,写起来贼费劲。
大家应该都知道可以通过 github.com/pkg/errors
这个包来处理 err,WithStack(err)
函数可以打印 stack。
注意,使用 log.Errorf("%+v", err)
才会打印 stackTrack,使用 %v %s
不行。
但是如果多次使用 WithStack(err)
,会将 stack 打印多遍,err 信息可能非常长。像这样:
err_test.go:35: err: normal error
github.com/win5do/golang-microservice-demo/pkg/lib/errx.errMulti
/Users/wufeng/code/codebase/self/gorm-demo/pkg/lib/errx/err_test.go:29
github.com/win5do/golang-microservice-demo/pkg/lib/errx.TestOriginWithStack
/Users/wufeng/code/codebase/self/gorm-demo/pkg/lib/errx/err_test.go:35
testing.tRunner
/usr/local/Cellar/go/1.15.5/libexec/src/testing/testing.go:1123
runtime.goexit
/usr/local/Cellar/go/1.15.5/libexec/src/runtime/asm_amd64.s:1374
github.com/win5do/golang-microservice-demo/pkg/lib/errx.errMulti
/Users/wufeng/code/codebase/self/gorm-demo/pkg/lib/errx/err_test.go:30
github.com/win5do/golang-microservice-demo/pkg/lib/errx.TestOriginWithStack
/Users/wufeng/code/codebase/self/gorm-demo/pkg/lib/errx/err_test.go:35
testing.tRunner
/usr/local/Cellar/go/1.15.5/libexec/src/testing/testing.go:1123
runtime.goexit
/usr/local/Cellar/go/1.15.5/libexec/src/runtime/asm_amd64.s:1374
github.com/win5do/golang-microservice-demo/pkg/lib/errx.errMulti
/Users/wufeng/code/codebase/self/gorm-demo/pkg/lib/errx/err_test.go:31
github.com/win5do/golang-microservice-demo/pkg/lib/errx.TestOriginWithStack
/Users/wufeng/code/codebase/self/gorm-demo/pkg/lib/errx/err_test.go:35
testing.tRunner
/usr/local/Cellar/go/1.15.5/libexec/src/testing/testing.go:1123
runtime.goexit
/usr/local/Cellar/go/1.15.5/libexec/src/runtime/asm_amd64.s:1374
可以看到这个 stack 信息重复了三次。可以人肉去 check 下层有没有使用 WithStack(err)
,如果下层用了上层就不用。但这样会增加心智负担,容易出错。
我们可以在调用是使用一个 wrap 函数,判断一下是否已经执行 WithStack(err)
。
但是 github.com/pkg/errors
自定义的 error 类型 withStack
是私有类型,如何去判断是否已经执行 WithStack(err)
呢?
好在 StackTrace
不是私有类型,所以我们可以使用 interface 的一个小技巧,自己定义一个 interface,如果拥有 StackTrace()
方法则不再执行 WithStack(err)
。 像这样:
type stackTracer interface {
StackTrace() errors2.StackTrace
}
func WithStackOnce(err error) error {
if !stackFlag {
return err
}
_, ok := err.(stackTracer)
if ok {
return err
}
return errors2.WithStack(err)
}
有人可能要问 StackTrace
也是私有类型咋办?那就 fork 然后直接改源码吧。
现在使用这个 wrap 函数打印出来的 stackTrace 就不会重复和冗长。像这样:
err_test.go:21: err: normal error
github.com/win5do/golang-microservice-demo/pkg/lib/errx.WithStackOnce
/Users/wufeng/code/codebase/self/gorm-demo/pkg/lib/errx/err.go:27
github.com/win5do/golang-microservice-demo/pkg/lib/errx.errOnce
/Users/wufeng/code/codebase/self/gorm-demo/pkg/lib/errx/err_test.go:13
github.com/win5do/golang-microservice-demo/pkg/lib/errx.TestWithStackOnce
/Users/wufeng/code/codebase/self/gorm-demo/pkg/lib/errx/err_test.go:19
testing.tRunner
/usr/local/Cellar/go/1.15.5/libexec/src/testing/testing.go:1123
runtime.goexit
/usr/local/Cellar/go/1.15.5/libexec/src/runtime/asm_amd64.s:1374
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。