errstack

package module
v1.0.2 Latest Latest
Warning

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

Go to latest
Published: Mar 9, 2020 License: Apache-2.0 Imports: 10 Imported by: 36

README

errstack

GoDoc

stacked error wrappers

Package errstack provides a way to augment errors with a functional type and iteratively build new request error.

How to handle errors?

Rule of thumb:

An error should be handled only once. Logging an error is handling an error. So an error should either be logged or propagated.

Motivation

In software we have 3 types of errors:

  • Infrastructure - this errors come from the requests to other services.
  • Domain - this is the type of errors when we detect that our internal application state is wrong.
  • Request - a user request error.

Request errors maps parameters (application inputs) to the cause description (why the value of wrong).

Domain errors usually arise form some model bug or unhandled condition.

Infrastructure errors arise from the system or connection error.

It is very handy to know the place (stack) where the error Infrastructure or Domain error arose - that's why they are threatened especially and are augmented with the stack trace.

Credits

The initial work was done for the AgFlow project.

Documentation

Overview

Package errstack provides a way to augment errors with a functional type and iteratively build new request error.

In software we have 3 types of errors:

* Infrastructure - this errors come from the requests to other services.

* Domain - this is the type of errors when we detect that our internal application state is wrong.

* Request - a user request error.

Request errors maps parameters (or input data fields) to the explanation (why the value of wrong).

Domain errors usually arise form some model bug or unhandled condition.

Infrastructure errors arise from the system or connection error.

It is very handy to know the place (stack) where the error Infrastructure or Domain error arose - that's why they are threatened especially and are augmented with the stack trace.

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

func CallAndAssign

func CallAndAssign(err *error, f func() error)

CallAndAssign assigns error returned from the function to the err pointer if needed. Usually useful in defer statements, eg:

CallAndAssign(&err, someFile.Close)

func CallAndLog

func CallAndLog(l Logger, f func() error)

CallAndLog calls function which may return error and logs it. The intention of this function is to be used with `go` and `defer` clauses.

func IsKind

func IsKind(kind Kind, err error) bool

IsKind reports whether err is an *Error of the given Kind. If err is nil then Is returns false.

func IsTimeout

func IsTimeout(e error) bool

IsTimeout checks if given error is a timeout (net/url.Error)

func Join

func Join(es ...error) error

Join creates a new error from list of errors. It filters out nil errors. If there is no not-nil error it returs nil.

func Log

func Log(l Logger, err error)

Log logs error if it's not nil

func RootErr

func RootErr(err error) error

RootErr returns the underlying cause of the error, if possible. Normally it should be the root error. This method uses `HasUnderlying` interface to extract the cause error.

If the error does not implement the `HasUnderlying`, the original error will be returned. If the error is nil, nil will be returned without further investigation.

func Seq

func Seq(ss ...string) error

Seq returns a standard error from a string sequence

Types

type Builder

type Builder interface {
	// Fork creates a new builder which shares the same space but all new added errors
	// will be assigned to keys prefixed with `prefix`
	Fork(prefix string) Builder
	// ForkIdx is a handy function to call Fork with an `int` (eg: index keys / rows)
	ForkIdx(idx int) Builder

	// Putter returns a Putter which abstract error setting from error key.
	Putter(key string) Putter

	// Puts new error under the key. You can put multiple errors under the same key
	// and they will be agregated
	Put(key string, value interface{})
	// Get returns errors under `key`. Get is aware about 'prefix' and it will add it
	// to the the key.
	Get(key string) interface{}

	// NotNil checks if there are any errors in the builder.
	NotNil() bool
	// Converts the Builder into a request error.
	ToReqErr() E
	// ToList transforms Builder errors into a list
	ToList() []ListNode
}

Builder is a type to incrementally build set of errors under common key structure Builder intentionally doesn't implement standard Error interface. You have to explicitly convert it into an Error (using ToReqErr) once all checks are done. Basic idea of builder is to easily combine request errors. Example:

var errb = NewBuilder()
if len(obj.Name) < 3 {
	errb.Put("first_name", "name is too short")
}
if strings.Contains(obj.Name, "%") {
	errb.Put("first_name", "name contains invalid characters")
}
...
return errb.ToReqErr()

func NewBuilder

func NewBuilder() Builder

NewBuilder creates new builders with given prefix being appended to each error keys. This structure is not thread safe.

type E

type E interface {
	error
	HasStatusCode
	HasStacktrace
	json.Marshaler
	IsReq() bool
	Kind() Kind
	WithMsg(string) E
	Details() map[string]interface{}
	Add(key string, payload interface{}) // add more details to the error
}

E is error with more information. It is able to marshall itself to json as response. Result of Error() method should include stacktrace. Therefore it should not be displayed directly to the user

func New

func New(kind Kind, msg string) E

New creates a new error E

func NewDomain

func NewDomain(s string) E

NewDomain creates new domain error from string Domain error is classified as an Infrastructure error.

