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