README

Emperror

Mentioned in Awesome Go

GitHub Workflow Status Codecov Go Report Card Go Version PkgGoDev FOSSA Status

The Emperor takes care of all errors personally.

Go's philosophy encourages to gracefully handle errors whenever possible, but some times recovering from an error is not.

In those cases handling the error means making the best effort to record every detail for later inspection, doing that as high in the application stack as possible.

This project provides tools to make error handling easier.

Read more about the topic here:

Features

Installation

go get emperror.dev/emperror

Usage

Log errors

Logging is one of the most common target to record error events.

Emperror has two logger integrations by default:

Annotate errors passing through an error handler

Emperror can annotate errors with details as defined in emperror.dev/errors

package main

import (
	"emperror.dev/emperror"
	"emperror.dev/errors"
)

func main() {
	handler := emperror.WithDetails(newHandler(), "key", "value")

	err := errors.New("error")

	// handled error will receive the handler details
	handler.Handle(err)
}

Panics and recovers

package main

import (
	"emperror.dev/emperror"
	"emperror.dev/errors"
)

func main() {
	var handler emperror.Handler = newHandler()

	// Recover from panics and handle them as errors
	defer emperror.HandleRecover(handler)

	// nil errors will not panic
	emperror.Panic(nil)

	// this will panic if foo returns with a non-nil error
	// useful in main func for initial setup where "if err != nil" does not make much sense
	emperror.Panic(foo())
}

func foo() error {
	return errors.New("error")
}

Filter errors

Sometimes you might not want to handle certain errors that reach the error handler. A common example is a catch-all error handler in a server. You want to return business errors to the client.

package main

import (
	"emperror.dev/emperror"
	"emperror.dev/errors/match"
)

func main() {
	var handler emperror.Handler = emperror.WithFilter(newHandler(), match.Any{/*any emperror.ErrorMatcher*/})

    // errors matching the provided matcher will not be handled
	handler.Handle(err)
}

Development

When all coding and testing is done, please run the test suite:

./pleasew lint
./pleasew gotest

License

The MIT License (MIT). Please see License File for more information.

FOSSA Status

Expand ▾ Collapse ▴

Documentation

Overview

Package emperror provides error handling solutions and tools for libraries and applications.

Index

Examples

Constants

This section is empty.

Variables

This section is empty.

Functions

func Context

func Context(err error) []interface{}

Context extracts the context key-value pairs from an error (or error chain). Deprecated: use emperror.dev/errors.GetDetails instead.

func ExposeStackTrace

func ExposeStackTrace(err error) error

ExposeStackTrace exposes the stack trace (if any) in the outer error.

func ForEachCause

func ForEachCause(err error, fn func(err error) bool)

ForEachCause loops through an error chain and calls a function for each of them, starting with the topmost one.

The function can return false to break the loop before it ends.

Deprecated: use emperror.dev/errors.UnwrapEach instead.

func Handle

func Handle(handler Handler, err error)

Handle handles an error (if one occurred).

Deprecated: no replacement. ErrorHandler should return if err is nil.

func HandleRecover

func HandleRecover(handler ErrorHandler)

HandleRecover recovers from a panic and handles the error.

defer emperror.HandleRecover(errorHandler)

func Panic

func Panic(err error)

Panic panics if the passed error is not nil. If the error does not contain any stack trace, the function attaches one, starting from the frame of the "Panic" function call.

This function is useful with HandleRecover when panic is used as a flow control tool to stop the application.

func Recover

func Recover(r interface{}) (err error)

Recover accepts a recovered panic (if any) and converts it to an error (if necessary).

func With

func With(err error, keyvals ...interface{}) error

With returns a new error with keyvals context appended to it. If the wrapped error is already a contextual error created by With keyvals is appended to the existing context, but a new error is returned. Deprecated: use emperror.dev/errors.WithDetails instead.

func Wrap

func Wrap(err error, message string) error

Wrap returns an error annotating err with a stack trace at the point Wrap is called (if there is none attached to the error yet), and the supplied message. If err is nil, Wrap returns nil.

Note: do not use this method when passing errors between goroutines. Deprecated: use emperror.dev/errors.WrapIf instead.

func WrapWith

func WrapWith(err error, message string, keyvals ...interface{}) error

WrapWith returns an error annotating err with a stack trace at the point Wrap is called (if there is none attached to the error yet), the supplied message, and the supplied context. If err is nil, Wrap returns nil.

Note: do not use this method when passing errors between goroutines. Deprecated: use emperror.dev/errors.WrapIfWithDetails instead.

func Wrapf

func Wrapf(err error, format string, args ...interface{}) error

Wrapf returns an error annotating err with a stack trace at the point Wrapf is call (if there is none attached to the error yet), and the format specifier. If err is nil, Wrapf returns nil.

