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  }