adverr/README.md
2020-09-09 14:07:55 +02:00

3.3 KiB

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

Import

import "git.tordarus.net/tordarus/adverr"

Create error templates

var (
    ErrDoStuffFailed = adverr.NewErrTmpl("ErrDoStuffFailed", "Could'nt do stuff because of %s")
)

Create independent error (without error template)

func doStuffWithIndependentErr() error {
    return adverr.New("Could'nt do stuff")
}

Create error based on template

func doStuff() error {
    return ErrDoStuffFailed.New("reasons")
}

Print errors on stderr convieniently

adverr.Print(myErr)
adverr.Println(myErr)

Print 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

Wrap errors (Causality of 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
}

Retrieve 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