error.go (5087B)
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 if e == nil { 95 return nil 96 } 97 98 var err error 99 100 switch e := e.(type) { 101 case *Error: 102 return e 103 case error: 104 err = e 105 default: 106 err = fmt.Errorf("%v", e) 107 } 108 109 stack := make([]uintptr, MaxStackDepth) 110 length := runtime.Callers(2+skip, stack[:]) 111 return &Error{ 112 Err: err, 113 stack: stack[:length], 114 } 115 } 116 117 // WrapPrefix makes an Error from the given value. If that value is already an 118 // error then it will be used directly, if not, it will be passed to 119 // fmt.Errorf("%v"). The prefix parameter is used to add a prefix to the 120 // error message when calling Error(). The skip parameter indicates how far 121 // up the stack to start the stacktrace. 0 is from the current call, 122 // 1 from its caller, etc. 123 func WrapPrefix(e interface{}, prefix string, skip int) *Error { 124 if e == nil { 125 return nil 126 } 127 128 err := Wrap(e, 1+skip) 129 130 if err.prefix != "" { 131 prefix = fmt.Sprintf("%s: %s", prefix, err.prefix) 132 } 133 134 return &Error{ 135 Err: err.Err, 136 stack: err.stack, 137 prefix: prefix, 138 } 139 140 } 141 142 // Errorf creates a new error with the given message. You can use it 143 // as a drop-in replacement for fmt.Errorf() to provide descriptive 144 // errors in return values. 145 func Errorf(format string, a ...interface{}) *Error { 146 return Wrap(fmt.Errorf(format, a...), 1) 147 } 148 149 // Error returns the underlying error's message. 150 func (err *Error) Error() string { 151 152 msg := err.Err.Error() 153 if err.prefix != "" { 154 msg = fmt.Sprintf("%s: %s", err.prefix, msg) 155 } 156 157 return msg 158 } 159 160 // Stack returns the callstack formatted the same way that go does 161 // in runtime/debug.Stack() 162 func (err *Error) Stack() []byte { 163 buf := bytes.Buffer{} 164 165 for _, frame := range err.StackFrames() { 166 buf.WriteString(frame.String()) 167 } 168 169 return buf.Bytes() 170 } 171 172 // Callers satisfies the bugsnag ErrorWithCallerS() interface 173 // so that the stack can be read out. 174 func (err *Error) Callers() []uintptr { 175 return err.stack 176 } 177 178 // ErrorStack returns a string that contains both the 179 // error message and the callstack. 180 func (err *Error) ErrorStack() string { 181 return err.TypeName() + " " + err.Error() + "\n" + string(err.Stack()) 182 } 183 184 // StackFrames returns an array of frames containing information about the 185 // stack. 186 func (err *Error) StackFrames() []StackFrame { 187 if err.frames == nil { 188 err.frames = make([]StackFrame, len(err.stack)) 189 190 for i, pc := range err.stack { 191 err.frames[i] = NewStackFrame(pc) 192 } 193 } 194 195 return err.frames 196 } 197 198 // TypeName returns the type this error. e.g. *errors.stringError. 199 func (err *Error) TypeName() string { 200 if _, ok := err.Err.(uncaughtPanic); ok { 201 return "panic" 202 } 203 return reflect.TypeOf(err.Err).String() 204 } 205 206 // Return the wrapped error (implements api for As function). 207 func (err *Error) Unwrap() error { 208 return err.Err 209 }