I would like to annotate errors with stack traces, therefore I am using /pkg/errors package.
Go 1.13 added the %w - formatting verb to Wrap errors.
The following program not using %w prints a nice stack trace:
https://play.golang.org/p/eAwMrwqjCWX
The following only slightly modified program using %w not:
https://play.golang.org/p/am34kdC0E3o
How am I supposed to use %w together with error wrapping and stack traces?
The reason is that errors.Errorf initializes new fundamental error type (in pkg/errors). If you will look at its method Format(s fmt.State, verb rune) (which is triggered when you do 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)
}
}
So you see that it just prints io.WriteString(s, f.msg).
Also, Errorf is:
func Errorf(format string, args ...interface{}) error {
return &fundamental{
msg: fmt.Sprintf(format, args...),
stack: callers(),
}
}
Because of the line: fmt.Sprintf(format, args...) you see the error of go vet (you can't pass %w to Sprintf), and in the print of the error msg you just see:
Es gab einen Fehler: %!w(*errors.fundamental=&{failing unconditionally 0xc0000a2000})
Whereas when using Wrap method, you get withStack error type, which its Format method is:
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())
}
}
Thanks to this line fmt.Fprintf(s, "%+v", w.Cause()), you see a nice recursive stack trace.
Basically, you need to use %w like this:
fmt.Errorf("got error: %w", err)
But this won't help in case you want to print the stack.
What you can do maybe is to implement yourself a function for recursively printing the stack trace, which will work on an error chain of either fmt or 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
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With