github.com/insionng/yougam@v0.0.0-20170714101924-2bc18d833463/libraries/juju/errors/error.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 "reflect" 9 "runtime" 10 ) 11 12 // Err holds a description of an error along with information about 13 // where the error was created. 14 // 15 // It may be embedded in custom error types to add extra information that 16 // this errors package can understand. 17 type Err struct { 18 // message holds an annotation of the error. 19 message string 20 21 // cause holds the cause of the error as returned 22 // by the Cause method. 23 cause error 24 25 // previous holds the previous error in the error stack, if any. 26 previous error 27 28 // file and line hold the source code location where the error was 29 // created. 30 file string 31 line int 32 } 33 34 // NewErr is used to return an Err for the purpose of embedding in other 35 // structures. The location is not specified, and needs to be set with a call 36 // to SetLocation. 37 // 38 // For example: 39 // type FooError struct { 40 // errors.Err 41 // code int 42 // } 43 // 44 // func NewFooError(code int) error { 45 // err := &FooError{errors.NewErr("foo"), code} 46 // err.SetLocation(1) 47 // return err 48 // } 49 func NewErr(format string, args ...interface{}) Err { 50 return Err{ 51 message: fmt.Sprintf(format, args...), 52 } 53 } 54 55 // NewErrWithCause is used to return an Err with case by other error for the purpose of embedding in other 56 // structures. The location is not specified, and needs to be set with a call 57 // to SetLocation. 58 // 59 // For example: 60 // type FooError struct { 61 // errors.Err 62 // code int 63 // } 64 // 65 // func (e *FooError) Annotate(format string, args ...interface{}) error { 66 // err := &FooError{errors.NewErrWithCause(e.Err, format, args...), e.code} 67 // err.SetLocation(1) 68 // return err 69 // }) 70 func NewErrWithCause(other error, format string, args ...interface{}) Err { 71 return Err{ 72 message: fmt.Sprintf(format, args...), 73 cause: Cause(other), 74 previous: other, 75 } 76 } 77 78 // Location is the file and line of where the error was most recently 79 // created or annotated. 80 func (e *Err) Location() (filename string, line int) { 81 return e.file, e.line 82 } 83 84 // Underlying returns the previous error in the error stack, if any. A client 85 // should not ever really call this method. It is used to build the error 86 // stack and should not be introspected by client calls. Or more 87 // specifically, clients should not depend on anything but the `Cause` of an 88 // error. 89 func (e *Err) Underlying() error { 90 return e.previous 91 } 92 93 // The Cause of an error is the most recent error in the error stack that 94 // meets one of these criteria: the original error that was raised; the new 95 // error that was passed into the Wrap function; the most recently masked 96 // error; or nil if the error itself is considered the Cause. Normally this 97 // method is not invoked directly, but instead through the Cause stand alone 98 // function. 99 func (e *Err) Cause() error { 100 return e.cause 101 } 102 103 // Message returns the message stored with the most recent location. This is 104 // the empty string if the most recent call was Trace, or the message stored 105 // with Annotate or Mask. 106 func (e *Err) Message() string { 107 return e.message 108 } 109 110 // Error implements error.Error. 111 func (e *Err) Error() string { 112 // We want to walk up the stack of errors showing the annotations 113 // as long as the cause is the same. 114 err := e.previous 115 if !sameError(Cause(err), e.cause) && e.cause != nil { 116 err = e.cause 117 } 118 switch { 119 case err == nil: 120 return e.message 121 case e.message == "": 122 return err.Error() 123 } 124 return fmt.Sprintf("%s: %v", e.message, err) 125 } 126 127 // SetLocation records the source location of the error at callDepth stack 128 // frames above the call. 129 func (e *Err) SetLocation(callDepth int) { 130 _, file, line, _ := runtime.Caller(callDepth + 1) 131 e.file = trimGoPath(file) 132 e.line = line 133 } 134 135 // StackTrace returns one string for each location recorded in the stack of 136 // errors. The first value is the originating error, with a line for each 137 // other annotation or tracing of the error. 138 func (e *Err) StackTrace() []string { 139 return errorStack(e) 140 } 141 142 // Ideally we'd have a way to check identity, but deep equals will do. 143 func sameError(e1, e2 error) bool { 144 return reflect.DeepEqual(e1, e2) 145 }