-
Notifications
You must be signed in to change notification settings - Fork 1.4k
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
slog: Support inline group fields per spec #1263
Conversation
converted.Return() | ||
} | ||
return nil | ||
} | ||
|
||
func convertAttrToField(attr slog.Attr) zapcore.Field { | ||
func convertAttrToFields(attr slog.Attr) *fieldSlice { | ||
fields := _fieldSlices.Get() |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think this will be less prone to use-after-free bugs if "get from pool" and "defer return to pool" are next to each other.
Consider turning convertAttrToFields
into addAttrAsFields(enc, attr)
, moving the AddTo inside the function, which will allow a defer fields.Return()
right after the Get.
If that's unavoidable, we can Get from pool up above in MarshalLogObject outside the for loop,
pass into convertAttrToFields(attr, &converted)
, and then reset and re-use between each iteration:
converted := _fieldSlices.Get()
defer converted.Return()
for _, attr := range gs {
converted.Reset()
convertAttrToFields(attr, &converted)
converted.AddTo(enc)
}
for i := 0; i < s.Len(); i++ { | ||
(*s)[i].AddTo(enc) | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Any reason not to use range
here?
for i := 0; i < s.Len(); i++ { | |
(*s)[i].AddTo(enc) | |
} | |
for _, f := range (*s) { | |
f.AddTo(enc) | |
} |
converted := convertAttrToFields(attr) | ||
fields.Extend(converted) | ||
converted.Return() |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think if you take my suggestion to make convert
take a *fieldSlice
,
this won't need to borrow a new slice at all.
converted := convertAttrToFields(attr) | |
fields.Extend(converted) | |
converted.Return() | |
convertAttrToFields(attr, &fields) |
converted := convertAttrToFields(attr) | ||
fields.Extend(converted) | ||
converted.Return() |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Same
converted := convertAttrToFields(attr) | |
fields.Extend(converted) | |
converted.Return() | |
convertAttrToFields(attr, &fields) |
tmp := [...]zapcore.Field{ | ||
zap.Namespace(group), | ||
} | ||
|
||
cloned := *h | ||
cloned.core = h.core.With(tmp[:]) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm 75% certain that the manual allocation won't have any effect.
It will behave the same as h.core.With(zap.Namespace(group))
:
if the slice escapes, it'll be heap allocated anyway, and if it doesn't escape, it was going to be stack allocated anyway.
} | ||
|
||
// withFields returns a cloned Handler with the given fields. | ||
func (h *Handler) withFields(fields ...zapcore.Field) *Handler { | ||
func (h *Handler) withFields(fields *fieldSlice) *Handler { | ||
defer fields.Return() |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is super sus.
As a reader, it's super not clear why/how this is being freed here.
_fieldSlices.Put(s) | ||
} | ||
|
||
var _fieldSlices = pool.New(func() *fieldSlice { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Consider adding a clarifying comment that the pooled value MUST be a pointer type to avoid allocations.
Thanks for the pass, just a rough draft atm (in case it wasn't obvious). Agree with your comments, will rework some of this soon. |
…oup. (#1408) This change adds a test based on testing/slogtest that verifies compliance with the slog handler contract (a draft of this was available in #1335), and fixes all resulting issues. The two remaining issues were: - `Group("", attrs)` should inline the new fields instead of creating a group with an empty name. This was fixed with the use of `zap.Inline`. - Groups without any attributes should not be created. That is, `logger.WithGroup("foo").Info("bar")` should not create an empty "foo" namespace (`"foo": {}`). This was fixed by keeping track of unapplied groups and applying them the first time a field is serialized. Following this change, slogtest passes as expected. Refs #1333 Resolves #1334, #1401, #1402 Supersedes #1263, #1335 ### TESTS - passed. arukiidou#1 - This also works in Go 1.22 --------- Signed-off-by: junya koyama <[email protected]> Co-authored-by: Abhinav Gupta <[email protected]>
Superseded by #1408 |
[]zap.Field
storage.Before:
After: