errors

package module
v0.1.0 Latest Latest
Warning

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

Go to latest
Published: Feb 2, 2023 License: Apache-2.0 Imports: 7 Imported by: 8

README

Errors

A modern error handling package to add additional structured fields to errors. This allows you to keep the only handle errors once rule while not losing context where the error occurred.

  • errors.Wrap(err, "while reading") includes a stack trace so logging can report the exact location where the error occurred. You can also call Wrapf()
  • errors.WithStack(err) for when you don't need a message, just a stack trace to where the error occurred.
  • errors.WithFields{"fileName": fileName}.Wrap(err, "while reading") Attach additional fields to the error and a stack trace to give structured logging as much context to the error as possible. You can also call Wrapf()
  • errors.WithFields{"fileName": fileName}.WithStack(err) for when you don't need a message, just a stack trace and some fields attached.
  • errors.WithFields{"fileName": fileName}.Error("while reading") when you want to create a string error with some fields attached. You can also call Errorf()
Extract structured data from wrapped errors

Convenience functions to extract all stack and field information from the error.

  • errors.ToLogrus() logrus.Fields
  • errors.ToMap() map[string]interface{}
Example
err := io.EOF
err = errors.WithFields{"fileName": "file.txt"}.Wrap(err, "while reading")
m := errors.ToMap(err)
fmt.Printf("%#v\n", m)
// OUTPUT
// map[string]interface {}{
//   "excFileName":"/path/to/wrap_test.go",
//   "excFuncName":"my_package.ReadAFile",
//   "excLineNum":42,
//   "excType":"*errors.errorString",
//   "excValue":"while reading: EOF",
//   "fileName":"file.txt"
//  }

Convenience to std error library methods

Provides pass through access to the standard errors.Is(), errors.As(), errors.Unwrap() so you don't need to import this package and the standard error package.

Supported by internal tooling

If you are working at mailgun and are using scaffold; using logrus.WithError(err) will cause logrus to automatically retrieve the fields attached to the error and index them into our logging system as separate searchable fields.

Perfect for passing additional information to http handler middleware

If you have custom http middleware for handling unhandled errors, this is an excellent way to easily pass additional information about the request up to the error handling middleware.

Adding structured fields to an error

Wraps the original error while providing structured field data

_, err := ioutil.ReadFile(fileName)
if err != nil {
        return errors.WithFields{"file": fileName}.Wrap(err, "while reading")
}

Retrieving the structured fields

Using errors.WithFields{} stores the provided fields for later retrieval by upstream code or structured logging systems

// Pass to logrus as structured logging
logrus.WithFields(errors.ToLogrus(err)).Error("open file error")

Support for standard golang introspection functions

Errors wrapped with errors.WithFields{} are compatible with standard library introspection functions errors.Unwrap(), errors.Is() and errors.As()

ErrQuery := errors.New("query error")
wrap := errors.WithFields{"key1": "value1"}.Wrap(err, "message")
errors.Is(wrap, ErrQuery) // == true

Proper Usage

The fields wrapped by errors.WithFields{} are not intended to be used to by code to decide how an error should be handled. It is intended as a convenience where the failure is well known, but the context is dynamic. In other words, you know the database returned an unrecoverable query error, but you want to attach localized context information to the error.

As an example

func (r *Repository) FetchAuthor(customerID, isbn string) (Author, error) {
    // Returns ErrorNotFound{} if not exist
    book, err := r.fetchBook(isbn)
    if err != nil {
        return nil, errors.WithFields{"customer.id": customerID, "isbn": isbn}.Wrap(err, "while fetching book")
    }
    // Returns ErrorNotFound{} if not exist
    author, err := r.fetchAuthorByBook(book)
    if err != nil {
        return nil, errors.WithFields{"customer.id" customerID, "book": book}.Wrap(err, "while fetching author")
    }
    return author, nil
}

Now you can easily search your structured logs for errors related to customer.id.

You should continue to create and inspect custom error types


type ErrAuthorNotFound struct {
    Msg string
}

func (e *ErrAuthorNotFound) Error() string {
    return e.Msg
}

func (e *ErrAuthorNotFound) Is(target error) bool {
    _, ok := target.(*NotFoundError)
    return ok
}

func main() {
    r := Repository{}
    author, err := r.FetchAuthor("isbn-213f-23422f52356")
    if err != nil {
        // Fetch the original and determine if the error is recoverable
        if error.Is(err, &ErrAuthorNotFound{}) {
            author, err := r.AddBook("isbn-213f-23422f52356", "charles", "darwin")
        }
        if err != nil {
            logrus.WithFields(errors.ToLogrus(err)).
				WithError(err).Error("while fetching author")
            os.Exit(1)
        }
    }
    fmt.Printf("Author %+v\n", author)
}

Documentation

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

func As

func As(err error, target any) bool

As finds the first error in err's chain that matches target, and if so, sets target to that error value and returns true.

func Is

func Is(err, target error) bool

Is reports whether any error in err's chain matches target.

func Last

func Last(err error, target any) bool

Last finds the last error in err's chain that matches target, and if one is found, sets target to that error value and returns true. Otherwise, it returns false.

The chain consists of err itself followed by the sequence of errors obtained by repeatedly calling Unwrap.

An error matches target if the error's concrete value is assignable to the value pointed to by target, or if the error has a method `As(any) bool` such that As(target) returns true.

An error type might provide an As() method so it can be treated as if it were a different error type.

Last panics if target is not a non-nil pointer to either a type that implements error, or to any interface type.

NOTE: Last() is much slower than As(). Therefore As() should always be used unless you absolutely need Last() to retrieve the last error in the error chain that matches the target.

func New

func New(text string) error

New returns an error that formats as the given text. Each call to New returns a distinct error value even if the text is identical.

func ToLogrus

func ToLogrus(err error) logrus.Fields

ToLogrus Returns the context and stacktrace information for the underlying error as logrus.Fields{} returns empty logrus.Fields{} if err has no context or no stacktrace

logrus.WithFields(errors.ToLogrus(err)).WithField("tid", 1).Error(err)

func ToMap

func ToMap(err error) map[string]interface{}

ToMap Returns the context for the underlying error as map[string]interface{} If no context is available returns nil

func Unwrap

func Unwrap(err error) error

Unwrap returns the result of calling the Unwrap method on err, if err's type contains an Unwrap method returning error. Otherwise, Unwrap returns nil.

func WithStack

func WithStack(err error) error

WithStack annotates err with a stack trace at the point WithStack was called. If err is nil, WithStack returns nil.

func Wrap

func Wrap(err error, msg string) error

Wrap wraps the error and attaches stack information to the error

func Wrapf

func Wrapf(err error, format string, a ...any) error

Wrapf is identical to Wrap but formats the error before wrapping.

Types

type HasFields

type HasFields interface {
	Fields() map[string]interface{}
}

HasFields Implement this interface to pass along unstructured context to the logger

type HasFormat

type HasFormat interface {
	Format(st fmt.State, verb rune)
}

HasFormat True if the interface has the format method (from fmt package)

type WithFields

type WithFields map[string]interface{}

WithFields Creates errors that conform to the `HasFields` interface

func (WithFields) Error

func (f WithFields) Error(msg string) error

func (WithFields) Errorf

func (f WithFields) Errorf(format string, args ...interface{}) error

func (WithFields) WithStack

func (f WithFields) WithStack(err error) error

WithStack returns an error annotating err with a stack trace at the point WithStack is called If err is nil, WithStack returns nil.

func (WithFields) Wrap

func (f WithFields) Wrap(err error, msg string) error

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.

func (WithFields) Wrapf

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

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.

Directories

Path Synopsis

Jump to

Keyboard shortcuts

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