Skip to content

Commit 65adfd8

Browse files
authored
Make Fields method accept both map and slice (#352)
1 parent cdd7417 commit 65adfd8

File tree

7 files changed

+255
-34
lines changed

7 files changed

+255
-34
lines changed

benchmark_test.go

Lines changed: 20 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -148,16 +148,16 @@ func BenchmarkLogFieldType(b *testing.B) {
148148
{"a", "a", 0},
149149
}
150150
objects := []obj{
151-
obj{"a", "a", 0},
152-
obj{"a", "a", 0},
153-
obj{"a", "a", 0},
154-
obj{"a", "a", 0},
155-
obj{"a", "a", 0},
156-
obj{"a", "a", 0},
157-
obj{"a", "a", 0},
158-
obj{"a", "a", 0},
159-
obj{"a", "a", 0},
160-
obj{"a", "a", 0},
151+
{"a", "a", 0},
152+
{"a", "a", 0},
153+
{"a", "a", 0},
154+
{"a", "a", 0},
155+
{"a", "a", 0},
156+
{"a", "a", 0},
157+
{"a", "a", 0},
158+
{"a", "a", 0},
159+
{"a", "a", 0},
160+
{"a", "a", 0},
161161
}
162162
errs := []error{errors.New("a"), errors.New("b"), errors.New("c"), errors.New("d"), errors.New("e")}
163163
types := map[string]func(e *Event) *Event{
@@ -272,16 +272,16 @@ func BenchmarkContextFieldType(b *testing.B) {
272272
{"a", "a", 0},
273273
}
274274
objects := []obj{
275-
obj{"a", "a", 0},
276-
obj{"a", "a", 0},
277-
obj{"a", "a", 0},
278-
obj{"a", "a", 0},
279-
obj{"a", "a", 0},
280-
obj{"a", "a", 0},
281-
obj{"a", "a", 0},
282-
obj{"a", "a", 0},
283-
obj{"a", "a", 0},
284-
obj{"a", "a", 0},
275+
{"a", "a", 0},
276+
{"a", "a", 0},
277+
{"a", "a", 0},
278+
{"a", "a", 0},
279+
{"a", "a", 0},
280+
{"a", "a", 0},
281+
{"a", "a", 0},
282+
{"a", "a", 0},
283+
{"a", "a", 0},
284+
{"a", "a", 0},
285285
}
286286
errs := []error{errors.New("a"), errors.New("b"), errors.New("c"), errors.New("d"), errors.New("e")}
287287
types := map[string]func(c Context) Context{

binary_test.go

Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -364,6 +364,42 @@ func ExampleEvent_Durs() {
364364
// Output: {"foo":"bar","durs":[10000,20000],"message":"hello world"}
365365
}
366366

367+
func ExampleEvent_Fields_map() {
368+
fields := map[string]interface{}{
369+
"bar": "baz",
370+
"n": 1,
371+
}
372+
373+
dst := bytes.Buffer{}
374+
log := New(&dst)
375+
376+
log.Log().
377+
Str("foo", "bar").
378+
Fields(fields).
379+
Msg("hello world")
380+
381+
fmt.Println(decodeIfBinaryToString(dst.Bytes()))
382+
// Output: {"foo":"bar","bar":"baz","n":1,"message":"hello world"}
383+
}
384+
385+
func ExampleEvent_Fields_slice() {
386+
fields := []interface{}{
387+
"bar", "baz",
388+
"n", 1,
389+
}
390+
391+
dst := bytes.Buffer{}
392+
log := New(&dst)
393+
394+
log.Log().
395+
Str("foo", "bar").
396+
Fields(fields).
397+
Msg("hello world")
398+
399+
fmt.Println(decodeIfBinaryToString(dst.Bytes()))
400+
// Output: {"foo":"bar","bar":"baz","n":1,"message":"hello world"}
401+
}
402+
367403
func ExampleContext_Dict() {
368404
dst := bytes.Buffer{}
369405
log := New(&dst).With().
@@ -510,3 +546,39 @@ func ExampleContext_Durs() {
510546
fmt.Println(decodeIfBinaryToString(dst.Bytes()))
511547
// Output: {"foo":"bar","durs":[10000,20000],"message":"hello world"}
512548
}
549+
550+
func ExampleContext_Fields_map() {
551+
fields := map[string]interface{}{
552+
"bar": "baz",
553+
"n": 1,
554+
}
555+
556+
dst := bytes.Buffer{}
557+
log := New(&dst).With().
558+
Str("foo", "bar").
559+
Fields(fields).
560+
Logger()
561+
562+
log.Log().Msg("hello world")
563+
564+
fmt.Println(decodeIfBinaryToString(dst.Bytes()))
565+
// Output: {"foo":"bar","bar":"baz","n":1,"message":"hello world"}
566+
}
567+
568+
func ExampleContext_Fields_slice() {
569+
fields := []interface{}{
570+
"bar", "baz",
571+
"n", 1,
572+
}
573+
574+
dst := bytes.Buffer{}
575+
log := New(&dst).With().
576+
Str("foo", "bar").
577+
Fields(fields).
578+
Logger()
579+
580+
log.Log().Msg("hello world")
581+
582+
fmt.Println(decodeIfBinaryToString(dst.Bytes()))
583+
// Output: {"foo":"bar","bar":"baz","n":1,"message":"hello world"}
584+
}

context.go

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,8 +18,10 @@ func (c Context) Logger() Logger {
1818
return c.l
1919
}
2020

21-
// Fields is a helper function to use a map to set fields using type assertion.
22-
func (c Context) Fields(fields map[string]interface{}) Context {
21+
// Fields is a helper function to use a map or slice to set fields using type assertion.
22+
// Only map[string]interface{} and []interface{} are accepted. []interface{} must
23+
// alternate string keys and arbitrary values, and extraneous ones are ignored.
24+
func (c Context) Fields(fields interface{}) Context {
2325
c.l.context = appendFields(c.l.context, fields)
2426
return c
2527
}

event.go

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -148,8 +148,10 @@ func (e *Event) msg(msg string) {
148148
}
149149
}
150150

151-
// Fields is a helper function to use a map to set fields using type assertion.
152-
func (e *Event) Fields(fields map[string]interface{}) *Event {
151+
// Fields is a helper function to use a map or slice to set fields using type assertion.
152+
// Only map[string]interface{} and []interface{} are accepted. []interface{} must
153+
// alternate string keys and arbitrary values, and extraneous ones are ignored.
154+
func (e *Event) Fields(fields interface{}) *Event {
153155
if e == nil {
154156
return e
155157
}

fields.go

Lines changed: 29 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -12,15 +12,36 @@ func isNilValue(i interface{}) bool {
1212
return (*[2]uintptr)(unsafe.Pointer(&i))[1] == 0
1313
}
1414

15-
func appendFields(dst []byte, fields map[string]interface{}) []byte {
16-
keys := make([]string, 0, len(fields))
17-
for key := range fields {
18-
keys = append(keys, key)
15+
func appendFields(dst []byte, fields interface{}) []byte {
16+
switch fields := fields.(type) {
17+
case []interface{}:
18+
if n := len(fields); n&0x1 == 1 { // odd number
19+
fields = fields[:n-1]
20+
}
21+
dst = appendFieldList(dst, fields)
22+
case map[string]interface{}:
23+
keys := make([]string, 0, len(fields))
24+
for key := range fields {
25+
keys = append(keys, key)
26+
}
27+
sort.Strings(keys)
28+
kv := make([]interface{}, 2)
29+
for _, key := range keys {
30+
kv[0], kv[1] = key, fields[key]
31+
dst = appendFieldList(dst, kv)
32+
}
1933
}
20-
sort.Strings(keys)
21-
for _, key := range keys {
22-
dst = enc.AppendKey(dst, key)
23-
val := fields[key]
34+
return dst
35+
}
36+
37+
func appendFieldList(dst []byte, kvList []interface{}) []byte {
38+
for i, n := 0, len(kvList); i < n; i += 2 {
39+
key, val := kvList[i], kvList[i+1]
40+
if key, ok := key.(string); ok {
41+
dst = enc.AppendKey(dst, key)
42+
} else {
43+
continue
44+
}
2445
if val, ok := val.(LogObjectMarshaler); ok {
2546
e := newEvent(nil, 0)
2647
e.buf = e.buf[:0]

log_example_test.go

Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -335,6 +335,38 @@ func ExampleEvent_Durs() {
335335
// Output: {"foo":"bar","durs":[10000,20000],"message":"hello world"}
336336
}
337337

338+
func ExampleEvent_Fields_map() {
339+
fields := map[string]interface{}{
340+
"bar": "baz",
341+
"n": 1,
342+
}
343+
344+
log := zerolog.New(os.Stdout)
345+
346+
log.Log().
347+
Str("foo", "bar").
348+
Fields(fields).
349+
Msg("hello world")
350+
351+
// Output: {"foo":"bar","bar":"baz","n":1,"message":"hello world"}
352+
}
353+
354+
func ExampleEvent_Fields_slice() {
355+
fields := []interface{}{
356+
"bar", "baz",
357+
"n", 1,
358+
}
359+
360+
log := zerolog.New(os.Stdout)
361+
362+
log.Log().
363+
Str("foo", "bar").
364+
Fields(fields).
365+
Msg("hello world")
366+
367+
// Output: {"foo":"bar","bar":"baz","n":1,"message":"hello world"}
368+
}
369+
338370
func ExampleContext_Dict() {
339371
log := zerolog.New(os.Stdout).With().
340372
Str("foo", "bar").
@@ -484,3 +516,35 @@ func ExampleContext_MacAddr() {
484516

485517
// Output: {"hostMAC":"00:14:22:01:23:45","message":"hello world"}
486518
}
519+
520+
func ExampleContext_Fields_map() {
521+
fields := map[string]interface{}{
522+
"bar": "baz",
523+
"n": 1,
524+
}
525+
526+
log := zerolog.New(os.Stdout).With().
527+
Str("foo", "bar").
528+
Fields(fields).
529+
Logger()
530+
531+
log.Log().Msg("hello world")
532+
533+
// Output: {"foo":"bar","bar":"baz","n":1,"message":"hello world"}
534+
}
535+
536+
func ExampleContext_Fields_slice() {
537+
fields := []interface{}{
538+
"bar", "baz",
539+
"n", 1,
540+
}
541+
542+
log := zerolog.New(os.Stdout).With().
543+
Str("foo", "bar").
544+
Fields(fields).
545+
Logger()
546+
547+
log.Log().Msg("hello world")
548+
549+
// Output: {"foo":"bar","bar":"baz","n":1,"message":"hello world"}
550+
}

log_test.go

Lines changed: 62 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -246,6 +246,66 @@ func TestFieldsMapNilPnt(t *testing.T) {
246246
}
247247
}
248248

249+
func TestFieldsSlice(t *testing.T) {
250+
out := &bytes.Buffer{}
251+
log := New(out)
252+
log.Log().Fields([]interface{}{
253+
"nil", nil,
254+
"string", "foo",
255+
"bytes", []byte("bar"),
256+
"error", errors.New("some error"),
257+
"bool", true,
258+
"int", int(1),
259+
"int8", int8(2),
260+
"int16", int16(3),
261+
"int32", int32(4),
262+
"int64", int64(5),
263+
"uint", uint(6),
264+
"uint8", uint8(7),
265+
"uint16", uint16(8),
266+
"uint32", uint32(9),
267+
"uint64", uint64(10),
268+
"float32", float32(11),
269+
"float64", float64(12),
270+
"ipv6", net.IP{0x20, 0x01, 0x0d, 0xb8, 0x85, 0xa3, 0x00, 0x00, 0x00, 0x00, 0x8a, 0x2e, 0x03, 0x70, 0x73, 0x34},
271+
"dur", 1 * time.Second,
272+
"time", time.Time{},
273+
"obj", obj{"a", "b", 1},
274+
}).Msg("")
275+
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 {
276+
t.Errorf("invalid log output:\ngot: %v\nwant: %v", got, want)
277+
}
278+
}
279+
280+
func TestFieldsSliceExtraneous(t *testing.T) {
281+
out := &bytes.Buffer{}
282+
log := New(out)
283+
log.Log().Fields([]interface{}{
284+
"string", "foo",
285+
"error", errors.New("some error"),
286+
32, "valueForNonStringKey",
287+
"bool", true,
288+
"int", int(1),
289+
"keyWithoutValue",
290+
}).Msg("")
291+
if got, want := decodeIfBinaryToString(out.Bytes()), `{"string":"foo","error":"some error","bool":true,"int":1}`+"\n"; got != want {
292+
t.Errorf("invalid log output:\ngot: %v\nwant: %v", got, want)
293+
}
294+
}
295+
296+
func TestFieldsNotMapSlice(t *testing.T) {
297+
out := &bytes.Buffer{}
298+
log := New(out)
299+
log.Log().
300+
Fields(obj{"a", "b", 1}).
301+
Fields("string").
302+
Fields(1).
303+
Msg("")
304+
if got, want := decodeIfBinaryToString(out.Bytes()), `{}`+"\n"; got != want {
305+
t.Errorf("invalid log output:\ngot: %v\nwant: %v", got, want)
306+
}
307+
}
308+
249309
func TestFields(t *testing.T) {
250310
out := &bytes.Buffer{}
251311
log := New(out)
@@ -336,7 +396,7 @@ func TestFieldsArraySingleElement(t *testing.T) {
336396
Floats32("float32", []float32{11}).
337397
Floats64("float64", []float64{12}).
338398
Durs("dur", []time.Duration{1 * time.Second}).
339-
Times("time", []time.Time{time.Time{}}).
399+
Times("time", []time.Time{{}}).
340400
Msg("")
341401
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 {
342402
t.Errorf("invalid log output:\ngot: %v\nwant: %v", got, want)
@@ -363,7 +423,7 @@ func TestFieldsArrayMultipleElement(t *testing.T) {
363423
Floats32("float32", []float32{11, 0}).
364424
Floats64("float64", []float64{12, 0}).
365425
Durs("dur", []time.Duration{1 * time.Second, 0}).
366-
Times("time", []time.Time{time.Time{}, time.Time{}}).
426+
Times("time", []time.Time{{}, {}}).
367427
Msg("")
368428
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 {
369429
t.Errorf("invalid log output:\ngot: %v\nwant: %v", got, want)

0 commit comments

Comments
 (0)