Note: do not use this method when passing errors between goroutines. Deprecated: use emperror.dev/errors.WrapIff instead.

Types

type ContextAwareHandler

type ContextAwareHandler interface {
	// Handle handles an error.
	Handle(ctx context.Context, err error)
}

ContextAwareHandler is similar to Handler, except it receives a context as well. It is useful in request terminal error handling situations. An implementation MAY extract information from the context and annotate err with it.

Deprecated: user ErrorHandlerContext instead.

func MakeContextAware

func MakeContextAware(handler Handler) ContextAwareHandler

MakeContextAware wraps an error handler and turns it into a ContextAwareHandler.

Deprecated: no replacement at this time.

type ContextExtractor

type ContextExtractor func(ctx context.Context) map[string]interface{}

ContextExtractor extracts a map of details from a context.

func ContextExtractors

func ContextExtractors(extractors ...ContextExtractor) ContextExtractor

ContextExtractors combines a list of ContextExtractor. The returned extractor aggregates the result of the underlying extractors.

type ErrorHandler

type ErrorHandler interface {
	// Handle handles an error.
	//
	// If err is nil, Handle should immediately return.
	Handle(err error)
}

ErrorHandler is a generic error handler that allows applications (and libraries) to handle errors without worrying about the actual error handling strategy (logging, error tracking service, etc).

type ErrorHandlerContext

type ErrorHandlerContext interface {
	// HandleContext handles an error.
	//
	// If err is nil, HandleContext should immediately return.
	HandleContext(ctx context.Context, err error)
}

ErrorHandlerContext is an optional interface that MAY be implemented by an ErrorHandler. It is similar to ErrorHandler, but it receives a context as the first parameter. An implementation MAY extract information from the context and annotate err with it.

ErrorHandlerContext MAY honor the deadline carried by the context, but that's not a hard requirement.

type ErrorHandlerContextFunc

type ErrorHandlerContextFunc func(ctx context.Context, err error)

ErrorHandlerContextFunc wraps a function and turns it into an ErrorHandlerContext if the function's definition matches the interface.

func (ErrorHandlerContextFunc) Handle

func (h ErrorHandlerContextFunc) Handle(err error)

func (ErrorHandlerContextFunc) HandleContext

func (h ErrorHandlerContextFunc) HandleContext(ctx context.Context, err error)

type ErrorHandlerFacade

type ErrorHandlerFacade interface {
	ErrorHandler
	ErrorHandlerContext
}

ErrorHandlerFacade is a combination of ErrorHandler and ErrorHandlerContext. It's sole purpose is to make the API of the package concise by exposing a common interface type for returned handlers. It's not supposed to be used by consumers of this package.

It goes directly against the "Use interfaces, return structs" idiom of Go, but at the current phase of the package the smaller API surface makes more sense.

In the future it might get replaced with concrete types.

func HandlerWithPrefix

func HandlerWithPrefix(handler Handler, keyvals ...interface{}) ErrorHandlerFacade

HandlerWithPrefix returns a new error handler with keyvals context prepended to it. If the wrapped error handler is already a contextual error handler created by HandlerWith or HandlerWithPrefix keyvals is prepended to the existing context, but a new error handler is returned.

The created handler will prepend it's own context to the handled errors. Deprecated: no replacement at this time.

func NewErrorHandlerContext

func NewErrorHandlerContext(handler ErrorHandler, extractor ContextExtractor) ErrorHandlerFacade

NewErrorHandlerContext returns an error handler that extracts details from the provided context (if any) and annotates the handled error with them.

Deprecated: use WithContextExtractor.

func WithContextExtractor

func WithContextExtractor(handler ErrorHandler, extractor ContextExtractor) ErrorHandlerFacade

WithContextExtractor returns an error handler that extracts details from the provided context (if any) and annotates the handled error with them.

func WithDetails

func WithDetails(handler ErrorHandler, details ...interface{}) ErrorHandlerFacade

WithDetails returns a new error handler that annotates every error with a set of key-value pairs.

func WithFilter

func WithFilter(handler ErrorHandler, matcher ErrorMatcher) ErrorHandlerFacade

WithDetails returns a new error handler that discards errors matching any of the specified filters. Otherwise it passes errors to the next handler.

Example

Code:

err := errors.New("no more errors")
err2 := errors.New("one last error")
isErr := errors.New("is")

handler := WithFilter(
	ErrorHandlerFunc(func(err error) { fmt.Println(err) }),
	match.Is(isErr).MatchError,
)

handler.Handle(err)
handler.Handle(isErr)
handler.HandleContext(context.Background(), err2)
handler.HandleContext(context.Background(), isErr)
no more errors
one last error

type ErrorHandlerFunc

