使用/pkg/errors和golang 1.13格式化动词%w处理错误

u3r8eeie  于 2023-10-14  发布在  Go
关注(0)|答案(2)|浏览(101)

我想用堆栈跟踪注解错误,因此我使用/pkg/errors包。
Go 1.13添加了%w -格式化动词来Wrap errors。
下面的程序不使用%w打印一个很好的堆栈跟踪:
https://play.golang.org/p/eAwMrwqjCWX
以下仅使用%w not稍微修改的程序:
https://play.golang.org/p/am34kdC0E3o
我应该如何将%w与错误 Package 和堆栈跟踪一起使用?

b1payxdu

b1payxdu1#

原因是errors.Errorf删除了新的fundamental错误类型(在pkg/errors中)。如果你看看它的方法Format(s fmt.State, verb rune)(当你执行log.Printf时触发):

func (f *fundamental) Format(s fmt.State, verb rune) {
    switch verb {
    case 'v':
        if s.Flag('+') {
            io.WriteString(s, f.msg)
            f.stack.Format(s, verb)
            return
        }
        fallthrough
    case 's':
        io.WriteString(s, f.msg)
    case 'q':
        fmt.Fprintf(s, "%q", f.msg)
    }
}

所以你可以看到它只打印io.WriteString(s, f.msg)Errorf是:

func Errorf(format string, args ...interface{}) error {
    return &fundamental{
        msg:   fmt.Sprintf(format, args...),
        stack: callers(),
    }
}

因为这条线:fmt.Sprintf(format, args...)你会看到go vet的错误(你不能把%w传递给Sprintf),在错误消息的打印中你会看到:

Es gab einen Fehler: %!w(*errors.fundamental=&{failing unconditionally 0xc0000a2000})

而当使用Wrap方法时,您将获得withStack错误类型,其Format方法为:

func (w *withStack) Format(s fmt.State, verb rune) {
    switch verb {
    case 'v':
        if s.Flag('+') {
            fmt.Fprintf(s, "%+v", w.Cause())
            w.stack.Format(s, verb)
            return
        }
        fallthrough
    case 's':
        io.WriteString(s, w.Error())
    case 'q':
        fmt.Fprintf(s, "%q", w.Error())
    }
}

由于这行fmt.Fprintf(s, "%+v", w.Cause()),您可以看到一个很好的递归堆栈跟踪。
基本上,你需要像这样使用%w

fmt.Errorf("got error: %w", err)

但如果你想打印堆栈,这不会有帮助。
你可以做的是自己实现一个递归打印堆栈跟踪的函数,它将在fmtpkg/errors的错误链上工作:

type stackTracer interface {
             StackTrace() errors.StackTrace
}

type unwrapper interface {
    Unwrap() error
}

func printStack(err error) {
    if err == nil {
        return
    }
    
    if ster, ok := err.(stackTracer); ok {
        fmt.Printf("%+v", ster)
    }
    
    
    if wrapped, ok := err.(unwrapper); ok {
        printStack(wrapped.Unwrap())
    }
}

https://play.golang.org/p/OsEPD6guWtO

gmol1639

gmol16392#

github.com/pkg/errors已被弃用,并且不会获得更新的Go(错误处理)功能。不久前,我将its spiritual successor命名为gitlab.com/tozd/go/errors,它解决了github.com/pkg/errors的一长串问题。其中之一,errors.Errorf只适用于%w。您可以使用gitlab.com/tozd/go/errors作为github.com/pkg/errors的直接替代品,然后您的示例程序看起来像:

package main

import (
    "log"

    "gitlab.com/tozd/go/errors"
)

func myerror() error {
    return errors.New("failing unconditionally")
}

func main() {
    if err := myerror(); err != nil {
        err = errors.Errorf("Es gab einen Fehler: %w", err)
        log.Printf("%+v", err)
    }
}

输出量:

2009/11/10 23:00:00 Es gab einen Fehler: failing unconditionally
main.myerror
    /tmp/sandbox3646048541/prog.go:10
main.main
    /tmp/sandbox3646048541/prog.go:14
runtime.main
    /usr/local/go-faketime/src/runtime/proc.go:267
runtime.goexit
    /usr/local/go-faketime/src/runtime/asm_amd64.s:1650

请注意,堆栈跟踪正确地从myerror开始(也就是原始错误发生的地方),而不是从main开始(您调用Errorf的地方)。Errorf正确地检测到原始错误已经有堆栈跟踪,并且不会添加另一个堆栈跟踪或覆盖它。

相关问题