-
Notifications
You must be signed in to change notification settings - Fork 39
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
Add warp and cause interface #23
base: master
Are you sure you want to change the base?
Changes from all 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,29 +59,25 @@ 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. | ||||||
// printf-style formatting is enabled. | ||||||
message string | ||||||
// The workaround field: how to work around this error. | ||||||
// It's used to teach the users how to solve the error if occurring in the real environment. | ||||||
Workaround string | ||||||
workaround string | ||||||
// Description is the expanded detail of why this error occurred. | ||||||
// This could be written by developer at a static env, | ||||||
// 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 | ||||||
description string | ||||||
// 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,22 @@ func (e *Error) MessageTemplate() string { | |||||
return e.message | ||||||
} | ||||||
|
||||||
// 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 +196,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 +237,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 +248,10 @@ type jsonError struct { | |||||
// This function is reserved for compatibility. | ||||||
func (e *Error) MarshalJSON() ([]byte, error) { | ||||||
return json.Marshal(&jsonError{ | ||||||
Error: e.getMsg(), | ||||||
Description: e.Description, | ||||||
Workaround: e.Workaround, | ||||||
Error: e.GetMsg(), | ||||||
Description: e.description, | ||||||
Workaround: e.workaround, | ||||||
RFCCode: e.RFCCode(), | ||||||
Class: e.class.ID, | ||||||
Line: e.line, | ||||||
File: e.file, | ||||||
}) | ||||||
|
@@ -273,9 +269,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 +278,84 @@ 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) | ||||||
} | ||||||
|
||||||
type NormalizeOption func(*Error) | ||||||
|
||||||
// Description returns a NormalizeOption to set description. | ||||||
func Description(desc string) NormalizeOption { | ||||||
return func(e *Error) { | ||||||
e.description = desc | ||||||
} | ||||||
} | ||||||
|
||||||
// Workaround returns a NormalizeOption to set workaround. | ||||||
func Workaround(wr string) NormalizeOption { | ||||||
return func(e *Error) { | ||||||
e.workaround = wr | ||||||
} | ||||||
} | ||||||
|
||||||
// RFCCodeText returns a NormalizeOption to set RFC error code. | ||||||
func RFCCodeText(codeText string) NormalizeOption { | ||||||
return func(e *Error) { | ||||||
e.codeText = ErrCodeText(codeText) | ||||||
} | ||||||
} | ||||||
|
||||||
// MySQLErrorCode returns a NormalizeOption to set error code. | ||||||
func MySQLErrorCode(code int) NormalizeOption { | ||||||
return func(e *Error) { | ||||||
e.code = ErrCode(code) | ||||||
} | ||||||
} | ||||||
|
||||||
// Normalize creates a new Error object. | ||||||
func Normalize(message string, opts ...NormalizeOption) *Error { | ||||||
e := &Error{ | ||||||
message: message, | ||||||
} | ||||||
for _, opt := range opts { | ||||||
opt(e) | ||||||
} | ||||||
return e | ||||||
} | ||||||
|
||||||
// CauseError returns zap.Field contains cause error. | ||||||
func CauseError(err *Error) zap.Field { | ||||||
return zap.Field{Key: "error", Type: zapcore.ErrorType, Interface: err.FastGenWithCause()} | ||||||
} | ||||||
|
||||||
// CauseError returns zap.Field contains 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.
Suggested 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. BTW, I think we can combine these two methods? And Wrap the cause error inside of this function so that we can directly use something like |
||||||
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 |
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