Skip to content

Added variadic StrsV, ObjectsV, and StringersV#752

Merged
rs merged 3 commits intors:masterfrom
IDisposable:feature/variadic-stringers
Mar 27, 2026
Merged

Added variadic StrsV, ObjectsV, and StringersV#752
rs merged 3 commits intors:masterfrom
IDisposable:feature/variadic-stringers

Conversation

@IDisposable
Copy link
Copy Markdown
Contributor

This allows you to just list all the string, object, or stringer that you want added to a Event or Context.

The variadic versions are suffixed with V so as to not be a breaking change.

If you have an existing array of objects that implement the LogObjectMarshaler or fmt.Stringer interfaces, unfortunately go doesn't consider those slices as identical types, so you have to allocate an array and copy the entries, there are generic helper methods in global_118.go zerolog.AsLogObjectMarshalers and zerolog.AsStringers if you're using go 1.18 or later.

Somewhat addresses #551

This allows you to just list all the string, object, or stringer that you want added to a Event or Context.

The variadic versions are suffixed with V so as to not be a breaking change.

If you have an existing array of objects that implement the LogObjectMarshaler or fmt.Stringer interfaces, unfortunately go doesn't consider those slices as identical types, so you have to allocate an array and copy the entries, there are generic helper methods in global_118.go zerolog.AsLogObjectMarshalers and zerolog.AsStringers if you're using go 1.18 or later.

Somewhat addresses rs#551
Copilot AI review requested due to automatic review settings January 29, 2026 01:15
Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Adds variadic convenience helpers for adding arrays of strings, stringers, and log objects to Event and Context without breaking existing slice-based APIs, plus Go 1.18+ helpers to convert typed slices into the required interface slices.

Changes:

  • Added ObjectsV, StrsV, and StringersV to Event and Context as variadic wrappers over existing plural methods.
  • Added Go 1.18+ generic helpers AsLogObjectMarshalers and AsStringers to ease converting []T into []LogObjectMarshaler / []fmt.Stringer.
  • Updated unit tests, examples, benchmarks, and CI (added -race to CBOR test run).

Reviewed changes

Copilot reviewed 8 out of 8 changed files in this pull request and generated 7 comments.

Show a summary per file
File Description
event.go Adds variadic *Event APIs and updates doc comments around plural helpers.
context.go Adds variadic Context APIs mirroring Event behavior.
globals_118.go Introduces Go 1.18+ generic slice conversion helpers for Objects/Stringers.
log_test.go Expands plural-field tests to cover new variadic helpers and object arrays.
log_example_test.go Adds examples demonstrating Objects(…), ObjectsV(…), and Stringers(…)/StringersV(…) usage.
event_test.go Adds nil-event coverage for the new variadic methods.
benchmark_test.go Extends benchmarks to include the new variadic methods.
.github/workflows/test.yml Runs CBOR-tagged tests with -race.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread event.go Outdated
Comment thread log_example_test.go Outdated
Comment thread log_test.go Outdated
Comment thread globals_118.go
Comment thread globals_118.go
Comment thread context.go
Comment thread event.go Outdated
@IDisposable
Copy link
Copy Markdown
Contributor Author

IDisposable commented Jan 29, 2026

We could have a generic free function (non member method) doing something like this (conditionally compiled for go 1.18 or later) but it would have to duplicate the code of the existing methods*

//go:build go1.18
// +build go1.18

package zerolog

import "fmt"

// ObjectsV adds the field key with objs as an array of objects that
// implement the LogObjectMarshaler interface to the event.
//
// This is a generic free function version that accepts a slice of
// LogObjectMarshaler-derived values.
func ObjectsE[T LogObjectMarshaler](e *Event, key string, objs []T) *Event {
	if e == nil {
		return e
	}
	e.buf = enc.AppendArrayStart(enc.AppendKey(e.buf, key))
	for i, obj := range objs {
		e.buf = appendObject(e.buf, obj, e.stack, e.ctx, e.ch)
		if i < (len(objs) - 1) {
			e.buf = enc.AppendArrayDelim(e.buf)
		}
	}
	e.buf = enc.AppendArrayEnd(e.buf)
	return e
}

Unfortunately, that requires we pass the *Event explicitly (not as a "this pointer") so the syntax in use looks hideous

func ExampleObjects() {
	// User implements zerolog.LogObjectMarshaler
	u := User{"John", 35, time.Time{}}
	u2 := User{"Bono", 54, time.Time{}}
	users := []User{u, u2}

	logger := zerolog.New(os.Stdout)
	event := logger.Log()

	zerolog.ObjectsE(event, "users", users).Msg("hello world")
	// Output: {"users":[{"name":"John","age":35,"created":"0001-01-01T00:00:00Z"},{"name":"Bono","age":54,"created":"0001-01-01T00:00:00Z"}],"message":"hello world"}
}

