Compare commits

...

5 Commits

Author SHA1 Message Date
84b2be3169 v0.0.235 added .Enum(..) to exerr 2023-08-09 14:40:16 +02:00
c872cecc67 v0.0.234 2023-08-09 10:39:14 +02:00
99cd92729e v0.0.233 IncludeMetaInGinOutput 2023-08-09 10:37:59 +02:00
ac416f7b69 v0.0.232 2023-08-08 18:01:00 +02:00
e10140e143 v0.0.231 2023-08-08 16:10:31 +02:00
9 changed files with 205 additions and 66 deletions

View File

@@ -209,36 +209,10 @@ func fmtOutput(cs string, enums []EnumDef, pkgname string) string {
str += "\n" str += "\n"
str += "import \"gogs.mikescher.com/BlackForestBytes/goext/langext\"" + "\n" str += "import \"gogs.mikescher.com/BlackForestBytes/goext/langext\"" + "\n"
str += "import \"gogs.mikescher.com/BlackForestBytes/goext/enums\"" + "\n"
str += "\n" str += "\n"
str += "const ChecksumGenerator = \"" + cs + "\"" + "\n" str += "const ChecksumGenerator = \"" + cs + "\" // GoExtVersion: " + goext.GoextVersion + "\n"
str += "\n"
str += "type Enum interface {" + "\n"
str += " Valid() bool" + "\n"
str += " ValuesAny() []any" + "\n"
str += " ValuesMeta() []EnumMetaValue" + "\n"
str += " VarName() string" + "\n"
str += "}" + "\n"
str += "" + "\n"
str += "type StringEnum interface {" + "\n"
str += " Enum" + "\n"
str += " String() string" + "\n"
str += "}" + "\n"
str += "" + "\n"
str += "type DescriptionEnum interface {" + "\n"
str += " Enum" + "\n"
str += " Description() string" + "\n"
str += "}" + "\n"
str += "\n"
str += "type EnumMetaValue struct {" + "\n"
str += " VarName string `json:\"varName\"`" + "\n"
str += " Value any `json:\"value\"`" + "\n"
str += " Description *string `json:\"description\"`" + "\n"
str += "}" + "\n"
str += "\n" str += "\n"
for _, enumdef := range enums { for _, enumdef := range enums {
@@ -292,7 +266,7 @@ func fmtOutput(cs string, enums []EnumDef, pkgname string) string {
str += "}" + "\n" str += "}" + "\n"
str += "" + "\n" str += "" + "\n"
str += "func (e " + enumdef.EnumTypeName + ") ValuesMeta() []EnumMetaValue {" + "\n" str += "func (e " + enumdef.EnumTypeName + ") ValuesMeta() []enums.EnumMetaValue {" + "\n"
str += " return " + enumdef.EnumTypeName + "ValuesMeta()" str += " return " + enumdef.EnumTypeName + "ValuesMeta()"
str += "}" + "\n" str += "}" + "\n"
str += "" + "\n" str += "" + "\n"
@@ -322,11 +296,11 @@ func fmtOutput(cs string, enums []EnumDef, pkgname string) string {
str += "}" + "\n" str += "}" + "\n"
str += "" + "\n" str += "" + "\n"
str += "func (e " + enumdef.EnumTypeName + ") Meta() EnumMetaValue {" + "\n" str += "func (e " + enumdef.EnumTypeName + ") Meta() enums.EnumMetaValue {" + "\n"
if hasDescr { if hasDescr {
str += " return EnumMetaValue{VarName: e.VarName(), Value: e, Description: langext.Ptr(e.Description())}" str += " return enums.EnumMetaValue{VarName: e.VarName(), Value: e, Description: langext.Ptr(e.Description())}"
} else { } else {
str += " return EnumMetaValue{VarName: e.VarName(), Value: e, Description: nil}" str += " return enums.EnumMetaValue{VarName: e.VarName(), Value: e, Description: nil}"
} }
str += "}" + "\n" str += "}" + "\n"
str += "" + "\n" str += "" + "\n"
@@ -346,8 +320,8 @@ func fmtOutput(cs string, enums []EnumDef, pkgname string) string {
str += "}" + "\n" str += "}" + "\n"
str += "" + "\n" str += "" + "\n"
str += "func " + enumdef.EnumTypeName + "ValuesMeta() []EnumMetaValue {" + "\n" str += "func " + enumdef.EnumTypeName + "ValuesMeta() []enums.EnumMetaValue {" + "\n"
str += " return []EnumMetaValue{" + "\n" str += " return []enums.EnumMetaValue{" + "\n"
for _, v := range enumdef.Values { for _, v := range enumdef.Values {
str += " " + v.VarName + ".Meta(),\n" str += " " + v.VarName + ".Meta(),\n"
} }

24
enums/enum.go Normal file
View File

@@ -0,0 +1,24 @@
package enums
type Enum interface {
Valid() bool
ValuesAny() []any
ValuesMeta() []EnumMetaValue
VarName() string
}
type StringEnum interface {
Enum
String() string
}
type DescriptionEnum interface {
Enum
Description() string
}
type EnumMetaValue struct {
VarName string `json:"varName"`
Value any `json:"value"`
Description *string `json:"description"`
}

View File

@@ -9,6 +9,7 @@ import (
"github.com/rs/zerolog" "github.com/rs/zerolog"
"go.mongodb.org/mongo-driver/bson/primitive" "go.mongodb.org/mongo-driver/bson/primitive"
"gogs.mikescher.com/BlackForestBytes/goext/dataext" "gogs.mikescher.com/BlackForestBytes/goext/dataext"
"gogs.mikescher.com/BlackForestBytes/goext/enums"
"gogs.mikescher.com/BlackForestBytes/goext/langext" "gogs.mikescher.com/BlackForestBytes/goext/langext"
"net/http" "net/http"
"os" "os"
@@ -80,6 +81,10 @@ func New(t ErrorType, msg string) *Builder {
} }
func Wrap(err error, msg string) *Builder { func Wrap(err error, msg string) *Builder {
if err == nil {
return &Builder{errorData: newExErr(CatSystem, TypeInternal, msg)} // prevent NPE if we call Wrap with err==nil
}
if !pkgconfig.RecursiveErrors { if !pkgconfig.RecursiveErrors {
v := FromError(err) v := FromError(err)
v.Message = msg v.Message = msg
@@ -277,6 +282,10 @@ func (b *Builder) Stringer(key string, val fmt.Stringer) *Builder {
} }
} }
func (b *Builder) Enum(key string, val enums.Enum) *Builder {
return b.addMeta(key, MDTEnum, newEnumWrap(val))
}
func (b *Builder) Stack() *Builder { func (b *Builder) Stack() *Builder {
return b.addMeta("@Stack", MDTString, string(debug.Stack())) return b.addMeta("@Stack", MDTString, string(debug.Stack()))
} }

View File

@@ -6,32 +6,35 @@ import (
) )
type ErrorPackageConfig struct { type ErrorPackageConfig struct {
ZeroLogErrTraces bool // autom print zerolog logs on .Build() (for SevErr and SevFatal) ZeroLogErrTraces bool // autom print zerolog logs on .Build() (for SevErr and SevFatal)
ZeroLogAllTraces bool // autom print zerolog logs on .Build() (for all Severities) ZeroLogAllTraces bool // autom print zerolog logs on .Build() (for all Severities)
RecursiveErrors bool // errors contains their Origin-Error RecursiveErrors bool // errors contains their Origin-Error
ExtendedGinOutput bool // Log extended data (trace, meta, ...) to gin in err.Output() ExtendedGinOutput bool // Log extended data (trace, meta, ...) to gin in err.Output()
ExtendGinOutput func(err *ExErr, json map[string]any) // (Optionally) extend the gin output with more fields IncludeMetaInGinOutput bool // Log meta fields ( from e.g. `.Str(key, val).Build()` ) to gin in err.Output()
ExtendGinDataOutput func(err *ExErr, depth int, json map[string]any) // (Optionally) extend the gin `__data` output with more fields ExtendGinOutput func(err *ExErr, json map[string]any) // (Optionally) extend the gin output with more fields
ExtendGinDataOutput func(err *ExErr, depth int, json map[string]any) // (Optionally) extend the gin `__data` output with more fields
} }
type ErrorPackageConfigInit struct { type ErrorPackageConfigInit struct {
ZeroLogErrTraces bool ZeroLogErrTraces bool
ZeroLogAllTraces bool ZeroLogAllTraces bool
RecursiveErrors bool RecursiveErrors bool
ExtendedGinOutput bool ExtendedGinOutput bool
ExtendGinOutput func(err *ExErr, json map[string]any) IncludeMetaInGinOutput bool
ExtendGinDataOutput func(err *ExErr, depth int, json map[string]any) ExtendGinOutput func(err *ExErr, json map[string]any)
ExtendGinDataOutput func(err *ExErr, depth int, json map[string]any)
} }
var initialized = false var initialized = false
var pkgconfig = ErrorPackageConfig{ var pkgconfig = ErrorPackageConfig{
ZeroLogErrTraces: true, ZeroLogErrTraces: true,
ZeroLogAllTraces: false, ZeroLogAllTraces: false,
RecursiveErrors: true, RecursiveErrors: true,
ExtendedGinOutput: false, ExtendedGinOutput: false,
ExtendGinOutput: func(err *ExErr, json map[string]any) {}, IncludeMetaInGinOutput: true,
ExtendGinDataOutput: func(err *ExErr, depth int, json map[string]any) {}, ExtendGinOutput: func(err *ExErr, json map[string]any) {},
ExtendGinDataOutput: func(err *ExErr, depth int, json map[string]any) {},
} }
// Init initializes the exerr packages // Init initializes the exerr packages
@@ -53,12 +56,13 @@ func Init(cfg ErrorPackageConfigInit) {
} }
pkgconfig = ErrorPackageConfig{ pkgconfig = ErrorPackageConfig{
ZeroLogErrTraces: cfg.ZeroLogErrTraces, ZeroLogErrTraces: cfg.ZeroLogErrTraces,
ZeroLogAllTraces: cfg.ZeroLogAllTraces, ZeroLogAllTraces: cfg.ZeroLogAllTraces,
RecursiveErrors: cfg.RecursiveErrors, RecursiveErrors: cfg.RecursiveErrors,
ExtendedGinOutput: cfg.ExtendedGinOutput, ExtendedGinOutput: cfg.ExtendedGinOutput,
ExtendGinOutput: ego, IncludeMetaInGinOutput: cfg.IncludeMetaInGinOutput,
ExtendGinDataOutput: egdo, ExtendGinOutput: ego,
ExtendGinDataOutput: egdo,
} }
initialized = true initialized = true

View File

@@ -8,7 +8,7 @@ import (
"time" "time"
) )
func (ee *ExErr) toJson(depth int, applyExtendListener bool) langext.H { func (ee *ExErr) toJson(depth int, applyExtendListener bool, outputMeta bool) langext.H {
ginJson := langext.H{} ginJson := langext.H{}
if ee.UniqueID != "" { if ee.UniqueID != "" {
@@ -39,7 +39,15 @@ func (ee *ExErr) toJson(depth int, applyExtendListener bool) langext.H {
ginJson["wrappedErrType"] = ee.WrappedErrType ginJson["wrappedErrType"] = ee.WrappedErrType
} }
if ee.OriginalError != nil { if ee.OriginalError != nil {
ginJson["original"] = ee.OriginalError.toJson(depth+1, applyExtendListener) ginJson["original"] = ee.OriginalError.toJson(depth+1, applyExtendListener, outputMeta)
}
if outputMeta {
metaJson := langext.H{}
for metaKey, metaVal := range ee.Meta {
metaJson[metaKey] = metaVal.rawValueForJson()
}
ginJson["meta"] = metaJson
} }
if applyExtendListener { if applyExtendListener {
@@ -55,7 +63,8 @@ func (ee *ExErr) toJson(depth int, applyExtendListener bool) langext.H {
// Parameters: // Parameters:
// - [applyExtendListener]: if false the pkgconfig.ExtendGinOutput / pkgconfig.ExtendGinDataOutput will not be applied // - [applyExtendListener]: if false the pkgconfig.ExtendGinOutput / pkgconfig.ExtendGinDataOutput will not be applied
// - [includeWrappedErrors]: if false we do not include the recursive/wrapped errors in `__data` // - [includeWrappedErrors]: if false we do not include the recursive/wrapped errors in `__data`
func (ee *ExErr) ToAPIJson(applyExtendListener bool, includeWrappedErrors bool) langext.H { // - [includeMetaFields]: if true we also include meta-values (aka from `.Str(key, value).Build()`), needs includeWrappedErrors=true
func (ee *ExErr) ToAPIJson(applyExtendListener bool, includeWrappedErrors bool, includeMetaFields bool) langext.H {
apiOutput := langext.H{ apiOutput := langext.H{
"errorid": ee.UniqueID, "errorid": ee.UniqueID,
@@ -65,7 +74,7 @@ func (ee *ExErr) ToAPIJson(applyExtendListener bool, includeWrappedErrors bool)
} }
if includeWrappedErrors { if includeWrappedErrors {
apiOutput["__data"] = ee.toJson(0, applyExtendListener) apiOutput["__data"] = ee.toJson(0, applyExtendListener, includeMetaFields)
} }
if applyExtendListener { if applyExtendListener {
@@ -97,7 +106,7 @@ func (ee *ExErr) Output(g *gin.Context) {
statuscode = *baseType.DefaultStatusCode statuscode = *baseType.DefaultStatusCode
} }
ginOutput := ee.ToAPIJson(true, pkgconfig.ExtendedGinOutput) ginOutput := ee.ToAPIJson(true, pkgconfig.ExtendedGinOutput, pkgconfig.IncludeMetaInGinOutput)
g.Render(statuscode, json.GoJsonRender{Data: ginOutput, NilSafeSlices: true, NilSafeMaps: true}) g.Render(statuscode, json.GoJsonRender{Data: ginOutput, NilSafeSlices: true, NilSafeMaps: true})
} }

View File

@@ -43,6 +43,7 @@ const (
MDTID metaDataType = "ID" MDTID metaDataType = "ID"
MDTAny metaDataType = "Interface" MDTAny metaDataType = "Interface"
MDTNil metaDataType = "Nil" MDTNil metaDataType = "Nil"
MDTEnum metaDataType = "Enum"
) )
type MetaValue struct { type MetaValue struct {
@@ -131,6 +132,8 @@ func (v MetaValue) SerializeValue() (string, error) {
return string(r), nil return string(r), nil
case MDTNil: case MDTNil:
return "", nil return "", nil
case MDTEnum:
return v.Value.(EnumWrap).Serialize(), nil
} }
return "", errors.New("Unknown type: " + string(v.DataType)) return "", errors.New("Unknown type: " + string(v.DataType))
} }
@@ -208,6 +211,8 @@ func (v MetaValue) ShortString(lim int) string {
return langext.StrLimit(string(r), lim, "...") return langext.StrLimit(string(r), lim, "...")
case MDTNil: case MDTNil:
return "<<null>>" return "<<null>>"
case MDTEnum:
return v.Value.(EnumWrap).String()
} }
return "(err)" return "(err)"
} }
@@ -270,6 +275,14 @@ func (v MetaValue) Apply(key string, evt *zerolog.Event) *zerolog.Event {
return evt.Ints32(key, v.Value.([]int32)) return evt.Ints32(key, v.Value.([]int32))
case MDTNil: case MDTNil:
return evt.Str(key, "<<null>>") return evt.Str(key, "<<null>>")
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)
}
} }
return evt.Str(key, "(err)") return evt.Str(key, "(err)")
} }
@@ -511,6 +524,10 @@ func (v *MetaValue) Deserialize(value string, datatype metaDataType) error {
v.Value = nil v.Value = nil
v.DataType = datatype v.DataType = datatype
return nil return nil
case MDTEnum:
v.Value = deserializeEnumWrap(value)
v.DataType = datatype
return nil
} }
return errors.New("Unknown type: " + string(datatype)) return errors.New("Unknown type: " + string(datatype))
} }
@@ -581,10 +598,55 @@ func (v MetaValue) ValueString() string {
return string(r) return string(r)
case MDTNil: case MDTNil:
return "<<null>>" return "<<null>>"
case MDTEnum:
return v.Value.(EnumWrap).String()
} }
return "(err)" return "(err)"
} }
// 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
}
return v.Value.(AnyWrap).Serialize()
}
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
}
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
}
return v.Value
}
func (mm MetaMap) FormatOneLine(singleMaxLen int) string { func (mm MetaMap) FormatOneLine(singleMaxLen int) string {
r := "" r := ""

View File

@@ -4,6 +4,7 @@ import (
"encoding/json" "encoding/json"
"fmt" "fmt"
"github.com/rs/zerolog/log" "github.com/rs/zerolog/log"
"gogs.mikescher.com/BlackForestBytes/goext/enums"
"gogs.mikescher.com/BlackForestBytes/goext/langext" "gogs.mikescher.com/BlackForestBytes/goext/langext"
"strings" "strings"
) )
@@ -131,3 +132,58 @@ func deserializeAnyWrap(v string) AnyWrap {
} }
} }
} }
type EnumWrap struct {
Type string
ValueString string
ValueRaw enums.Enum // `ValueRaw` is lost during serialization roundtrip
IsNil bool
}
func newEnumWrap(val enums.Enum) EnumWrap {
t := fmt.Sprintf("%T", val)
arr := strings.Split(t, ".")
if len(arr) > 0 {
t = arr[len(arr)-1]
}
if langext.IsNil(val) {
return EnumWrap{Type: t, ValueString: "", ValueRaw: val, IsNil: true}
}
if enumstr, ok := val.(enums.StringEnum); ok {
return EnumWrap{Type: t, ValueString: enumstr.String(), ValueRaw: val, IsNil: false}
}
return EnumWrap{Type: t, ValueString: fmt.Sprintf("%v", val), ValueRaw: val, IsNil: false}
}
func (w EnumWrap) Serialize() string {
if w.IsNil {
return "!nil" + ":" + w.Type
}
return w.Type + ":" + w.ValueString
}
func (w EnumWrap) String() string {
if w.IsNil {
return w.Type + "<<nil>>"
}
return "[" + w.Type + "] " + w.ValueString
}
func deserializeEnumWrap(v string) EnumWrap {
r := strings.SplitN(v, ":", 2)
if len(r) == 2 && r[0] == "!nil" {
return EnumWrap{Type: r[1], ValueString: v, ValueRaw: nil, IsNil: true}
}
if len(r) == 0 {
return EnumWrap{}
} else if len(r) == 1 {
return EnumWrap{Type: "", ValueString: v, ValueRaw: nil, IsNil: false}
} else {
return EnumWrap{Type: r[0], ValueString: r[1], ValueRaw: nil, IsNil: false}
}
}

View File

@@ -149,11 +149,12 @@ func (w *GinWrapper) NoRoute(handler WHandlerFunc) {
if w.bufferBody { if w.bufferBody {
handlers = append(handlers, BodyBuffer) handlers = append(handlers, BodyBuffer)
} }
handlers = append(handlers, Wrap(w, handler))
middlewareNames := langext.ArrMap(handlers, func(v gin.HandlerFunc) string { return nameOfFunction(v) }) middlewareNames := langext.ArrMap(handlers, func(v gin.HandlerFunc) string { return nameOfFunction(v) })
handlerName := nameOfFunction(handler) handlerName := nameOfFunction(handler)
handlers = append(handlers, Wrap(w, handler))
w.engine.NoRoute(handlers...) w.engine.NoRoute(handlers...)
w.routeSpecs = append(w.routeSpecs, ginRouteSpec{ w.routeSpecs = append(w.routeSpecs, ginRouteSpec{

View File

@@ -1,5 +1,5 @@
package goext package goext
const GoextVersion = "0.0.230" const GoextVersion = "0.0.235"
const GoextVersionTimestamp = "2023-08-08T16:09:02+0200" const GoextVersionTimestamp = "2023-08-09T14:40:16+0200"