errcat

package module
v0.0.0-...-335044f Latest Latest
Warning

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

Go to latest
Published: Sep 17, 2018 License: Apache-2.0 Imports: 6 Imported by: 2

README

errcat

ERRor CATegories -- a technique (and supporting library) for error handling in Go(lang).

Documentation

Overview

errcat is a simple universal error type that helps you produce errors that are both easy to categorize and handle, and also easy to maintain the original messages of.

errcat does this by separating the two major parts of an error: the category and the message.

The category is a value which you can switch on. *It is expected that the category field may be reassigned* as the error propagates up the stack.

The message is the human-readable description of the error that occured. It *may* be further prepended with additional context info as it propagates out... or, not. The message may be redundant with the category: it is expected that the message will be printed to a user, while the category will not necessarily reach the user (it may be consumed by another layer of code, which may choose to re-categorize the error on its way up).

Additional "details" may be attached in the Error.Details field; sometimes this can be used to provide key-value pairs which are useful in logging for other remote systems which must handle errors. However, usage of this should be minimized unless good reason is known; all handling logic should branch primarily on the category field, because that's what it's there for.

errcat is specifically designed to be *serializable*, and just as importantly, *unserializable* again. This is helpful for making API-driven applications with consistent and reliably round-trip-able errors. errcat errors in json should appear as a very simple object:

{"category":"your_tag", "msg":"full text goes here"}

If details are present, they're an additional map[string]string:

{"category":"your_tag", "msg":"full text", "details":{"foo":"bar"}}

Typical usage patterns involve a const block in each package which enumerates the set of error category values that this package may return. When calling functions using the errcat convention, the callers may switch upon the returned Error's Category property:

result, err := somepkg.SomeFunc()
switch errcat.Category(err) {
case nil:
	// good!  pass!
case somepkg.ErrAlreadyDone:
	// good!  pass!
case somepkg.ErrDataCorruption:
	// ... handle ...
default:
	panic("bug: unknown error category")
}

Use the public functions of this package to create errors, and accessor functions (like 'errcat.Category' for example) to access the properties. All your code should use the stdlib `error` interface and these package functions. Using the interfaces rather than a concrete type means you (or others) can easily vendor this library even under different import paths, and all of your error types will interact correctly. Prefering the `error` type to the `errcat.Error` interface avoids common developer irritants that may otherwise arrise from type specificity when putting both types into a variable named "err"; all of the errcat package funcs both take and return `error` interfaces for this reason.

Functions internal to packages may chose to panic up their errors. It is idiomatic to recover such internal panics and return the error as normal at the top of the package even when using panics as a non-local return system internally.

Index

Constants

View Source
const ErrCategoryFilterRejection = errorCategory("errcat-category-filter-rejection")

Variables

This section is empty.

Functions

func AppendDetail

func AppendDetail(err error, key string, value string) error

Return a new error with the same category and message, and the given k-v pair of details appended.

Nil errors will be passed through. Non-errcat errors are also passed through; the details will be lost (caveat emptor; do not use this method if you haven't already normalized your errors into errcat form).

func Category

func Category(err error) interface{}

Return the value of `err.(errcat.Error).Category()` if that typecast works, or the sentinel value `errcat.unknown` if the typecast fails, or nil if the error is nil.

This is useful for switching on the category of an error, even when functions declare that they return the broader `error` interface, like so:

result, err := somepkg.SomeFunc()
switch errcat.Category(err) {
case nil:
	// good!  pass!
case somepkg.ErrAlreadyDone:
	// good!  pass!
case somepkg.ErrDataCorruption:
	// ... handle ...
default:
	panic("bug: unknown error category")
}

func Details

func Details(err error) map[string]string

Return the value of `err.(errcat.Error).Details()` if that typecast works, or nil if the typecast fails, or nil if the error is nil.

func ErrorDetailed

func ErrorDetailed(category interface{}, msg string, details map[string]string) error

Return a new error with the given category, message, and details map.

func ErrorShouldHaveCategory

func ErrorShouldHaveCategory(actual interface{}, expectedClause ...interface{}) string

ErrorShouldHaveCategory is a helper function for GoConvey-style testing: it takes an "actual" value (which should be an error), and a description of what errcat "Category" it should have, and returns either an empty string if the error matches the category, or a string describing how it's out of line if there's no match. (In other words: return of empty string means "success!", and any other string means "failure: because %s".)

(It's easy to include it in this package because the GoConvey assertion functions don't require any GoConvey *types*, so we don't have to add any dependencies in order to provide these helpers. Ignore this function if you don't use GoConvey.)

func Errorf

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

Return a new error with the given category, and a message composed of `fmt.Sprintf`'ing the remaining arguments.

func PrefixAnnotate

func PrefixAnnotate(err error, msg string, details [][2]string) error

func Recategorize

func Recategorize(category interface{}, err error) error

Return a new error with the same message and details of the given error and a category assigned to the new value.

If the given error is nil, nil will be returned.

func RequireErrorHasCategory

func RequireErrorHasCategory(e *error, category interface{})

Filters an error value, forcing it to an ErrCategoryFilterRejection error if it does not have a category of the type specified.

The typical/recommended usage for this is as a defer at the top of your function (so it's easy to see), bound to your actual return value (so it's impossible for an error to leave without hitting it):

func foobar() (err error) {
	defer errcat.RequireErrorHasCategory(&err, ErrorCategory)
}

This makes for self-documenting code, and ensures that *if* you *do* make a coding error and return an inconsistent category, it is caught immediately -- and we'll record the line number the error was returned from, so you can find and fix it quickly.

(Yes, we all wish Go had a type system strong enough to simply check this at compile time, which is normal in other languages. Alas. Nonetheless, here's our attempt to do the best we can, even if it's merely at runtime.)

This method mutates the error pointer you give it, so the error simply continues to return; it does not disrupt your control flow. You may also want to panic, though, since surely (surely; that's what you're declaring, if you use this feature) you are encountering a major bug: for this, use the `RequireErrorHasCategoryOrPanic` function.

func RequireErrorHasCategoryOrPanic

func RequireErrorHasCategoryOrPanic(e *error, category interface{})

Identical to `RequireErrorHasCategory`, but panics.

Types

type Error

type Error interface {
	Category() interface{}      // The category value.  Must be serializable as a string.  Any programatic error handling should switch on this field (exclusively!).
	Message() string            // A human-readable message to print.
	Details() map[string]string // A map of optional "details".
	error                       // Errcat error interfaces are also always stdlib errors.  The `Error() string` method typically aliases `Message() string`.
}

Jump to

Keyboard shortcuts

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