type ErrorHandlerFunc func(err error)

ErrorHandlerFunc wraps a function and turns it into an ErrorHandler if the function's definition matches the interface.

func (ErrorHandlerFunc) Handle

func (h ErrorHandlerFunc) Handle(err error)

func (ErrorHandlerFunc) HandleContext

func (h ErrorHandlerFunc) HandleContext(_ context.Context, err error)

type ErrorHandlerSet

type ErrorHandlerSet = ErrorHandlerFacade

ErrorHandlerSet is a combination of ErrorHandler and ErrorHandlerContext. It's sole purpose is to make the API of the package concise by exposing a common interface type for returned handlers. It's not supposed to be used by consumers of this package.

It goes directly against the "Use interfaces, return structs" idiom of Go, but at the current phase of the package the smaller API surface makes more sense.

In the future it might get replaced with concrete types.

Deprecated: use ErrorHandlerFacade.

type ErrorHandlers

type ErrorHandlers []ErrorHandler

ErrorHandlers combines a number of error handlers into a single one.

func (ErrorHandlers) Close

func (h ErrorHandlers) Close() error

Close calls Close on the underlying handlers (if there is any closable handler).

func (ErrorHandlers) Handle

func (h ErrorHandlers) Handle(err error)

func (ErrorHandlers) HandleContext

func (h ErrorHandlers) HandleContext(ctx context.Context, err error)

type ErrorMatcher

type ErrorMatcher func(err error) bool

ErrorMatcher checks if an error matches a certain condition.

type Errors

type Errors interface {
	// Errors returns the list of wrapped errors.
	Errors() []error
}

Errors is responsible for listing multiple errors. Deprecated: use multi error tools from emperror.dev/errors instead.

type Handler

type Handler interface {
	// Handle handles an error.
	Handle(err error)
}

Handler is a generic error handler. It allows applications (and libraries) to handle errors without worrying about the actual error handling strategy (logging, error tracking service, etc).

Deprecated: use ErrorHandler instead.

func HandlerWith

func HandlerWith(handler Handler, keyvals ...interface{}) Handler

HandlerWith returns a new error handler with keyvals context appended to it. If the wrapped error handler is already a contextual error handler created by HandlerWith or HandlerWithPrefix keyvals is appended to the existing context, but a new error handler is returned.

The created handler will prepend it's own context to the handled errors. Deprecated: use WithDetails instead.

func HandlerWithDetails

func HandlerWithDetails(handler Handler, details ...interface{}) Handler

HandlerWithDetails returns a new error handler annotated with key-value pairs.

The created handler will add it's own details to the handled errors. Deprecated: use WithDetails instead.

func NewCompositeHandler

func NewCompositeHandler(handlers ...Handler) Handler

NewCompositeHandler returns a new compositeHandler. Deprecated: use Handlers instead.

func NewNoopHandler

func NewNoopHandler() Handler

NewNoopHandler creates a no-op error handler that discards all received errors. Useful in examples and as a fallback error handler.

Deprecated: use NoopHandler.

type HandlerFunc

type HandlerFunc func(err error)

HandlerFunc wraps a function and turns it into an error handler.

Deprecated: use ErrorHandlerFunc.

func (HandlerFunc) Handle

func (h HandlerFunc) Handle(err error)

Handle calls the underlying function.

type Handlers

type Handlers []Handler

Handlers collects a number of error handlers into a single one.

Deprecated: use ErrorHandlers instead.

func (Handlers) Close

func (h Handlers) Close() error

Close calls Close on the underlying handlers (if there is any closable handler).

func (Handlers) Handle

func (h Handlers) Handle(err error)

type MultiErrorBuilder

type MultiErrorBuilder struct {
	Message        string
	SingleWrapMode SingleWrapMode
	// contains filtered or unexported fields
}

MultiErrorBuilder provides an interface for aggregating errors and exposing them as a single value. Deprecated: use multi error tools from emperror.dev/errors instead.

func NewMultiErrorBuilder

func NewMultiErrorBuilder() *MultiErrorBuilder

NewMultiErrorBuilder returns a new MultiErrorBuilder.

func (*MultiErrorBuilder) Add

func (b *MultiErrorBuilder) Add(err error)

Add adds an error to the list.

Calling this method concurrently is not safe.

func (*MultiErrorBuilder) ErrOrNil

func (b *MultiErrorBuilder) ErrOrNil() error

ErrOrNil returns a multiError the builder aggregates a list of errors, or returns nil if the list of errors is empty.

It is useful to avoid checking if there are any errors added to the list.

type NoopHandler

type NoopHandler struct{}

NoopHandler is a no-op error handler that discards all received errors.

It implements both ErrorHandler and ErrorHandlerContext interfaces.

func (NoopHandler) Handle

