Documentation
¶
Overview ¶
Package errorv provides a simple error API that works well with structured logging.
Many of the ideas, much of the code, and even the text for the documentation of this package is based on the excellent github.com/pkg/errors package (https://github.com/pkg/errors).
A key difference between this package and github.com/pkg/errors is that this package has been designed to suit programs that make use of structured logging. Some of the ideas in this package were proposed for package github.com/pkg/errors, but after a reasonable amount of consideration, were ultimately not included in that package. (See https://github.com/pkg/errors/issues/34 for details).
If you are not using structured logging in your application and have no intention of doing so, use the github.com/pkg/errors package in preference to this one.
Background ¶
The traditional error handling idiom in Go is roughly akin to
if err != nil {
return err
}
which applied recursively up the call stack results in error reports without context or debugging information. The errorv package allows programmers to add context to the failure path in their code in a way that does not destroy the original value of the error.
Adding context to an error ¶
The errorv.Wrap function returns a new error that adds context to the original error. For example
name := "some-file"
number := 53
err := doSomethingWith(name, number)
if err != nil {
return errorv.Wrap(err, "cannot do something",
"name", name,
"number", number,
)
}
Retrieving the cause of an error ¶
Using errorv.Wrap constructs a stack of errors, adding context to the preceding error. Depending on the nature of the error it may be necessary to reverse the operation of errorv.Wrap to retrieve the original error for inspection. Any error value which implements this interface can be inspected by errorv.Cause.
type causer interface {
Cause() error
}
errorv.Cause will recursively retrieve the topmost error which does not implement causer, which is assumed to be the original cause. For example:
switch err := errorv.Cause(err).(type) {
case *MyError:
// handle specifically
default:
// unknown error
}
Retrieving key value pairs for structured logging ¶
Errors created by `errorv.Wrap` and `errorv.New` implement the following interface:
type keyvalser interface {
Keyvals() []interface{}
}
The Keyvals method returns an array of alternating keys and values. The first key will always be "msg" and its value will be a string containing the message associated with the wrapped error.
Example using go-kit logging (https://github.com/go-kit/kit/tree/master/log):
// logError logs details of an error to a structured error log.
func logError(logger log.Logger, err error) {
// start with timestamp and error level
keyvals := []interface{}{
"ts", time.Now().Format(time.RFC3339Nano),
"level", "error",
}
type keyvalser interface {
Keyvals() []interface{}
}
if kv, ok := err.(keyvalser); ok {
// error contains structured information, first key/value
// pair will be "msg".
keyvals = append(keyvals, kv.Keyvals()...)
} else {
// error does not contain structured information, use the
// Error() string as the message.
keyvals = append(keyvals, "msg", err.Error())
}
logger.Log(keyvals...)
}
This interface works well with the github.com/jjeffery/kv package, which provides improved type safety and clarity when working with key value pairs.
GOOD ADVICE: Do not use the `Keyvals` method on an error to retrieve the individual key/value pairs associated with an error for processing by the calling program.
Example ¶
package main
import (
"fmt"
"github.com/jjeffery/errorv"
)
func main() {
err := errorv.New("first error",
"card", "ace",
"suite", "spades")
fmt.Println(err)
err = errorv.Wrap(err, "second error",
"piece", "rook",
"color", "black",
)
fmt.Println(err)
}
Output: first error card=ace suite=spades second error piece=rook color=black: first error card=ace suite=spades
Index ¶
Examples ¶
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
func Cause ¶
Cause returns the underlying cause of the error, if possible. An error value has a cause if it implements the following interface:
type causer interface {
Cause() error
}
If the error does not implement Cause, the original error will be returned. If the error is nil, nil will be returned without further investigation.
Cause is compatible with the Cause function in package "github.com/pkg/errors". The implementation and documentation of Cause has been copied from that package.
Example ¶
package main
import (
"fmt"
"github.com/jjeffery/errorv"
)
func main() {
// tests if an error is a not found error
type notFounder interface {
NotFound() bool
}
err := getError()
if notFound, ok := errorv.Cause(err).(notFounder); ok {
fmt.Printf("Not found: %v", notFound.NotFound())
}
}
func getError() error {
return fmt.Errorf("not a not found error")
}
Output:
func New ¶
New creates a new error.
Example ¶
package main
import (
"fmt"
"github.com/jjeffery/errorv"
)
func getNameOfThing() string {
return "!not-valid"
}
func isValidName(name string) bool {
return false
}
func main() {
name := getNameOfThing()
if !isValidName(name) {
fmt.Println(errorv.New("invalid name", "name", name))
}
}
Output: invalid name name=!not-valid
Types ¶
type Context ¶
type Context interface {
New(msg string, keyvals ...interface{}) error
Wrap(err error, msg string, keyvals ...interface{}) error
NewContext(keyvals ...interface{}) Context
}
A Context can be useful for specifying common key/value pairs that will be attached to all error messages created from that context.
Example ¶
package main
import (
"fmt"
"github.com/jjeffery/errorv"
)
var userID = "u1"
var documentID = "d1"
func main() {
// ... if a function has been called with userID and DocumentID ...
errorv := errorv.NewContext("userID", userID, "documentID", documentID)
n, err := doOneThing()
if err != nil {
// will include key value pairs for userID and document ID
fmt.Println(errorv.Wrap(err, "cannot do one thing"))
}
if err := doAnotherThing(n); err != nil {
// will include key value pairs for userID, document ID and n
fmt.Println(errorv.Wrap(err, "cannot do another thing", "n", n))
}
}
func doOneThing() (int, error) {
return 0, fmt.Errorf("doOneThing: unable to finish")
}
func doAnotherThing(n int) error {
return fmt.Errorf("doAnotherThing: not working properly")
}
Output: cannot do one thing userID=u1 documentID=d1: doOneThing: unable to finish cannot do another thing userID=u1 documentID=d1 n=0: doAnotherThing: not working properly
func NewContext ¶
func NewContext(keyvals ...interface{}) Context
NewContext creates a new error context with information that will be associated with any errors created from that context.