2023-07-24 10:42:39 +02:00
|
|
|
package exerr
|
|
|
|
|
|
|
|
import (
|
|
|
|
"encoding/json"
|
|
|
|
"fmt"
|
|
|
|
"go.mongodb.org/mongo-driver/bson/primitive"
|
|
|
|
"gogs.mikescher.com/BlackForestBytes/goext/langext"
|
|
|
|
"reflect"
|
|
|
|
"time"
|
|
|
|
)
|
|
|
|
|
|
|
|
var reflectTypeStr = reflect.TypeOf("")
|
|
|
|
|
2023-07-24 11:11:15 +02:00
|
|
|
func FromError(err error) *ExErr {
|
2024-09-13 18:06:49 +02:00
|
|
|
|
|
|
|
//goland:noinspection GoTypeAssertionOnErrors
|
2023-07-24 10:42:39 +02:00
|
|
|
if verr, ok := err.(*ExErr); ok {
|
|
|
|
// A simple ExErr
|
|
|
|
return verr
|
|
|
|
}
|
|
|
|
|
2024-10-08 19:22:17 +02:00
|
|
|
//goland:noinspection GoTypeAssertionOnErrors
|
|
|
|
if verr, ok := err.(langext.PanicWrappedErr); ok {
|
|
|
|
return &ExErr{
|
|
|
|
UniqueID: newID(),
|
|
|
|
Category: CatForeign,
|
|
|
|
Type: TypePanic,
|
|
|
|
Severity: SevErr,
|
|
|
|
Timestamp: time.Time{},
|
|
|
|
StatusCode: nil,
|
|
|
|
Message: "A panic occured",
|
|
|
|
WrappedErrType: fmt.Sprintf("%T", verr),
|
|
|
|
WrappedErr: err,
|
|
|
|
Caller: "",
|
|
|
|
OriginalError: nil,
|
|
|
|
Meta: MetaMap{
|
2024-10-13 16:33:39 +02:00
|
|
|
"panic_object": {DataType: MDTString, Value: fmt.Sprintf("%+v", verr.RecoveredObj())},
|
|
|
|
"panic_type": {DataType: MDTString, Value: fmt.Sprintf("%T", verr.RecoveredObj())},
|
2024-10-08 19:22:17 +02:00
|
|
|
"stack": {DataType: MDTString, Value: verr.Stack},
|
|
|
|
},
|
|
|
|
Extra: make(map[string]any),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
//goland:noinspection GoTypeAssertionOnErrors
|
|
|
|
if verr, ok := err.(*langext.PanicWrappedErr); ok && verr != nil {
|
|
|
|
return &ExErr{
|
|
|
|
UniqueID: newID(),
|
|
|
|
Category: CatForeign,
|
|
|
|
Type: TypePanic,
|
|
|
|
Severity: SevErr,
|
|
|
|
Timestamp: time.Time{},
|
|
|
|
StatusCode: nil,
|
|
|
|
Message: "A panic occured",
|
|
|
|
WrappedErrType: fmt.Sprintf("%T", verr),
|
|
|
|
WrappedErr: err,
|
|
|
|
Caller: "",
|
|
|
|
OriginalError: nil,
|
|
|
|
Meta: MetaMap{
|
2024-10-13 16:10:55 +02:00
|
|
|
"panic_object": {DataType: MDTString, Value: fmt.Sprintf("%+v", verr.RecoveredObj())},
|
|
|
|
"panic_type": {DataType: MDTString, Value: fmt.Sprintf("%T", verr.RecoveredObj())},
|
2024-10-08 19:22:17 +02:00
|
|
|
"stack": {DataType: MDTString, Value: verr.Stack},
|
|
|
|
},
|
|
|
|
Extra: make(map[string]any),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-07-24 10:42:39 +02:00
|
|
|
// A foreign error (eg a MongoDB exception)
|
|
|
|
return &ExErr{
|
|
|
|
UniqueID: newID(),
|
|
|
|
Category: CatForeign,
|
|
|
|
Type: TypeInternal,
|
|
|
|
Severity: SevErr,
|
|
|
|
Timestamp: time.Time{},
|
|
|
|
StatusCode: nil,
|
|
|
|
Message: err.Error(),
|
|
|
|
WrappedErrType: fmt.Sprintf("%T", err),
|
2023-08-08 13:09:15 +02:00
|
|
|
WrappedErr: err,
|
2023-07-24 10:42:39 +02:00
|
|
|
Caller: "",
|
|
|
|
OriginalError: nil,
|
|
|
|
Meta: getForeignMeta(err),
|
2024-07-27 23:44:18 +02:00
|
|
|
Extra: make(map[string]any),
|
2023-07-24 10:42:39 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func newExErr(cat ErrorCategory, errtype ErrorType, msg string) *ExErr {
|
|
|
|
return &ExErr{
|
|
|
|
UniqueID: newID(),
|
|
|
|
Category: cat,
|
|
|
|
Type: errtype,
|
|
|
|
Severity: SevErr,
|
|
|
|
Timestamp: time.Now(),
|
|
|
|
StatusCode: nil,
|
|
|
|
Message: msg,
|
|
|
|
WrappedErrType: "",
|
2023-08-08 13:09:15 +02:00
|
|
|
WrappedErr: nil,
|
2023-07-24 10:42:39 +02:00
|
|
|
Caller: callername(2),
|
|
|
|
OriginalError: nil,
|
|
|
|
Meta: make(map[string]MetaValue),
|
2024-07-27 23:44:18 +02:00
|
|
|
Extra: make(map[string]any),
|
2023-07-24 10:42:39 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func wrapExErr(e *ExErr, msg string, cat ErrorCategory, stacktraceskip int) *ExErr {
|
|
|
|
return &ExErr{
|
|
|
|
UniqueID: newID(),
|
|
|
|
Category: cat,
|
|
|
|
Type: TypeWrap,
|
2024-06-03 13:48:30 +02:00
|
|
|
Severity: e.Severity,
|
2023-07-24 10:42:39 +02:00
|
|
|
Timestamp: time.Now(),
|
|
|
|
StatusCode: e.StatusCode,
|
|
|
|
Message: msg,
|
|
|
|
WrappedErrType: "",
|
2023-08-08 13:09:15 +02:00
|
|
|
WrappedErr: nil,
|
2023-07-24 10:42:39 +02:00
|
|
|
Caller: callername(1 + stacktraceskip),
|
|
|
|
OriginalError: e,
|
|
|
|
Meta: make(map[string]MetaValue),
|
2024-07-27 23:44:18 +02:00
|
|
|
Extra: langext.CopyMap(langext.ForceMap(e.Extra)),
|
2023-07-24 10:42:39 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func getForeignMeta(err error) (mm MetaMap) {
|
|
|
|
mm = make(map[string]MetaValue)
|
|
|
|
|
|
|
|
defer func() {
|
|
|
|
if panicerr := recover(); panicerr != nil {
|
|
|
|
New(TypePanic, "Panic while trying to get foreign meta").
|
|
|
|
Str("source", err.Error()).
|
|
|
|
Interface("panic-object", panicerr).
|
|
|
|
Stack().
|
|
|
|
Print()
|
|
|
|
}
|
|
|
|
}()
|
|
|
|
|
|
|
|
rval := reflect.ValueOf(err)
|
|
|
|
if rval.Kind() == reflect.Interface || rval.Kind() == reflect.Ptr {
|
|
|
|
rval = reflect.ValueOf(err).Elem()
|
|
|
|
}
|
|
|
|
|
|
|
|
mm.add("foreign.errortype", MDTString, rval.Type().String())
|
|
|
|
|
|
|
|
for k, v := range addMetaPrefix("foreign", getReflectedMetaValues(err, 8)) {
|
|
|
|
mm[k] = v
|
|
|
|
}
|
|
|
|
|
|
|
|
return mm
|
|
|
|
}
|
|
|
|
|
|
|
|
func getReflectedMetaValues(value interface{}, remainingDepth int) map[string]MetaValue {
|
|
|
|
|
|
|
|
if remainingDepth <= 0 {
|
|
|
|
return map[string]MetaValue{}
|
|
|
|
}
|
|
|
|
|
|
|
|
if langext.IsNil(value) {
|
|
|
|
return map[string]MetaValue{"": {DataType: MDTNil, Value: nil}}
|
|
|
|
}
|
|
|
|
|
|
|
|
rval := reflect.ValueOf(value)
|
|
|
|
|
|
|
|
if rval.Type().Kind() == reflect.Ptr {
|
|
|
|
|
|
|
|
if rval.IsNil() {
|
|
|
|
return map[string]MetaValue{"*": {DataType: MDTNil, Value: nil}}
|
|
|
|
}
|
|
|
|
|
|
|
|
elem := rval.Elem()
|
|
|
|
|
|
|
|
return addMetaPrefix("*", getReflectedMetaValues(elem.Interface(), remainingDepth-1))
|
|
|
|
}
|
|
|
|
|
|
|
|
if !rval.CanInterface() {
|
|
|
|
return map[string]MetaValue{"": {DataType: MDTString, Value: "<<no-interface>>"}}
|
|
|
|
}
|
|
|
|
|
|
|
|
raw := rval.Interface()
|
|
|
|
|
|
|
|
switch ifraw := raw.(type) {
|
|
|
|
case time.Time:
|
|
|
|
return map[string]MetaValue{"": {DataType: MDTTime, Value: ifraw}}
|
|
|
|
case time.Duration:
|
|
|
|
return map[string]MetaValue{"": {DataType: MDTDuration, Value: ifraw}}
|
|
|
|
case int:
|
|
|
|
return map[string]MetaValue{"": {DataType: MDTInt, Value: ifraw}}
|
|
|
|
case int8:
|
|
|
|
return map[string]MetaValue{"": {DataType: MDTInt8, Value: ifraw}}
|
|
|
|
case int16:
|
|
|
|
return map[string]MetaValue{"": {DataType: MDTInt16, Value: ifraw}}
|
|
|
|
case int32:
|
|
|
|
return map[string]MetaValue{"": {DataType: MDTInt32, Value: ifraw}}
|
|
|
|
case int64:
|
|
|
|
return map[string]MetaValue{"": {DataType: MDTInt64, Value: ifraw}}
|
|
|
|
case string:
|
|
|
|
return map[string]MetaValue{"": {DataType: MDTString, Value: ifraw}}
|
|
|
|
case bool:
|
|
|
|
return map[string]MetaValue{"": {DataType: MDTBool, Value: ifraw}}
|
|
|
|
case []byte:
|
|
|
|
return map[string]MetaValue{"": {DataType: MDTBytes, Value: ifraw}}
|
|
|
|
case float32:
|
|
|
|
return map[string]MetaValue{"": {DataType: MDTFloat32, Value: ifraw}}
|
|
|
|
case float64:
|
|
|
|
return map[string]MetaValue{"": {DataType: MDTFloat64, Value: ifraw}}
|
|
|
|
case []int:
|
|
|
|
return map[string]MetaValue{"": {DataType: MDTIntArray, Value: ifraw}}
|
|
|
|
case []int32:
|
|
|
|
return map[string]MetaValue{"": {DataType: MDTInt32Array, Value: ifraw}}
|
|
|
|
case primitive.ObjectID:
|
|
|
|
return map[string]MetaValue{"": {DataType: MDTObjectID, Value: ifraw}}
|
|
|
|
case []string:
|
|
|
|
return map[string]MetaValue{"": {DataType: MDTStringArray, Value: ifraw}}
|
|
|
|
}
|
|
|
|
|
|
|
|
if rval.Type().Kind() == reflect.Struct {
|
|
|
|
m := make(map[string]MetaValue)
|
|
|
|
for i := 0; i < rval.NumField(); i++ {
|
|
|
|
fieldtype := rval.Type().Field(i)
|
|
|
|
|
|
|
|
fieldname := fieldtype.Name
|
|
|
|
|
|
|
|
if fieldtype.IsExported() {
|
|
|
|
for k, v := range addMetaPrefix(fieldname, getReflectedMetaValues(rval.Field(i).Interface(), remainingDepth-1)) {
|
|
|
|
m[k] = v
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return m
|
|
|
|
}
|
|
|
|
|
|
|
|
if rval.Type().ConvertibleTo(reflectTypeStr) {
|
|
|
|
return map[string]MetaValue{"": {DataType: MDTString, Value: rval.Convert(reflectTypeStr).String()}}
|
|
|
|
}
|
|
|
|
|
|
|
|
jsonval, err := json.Marshal(value)
|
|
|
|
if err != nil {
|
2024-04-13 22:08:45 +02:00
|
|
|
return map[string]MetaValue{"": {DataType: MDTString, Value: fmt.Sprintf("Failed to Marshal %T:\n%+v", value, value)}}
|
2023-07-24 10:42:39 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
return map[string]MetaValue{"": {DataType: MDTString, Value: string(jsonval)}}
|
|
|
|
}
|
|
|
|
|
|
|
|
func addMetaPrefix(prefix string, m map[string]MetaValue) map[string]MetaValue {
|
|
|
|
if len(m) == 1 {
|
|
|
|
for k, v := range m {
|
|
|
|
if k == "" {
|
|
|
|
return map[string]MetaValue{prefix: v}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
r := make(map[string]MetaValue, len(m))
|
|
|
|
for k, v := range m {
|
|
|
|
r[prefix+"."+k] = v
|
|
|
|
}
|
|
|
|
return r
|
|
|
|
}
|