From c0a635eb85d02cb7e4f0b2da8389de83837e4486 Mon Sep 17 00:00:00 2001 From: Aaron Paterson <9441877+MayCXC@users.noreply.github.com> Date: Thu, 15 Aug 2024 11:42:01 -0400 Subject: [PATCH 1/5] add `func DictObject` --- field.go | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/field.go b/field.go index 6743930b8..38293cfd1 100644 --- a/field.go +++ b/field.go @@ -431,6 +431,10 @@ func (d dictObject) MarshalLogObject(enc zapcore.ObjectEncoder) error { return nil } +func DictObject(val ...Field) zapcore.ObjectMarshaler { + return dictObject(val) +} + // We discovered an issue where zap.Any can cause a performance degradation // when used in new goroutines. // From eee380e0c9eddb0a8139b9656ebe2f8b3fb3adae Mon Sep 17 00:00:00 2001 From: "maycxc.github.io" <9441877+MayCXC@users.noreply.github.com> Date: Sat, 17 Aug 2024 05:07:59 -0400 Subject: [PATCH 2/5] add DictObject tests --- example_test.go | 54 +++++++++++++++++++++++++++++++++++++++++++ field.go | 2 ++ zapcore/field_test.go | 10 ++++++++ 3 files changed, 66 insertions(+) diff --git a/example_test.go b/example_test.go index af7df0e25..bb5310717 100644 --- a/example_test.go +++ b/example_test.go @@ -388,6 +388,60 @@ func ExampleObjects() { // {"level":"debug","msg":"opening connections","addrs":[{"ip":"123.45.67.89","port":4040},{"ip":"127.0.0.1","port":4041},{"ip":"192.168.0.1","port":4042}]} } +func ExampleDictObject() { + logger := zap.NewExample() + defer logger.Sync() + + // Use DictObject to create zapcore.ObjectMarshaler implementations from Field arrays, + // then use the Object and Objects field constructors to turn them back into a Field. + + logger.Debug("worker received job", + zap.Object("w1", + zap.DictObject( + zap.Int("id", 402000), + zap.String("description", "compress image data"), + zap.Int("priority", 3), + ), + )) + + d1, err := time.ParseDuration("68ms") + if(err != nil) { + panic(err) + } + d2, err := time.ParseDuration("79ms") + if(err != nil) { + panic(err) + } + d3, err := time.ParseDuration("57ms") + if(err != nil) { + panic(err) + } + + logger.Info("worker status checks", + zap.Objects("job batch enqueued", + []zapcore.ObjectMarshaler{ + zap.DictObject( + zap.String("worker", "w1"), + zap.Int("load", 419), + zap.Duration("latency", d1), + ), + zap.DictObject( + zap.String("worker", "w2"), + zap.Int("load", 520), + zap.Duration("latency", d2), + ), + zap.DictObject( + zap.String("worker", "w3"), + zap.Int("load", 310), + zap.Duration("latency", d3), + ), + }, + )) + // Output: + // {"level":"debug","msg":"worker received job","w1":{"id":402000,"description":"compress image data","priority":3}} + // {"level":"info","msg":"worker status checks","job batch enqueued":[{"worker":"w1","load":419,"latency":"68ms"},{"worker":"w2","load":520,"latency":"79ms"},{"worker":"w3","load":310,"latency":"57ms"}]} +} + func ExampleObjectValues() { logger := zap.NewExample() defer logger.Sync() diff --git a/field.go b/field.go index 38293cfd1..cd3d67969 100644 --- a/field.go +++ b/field.go @@ -431,6 +431,8 @@ func (d dictObject) MarshalLogObject(enc zapcore.ObjectEncoder) error { return nil } +// DictObject exposes the [zapcore.ObjectMarshaler] for an array of [Field] to +// use with functions like [Object] and [Objects]. func DictObject(val ...Field) zapcore.ObjectMarshaler { return dictObject(val) } diff --git a/zapcore/field_test.go b/zapcore/field_test.go index 06bcef2e1..2b6d8c369 100644 --- a/zapcore/field_test.go +++ b/zapcore/field_test.go @@ -320,6 +320,16 @@ func TestEquals(t *testing.T) { b: zap.Dict("k", zap.String("a", "d")), want: false, }, + { + a: zap.Object("k", zap.DictObject(zap.String("a", "b"))), + b: zap.Object("k", zap.DictObject(zap.String("a", "b"))), + want: true, + }, + { + a: zap.Object("k", zap.DictObject(zap.String("a", "b"))), + b: zap.Object("k", zap.DictObject(zap.String("a", "d"))), + want: false, + }, } for _, tt := range tests { From 787219f898e1e394fcb915c6681116649d4d73ac Mon Sep 17 00:00:00 2001 From: Aaron Paterson <9441877+MayCXC@users.noreply.github.com> Date: Sat, 17 Aug 2024 15:24:24 -0400 Subject: [PATCH 3/5] ParseDuration -> * duration Co-authored-by: Abhinav Gupta --- example_test.go | 15 +++------------ 1 file changed, 3 insertions(+), 12 deletions(-) diff --git a/example_test.go b/example_test.go index bb5310717..c479a0aed 100644 --- a/example_test.go +++ b/example_test.go @@ -404,18 +404,9 @@ func ExampleDictObject() { ), )) - d1, err := time.ParseDuration("68ms") - if(err != nil) { - panic(err) - } - d2, err := time.ParseDuration("79ms") - if(err != nil) { - panic(err) - } - d3, err := time.ParseDuration("57ms") - if(err != nil) { - panic(err) - } + d1 := 68 * time.Millisecond + d2 := 79 * time.Millisecond + d3 := 57 * time.Millisecond logger.Info("worker status checks", zap.Objects("job batch enqueued", From f17a061b15c6415815231b6b21411f2fed793a5a Mon Sep 17 00:00:00 2001 From: Aaron Paterson <9441877+MayCXC@users.noreply.github.com> Date: Sat, 17 Aug 2024 15:34:41 -0400 Subject: [PATCH 4/5] reword DictObject doc comment --- field.go | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/field.go b/field.go index cd3d67969..8441d1afb 100644 --- a/field.go +++ b/field.go @@ -431,8 +431,9 @@ func (d dictObject) MarshalLogObject(enc zapcore.ObjectEncoder) error { return nil } -// DictObject exposes the [zapcore.ObjectMarshaler] for an array of [Field] to -// use with functions like [Object] and [Objects]. +// DictObject constructs a [zapcore.ObjectMarshaler] with the given list of fields. +// The resulting object marshaler can be used as input to [Object], [Objects], or +// any other functions that expect an object marshaler. func DictObject(val ...Field) zapcore.ObjectMarshaler { return dictObject(val) } From 0747df3f141236241f470b878f779360c4d5d8c3 Mon Sep 17 00:00:00 2001 From: "maycxc.github.io" <9441877+MayCXC@users.noreply.github.com> Date: Sat, 17 Aug 2024 16:17:14 -0400 Subject: [PATCH 5/5] add TestDictObject --- field_test.go | 42 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 42 insertions(+) diff --git a/field_test.go b/field_test.go index f87f1592e..ee3d5fe5b 100644 --- a/field_test.go +++ b/field_test.go @@ -314,3 +314,45 @@ func TestDict(t *testing.T) { }) } } + +func TestDictObject(t *testing.T) { + tests := []struct { + desc string + field Field + expected any + }{ + { + "empty", + Object("", DictObject()), + map[string]any{}, + }, + { + "object", + Object("", DictObject(String("k", "v"))), + map[string]any{"k": "v"}, + }, + { + "objects", + Objects("", []zapcore.ObjectMarshaler{ + DictObject(String("k", "v")), + DictObject(String("k2", "v2")), + }), + []any{ + map[string]any{"k": "v"}, + map[string]any{"k2": "v2"}, + }, + }, + } + + for _, tt := range tests { + t.Run(tt.desc, func(t *testing.T) { + enc := zapcore.NewMapObjectEncoder() + tt.field.Key = "k" + tt.field.AddTo(enc) + assert.Equal(t, tt.expected, enc.Fields["k"], "unexpected map contents") + assert.Len(t, enc.Fields, 1, "found extra keys in map: %v", enc.Fields) + + assertCanBeReused(t, tt.field) + }) + } +}