123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330 |
- // Copyright 2014 Canonical Ltd.
- // Licensed under the LGPLv3, see LICENCE file for details.
- package errors
- import (
- "fmt"
- "strings"
- )
- // New is a drop in replacement for the standard library errors module that records
- // the location that the error is created.
- //
- // For example:
- // return errors.New("validation failed")
- //
- func New(message string) error {
- err := &Err{message: message}
- err.SetLocation(1)
- return err
- }
- // Errorf creates a new annotated error and records the location that the
- // error is created. This should be a drop in replacement for fmt.Errorf.
- //
- // For example:
- // return errors.Errorf("validation failed: %s", message)
- //
- func Errorf(format string, args ...interface{}) error {
- err := &Err{message: fmt.Sprintf(format, args...)}
- err.SetLocation(1)
- return err
- }
- // Trace adds the location of the Trace call to the stack. The Cause of the
- // resulting error is the same as the error parameter. If the other error is
- // nil, the result will be nil.
- //
- // For example:
- // if err := SomeFunc(); err != nil {
- // return errors.Trace(err)
- // }
- //
- func Trace(other error) error {
- if other == nil {
- return nil
- }
- err := &Err{previous: other, cause: Cause(other)}
- err.SetLocation(1)
- return err
- }
- // Annotate is used to add extra context to an existing error. The location of
- // the Annotate call is recorded with the annotations. The file, line and
- // function are also recorded.
- //
- // For example:
- // if err := SomeFunc(); err != nil {
- // return errors.Annotate(err, "failed to frombulate")
- // }
- //
- func Annotate(other error, message string) error {
- if other == nil {
- return nil
- }
- err := &Err{
- previous: other,
- cause: Cause(other),
- message: message,
- }
- err.SetLocation(1)
- return err
- }
- // Annotatef is used to add extra context to an existing error. The location of
- // the Annotate call is recorded with the annotations. The file, line and
- // function are also recorded.
- //
- // For example:
- // if err := SomeFunc(); err != nil {
- // return errors.Annotatef(err, "failed to frombulate the %s", arg)
- // }
- //
- func Annotatef(other error, format string, args ...interface{}) error {
- if other == nil {
- return nil
- }
- err := &Err{
- previous: other,
- cause: Cause(other),
- message: fmt.Sprintf(format, args...),
- }
- err.SetLocation(1)
- return err
- }
- // DeferredAnnotatef annotates the given error (when it is not nil) with the given
- // format string and arguments (like fmt.Sprintf). If *err is nil, DeferredAnnotatef
- // does nothing. This method is used in a defer statement in order to annotate any
- // resulting error with the same message.
- //
- // For example:
- //
- // defer DeferredAnnotatef(&err, "failed to frombulate the %s", arg)
- //
- func DeferredAnnotatef(err *error, format string, args ...interface{}) {
- if *err == nil {
- return
- }
- newErr := &Err{
- message: fmt.Sprintf(format, args...),
- cause: Cause(*err),
- previous: *err,
- }
- newErr.SetLocation(1)
- *err = newErr
- }
- // Wrap changes the Cause of the error. The location of the Wrap call is also
- // stored in the error stack.
- //
- // For example:
- // if err := SomeFunc(); err != nil {
- // newErr := &packageError{"more context", private_value}
- // return errors.Wrap(err, newErr)
- // }
- //
- func Wrap(other, newDescriptive error) error {
- err := &Err{
- previous: other,
- cause: newDescriptive,
- }
- err.SetLocation(1)
- return err
- }
- // Wrapf changes the Cause of the error, and adds an annotation. The location
- // of the Wrap call is also stored in the error stack.
- //
- // For example:
- // if err := SomeFunc(); err != nil {
- // return errors.Wrapf(err, simpleErrorType, "invalid value %q", value)
- // }
- //
- func Wrapf(other, newDescriptive error, format string, args ...interface{}) error {
- err := &Err{
- message: fmt.Sprintf(format, args...),
- previous: other,
- cause: newDescriptive,
- }
- err.SetLocation(1)
- return err
- }
- // Mask masks the given error with the given format string and arguments (like
- // fmt.Sprintf), returning a new error that maintains the error stack, but
- // hides the underlying error type. The error string still contains the full
- // annotations. If you want to hide the annotations, call Wrap.
- func Maskf(other error, format string, args ...interface{}) error {
- if other == nil {
- return nil
- }
- err := &Err{
- message: fmt.Sprintf(format, args...),
- previous: other,
- }
- err.SetLocation(1)
- return err
- }
- // Mask hides the underlying error type, and records the location of the masking.
- func Mask(other error) error {
- if other == nil {
- return nil
- }
- err := &Err{
- previous: other,
- }
- err.SetLocation(1)
- return err
- }
- // Cause returns the cause of the given error. This will be either the
- // original error, or the result of a Wrap or Mask call.
- //
- // Cause is the usual way to diagnose errors that may have been wrapped by
- // the other errors functions.
- func Cause(err error) error {
- var diag error
- if err, ok := err.(causer); ok {
- diag = err.Cause()
- }
- if diag != nil {
- return diag
- }
- return err
- }
- type causer interface {
- Cause() error
- }
- type wrapper interface {
- // Message returns the top level error message,
- // not including the message from the Previous
- // error.
- Message() string
- // Underlying returns the Previous error, or nil
- // if there is none.
- Underlying() error
- }
- type locationer interface {
- Location() (string, int)
- }
- var (
- _ wrapper = (*Err)(nil)
- _ locationer = (*Err)(nil)
- _ causer = (*Err)(nil)
- )
- // Details returns information about the stack of errors wrapped by err, in
- // the format:
- //
- // [{filename:99: error one} {otherfile:55: cause of error one}]
- //
- // This is a terse alternative to ErrorStack as it returns a single line.
- func Details(err error) string {
- if err == nil {
- return "[]"
- }
- var s []byte
- s = append(s, '[')
- for {
- s = append(s, '{')
- if err, ok := err.(locationer); ok {
- file, line := err.Location()
- if file != "" {
- s = append(s, fmt.Sprintf("%s:%d", file, line)...)
- s = append(s, ": "...)
- }
- }
- if cerr, ok := err.(wrapper); ok {
- s = append(s, cerr.Message()...)
- err = cerr.Underlying()
- } else {
- s = append(s, err.Error()...)
- err = nil
- }
- s = append(s, '}')
- if err == nil {
- break
- }
- s = append(s, ' ')
- }
- s = append(s, ']')
- return string(s)
- }
- // ErrorStack returns a string representation of the annotated error. If the
- // error passed as the parameter is not an annotated error, the result is
- // simply the result of the Error() method on that error.
- //
- // If the error is an annotated error, a multi-line string is returned where
- // each line represents one entry in the annotation stack. The full filename
- // from the call stack is used in the output.
- //
- // first error
- // github.com/juju/errors/annotation_test.go:193:
- // github.com/juju/errors/annotation_test.go:194: annotation
- // github.com/juju/errors/annotation_test.go:195:
- // github.com/juju/errors/annotation_test.go:196: more context
- // github.com/juju/errors/annotation_test.go:197:
- func ErrorStack(err error) string {
- return strings.Join(errorStack(err), "\n")
- }
- func errorStack(err error) []string {
- if err == nil {
- return nil
- }
- // We want the first error first
- var lines []string
- for {
- var buff []byte
- if err, ok := err.(locationer); ok {
- file, line := err.Location()
- // Strip off the leading GOPATH/src path elements.
- file = trimGoPath(file)
- if file != "" {
- buff = append(buff, fmt.Sprintf("%s:%d", file, line)...)
- buff = append(buff, ": "...)
- }
- }
- if cerr, ok := err.(wrapper); ok {
- message := cerr.Message()
- buff = append(buff, message...)
- // If there is a cause for this error, and it is different to the cause
- // of the underlying error, then output the error string in the stack trace.
- var cause error
- if err1, ok := err.(causer); ok {
- cause = err1.Cause()
- }
- err = cerr.Underlying()
- if cause != nil && !sameError(Cause(err), cause) {
- if message != "" {
- buff = append(buff, ": "...)
- }
- buff = append(buff, cause.Error()...)
- }
- } else {
- buff = append(buff, err.Error()...)
- err = nil
- }
- lines = append(lines, string(buff))
- if err == nil {
- break
- }
- }
- // reverse the lines to get the original error, which was at the end of
- // the list, back to the start.
- var result []string
- for i := len(lines); i > 0; i-- {
- result = append(result, lines[i-1])
- }
- return result
- }
|