Documentation
¶
Overview ¶
Package errors provides error handling primitives that add stack traces and metadata to errors.
Key Concepts:
- Adding Stack traces: All the error creation and wrapping functions ensure a stack trace is recorded for the error.
- Adding Context: The `errors.Wrap` and `errors.Wrapf` functions adds an additional string context to an error.
- Adding Structured data: The `errors.Wraps` and `errors.Slog` functions adds structured data to errors.
- Formatted Printing: Errors returned from this package implement the `fmt.Formatter` interface- verbose printing options will show the stack trace.
- Retrieving underlying errors: In addition to standard `errors.Unwrap`, `errors.Is`, and `errors.As`, there are `errors.AsType`, `errors.Cause`, and `errors.UnwrapGroups`.
- Retrieving the Stack Trace: `errors.GetStackTracer` retrieves the stack trace from the error.
- Retrieving the structured data: `errors.SlogRecord` retrieves structured data as an slog.Record.
Formatted printing of errors ¶
All error values returned from this package implement fmt.Formatter and can be formatted by the fmt package. The following verbs are supported
%s print the error. If the error has a Cause it will be
printed recursively with colon separations
%v see %s
%+v extended format. Each Frame of the error's StackTrace will
be printed in detail.
%-v similar to %s but newline separated. No stack traces included.
Example (StackTrace) ¶
package main
import (
"fmt"
"strings"
"github.com/gregwebs/errors"
"github.com/gregwebs/stackfmt"
)
func functionLines(s string) string {
lines := []string{}
for _, line := range strings.SplitAfter(s, "\n") {
if strings.HasPrefix(line, "github.com") {
lines = append(lines, line)
} else if strings.HasPrefix(line, "\t") || len(lines) == 0 {
continue
} else {
break
}
}
return strings.Join(lines, "")
}
func newWrappedErr() error {
e1 := errors.New("cause")
e2 := errors.Wrap(e1, "inner")
e3 := errors.Wrap(e2, "middle")
return errors.Wrap(e3, "outer")
}
func main() {
type stackTracer interface {
StackTrace() stackfmt.StackTrace
}
err, ok := errors.Cause(newWrappedErr()).(stackTracer)
if !ok {
panic("oops, err does not implement stackTracer")
}
st := err.StackTrace()
fmt.Print(functionLines(fmt.Sprintf("%+v", st)))
}
Output: github.com/gregwebs/errors_test.newWrappedErr github.com/gregwebs/errors_test.Example_stackTrace
Index ¶
- func AddStack(err error) error
- func AddStackSkip(err error, skip int) error
- func As(err error, target any) bool
- func AsType[Err error](err error) (Err, bool)
- func Cause(err error) error
- func Errorf(format string, args ...interface{}) error
- func GetStackTracer(origErr error) stackfmt.StackTracer
- func HandleFmtWriteError(handler func(err error))
- func HasStack(err error) bool
- func Is(err, target error) bool
- func IsNil(err error) bool
- func Join(errs ...error) error
- func Joins(errs ...error) []error
- func New(message string) error
- func Slog(msg string, args ...interface{}) slogerr.StructuredError
- func SlogRecord(inputErr error) *slog.Record
- func Unwrap(err error) error
- func Unwraps(err error) []error
- func Wrap(err error, message string) error
- func WrapFn(msg string) func(error) error
- func WrapNoStack(err error, message string) error
- func Wrapf(err error, format string, args ...interface{}) error
- func WrapfFn(msg string, args ...interface{}) func(error) error
- func Wraps(err error, msg string, args ...interface{}) slogerr.StructuredError
Examples ¶
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
func AddStack ¶ added in v0.12.0
AddStack annotates err with a stack trace at the point WithStack was called. It will first check with HasStack to see if a stack trace already exists before creating another one.
Example ¶
package main
import (
stderrors "errors"
"fmt"
"strings"
"github.com/gregwebs/errors"
)
// Returns the first 'n' lines of a given string, where each line is separated by '\n'.
func firstNLines(s string, n int) string {
allLines := strings.SplitN(s, "\n", n+1)
if n > len(allLines) {
n = len(allLines)
}
return strings.Join(allLines[0:n], "\n")
}
func main() {
// stderrors is the standard library errors: no stack trace
cause := stderrors.New("whoops")
fmt.Print(firstNLines(fmt.Sprintf("%+v\n", cause), 2))
// Add a stack trace to it
err := errors.AddStack(cause)
fmt.Print(firstNLines(fmt.Sprintf("%+v\n", err), 2))
}
Output: whoops whoops github.com/gregwebs/errors_test.ExampleAddStack
func AddStackSkip ¶ added in v1.1.0
Same as AddStack but specify an additional number of callers to skip
Example ¶
package main
import (
stderrors "errors"
"fmt"
"strings"
"github.com/gregwebs/errors"
)
func functionLines(s string) string {
lines := []string{}
for _, line := range strings.SplitAfter(s, "\n") {
if strings.HasPrefix(line, "github.com") {
lines = append(lines, line)
} else if strings.HasPrefix(line, "\t") || len(lines) == 0 {
continue
} else {
break
}
}
return strings.Join(lines, "")
}
func main() {
// stderrors is the standard library errors: no stack trace
inner := func() {
cause := stderrors.New("whoops")
err := errors.AddStack(cause)
fmt.Print(functionLines(fmt.Sprintf("%+v\n", err)))
fmt.Println("---")
// Add a stack trace to it
err = errors.AddStackSkip(cause, 1)
fmt.Print(functionLines(fmt.Sprintf("%+v\n", err)))
}
inner()
}
Output: github.com/gregwebs/errors_test.ExampleAddStackSkip.func1 github.com/gregwebs/errors_test.ExampleAddStackSkip --- github.com/gregwebs/errors_test.ExampleAddStackSkip
func AsType ¶ added in v1.11.0
AsType is equivalient to As and returns the same boolean. Instead of instantiating a struct and passing it by pointer, the type of the error is given as the generic argument It is instantiated and returned.
Example ¶
package main
import (
"fmt"
"github.com/gregwebs/errors"
)
type ErrEmpty struct{}
func (et ErrEmpty) Error() string {
return "empty"
}
func main() {
err := errors.Wrap(ErrEmpty{}, "failed")
cause, _ := errors.AsType[ErrEmpty](err)
fmt.Printf("%v", cause)
}
Output: empty
func Cause ¶
Cause returns the underlying cause of the error, if possible. Unwrap goes just one level deep, but Cause goes all the way to the bottom If nil is given, it will return nil
Example ¶
package main
import (
"fmt"
"github.com/gregwebs/errors"
)
func newWrappedErr() error {
e1 := errors.New("cause")
e2 := errors.Wrap(e1, "inner")
e3 := errors.Wrap(e2, "middle")
return errors.Wrap(e3, "outer")
}
func main() {
err := newWrappedErr()
fmt.Println(err)
fmt.Println(errors.Cause(err))
}
Output: outer: middle: inner: cause cause
func Errorf ¶ added in v0.3.0
Errorf formats according to a format specifier and returns the string as a value that satisfies error. Errorf also records the stack trace at the point it was called.
Example ¶
package main
import (
"fmt"
"strings"
"github.com/gregwebs/errors"
)
func main() {
err := errors.Errorf("whoops: %s", "foo")
verbose := fmt.Sprintf("%+v", err)
fmt.Print(strings.Join(strings.SplitN(verbose, "\n", 3)[0:2], "\n"))
}
Output: whoops: foo github.com/gregwebs/errors_test.ExampleErrorf
func GetStackTracer ¶ added in v0.12.0
func GetStackTracer(origErr error) stackfmt.StackTracer
GetStackTracer will return the first StackTracer in the causer chain. This function is used by AddStack to avoid creating redundant stack traces.
You can also use the StackTracer interface on the returned error to get the stack trace.
func HandleFmtWriteError ¶ added in v1.30.0
func HandleFmtWriteError(handler func(err error))
HandleFmtWriteError handles (rare) errors when writing to fmt.State. It defaults to printing the errors.
func HasStack ¶ added in v0.12.0
HasStack returns true if the error will find a stack trace It does not unwrap errors It looks for stackfmt.StackTracer, stackfmt.StackTraceFormatter, or the method HasStack() bool
func IsNil ¶ added in v1.21.0
IsNil performs additional checks besides == nil This helps deal with a design issue with Go interfaces: https://go.dev/doc/faq#nil_error It will return true if the error interface contains a nil pointer, interface, slice, array or map It will return true if the slice or array or map is empty
Example ¶
package main
import (
"fmt"
"github.com/gregwebs/errors"
)
type ErrEmpty struct{}
func (et ErrEmpty) Error() string {
return "empty"
}
func main() {
var empty *ErrEmpty = nil //nolint:staticcheck
var err error = empty
fmt.Println(err == nil) //nolint:staticcheck
fmt.Println(errors.IsNil(err))
}
Output: false true
func Joins ¶ added in v1.14.0
The same as errors.Join but returns the array rather than wrapping it. Also uses isNil for a better nil check.
func New ¶
New returns an error with the supplied message. New also records the stack trace at the point it was called.
Example ¶
package main
import (
"fmt"
"github.com/gregwebs/errors"
)
func main() {
err := errors.New("whoops")
fmt.Println(err)
}
Output: whoops
func Slog ¶ added in v1.7.0
func Slog(msg string, args ...interface{}) slogerr.StructuredError
Slog creates an error that instead of generating a format string generates a structured slog Record. Accepts as args any valid slog args. Also accepts `[]slog.Attr` as a single argument to avoid having to cast that argument. The slog Record can be retrieved with SlogRecord. Structured errors are more often created by wrapping existing errors with Wraps.
func SlogRecord ¶ added in v1.2.0
SlogRecord traverses the error chain, calling Unwrap(), to look for slog Records All records will be merged and mesage strings are joined together The message string may contain some of the structured information This depends on defining ErrorNoUnwrap from the interface ErrorNotUnwrapped
if record := errors.SlogRecord(errIn); record != nil {
record.Add(logArgs...)
if err := slog.Default().Handler().Handle(ctx, *record); err != nil {
slog.ErrorContext(ctx, fmt.Sprintf("%+v", err))
}
}
func Unwrap ¶ added in v0.12.0
Unwrap uses the Unwrap method to return the next error in the chain or nil. This is the same as the standard errors.Unwrap
func Wrap ¶
Wrap returns an error annotating err with a stack trace at the point Wrap is called, and the supplied message. If err is nil, Wrap returns nil.
Example ¶
package main
import (
"fmt"
"github.com/gregwebs/errors"
)
func main() {
cause := errors.New("whoops")
err := errors.Wrap(cause, "oh noes")
fmt.Println(err)
}
Output: oh noes: whoops
func WrapNoStack ¶ added in v1.21.0
WrapNoStack does not add a new stack trace. WrapNoStack annotates err with a new message. If err is nil, returns nil. When used consecutively, it will append the message strings rather than creating a new error
Example ¶
package main
import (
stderrors "errors"
"fmt"
"github.com/gregwebs/errors"
)
func main() {
cause := stderrors.New("whoops")
err := errors.WrapNoStack(cause, "oh noes")
fmt.Printf("%+v", err)
}
Output: whoops oh noes
func Wrapf ¶ added in v0.2.0
Wrapf returns an error annotating err with a stack trace at the point Wrapf is call, and the format specifier. If err is nil, Wrapf returns nil.
Example ¶
package main
import (
"fmt"
"github.com/gregwebs/errors"
)
func main() {
cause := errors.New("whoops")
err := errors.Wrapf(cause, "oh noes #%d", 2)
fmt.Println(err)
}
Output: oh noes #2: whoops
func Wraps ¶ added in v1.2.0
func Wraps(err error, msg string, args ...interface{}) slogerr.StructuredError
Wraps ends with an "s" to indicate it is Structured. Accepts as args any valid slog args. These will generate an slog Record Also accepts []slog.Attr as a single argument to avoid having to cast that argument.
Types ¶
This section is empty.