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

残缺代码参考:https://github.com/win5do/go-...