64
64
//
65
65
// Retrieving the stack trace of an error or wrapper
66
66
//
67
- // New, Errorf, Wrap, and Wrapf record a stack trace at the point they are
68
- // invoked. This information can be retrieved with the following interface.
69
- //
70
- // type stackTracer interface {
71
- // StackTrace() errors.StackTrace
72
- // }
73
- //
74
- // Where errors.StackTrace is defined as
67
+ // New, Errorf, Wrap, and Wrapf record a stack trace at the point they are invoked.
68
+ // This information can be retrieved with the StackTracer interface that returns
69
+ // a StackTrace. Where errors.StackTrace is defined as
75
70
//
76
71
// type StackTrace []Frame
77
72
//
78
73
// The Frame type represents a call site in the stack trace. Frame supports
79
74
// the fmt.Formatter interface that can be used for printing information about
80
75
// the stack trace of this error. For example:
81
76
//
82
- // if err, ok := err.(stackTracer ); ok {
83
- // for _, f := range err .StackTrace() {
77
+ // if stacked := errors.GetStackTracer(err ); stacked != nil {
78
+ // for _, f := range stacked .StackTrace() {
84
79
// fmt.Printf("%+s:%d", f)
85
80
// }
86
81
// }
87
82
//
88
- // stackTracer interface is not exported by this package, but is considered a part
89
- // of stable public API.
90
- //
91
83
// See the documentation for Frame.Format for more details.
92
84
package errors
93
85
@@ -115,6 +107,21 @@ func Errorf(format string, args ...interface{}) error {
115
107
}
116
108
}
117
109
110
+ // StackTraceAware is an optimization to avoid repetitive traversals of an error chain.
111
+ // HasStack checks for this marker first.
112
+ // Annotate/Wrap and Annotatef/Wrapf will produce this marker.
113
+ type StackTraceAware interface {
114
+ HasStack () bool
115
+ }
116
+
117
+ // HasStack tells whether a StackTracer exists in the error chain
118
+ func HasStack (err error ) bool {
119
+ if errWithStack , ok := err .(StackTraceAware ); ok {
120
+ return errWithStack .HasStack ()
121
+ }
122
+ return GetStackTracer (err ) != nil
123
+ }
124
+
118
125
// fundamental is an error that has a message and a stack, but no caller.
119
126
type fundamental struct {
120
127
msg string
@@ -145,12 +152,38 @@ func WithStack(err error) error {
145
152
if err == nil {
146
153
return nil
147
154
}
155
+
148
156
return & withStack {
149
157
err ,
150
158
callers (),
151
159
}
152
160
}
153
161
162
+ // AddStack is similar to WithStack.
163
+ // However, it will first check with HasStack to see if a stack trace already exists in the causer chain before creating another one.
164
+ func AddStack (err error ) error {
165
+ if HasStack (err ) {
166
+ return err
167
+ }
168
+ return WithStack (err )
169
+ }
170
+
171
+ // GetStackTracer will return the first StackTracer in the causer chain.
172
+ // This function is used by AddStack to avoid creating redundant stack traces.
173
+ //
174
+ // You can also use the StackTracer interface on the returned error to get the stack trace.
175
+ func GetStackTracer (origErr error ) StackTracer {
176
+ var stacked StackTracer
177
+ WalkDeep (origErr , func (err error ) bool {
178
+ if stackTracer , ok := err .(StackTracer ); ok {
179
+ stacked = stackTracer
180
+ return true
181
+ }
182
+ return false
183
+ })
184
+ return stacked
185
+ }
186
+
154
187
type withStack struct {
155
188
error
156
189
* stack
@@ -175,15 +208,19 @@ func (w *withStack) Format(s fmt.State, verb rune) {
175
208
}
176
209
177
210
// Wrap returns an error annotating err with a stack trace
178
- // at the point Wrap is called, and the supplied message.
179
- // If err is nil, Wrap returns nil.
211
+ // at the point Annotate is called, and the supplied message.
212
+ // If err is nil, Annotate returns nil.
213
+ //
214
+ // Deprecated: use Annotate instead
180
215
func Wrap (err error , message string ) error {
181
216
if err == nil {
182
217
return nil
183
218
}
219
+ hasStack := HasStack (err )
184
220
err = & withMessage {
185
- cause : err ,
186
- msg : message ,
221
+ cause : err ,
222
+ msg : message ,
223
+ causeHasStack : hasStack ,
187
224
}
188
225
return & withStack {
189
226
err ,
@@ -192,15 +229,19 @@ func Wrap(err error, message string) error {
192
229
}
193
230
194
231
// Wrapf returns an error annotating err with a stack trace
195
- // at the point Wrapf is call, and the format specifier.
196
- // If err is nil, Wrapf returns nil.
232
+ // at the point Annotatef is call, and the format specifier.
233
+ // If err is nil, Annotatef returns nil.
234
+ //
235
+ // Deprecated: use Annotatef instead
197
236
func Wrapf (err error , format string , args ... interface {}) error {
198
237
if err == nil {
199
238
return nil
200
239
}
240
+ hasStack := HasStack (err )
201
241
err = & withMessage {
202
- cause : err ,
203
- msg : fmt .Sprintf (format , args ... ),
242
+ cause : err ,
243
+ msg : fmt .Sprintf (format , args ... ),
244
+ causeHasStack : hasStack ,
204
245
}
205
246
return & withStack {
206
247
err ,
@@ -215,18 +256,21 @@ func WithMessage(err error, message string) error {
215
256
return nil
216
257
}
217
258
return & withMessage {
218
- cause : err ,
219
- msg : message ,
259
+ cause : err ,
260
+ msg : message ,
261
+ causeHasStack : HasStack (err ),
220
262
}
221
263
}
222
264
223
265
type withMessage struct {
224
- cause error
225
- msg string
266
+ cause error
267
+ msg string
268
+ causeHasStack bool
226
269
}
227
270
228
- func (w * withMessage ) Error () string { return w .msg + ": " + w .cause .Error () }
229
- func (w * withMessage ) Cause () error { return w .cause }
271
+ func (w * withMessage ) Error () string { return w .msg + ": " + w .cause .Error () }
272
+ func (w * withMessage ) Cause () error { return w .cause }
273
+ func (w * withMessage ) HasStack () bool { return w .causeHasStack }
230
274
231
275
func (w * withMessage ) Format (s fmt.State , verb rune ) {
232
276
switch verb {
@@ -254,16 +298,34 @@ func (w *withMessage) Format(s fmt.State, verb rune) {
254
298
// be returned. If the error is nil, nil will be returned without further
255
299
// investigation.
256
300
func Cause (err error ) error {
301
+ cause := Unwrap (err )
302
+ if cause == nil {
303
+ return err
304
+ }
305
+ return Cause (cause )
306
+ }
307
+
308
+ // Unwrap uses causer to return the next error in the chain or nil.
309
+ // This goes one-level deeper, whereas Cause goes as far as possible
310
+ func Unwrap (err error ) error {
257
311
type causer interface {
258
312
Cause () error
259
313
}
314
+ if unErr , ok := err .(causer ); ok {
315
+ return unErr .Cause ()
316
+ }
317
+ return nil
318
+ }
260
319
261
- for err != nil {
262
- cause , ok := err .(causer )
263
- if ! ok {
264
- break
320
+ // Find an error in the chain that matches a test function
321
+ func Find (origErr error , test func (error ) bool ) error {
322
+ var foundErr error
323
+ WalkDeep (origErr , func (err error ) bool {
324
+ if test (err ) {
325
+ foundErr = err
326
+ return true
265
327
}
266
- err = cause . Cause ()
267
- }
268
- return err
328
+ return false
329
+ })
330
+ return foundErr
269
331
}
0 commit comments