modelRT/common/errcode/error.go

163 lines
3.9 KiB
Go

// Package errcode provides internal error definition and business error definition
package errcode
import (
"encoding/json"
"fmt"
"path"
"runtime"
)
var codes = map[int]struct{}{}
// AppError define struct of internal error. occurred field records the location where the error is triggered
type AppError struct {
code int
msg string
cause error
occurred string
}
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
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
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()
}
// As define func return result of whether any error in err's tree matches target. implemented to support errors.As(err, target)
func (e *AppError) As(target any) bool {
t, ok := target.(**AppError)
if !ok {
return false
}
*t = e
return true
}
// 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 define func return the location where the error is triggered
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 define func convert AppError to structured error for better readability
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
}