Advanced error handling for Go
calltrace.go | ||
doc.go | ||
error_test.go | ||
error_tmpl_test.go | ||
error_tmpl.go | ||
error.go | ||
globals.go | ||
go.mod | ||
README.md | ||
utils.go |
adverr
Package adverr implements errors with call stack traces as well as error templates for error equality
Installation
go get git.tordarus.net/tordarus/adverr
Usage examples
Importing
import "git.tordarus.net/tordarus/adverr"
Creating error templates
var (
ErrDoStuffFailed = adverr.NewErrTmpl("ErrDoStuffFailed", "Could'nt do stuff because of %s")
)
Creating independent error (without error template)
func doStuffWithIndependentErr() error {
return adverr.New("Could'nt do stuff")
}
Creating error based on template
func doStuff() error {
return ErrDoStuffFailed.New("reasons")
}
Printing errors on stderr convieniently
adverr.Print(myErr)
adverr.Println(myErr)
Printing errors on stderr and exit with exit code
adverr.Fatal(myErr, 1)
adverr.Fatalln(myErr, 1)
Advantages of error templates
two errors made by the same template will return true when called with errors.Is()
func doStuffAndFailWithMsg(msg string) error {
return ErrDoStuffFailed.New(msg)
}
err1 := doStuffAndFailWithMsg("err1")
err2 := doStuffAndFailWithMsg("err2")
fmt.Println(errors.Is(err1, err2)) // true
fmt.Println(err1 == err2) // false
fmt.Println(err1.Error() == err2.Error()) // false
Wrapping errors
By wrapping errors, you can provide an error that is caused by another error.
A 'Caused by' section will be printed in the stack trace showing the original error.
You can also retrieve the original error by using errors.Unwrap()
func doStuffWrapped() error {
err := doStuff()
if err != nil {
return adverr.Wrap("doStuffWrapped failed", err)
}
return nil
}
Retrieving call stack trace (for debugging purposes)
fmt.Println(adverr.Trace())
Example of a printed error
Code:
package main
import (
"adverr"
"errors"
)
var (
ErrDoStuffFailed = adverr.NewErrTmpl("ErrDoStuffFailed", "Could'nt do stuff because of %s")
)
func main() {
err := doStuffInAnotherGoroutine()
if err != nil {
adverr.Fatalln(err, 1)
}
}
func doStuff() error {
err := doGoNativeStuff()
if err != nil {
return ErrDoStuffFailed.Wrap(err, "reasons")
}
return nil
}
func doStuffInAnotherGoroutine() error {
ch := make(chan error, 1)
go func() {
ch <- doStuff()
close(ch)
}()
err := <-ch
if err != nil {
return adverr.Wrap("Goroutine failed because of errors", err)
}
return nil
}
func doGoNativeStuff() error {
return errors.New("some go error")
}
Output:
adverr.Error: Goroutine failed because of errors
at main.doStuffInAnotherGoroutine (/home/user/go/src/test/main.go:38)
at main.main (/home/user/go/src/test/main.go:13)
at runtime.main (/usr/local/go/src/runtime/proc.go:204)
Caused by ErrDoStuffFailed: Could'nt do stuff because of reasons
at main.doStuff (/home/user/go/src/test/main.go:22)
at main.doStuffInAnotherGoroutine.func1 (/home/user/go/src/test/main.go:32)
Caused by errors.errorString: some go error
(Unknown source)
Globals
You can set the maximum limit of the call stack trace via
adverr.CallStackLength = 50 // default value: 100
If you are in a productive environment, consider disabling call traces completely for performance reasons:
adverr.DisableTrace = true // default value: false