157 lines
4.1 KiB
Go
157 lines
4.1 KiB
Go
|
|
package errcode
|
||
|
|
|
||
|
|
import (
|
||
|
|
"encoding/json"
|
||
|
|
"fmt"
|
||
|
|
"path"
|
||
|
|
"runtime"
|
||
|
|
)
|
||
|
|
|
||
|
|
var codes = map[int]struct{}{}
|
||
|
|
|
||
|
|
// AppError define struct of internal error
|
||
|
|
type AppError struct {
|
||
|
|
code int
|
||
|
|
msg string
|
||
|
|
cause error
|
||
|
|
occurred string // 保存由底层错误导致AppErr发生时的位置
|
||
|
|
}
|
||
|
|
|
||
|
|
func (e *AppError) Error() string {
|
||
|
|
if e == nil {
|
||
|
|
return ""
|
||
|
|
}
|
||
|
|
errBytes, err := json.Marshal(e.toStructuredError())
|
||
|
|
if err != nil {
|
||
|
|
return fmt.Sprintf("Error() is error: json marshal error: %v", err)
|
||
|
|
}
|
||
|
|
return string(errBytes)
|
||
|
|
}
|
||
|
|
|
||
|
|
func (e *AppError) String() string {
|
||
|
|
return e.Error()
|
||
|
|
}
|
||
|
|
|
||
|
|
// Code define func return error code
|
||
|
|
func (e *AppError) Code() int {
|
||
|
|
return e.code
|
||
|
|
}
|
||
|
|
|
||
|
|
// Msg define func return error msg
|
||
|
|
func (e *AppError) Msg() string {
|
||
|
|
return e.msg
|
||
|
|
}
|
||
|
|
|
||
|
|
// Cause define func return base error
|
||
|
|
func (e *AppError) Cause() error {
|
||
|
|
return e.cause
|
||
|
|
}
|
||
|
|
|
||
|
|
// WithCause define func return top level predefined errors,where the cause field contains the underlying base error
|
||
|
|
// 在逻辑执行中出现错误, 比如dao层返回的数据库查询错误
|
||
|
|
// 可以在领域层返回预定义的错误前附加上导致错误的基础错误。
|
||
|
|
// 如果业务模块预定义的错误码比较详细, 可以使用这个方法, 反之错误码定义的比较笼统建议使用Wrap方法包装底层错误生成项目自定义Error
|
||
|
|
// 并将其记录到日志后再使用预定义错误码返回接口响应
|
||
|
|
func (e *AppError) WithCause(err error) *AppError {
|
||
|
|
newErr := e.Clone()
|
||
|
|
newErr.cause = err
|
||
|
|
newErr.occurred = getAppErrOccurredInfo()
|
||
|
|
return newErr
|
||
|
|
}
|
||
|
|
|
||
|
|
// Wrap define func packaging information and errors returned by the underlying logic
|
||
|
|
// 用于逻辑中包装底层函数返回的error 和 WithCause 一样都是为了记录错误链条
|
||
|
|
// 该方法生成的error 用于日志记录, 返回响应请使用预定义好的error
|
||
|
|
func Wrap(msg string, err error) *AppError {
|
||
|
|
if err == nil {
|
||
|
|
return nil
|
||
|
|
}
|
||
|
|
appErr := &AppError{code: -1, msg: msg, cause: err}
|
||
|
|
appErr.occurred = getAppErrOccurredInfo()
|
||
|
|
return appErr
|
||
|
|
}
|
||
|
|
|
||
|
|
// UnWrap define func return the error wrapped in structure
|
||
|
|
func (e *AppError) UnWrap() error {
|
||
|
|
return e.cause
|
||
|
|
}
|
||
|
|
|
||
|
|
// Is define func return result of whether any error in err's tree matches target. implemented to support errors.Is(err, target)
|
||
|
|
func (e *AppError) Is(target error) bool {
|
||
|
|
targetErr, ok := target.(*AppError)
|
||
|
|
if !ok {
|
||
|
|
return false
|
||
|
|
}
|
||
|
|
return targetErr.Code() == e.Code()
|
||
|
|
}
|
||
|
|
|
||
|
|
// Clone define func return a new AppError with source AppError's code, msg, cause, occurred
|
||
|
|
func (e *AppError) Clone() *AppError {
|
||
|
|
return &AppError{
|
||
|
|
code: e.code,
|
||
|
|
msg: e.msg,
|
||
|
|
cause: e.cause,
|
||
|
|
occurred: e.occurred,
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
func newError(code int, msg string) *AppError {
|
||
|
|
if code > -1 {
|
||
|
|
if _, duplicated := codes[code]; duplicated {
|
||
|
|
panic(fmt.Sprintf("预定义错误码 %d 不能重复, 请检查后更换", code))
|
||
|
|
}
|
||
|
|
codes[code] = struct{}{}
|
||
|
|
}
|
||
|
|
|
||
|
|
return &AppError{code: code, msg: msg}
|
||
|
|
}
|
||
|
|
|
||
|
|
// getAppErrOccurredInfo 获取项目中调用Wrap或者WithCause方法时的程序位置, 方便排查问题
|
||
|
|
func getAppErrOccurredInfo() string {
|
||
|
|
pc, file, line, ok := runtime.Caller(2)
|
||
|
|
if !ok {
|
||
|
|
return ""
|
||
|
|
}
|
||
|
|
file = path.Base(file)
|
||
|
|
funcName := runtime.FuncForPC(pc).Name()
|
||
|
|
triggerInfo := fmt.Sprintf("func: %s, file: %s, line: %d", funcName, file, line)
|
||
|
|
return triggerInfo
|
||
|
|
}
|
||
|
|
|
||
|
|
// AppendMsg define func append a message to the existing error message
|
||
|
|
func (e *AppError) AppendMsg(msg string) *AppError {
|
||
|
|
n := e.Clone()
|
||
|
|
n.msg = fmt.Sprintf("%s, %s", e.msg, msg)
|
||
|
|
return n
|
||
|
|
}
|
||
|
|
|
||
|
|
// SetMsg define func set error message into specify field
|
||
|
|
func (e *AppError) SetMsg(msg string) *AppError {
|
||
|
|
n := e.Clone()
|
||
|
|
n.msg = msg
|
||
|
|
return n
|
||
|
|
}
|
||
|
|
|
||
|
|
type formattedErr struct {
|
||
|
|
Code int `json:"code"`
|
||
|
|
Msg string `json:"msg"`
|
||
|
|
Cause interface{} `json:"cause"`
|
||
|
|
Occurred string `json:"occurred"`
|
||
|
|
}
|
||
|
|
|
||
|
|
// toStructuredError 在JSON Encode 前把Error进行格式化
|
||
|
|
func (e *AppError) toStructuredError() *formattedErr {
|
||
|
|
fe := new(formattedErr)
|
||
|
|
fe.Code = e.Code()
|
||
|
|
fe.Msg = e.Msg()
|
||
|
|
fe.Occurred = e.occurred
|
||
|
|
if e.cause != nil {
|
||
|
|
if appErr, ok := e.cause.(*AppError); ok {
|
||
|
|
fe.Cause = appErr.toStructuredError()
|
||
|
|
} else {
|
||
|
|
fe.Cause = e.cause.Error()
|
||
|
|
}
|
||
|
|
}
|
||
|
|
return fe
|
||
|
|
}
|