error.go

  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.
 46package errors
 47
 48import (
 49	"bytes"
 50	"fmt"
 51	"reflect"
 52	"runtime"
 53)
 54
 55// The maximum number of stackframes on any error.
 56var MaxStackDepth = 50
 57
 58// Error is an error with an attached stacktrace. It can be used
 59// wherever the builtin error interface is expected.
 60type 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.
 71func 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.
 93func 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.
119func 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.
138func 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.
158func Errorf(format string, a ...interface{}) *Error {
159	return Wrap(fmt.Errorf(format, a...), 1)
160}
161
162// Error returns the underlying error's message.
163func (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()
175func (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.
187func (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.
193func (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.
199func (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.
212func (err *Error) TypeName() string {
213	if _, ok := err.Err.(uncaughtPanic); ok {
214		return "panic"
215	}
216	return reflect.TypeOf(err.Err).String()
217}