Go package for adding extra context to errors.
This implementaton of the error
interface adds fields (i.e. map[string]any
) to a context.Context
and attaches that to the returned error. There are a few fields defined by this package but most
if err != nil {
ctx = ctxerr.SetField(ctx, "field", "value")
return ctxerr.Wrap(ctx, err, "ERROR_CODE", "message")
}
If you pass the context through your functions then fields can be added when the the data first appears and are automatically available in deeper functions.
In this example, even though params
are set in a the top function, foo
, they are available even in the err
returned by the deepest function, baz
.
func foo(req request) {
ctx := req.Context()
ctx = ctxerr.SetField(ctx, "params", req.Params)
if err := baz(ctx); err != nil {
err = ctxerr.QuickWrap(ctx, err)
ctxerr.Handle(err)
return
}
}
func bar(ctx context.Context) error {
// If err == nil; (Quick)Wrap will return nil
return ctxerr.QuickWrap(ctx, baz(ctx))
}
func baz(ctx context.Context) error {
return ctxerr.New(ctx, "NOT_IMPLEMENTED", "function not implemented")
}
There are helper functions NewHTTP
and WrapHTTP
that set fields inline for a status code and an 'action'. Actions are what this package calls external user facing messages. They are the onces that can be shown to users without revealing internal details of your application.
There is a subpackage ctxerr/http that simplifies returning error JSON to an http request.
Errors should only be handled once. Rather than calling log
at the topmost return call ctxerr.Handle(err)
so all errors can be handled in the same way. This is especially helpful in go func()
and defer func()
.
Configuration is done through hooks.
AddCreateHook
adds hooks that are run on ever New
or (Quick)Wrap
. Builtin hooks add the error code and a location of the error to the context.
AddHandleHook
adds hooks that are run on Handle
. If no hooks exist it will run a default log hook. Use this to create a hook to log consistently however you want or even create a metric on each error by code.
Common configurations might be available in the packages under ctxerrhelper. There each package has its own go.mod
file to avoid adding extra dependencies to your service.