-
Notifications
You must be signed in to change notification settings - Fork 41
Add warp and cause interface #23
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
Changes from 6 commits
2493a22
b667838
b17b426
2548b76
a28915e
2d79068
7b34aeb
57ec461
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -6,6 +6,7 @@ | |
| # Folders | ||
| _obj | ||
| _test | ||
| .idea | ||
|
|
||
| # Architecture specific extensions/prefixes | ||
| *.[568vq] | ||
|
|
||
This file was deleted.
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -19,8 +19,21 @@ import ( | |
| "runtime" | ||
| "strconv" | ||
| "strings" | ||
|
|
||
| "go.uber.org/zap" | ||
| "go.uber.org/zap/zapcore" | ||
| ) | ||
|
|
||
| // ErrCode represents a specific error type in a error class. | ||
| // Same error code can be used in different error classes. | ||
| type ErrCode int | ||
|
|
||
| // ErrCodeText is a textual error code that represents a specific error type in a error class. | ||
| type ErrCodeText string | ||
|
|
||
| type ErrorID string | ||
| type RFCErrorCode string | ||
|
|
||
| // Error is the 'prototype' of a type of errors. | ||
| // Use DefineError to make a *Error: | ||
| // var ErrUnavailable = ClassRegion.DefineError(). | ||
|
|
@@ -46,8 +59,7 @@ import ( | |
| // // handle this error. | ||
| // } | ||
| type Error struct { | ||
| class *ErrClass | ||
| code ErrCode | ||
| code ErrCode | ||
| // codeText is the textual describe of the error code | ||
| codeText ErrCodeText | ||
| // message is a template of the description of this error. | ||
|
|
@@ -61,14 +73,11 @@ type Error struct { | |
| // and the more detail this field explaining the better, | ||
| // even some guess of the cause could be included. | ||
| Description string | ||
| args []interface{} | ||
| file string | ||
| line int | ||
| } | ||
|
|
||
| // Class returns ErrClass | ||
| func (e *Error) Class() *ErrClass { | ||
| return e.class | ||
| // Cause is used to warp some third party error. | ||
| cause error | ||
| args []interface{} | ||
| file string | ||
| line int | ||
| } | ||
|
|
||
| // Code returns the numeric code of this error. | ||
|
|
@@ -84,23 +93,7 @@ func (e *Error) Code() ErrCode { | |
| // The error code is a 3-tuple of abbreviated component name, error class and error code, | ||
| // joined by a colon like {Component}:{ErrorClass}:{InnerErrorCode}. | ||
| func (e *Error) RFCCode() RFCErrorCode { | ||
| ec := e.Class() | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We should return the |
||
| if ec == nil { | ||
| return RFCErrorCode(e.ID()) | ||
| } | ||
| reg := ec.registry | ||
| // Maybe top-level errors. | ||
| if reg.Name == "" { | ||
| return RFCErrorCode(fmt.Sprintf("%s:%s", | ||
| ec.Description, | ||
| e.ID(), | ||
| )) | ||
| } | ||
| return RFCErrorCode(fmt.Sprintf("%s:%s:%s", | ||
| reg.Name, | ||
| ec.Description, | ||
| e.ID(), | ||
| )) | ||
| return RFCErrorCode(e.ID()) | ||
| } | ||
|
|
||
| // ID returns the ID of this error. | ||
|
|
@@ -122,16 +115,50 @@ func (e *Error) MessageTemplate() string { | |
| return e.message | ||
| } | ||
|
|
||
| // NewError creates a standard error object. | ||
| func NewError(code int, message string) *Error { | ||
| return &Error{ | ||
| code: ErrCode(code), | ||
| message: message, | ||
| } | ||
| } | ||
|
|
||
| // NewErrorWithText creates a standard error object using text error code. | ||
| func NewErrorWithText(code string, message string) *Error { | ||
| return &Error{ | ||
| codeText: ErrCodeText(code), | ||
| message: message, | ||
| } | ||
| } | ||
|
|
||
| // SetWorkaround sets the workaround for standard error. | ||
| func (e *Error) SetWorkaround(workaround string) *Error { | ||
| e.Workaround = workaround | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Can we allow users to dynamically change the fields?
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This is a decorator function. Since old error in TiDB do not have workaround field. Using this method can add workaround with miniumn change. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Reference to the demo https://play.golang.org/p/rJBVbbkbXLP, we can minimize the change and make the error immutable. |
||
| return e | ||
| } | ||
|
|
||
| // SetWorkaround sets the description for standard error. | ||
| func (e *Error) SetDescription(description string) *Error { | ||
| e.Description = description | ||
| return e | ||
| } | ||
|
|
||
| // SetErrCodeText sets the text error code for standard error. | ||
| func (e *Error) SetErrCodeText(codeText string) *Error { | ||
| e.codeText = ErrCodeText(codeText) | ||
| return e | ||
| } | ||
|
|
||
| // Error implements error interface. | ||
| func (e *Error) Error() string { | ||
| describe := e.codeText | ||
| if len(describe) == 0 { | ||
| describe = ErrCodeText(strconv.Itoa(int(e.code))) | ||
| } | ||
| return fmt.Sprintf("[%s] %s", e.RFCCode(), e.getMsg()) | ||
| return fmt.Sprintf("[%s] %s", e.RFCCode(), e.GetMsg()) | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think we should use the local variable |
||
| } | ||
|
|
||
| func (e *Error) getMsg() string { | ||
| func (e *Error) GetMsg() string { | ||
| if len(e.args) > 0 { | ||
| return fmt.Sprintf(e.message, e.args...) | ||
| } | ||
|
|
@@ -197,9 +224,8 @@ func (e *Error) Equal(err error) bool { | |
| if !ok { | ||
| return false | ||
| } | ||
| classEquals := e.class.Equal(inErr.class) | ||
| idEquals := e.ID() == inErr.ID() | ||
| return classEquals && idEquals | ||
| return idEquals | ||
| } | ||
|
|
||
| // NotEqual checks if err is not equal to e. | ||
|
|
@@ -239,7 +265,6 @@ type jsonError struct { | |
| Error string `json:"message"` | ||
| Description string `json:"description,omitempty"` | ||
| Workaround string `json:"workaround,omitempty"` | ||
| Class ErrClassID `json:"classID"` | ||
| File string `json:"file"` | ||
| Line int `json:"line"` | ||
| } | ||
|
|
@@ -251,11 +276,10 @@ type jsonError struct { | |
| // This function is reserved for compatibility. | ||
| func (e *Error) MarshalJSON() ([]byte, error) { | ||
| return json.Marshal(&jsonError{ | ||
| Error: e.getMsg(), | ||
| Error: e.GetMsg(), | ||
| Description: e.Description, | ||
| Workaround: e.Workaround, | ||
| RFCCode: e.RFCCode(), | ||
| Class: e.class.ID, | ||
| Line: e.line, | ||
| File: e.file, | ||
| }) | ||
|
|
@@ -273,9 +297,7 @@ func (e *Error) UnmarshalJSON(data []byte) error { | |
| return Trace(err) | ||
| } | ||
| codes := strings.Split(string(err.RFCCode), ":") | ||
| regName := codes[0] | ||
| className := codes[1] | ||
| innerCode := codes[2] | ||
| innerCode := codes[0] | ||
| if i, errAtoi := strconv.Atoi(innerCode); errAtoi == nil { | ||
| e.code = ErrCode(i) | ||
| } else { | ||
|
|
@@ -284,10 +306,41 @@ func (e *Error) UnmarshalJSON(data []byte) error { | |
| e.line = err.Line | ||
| e.file = err.File | ||
| e.message = err.Error | ||
| e.class = &ErrClass{ | ||
| Description: className, | ||
| ID: err.Class, | ||
| registry: &Registry{Name: regName}, | ||
| } | ||
| return nil | ||
| } | ||
|
|
||
| func (e *Error) Wrap(err error) *Error { | ||
| e.cause = err | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think we should return a new instance of when |
||
| return e | ||
| } | ||
|
|
||
| func (e *Error) Cause() error { | ||
| root := Unwrap(e.cause) | ||
| if root == nil { | ||
| return e.cause | ||
| } | ||
| return root | ||
| } | ||
|
|
||
| func (e *Error) FastGenWithCause(args ...interface{}) error { | ||
| err := *e | ||
| err.message = e.cause.Error() | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. will panic here is |
||
| err.args = args | ||
| return SuspendStack(&err) | ||
| } | ||
|
|
||
| func (e *Error) GenWithStackByCause(args ...interface{}) error { | ||
| err := *e | ||
| err.message = e.cause.Error() | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. ditto |
||
| err.args = args | ||
| err.fillLineAndFile(1) | ||
| return AddStack(&err) | ||
| } | ||
|
|
||
| func CauseError(err *Error) zap.Field { | ||
| return zap.Field{Key: "error", Type: zapcore.ErrorType, Interface: err.FastGenWithCause()} | ||
| } | ||
|
|
||
| func DetailError(err *Error) zap.Field { | ||
| return zap.Field{Key: "error", Type: zapcore.ErrorType, Interface: err.FastGenByArgs()} | ||
| } | ||
|
Comment on lines
+354
to
+361
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. According to discussion with @rleungx, we can remove this two functions and use |
||
This file was deleted.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
typo