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