From 6f8a5f9ccb38784f11fc2d65219b208d21c83fee Mon Sep 17 00:00:00 2001 From: Matas Date: Sat, 18 Sep 2021 20:22:21 +0300 Subject: [PATCH] Add Stringers support (#360) --- event.go | 16 +++++++++++----- internal/cbor/string.go | 27 +++++++++++++++++++++++++++ internal/json/string.go | 32 ++++++++++++++++++++++++++++++-- log_test.go | 10 +++++++--- 4 files changed, 75 insertions(+), 10 deletions(-) diff --git a/event.go b/event.go index 6aaa618e..b4099558 100644 --- a/event.go +++ b/event.go @@ -257,18 +257,24 @@ func (e *Event) Strs(key string, vals []string) *Event { return e } -// Stringer adds the field key with val.String() (or null if val is nil) to the *Event context. +// Stringer adds the field key with val.String() (or null if val is nil) +// to the *Event context. func (e *Event) Stringer(key string, val fmt.Stringer) *Event { if e == nil { return e } + e.buf = enc.AppendStringer(enc.AppendKey(e.buf, key), val) + return e +} - if val != nil { - e.buf = enc.AppendString(enc.AppendKey(e.buf, key), val.String()) +// Stringers adds the field key with vals where each individual val +// is used as val.String() (or null if val is empty) to the *Event +// context. +func (e *Event) Stringers(key string, vals []fmt.Stringer) *Event { + if e == nil { return e } - - e.buf = enc.AppendInterface(enc.AppendKey(e.buf, key), nil) + e.buf = enc.AppendStringers(enc.AppendKey(e.buf, key), vals) return e } diff --git a/internal/cbor/string.go b/internal/cbor/string.go index ff42afab..e7f90df4 100644 --- a/internal/cbor/string.go +++ b/internal/cbor/string.go @@ -1,5 +1,7 @@ package cbor +import "fmt" + // AppendStrings encodes and adds an array of strings to the dst byte array. func (e Encoder) AppendStrings(dst []byte, vals []string) []byte { major := majorTypeArray @@ -30,6 +32,31 @@ func (Encoder) AppendString(dst []byte, s string) []byte { return append(dst, s...) } +// AppendStringers encodes and adds an array of Stringer values +// to the dst byte array. +func (e Encoder) AppendStringers(dst []byte, vals []fmt.Stringer) []byte { + if len(vals) == 0 { + return e.AppendArrayEnd(e.AppendArrayStart(dst)) + } + dst = e.AppendArrayStart(dst) + dst = e.AppendStringer(dst, vals[0]) + if len(vals) > 1 { + for _, val := range vals[1:] { + dst = e.AppendStringer(dst, val) + } + } + return e.AppendArrayEnd(dst) +} + +// AppendStringer encodes and adds the Stringer value to the dst +// byte array. +func (e Encoder) AppendStringer(dst []byte, val fmt.Stringer) []byte { + if val == nil { + return e.AppendNil(dst) + } + return e.AppendString(dst, val.String()) +} + // AppendBytes encodes and adds an array of bytes to the dst byte array. func (Encoder) AppendBytes(dst, s []byte) []byte { major := majorTypeByteString diff --git a/internal/json/string.go b/internal/json/string.go index 815906ff..46698b54 100644 --- a/internal/json/string.go +++ b/internal/json/string.go @@ -1,6 +1,9 @@ package json -import "unicode/utf8" +import ( + "fmt" + "unicode/utf8" +) const hex = "0123456789abcdef" @@ -60,7 +63,32 @@ func (Encoder) AppendString(dst []byte, s string) []byte { return append(dst, '"') } -// appendStringComplex is used by appendString to take over an in +// AppendStringers encodes the provided Stringer list to json and +// appends the encoded Stringer list to the input byte slice. +func (e Encoder) AppendStringers(dst []byte, vals []fmt.Stringer) []byte { + if len(vals) == 0 { + return append(dst, '[', ']') + } + dst = append(dst, '[') + dst = e.AppendStringer(dst, vals[0]) + if len(vals) > 1 { + for _, val := range vals[1:] { + dst = e.AppendStringer(append(dst, ','), val) + } + } + return append(dst, ']') +} + +// AppendStringer encodes the input Stringer to json and appends the +// encoded Stringer value to the input byte slice. +func (e Encoder) AppendStringer(dst []byte, val fmt.Stringer) []byte { + if val == nil { + return e.AppendInterface(dst, nil) + } + return e.AppendString(dst, val.String()) +} + +//// appendStringComplex is used by appendString to take over an in // progress JSON string encoding that encountered a character that needs // to be encoded. func appendStringComplex(dst []byte, s string, i int) []byte { diff --git a/log_test.go b/log_test.go index 8f49c8fe..2ffd47e7 100644 --- a/log_test.go +++ b/log_test.go @@ -354,6 +354,7 @@ func TestFieldsArrayEmpty(t *testing.T) { log := New(out) log.Log(). Strs("string", []string{}). + Stringers("stringer", []fmt.Stringer{}). Errs("err", []error{}). Bools("bool", []bool{}). Ints("int", []int{}). @@ -371,7 +372,7 @@ func TestFieldsArrayEmpty(t *testing.T) { Durs("dur", []time.Duration{}). Times("time", []time.Time{}). Msg("") - if got, want := decodeIfBinaryToString(out.Bytes()), `{"string":[],"err":[],"bool":[],"int":[],"int8":[],"int16":[],"int32":[],"int64":[],"uint":[],"uint8":[],"uint16":[],"uint32":[],"uint64":[],"float32":[],"float64":[],"dur":[],"time":[]}`+"\n"; got != want { + if got, want := decodeIfBinaryToString(out.Bytes()), `{"string":[],"stringer":[],"err":[],"bool":[],"int":[],"int8":[],"int16":[],"int32":[],"int64":[],"uint":[],"uint8":[],"uint16":[],"uint32":[],"uint64":[],"float32":[],"float64":[],"dur":[],"time":[]}`+"\n"; got != want { t.Errorf("invalid log output:\ngot: %v\nwant: %v", got, want) } } @@ -381,6 +382,7 @@ func TestFieldsArraySingleElement(t *testing.T) { log := New(out) log.Log(). Strs("string", []string{"foo"}). + Stringers("stringer", []fmt.Stringer{net.IP{127, 0, 0, 1}}). Errs("err", []error{errors.New("some error")}). Bools("bool", []bool{true}). Ints("int", []int{1}). @@ -398,7 +400,7 @@ func TestFieldsArraySingleElement(t *testing.T) { Durs("dur", []time.Duration{1 * time.Second}). Times("time", []time.Time{{}}). Msg("") - if got, want := decodeIfBinaryToString(out.Bytes()), `{"string":["foo"],"err":["some error"],"bool":[true],"int":[1],"int8":[2],"int16":[3],"int32":[4],"int64":[5],"uint":[6],"uint8":[7],"uint16":[8],"uint32":[9],"uint64":[10],"float32":[11],"float64":[12],"dur":[1000],"time":["0001-01-01T00:00:00Z"]}`+"\n"; got != want { + if got, want := decodeIfBinaryToString(out.Bytes()), `{"string":["foo"],"stringer":["127.0.0.1"],"err":["some error"],"bool":[true],"int":[1],"int8":[2],"int16":[3],"int32":[4],"int64":[5],"uint":[6],"uint8":[7],"uint16":[8],"uint32":[9],"uint64":[10],"float32":[11],"float64":[12],"dur":[1000],"time":["0001-01-01T00:00:00Z"]}`+"\n"; got != want { t.Errorf("invalid log output:\ngot: %v\nwant: %v", got, want) } } @@ -408,6 +410,7 @@ func TestFieldsArrayMultipleElement(t *testing.T) { log := New(out) log.Log(). Strs("string", []string{"foo", "bar"}). + Stringers("stringer", []fmt.Stringer{nil, net.IP{127, 0, 0, 1}}). Errs("err", []error{errors.New("some error"), nil}). Bools("bool", []bool{true, false}). Ints("int", []int{1, 0}). @@ -425,7 +428,7 @@ func TestFieldsArrayMultipleElement(t *testing.T) { Durs("dur", []time.Duration{1 * time.Second, 0}). Times("time", []time.Time{{}, {}}). Msg("") - if got, want := decodeIfBinaryToString(out.Bytes()), `{"string":["foo","bar"],"err":["some error",null],"bool":[true,false],"int":[1,0],"int8":[2,0],"int16":[3,0],"int32":[4,0],"int64":[5,0],"uint":[6,0],"uint8":[7,0],"uint16":[8,0],"uint32":[9,0],"uint64":[10,0],"float32":[11,0],"float64":[12,0],"dur":[1000,0],"time":["0001-01-01T00:00:00Z","0001-01-01T00:00:00Z"]}`+"\n"; got != want { + if got, want := decodeIfBinaryToString(out.Bytes()), `{"string":["foo","bar"],"stringer":[null,"127.0.0.1"],"err":["some error",null],"bool":[true,false],"int":[1,0],"int8":[2,0],"int16":[3,0],"int32":[4,0],"int64":[5,0],"uint":[6,0],"uint8":[7,0],"uint16":[8,0],"uint32":[9,0],"uint64":[10,0],"float32":[11,0],"float64":[12,0],"dur":[1000,0],"time":["0001-01-01T00:00:00Z","0001-01-01T00:00:00Z"]}`+"\n"; got != want { t.Errorf("invalid log output:\ngot: %v\nwant: %v", got, want) } } @@ -436,6 +439,7 @@ func TestFieldsDisabled(t *testing.T) { now := time.Now() log.Debug(). Str("string", "foo"). + Stringer("stringer", net.IP{127, 0, 0, 1}). Bytes("bytes", []byte("bar")). Hex("hex", []byte{0x12, 0xef}). AnErr("some_err", nil).