Documentation
¶
Overview ¶
Package errors provides sophisticated, but simple error handling for Go. Refer to the README.md, examples and unit tests for usage and details.
The original idea and code was based on https://github.com/upspin/upspin/blob/master/errors/errors.go, but has undergone substantial re-factoring to simplify usage and provide additional flexibility.
Index ¶
- Variables
- func Append(err error, errs ...error) error
- func As(err error, target interface{}) bool
- func ClearStacktrace(err error) error
- func Field(err error, key string) interface{}
- func GetField(err error, key string) (string, bool)
- func GetRootCause(err error) error
- func Ignore(f func() error)
- func Is(err, target error) bool
- func IsKind(expected Kind, err interface{}) bool
- func IsNotExist(err error) bool
- func Match(err1, err2 error) bool
- func PopulateStacktrace() bool
- func SetPopulateStacktrace(b bool)
- func Str(text string) error
- func TypeOf(val interface{}) string
- func Unwrap(err error) error
- func UnwrapAll(err error) error
- type DefaultKind
- type Error
- func (e *Error) Cause() error
- func (e *Error) ClearStacktrace() *Error
- func (e *Error) Error() string
- func (e *Error) ErrorNoTrace() string
- func (e *Error) Field(key string) interface{}
- func (e *Error) FormatError(printStack bool, fieldOrder ...string) string
- func (e *Error) GetField(key string) (string, bool)
- func (e *Error) Kind() Kind
- func (e *Error) MarshalJSON() ([]byte, error)
- func (e *Error) Op() string
- func (e *Error) UnmarshalJSON(b []byte) error
- func (e *Error) Unwrap() error
- func (e *Error) With(args ...interface{}) *Error
- func (e *Error) WithCause(err error) *Error
- func (e *Error) WithDefaultKind(kind Kind) *Error
- func (e *Error) WithKind(kind Kind) *Error
- func (e *Error) WithOp(op string) *Error
- type ErrorList
- type Kind
- type TFn
- type TemplateFn
Examples ¶
Constants ¶
This section is empty.
Variables ¶
var DefaultFieldOrder []string = nil
DefaultFieldOrder defines the default order of an Error's fields in its String and JSON representations.
The first empty string "" acts as alias for all fields that are unreferenced in the field order slice. They are printed in the order they were added in E() and With().
Trailing field keys after "" will be printed after any unreferenced fields.
"op", "kind" and "cause" fields are specially treated if they don't appear in the order slice. "op" and "kind" will be printed first among the unreferenced fields. "cause" will be printed last (even after all trailing fields). Hence the default nil is equivalent to []string{"op", "kind", "", "cause"}
var K = struct { Other Kind // Unclassified error. NotImplemented Kind // The functionality is not yet implemented. Invalid Kind // Invalid operation for this type of item. Permission Kind // Permission denied. IO Kind // External I/O error such as network failure. Exist Kind // Item already exists. NotExist Kind // Item does not exist. Also see NotFound! NotFound Kind // Item should exist but cannot be found. Also see NotExist! NotDir Kind // Item is not a directory. Finalized Kind // Part or content is already finalized. NotFinalized Kind // Part or content is not yet finalized. NoNetRoute Kind // No route found for the requested operation Internal Kind // Generic internal error AVProcessing Kind // error in audio/video processing AVInput Kind // error encountered in media input (stall, corruption, unexpected) NoMediaMatch Kind // None of the accepted media type matches the content Unavailable Kind // The server cannot handle the request temporarily (e.g. overloaded or down for maintenance). Cancelled Kind // The operation was cancelled. Timeout Kind // The operation was timed out. Warn Kind // The error is not an actual error, but rather a warning that something might be wrong. }{ Other: "unclassified error", NotImplemented: "not implemented", Invalid: "invalid", Permission: "permission denied", IO: "I/O error", Exist: "item already exists", NotExist: "item does not exist", NotFound: "item cannot be found", Finalized: "item is already finalized", NotFinalized: "item is not finalized", NoNetRoute: "no route found", Internal: "internal error", AVProcessing: "a/v processing error", AVInput: "a/v input error", NoMediaMatch: "no media type match", Unavailable: "service unavailable", Cancelled: "operation cancelled", Timeout: "operation timed out", Warn: "warning", }
K defines the kinds of errors.
var MarshalStacktrace = true
MarshalStacktrace controls whether stacktraces are marshaled to JSON or not. If enabled, an extra "stacktrace" field is added to the error's JSON struct.
var MarshalStacktraceAsArray = true
MarshalStacktraceAsArray controls whether stacktraces are marshaled to JSON as a single string blob or as JSON array containing the individual lines of the stacktrace.
var NilError = (*nilError)(nil)
NilError is an error that represents a "nil" error value, but allows to call the Error() method without panicking. NilError.Error() returns the empty string "".
var PrintStacktrace = true
PrintStacktrace controls whether stacktraces are printed per default or not.
var PrintStacktracePretty = true
PrintStacktracePretty enables additional formatting of stacktraces by aligning functions to the longest source filename.
Pretty print:
github.com/eluv-io/errors-go/stack_with_long_filename_test.go:6 createErrorWithExtraLongFilename() github.com/eluv-io/errors-go/stack_test.go:108 func4() github.com/eluv-io/errors-go/stack_test.go:109 func4() github.com/eluv-io/errors-go/stack_test.go:104 T.func3() github.com/eluv-io/errors-go/stack_test.go:99 T.func2() github.com/eluv-io/errors-go/stack_test.go:90 func1() github.com/eluv-io/errors-go/stack_test.go:47 TestStack() testing/testing.go:909 tRunner() runtime/asm_amd64.s:1357 goexit()
Regular:
github.com/eluv-io/errors-go/stack_with_long_filename_test.go:6 createErrorWithExtraLongFilename() github.com/eluv-io/errors-go/stack_test.go:108 func4() github.com/eluv-io/errors-go/stack_test.go:109 func4() github.com/eluv-io/errors-go/stack_test.go:104 T.func3() github.com/eluv-io/errors-go/stack_test.go:99 T.func2() github.com/eluv-io/errors-go/stack_test.go:90 func1() github.com/eluv-io/errors-go/stack_test.go:47 TestStack() testing/testing.go:909 tRunner() runtime/asm_amd64.s:1357 goexit()
var Separator = ":\n\t"
Separator is the string used to separate nested errors. By default, nested errors are indented on a new line.
Functions ¶
func Append ¶
Append appends one or multiple errors `errs` to the *ErrorList err and returns the updated error list.
If err is not an *ErrorList (any other type of error or nil), then a new list is created and err added to it. Then all additional errs are appended, unwrapping them if any of them are ErrorLists themselves.
Any nil errors within errs will be ignored. If the final list contains a single error, the error is returned instead of the list.
Example ¶
{ fmt.Println("nil error:") fmt.Println(errors.Append(nil, nil)) fmt.Println() } { var err error err = errors.Append(err, io.EOF) fmt.Println("single error:") fmt.Println(err) fmt.Println() } { var err error err = errors.Append(err, io.EOF) err = errors.Append(err, io.ErrNoProgress) err = errors.Append(err, io.ErrClosedPipe) fmt.Println("multiple errors:") fmt.Println(err) } { var err error err = errors.Append(errors.E("read", errors.K.IO, io.EOF), errors.E("write", errors.K.Cancelled)) fmt.Println("complex errors:") fmt.Println(err) /* With stacktraces enabled it would look like this: error-list count [2] 0: op [read] kind [I/O error] cause [EOF] github.com/eluv-io/errors-go/list_test.go:192 ExampleAppend() testing/run_example.go:64 runExample() testing/example.go:44 runExamples() testing/testing.go:1505 (*M).Run() _testmain.go:165 main() 1: op [write] kind [operation cancelled] github.com/eluv-io/errors-go/list_test.go:192 ExampleAppend() testing/run_example.go:64 runExample() testing/example.go:44 runExamples() testing/testing.go:1505 (*M).Run() _testmain.go:165 main() */ }
Output: nil error: <nil> single error: EOF multiple errors: error-list count [3] 0: EOF 1: multiple Read calls return no data or error 2: io: read/write on closed pipe complex errors: error-list count [2] 0: op [read] kind [I/O error] cause [EOF] 1: op [write] kind [operation cancelled]
func As ¶
As is a convenience function that simply calls the stdlib errors.As(): As finds the first error in err's chain that matches target, and if so, sets target to that error value and returns true.
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(interface{}) bool such that As(target) returns true. In the latter case, the As method is responsible for setting target.
As will panic if target is not a non-nil pointer to either a type that implements error, or to any interface type. As returns false if err is nil.
func ClearStacktrace ¶
ClearStacktrace removes the stacktrace from the given error if it's an instance of *Error. Does nothing otherwise.
func Field ¶ added in v1.0.3
Field returns the result of calling the Field() method on the given err if it is an *Error. Returns nil otherwise.
func GetField ¶
GetField returns the result of calling the GetField() method on the given err if it is an *Error. Returns "", false otherwise.
func GetRootCause ¶
GetRootCause returns the first nested error that is not an *Error object. Returns NilError if err is nil.
func Ignore ¶
func Ignore(f func() error)
Ignore simply ignores a potential error returned by the given function.
Useful in defer statements where the deferred function returns an error, i.e.
defer writer.Close()
can be written as
defer errors.Ignore(writer.Close)
func Is ¶
Is is a convenience function that simply calls the stdlib errors.Is() Is reports whether any error in err's chain matches target.
The chain consists of err itself followed by the sequence of errors obtained by repeatedly calling Unwrap.
An error is considered to match a target if it is equal to that target or if it implements a method Is(error) bool such that Is(target) returns true.
func IsKind ¶
IsKind reports whether err is an *Error of the given Kind. Returns false if err is nil.
func IsNotExist ¶
IsNotExist reports whether err is an *Error of Kind NotExist. Returns false if err is nil.
func Match ¶
Match compares two errors.
If one of the arguments is not of type *Error, Match will return reflect.DeepEqual(err1, err2).
Otherwise it returns true iff every non-zero element of the first error is equal to the corresponding element of the second. If the Cause field is a *Error, Match recurs on that field; otherwise it compares the strings returned by the Error methods. Elements that are in the second argument but not present in the first are ignored.
For example:
Match(errors.E("authorize", errors.Permission), err)
tests whether err is an Error with op=authorize and kind=Permission.
Example ¶
err := errors.Str("network unreachable") // Construct an error, one we pretend to have received from a test. got := errors.E("get", errors.K.IO, err) // Now construct a reference error, which might not have all // the fields of the error from the test. expect := errors.E().WithKind(errors.K.IO).WithCause(err) fmt.Println("Match:", errors.Match(expect, got)) // Now one that's incorrect - wrong Kind. got = errors.E().WithOp("get").WithKind(errors.K.Permission).WithCause(err) fmt.Println("Mismatch:", errors.Match(expect, got))
Output: Match: true Mismatch: false
func PopulateStacktrace ¶
func PopulateStacktrace() bool
func SetPopulateStacktrace ¶ added in v1.0.3
func SetPopulateStacktrace(b bool)
func TypeOf ¶
func TypeOf(val interface{}) string
TypeOf returns the type of the given value as string.
Types ¶
type DefaultKind ¶
type DefaultKind string
DefaultKind is a Kind that can be set on an Error or Template that is only used if the kind is not otherwise set e.g. with an explicit call to Error.Kind(kind) or by inheriting it from a nested error.
e := errors.Template("read user", errors.K.Invalid.Default()) return e(nested)
In the above example, the returned error will have the nested error's kind if it's defined or K.Invalid otherwise. If the template definition didn't use Default(), the returned error would always be K.Invalid, regardless of the kind in the nested error.
type Error ¶
type Error struct {
// contains filtered or unexported fields
}
Error is the type that implements the error interface and which is returned by E(), NoTrace(), etc.
Example ¶
// Single error. e1 := errors.E("get", errors.K.IO, io.EOF) fmt.Println("\nSimple error:") fmt.Println(e1) // Nested error. fmt.Println("\nNested error:") e2 := errors.E("read", e1) fmt.Println(e2)
Output: Simple error: op [get] kind [I/O error] cause [EOF] Nested error: op [read] kind [I/O error] cause: op [get] kind [I/O error] cause [EOF]
func E ¶
func E(args ...interface{}) *Error
E creates a new error initialized with the given (optional) operation, kind, cause and key-value fields. All arguments are optional, but if provided they have to be specified in that order.
If no kind is specified, the kind of the cause is used if available. Otherwise, K.Other is assigned.
The op should specify the operation that failed, e.g. "download" or "load config". It should not be an error "message" like "download failed" or "failed to load config" - the fact that the operation has failed is implied by the error itself.
Examples:
errors.E() --> error with kind set to errors.K.Other, all other fields empty errors.E("download") --> same as errors.E().WithOp("download") errors.E("download", errors.K.IO) --> same as errors.E().WithOp("download").WithKind(errors.K.IO) errors.E("download", errors.K.IO, err) --> same as errors.E().WithOp("download").WithKind(errors.K.IO).WithCause(err) errors.E("download", errors.K.IO, err, "file", f, "user", usr) --> same as errors.E()...With("file", f).With("user", usr) errors.E(errors.K.NotExist, "file", f) --> same as errors.E().WithKind(errors.K.NotExist).With("file", f)
Example ¶
reset := enableStacktraces() defer reset() err := getConfig() printError(err)
Output: op [getConfig] kind [I/O error] cause: op [readFile] kind [I/O error] filename [illegal-filename|*] cause [open illegal-filename|*: no such file or directory] github.com/eluv-io/errors-go/stack_example_test.go: readFile() github.com/eluv-io/errors-go/stack_example_test.go: getConfig() github.com/eluv-io/errors-go/stack_example_test.go: getConfig() github.com/eluv-io/errors-go/stack_example_test.go: ExampleE()
func FromContext ¶
FromContext creates an error from the given context and additional error arguments as passed to E(). It returns
- nil if ctx.Err() returns nil
- an error from the given args and kind Timeout if the ctx timed out
- an error from the given args and kind Cancelled if the ctx was cancelled
- an error from the given args and the cause set to ctx.Err() otherwise.
func GetRoot ¶
func GetRoot(err interface{}) *Error
GetRoot returns the innermost nested *Error of the given error, or nil if the provided object is not an *Error.
func NoTrace ¶
func NoTrace(args ...interface{}) *Error
NoTrace is the same as E, but does not populate a stack trace. Use in cases where the stacktrace is not desired.
func Wrap ¶
Wrap wraps the given error in an Error instance with E(err) if err is not an *Error itself - otherwise returns err as *Error unchanged. Returns nil if err is nil.
func (*Error) ClearStacktrace ¶
ClearStacktrace creates a copy of this error and removes the stacktrace from it and all nested causes.
func (*Error) Error ¶
Error returns the string presentation of this Error. Fields are ordered according to DefaultFieldOrder. The stacktrace is printed if available.
Example ¶
defer resetDefaultFieldOrder()() err := errors.E("get user", errors.K.IO, io.EOF, "account", "acme", "user", "joe") fmt.Println(err) errors.DefaultFieldOrder = []string{"op", "kind", "", "cause"} // same as default (nil) fmt.Println(err) errors.DefaultFieldOrder = []string{"kind"} fmt.Println(err) errors.DefaultFieldOrder = []string{"op", "user"} fmt.Println(err) errors.DefaultFieldOrder = []string{"op", "cause"} fmt.Println(err) fmt.Println() fmt.Println("Nested:") err = errors.E("get info", errors.K.Invalid, err) errors.DefaultFieldOrder = nil fmt.Println(err) // not putting the "cause" last is a bad idea with nested errors... errors.DefaultFieldOrder = []string{"op", "cause"} fmt.Println(err)
Output: op [get user] kind [I/O error] account [acme] user [joe] cause [EOF] op [get user] kind [I/O error] account [acme] user [joe] cause [EOF] kind [I/O error] op [get user] account [acme] user [joe] cause [EOF] op [get user] user [joe] kind [I/O error] account [acme] cause [EOF] op [get user] cause [EOF] kind [I/O error] account [acme] user [joe] Nested: op [get info] kind [invalid] cause: op [get user] kind [I/O error] account [acme] user [joe] cause [EOF] op [get info] cause: op [get user] cause [EOF] kind [I/O error] account [acme] user [joe] kind [invalid]
func (*Error) ErrorNoTrace ¶
ErrorNoTrace returns the error as string just like Error() but omits the stack trace.
func (*Error) Field ¶
Field returns the given field from this error or any nested errors. Returns nil if the field does not exist.
func (*Error) FormatError ¶
FormatError converts this error to a string like String(), but prints fields according to the given field order. See DefaultFieldOrder for more information.
A stacktrace (if available) is printed if printStack is true.
Example ¶
jsn := `{` e := errors.Template("parse", errors.K.Invalid, "account", 5, "json", jsn) var err error var user map[string]interface{} if err = json.Unmarshal([]byte(jsn), &user); err != nil { err = e(err, "reason", "failed to decode user") } fmt.Println(err) fmt.Println(errors.Wrap(err).FormatError(false, "op", "kind", "reason", "", "cause"))
Output: op [parse] kind [invalid] account [5] json [{] reason [failed to decode user] cause [unexpected end of JSON input] op [parse] kind [invalid] reason [failed to decode user] account [5] json [{] cause [unexpected end of JSON input]
func (*Error) GetField ¶
GetField attempts to retrieve the field with the given key in this Error and returns its value converted to a string with fmt.Sprint(val). If the field doesn't exist, it tries to find it (recursively) in the 'cause' of the error. Returns the retrieved field value and true if found, or the empty string and false if not found.
func (*Error) MarshalJSON ¶
MarshalJSON marshals this error as a JSON object
func (*Error) UnmarshalJSON ¶
UnmarshalJSON unmarshals the given JSON text, retaining the order of fields according to the JSON structure.
func (*Error) With ¶
With adds additional context information in the form of key value pairs and returns this error instance for call chaining.
func (*Error) WithCause ¶
WithCause sets the given original error and returns this error instance for call chaining. If the cause is an *Error and this error's kind is not yet initialized, it inherits the kind of the cause.
func (*Error) WithDefaultKind ¶
WithDefaultKind sets the given kind as default and returns this error instance for call chaining. The default kind is only used if the kind is not otherwise set e.g. with an explicit call to Error.Kind(kind) or by inheriting it from a nested error. It's equivalent to calling Error.With(kind.Default()).
type ErrorList ¶
type ErrorList struct {
Errors []error
}
ErrorList is a collection of errors.
func UnmarshalJsonErrorList ¶
UnmarshalJsonErrorList unmarshals a list of errors. JSON objects are unmarshalled into Error objects, strings into generic errors created with Str(s). Empty objects or strings are ignored.
The exact JSON structure is:
{ "errors": [ {"op": "op1"}, "EOF", {}, {"op": "op2"} ] }
Empty errors are removed from the returned list.
func (*ErrorList) ErrorOrNil ¶
ErrorOrNil returns an error interface if this Error represents a list of errors, or returns nil if the list of errors is empty.
func (*ErrorList) MarshalJSON ¶
func (*ErrorList) UnmarshalJSON ¶
type Kind ¶
type Kind string
Kind is the Go type for error kinds. Use the pre-defined kinds in errors.K, or
func (Kind) Default ¶
func (k Kind) Default() DefaultKind
Default turns this Kind into a default value. See DefaultKind for more information.
Example ¶
e := errors.Template("validate", errors.K.Invalid.Default()) nested1 := errors.E(io.EOF) fmt.Println(e(nested1)) // kind [invalid] nested2 := errors.E(errors.K.IO, io.EOF) fmt.Println(e(nested2)) // kind [I/O error]
Output: op [validate] kind [invalid] cause: kind [unclassified error] cause [EOF] op [validate] kind [I/O error] cause: kind [I/O error] cause [EOF]
type TemplateFn ¶
type TemplateFn func(fields ...interface{}) *Error
func TNoTrace ¶
func TNoTrace(fields ...interface{}) TemplateFn
TNoTrace is an alias for TemplateNoTrace
func Template ¶
func Template(fields ...interface{}) TemplateFn
Template returns a function that creates a base error with an initial set of fields. When called, additional fields can be passed that complement the error template:
e := errors.Template("unmarshal", K.Invalid) ... if invalid { return e("reason", "invalid format") } ... if err != nil { return e(err) }
Example ¶
var err error e := errors.Template("validate", errors.K.Invalid) // add fields err = e("reason", "invalid character", "character", "$") fmt.Println(err) // override kind and set cause err = e().WithKind(errors.K.IO).WithCause(io.EOF) fmt.Println(err) // Overriding kind and setting cause also works by simply passing them as // args to the template function... err = e(errors.K.IO, io.EOF) fmt.Println(err) // IfNotNil() returns an error only if the provided cause is not nil err = e.IfNotNil(nil) fmt.Println(err) e = e.Add("sub", "validateNestedData") err = e("reason", "missing hash") fmt.Println(err)
Output: op [validate] kind [invalid] reason [invalid character] character [$] op [validate] kind [I/O error] cause [EOF] op [validate] kind [I/O error] cause [EOF] <nil> op [validate] kind [invalid] sub [validateNestedData] reason [missing hash]
func TemplateNoTrace ¶
func TemplateNoTrace(fields ...interface{}) TemplateFn
TemplateNoTrace is like Template but produces an error without stacktrace information.
func (TemplateFn) Add ¶
func (t TemplateFn) Add(fields ...interface{}) TemplateFn
Add adds additional fields to this template.
Example ¶
reset := enableStacktraces() defer reset() e := errors.Template("example", errors.K.Invalid) e = e.Add("key", "value") printError(e(io.EOF))
Output: op [example] kind [invalid] key [value] cause [EOF] github.com/eluv-io/errors-go/stack_example_test.go: ExampleTemplateFn_Add()
func (TemplateFn) Fields ¶ added in v1.0.1
func (t TemplateFn) Fields(moreFields ...interface{}) []interface{}
Fields returns the fields of the template, excluding "op", "kind" and "cause", appended with the given fields. This is useful in situations where the same information is used for logging and in errors.
func (TemplateFn) IfNotNil ¶
func (t TemplateFn) IfNotNil(err error, fields ...interface{}) error
IfNotNil returns an error based on this template iff 'err' is not nil. Otherwise returns nil.
Example ¶
reset := enableStacktraces() defer reset() e := errors.Template("example", errors.K.Invalid, "key", "value") printError(e.IfNotNil(io.EOF))
Output: op [example] kind [invalid] key [value] cause [EOF] github.com/eluv-io/errors-go/stack_example_test.go: ExampleTemplateFn_IfNotNil()
func (TemplateFn) MarshalJSON ¶ added in v1.0.1
func (t TemplateFn) MarshalJSON() ([]byte, error)
MarshalJSON marshals this template to JSON.
func (TemplateFn) String ¶ added in v1.0.1
func (t TemplateFn) String() string
String returns a string representation of this template.