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

问题描述 投票:0回答:2

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

Go 1.13 添加了 %w - 格式化动词来换行错误。

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

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

以下仅使用 %w not 稍微修改过的程序:

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

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

go error-handling stack-trace
2个回答
2
投票

原因是

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)

但是如果您想打印堆栈,这将无济于事。

您也许可以做的是自己实现一个递归打印堆栈跟踪的函数,该函数将在

fmt
pkg/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


0
投票

github.com/pkg/errors
已被弃用,并且不会获得新 Go(错误处理)功能的更新。我不久前将它的精神继承者命名为
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
 正确检测到原始错误已经有堆栈跟踪,并且不会添加另一个错误或覆盖它。

© www.soinside.com 2019 - 2024. All rights reserved.