logger/unwrap: fix Tagged option #163

Merged
vtolstov merged 1 commits from logger_unwrap into v3 2022-12-29 23:19:57 +03:00
2 changed files with 42 additions and 7 deletions

View File

@ -35,6 +35,7 @@ var (
nilAngleBytes = []byte("<nil>") nilAngleBytes = []byte("<nil>")
circularShortBytes = []byte("<shown>") circularShortBytes = []byte("<shown>")
invalidAngleBytes = []byte("<invalid>") invalidAngleBytes = []byte("<invalid>")
filteredBytes = []byte("<filtered>")
openBracketBytes = []byte("[") openBracketBytes = []byte("[")
closeBracketBytes = []byte("]") closeBracketBytes = []byte("]")
percentBytes = []byte("%") percentBytes = []byte("%")
@ -48,12 +49,13 @@ var (
type unwrap struct { type unwrap struct {
val interface{} val interface{}
s fmt.State s fmt.State
depth int
pointers map[uintptr]int pointers map[uintptr]int
opts *Options opts *Options
depth int
ignoreNextType bool ignoreNextType bool
} }
// Options struct
type Options struct { type Options struct {
Codec codec.Codec Codec codec.Codec
Indent string Indent string
@ -61,6 +63,7 @@ type Options struct {
Tagged bool Tagged bool
} }
// NewOptions creates new Options struct via provided args
func NewOptions(opts ...Option) Options { func NewOptions(opts ...Option) Options {
options := Options{ options := Options{
Indent: " ", Indent: " ",
@ -72,26 +75,31 @@ func NewOptions(opts ...Option) Options {
return options return options
} }
// Option func signature
type Option func(*Options) type Option func(*Options)
// Indent option specify indent level
func Indent(f string) Option { func Indent(f string) Option {
return func(o *Options) { return func(o *Options) {
o.Indent = f o.Indent = f
} }
} }
// Methods option toggles fmt.Stringer methods
func Methods(b bool) Option { func Methods(b bool) Option {
return func(o *Options) { return func(o *Options) {
o.Methods = b o.Methods = b
} }
} }
// Codec option automatic marshal arg via specified codec and write it to log
func Codec(c codec.Codec) Option { func Codec(c codec.Codec) Option {
return func(o *Options) { return func(o *Options) {
o.Codec = c o.Codec = c
} }
} }
// Tagged option toggles output only logger:"take" fields
func Tagged(b bool) Option { func Tagged(b bool) Option {
return func(o *Options) { return func(o *Options) {
o.Tagged = b o.Tagged = b
@ -204,10 +212,8 @@ func (f *unwrap) formatPtr(v reflect.Value) {
switch { switch {
case nilFound: case nilFound:
_, _ = f.s.Write(nilAngleBytes) _, _ = f.s.Write(nilAngleBytes)
case cycleFound: case cycleFound:
_, _ = f.s.Write(circularShortBytes) _, _ = f.s.Write(circularShortBytes)
default: default:
f.ignoreNextType = true f.ignoreNextType = true
f.format(ve) f.format(ve)
@ -263,7 +269,7 @@ func (f *unwrap) format(v reflect.Value) {
// Call Stringer/error interfaces if they exist and the handle methods // Call Stringer/error interfaces if they exist and the handle methods
// flag is enabled. // flag is enabled.
if !f.opts.Methods { if f.opts.Methods {
if (kind != reflect.Invalid) && (kind != reflect.Interface) { if (kind != reflect.Invalid) && (kind != reflect.Interface) {
if handled := handleMethods(f.opts, f.s, v); handled { if handled := handleMethods(f.opts, f.s, v); handled {
return return
@ -342,6 +348,7 @@ func (f *unwrap) format(v reflect.Value) {
_, _ = f.s.Write(closeMapBytes) _, _ = f.s.Write(closeMapBytes)
case reflect.Struct: case reflect.Struct:
numFields := v.NumField() numFields := v.NumField()
numWritten := 0
_, _ = f.s.Write(openBraceBytes) _, _ = f.s.Write(openBraceBytes)
f.depth++ f.depth++
vt := v.Type() vt := v.Type()
@ -349,9 +356,12 @@ func (f *unwrap) format(v reflect.Value) {
for i := 0; i < numFields; i++ { for i := 0; i < numFields; i++ {
sv, ok := vt.Field(i).Tag.Lookup("logger") sv, ok := vt.Field(i).Tag.Lookup("logger")
if ok { if ok {
if sv == "omit" { switch sv {
case "omit":
prevSkip = true prevSkip = true
continue continue
case "take":
break
} }
} else if f.opts.Tagged { } else if f.opts.Tagged {
prevSkip = true prevSkip = true
@ -370,8 +380,12 @@ func (f *unwrap) format(v reflect.Value) {
_, _ = f.s.Write(colonBytes) _, _ = f.s.Write(colonBytes)
} }
f.format(f.unpackValue(v.Field(i))) f.format(f.unpackValue(v.Field(i)))
numWritten++
} }
f.depth-- f.depth--
if numWritten == 0 && f.depth < 0 {
_, _ = f.s.Write(filteredBytes)
}
_, _ = f.s.Write(closeBraceBytes) _, _ = f.s.Write(closeBraceBytes)
case reflect.Uintptr: case reflect.Uintptr:
getHexPtr(f.s, uintptr(v.Uint())) getHexPtr(f.s, uintptr(v.Uint()))

View File

@ -57,10 +57,11 @@ func TestOmit(t *testing.T) {
type val struct { type val struct {
Key1 string `logger:"omit"` Key1 string `logger:"omit"`
Key2 string `logger:"take"` Key2 string `logger:"take"`
Key3 string
} }
v1 := &val{Key1: "val1", Key2: "val2"} v1 := &val{Key1: "val1", Key2: "val2", Key3: "val3"}
buf := fmt.Sprintf("%#v", Unwrap(v1)) buf := fmt.Sprintf("%#v", Unwrap(v1))
if strings.Compare(buf, `&unwrap.val{Key2:"val2"}`) != 0 { if strings.Compare(buf, `&unwrap.val{Key2:"val2", Key3:"val3"}`) != 0 {
t.Fatalf("not proper written %s", buf) t.Fatalf("not proper written %s", buf)
} }
} }
@ -77,3 +78,23 @@ func TestTagged(t *testing.T) {
t.Fatalf("not proper written %s", buf) t.Fatalf("not proper written %s", buf)
} }
} }
func TestTaggedNested(t *testing.T) {
type val struct {
key string `logger:"take"`
val string `logger:"omit"`
unk string
}
type str struct {
key string `logger:"omit"`
val *val `logger:"take"`
}
var iface interface{}
v := &str{val: &val{key: "test", unk: "unk"}}
iface = v
buf := fmt.Sprintf("%#v", Unwrap(iface, Tagged(true)))
if strings.Compare(buf, `&unwrap.str{val:(*unwrap.val){key:"test"}}`) != 0 {
t.Fatalf("not proper written %s", buf)
}
}