For Stringers it's even uglier because we have to inline the code for enc.AppendStringers() so nope...

// StringersV adds the field key with vals to the *Event context.
// If any val is not nil, it is added by calling val.String().
// If val is nil, it is encoded as null without calling String().
//
// This is a generic free function version that accepts a listslice of
// fmt.Stringer values.
func StringersV(e *Event, key string, vals []fmt.Stringer) *Event {
	if e == nil {
		return e
	}
	e.buf = // insert loop from enc.AppendStringers ... ugh
	// enc.AppendStringers(enc.AppendKey(e.buf, key), vals)
	return e
}
  • The free-function versions could be the only place the emitting code exists, since they could be called from the *Event and Context methods

@IDisposable
Copy link
Copy Markdown
Contributor Author

@rs I'm sure you're really busy... is there anything I can do to help?

@rs
Copy link
Copy Markdown
Owner

rs commented Feb 21, 2026

We could just not expose those methods to older versions of go with no fallback implementation.

@IDisposable
Copy link
Copy Markdown
Contributor Author

Is there a minimum version of go you're targeting?

@rs
Copy link
Copy Markdown
Owner

rs commented Feb 21, 2026

Not really. We may set it to 1.18, seems reasonable

Copy link
Copy Markdown
Contributor Author

@IDisposable IDisposable left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this is ready to merge @rs

Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 10 out of 10 changed files in this pull request and generated 5 comments.


💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread log_example_test.go
ObjectsV("users", u, u2).
Msg("hello world")

// Output: {"users":[{"name":"John","age":35,"created":"0001-01-01T00:00:00Z"},{"name":"Bono","age":54,"created":"0001-01-01T00:00:00Z"}],"message":"hello world"}
Copy link

Copilot AI Mar 1, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The ExampleEvent_ObjectsV expected output has an extra leading space after // Output: (// Output: { ... }). Go’s example output matcher treats that space as part of the expected output, so this example will fail. Remove the extra space so the expected output starts with {.

Suggested change
// Output: {"users":[{"name":"John","age":35,"created":"0001-01-01T00:00:00Z"},{"name":"Bono","age":54,"created":"0001-01-01T00:00:00Z"}],"message":"hello world"}
// Output: {"users":[{"name":"John","age":35,"created":"0001-01-01T00:00:00Z"},{"name":"Bono","age":54,"created":"0001-01-01T00:00:00Z"}],"message":"hello world"}

Copilot uses AI. Check for mistakes.
Comment thread log_example_test.go
Comment on lines +663 to +669
// net.IP values implement fmt.Stringer and can be used with StringersV
a := net.IP{127, 0, 0, 1}
b := net.IP{127, 0, 0, 2}
ips := []net.IP{a, b}

log.Log().
Stringers("ips", zerolog.AsStringers(ips)).
Copy link

Copilot AI Mar 1, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This comment says net.IP values “can be used with StringersV”, but the example demonstrates Stringers + AsStringers(...). Please align the comment with what’s being demonstrated (or mention both APIs) to avoid confusing readers.

Copilot uses AI. Check for mistakes.
Comment thread globals_118.go
Comment on lines +10 to +29
func AsLogObjectMarshalers[T LogObjectMarshaler](objs []T) []LogObjectMarshaler {
if objs == nil {
return nil
}
s := make([]LogObjectMarshaler, len(objs))
for i, v := range objs {
s[i] = v
}
return s
}

func AsStringers[T fmt.Stringer](objs []T) []fmt.Stringer {
if objs == nil {
return nil
}
s := make([]fmt.Stringer, len(objs))
for i, v := range objs {
s[i] = v
}
return s
Copy link

Copilot AI Mar 1, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

AsLogObjectMarshalers and AsStringers are exported, but they don’t have GoDoc comments. If this repo runs any linting (or for pkg.go.dev quality), add doc comments starting with the function name and describing the allocation/copy behavior and intended use case.

Copilot uses AI. Check for mistakes.
Comment thread context.go
Comment thread context.go
return c
}

// StringersV adds the field key with vals to the logger context where each
Copy link

Copilot AI Mar 1, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There’s an extra double-space in the doc comment ("vals to"). Consider tightening it to a single space for readability.

Suggested change
// StringersV adds the field key with vals to the logger context where each
// StringersV adds the field key with vals to the logger context where each

Copilot uses AI. Check for mistakes.
@rs rs merged commit e133b6a into rs:master Mar 27, 2026
9 checks passed
@IDisposable IDisposable deleted the feature/variadic-stringers branch March 27, 2026 18:53
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants