Skip to content

Commit

Permalink
Add func DictObject (#1460)
Browse files Browse the repository at this point in the history
Allows fields to be used to construct `ObjectMarshaler`s that can then
be used with other functions like `func Objects`. Link to issue:
#1458

---------

Co-authored-by: Abhinav Gupta <mail@abhinavg.net>
  • Loading branch information
MayCXC and abhinav authored Aug 18, 2024
1 parent 3f27eb9 commit 5786471
Show file tree
Hide file tree
Showing 4 changed files with 104 additions and 0 deletions.
45 changes: 45 additions & 0 deletions example_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -388,6 +388,51 @@ 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 := 68 * time.Millisecond
d2 := 79 * time.Millisecond
d3 := 57 * time.Millisecond

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()
Expand Down
7 changes: 7 additions & 0 deletions field.go
Original file line number Diff line number Diff line change
Expand Up @@ -431,6 +431,13 @@ func (d dictObject) MarshalLogObject(enc zapcore.ObjectEncoder) error {
return nil
}

// 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)
}

// We discovered an issue where zap.Any can cause a performance degradation
// when used in new goroutines.
//
Expand Down
42 changes: 42 additions & 0 deletions field_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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)
})
}
}
10 changes: 10 additions & 0 deletions zapcore/field_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down

0 comments on commit 5786471

Please sign in to comment.