github.com/team-ide/go-dialect@v1.9.20/vitess/vterrors/vterrors.go (about) 1 /* 2 Copyright 2019 The Vitess Authors. 3 4 Licensed under the Apache License, Version 2.0 (the "License"); 5 you may not use this file except in compliance with the License. 6 You may obtain a copy of the License at 7 8 http://www.apache.org/licenses/LICENSE-2.0 9 10 Unless required by applicable law or agreed to in writing, software 11 distributed under the License is distributed on an "AS IS" BASIS, 12 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 See the License for the specific language governing permissions and 14 limitations under the License. 15 */ 16 17 // Package vterrors provides simple error handling primitives for Vitess 18 // 19 // In all Vitess code, errors should be propagated using vterrors.Wrapf() 20 // and not fmt.Errorf(). This makes sure that stacktraces are kept and 21 // propagated correctly. 22 // 23 // New errors should be created using vterrors.New or vterrors.Errorf 24 // 25 // Vitess uses canonical error codes for error reporting. This is based 26 // on years of industry experience with error reporting. This idea is 27 // that errors should be classified into a small set of errors (10 or so) 28 // with very specific meaning. Each error has a code, and a message. When 29 // errors are passed around (even through RPCs), the code is 30 // propagated. To handle errors, only the code should be looked at (and 31 // not string-matching on the error message). 32 // 33 // Error codes are defined in /proto/vtrpc.proto. Along with an 34 // RPCError message that can be used to transmit errors through RPCs, in 35 // the message payloads. These codes match the names and numbers defined 36 // by gRPC. 37 // 38 // A standardized error implementation that allows you to build an error 39 // with an associated canonical code is also defined. 40 // While sending an error through gRPC, these codes are transmitted 41 // using gRPC's error propagation mechanism and decoded back to 42 // the original code on the other end. 43 // 44 // Retrieving the cause of an error 45 // 46 // Using vterrors.Wrap constructs a stack of errors, adding context to the 47 // preceding error, instead of simply building up a string. 48 // Depending on the nature of the error it may be necessary to reverse the 49 // operation of errors.Wrap to retrieve the original error for inspection. 50 // Any error value which implements this interface 51 // 52 // type causer interface { 53 // Cause() error 54 // } 55 // 56 // can be inspected by vterrors.Cause and vterrors.RootCause. 57 // 58 // * vterrors.Cause will find the immediate cause if one is available, or nil 59 // if the error is not a `causer` or if no cause is available. 60 // * vterrors.RootCause will recursively retrieve 61 // the topmost error which does not implement causer, which is assumed to be 62 // the original cause. For example: 63 // 64 // switch err := errors.RootCause(err).(type) { 65 // case *MyError: 66 // // handle specifically 67 // default: 68 // // unknown error 69 // } 70 // 71 // causer interface is not exported by this package, but is considered a part 72 // of stable public API. 73 // 74 // Formatted printing of errors 75 // 76 // All error values returned from this package implement fmt.Formatter and can 77 // be formatted by the fmt package. The following verbs are supported 78 // 79 // %s print the error. If the error has a Cause it will be 80 // printed recursively 81 // %v extended format. Each Frame of the error's StackTrace will 82 // be printed in detail. 83 // 84 // Most but not all of the code in this file was originally copied from 85 // https://github.com/pkg/errors/blob/v0.8.0/errors.go 86 package vterrors 87 88 import ( 89 "fmt" 90 "io" 91 92 "context" 93 94 vtrpcpb "github.com/team-ide/go-dialect/vitess/vtrpc" 95 ) 96 97 // LogErrStacks controls whether or not printing errors includes the 98 // embedded stack trace in the output. 99 var LogErrStacks bool 100 101 func init() { 102 //flag.BoolVar(&LogErrStacks, "log_err_stacks", false, "log stack traces for errors") 103 } 104 105 // New returns an error with the supplied message. 106 // New also records the stack trace at the point it was called. 107 func New(code vtrpcpb.Code, message string) error { 108 return &fundamental{ 109 msg: message, 110 code: code, 111 stack: callers(), 112 } 113 } 114 115 // Errorf formats according to a format specifier and returns the string 116 // as a value that satisfies error. 117 // Errorf also records the stack trace at the point it was called. 118 func Errorf(code vtrpcpb.Code, format string, args ...interface{}) error { 119 return &fundamental{ 120 msg: fmt.Sprintf(format, args...), 121 code: code, 122 stack: callers(), 123 } 124 } 125 126 // NewErrorf formats according to a format specifier and returns the string 127 // as a value that satisfies error. 128 // NewErrorf also records the stack trace at the point it was called. 129 func NewErrorf(code vtrpcpb.Code, state State, format string, args ...interface{}) error { 130 return &fundamental{ 131 msg: fmt.Sprintf(format, args...), 132 code: code, 133 state: state, 134 stack: callers(), 135 } 136 } 137 138 // fundamental is an error that has a message and a stack, but no caller. 139 type fundamental struct { 140 msg string 141 code vtrpcpb.Code 142 state State 143 *stack 144 } 145 146 func (f *fundamental) Error() string { return f.msg } 147 148 func (f *fundamental) Format(s fmt.State, verb rune) { 149 switch verb { 150 case 'v': 151 panicIfError(io.WriteString(s, "Code: "+f.code.String()+"\n")) 152 panicIfError(io.WriteString(s, f.msg+"\n")) 153 if LogErrStacks { 154 f.stack.Format(s, verb) 155 } 156 return 157 case 's': 158 panicIfError(io.WriteString(s, f.msg)) 159 case 'q': 160 panicIfError(fmt.Fprintf(s, "%q", f.msg)) 161 } 162 } 163 164 // Code returns the error code if it's a vtError. 165 // If err is nil, it returns ok. 166 func Code(err error) vtrpcpb.Code { 167 if err == nil { 168 return vtrpcpb.Code_OK 169 } 170 if err, ok := err.(*fundamental); ok { 171 return err.code 172 } 173 174 cause := Cause(err) 175 if cause != err && cause != nil { 176 // If we did not find an error code at the outer level, let's find the cause and check it's code 177 return Code(cause) 178 } 179 180 // Handle some special cases. 181 switch err { 182 case context.Canceled: 183 return vtrpcpb.Code_CANCELED 184 case context.DeadlineExceeded: 185 return vtrpcpb.Code_DEADLINE_EXCEEDED 186 } 187 return vtrpcpb.Code_UNKNOWN 188 } 189 190 // ErrState returns the error state if it's a vtError. 191 // If err is nil, it returns Undefined. 192 func ErrState(err error) State { 193 if err == nil { 194 return Undefined 195 } 196 if err, ok := err.(*fundamental); ok { 197 return err.state 198 } 199 200 cause := Cause(err) 201 if cause != err && cause != nil { 202 // If we did not find an error state at the outer level, let's find the cause and check it's state 203 return ErrState(cause) 204 } 205 return Undefined 206 } 207 208 // Wrap returns an error annotating err with a stack trace 209 // at the point Wrap is called, and the supplied message. 210 // If err is nil, Wrap returns nil. 211 func Wrap(err error, message string) error { 212 if err == nil { 213 return nil 214 } 215 return &wrapping{ 216 cause: err, 217 msg: message, 218 stack: callers(), 219 } 220 } 221 222 // Wrapf returns an error annotating err with a stack trace 223 // at the point Wrapf is call, and the format specifier. 224 // If err is nil, Wrapf returns nil. 225 func Wrapf(err error, format string, args ...interface{}) error { 226 if err == nil { 227 return nil 228 } 229 return &wrapping{ 230 cause: err, 231 msg: fmt.Sprintf(format, args...), 232 stack: callers(), 233 } 234 } 235 236 type wrapping struct { 237 cause error 238 msg string 239 stack *stack 240 } 241 242 func (w *wrapping) Error() string { return w.msg + ": " + w.cause.Error() } 243 func (w *wrapping) Cause() error { return w.cause } 244 245 func (w *wrapping) Format(s fmt.State, verb rune) { 246 if rune('v') == verb { 247 panicIfError(fmt.Fprintf(s, "%v\n", w.Cause())) 248 panicIfError(io.WriteString(s, w.msg)) 249 if LogErrStacks { 250 w.stack.Format(s, verb) 251 } 252 return 253 } 254 255 if rune('s') == verb || rune('q') == verb { 256 panicIfError(io.WriteString(s, w.Error())) 257 } 258 } 259 260 // since we can't return an error, let's panic if something goes wrong here 261 func panicIfError(_ int, err error) { 262 if err != nil { 263 panic(err) 264 } 265 } 266 267 // RootCause returns the underlying cause of the error, if possible. 268 // An error value has a cause if it implements the following 269 // interface: 270 // 271 // type causer interface { 272 // Cause() error 273 // } 274 // 275 // If the error does not implement Cause, the original error will 276 // be returned. If the error is nil, nil will be returned without further 277 // investigation. 278 func RootCause(err error) error { 279 for { 280 cause := Cause(err) 281 if cause == nil { 282 return err 283 } 284 err = cause 285 } 286 } 287 288 // 289 // Cause will return the immediate cause, if possible. 290 // An error value has a cause if it implements the following 291 // interface: 292 // 293 // type causer interface { 294 // Cause() error 295 // } 296 // If the error does not implement Cause, nil will be returned 297 func Cause(err error) error { 298 type causer interface { 299 Cause() error 300 } 301 302 causerObj, ok := err.(causer) 303 if !ok { 304 return nil 305 } 306 307 return causerObj.Cause() 308 } 309 310 // Equals returns true iff the error message and the code returned by Code() 311 // are equal. 312 func Equals(a, b error) bool { 313 if a == nil && b == nil { 314 // Both are nil. 315 return true 316 } 317 318 if a == nil || b == nil { 319 // One of the two is nil, since we know both are not nil. 320 return false 321 } 322 323 return a.Error() == b.Error() && Code(a) == Code(b) 324 } 325 326 // Print is meant to print the vtError object in test failures. 327 // For comparing two vterrors, use Equals() instead. 328 func Print(err error) string { 329 return fmt.Sprintf("%v: %v\n", Code(err), err.Error()) 330 }