Make Fields method accept both map and slice (#352)
This commit is contained in:
parent
cdd74175d0
commit
65adfd88ec
|
@ -148,16 +148,16 @@ func BenchmarkLogFieldType(b *testing.B) {
|
||||||
{"a", "a", 0},
|
{"a", "a", 0},
|
||||||
}
|
}
|
||||||
objects := []obj{
|
objects := []obj{
|
||||||
obj{"a", "a", 0},
|
{"a", "a", 0},
|
||||||
obj{"a", "a", 0},
|
{"a", "a", 0},
|
||||||
obj{"a", "a", 0},
|
{"a", "a", 0},
|
||||||
obj{"a", "a", 0},
|
{"a", "a", 0},
|
||||||
obj{"a", "a", 0},
|
{"a", "a", 0},
|
||||||
obj{"a", "a", 0},
|
{"a", "a", 0},
|
||||||
obj{"a", "a", 0},
|
{"a", "a", 0},
|
||||||
obj{"a", "a", 0},
|
{"a", "a", 0},
|
||||||
obj{"a", "a", 0},
|
{"a", "a", 0},
|
||||||
obj{"a", "a", 0},
|
{"a", "a", 0},
|
||||||
}
|
}
|
||||||
errs := []error{errors.New("a"), errors.New("b"), errors.New("c"), errors.New("d"), errors.New("e")}
|
errs := []error{errors.New("a"), errors.New("b"), errors.New("c"), errors.New("d"), errors.New("e")}
|
||||||
types := map[string]func(e *Event) *Event{
|
types := map[string]func(e *Event) *Event{
|
||||||
|
@ -272,16 +272,16 @@ func BenchmarkContextFieldType(b *testing.B) {
|
||||||
{"a", "a", 0},
|
{"a", "a", 0},
|
||||||
}
|
}
|
||||||
objects := []obj{
|
objects := []obj{
|
||||||
obj{"a", "a", 0},
|
{"a", "a", 0},
|
||||||
obj{"a", "a", 0},
|
{"a", "a", 0},
|
||||||
obj{"a", "a", 0},
|
{"a", "a", 0},
|
||||||
obj{"a", "a", 0},
|
{"a", "a", 0},
|
||||||
obj{"a", "a", 0},
|
{"a", "a", 0},
|
||||||
obj{"a", "a", 0},
|
{"a", "a", 0},
|
||||||
obj{"a", "a", 0},
|
{"a", "a", 0},
|
||||||
obj{"a", "a", 0},
|
{"a", "a", 0},
|
||||||
obj{"a", "a", 0},
|
{"a", "a", 0},
|
||||||
obj{"a", "a", 0},
|
{"a", "a", 0},
|
||||||
}
|
}
|
||||||
errs := []error{errors.New("a"), errors.New("b"), errors.New("c"), errors.New("d"), errors.New("e")}
|
errs := []error{errors.New("a"), errors.New("b"), errors.New("c"), errors.New("d"), errors.New("e")}
|
||||||
types := map[string]func(c Context) Context{
|
types := map[string]func(c Context) Context{
|
||||||
|
|
|
@ -364,6 +364,42 @@ func ExampleEvent_Durs() {
|
||||||
// Output: {"foo":"bar","durs":[10000,20000],"message":"hello world"}
|
// Output: {"foo":"bar","durs":[10000,20000],"message":"hello world"}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func ExampleEvent_Fields_map() {
|
||||||
|
fields := map[string]interface{}{
|
||||||
|
"bar": "baz",
|
||||||
|
"n": 1,
|
||||||
|
}
|
||||||
|
|
||||||
|
dst := bytes.Buffer{}
|
||||||
|
log := New(&dst)
|
||||||
|
|
||||||
|
log.Log().
|
||||||
|
Str("foo", "bar").
|
||||||
|
Fields(fields).
|
||||||
|
Msg("hello world")
|
||||||
|
|
||||||
|
fmt.Println(decodeIfBinaryToString(dst.Bytes()))
|
||||||
|
// Output: {"foo":"bar","bar":"baz","n":1,"message":"hello world"}
|
||||||
|
}
|
||||||
|
|
||||||
|
func ExampleEvent_Fields_slice() {
|
||||||
|
fields := []interface{}{
|
||||||
|
"bar", "baz",
|
||||||
|
"n", 1,
|
||||||
|
}
|
||||||
|
|
||||||
|
dst := bytes.Buffer{}
|
||||||
|
log := New(&dst)
|
||||||
|
|
||||||
|
log.Log().
|
||||||
|
Str("foo", "bar").
|
||||||
|
Fields(fields).
|
||||||
|
Msg("hello world")
|
||||||
|
|
||||||
|
fmt.Println(decodeIfBinaryToString(dst.Bytes()))
|
||||||
|
// Output: {"foo":"bar","bar":"baz","n":1,"message":"hello world"}
|
||||||
|
}
|
||||||
|
|
||||||
func ExampleContext_Dict() {
|
func ExampleContext_Dict() {
|
||||||
dst := bytes.Buffer{}
|
dst := bytes.Buffer{}
|
||||||
log := New(&dst).With().
|
log := New(&dst).With().
|
||||||
|
@ -510,3 +546,39 @@ func ExampleContext_Durs() {
|
||||||
fmt.Println(decodeIfBinaryToString(dst.Bytes()))
|
fmt.Println(decodeIfBinaryToString(dst.Bytes()))
|
||||||
// Output: {"foo":"bar","durs":[10000,20000],"message":"hello world"}
|
// Output: {"foo":"bar","durs":[10000,20000],"message":"hello world"}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func ExampleContext_Fields_map() {
|
||||||
|
fields := map[string]interface{}{
|
||||||
|
"bar": "baz",
|
||||||
|
"n": 1,
|
||||||
|
}
|
||||||
|
|
||||||
|
dst := bytes.Buffer{}
|
||||||
|
log := New(&dst).With().
|
||||||
|
Str("foo", "bar").
|
||||||
|
Fields(fields).
|
||||||
|
Logger()
|
||||||
|
|
||||||
|
log.Log().Msg("hello world")
|
||||||
|
|
||||||
|
fmt.Println(decodeIfBinaryToString(dst.Bytes()))
|
||||||
|
// Output: {"foo":"bar","bar":"baz","n":1,"message":"hello world"}
|
||||||
|
}
|
||||||
|
|
||||||
|
func ExampleContext_Fields_slice() {
|
||||||
|
fields := []interface{}{
|
||||||
|
"bar", "baz",
|
||||||
|
"n", 1,
|
||||||
|
}
|
||||||
|
|
||||||
|
dst := bytes.Buffer{}
|
||||||
|
log := New(&dst).With().
|
||||||
|
Str("foo", "bar").
|
||||||
|
Fields(fields).
|
||||||
|
Logger()
|
||||||
|
|
||||||
|
log.Log().Msg("hello world")
|
||||||
|
|
||||||
|
fmt.Println(decodeIfBinaryToString(dst.Bytes()))
|
||||||
|
// Output: {"foo":"bar","bar":"baz","n":1,"message":"hello world"}
|
||||||
|
}
|
||||||
|
|
|
@ -18,8 +18,10 @@ func (c Context) Logger() Logger {
|
||||||
return c.l
|
return c.l
|
||||||
}
|
}
|
||||||
|
|
||||||
// Fields is a helper function to use a map to set fields using type assertion.
|
// Fields is a helper function to use a map or slice to set fields using type assertion.
|
||||||
func (c Context) Fields(fields map[string]interface{}) Context {
|
// Only map[string]interface{} and []interface{} are accepted. []interface{} must
|
||||||
|
// alternate string keys and arbitrary values, and extraneous ones are ignored.
|
||||||
|
func (c Context) Fields(fields interface{}) Context {
|
||||||
c.l.context = appendFields(c.l.context, fields)
|
c.l.context = appendFields(c.l.context, fields)
|
||||||
return c
|
return c
|
||||||
}
|
}
|
||||||
|
|
6
event.go
6
event.go
|
@ -148,8 +148,10 @@ func (e *Event) msg(msg string) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Fields is a helper function to use a map to set fields using type assertion.
|
// Fields is a helper function to use a map or slice to set fields using type assertion.
|
||||||
func (e *Event) Fields(fields map[string]interface{}) *Event {
|
// Only map[string]interface{} and []interface{} are accepted. []interface{} must
|
||||||
|
// alternate string keys and arbitrary values, and extraneous ones are ignored.
|
||||||
|
func (e *Event) Fields(fields interface{}) *Event {
|
||||||
if e == nil {
|
if e == nil {
|
||||||
return e
|
return e
|
||||||
}
|
}
|
||||||
|
|
37
fields.go
37
fields.go
|
@ -12,15 +12,36 @@ func isNilValue(i interface{}) bool {
|
||||||
return (*[2]uintptr)(unsafe.Pointer(&i))[1] == 0
|
return (*[2]uintptr)(unsafe.Pointer(&i))[1] == 0
|
||||||
}
|
}
|
||||||
|
|
||||||
func appendFields(dst []byte, fields map[string]interface{}) []byte {
|
func appendFields(dst []byte, fields interface{}) []byte {
|
||||||
keys := make([]string, 0, len(fields))
|
switch fields := fields.(type) {
|
||||||
for key := range fields {
|
case []interface{}:
|
||||||
keys = append(keys, key)
|
if n := len(fields); n&0x1 == 1 { // odd number
|
||||||
|
fields = fields[:n-1]
|
||||||
|
}
|
||||||
|
dst = appendFieldList(dst, fields)
|
||||||
|
case map[string]interface{}:
|
||||||
|
keys := make([]string, 0, len(fields))
|
||||||
|
for key := range fields {
|
||||||
|
keys = append(keys, key)
|
||||||
|
}
|
||||||
|
sort.Strings(keys)
|
||||||
|
kv := make([]interface{}, 2)
|
||||||
|
for _, key := range keys {
|
||||||
|
kv[0], kv[1] = key, fields[key]
|
||||||
|
dst = appendFieldList(dst, kv)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
sort.Strings(keys)
|
return dst
|
||||||
for _, key := range keys {
|
}
|
||||||
dst = enc.AppendKey(dst, key)
|
|
||||||
val := fields[key]
|
func appendFieldList(dst []byte, kvList []interface{}) []byte {
|
||||||
|
for i, n := 0, len(kvList); i < n; i += 2 {
|
||||||
|
key, val := kvList[i], kvList[i+1]
|
||||||
|
if key, ok := key.(string); ok {
|
||||||
|
dst = enc.AppendKey(dst, key)
|
||||||
|
} else {
|
||||||
|
continue
|
||||||
|
}
|
||||||
if val, ok := val.(LogObjectMarshaler); ok {
|
if val, ok := val.(LogObjectMarshaler); ok {
|
||||||
e := newEvent(nil, 0)
|
e := newEvent(nil, 0)
|
||||||
e.buf = e.buf[:0]
|
e.buf = e.buf[:0]
|
||||||
|
|
|
@ -335,6 +335,38 @@ func ExampleEvent_Durs() {
|
||||||
// Output: {"foo":"bar","durs":[10000,20000],"message":"hello world"}
|
// Output: {"foo":"bar","durs":[10000,20000],"message":"hello world"}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func ExampleEvent_Fields_map() {
|
||||||
|
fields := map[string]interface{}{
|
||||||
|
"bar": "baz",
|
||||||
|
"n": 1,
|
||||||
|
}
|
||||||
|
|
||||||
|
log := zerolog.New(os.Stdout)
|
||||||
|
|
||||||
|
log.Log().
|
||||||
|
Str("foo", "bar").
|
||||||
|
Fields(fields).
|
||||||
|
Msg("hello world")
|
||||||
|
|
||||||
|
// Output: {"foo":"bar","bar":"baz","n":1,"message":"hello world"}
|
||||||
|
}
|
||||||
|
|
||||||
|
func ExampleEvent_Fields_slice() {
|
||||||
|
fields := []interface{}{
|
||||||
|
"bar", "baz",
|
||||||
|
"n", 1,
|
||||||
|
}
|
||||||
|
|
||||||
|
log := zerolog.New(os.Stdout)
|
||||||
|
|
||||||
|
log.Log().
|
||||||
|
Str("foo", "bar").
|
||||||
|
Fields(fields).
|
||||||
|
Msg("hello world")
|
||||||
|
|
||||||
|
// Output: {"foo":"bar","bar":"baz","n":1,"message":"hello world"}
|
||||||
|
}
|
||||||
|
|
||||||
func ExampleContext_Dict() {
|
func ExampleContext_Dict() {
|
||||||
log := zerolog.New(os.Stdout).With().
|
log := zerolog.New(os.Stdout).With().
|
||||||
Str("foo", "bar").
|
Str("foo", "bar").
|
||||||
|
@ -484,3 +516,35 @@ func ExampleContext_MacAddr() {
|
||||||
|
|
||||||
// Output: {"hostMAC":"00:14:22:01:23:45","message":"hello world"}
|
// Output: {"hostMAC":"00:14:22:01:23:45","message":"hello world"}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func ExampleContext_Fields_map() {
|
||||||
|
fields := map[string]interface{}{
|
||||||
|
"bar": "baz",
|
||||||
|
"n": 1,
|
||||||
|
}
|
||||||
|
|
||||||
|
log := zerolog.New(os.Stdout).With().
|
||||||
|
Str("foo", "bar").
|
||||||
|
Fields(fields).
|
||||||
|
Logger()
|
||||||
|
|
||||||
|
log.Log().Msg("hello world")
|
||||||
|
|
||||||
|
// Output: {"foo":"bar","bar":"baz","n":1,"message":"hello world"}
|
||||||
|
}
|
||||||
|
|
||||||
|
func ExampleContext_Fields_slice() {
|
||||||
|
fields := []interface{}{
|
||||||
|
"bar", "baz",
|
||||||
|
"n", 1,
|
||||||
|
}
|
||||||
|
|
||||||
|
log := zerolog.New(os.Stdout).With().
|
||||||
|
Str("foo", "bar").
|
||||||
|
Fields(fields).
|
||||||
|
Logger()
|
||||||
|
|
||||||
|
log.Log().Msg("hello world")
|
||||||
|
|
||||||
|
// Output: {"foo":"bar","bar":"baz","n":1,"message":"hello world"}
|
||||||
|
}
|
||||||
|
|
64
log_test.go
64
log_test.go
|
@ -246,6 +246,66 @@ func TestFieldsMapNilPnt(t *testing.T) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestFieldsSlice(t *testing.T) {
|
||||||
|
out := &bytes.Buffer{}
|
||||||
|
log := New(out)
|
||||||
|
log.Log().Fields([]interface{}{
|
||||||
|
"nil", nil,
|
||||||
|
"string", "foo",
|
||||||
|
"bytes", []byte("bar"),
|
||||||
|
"error", errors.New("some error"),
|
||||||
|
"bool", true,
|
||||||
|
"int", int(1),
|
||||||
|
"int8", int8(2),
|
||||||
|
"int16", int16(3),
|
||||||
|
"int32", int32(4),
|
||||||
|
"int64", int64(5),
|
||||||
|
"uint", uint(6),
|
||||||
|
"uint8", uint8(7),
|
||||||
|
"uint16", uint16(8),
|
||||||
|
"uint32", uint32(9),
|
||||||
|
"uint64", uint64(10),
|
||||||
|
"float32", float32(11),
|
||||||
|
"float64", float64(12),
|
||||||
|
"ipv6", net.IP{0x20, 0x01, 0x0d, 0xb8, 0x85, 0xa3, 0x00, 0x00, 0x00, 0x00, 0x8a, 0x2e, 0x03, 0x70, 0x73, 0x34},
|
||||||
|
"dur", 1 * time.Second,
|
||||||
|
"time", time.Time{},
|
||||||
|
"obj", obj{"a", "b", 1},
|
||||||
|
}).Msg("")
|
||||||
|
if got, want := decodeIfBinaryToString(out.Bytes()), `{"nil":null,"string":"foo","bytes":"bar","error":"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,"ipv6":"2001:db8:85a3::8a2e:370:7334","dur":1000,"time":"0001-01-01T00:00:00Z","obj":{"Pub":"a","Tag":"b","priv":1}}`+"\n"; got != want {
|
||||||
|
t.Errorf("invalid log output:\ngot: %v\nwant: %v", got, want)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestFieldsSliceExtraneous(t *testing.T) {
|
||||||
|
out := &bytes.Buffer{}
|
||||||
|
log := New(out)
|
||||||
|
log.Log().Fields([]interface{}{
|
||||||
|
"string", "foo",
|
||||||
|
"error", errors.New("some error"),
|
||||||
|
32, "valueForNonStringKey",
|
||||||
|
"bool", true,
|
||||||
|
"int", int(1),
|
||||||
|
"keyWithoutValue",
|
||||||
|
}).Msg("")
|
||||||
|
if got, want := decodeIfBinaryToString(out.Bytes()), `{"string":"foo","error":"some error","bool":true,"int":1}`+"\n"; got != want {
|
||||||
|
t.Errorf("invalid log output:\ngot: %v\nwant: %v", got, want)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestFieldsNotMapSlice(t *testing.T) {
|
||||||
|
out := &bytes.Buffer{}
|
||||||
|
log := New(out)
|
||||||
|
log.Log().
|
||||||
|
Fields(obj{"a", "b", 1}).
|
||||||
|
Fields("string").
|
||||||
|
Fields(1).
|
||||||
|
Msg("")
|
||||||
|
if got, want := decodeIfBinaryToString(out.Bytes()), `{}`+"\n"; got != want {
|
||||||
|
t.Errorf("invalid log output:\ngot: %v\nwant: %v", got, want)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func TestFields(t *testing.T) {
|
func TestFields(t *testing.T) {
|
||||||
out := &bytes.Buffer{}
|
out := &bytes.Buffer{}
|
||||||
log := New(out)
|
log := New(out)
|
||||||
|
@ -336,7 +396,7 @@ func TestFieldsArraySingleElement(t *testing.T) {
|
||||||
Floats32("float32", []float32{11}).
|
Floats32("float32", []float32{11}).
|
||||||
Floats64("float64", []float64{12}).
|
Floats64("float64", []float64{12}).
|
||||||
Durs("dur", []time.Duration{1 * time.Second}).
|
Durs("dur", []time.Duration{1 * time.Second}).
|
||||||
Times("time", []time.Time{time.Time{}}).
|
Times("time", []time.Time{{}}).
|
||||||
Msg("")
|
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"],"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)
|
t.Errorf("invalid log output:\ngot: %v\nwant: %v", got, want)
|
||||||
|
@ -363,7 +423,7 @@ func TestFieldsArrayMultipleElement(t *testing.T) {
|
||||||
Floats32("float32", []float32{11, 0}).
|
Floats32("float32", []float32{11, 0}).
|
||||||
Floats64("float64", []float64{12, 0}).
|
Floats64("float64", []float64{12, 0}).
|
||||||
Durs("dur", []time.Duration{1 * time.Second, 0}).
|
Durs("dur", []time.Duration{1 * time.Second, 0}).
|
||||||
Times("time", []time.Time{time.Time{}, time.Time{}}).
|
Times("time", []time.Time{{}, {}}).
|
||||||
Msg("")
|
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"],"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)
|
t.Errorf("invalid log output:\ngot: %v\nwant: %v", got, want)
|
||||||
|
|
Loading…
Reference in New Issue