| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224 |
- // Package errors provides errors that have stack-traces.
- //
- // This is particularly useful when you want to understand the
- // state of execution when an error was returned unexpectedly.
- //
- // It provides the type *Error which implements the standard
- // golang error interface, so you can use this library interchangably
- // with code that is expecting a normal error return.
- //
- // For example:
- //
- // package crashy
- //
- // import "github.com/go-errors/errors"
- //
- // var Crashed = errors.Errorf("oh dear")
- //
- // func Crash() error {
- // return errors.New(Crashed)
- // }
- //
- // This can be called as follows:
- //
- // package main
- //
- // import (
- // "crashy"
- // "fmt"
- // "github.com/go-errors/errors"
- // )
- //
- // func main() {
- // err := crashy.Crash()
- // if err != nil {
- // if errors.Is(err, crashy.Crashed) {
- // fmt.Println(err.(*errors.Error).ErrorStack())
- // } else {
- // panic(err)
- // }
- // }
- // }
- //
- // This package was original written to allow reporting to Bugsnag,
- // but after I found similar packages by Facebook and Dropbox, it
- // was moved to one canonical location so everyone can benefit.
- package errors
- import (
- "bytes"
- "fmt"
- "reflect"
- "runtime"
- )
- // The maximum number of stackframes on any error.
- var MaxStackDepth = 50
- // Error is an error with an attached stacktrace. It can be used
- // wherever the builtin error interface is expected.
- type Error struct {
- Err error
- stack []uintptr
- frames []StackFrame
- prefix string
- }
- // New makes an Error from the given value. If that value is already an
- // error then it will be used directly, if not, it will be passed to
- // fmt.Errorf("%v"). The stacktrace will point to the line of code that
- // called New.
- func New(e interface{}) *Error {
- var err error
- switch e := e.(type) {
- case error:
- err = e
- default:
- err = fmt.Errorf("%v", e)
- }
- stack := make([]uintptr, MaxStackDepth)
- length := runtime.Callers(2, stack[:])
- return &Error{
- Err: err,
- stack: stack[:length],
- }
- }
- // Wrap makes an Error from the given value. If that value is already an
- // error then it will be used directly, if not, it will be passed to
- // fmt.Errorf("%v"). The skip parameter indicates how far up the stack
- // to start the stacktrace. 0 is from the current call, 1 from its caller, etc.
- func Wrap(e interface{}, skip int) *Error {
- if e == nil {
- return nil
- }
- var err error
- switch e := e.(type) {
- case *Error:
- return e
- case error:
- err = e
- default:
- err = fmt.Errorf("%v", e)
- }
- stack := make([]uintptr, MaxStackDepth)
- length := runtime.Callers(2+skip, stack[:])
- return &Error{
- Err: err,
- stack: stack[:length],
- }
- }
- // WrapPrefix makes an Error from the given value. If that value is already an
- // error then it will be used directly, if not, it will be passed to
- // fmt.Errorf("%v"). The prefix parameter is used to add a prefix to the
- // error message when calling Error(). The skip parameter indicates how far
- // up the stack to start the stacktrace. 0 is from the current call,
- // 1 from its caller, etc.
- func WrapPrefix(e interface{}, prefix string, skip int) *Error {
- if e == nil {
- return nil
- }
- err := Wrap(e, 1+skip)
- if err.prefix != "" {
- prefix = fmt.Sprintf("%s: %s", prefix, err.prefix)
- }
- return &Error{
- Err: err.Err,
- stack: err.stack,
- prefix: prefix,
- }
- }
- // Is detects whether the error is equal to a given error. Errors
- // are considered equal by this function if they are the same object,
- // or if they both contain the same error inside an errors.Error.
- func Is(e error, original error) bool {
- if e == original {
- return true
- }
- if e, ok := e.(*Error); ok {
- return Is(e.Err, original)
- }
- if original, ok := original.(*Error); ok {
- return Is(e, original.Err)
- }
- return false
- }
- // Errorf creates a new error with the given message. You can use it
- // as a drop-in replacement for fmt.Errorf() to provide descriptive
- // errors in return values.
- func Errorf(format string, a ...interface{}) *Error {
- return Wrap(fmt.Errorf(format, a...), 1)
- }
- // Error returns the underlying error's message.
- func (err *Error) Error() string {
- msg := err.Err.Error()
- if err.prefix != "" {
- msg = fmt.Sprintf("%s: %s", err.prefix, msg)
- }
- return msg
- }
- // Stack returns the callstack formatted the same way that go does
- // in runtime/debug.Stack()
- func (err *Error) Stack() []byte {
- buf := bytes.Buffer{}
- for _, frame := range err.StackFrames() {
- buf.WriteString(frame.String())
- }
- return buf.Bytes()
- }
- // Callers satisfies the bugsnag ErrorWithCallerS() interface
- // so that the stack can be read out.
- func (err *Error) Callers() []uintptr {
- return err.stack
- }
- // ErrorStack returns a string that contains both the
- // error message and the callstack.
- func (err *Error) ErrorStack() string {
- return err.TypeName() + " " + err.Error() + "\n" + string(err.Stack())
- }
- // StackFrames returns an array of frames containing information about the
- // stack.
- func (err *Error) StackFrames() []StackFrame {
- if err.frames == nil {
- err.frames = make([]StackFrame, len(err.stack))
- for i, pc := range err.stack {
- err.frames[i] = NewStackFrame(pc)
- }
- }
- return err.frames
- }
- // TypeName returns the type this error. e.g. *errors.stringError.
- func (err *Error) TypeName() string {
- if _, ok := err.Err.(uncaughtPanic); ok {
- return "panic"
- }
- return reflect.TypeOf(err.Err).String()
- }
|