iot_server/internal/pkg/errort/error.go

196 lines
5.5 KiB
Go
Raw Permalink Normal View History

2023-08-28 06:49:44 +00:00
package errort
import (
"fmt"
"runtime"
"strconv"
"github.com/pkg/errors"
)
type EdgeX interface {
// Error obtains the error message associated with the error.
Error() string
// DebugMessages returns a detailed string for debug purpose.
DebugMessages() string
// Message returns the first level error message without further details.
Message() string
// Code returns the status code of this error.
Code() uint32
}
// CommonEdgeX generalizes an error structure which can be used for any type of EdgeX error.
type CommonEdgeX struct {
// callerInfo contains information of function call stacks.
callerInfo string
// message contains detailed information about the error.
message string
// code is the status code to represent this error.
// We are using the standard HTTP status code: https://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html
code uint32
// err is a nested error which is used to form a chain of errors for better context.
err error
}
func Is(code uint32, err error) bool {
ce, ok := As(err)
if !ok {
return false
}
return ce.code == code
}
func As(err error) (*CommonEdgeX, bool) {
var ce = new(CommonEdgeX)
ok := errors.As(err, &ce)
return ce, ok
}
func Kind(err error) uint32 {
var e CommonEdgeX
if !errors.As(err, &e) {
return KindUnknown
}
// We want to return the first "Kind" we see that isn't Unknown, because
// the higher in the stack the Kind was specified the more context we had.
if e.code != KindUnknown || e.err == nil {
return e.code
}
return e.code
}
// Error creates an error message taking all nested and wrapped errors into account.
func (ce CommonEdgeX) Error() string {
if ce.err == nil {
return "[Code(" + strconv.Itoa(int(ce.Code())) + ")] " + ce.message
}
// ce.err.Error functionality gets the error message of the nested error and which will handle both CommonEdgeX
// types and Go standard errors(both wrapped and non-wrapped).
if ce.message != "" {
return ce.message + " -> " + ce.err.Error()
}
return ce.err.Error()
}
// DebugMessages returns a string taking all nested and wrapped operations and errors into account.
func (ce CommonEdgeX) DebugMessages() string {
if ce.err == nil {
return ce.callerInfo + ": " + ce.message
}
if w, ok := ce.err.(CommonEdgeX); ok {
return ce.callerInfo + ": " + ce.message + " -> " + w.DebugMessages()
} else {
return ce.callerInfo + ": " + ce.message + " -> " + ce.err.Error()
}
}
// Message returns the first level error message without further details.
func (ce CommonEdgeX) Message() string {
if ce.message == "" && ce.err != nil {
if w, ok := ce.err.(CommonEdgeX); ok {
return w.Message()
} else {
return ce.err.Error()
}
}
return ce.message
}
// Code returns the status code of this error.
func (ce CommonEdgeX) Code() uint32 {
return ce.code
}
// Unwrap retrieves the next nested error in the wrapped error chain.
// This is used by the new wrapping and unwrapping features available in Go 1.13 and aids in traversing the error chain
// of wrapped errors.
func (ce CommonEdgeX) Unwrap() error {
return ce.err
}
// Is determines if an error is of type CommonEdgeX.
// This is used by the new wrapping and unwrapping features available in Go 1.13 and aids the errors.Is function when
// determining is an error or any error in the wrapped chain contains an error of a particular type.
func (ce CommonEdgeX) Is(err error) bool {
switch err.(type) {
case CommonEdgeX:
return true
default:
return false
}
}
func (ce CommonEdgeX) Cause() error {
return ce.err
}
// NewCommonErr 封装自定义错误
// 使用场景: 在底层调用第三方服务或包时需要封装自定义错误
func NewCommonErr(code uint32, wrappedError error) error {
_, ok := As(wrappedError)
if ok {
// 已经是自定义错误则不在封装
return wrappedError
}
return errors.WithStack(&CommonEdgeX{
code: code,
err: wrappedError,
})
}
// NewCommonEdgeX alias NewCommonErr, 建议直接使用 NewCommonErr
// Deprecated
func NewCommonEdgeX(code uint32, message string, wrappedError error) error {
if wrappedError != nil {
return NewCommonErr(code, fmt.Errorf("%s: %w", message, wrappedError))
} else {
return NewCommonErr(code, fmt.Errorf("%s", message))
}
}
// NewCommonEdgeXWrapper 封装自定义错误,1.取出自定义错误,2.未知错误统一使用DefaultSystemError
// 使用场景: 最上层需要取自定义错误码时调用
// Deprecated
func NewCommonEdgeXWrapper(wrappedError error) CommonEdgeX {
code := DefaultSystemError
// 优先断言默认edge错误否则直接转换rpc的错误
if w, ok := As(wrappedError); ok {
code = w.code
} else {
ew := ConvertFromRPC(wrappedError)
if w, ok = As(ew); ok {
code = w.code
}
}
// 非内部定制错误,强制转换为系统错误
if code < DefaultSystemError {
code = DefaultSystemError
}
return CommonEdgeX{
callerInfo: getCallerInformation(),
message: "",
code: code,
err: wrappedError,
}
}
// getCallerInformation generates information about the caller function. This function skips the caller which has
// invoked this function, but rather introspects the calling function 3 frames below this frame in the call stack. This
// function is a helper function which eliminates the need for the 'callerInfo' field in the `CommonEdgeX` type and
// providing an 'callerInfo' string when creating an 'CommonEdgeX'
func getCallerInformation() string {
pc := make([]uintptr, 10)
runtime.Callers(3, pc)
f := runtime.FuncForPC(pc[0])
file, line := f.FileLine(pc[0])
return fmt.Sprintf("[%s]-%s(line %d)", file, f.Name(), line)
}