Documentation
¶
Overview ¶
Package trackerr aims to facilitate creation of referenceable errors and elegant stack traces.
It was crafted in frustration trying to navigate Go's printed error stacks and the challenge of reliably asserting specific error types while testing.
Index ¶
- Variables
- func All(e error, targets ...error) bool
- func AllOrdered(e error, targets ...error) bool
- func Any(e error, targets ...error) bool
- func Debug(e error) (int, error)
- func DebugPanic(catch *error)
- func ErrorStack(e error) string
- func ErrorStackf(e error, f ErrorFormatter) string
- func ErrorWithoutCause(e error) string
- func HasTracked(e error) bool
- func Initialised()
- func Is(e, target error) bool
- func IsTracked(e error) bool
- func IsTrackerr(e error) bool
- func SliceStack(e error) []error
- func Squash(e error) error
- func Squashf(e error, f ErrorFormatter) error
- func Stack(e error, errs ...ErrorThatWraps) error
- func Unwrap(e error) error
- type ErrorFormatter
- type ErrorThatWraps
- type IntRealm
- type Realm
- type TrackedError
- func (e TrackedError) Because(msg string, args ...any) error
- func (e TrackedError) BecauseOf(rootCause error, msg string, args ...any) error
- func (e TrackedError) CausedBy(rootCause error, causes ...ErrorThatWraps) error
- func (e TrackedError) Error() string
- func (e TrackedError) Is(other error) bool
- func (e TrackedError) Unwrap() error
- type UntrackedError
- func (e UntrackedError) Because(msg string, args ...any) error
- func (e UntrackedError) BecauseOf(rootCause error, msg string, args ...any) error
- func (e UntrackedError) CausedBy(rootCause error, causes ...ErrorThatWraps) error
- func (e UntrackedError) Error() string
- func (e UntrackedError) Unwrap() error
Constants ¶
This section is empty.
Variables ¶
var ( // ErrTodo is a convenience trackable error for specifying a TODO. // // This can be useful if you're taking a stepwise refinement or test driven // approach to writing code. ErrTodo = New("TODO: Implementation needed") // ErrBug is a convenience trackable error for use at the site of known bugs. ErrBug = New("BUG: Fix needed") // ErrInsane is a convenience trackable error for sanity checking. ErrInsane = New("Sanity check failed!!") )
Functions ¶
func AllOrdered ¶ added in v0.13.0
AllOrdered returns true only if errors.Is returns true for all targets and the order of the targets matches the descending order of errors.
This does not mean the depth of the error stack must be the same as the number of targets. If three targets are supplied then true is returned if during descent of the error stack:
THE three targets exist in the error stack AND first target is found first AND the second target is found second And the third target is found last
func Debug ¶
Debug pretty prints the error stack trace to terminal for debugging purposes.
If e is nil then a message will be printed indicating so. This function is not designed for logging, just day to day manual debugging.
func DebugPanic ¶
func DebugPanic(catch *error)
DebugPanic recovers from a panic, prints out the error using the Debug function, and finally sets it as the catch error's pointer value.
If nil is passed as the catch then the panic continues after printing.
If the panic value is not an error the panic will continue!
This function is not designed for logging, just day to day manual debugging.
func ErrorStack ¶
ErrorStack calls ErrorStackf with simple default formatting.
Workflow error ⤷ Failed to read data ⤷ Error handling CSV file ⤷ open splay/example/data/acid-rain.csv ⤷ no such file or directory
func ErrorStackf ¶ added in v0.12.0
func ErrorStackf(e error, f ErrorFormatter) string
ErrorStackf returns a human readable stack trace for the error. The format function f may be nil for no formatting.
alice := trackerr.Untracked("Alice's message")
bob := trackerr.Checkpoint(alice, "Bob's message")
charlie := trackerr.Wrap(bob, "Charlie's message")
dan := trackerr.Wrap(charlie, "Dan's message")
s := trackerr.ErrorStackf(e, func(errMsg string, err error, isFirst bool) string {
if isFirst {
return "ERROR: " + errMsg
}
if trackerr.IsCheckpoint(err) {
return "*** " + errMsg + " ***"
}
return "Caused by: " + errMsg
}
// ERROR: Dan's message
// Caused by: Charlie's message
// *** Bob's message ***
// Caused by: Alice's message
func ErrorWithoutCause ¶
ErrorWithoutCause removes the cause from error messages that use the format '%s: %w'. Where s is the error message and w is the cause's message.
func HasTracked ¶
HasTracked returns true if the error or one of the underlying causes are tracked, i.e. those created via the New or Track functions.
func Initialised ¶ added in v0.12.0
func Initialised()
Initialised causes all future calls to New or Track to panic.
When called from an init function in the main package, it prevents creation of trackable errors after program initialisation.
package main
import "github.com/PaulioRandall/go-trackerr"
func init() {
trackerr.Initialised()
}
func IsTracked ¶
IsTracked returns true if the error is being tracked, i.e. those created via the New or Track functions.
func IsTrackerr ¶ added in v0.12.0
IsTrackerr returns true if the error is either an UntrackedError or TrackedError from this package. That is, if it's an error defined by go-trackerr.
func SliceStack ¶ added in v0.17.0
SliceStack recursively unwraps the error returning a slice of errors. The passed error e will be first and root cause last.
charlie := trackerr.Untracked("Charlie's message")
bob := trackerr.Wrap(charlie, "Bob's message")
alice := trackerr.Wrap(bob, "Alice's message")
result := SliceStack(alice)
// result: [
// alice,
// bob,
// charlie,
// ]
func Squash ¶ added in v0.19.0
Squash calls trackerr.ErrorStack with the error e then uses the result as the message for a new error; which is returned.
func Squashf ¶ added in v0.19.0
func Squashf(e error, f ErrorFormatter) error
Squashf is the same as squash but allows an ErrorFormatter to be used to format the error stack string.
func Stack ¶ added in v0.15.0
func Stack(e error, errs ...ErrorThatWraps) error
Stack accepts a an array of ErrorWrappers and converts it into a stack trace by recursively calling CasuedBy.
The first item is the root cause and the last item the head.
head := trackerr.New("head message")
mid := trackerr.New("mid level message")
root := trackerr.New("root cause message")
e := Stack(root, mid, head)
// head message
// ⤷ mid level message
// ⤷ root cause message
Types ¶
type ErrorFormatter ¶ added in v0.12.0
ErrorFormatter formats an error for stack trace printing.
Each error string will be printed on a line of its own so implementations should not prefix or suffix a linefeed unless they want gappy print outs.
type ErrorThatWraps ¶ added in v0.16.0
type ErrorThatWraps interface {
error
// CausedBy wraps the rootCause within the first item in causes. Then the
// second item in causes wraps the first. Then the third item wraps the
// second and so on. Finally, the receiving error wraps the result before
// returning.
CausedBy(rootCause error, causes ...ErrorThatWraps) error
// Unwrap returns the error's underlying cause or nil if none exists.
Unwrap() error
}
ErrorThatWraps represents an error that wraps new untracked errors.
type IntRealm ¶
type IntRealm struct {
// contains filtered or unexported fields
}
IntRealm is a Realm that uses a simple incrementing integer field as the pool of unique IDs.
realm := IntRealm{}
The recommended way to use this package is to ignore this struct and use the New or Track package functions. If this package's API is used as intended then it would be impossible to cause an integer overflow scenario in any real world use case. However, Realms were conceived for such an event and for those who really hate the idea of relying on a singleton they have no control over.
type Realm ¶
type Realm interface {
// New is an alias for Track.
New(msg string, args ...any) *TrackedError
// Track returns a new tracked error, that is, one with a tracking ID.
Track(msg string, args ...any) *TrackedError
}
Realm represents a space where each trackable error (stack trace node) has its own unique ID.
There is a private package scooped Realm that will suffice for most purposes. It is used via the package scooped Track functions.
Receiving functions are designed to be called during package initialisation. This means it should only be used to initialise package global variables and within init functions. The exception is where Realms are in use.
Furthermore, all functions return a shallow copy of any passed or receiving errors creating a somewhat immutability based ecosystem.
This interface is primarily for documentation.
type TrackedError ¶
type TrackedError struct {
// contains filtered or unexported fields
}
TrackedError represents a trackable node in an error stack.
func New ¶ added in v0.15.0
func New(msg string, args ...any) *TrackedError
New is an alias for Track.
func Track ¶
func Track(msg string, args ...any) *TrackedError
Track returns a new tracked error from this package's global Realm.
This is the recommended way to use to create all trackable errors.
func (TrackedError) Because ¶ added in v0.13.0
func (e TrackedError) Because(msg string, args ...any) error
Because constructs a cause from msg and args.
wrapper := trackerr.New("wrapper message")
e := wrapper.Because("cause message")
```
wrapper message
⤷ cause message
```
func (TrackedError) BecauseOf ¶
func (e TrackedError) BecauseOf(rootCause error, msg string, args ...any) error
BecauseOf creates a new error using the msg, args, and cause as arguments then attaches the result as the cause of the receiving error.
Put another way, the cause argument becomes the root cause in the error stack.
top := trackerr.New("top message")
rootCause := trackerr.New("root cause message")
e := top.BecauseOf(rootCause, "middle message")
```
top message
⤷ middle message
⤷ root cause message
```
func (TrackedError) CausedBy ¶ added in v0.13.0
func (e TrackedError) CausedBy(rootCause error, causes ...ErrorThatWraps) error
CausedBy wraps the rootCause within the first item in causes. Then the second item in causes wraps the first. Then the third item wraps the second and so on. Finally, the receiving error wraps the result before returning.
head := trackerr.New("head message")
causeA := trackerr.New("cause message A")
causeB := trackerr.New("cause message B")
rootCause := trackerr.Untracked("root cause message")
e := head.CausedBy(rootCause, causeB, causeA)
```
head message
⤷ cause message A
⤷ cause message B
⤷ root cause message
```
CausedBy will very often be used to wrap a single error.
head := trackerr.New("head message")
cause := trackerr.Untracked("cause message")
e := head.CausedBy(cause)
```
head message
⤷ cause message
```
func (TrackedError) Error ¶ added in v0.13.0
func (e TrackedError) Error() string
Error satisfies the error interface.
func (TrackedError) Is ¶
func (e TrackedError) Is(other error) bool
Is returns true if the passed error is equivalent to the receiving error. This is a shallow comparison so causes are not checked.
It satisfies the Is function referenced by errors.Is in the standard errors package.
func (TrackedError) Unwrap ¶ added in v0.13.0
func (e TrackedError) Unwrap() error
Unwrap returns the error's underlying cause or nil if none exists.
It is designed to work with errors.Is exposed by the standard errors package.
type UntrackedError ¶
type UntrackedError struct {
// contains filtered or unexported fields
}
UntrackedError represents an untracked error in an error stack.
func Untracked ¶
func Untracked(msg string, args ...any) *UntrackedError
Untracked returns a new error without a tracking ID.
This is the same as calling errors.New except for the handy fmt.Sprintf function signature and the resultant error has a few extra receiving functions for attaching causal errors.
func (UntrackedError) Because ¶
func (e UntrackedError) Because(msg string, args ...any) error
Because constructs a cause from msg and args.
wrapper := trackerr.New("wrapper message")
e := wrapper.Because("cause message")
```
wrapper message
⤷ cause message
```
func (UntrackedError) BecauseOf ¶ added in v0.12.0
func (e UntrackedError) BecauseOf(rootCause error, msg string, args ...any) error
BecauseOf creates a new error using the msg, args, and cause as arguments then attaches the result as the cause of the receiving error.
Put another way, the cause argument becomes the root cause in the error stack.
top := trackerr.New("top message")
rootCause := trackerr.New("root cause message")
e := top.BecauseOf(rootCause, "middle message")
```
top message
⤷ middle message
⤷ root cause message
```
func (UntrackedError) CausedBy ¶
func (e UntrackedError) CausedBy(rootCause error, causes ...ErrorThatWraps) error
CausedBy wraps the rootCause within the first item in causes. Then the second item in causes wraps the first. Then the third item wraps the second and so on. Finally, the receiving error wraps the result before returning.
head := trackerr.New("head message")
causeA := trackerr.New("cause message A")
causeB := trackerr.New("cause message B")
rootCause := trackerr.Untracked("root cause message")
e := head.CausedBy(rootCause, causeB, causeA)
```
head message
⤷ cause message A
⤷ cause message B
⤷ root cause message
```
CausedBy will very often be used to wrap a single error.
head := trackerr.New("head message")
cause := trackerr.Untracked("cause message")
e := head.CausedBy(cause)
```
head message
⤷ cause message
```
func (UntrackedError) Error ¶ added in v0.12.0
func (e UntrackedError) Error() string
Error satisfies the error interface.
func (UntrackedError) Unwrap ¶
func (e UntrackedError) Unwrap() error
Unwrap returns the error's underlying cause or nil if none exists.
It is designed to work with errors.Is exposed by the standard errors package.