Scott Baker | 1fe7273 | 2019-10-21 10:58:51 -0700 | [diff] [blame^] | 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 | } |