copy bmerr stuff from bm
This commit is contained in:
parent
fd33b43f31
commit
17383894a7
1
exerr/builder.go
Normal file
1
exerr/builder.go
Normal file
@ -0,0 +1 @@
|
||||
package exerr
|
1
exerr/defaults.go
Normal file
1
exerr/defaults.go
Normal file
@ -0,0 +1 @@
|
||||
package exerr
|
17
exerr/errinit.go
Normal file
17
exerr/errinit.go
Normal file
@ -0,0 +1,17 @@
|
||||
package exerr
|
||||
|
||||
type ErrorPackageConfig struct {
|
||||
LogTraces bool
|
||||
RecursiveErrors bool
|
||||
}
|
||||
|
||||
var pkgconfig = ErrorPackageConfig{
|
||||
LogTraces: true,
|
||||
}
|
||||
|
||||
// Init initializes the exerr packages
|
||||
// Must be called at the program start, before (!) any errors
|
||||
// Is not thread-safe
|
||||
func Init(cfg ErrorPackageConfig) {
|
||||
pkgconfig = cfg
|
||||
}
|
32
exerr/exerr.go
Normal file
32
exerr/exerr.go
Normal file
@ -0,0 +1,32 @@
|
||||
package exerr
|
||||
|
||||
import (
|
||||
"time"
|
||||
)
|
||||
|
||||
type ExErr struct {
|
||||
UniqueID string `json:"uniqueID"`
|
||||
|
||||
Timestamp time.Time `json:"timestamp"`
|
||||
Category ErrorCategory `json:"category"`
|
||||
Severity ErrorSeverity `json:"severity"`
|
||||
Type ErrorType `json:"type"`
|
||||
Source ErrorSource `json:"source"`
|
||||
|
||||
Message string `json:"message"`
|
||||
Caller string `json:"caller"`
|
||||
|
||||
Meta MetaMap `json:"meta"`
|
||||
}
|
||||
|
||||
func (ee ExErr) Error() string {
|
||||
|
||||
}
|
||||
|
||||
func (ee ExErr) Unwrap() error {
|
||||
|
||||
}
|
||||
|
||||
func (ee ExErr) Is(err error) bool {
|
||||
|
||||
}
|
146
exerr/foreign.go
Normal file
146
exerr/foreign.go
Normal file
@ -0,0 +1,146 @@
|
||||
package exerr
|
||||
|
||||
import (
|
||||
"bringman.de/common/shared/langext"
|
||||
"encoding/json"
|
||||
"go.mongodb.org/mongo-driver/bson/primitive"
|
||||
"reflect"
|
||||
"time"
|
||||
)
|
||||
|
||||
var reflectTypeStr = reflect.TypeOf("")
|
||||
|
||||
func getForeignMeta(err error) (mm MetaMap) {
|
||||
mm = make(map[string]MetaValue)
|
||||
|
||||
defer func() {
|
||||
if panicerr := recover(); panicerr != nil {
|
||||
New(ErrPanic, "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 {
|
||||
panic(err) // gets recovered later up
|
||||
}
|
||||
|
||||
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
|
||||
}
|
35
exerr/listener.go
Normal file
35
exerr/listener.go
Normal file
@ -0,0 +1,35 @@
|
||||
package exerr
|
||||
|
||||
import "sync"
|
||||
|
||||
type Method string
|
||||
|
||||
const (
|
||||
MethodOutput Method = "OUTPUT"
|
||||
MethodPrint Method = "PRINT"
|
||||
MethodBuild Method = "BUILD"
|
||||
MethodFatal Method = "FATAL"
|
||||
)
|
||||
|
||||
type Listener = func(method Method, v BMError)
|
||||
|
||||
var listenerLock = sync.Mutex{}
|
||||
var listener = make([]Listener, 0)
|
||||
|
||||
func RegisterListener(l Listener) {
|
||||
listenerLock.Lock()
|
||||
defer listenerLock.Unlock()
|
||||
|
||||
listener = append(listener, l)
|
||||
}
|
||||
|
||||
func (b *Builder) CallListener(m Method) {
|
||||
valErr := b.toBMError()
|
||||
|
||||
listenerLock.Lock()
|
||||
defer listenerLock.Unlock()
|
||||
|
||||
for _, v := range listener {
|
||||
v(m, valErr)
|
||||
}
|
||||
}
|
698
exerr/meta.go
Normal file
698
exerr/meta.go
Normal file
@ -0,0 +1,698 @@
|
||||
package exerr
|
||||
|
||||
import (
|
||||
"bringman.de/common/shared/langext"
|
||||
spbmodels "bringman.de/proto/common/models"
|
||||
"encoding/hex"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"github.com/rs/zerolog"
|
||||
"github.com/rs/zerolog/log"
|
||||
"go.mongodb.org/mongo-driver/bson"
|
||||
"go.mongodb.org/mongo-driver/bson/primitive"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
type MetaMap map[string]MetaValue
|
||||
|
||||
type metaDataType string
|
||||
|
||||
const (
|
||||
MDTString metaDataType = "String"
|
||||
MDTStringPtr metaDataType = "StringPtr"
|
||||
MDTInt metaDataType = "Int"
|
||||
MDTInt8 metaDataType = "Int8"
|
||||
MDTInt16 metaDataType = "Int16"
|
||||
MDTInt32 metaDataType = "Int32"
|
||||
MDTInt64 metaDataType = "Int64"
|
||||
MDTFloat32 metaDataType = "Float32"
|
||||
MDTFloat64 metaDataType = "Float64"
|
||||
MDTBool metaDataType = "Bool"
|
||||
MDTBytes metaDataType = "Bytes"
|
||||
MDTObjectID metaDataType = "ObjectID"
|
||||
MDTTime metaDataType = "Time"
|
||||
MDTDuration metaDataType = "Duration"
|
||||
MDTStringArray metaDataType = "StringArr"
|
||||
MDTIntArray metaDataType = "IntArr"
|
||||
MDTInt32Array metaDataType = "Int32Arr"
|
||||
MDTID metaDataType = "ID"
|
||||
MDTAny metaDataType = "Interface"
|
||||
MDTNil metaDataType = "Nil"
|
||||
)
|
||||
|
||||
type MetaValue struct {
|
||||
DataType metaDataType `json:"dataType"`
|
||||
Value interface{} `json:"value"`
|
||||
}
|
||||
|
||||
type metaValueSerialization struct {
|
||||
DataType metaDataType `bson:"dataType"`
|
||||
Value string `bson:"value"`
|
||||
Raw interface{} `bson:"raw"`
|
||||
}
|
||||
|
||||
func (v MetaValue) SerializeValue() (string, error) {
|
||||
switch v.DataType {
|
||||
case MDTString:
|
||||
return v.Value.(string), nil
|
||||
case MDTID:
|
||||
return v.Value.(IDWrap).Serialize(), nil
|
||||
case MDTAny:
|
||||
return v.Value.(AnyWrap).Serialize(), nil
|
||||
case MDTStringPtr:
|
||||
if langext.IsNil(v.Value) {
|
||||
return "#", nil
|
||||
}
|
||||
r := v.Value.(*string)
|
||||
if r != nil {
|
||||
return "*" + *r, nil
|
||||
} else {
|
||||
return "#", nil
|
||||
}
|
||||
case MDTInt:
|
||||
return strconv.Itoa(v.Value.(int)), nil
|
||||
case MDTInt8:
|
||||
return strconv.FormatInt(int64(v.Value.(int8)), 10), nil
|
||||
case MDTInt16:
|
||||
return strconv.FormatInt(int64(v.Value.(int16)), 10), nil
|
||||
case MDTInt32:
|
||||
return strconv.FormatInt(int64(v.Value.(int32)), 10), nil
|
||||
case MDTInt64:
|
||||
return strconv.FormatInt(v.Value.(int64), 10), nil
|
||||
case MDTFloat32:
|
||||
return strconv.FormatFloat(float64(v.Value.(float32)), 'X', -1, 32), nil
|
||||
case MDTFloat64:
|
||||
return strconv.FormatFloat(v.Value.(float64), 'X', -1, 64), nil
|
||||
case MDTBool:
|
||||
if v.Value.(bool) {
|
||||
return "true", nil
|
||||
} else {
|
||||
return "false", nil
|
||||
}
|
||||
case MDTBytes:
|
||||
return hex.EncodeToString(v.Value.([]byte)), nil
|
||||
case MDTObjectID:
|
||||
return v.Value.(primitive.ObjectID).Hex(), nil
|
||||
case MDTTime:
|
||||
return strconv.FormatInt(v.Value.(time.Time).Unix(), 10) + "|" + strconv.FormatInt(int64(v.Value.(time.Time).Nanosecond()), 10), nil
|
||||
case MDTDuration:
|
||||
return v.Value.(time.Duration).String(), nil
|
||||
case MDTStringArray:
|
||||
if langext.IsNil(v.Value) {
|
||||
return "#", nil
|
||||
}
|
||||
r, err := json.Marshal(v.Value.([]string))
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return string(r), nil
|
||||
case MDTIntArray:
|
||||
if langext.IsNil(v.Value) {
|
||||
return "#", nil
|
||||
}
|
||||
r, err := json.Marshal(v.Value.([]int))
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return string(r), nil
|
||||
case MDTInt32Array:
|
||||
if langext.IsNil(v.Value) {
|
||||
return "#", nil
|
||||
}
|
||||
r, err := json.Marshal(v.Value.([]int32))
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return string(r), nil
|
||||
case MDTNil:
|
||||
return "", nil
|
||||
}
|
||||
return "", errors.New("Unknown type: " + string(v.DataType))
|
||||
}
|
||||
|
||||
func (v MetaValue) ShortString(lim int) string {
|
||||
switch v.DataType {
|
||||
case MDTString:
|
||||
r := strings.ReplaceAll(v.Value.(string), "\r", "")
|
||||
r = strings.ReplaceAll(r, "\n", "\\n")
|
||||
r = strings.ReplaceAll(r, "\t", "\\t")
|
||||
return langext.StrLimit(r, lim, "...")
|
||||
case MDTID:
|
||||
return v.Value.(IDWrap).String()
|
||||
case MDTAny:
|
||||
return v.Value.(AnyWrap).String()
|
||||
case MDTStringPtr:
|
||||
if langext.IsNil(v.Value) {
|
||||
return "<<null>>"
|
||||
}
|
||||
r := langext.CoalesceString(v.Value.(*string), "<<null>>")
|
||||
r = strings.ReplaceAll(r, "\r", "")
|
||||
r = strings.ReplaceAll(r, "\n", "\\n")
|
||||
r = strings.ReplaceAll(r, "\t", "\\t")
|
||||
return langext.StrLimit(r, lim, "...")
|
||||
case MDTInt:
|
||||
return strconv.Itoa(v.Value.(int))
|
||||
case MDTInt8:
|
||||
return strconv.FormatInt(int64(v.Value.(int8)), 10)
|
||||
case MDTInt16:
|
||||
return strconv.FormatInt(int64(v.Value.(int16)), 10)
|
||||
case MDTInt32:
|
||||
return strconv.FormatInt(int64(v.Value.(int32)), 10)
|
||||
case MDTInt64:
|
||||
return strconv.FormatInt(v.Value.(int64), 10)
|
||||
case MDTFloat32:
|
||||
return strconv.FormatFloat(float64(v.Value.(float32)), 'g', 4, 32)
|
||||
case MDTFloat64:
|
||||
return strconv.FormatFloat(v.Value.(float64), 'g', 4, 64)
|
||||
case MDTBool:
|
||||
return fmt.Sprintf("%v", v.Value.(bool))
|
||||
case MDTBytes:
|
||||
return langext.StrLimit(hex.EncodeToString(v.Value.([]byte)), lim, "...")
|
||||
case MDTObjectID:
|
||||
return v.Value.(primitive.ObjectID).Hex()
|
||||
case MDTTime:
|
||||
return v.Value.(time.Time).Format(time.RFC3339)
|
||||
case MDTDuration:
|
||||
return v.Value.(time.Duration).String()
|
||||
case MDTStringArray:
|
||||
if langext.IsNil(v.Value) {
|
||||
return "<<null>>"
|
||||
}
|
||||
r, err := json.Marshal(v.Value.([]string))
|
||||
if err != nil {
|
||||
return "(err)"
|
||||
}
|
||||
return langext.StrLimit(string(r), lim, "...")
|
||||
case MDTIntArray:
|
||||
if langext.IsNil(v.Value) {
|
||||
return "<<null>>"
|
||||
}
|
||||
r, err := json.Marshal(v.Value.([]int))
|
||||
if err != nil {
|
||||
return "(err)"
|
||||
}
|
||||
return langext.StrLimit(string(r), lim, "...")
|
||||
case MDTInt32Array:
|
||||
if langext.IsNil(v.Value) {
|
||||
return "<<null>>"
|
||||
}
|
||||
r, err := json.Marshal(v.Value.([]int32))
|
||||
if err != nil {
|
||||
return "(err)"
|
||||
}
|
||||
return langext.StrLimit(string(r), lim, "...")
|
||||
case MDTNil:
|
||||
return "<<null>>"
|
||||
}
|
||||
return "(err)"
|
||||
}
|
||||
|
||||
func (v MetaValue) Apply(key string, evt *zerolog.Event) *zerolog.Event {
|
||||
switch v.DataType {
|
||||
case MDTString:
|
||||
return evt.Str(key, v.Value.(string))
|
||||
case MDTID:
|
||||
return evt.Str(key, v.Value.(IDWrap).Value)
|
||||
case MDTAny:
|
||||
if v.Value.(AnyWrap).IsError {
|
||||
return evt.Str(key, "(err)")
|
||||
} else {
|
||||
return evt.Str(key, v.Value.(AnyWrap).Json)
|
||||
}
|
||||
case MDTStringPtr:
|
||||
if langext.IsNil(v.Value) {
|
||||
return evt.Str(key, "<<null>>")
|
||||
}
|
||||
return evt.Str(key, langext.CoalesceString(v.Value.(*string), "<<null>>"))
|
||||
case MDTInt:
|
||||
return evt.Int(key, v.Value.(int))
|
||||
case MDTInt8:
|
||||
return evt.Int8(key, v.Value.(int8))
|
||||
case MDTInt16:
|
||||
return evt.Int16(key, v.Value.(int16))
|
||||
case MDTInt32:
|
||||
return evt.Int32(key, v.Value.(int32))
|
||||
case MDTInt64:
|
||||
return evt.Int64(key, v.Value.(int64))
|
||||
case MDTFloat32:
|
||||
return evt.Float32(key, v.Value.(float32))
|
||||
case MDTFloat64:
|
||||
return evt.Float64(key, v.Value.(float64))
|
||||
case MDTBool:
|
||||
return evt.Bool(key, v.Value.(bool))
|
||||
case MDTBytes:
|
||||
return evt.Bytes(key, v.Value.([]byte))
|
||||
case MDTObjectID:
|
||||
return evt.Str(key, v.Value.(primitive.ObjectID).Hex())
|
||||
case MDTTime:
|
||||
return evt.Time(key, v.Value.(time.Time))
|
||||
case MDTDuration:
|
||||
return evt.Dur(key, v.Value.(time.Duration))
|
||||
case MDTStringArray:
|
||||
if langext.IsNil(v.Value) {
|
||||
return evt.Strs(key, nil)
|
||||
}
|
||||
return evt.Strs(key, v.Value.([]string))
|
||||
case MDTIntArray:
|
||||
if langext.IsNil(v.Value) {
|
||||
return evt.Ints(key, nil)
|
||||
}
|
||||
return evt.Ints(key, v.Value.([]int))
|
||||
case MDTInt32Array:
|
||||
if langext.IsNil(v.Value) {
|
||||
return evt.Ints32(key, nil)
|
||||
}
|
||||
return evt.Ints32(key, v.Value.([]int32))
|
||||
case MDTNil:
|
||||
return evt.Str(key, "<<null>>")
|
||||
}
|
||||
return evt.Str(key, "(err)")
|
||||
}
|
||||
|
||||
func (v MetaValue) MarshalJSON() ([]byte, error) {
|
||||
str, err := v.SerializeValue()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return json.Marshal(string(v.DataType) + ":" + str)
|
||||
}
|
||||
|
||||
func (v *MetaValue) UnmarshalJSON(data []byte) error {
|
||||
var str = ""
|
||||
err := json.Unmarshal(data, &str)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
split := strings.SplitN(str, ":", 2)
|
||||
if len(split) != 2 {
|
||||
return errors.New("failed to decode MetaValue: '" + str + "'")
|
||||
}
|
||||
|
||||
return v.Deserialize(split[1], metaDataType(split[0]))
|
||||
}
|
||||
|
||||
func (v MetaValue) MarshalBSON() ([]byte, error) {
|
||||
serval, err := v.SerializeValue()
|
||||
if err != nil {
|
||||
return nil, Wrap(err, "failed to bson-marshal MetaValue (serialize)").Build()
|
||||
}
|
||||
|
||||
// this is an kinda ugly hack - but serialization to mongodb and back can loose the correct type information....
|
||||
bin, err := bson.Marshal(metaValueSerialization{
|
||||
DataType: v.DataType,
|
||||
Value: serval,
|
||||
Raw: v.Value,
|
||||
})
|
||||
if err != nil {
|
||||
return nil, Wrap(err, "failed to bson-marshal MetaValue (marshal)").Build()
|
||||
}
|
||||
|
||||
return bin, nil
|
||||
}
|
||||
|
||||
func (v *MetaValue) UnmarshalBSON(bytes []byte) error {
|
||||
var serval metaValueSerialization
|
||||
err := bson.Unmarshal(bytes, &serval)
|
||||
if err != nil {
|
||||
return Wrap(err, "failed to bson-unmarshal MetaValue (unmarshal)").Build()
|
||||
}
|
||||
|
||||
err = v.Deserialize(serval.Value, serval.DataType)
|
||||
if err != nil {
|
||||
return Wrap(err, "failed to deserialize MetaValue from bson").Str("raw", serval.Value).Build()
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (v *MetaValue) Deserialize(value string, datatype metaDataType) error {
|
||||
switch datatype {
|
||||
case MDTString:
|
||||
v.Value = value
|
||||
v.DataType = datatype
|
||||
return nil
|
||||
case MDTID:
|
||||
v.Value = deserializeIDWrap(value)
|
||||
v.DataType = datatype
|
||||
return nil
|
||||
case MDTAny:
|
||||
v.Value = deserializeAnyWrap(value)
|
||||
v.DataType = datatype
|
||||
return nil
|
||||
case MDTStringPtr:
|
||||
if len(value) <= 0 || (value[0] != '*' && value[0] != '#') {
|
||||
return errors.New("Invalid StringPtr: " + value)
|
||||
} else if value == "#" {
|
||||
v.Value = nil
|
||||
v.DataType = datatype
|
||||
return nil
|
||||
} else {
|
||||
r, err := valueFromProto(value[1:], MDTString)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
v.Value = langext.Ptr(r.Value.(string))
|
||||
v.DataType = datatype
|
||||
return nil
|
||||
}
|
||||
case MDTInt:
|
||||
pv, err := strconv.ParseInt(value, 10, 0)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
v.Value = int(pv)
|
||||
v.DataType = datatype
|
||||
return nil
|
||||
case MDTInt8:
|
||||
pv, err := strconv.ParseInt(value, 10, 8)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
v.Value = int8(pv)
|
||||
v.DataType = datatype
|
||||
return nil
|
||||
case MDTInt16:
|
||||
pv, err := strconv.ParseInt(value, 10, 16)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
v.Value = int16(pv)
|
||||
v.DataType = datatype
|
||||
return nil
|
||||
case MDTInt32:
|
||||
pv, err := strconv.ParseInt(value, 10, 32)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
v.Value = int32(pv)
|
||||
v.DataType = datatype
|
||||
return nil
|
||||
case MDTInt64:
|
||||
pv, err := strconv.ParseInt(value, 10, 64)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
v.Value = pv
|
||||
v.DataType = datatype
|
||||
return nil
|
||||
case MDTFloat32:
|
||||
pv, err := strconv.ParseFloat(value, 64)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
v.Value = float32(pv)
|
||||
v.DataType = datatype
|
||||
return nil
|
||||
case MDTFloat64:
|
||||
pv, err := strconv.ParseFloat(value, 64)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
v.Value = pv
|
||||
v.DataType = datatype
|
||||
return nil
|
||||
case MDTBool:
|
||||
if value == "true" {
|
||||
v.Value = true
|
||||
v.DataType = datatype
|
||||
return nil
|
||||
}
|
||||
if value == "false" {
|
||||
v.Value = false
|
||||
v.DataType = datatype
|
||||
return nil
|
||||
}
|
||||
return errors.New("invalid bool value: " + value)
|
||||
case MDTBytes:
|
||||
r, err := hex.DecodeString(value)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
v.Value = r
|
||||
v.DataType = datatype
|
||||
return nil
|
||||
case MDTObjectID:
|
||||
r, err := primitive.ObjectIDFromHex(value)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
v.Value = r
|
||||
v.DataType = datatype
|
||||
return nil
|
||||
case MDTTime:
|
||||
ps := strings.Split(value, "|")
|
||||
if len(ps) != 2 {
|
||||
return errors.New("invalid time.time: " + value)
|
||||
}
|
||||
p1, err := strconv.ParseInt(ps[0], 10, 64)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
p2, err := strconv.ParseInt(ps[1], 10, 32)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
v.Value = time.Unix(p1, p2)
|
||||
v.DataType = datatype
|
||||
return nil
|
||||
case MDTDuration:
|
||||
r, err := time.ParseDuration(value)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
v.Value = r
|
||||
v.DataType = datatype
|
||||
return nil
|
||||
case MDTStringArray:
|
||||
if value == "#" {
|
||||
v.Value = nil
|
||||
v.DataType = datatype
|
||||
return nil
|
||||
}
|
||||
pj := make([]string, 0)
|
||||
err := json.Unmarshal([]byte(value), &pj)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
v.Value = pj
|
||||
v.DataType = datatype
|
||||
return nil
|
||||
case MDTIntArray:
|
||||
if value == "#" {
|
||||
v.Value = nil
|
||||
v.DataType = datatype
|
||||
return nil
|
||||
}
|
||||
pj := make([]int, 0)
|
||||
err := json.Unmarshal([]byte(value), &pj)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
v.Value = pj
|
||||
v.DataType = datatype
|
||||
return nil
|
||||
case MDTInt32Array:
|
||||
if value == "#" {
|
||||
v.Value = nil
|
||||
v.DataType = datatype
|
||||
return nil
|
||||
}
|
||||
pj := make([]int32, 0)
|
||||
err := json.Unmarshal([]byte(value), &pj)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
v.Value = pj
|
||||
v.DataType = datatype
|
||||
return nil
|
||||
case MDTNil:
|
||||
v.Value = nil
|
||||
v.DataType = datatype
|
||||
return nil
|
||||
}
|
||||
return errors.New("Unknown type: " + string(datatype))
|
||||
}
|
||||
|
||||
func (v MetaValue) ValueString() string {
|
||||
switch v.DataType {
|
||||
case MDTString:
|
||||
return v.Value.(string)
|
||||
case MDTID:
|
||||
return v.Value.(IDWrap).String()
|
||||
case MDTAny:
|
||||
return v.Value.(AnyWrap).String()
|
||||
case MDTStringPtr:
|
||||
if langext.IsNil(v.Value) {
|
||||
return "<<null>>"
|
||||
}
|
||||
return langext.CoalesceString(v.Value.(*string), "<<null>>")
|
||||
case MDTInt:
|
||||
return strconv.Itoa(v.Value.(int))
|
||||
case MDTInt8:
|
||||
return strconv.FormatInt(int64(v.Value.(int8)), 10)
|
||||
case MDTInt16:
|
||||
return strconv.FormatInt(int64(v.Value.(int16)), 10)
|
||||
case MDTInt32:
|
||||
return strconv.FormatInt(int64(v.Value.(int32)), 10)
|
||||
case MDTInt64:
|
||||
return strconv.FormatInt(v.Value.(int64), 10)
|
||||
case MDTFloat32:
|
||||
return strconv.FormatFloat(float64(v.Value.(float32)), 'g', 4, 32)
|
||||
case MDTFloat64:
|
||||
return strconv.FormatFloat(v.Value.(float64), 'g', 4, 64)
|
||||
case MDTBool:
|
||||
return fmt.Sprintf("%v", v.Value.(bool))
|
||||
case MDTBytes:
|
||||
return hex.EncodeToString(v.Value.([]byte))
|
||||
case MDTObjectID:
|
||||
return v.Value.(primitive.ObjectID).Hex()
|
||||
case MDTTime:
|
||||
return v.Value.(time.Time).Format(time.RFC3339Nano)
|
||||
case MDTDuration:
|
||||
return v.Value.(time.Duration).String()
|
||||
case MDTStringArray:
|
||||
if langext.IsNil(v.Value) {
|
||||
return "<<null>>"
|
||||
}
|
||||
r, err := json.MarshalIndent(v.Value.([]string), "", " ")
|
||||
if err != nil {
|
||||
return "(err)"
|
||||
}
|
||||
return string(r)
|
||||
case MDTIntArray:
|
||||
if langext.IsNil(v.Value) {
|
||||
return "<<null>>"
|
||||
}
|
||||
r, err := json.MarshalIndent(v.Value.([]int), "", " ")
|
||||
if err != nil {
|
||||
return "(err)"
|
||||
}
|
||||
return string(r)
|
||||
case MDTInt32Array:
|
||||
if langext.IsNil(v.Value) {
|
||||
return "<<null>>"
|
||||
}
|
||||
r, err := json.MarshalIndent(v.Value.([]int32), "", " ")
|
||||
if err != nil {
|
||||
return "(err)"
|
||||
}
|
||||
return string(r)
|
||||
case MDTNil:
|
||||
return "<<null>>"
|
||||
}
|
||||
return "(err)"
|
||||
}
|
||||
|
||||
func valueFromProto(value string, datatype metaDataType) (MetaValue, error) {
|
||||
obj := MetaValue{}
|
||||
err := obj.Deserialize(value, datatype)
|
||||
if err != nil {
|
||||
return MetaValue{}, err
|
||||
}
|
||||
return obj, nil
|
||||
}
|
||||
|
||||
func metaFromProto(proto []*spbmodels.CustomError_MetaValue) MetaMap {
|
||||
r := make(MetaMap)
|
||||
|
||||
for _, v := range proto {
|
||||
mval, err := valueFromProto(v.Value, metaDataType(v.Type))
|
||||
if err != nil {
|
||||
log.Warn().Err(err).Msg("metaFromProto failed for " + v.Key)
|
||||
continue
|
||||
}
|
||||
r[v.Key] = mval
|
||||
}
|
||||
|
||||
return r
|
||||
}
|
||||
|
||||
func (mm MetaMap) ToProto() []*spbmodels.CustomError_MetaValue {
|
||||
if mm == nil {
|
||||
return make([]*spbmodels.CustomError_MetaValue, 0)
|
||||
}
|
||||
r := make([]*spbmodels.CustomError_MetaValue, 0, len(mm))
|
||||
for k, v := range mm {
|
||||
strval, err := v.SerializeValue()
|
||||
if err != nil {
|
||||
log.Warn().Err(err).Msg("MetaMap.ToProto failed for " + k)
|
||||
continue
|
||||
}
|
||||
|
||||
r = append(r, &spbmodels.CustomError_MetaValue{
|
||||
Key: k,
|
||||
Type: string(v.DataType),
|
||||
Value: strval,
|
||||
})
|
||||
}
|
||||
return r
|
||||
}
|
||||
|
||||
func (mm MetaMap) FormatOneLine(singleMaxLen int) string {
|
||||
r := ""
|
||||
|
||||
i := 0
|
||||
for key, val := range mm {
|
||||
if i > 0 {
|
||||
r += ", "
|
||||
}
|
||||
|
||||
r += "\"" + key + "\""
|
||||
r += ": "
|
||||
r += "\"" + val.ShortString(singleMaxLen) + "\""
|
||||
|
||||
i++
|
||||
}
|
||||
|
||||
return r
|
||||
}
|
||||
|
||||
func (mm MetaMap) FormatMultiLine(indentFront string, indentKeys string, maxLenValue int) string {
|
||||
r := ""
|
||||
|
||||
r += indentFront + "{" + "\n"
|
||||
for key, val := range mm {
|
||||
if key == "gin.body" {
|
||||
continue
|
||||
}
|
||||
|
||||
r += indentFront
|
||||
r += indentKeys
|
||||
r += "\"" + key + "\""
|
||||
r += ": "
|
||||
r += "\"" + val.ShortString(maxLenValue) + "\""
|
||||
r += ",\n"
|
||||
}
|
||||
r += indentFront + "}"
|
||||
|
||||
return r
|
||||
}
|
||||
|
||||
func (mm MetaMap) Any() bool {
|
||||
return len(mm) > 0
|
||||
}
|
||||
|
||||
func (mm MetaMap) Apply(evt *zerolog.Event) *zerolog.Event {
|
||||
for key, val := range mm {
|
||||
evt = val.Apply(key, evt)
|
||||
}
|
||||
return evt
|
||||
}
|
||||
|
||||
func (mm MetaMap) add(key string, mdtype metaDataType, val interface{}) {
|
||||
if _, ok := mm[key]; !ok {
|
||||
mm[key] = MetaValue{DataType: mdtype, Value: val}
|
||||
return
|
||||
}
|
||||
for i := 2; ; i++ {
|
||||
realkey := key + "-" + strconv.Itoa(i)
|
||||
if _, ok := mm[realkey]; !ok {
|
||||
mm[realkey] = MetaValue{DataType: mdtype, Value: val}
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
14
exerr/stacktrace.go
Normal file
14
exerr/stacktrace.go
Normal file
@ -0,0 +1,14 @@
|
||||
package exerr
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"runtime"
|
||||
)
|
||||
|
||||
func callername(skip int) string {
|
||||
pc := make([]uintptr, 15)
|
||||
n := runtime.Callers(skip+2, pc)
|
||||
frames := runtime.CallersFrames(pc[:n])
|
||||
frame, _ := frames.Next()
|
||||
return fmt.Sprintf("%s:%d %s", frame.File, frame.Line, frame.Function)
|
||||
}
|
133
exerr/wrapper.go
Normal file
133
exerr/wrapper.go
Normal file
@ -0,0 +1,133 @@
|
||||
package exerr
|
||||
|
||||
import (
|
||||
"bringman.de/common/shared/langext"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"github.com/rs/zerolog/log"
|
||||
"strings"
|
||||
)
|
||||
|
||||
//
|
||||
// These are wrapper objects, because for some metadata-types we need to serialize a bit more complex data
|
||||
// (eg thy actual type for ID objects, or the json representation for any types)
|
||||
//
|
||||
|
||||
type IDWrap struct {
|
||||
Type string
|
||||
Value string
|
||||
IsNil bool
|
||||
}
|
||||
|
||||
func newIDWrap(val fmt.Stringer) IDWrap {
|
||||
t := fmt.Sprintf("%T", val)
|
||||
arr := strings.Split(t, ".")
|
||||
if len(arr) > 0 {
|
||||
t = arr[len(arr)-1]
|
||||
}
|
||||
|
||||
if langext.IsNil(val) {
|
||||
return IDWrap{Type: t, Value: "", IsNil: true}
|
||||
}
|
||||
|
||||
v := val.String()
|
||||
return IDWrap{Type: t, Value: v, IsNil: false}
|
||||
}
|
||||
|
||||
func (w IDWrap) Serialize() string {
|
||||
if w.IsNil {
|
||||
return "!nil" + ":" + w.Type
|
||||
}
|
||||
return w.Type + ":" + w.Value
|
||||
}
|
||||
|
||||
func (w IDWrap) String() string {
|
||||
if w.IsNil {
|
||||
return w.Type + "<<nil>>"
|
||||
}
|
||||
return w.Type + "(" + w.Value + ")"
|
||||
}
|
||||
|
||||
func deserializeIDWrap(v string) IDWrap {
|
||||
r := strings.SplitN(v, ":", 2)
|
||||
|
||||
if len(r) == 2 && r[0] == "!nil" {
|
||||
return IDWrap{Type: r[1], Value: v, IsNil: true}
|
||||
}
|
||||
|
||||
if len(r) == 0 {
|
||||
return IDWrap{}
|
||||
} else if len(r) == 1 {
|
||||
return IDWrap{Type: "", Value: v, IsNil: false}
|
||||
} else {
|
||||
return IDWrap{Type: r[0], Value: r[1], IsNil: false}
|
||||
}
|
||||
}
|
||||
|
||||
type AnyWrap struct {
|
||||
Type string
|
||||
Json string
|
||||
IsError bool
|
||||
IsNil bool
|
||||
}
|
||||
|
||||
func newAnyWrap(val any) (result AnyWrap) {
|
||||
result = AnyWrap{Type: "", Json: "", IsError: true, IsNil: false} // ensure a return in case of recover()
|
||||
|
||||
defer func() {
|
||||
if err := recover(); err != nil {
|
||||
// send error should never crash our program
|
||||
log.Error().Interface("err", err).Msg("Panic while trying to marshal anywrap ( bmerror.Interface )")
|
||||
}
|
||||
}()
|
||||
|
||||
t := fmt.Sprintf("%T", val)
|
||||
|
||||
if langext.IsNil(val) {
|
||||
return AnyWrap{Type: t, Json: "", IsError: false, IsNil: true}
|
||||
}
|
||||
|
||||
j, err := json.Marshal(val)
|
||||
if err == nil {
|
||||
return AnyWrap{Type: t, Json: string(j), IsError: false, IsNil: false}
|
||||
} else {
|
||||
return AnyWrap{Type: t, Json: "", IsError: true, IsNil: false}
|
||||
}
|
||||
}
|
||||
|
||||
func (w AnyWrap) Serialize() string {
|
||||
if w.IsError {
|
||||
return "ERR" + ":" + w.Type + ":" + w.Json
|
||||
} else if w.IsNil {
|
||||
return "NIL" + ":" + w.Type + ":" + w.Json
|
||||
} else {
|
||||
return "OK" + ":" + w.Type + ":" + w.Json
|
||||
}
|
||||
}
|
||||
|
||||
func (w AnyWrap) String() string {
|
||||
if w.IsError {
|
||||
return "(error)"
|
||||
} else if w.IsNil {
|
||||
return "(nil)"
|
||||
} else {
|
||||
return w.Json
|
||||
}
|
||||
}
|
||||
|
||||
func deserializeAnyWrap(v string) AnyWrap {
|
||||
r := strings.SplitN(v, ":", 3)
|
||||
if len(r) != 3 {
|
||||
return AnyWrap{IsError: true, Type: "", Json: "", IsNil: false}
|
||||
} else {
|
||||
if r[0] == "OK" {
|
||||
return AnyWrap{IsError: false, Type: r[1], Json: r[2], IsNil: false}
|
||||
} else if r[0] == "ERR" {
|
||||
return AnyWrap{IsError: true, Type: r[1], Json: r[2], IsNil: false}
|
||||
} else if r[0] == "NIL" {
|
||||
return AnyWrap{IsError: false, Type: r[1], Json: "", IsNil: true}
|
||||
} else {
|
||||
return AnyWrap{IsError: true, Type: "", Json: "", IsNil: false}
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user