func NewDomainF

func NewDomainF(format string, a ...interface{}) E

NewDomainF creates new domain error using string formatter

func NewIO

func NewIO(s string) E

NewIO creates new infrastructural error from string

func NewIOf

func NewIOf(f string, a ...interface{}) E

NewIOf creates new infrastructural error using string formatter

func NewReq

func NewReq(s string) E

NewReq creates request error from string

func NewReqDetails

func NewReqDetails(key string, details interface{}, msg string) E

NewReqDetails creates a request error. Key inform which request parameter was invalid and details contains reason of error

func NewReqF

func NewReqF(f string, a ...interface{}) E

NewReqF creates request error from format

func Wrap

func Wrap(err error, kind Kind, msg string) E

Wrap creates new error using error and string message

func WrapAsDomain

func WrapAsDomain(err error, message string) E

WrapAsDomain creates new domain error using error and string message Domain error is classified as an Infrastructure error.

func WrapAsDomainF

func WrapAsDomainF(err error, f string, a ...interface{}) E

WrapAsDomainF creates new domain error wrapping given error and using string formatter for description. Domain error is classified as an Infrastructure error.

func WrapAsIO

func WrapAsIO(e error, messages ...string) E

WrapAsIO creates new infrastructure error from simple error If input argument is nil, nil is returned.

func WrapAsIOf

func WrapAsIOf(err error, f string, a ...interface{}) E

WrapAsIOf creates new infrastructural error wrapping given error and using string formatter for description.

func WrapAsReq

func WrapAsReq(err error, message string) E

WrapAsReq creates new request error from simple error If input argument is nil, nil is returned. If input argument is already errstack.E, it is returned unchanged.

func WrapAsReqF

func WrapAsReqF(err error, f string, a ...interface{}) E

WrapAsReqF creates new request error from simple error and creates message from format If input argument is nil, nil is returned. If input argument is already errstack.E, it is returned unchanged.

type HasStacktrace

type HasStacktrace interface {
	Stacktrace() stack.Stack
}

HasStacktrace provides a function to return the the root stacktrace

type HasStatusCode

type HasStatusCode interface {
	StatusCode() int
}

HasStatusCode provides a function to return the HTTP status code.

type HasUnderlying

type HasUnderlying interface {
	// Cause returns the underlying error (if any) causing the failure.
	Cause() error
}

HasUnderlying describes entity (usually an error) which has underlying error.

type Kind

type Kind uint8

Kind defines the kind of error that must act differently depending on the error

const (
	Other         Kind = iota // Unclassified error.
	Invalid                   // Invalid operation for this type of item.
	Permission                // Permission denied.
	IO                        // I/O error such as network failure.
	Exist                     // Item already exists.
	NotExist                  // Item does not exist.
	IsDir                     // Item is a directory.
	NotDir                    // Item is not a directory.
	NotEmpty                  // Directory not empty.
	Private                   // Information withheld.
	CannotDecrypt             // No wrapped key for user with read access.
	Transient                 // A transient error.
	BrokenLink                // Link target does not exist.
	Request                   // General request error
	Domain                    // Internal error causing business domain problem or inconsistency
)

Kinds of errors.

type ListNode

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

ListNode is a representation used by Builder.ToList interface function

type Logger

type Logger interface {
	Error(msg string, ctx ...interface{})
}

Logger defines the reporting interface for utils error functions

type Putter

type Putter interface {
	Put(interface{})
	Fork(prefix string) Putter
	ForkIdx(idx int) Putter
}

Putter is an interface which provides a way to set an error abstracting from it's identifier (key). Please refer to the Builder documentation to see the example below without using Putter. Example:

validateName(obj.FirstName, errb.Putter("first_name"))
validateName(obj.LastName, errb.Putter("last_name"))

func validateName(name string, errp.Putter) {
	if len(name) < 3 {
		errp.Put(name is too short")
	}
	if strings.Contains(name, "%") {
		errp.Put(name contains invalid characters")
	}
}

type StubPutter

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

StubPutter ignores all input. It only contains information whether any input was submitted

func (*StubPutter) Fork

func (rp *StubPutter) Fork(_ string) Putter

Fork does nothing

func (*StubPutter) ForkIdx

func (rp *StubPutter) ForkIdx(_ int) Putter

ForkIdx does nothing

func (*StubPutter) HasError

func (rp *StubPutter) HasError() bool

HasError checks if any error occurred

func (*StubPutter) Put

func (rp *StubPutter) Put(_ interface{})

Put adds new error

type UntilFirst

type UntilFirst struct {
	Err E
}

UntilFirst is a struct to easily chain a sequence of operations until a first error arise

func UntilFirstDo

func UntilFirstDo(f func() E) *UntilFirst

UntilFirstDo is utility method for using UntilFirst

func (*UntilFirst) Do

func (ue *UntilFirst) Do(f func() E) *UntilFirst

Do performs an action

Jump to

Keyboard shortcuts

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