error.go raw

   1  // Package errors provides errors that have stack-traces.
   2  //
   3  // This is particularly useful when you want to understand the
   4  // state of execution when an error was returned unexpectedly.
   5  //
   6  // It provides the type *Error which implements the standard
   7  // golang error interface, so you can use this library interchangably
   8  // with code that is expecting a normal error return.
   9  //
  10  // For example:
  11  //
  12  //  package crashy
  13  //
  14  //  import "github.com/go-errors/errors"
  15  //
  16  //  var Crashed = errors.Errorf("oh dear")
  17  //
  18  //  func Crash() error {
  19  //      return errors.New(Crashed)
  20  //  }
  21  //
  22  // This can be called as follows:
  23  //
  24  //  package main
  25  //
  26  //  import (
  27  //      "crashy"
  28  //      "fmt"
  29  //      "github.com/go-errors/errors"
  30  //  )
  31  //
  32  //  func main() {
  33  //      err := crashy.Crash()
  34  //      if err != nil {
  35  //          if errors.Is(err, crashy.Crashed) {
  36  //              fmt.Println(err.(*errors.Error).ErrorStack())
  37  //          } else {
  38  //              panic(err)
  39  //          }
  40  //      }
  41  //  }
  42  //
  43  // This package was original written to allow reporting to Bugsnag,
  44  // but after I found similar packages by Facebook and Dropbox, it
  45  // was moved to one canonical location so everyone can benefit.
  46  package errors
  47  
  48  import (
  49  	"bytes"
  50  	"fmt"
  51  	"reflect"
  52  	"runtime"
  53  )
  54  
  55  // The maximum number of stackframes on any error.
  56  var MaxStackDepth = 50
  57  
  58  // Error is an error with an attached stacktrace. It can be used
  59  // wherever the builtin error interface is expected.
  60  type Error struct {
  61  	Err    error
  62  	stack  []uintptr
  63  	frames []StackFrame
  64  	prefix string
  65  }
  66  
  67  // New makes an Error from the given value. If that value is already an
  68  // error then it will be used directly, if not, it will be passed to
  69  // fmt.Errorf("%v"). The stacktrace will point to the line of code that
  70  // called New.
  71  func New(e interface{}) *Error {
  72  	var err error
  73  
  74  	switch e := e.(type) {
  75  	case error:
  76  		err = e
  77  	default:
  78  		err = fmt.Errorf("%v", e)
  79  	}
  80  
  81  	stack := make([]uintptr, MaxStackDepth)
  82  	length := runtime.Callers(2, stack[:])
  83  	return &Error{
  84  		Err:   err,
  85  		stack: stack[:length],
  86  	}
  87  }
  88  
  89  // Wrap makes an Error from the given value. If that value is already an
  90  // error then it will be used directly, if not, it will be passed to
  91  // fmt.Errorf("%v"). The skip parameter indicates how far up the stack
  92  // to start the stacktrace. 0 is from the current call, 1 from its caller, etc.
  93  func Wrap(e interface{}, skip int) *Error {
  94  	var err error
  95  
  96  	switch e := e.(type) {
  97  	case *Error:
  98  		return e
  99  	case error:
 100  		err = e
 101  	default:
 102  		err = fmt.Errorf("%v", e)
 103  	}
 104  
 105  	stack := make([]uintptr, MaxStackDepth)
 106  	length := runtime.Callers(2+skip, stack[:])
 107  	return &Error{
 108  		Err:   err,
 109  		stack: stack[:length],
 110  	}
 111  }
 112  
 113  // WrapPrefix makes an Error from the given value. If that value is already an
 114  // error then it will be used directly, if not, it will be passed to
 115  // fmt.Errorf("%v"). The prefix parameter is used to add a prefix to the
 116  // error message when calling Error(). The skip parameter indicates how far
 117  // up the stack to start the stacktrace. 0 is from the current call,
 118  // 1 from its caller, etc.
 119  func WrapPrefix(e interface{}, prefix string, skip int) *Error {
 120  
 121  	err := Wrap(e, 1+skip)
 122  
 123  	if err.prefix != "" {
 124  		prefix = fmt.Sprintf("%s: %s", prefix, err.prefix)
 125  	}
 126  
 127  	return &Error{
 128  		Err:    err.Err,
 129  		stack:  err.stack,
 130  		prefix: prefix,
 131  	}
 132  
 133  }
 134  
 135  // Is detects whether the error is equal to a given error. Errors
 136  // are considered equal by this function if they are the same object,
 137  // or if they both contain the same error inside an errors.Error.
 138  func Is(e error, original error) bool {
 139  
 140  	if e == original {
 141  		return true
 142  	}
 143  
 144  	if e, ok := e.(*Error); ok {
 145  		return Is(e.Err, original)
 146  	}
 147  
 148  	if original, ok := original.(*Error); ok {
 149  		return Is(e, original.Err)
 150  	}
 151  
 152  	return false
 153  }
 154  
 155  // Errorf creates a new error with the given message. You can use it
 156  // as a drop-in replacement for fmt.Errorf() to provide descriptive
 157  // errors in return values.
 158  func Errorf(format string, a ...interface{}) *Error {
 159  	return Wrap(fmt.Errorf(format, a...), 1)
 160  }
 161  
 162  // Error returns the underlying error's message.
 163  func (err *Error) Error() string {
 164  
 165  	msg := err.Err.Error()
 166  	if err.prefix != "" {
 167  		msg = fmt.Sprintf("%s: %s", err.prefix, msg)
 168  	}
 169  
 170  	return msg
 171  }
 172  
 173  // Stack returns the callstack formatted the same way that go does
 174  // in runtime/debug.Stack()
 175  func (err *Error) Stack() []byte {
 176  	buf := bytes.Buffer{}
 177  
 178  	for _, frame := range err.StackFrames() {
 179  		buf.WriteString(frame.String())
 180  	}
 181  
 182  	return buf.Bytes()
 183  }
 184  
 185  // Callers satisfies the bugsnag ErrorWithCallerS() interface
 186  // so that the stack can be read out.
 187  func (err *Error) Callers() []uintptr {
 188  	return err.stack
 189  }
 190  
 191  // ErrorStack returns a string that contains both the
 192  // error message and the callstack.
 193  func (err *Error) ErrorStack() string {
 194  	return err.TypeName() + " " + err.Error() + "\n" + string(err.Stack())
 195  }
 196  
 197  // StackFrames returns an array of frames containing information about the
 198  // stack.
 199  func (err *Error) StackFrames() []StackFrame {
 200  	if err.frames == nil {
 201  		err.frames = make([]StackFrame, len(err.stack))
 202  
 203  		for i, pc := range err.stack {
 204  			err.frames[i] = NewStackFrame(pc)
 205  		}
 206  	}
 207  
 208  	return err.frames
 209  }
 210  
 211  // TypeName returns the type this error. e.g. *errors.stringError.
 212  func (err *Error) TypeName() string {
 213  	if _, ok := err.Err.(uncaughtPanic); ok {
 214  		return "panic"
 215  	}
 216  	return reflect.TypeOf(err.Err).String()
 217  }
 218