github.com/pingcap/tiflow@v0.0.0-20240520035814-5bf52d54e205/dm/pkg/terror/terror.go (about) 1 // Copyright 2019 PingCAP, Inc. 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // See the License for the specific language governing permissions and 12 // limitations under the License. 13 14 package terror 15 16 import ( 17 "fmt" 18 "io" 19 "sync" 20 21 "github.com/pingcap/errors" 22 ) 23 24 const ( 25 errBaseFormat = "[code=%d:class=%s:scope=%s:level=%s]" 26 ) 27 28 // codeToErrorMap maps from error code to base error, it is used for error 29 // retrieval by error code. 30 var codeToErrorMap = new(sync.Map) 31 32 // ErrCode is used as the unique identifier of a specific error type. 33 // 34 //go:generate stringer -type=ErrCode -trimprefix=code 35 type ErrCode int 36 37 // ErrClass represents a class of errors. 38 type ErrClass int 39 40 // Error classes. 41 const ( 42 ClassDatabase ErrClass = iota + 1 43 ClassFunctional 44 ClassConfig 45 ClassBinlogOp 46 ClassCheckpoint 47 ClassTaskCheck 48 ClassSourceCheck 49 ClassRelayEventLib 50 ClassRelayUnit 51 ClassDumpUnit 52 ClassLoadUnit 53 ClassSyncUnit 54 ClassDMMaster 55 ClassDMWorker 56 ClassDMTracer 57 ClassSchemaTracker 58 ClassScheduler 59 ClassDMCtl 60 ClassNotSet 61 ClassOpenAPI 62 ClassValidator 63 ClassHA 64 ) 65 66 var errClass2Str = map[ErrClass]string{ 67 ClassDatabase: "database", 68 ClassFunctional: "functional", 69 ClassConfig: "config", 70 ClassBinlogOp: "binlog-op", 71 ClassCheckpoint: "checkpoint", 72 ClassTaskCheck: "task-check", 73 ClassSourceCheck: "source-check", 74 ClassRelayEventLib: "relay-event-lib", 75 ClassRelayUnit: "relay-unit", 76 ClassDumpUnit: "dump-unit", 77 ClassLoadUnit: "load-unit", 78 ClassSyncUnit: "sync-unit", 79 ClassDMMaster: "dm-master", 80 ClassDMWorker: "dm-worker", 81 ClassDMTracer: "dm-tracer", 82 ClassValidator: "validator", 83 ClassHA: "ha", 84 ClassSchemaTracker: "schema-tracker", 85 ClassScheduler: "scheduler", 86 ClassDMCtl: "dmctl", 87 ClassNotSet: "not-set", 88 ClassOpenAPI: "openapi", 89 } 90 91 // String implements fmt.Stringer interface. 92 func (ec ErrClass) String() string { 93 if s, ok := errClass2Str[ec]; ok { 94 return s 95 } 96 return fmt.Sprintf("unknown error class: %d", ec) 97 } 98 99 // ErrScope represents the error occurs environment, such as upstream DB error, 100 // downstream DB error, DM internal error etc. 101 type ErrScope int 102 103 // Error scopes. 104 const ( 105 ScopeNotSet ErrScope = iota 106 ScopeUpstream 107 ScopeDownstream 108 ScopeInternal 109 ) 110 111 var errScope2Str = map[ErrScope]string{ 112 ScopeNotSet: "not-set", 113 ScopeUpstream: "upstream", 114 ScopeDownstream: "downstream", 115 ScopeInternal: "internal", 116 } 117 118 // String implements fmt.Stringer interface. 119 func (es ErrScope) String() string { 120 if s, ok := errScope2Str[es]; ok { 121 return s 122 } 123 return fmt.Sprintf("unknown error scope: %d", es) 124 } 125 126 // ErrLevel represents the emergency level of a specific error type. 127 type ErrLevel int 128 129 // Error levels. 130 const ( 131 LevelLow ErrLevel = iota + 1 132 LevelMedium 133 LevelHigh 134 ) 135 136 var errLevel2Str = map[ErrLevel]string{ 137 LevelLow: "low", 138 LevelMedium: "medium", 139 LevelHigh: "high", 140 } 141 142 // String implements fmt.Stringer interface. 143 func (el ErrLevel) String() string { 144 if s, ok := errLevel2Str[el]; ok { 145 return s 146 } 147 return fmt.Sprintf("unknown error level: %d", el) 148 } 149 150 // Error implements error interface and add more useful fields. 151 type Error struct { 152 code ErrCode 153 class ErrClass 154 scope ErrScope 155 level ErrLevel 156 message string 157 workaround string 158 args []interface{} 159 rawCause error 160 stack errors.StackTracer 161 } 162 163 // New creates a new *Error instance. 164 func New(code ErrCode, class ErrClass, scope ErrScope, level ErrLevel, message string, workaround string) *Error { 165 err := &Error{ 166 code: code, 167 class: class, 168 scope: scope, 169 level: level, 170 message: message, 171 workaround: workaround, 172 } 173 codeToErrorMap.Store(code, err) 174 return err 175 } 176 177 // Code returns ErrCode. 178 func (e *Error) Code() ErrCode { 179 return e.code 180 } 181 182 // Class returns ErrClass. 183 func (e *Error) Class() ErrClass { 184 return e.class 185 } 186 187 // Scope returns ErrScope. 188 func (e *Error) Scope() ErrScope { 189 return e.scope 190 } 191 192 // Level returns ErrLevel. 193 func (e *Error) Level() ErrLevel { 194 return e.level 195 } 196 197 // Message returns the formatted error message. 198 func (e *Error) Message() string { 199 return e.getMsg() 200 } 201 202 // Workaround returns ErrWorkaround. 203 func (e *Error) Workaround() string { 204 return e.workaround 205 } 206 207 // ErrorWithoutWorkaround returns err msg without workaround, in some place like cloud we want display it separately. 208 func (e *Error) ErrorWithoutWorkaround() string { 209 var str string 210 if e.getMsg() != "" { 211 str += fmt.Sprintf("Message: %s", e.getMsg()) 212 } 213 if e.rawCause != nil { 214 if len(str) > 0 { 215 str += ", " 216 } 217 str += fmt.Sprintf("RawCause: %s", Message(e.rawCause)) 218 } 219 return str 220 } 221 222 // Error implements error interface. 223 func (e *Error) Error() string { 224 str := fmt.Sprintf(errBaseFormat, e.code, e.class, e.scope, e.level) 225 tmp := e.ErrorWithoutWorkaround() 226 if tmp != "" { 227 str += ", " + tmp 228 } 229 if e.workaround != "" { 230 str += fmt.Sprintf(", Workaround: %s", e.workaround) 231 } 232 return str 233 } 234 235 // Format accepts flags that alter the printing of some verbs. 236 func (e *Error) Format(s fmt.State, verb rune) { 237 switch verb { 238 case 'v': 239 if s.Flag('+') { 240 //nolint:errcheck 241 io.WriteString(s, e.Error()) 242 if e.stack != nil { 243 e.stack.StackTrace().Format(s, verb) 244 } 245 return 246 } 247 fallthrough 248 case 's': 249 //nolint:errcheck 250 io.WriteString(s, e.Error()) 251 case 'q': 252 fmt.Fprintf(s, "%q", e.Error()) 253 } 254 } 255 256 // Cause implements causer.Cause defined in pingcap/errors 257 // and returns the raw cause of an *Error. 258 func (e *Error) Cause() error { 259 return e.rawCause 260 } 261 262 func (e *Error) getMsg() string { 263 if len(e.args) > 0 { 264 return fmt.Sprintf(e.message, e.args...) 265 } 266 return e.message 267 } 268 269 // Equal checks if err equals to e. 270 func (e *Error) Equal(err error) bool { 271 if error(e) == err { 272 return true 273 } 274 inErr, ok := err.(*Error) 275 return ok && e.code == inErr.code 276 } 277 278 // SetMessage clones an Error and resets its message. 279 func (e *Error) SetMessage(message string) *Error { 280 err := *e 281 err.message = message 282 err.args = append([]interface{}{}, e.args...) 283 return &err 284 } 285 286 // New generates a new *Error with the same class and code, and replace message with new message. 287 func (e *Error) New(message string) error { 288 return e.stackLevelGeneratef(1, message) 289 } 290 291 // Generate generates a new *Error with the same class and code, and new arguments. 292 func (e *Error) Generate(args ...interface{}) error { 293 return e.stackLevelGeneratef(1, e.message, args...) 294 } 295 296 // Generatef generates a new *Error with the same class and code, and a new formatted message. 297 func (e *Error) Generatef(format string, args ...interface{}) error { 298 return e.stackLevelGeneratef(1, format, args...) 299 } 300 301 // stackLevelGeneratef is an inner interface to generate new *Error. 302 func (e *Error) stackLevelGeneratef(stackSkipLevel int, format string, args ...interface{}) error { 303 return &Error{ 304 code: e.code, 305 class: e.class, 306 scope: e.scope, 307 level: e.level, 308 message: format, 309 workaround: e.workaround, 310 args: args, 311 stack: errors.NewStack(stackSkipLevel), 312 } 313 } 314 315 // Delegate creates a new *Error with the same fields of the give *Error, 316 // except for new arguments, it also sets the err as raw cause of *Error. 317 func (e *Error) Delegate(err error, args ...interface{}) error { 318 if err == nil { 319 return nil 320 } 321 322 rawCause := err 323 // we only get the root rawCause 324 if tErr, ok := err.(*Error); ok && tErr.rawCause != nil { 325 rawCause = tErr.rawCause 326 } 327 328 return &Error{ 329 code: e.code, 330 class: e.class, 331 scope: e.scope, 332 level: e.level, 333 message: e.message, 334 workaround: e.workaround, 335 args: args, 336 rawCause: rawCause, 337 stack: errors.NewStack(0), 338 } 339 } 340 341 // AnnotateDelegate resets the message of *Error and Delegate with error and new args. 342 func (e *Error) AnnotateDelegate(err error, message string, args ...interface{}) error { 343 if err == nil { 344 return nil 345 } 346 return e.SetMessage(message).Delegate(err, args...) 347 } 348 349 // Annotate tries to convert err to *Error and adds a message to it. 350 // This API is designed to reset Error message but keeps its original trace stack. 351 func Annotate(err error, message string) error { 352 if err == nil { 353 return nil 354 } 355 e, ok := err.(*Error) 356 if !ok { 357 return errors.Annotate(err, message) 358 } 359 e.message = fmt.Sprintf("%s: %s", message, e.getMsg()) 360 e.args = nil 361 return e 362 } 363 364 // Annotatef tries to convert err to *Error and adds a message to it. 365 func Annotatef(err error, format string, args ...interface{}) error { 366 if err == nil { 367 return nil 368 } 369 e, ok := err.(*Error) 370 if !ok { 371 return errors.Annotatef(err, format, args...) 372 } 373 e.message = fmt.Sprintf("%s: %s", format, e.getMsg()) 374 e.args = args 375 return e 376 } 377 378 // Message returns `getMsg()` value if err is an *Error instance, else returns `Error()` value. 379 func Message(err error) string { 380 if err == nil { 381 return "" 382 } 383 e, ok := err.(*Error) 384 if !ok { 385 return err.Error() 386 } 387 return e.getMsg() 388 } 389 390 // WithScope tries to set given scope to *Error, if err is not an *Error instance, 391 // wrap it with error scope instead. 392 func WithScope(err error, scope ErrScope) error { 393 if err == nil { 394 return nil 395 } 396 e, ok := err.(*Error) 397 if !ok { 398 return errors.Annotatef(err, "error scope: %s", scope) 399 } 400 e.scope = scope 401 return e 402 } 403 404 // WithClass tries to set given class to *Error, if err is not an *Error instance, 405 // wrap it with error class instead. 406 func WithClass(err error, class ErrClass) error { 407 if err == nil { 408 return nil 409 } 410 e, ok := err.(*Error) 411 if !ok { 412 return errors.Annotatef(err, "error class: %s", class) 413 } 414 e.class = class 415 return e 416 } 417 418 // ErrorFromCode queries registered error from error code. 419 func ErrorFromCode(code ErrCode) (*Error, bool) { 420 value, ok := codeToErrorMap.Load(code) 421 if !ok { 422 return nil, false 423 } 424 return value.(*Error), true 425 }