2023-02-15 16:04:19 +01:00
|
|
|
package exerr
|
|
|
|
|
|
|
|
import (
|
|
|
|
"encoding/hex"
|
|
|
|
"encoding/json"
|
|
|
|
"errors"
|
|
|
|
"fmt"
|
|
|
|
"github.com/rs/zerolog"
|
|
|
|
"go.mongodb.org/mongo-driver/bson"
|
|
|
|
"go.mongodb.org/mongo-driver/bson/primitive"
|
|
|
|
"gogs.mikescher.com/BlackForestBytes/goext/langext"
|
2024-01-22 12:33:41 +01:00
|
|
|
"math"
|
2023-02-15 16:04:19 +01:00
|
|
|
"strconv"
|
|
|
|
"strings"
|
|
|
|
"time"
|
|
|
|
)
|
|
|
|
|
2023-07-24 10:42:39 +02:00
|
|
|
// This is a buffed up map[string]any
|
|
|
|
// we also save type information of the map-values
|
|
|
|
// which allows us to deserialize them back into te correct types later
|
|
|
|
|
2023-02-15 16:04:19 +01:00
|
|
|
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"
|
2023-08-09 14:40:16 +02:00
|
|
|
MDTEnum metaDataType = "Enum"
|
2023-02-15 16:04:19 +01:00
|
|
|
)
|
|
|
|
|
|
|
|
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
|
2023-08-09 14:40:16 +02:00
|
|
|
case MDTEnum:
|
|
|
|
return v.Value.(EnumWrap).Serialize(), nil
|
2023-02-15 16:04:19 +01:00
|
|
|
}
|
|
|
|
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>>"
|
2023-08-09 14:40:16 +02:00
|
|
|
case MDTEnum:
|
|
|
|
return v.Value.(EnumWrap).String()
|
2023-02-15 16:04:19 +01:00
|
|
|
}
|
|
|
|
return "(err)"
|
|
|
|
}
|
|
|
|
|
2023-09-27 16:18:21 +02:00
|
|
|
func (v MetaValue) Apply(key string, evt *zerolog.Event, limitLen *int) *zerolog.Event {
|
2023-02-15 16:04:19 +01:00
|
|
|
switch v.DataType {
|
|
|
|
case MDTString:
|
2023-09-27 16:18:21 +02:00
|
|
|
if limitLen == nil {
|
|
|
|
return evt.Str(key, v.Value.(string))
|
|
|
|
} else {
|
2023-09-29 16:00:40 +02:00
|
|
|
return evt.Str(key, langext.StrLimit(v.Value.(string), *limitLen, "..."))
|
2023-09-27 16:18:21 +02:00
|
|
|
}
|
2023-02-15 16:04:19 +01:00
|
|
|
case MDTID:
|
|
|
|
return evt.Str(key, v.Value.(IDWrap).Value)
|
|
|
|
case MDTAny:
|
|
|
|
if v.Value.(AnyWrap).IsError {
|
|
|
|
return evt.Str(key, "(err)")
|
|
|
|
} else {
|
2023-09-27 16:18:21 +02:00
|
|
|
if limitLen == nil {
|
|
|
|
return evt.Str(key, v.Value.(AnyWrap).Json)
|
|
|
|
} else {
|
2023-09-29 16:00:40 +02:00
|
|
|
return evt.Str(key, langext.StrLimit(v.Value.(AnyWrap).Json, *limitLen, "..."))
|
2023-09-27 16:18:21 +02:00
|
|
|
}
|
2023-02-15 16:04:19 +01:00
|
|
|
}
|
|
|
|
case MDTStringPtr:
|
|
|
|
if langext.IsNil(v.Value) {
|
|
|
|
return evt.Str(key, "<<null>>")
|
|
|
|
}
|
2023-09-27 16:18:21 +02:00
|
|
|
if limitLen == nil {
|
|
|
|
return evt.Str(key, langext.CoalesceString(v.Value.(*string), "<<null>>"))
|
|
|
|
} else {
|
2023-09-29 16:00:40 +02:00
|
|
|
return evt.Str(key, langext.StrLimit(langext.CoalesceString(v.Value.(*string), "<<null>>"), *limitLen, "..."))
|
2023-09-27 16:18:21 +02:00
|
|
|
}
|
2023-02-15 16:04:19 +01:00
|
|
|
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>>")
|
2023-08-09 14:40:16 +02:00
|
|
|
case MDTEnum:
|
|
|
|
if v.Value.(EnumWrap).IsNil {
|
|
|
|
return evt.Any(key, nil)
|
|
|
|
} else if v.Value.(EnumWrap).ValueRaw != nil {
|
|
|
|
return evt.Any(key, v.Value.(EnumWrap).ValueRaw)
|
|
|
|
} else {
|
|
|
|
return evt.Str(key, v.Value.(EnumWrap).ValueString)
|
|
|
|
}
|
2023-02-15 16:04:19 +01:00
|
|
|
}
|
|
|
|
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 {
|
2023-07-24 10:42:39 +02:00
|
|
|
v.Value = langext.Ptr(value[1:])
|
2023-02-15 16:04:19 +01:00
|
|
|
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
|
2023-08-09 14:40:16 +02:00
|
|
|
case MDTEnum:
|
|
|
|
v.Value = deserializeEnumWrap(value)
|
|
|
|
v.DataType = datatype
|
|
|
|
return nil
|
2023-02-15 16:04:19 +01:00
|
|
|
}
|
|
|
|
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>>"
|
2023-08-09 14:40:16 +02:00
|
|
|
case MDTEnum:
|
|
|
|
return v.Value.(EnumWrap).String()
|
2023-02-15 16:04:19 +01:00
|
|
|
}
|
|
|
|
return "(err)"
|
|
|
|
}
|
|
|
|
|
2023-08-09 10:37:59 +02:00
|
|
|
// rawValueForJson returns most-of-the-time the `Value` field
|
|
|
|
// but for some datatyes we do special processing
|
|
|
|
// all, so we can pluck the output value in json.Marshal without any suprises
|
|
|
|
func (v MetaValue) rawValueForJson() any {
|
|
|
|
if v.DataType == MDTAny {
|
|
|
|
if v.Value.(AnyWrap).IsNil {
|
|
|
|
return nil
|
|
|
|
}
|
2023-08-21 15:19:40 +02:00
|
|
|
if v.Value.(AnyWrap).IsError {
|
|
|
|
return bson.M{"@error": true}
|
|
|
|
}
|
2023-08-21 15:34:27 +02:00
|
|
|
jsonobj := primitive.M{}
|
|
|
|
jsonarr := primitive.A{}
|
2023-08-21 15:19:40 +02:00
|
|
|
if err := json.Unmarshal([]byte(v.Value.(AnyWrap).Json), &jsonobj); err == nil {
|
|
|
|
return jsonobj
|
2023-08-21 15:34:27 +02:00
|
|
|
} else if err := json.Unmarshal([]byte(v.Value.(AnyWrap).Json), &jsonarr); err == nil {
|
|
|
|
return jsonarr
|
2023-08-21 15:19:40 +02:00
|
|
|
} else {
|
|
|
|
return bson.M{"type": v.Value.(AnyWrap).Type, "data": v.Value.(AnyWrap).Json}
|
|
|
|
}
|
2023-08-09 10:37:59 +02:00
|
|
|
}
|
|
|
|
if v.DataType == MDTID {
|
|
|
|
if v.Value.(IDWrap).IsNil {
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
return v.Value.(IDWrap).Value
|
|
|
|
}
|
|
|
|
if v.DataType == MDTBytes {
|
|
|
|
return hex.EncodeToString(v.Value.([]byte))
|
|
|
|
}
|
|
|
|
if v.DataType == MDTDuration {
|
|
|
|
return v.Value.(time.Duration).String()
|
|
|
|
}
|
|
|
|
if v.DataType == MDTTime {
|
|
|
|
return v.Value.(time.Time).Format(time.RFC3339Nano)
|
|
|
|
}
|
|
|
|
if v.DataType == MDTObjectID {
|
|
|
|
return v.Value.(primitive.ObjectID).Hex()
|
|
|
|
}
|
|
|
|
if v.DataType == MDTNil {
|
|
|
|
return nil
|
|
|
|
}
|
2023-08-09 14:40:16 +02:00
|
|
|
if v.DataType == MDTEnum {
|
|
|
|
if v.Value.(EnumWrap).IsNil {
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
if v.Value.(EnumWrap).ValueRaw != nil {
|
|
|
|
return v.Value.(EnumWrap).ValueRaw
|
|
|
|
}
|
|
|
|
return v.Value.(EnumWrap).ValueString
|
|
|
|
}
|
2024-01-22 12:33:41 +01:00
|
|
|
if v.DataType == MDTFloat32 {
|
|
|
|
if math.IsNaN(float64(v.Value.(float32))) {
|
|
|
|
return "float64::NaN"
|
|
|
|
} else if math.IsInf(float64(v.Value.(float32)), +1) {
|
|
|
|
return "float64::+inf"
|
|
|
|
} else if math.IsInf(float64(v.Value.(float32)), -1) {
|
|
|
|
return "float64::-inf"
|
|
|
|
} else {
|
|
|
|
return v.Value
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if v.DataType == MDTFloat64 {
|
|
|
|
if math.IsNaN(v.Value.(float64)) {
|
|
|
|
return "float64::NaN"
|
|
|
|
} else if math.IsInf(v.Value.(float64), +1) {
|
|
|
|
return "float64::+inf"
|
|
|
|
} else if math.IsInf(v.Value.(float64), -1) {
|
|
|
|
return "float64::-inf"
|
|
|
|
} else {
|
|
|
|
return v.Value
|
|
|
|
}
|
|
|
|
}
|
2023-08-09 10:37:59 +02:00
|
|
|
return v.Value
|
|
|
|
}
|
|
|
|
|
2023-02-15 16:04:19 +01:00
|
|
|
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
|
|
|
|
}
|
|
|
|
|
2023-09-27 16:18:21 +02:00
|
|
|
func (mm MetaMap) Apply(evt *zerolog.Event, limitLen *int) *zerolog.Event {
|
2023-02-15 16:04:19 +01:00
|
|
|
for key, val := range mm {
|
2023-09-27 16:18:21 +02:00
|
|
|
evt = val.Apply(key, evt, limitLen)
|
2023-02-15 16:04:19 +01:00
|
|
|
}
|
|
|
|
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
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|