Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Expose DictObject #1458

Closed
MayCXC opened this issue Aug 14, 2024 · 8 comments
Closed

Expose DictObject #1458

MayCXC opened this issue Aug 14, 2024 · 8 comments

Comments

@MayCXC
Copy link
Contributor

MayCXC commented Aug 14, 2024

I would like to expose the ObjectMarshaler here: https://github.com/uber-go/zap/blob/v1.27.0/field.go#L425 like this:

func DictObject(val ...Field) zapcore.ObjectMarshaler {
    return dictObject(val)
}

so that field functions can be used to construct arbitrary object marshalers, to use with other functions like Objects:

zap.Objects("tuple", []zapcore.ObjectMarshaler{zap.DictObject(zap.String("foo", "bar"), zap.Int("F00", 3840)), zap.DictObject(zap.String("more", "fields"))})

and this would be especially nice if the signature of Objects were changed to use a vararg:

func Objects[T zapcore.ObjectMarshaler](key string, values ...T) Field
@abhinav
Copy link
Collaborator

abhinav commented Aug 16, 2024

and this would be especially nice if the signature of Objects were changed to use a vararg:

func Objects[T zapcore.ObjectMarshaler](key string, values ...T) Field

That would be a breaking change. We can't do that.

Also, note that even if we did, Objects wouldn't necessarily behave the way we want it to.
With the T type parameter, it's expected that all values are the same type.
It would be invalid to do this:

Objects("foo",
  DictObject(...),
  &MyObject{...},   // given func (*MyObject) MarshalLogObject(...)
)

This functionality is more conveniently possible with a separate function that takes a non-type-parameterized version:

func(name string, objs ...zapcore.ObjectMarshaler) zap.Field

I can't think of a good name for that function that stands apart from the other such helpers.

@MayCXC
Copy link
Contributor Author

MayCXC commented Aug 17, 2024

Also, note that even if we did, Objects wouldn't necessarily behave the way we want it to. With the T type parameter, it's expected that all values are the same type. It would be invalid to do this:

Objects("foo",
  DictObject(...),
  &MyObject{...},   // given func (*MyObject) MarshalLogObject(...)
)

This functionality is more conveniently possible with a separate function that takes a non-type-parameterized version:

func(name string, objs ...zapcore.ObjectMarshaler) zap.Field

I can't think of a good name for that function that stands apart from the other such helpers.

oh, will Objects[zapcore.ObjectMarshaler]("foo", DictObject(...), &MyObject{...}, ...) not work for that? func VariadicObjects(name string, objs ...zapcore.ObjectMarshaler) is my suggestion in this case.

@abhinav
Copy link
Collaborator

abhinav commented Aug 17, 2024

oh, will Objects[zapcore.ObjectMarshaler]("foo", DictObject(...), &MyObject{...}, ...) not work for that?

That would, but (I think) that you'd have to always specify the type constraint for that case. It would not be inferred.

edit: finishing up that thought:
For the vararg version, the type parameter doesn't add anything over func(string, ...zapcore.ObjectMarshaler), and in fact takes away the ability to pass multiple objects of different types in without adding a type parameter.
We needed the type parameter for Objects[T any](string, []T) because that affects the type of the slice—[]Foo cannot be passed in for []zapcore.ObjectMarshaler even if Foo implements the interface.

@abhinav
Copy link
Collaborator

abhinav commented Aug 17, 2024

I think "VariadicObjects" doesn't match the naming conventions we've been going for.
Maybe ObjectList or similar would be better if we end up adding this.
I'll wait for other maintainers to chime in on this, but we can keep it separate from DictObject, which by itself is a fine addition IMO.

@MayCXC
Copy link
Contributor Author

MayCXC commented Aug 17, 2024

Cool, agreed on both points. I'm wondering if Objects actually needs the generic parameter now, to me it seems like a slice of ObjectMarshalers works in all cases. Either way, I can just put

func Objectsooo(name string, objs ...zapcore.ObjectMarshaler) zap.Field {
  return zap.Objects[zapcore.ObjectMarshaler](string, objs)
}

in my own utility library or something, it doesn't need access to internals like DictObject.

@abhinav
Copy link
Collaborator

abhinav commented Aug 17, 2024

I'm wondering if Objects actually needs the generic parameter now, to me it seems like a slice of ObjectMarshalers works in all cases

The reason Objects needs the type parameter is this case:

type Item struct{ ... }
func (Item) MarshalLogObject(...) { ... }
items := []Item{a, b, c}
zap.Objects("foo", items)
// This:
//  func Objects[T zapcore.ObjectMarshaler](name string, items []T) Field
// Becomes:
//  func Objects(name string, items []Item) Field

It allows the items slice to be passed in as is.
If we did not have this, and the Objects function was:

func Objects(name string, items []zapcore.ObjectMarshaler) Field

Then we would not be able to pass items in as is, and would need to do this:

logItems := make([]zapcore.ObjectMarshaler, len(items))
for i, item := range items { logItems[i] = item }
zap.Objects("foo", logItems)

@MayCXC
Copy link
Contributor Author

MayCXC commented Aug 17, 2024

ah, I think I see, passing variadic arguments individually just sugars that last slice copy. thanks for taking me into and back out of the generic / slice invariance rabbithole :)

sywhang pushed a commit that referenced this issue Aug 18, 2024
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>
@MayCXC
Copy link
Contributor Author

MayCXC commented Aug 18, 2024

DictObject was merged 👍

@MayCXC MayCXC closed this as completed Aug 18, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Development

No branches or pull requests

2 participants