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

问题描述

我想用堆栈跟踪注释错误,因此我正在使用/ pkg / errors包。

Go 1.13在包装错误添加了%w-格式动词。

以下不使用%w的程序会打印出一个不错的堆栈跟踪:

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

以下仅使用%w进行略微修改的程序:

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

我应该如何将%w与错误包装和堆栈跟踪一起使用?

解决方法

原因是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),而在打印错误msg时,您只会看到:

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,w.Error())
    case 'q':
        fmt.Fprintf(s,w.Error())
    }
}

感谢fmt.Fprintf(s,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