blob: 994208d8d77be5ff8d1f84ddb3aa336bd8c5f3c9 [file] [log] [blame]
David K. Bainbridge528b3182017-01-23 08:51:59 -08001// Copyright 2014 Canonical Ltd.
2// Licensed under the LGPLv3, see LICENCE file for details.
3
4package errors
5
6import (
7 "fmt"
8 "strings"
9)
10
11// New is a drop in replacement for the standard libary errors module that records
12// the location that the error is created.
13//
14// For example:
15// return errors.New("validation failed")
16//
17func New(message string) error {
18 err := &Err{message: message}
19 err.SetLocation(1)
20 return err
21}
22
23// Errorf creates a new annotated error and records the location that the
24// error is created. This should be a drop in replacement for fmt.Errorf.
25//
26// For example:
27// return errors.Errorf("validation failed: %s", message)
28//
29func Errorf(format string, args ...interface{}) error {
30 err := &Err{message: fmt.Sprintf(format, args...)}
31 err.SetLocation(1)
32 return err
33}
34
35// Trace adds the location of the Trace call to the stack. The Cause of the
36// resulting error is the same as the error parameter. If the other error is
37// nil, the result will be nil.
38//
39// For example:
40// if err := SomeFunc(); err != nil {
41// return errors.Trace(err)
42// }
43//
44func Trace(other error) error {
45 if other == nil {
46 return nil
47 }
48 err := &Err{previous: other, cause: Cause(other)}
49 err.SetLocation(1)
50 return err
51}
52
53// Annotate is used to add extra context to an existing error. The location of
54// the Annotate call is recorded with the annotations. The file, line and
55// function are also recorded.
56//
57// For example:
58// if err := SomeFunc(); err != nil {
59// return errors.Annotate(err, "failed to frombulate")
60// }
61//
62func Annotate(other error, message string) error {
63 if other == nil {
64 return nil
65 }
66 err := &Err{
67 previous: other,
68 cause: Cause(other),
69 message: message,
70 }
71 err.SetLocation(1)
72 return err
73}
74
75// Annotatef is used to add extra context to an existing error. The location of
76// the Annotate call is recorded with the annotations. The file, line and
77// function are also recorded.
78//
79// For example:
80// if err := SomeFunc(); err != nil {
81// return errors.Annotatef(err, "failed to frombulate the %s", arg)
82// }
83//
84func Annotatef(other error, format string, args ...interface{}) error {
85 if other == nil {
86 return nil
87 }
88 err := &Err{
89 previous: other,
90 cause: Cause(other),
91 message: fmt.Sprintf(format, args...),
92 }
93 err.SetLocation(1)
94 return err
95}
96
97// DeferredAnnotatef annotates the given error (when it is not nil) with the given
98// format string and arguments (like fmt.Sprintf). If *err is nil, DeferredAnnotatef
99// does nothing. This method is used in a defer statement in order to annotate any
100// resulting error with the same message.
101//
102// For example:
103//
104// defer DeferredAnnotatef(&err, "failed to frombulate the %s", arg)
105//
106func DeferredAnnotatef(err *error, format string, args ...interface{}) {
107 if *err == nil {
108 return
109 }
110 newErr := &Err{
111 message: fmt.Sprintf(format, args...),
112 cause: Cause(*err),
113 previous: *err,
114 }
115 newErr.SetLocation(1)
116 *err = newErr
117}
118
119// Wrap changes the Cause of the error. The location of the Wrap call is also
120// stored in the error stack.
121//
122// For example:
123// if err := SomeFunc(); err != nil {
124// newErr := &packageError{"more context", private_value}
125// return errors.Wrap(err, newErr)
126// }
127//
128func Wrap(other, newDescriptive error) error {
129 err := &Err{
130 previous: other,
131 cause: newDescriptive,
132 }
133 err.SetLocation(1)
134 return err
135}
136
137// Wrapf changes the Cause of the error, and adds an annotation. The location
138// of the Wrap call is also stored in the error stack.
139//
140// For example:
141// if err := SomeFunc(); err != nil {
142// return errors.Wrapf(err, simpleErrorType, "invalid value %q", value)
143// }
144//
145func Wrapf(other, newDescriptive error, format string, args ...interface{}) error {
146 err := &Err{
147 message: fmt.Sprintf(format, args...),
148 previous: other,
149 cause: newDescriptive,
150 }
151 err.SetLocation(1)
152 return err
153}
154
155// Mask masks the given error with the given format string and arguments (like
156// fmt.Sprintf), returning a new error that maintains the error stack, but
157// hides the underlying error type. The error string still contains the full
158// annotations. If you want to hide the annotations, call Wrap.
159func Maskf(other error, format string, args ...interface{}) error {
160 if other == nil {
161 return nil
162 }
163 err := &Err{
164 message: fmt.Sprintf(format, args...),
165 previous: other,
166 }
167 err.SetLocation(1)
168 return err
169}
170
171// Mask hides the underlying error type, and records the location of the masking.
172func Mask(other error) error {
173 if other == nil {
174 return nil
175 }
176 err := &Err{
177 previous: other,
178 }
179 err.SetLocation(1)
180 return err
181}
182
183// Cause returns the cause of the given error. This will be either the
184// original error, or the result of a Wrap or Mask call.
185//
186// Cause is the usual way to diagnose errors that may have been wrapped by
187// the other errors functions.
188func Cause(err error) error {
189 var diag error
190 if err, ok := err.(causer); ok {
191 diag = err.Cause()
192 }
193 if diag != nil {
194 return diag
195 }
196 return err
197}
198
199type causer interface {
200 Cause() error
201}
202
203type wrapper interface {
204 // Message returns the top level error message,
205 // not including the message from the Previous
206 // error.
207 Message() string
208
209 // Underlying returns the Previous error, or nil
210 // if there is none.
211 Underlying() error
212}
213
214type locationer interface {
215 Location() (string, int)
216}
217
218var (
219 _ wrapper = (*Err)(nil)
220 _ locationer = (*Err)(nil)
221 _ causer = (*Err)(nil)
222)
223
224// Details returns information about the stack of errors wrapped by err, in
225// the format:
226//
227// [{filename:99: error one} {otherfile:55: cause of error one}]
228//
229// This is a terse alternative to ErrorStack as it returns a single line.
230func Details(err error) string {
231 if err == nil {
232 return "[]"
233 }
234 var s []byte
235 s = append(s, '[')
236 for {
237 s = append(s, '{')
238 if err, ok := err.(locationer); ok {
239 file, line := err.Location()
240 if file != "" {
241 s = append(s, fmt.Sprintf("%s:%d", file, line)...)
242 s = append(s, ": "...)
243 }
244 }
245 if cerr, ok := err.(wrapper); ok {
246 s = append(s, cerr.Message()...)
247 err = cerr.Underlying()
248 } else {
249 s = append(s, err.Error()...)
250 err = nil
251 }
252 s = append(s, '}')
253 if err == nil {
254 break
255 }
256 s = append(s, ' ')
257 }
258 s = append(s, ']')
259 return string(s)
260}
261
262// ErrorStack returns a string representation of the annotated error. If the
263// error passed as the parameter is not an annotated error, the result is
264// simply the result of the Error() method on that error.
265//
266// If the error is an annotated error, a multi-line string is returned where
267// each line represents one entry in the annotation stack. The full filename
268// from the call stack is used in the output.
269//
270// first error
271// github.com/juju/errors/annotation_test.go:193:
272// github.com/juju/errors/annotation_test.go:194: annotation
273// github.com/juju/errors/annotation_test.go:195:
274// github.com/juju/errors/annotation_test.go:196: more context
275// github.com/juju/errors/annotation_test.go:197:
276func ErrorStack(err error) string {
277 return strings.Join(errorStack(err), "\n")
278}
279
280func errorStack(err error) []string {
281 if err == nil {
282 return nil
283 }
284
285 // We want the first error first
286 var lines []string
287 for {
288 var buff []byte
289 if err, ok := err.(locationer); ok {
290 file, line := err.Location()
291 // Strip off the leading GOPATH/src path elements.
292 file = trimGoPath(file)
293 if file != "" {
294 buff = append(buff, fmt.Sprintf("%s:%d", file, line)...)
295 buff = append(buff, ": "...)
296 }
297 }
298 if cerr, ok := err.(wrapper); ok {
299 message := cerr.Message()
300 buff = append(buff, message...)
301 // If there is a cause for this error, and it is different to the cause
302 // of the underlying error, then output the error string in the stack trace.
303 var cause error
304 if err1, ok := err.(causer); ok {
305 cause = err1.Cause()
306 }
307 err = cerr.Underlying()
308 if cause != nil && !sameError(Cause(err), cause) {
309 if message != "" {
310 buff = append(buff, ": "...)
311 }
312 buff = append(buff, cause.Error()...)
313 }
314 } else {
315 buff = append(buff, err.Error()...)
316 err = nil
317 }
318 lines = append(lines, string(buff))
319 if err == nil {
320 break
321 }
322 }
323 // reverse the lines to get the original error, which was at the end of
324 // the list, back to the start.
325 var result []string
326 for i := len(lines); i > 0; i-- {
327 result = append(result, lines[i-1])
328 }
329 return result
330}