errutil

package module
v0.0.0-...-2c7a180 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Mar 25, 2025 License: MIT Imports: 9 Imported by: 2

README

errutil

Go Reference

Why?

While numerous error packages provide rich functionality, errutil is the minimal (opinionated) functionality GRAX needs for error traces. Minimal functionality leads to:

  • Consistent use through a codebase.
  • A faster implementation.
  • No assumptions to break. For example, when errors with meaning (UserNotFound) are offered.

Usage

With and friends produce an error containing location information. The locations in a chain of errors will surface in the Stack produced by BuildStack. The stack can then be logged, displayed, sent to a service etc.

The Wrap methods additionally wrap passed errors so errors.Is matches the original error. To understand when Wrap should be used instead of With, read the Whether to Wrap section of the Go 1.13 errors blog post.

Functions that do not expose Is/As errors as part of their contract, should look similar to:

func aFunc() error {
    ...
    return errutil.With(err)
}

Wrapping sentinel errors, should look similar to:

var ErrNotFound = errors.New("not found")

...
func aFunc() error {
    if err := bFunc(); err != nil {
        return errutil.Wrap(err, ErrNotFound)
    }
    ...
}

if err := aFunc(); err != nil {
    if errors.Is(err, ErrNotFound) {
        // handle not found case
    }
    ...
}

Wrapping unknown errors (discouraged), should look similar to:

if err := aFunc(); err != nil {
    return errutil.Wrap(err)
}

Wrapping custom errors that are not sentinels, should look similar to:

type CustomError struct {
    Text string
}

func (e CustomError) Error() string {
    return "text: " + e.Text
}

// Must use a pointer to avoid accidental matches. Does not need to be the same type as CustomError.
var ErrCustom = errors.New("custom error")

func (CustomError) Is(target error) bool {
    return target == ErrCustom
}

func aFunc() error {
    if err := bFunc(); err != nil {
        return errutil.Wrap(err, ErrCustom)
    }
    ... 
}

if err := aFunc(); err != nil {
    var cErr CustomError
    if errors.As(err, &cErr) {
        // use cErr.Text
    }
    ...
}

Custom errors should implement Baser or errors.Unwrap to maintain traces, similar to:

type CustomError struct {
    Err error
    Text string
}

func (e CustomError) Error() string {
    return "text: " + e.Text
}

func (e CustomError) Base() error {
    return e.Err
}

Simple logging could be done with:

if err := topOfCalls(); err != nil {
    log.Println(errutil.BuildStack(err))
}

Future improvements

  • Garbage reduction. Currently we maintain pointer equality in the same vein as errors.New as developers likely expect, however it requires heap allocation. This only shows up however in very fast loops.

Documentation

Index

Constants

This section is empty.

Variables

View Source
var ImportPrefix string

ImportPrefix strips off pkgs when formatting, to shorten. Only set in a pkg init().

Functions

func New

func New(t Tags) error

New returns a new error with stack information from the caller of New and tags t.

func NewFrameError

func NewFrameError(f Frame, t Tags, err error, wrap bool) error

NewFrameError enriches err with stack information from f and tags t. If wrap is true, the returned error will implement Unwrap returning err.

func With

func With(err error) error

With enriches err with stack information from the caller of With. The returned error will not implement Unwrap (used by errors.Is/As).

func WithStack

func WithStack(err error) error

WithStack returns a new error with complete stack information from the caller of WithStack.

This is useful in cases where errors aren't or can't be created and passed around, such as when recovering from panics.

func Witht

func Witht(err error, t Tags) error

Witht is like With but adds tags t to the error information.

func Wrap

func Wrap(err error, allowed ...error) error

Wrap enriches err with stack information from the caller of Wrap. The returned error will implement Unwrap (used by errors.Is/As) returning err if allowed is empty, or if an item in allowed matches err using errors.Is.

Generally, Wrap should only be used when the returned error is expected to be used with errors.Is or similar. Non-sentinel errors can implement errors.Is to be used with allowed.

func Wrapt

func Wrapt(err error, t Tags, allowed ...error) error

Wrapt is like Wrap but adds tags t to the error information.

Types

type Baser

type Baser interface {
	Base() error
}

Custom error types should implement to maintain stacks. Only one of Baser or errors.Unwrap is needed, preferring Baser.

type Frame

type Frame struct {
	// contains filtered or unexported fields
}

func Caller

func Caller(skip int) Frame

Caller(0) returns the frame for the caller of Caller.

func (Frame) Location

func (f Frame) Location() (pkg, fn, fileName string, line int)

type Stack

type Stack []StackFrame

func BuildStack

func BuildStack(err error) Stack

Always returns at least 1 StackFrame for a non-nil err. Do not call within error.Error().

func (Stack) String

func (s Stack) String() string

type StackFrame

type StackFrame struct {
	Pkg    string
	Func   string
	File   string
	Line   int
	Values map[string]any
}

func (StackFrame) String

func (f StackFrame) String() string

type Tags

type Tags map[string]any

Directories

Path Synopsis
test

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL