github.com/insionng/yougam@v0.0.0-20170714101924-2bc18d833463/libraries/juju/errors/functions.go (about) 1 // Copyright 2014 Canonical Ltd. 2 // Licensed under the LGPLv3, see LICENCE file for details. 3 4 package errors 5 6 import ( 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 // 17 func 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 // 29 func 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 // 44 func 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 // 62 func 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 // 84 func 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 // 106 func 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 // 128 func 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 // 145 func 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. 159 func 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. 172 func 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. 188 func 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 199 type causer interface { 200 Cause() error 201 } 202 203 type 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 214 type locationer interface { 215 Location() (string, int) 216 } 217 218 var ( 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. 230 func 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 // yougam/libraries/juju/errors/annotation_test.go:193: 272 // yougam/libraries/juju/errors/annotation_test.go:194: annotation 273 // yougam/libraries/juju/errors/annotation_test.go:195: 274 // yougam/libraries/juju/errors/annotation_test.go:196: more context 275 // yougam/libraries/juju/errors/annotation_test.go:197: 276 func ErrorStack(err error) string { 277 return strings.Join(errorStack(err), "\n") 278 } 279 280 func 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 }