Home > Blockchain >  How to use pkg/errors to annotate error & pretty print stack trace in golang?
How to use pkg/errors to annotate error & pretty print stack trace in golang?

Time:07-14

Consider the following code (https://go.dev/play/p/hDOyP3W_lqW)

package main

import (
    "log"

    "github.com/pkg/errors"
)

func myError() error {
    return errors.New("failing unconditionally")
}

func myError1() error {
    return errors.Errorf("annotate with additional debug info: % v", myError())
}

func myError2() error {
    return errors.Errorf("extra debug info: % v", myError1())
}

func main() {
    if err := myError2(); err != nil {
        log.Printf("% v", err)
    }
}

I originate the error with errors.New and annotate it with additional info using errors.Errorf.

It does what I want--record and print the stack trace & line number. However, the problem is that the output of log.Printf("% v", err) is verbose and repetitive:

2009/11/10 23:00:00 extra debug info: annotate with additional debug info: failing unconditionally
main.myError
    /tmp/sandbox3329712514/prog.go:10
main.myError1
    /tmp/sandbox3329712514/prog.go:14
main.myError2
    /tmp/sandbox3329712514/prog.go:18
main.main
    /tmp/sandbox3329712514/prog.go:22
runtime.main
    /usr/local/go-faketime/src/runtime/proc.go:250
runtime.goexit
    /usr/local/go-faketime/src/runtime/asm_amd64.s:1571
main.myError1
    /tmp/sandbox3329712514/prog.go:14
main.myError2
    /tmp/sandbox3329712514/prog.go:18
main.main
    /tmp/sandbox3329712514/prog.go:22
runtime.main
    /usr/local/go-faketime/src/runtime/proc.go:250
runtime.goexit
    /usr/local/go-faketime/src/runtime/asm_amd64.s:1571
main.myError2
    /tmp/sandbox3329712514/prog.go:18
main.main
    /tmp/sandbox3329712514/prog.go:22
runtime.main
    /usr/local/go-faketime/src/runtime/proc.go:250
runtime.goexit
    /usr/local/go-faketime/src/runtime/asm_amd64.s:1571

iiuc, errors package appends an additional copy of stack trace to the error every time of annotating the error, as can be seen in the below snippet

// repetitive (thrice) error stack
main.myError
    /tmp/sandbox3329712514/prog.go:10
main.myError1
    /tmp/sandbox3329712514/prog.go:14
main.myError2
    /tmp/sandbox3329712514/prog.go:18
main.main
    /tmp/sandbox3329712514/prog.go:22
runtime.main
    /usr/local/go-faketime/src/runtime/proc.go:250
runtime.goexit
    /usr/local/go-faketime/src/runtime/asm_amd64.s:1571
main.myError1
    /tmp/sandbox3329712514/prog.go:14
main.myError2
    /tmp/sandbox3329712514/prog.go:18
main.main
    /tmp/sandbox3329712514/prog.go:22
runtime.main
    /usr/local/go-faketime/src/runtime/proc.go:250
runtime.goexit
    /usr/local/go-faketime/src/runtime/asm_amd64.s:1571
main.myError2
    /tmp/sandbox3329712514/prog.go:18
main.main
    /tmp/sandbox3329712514/prog.go:22
runtime.main
    /usr/local/go-faketime/src/runtime/proc.go:250
runtime.goexit
    /usr/local/go-faketime/src/runtime/asm_amd64.s:1571

My desired output is

// Desired output
2009/11/10 23:00:00 extra debug info: annotate with additional debug info: failing unconditionally
main.myError
    /tmp/sandbox3329712514/prog.go:10
main.myError1
    /tmp/sandbox3329712514/prog.go:14
main.myError2
    /tmp/sandbox3329712514/prog.go:18
main.main
    /tmp/sandbox3329712514/prog.go:22
runtime.main
    /usr/local/go-faketime/src/runtime/proc.go:250
runtime.goexit
    /usr/local/go-faketime/src/runtime/asm_amd64.s:1571

One way to achieve that is to only use the errors package to originate the error and then use fmt.Errorf with % v to add additional info in the call stack (like this https://go.dev/play/p/OrWe6KUIL_m). However, it's error-prone and hard to enforce every developer to use this pattern in a large code base. Developers have to remember to use the errors package to originate the error and use fmt properly with % v %s to print out the stack trace.

I'm wondering if this is the desired behavior (verbose and repetitive). And is it possible to consistently use the errors package to annotate errors along the call stack without worrying about appending repetitive stack trace copies (e.g., the errors magically knows the error already has a stack trace)?

CodePudding user response:

there are v format specifiers for printing an error.

%s - print the error. If the error has a Cause it will be printed recursively.

%v – It will print only values. The field name will not be printed. This is the default way of printing a struct when using Println (print the error

  • Related