-
Notifications
You must be signed in to change notification settings - Fork 53
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
Support fast Zap logging #361
Conversation
Sets and maps have nondeterministic behavior in iteration, but Zap logs/JSON has arrays that are orderly, causing issues in testing. Instead, we only write one element for those that are affected and test the loop logic separately in TestNondeterministicZapLogging.
This change requires uber-go/zap#614. |
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.
Sweet! Things look good on the first pass. Some minor comments.
CHANGELOG.md
Outdated
@@ -5,7 +5,9 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/) | |||
and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html). | |||
|
|||
## [Unreleased] | |||
- No changes yet. | |||
- All structs and its underlying components, enums, and typedefs of non-primitives | |||
now implement `zapcore.ObjectMarshaler` or `zapcore.ArrayMarshaler`, depending |
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.
Discrepancy between the description and the changelog with respect to typedef support.
gen/enum.go
Outdated
@@ -125,6 +125,23 @@ func enum(g Generator, spec *compile.EnumSpec) error { | |||
return []byte(<$strconv>.FormatInt(int64(<$v>), 10)), nil | |||
} | |||
|
|||
<$zapcore := import "go.uber.org/zap/zapcore"> | |||
|
|||
// MarshalLogObject implements zapcore.ObjectMarshaler, allowing |
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.
nit: What about enabling
or implementing
instead of allowing
?
glide.yaml
Outdated
@@ -12,6 +12,8 @@ import: | |||
version: ^1 | |||
- package: github.com/fatih/structtag | |||
version: ^0.1.0 | |||
- package: go.uber.org/zap | |||
version: master |
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.
Let's pin to the latest stable release here (i.e. ^1.9.0
): https://github.com/uber-go/zap/releases
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.
We had to do a bugfix in zap (see uber-go/zap#614), and without it my tests would fail. I'll either have to pin to master or wait for the next release.
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.
Ah got it. We should see if we can cut a release before merging this in.
gen/field.go
Outdated
if <$fval> != nil { | ||
<- if (zapCanError .Type)>if err := <end -> | ||
<$enc>.Add<zapEncoder .Type>("<.Name>", <zapMarshalerPtr .Type $fval>) | ||
<- if (zapCanError .Type)>; err != nil { |
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 makes sense, but we need to check the same conditional twice consecutively. For readability, consider:
<- if (zapCanError .Type) ->
if err := <$enc>.Add<zapEncoder .Type>("<.Name>", <zapMarshaler .Type $fval>); err != nil {
return err
}
<- else ->
<$enc>.Add<zapEncoder .Type>("<.Name>", <zapMarshaler .Type $fval>)
<- end ->
It's a little more verbose, but might be more clear.
We could potentially inject a template function that would handle this for us, such as <withZapErrCheck .Type>
That would output a template which would wrap the result of the first argument in a if err := ...; err != nil { ... }
based on the result of (zapCanError .Type)
, but this might be overkill / less readable.
TL;DR: This is fine as is - feel free to disregard these suggestions.
// for consistency. | ||
base64 := g.Import("encoding/base64") | ||
return fmt.Sprintf("%v.StdEncoding.EncodeToString(%v)", base64, fieldValue), nil | ||
case *compile.MapSpec: |
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.
We can collapse these cases:
...
case *compile.MapSpec, *compile.SetSpec, *compile.ListSpec:
return z.listG.zapMarshaler(g, spec, t, fieldValue)
...
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.
they're slightly different: mapSpec --> z.mapG, setSpec --> z.setG, listSpec --> z.listG
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.
doh!
gen/zap.go
Outdated
return z.listG.zapMarshaler(g, spec, t, fieldValue) | ||
case *compile.StructSpec: | ||
return fieldValue, nil | ||
default: |
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.
We can remove the empty default
cases and let it fall-through to the panic
.
gen/zap.go
Outdated
*compile.I64Spec, *compile.DoubleSpec, *compile.StringSpec, *compile.BinarySpec: | ||
return false | ||
|
||
// Else |
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.
For consistency, how about Non-primitives
?
@@ -418,6 +480,13 @@ func (lhs EdgeMap) Equals(rhs EdgeMap) bool { | |||
return _Map_Edge_Edge_Equals(lhs, rhs) | |||
} | |||
|
|||
func (v EdgeMap) MarshalLogArray(enc zapcore.ArrayEncoder) error { | |||
return ((_EdgeMap_Zapper)(([]struct { |
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.
O.O
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 talked to Abhinav about this; he said we could probably clean that up, but if a user is creating ugly thrift structs, they deserve ugly code :P
Thanks for the update! I like that it's more explicit how err is handled in all of these cases. But now, we see a lot of repetition in the method calls. What if we took this one step further and did:
What are your thoughts? |
@amckinney I agree that looks cleaner, but I don't think For easier comparison:
|
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.
Awesome, this looks really great!
Please hold off on merging until we release |
(This supersedes #360.)
This adds a
zapcore.MarshalLogObject/Array
method to all generated structs and its underlying components, enums, and non-primitive typedefs, implementing Zap marshalers (i.e. ObjectMarshaler, ArrayMarshaler). For typedefs of primitives, the logging is up to the user, simply by casting it down to the root type and using the respectiveAdd/Append...
method of the Zap encoder.