问题描述
我想用堆栈跟踪注释错误,因此我正在使用/ pkg / errors包。
以下不使用%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)
但是如果您要打印纸叠,这将无济于事。
您可能要做的是为自己实现一个递归打印堆栈跟踪的函数,该函数将在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())
}
}