func (NoopHandler) Handle(_ error)

func (NoopHandler) HandleContext

func (NoopHandler) HandleContext(_ context.Context, _ error)

type SingleWrapMode

type SingleWrapMode int

SingleWrapMode defines how MultiErrorBuilder behaves when there is only one error in the list.

const (
	AlwaysWrap   SingleWrapMode = iota // Always return a multiError.
	ReturnSingle                       // Return the single error.
)

These constants cause MultiErrorBuilder to behave as described if there is only one error in the list.

type TestErrorHandler

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

TestErrorHandler is an ErrorHandler recording every error.

Useful when you want to test behavior with an ErrorHandler, but not with ErrorHandlerContext. In every other cases TestErrorHandlerFacade should be the default choice of test handler.

TestErrorHandler is safe for concurrent use.

func (*TestErrorHandler) Count

func (h *TestErrorHandler) Count() int

Count returns the number of recorded events.

func (*TestErrorHandler) Errors

func (h *TestErrorHandler) Errors() []error

Errors returns all handled errors.

func (*TestErrorHandler) Handle

func (h *TestErrorHandler) Handle(err error)

Handle records an error.

func (*TestErrorHandler) LastError

func (h *TestErrorHandler) LastError() error

LastError returns the last handled error (if any).

type TestErrorHandlerContext

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

TestErrorHandlerContext is an ErrorHandlerContext recording every error.

Useful when you want to test behavior with an ErrorHandlerContext, but not with ErrorHandler. In every other cases TestErrorHandlerFacade should be the default choice of test handler.

TestErrorHandlerContext is safe for concurrent use.

func (*TestErrorHandlerContext) Contexts

func (h *TestErrorHandlerContext) Contexts() []context.Context

Contexts returns contexts of all handled errors.

func (*TestErrorHandlerContext) Count

func (h *TestErrorHandlerContext) Count() int

Count returns the number of recorded events.

func (*TestErrorHandlerContext) Errors

func (h *TestErrorHandlerContext) Errors() []error

Errors returns all handled errors.

func (*TestErrorHandlerContext) HandleContext

func (h *TestErrorHandlerContext) HandleContext(ctx context.Context, err error)

HandleContext records an error.

func (*TestErrorHandlerContext) LastContext

func (h *TestErrorHandlerContext) LastContext() context.Context

LastContext returns the context of the last handled error (if any).

func (*TestErrorHandlerContext) LastError

func (h *TestErrorHandlerContext) LastError() error

LastError returns the last handled error (if any).

type TestErrorHandlerFacade

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

TestErrorHandlerFacade is an ErrorHandlerFacade recording every error.

TestErrorHandlerFacade is safe for concurrent use.

func (*TestErrorHandlerFacade) Contexts

func (h *TestErrorHandlerFacade) Contexts() []context.Context

Contexts returns contexts of all handled errors.

func (*TestErrorHandlerFacade) Count

func (h *TestErrorHandlerFacade) Count() int

Count returns the number of recorded events.

func (*TestErrorHandlerFacade) Errors

func (h *TestErrorHandlerFacade) Errors() []error

Errors returns all handled errors.

func (*TestErrorHandlerFacade) Handle

func (h *TestErrorHandlerFacade) Handle(err error)

Handle records an error.

func (*TestErrorHandlerFacade) HandleContext

func (h *TestErrorHandlerFacade) HandleContext(ctx context.Context, err error)

HandleContext records an error.

func (*TestErrorHandlerFacade) LastContext

func (h *TestErrorHandlerFacade) LastContext() context.Context

LastContext returns the context of the last handled error (if any).

func (*TestErrorHandlerFacade) LastError

func (h *TestErrorHandlerFacade) LastError() error

LastError returns the last handled error (if any).

type TestErrorHandlerSet

type TestErrorHandlerSet = TestErrorHandlerFacade

TestErrorHandlerSet is an ErrorHandlerSet recording every error.

TestErrorHandlerSet is safe for concurrent use.

Deprecated: use TestErrorHandlerFacade.

type TestHandler

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

TestHandler is a simple stub for the handler interface recording every error.

The TestHandler is safe for concurrent use.

Deprecated: use TestErrorHandler.

func NewTestHandler

func NewTestHandler() *TestHandler

NewTestHandler returns a new TestHandler.

func (*TestHandler) Count

func (h *TestHandler) Count() int

Count returns the number of events recorded in the logger.

func (*TestHandler) Errors

func (h *TestHandler) Errors() []error

Errors returns all handled errors.

func (*TestHandler) Handle

func (h *TestHandler) Handle(err error)

Handle records the error.

func (*TestHandler) LastError

func (h *TestHandler) LastError() error

LastError returns the last handled error (if any).

Directories

Path Synopsis
httperr
utils/keyvals