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

Support fast Zap logging #361

Merged
merged 25 commits into from
Aug 6, 2018
Merged

Support fast Zap logging #361

merged 25 commits into from
Aug 6, 2018

Conversation

mh-park
Copy link
Contributor

@mh-park mh-park commented Jul 27, 2018

(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 respective Add/Append... method of the Zap encoder.

@mh-park mh-park requested a review from abhinav July 27, 2018 18:55
mh-park added 6 commits July 27, 2018 16:11
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.
@mh-park
Copy link
Contributor Author

mh-park commented Jul 31, 2018

This change requires uber-go/zap#614.

@mh-park mh-park changed the title WIP: Zap logging support Support fast Zap logging Aug 2, 2018
@mh-park mh-park requested a review from amckinney August 2, 2018 19:49
Copy link
Contributor

@amckinney amckinney left a 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
Copy link
Contributor

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
Copy link
Contributor

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
Copy link
Contributor

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

Copy link
Contributor Author

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.

Copy link
Contributor

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 {
Copy link
Contributor

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:
Copy link
Contributor

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)
...

Copy link
Contributor Author

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

Copy link
Contributor

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:
Copy link
Contributor

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
Copy link
Contributor

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 {
Copy link
Contributor

Choose a reason for hiding this comment

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

O.O

Copy link
Contributor Author

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

@amckinney
Copy link
Contributor

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:

<$encAdd := <$enc>.Add<zapEncoder .Type>("<.Name>", <zapMarshalerPtr .Type $fval>)>
...
<- if (zapCanError .Type) ->
  if err := <$encAdd>; err != nil {
    return err
  }
<- else ->
  <$encAdd>
<- end ->
...

What are your thoughts?

@mh-park
Copy link
Contributor Author

mh-park commented Aug 3, 2018

@amckinney I agree that looks cleaner, but I don't think <$enc>.Add<zapEncoder .Type>("<.Name>", <zapMarshalerPtr .Type $fval>) will evaluate correctly if we put it inside another <>. If we want it to evaluate the way we want it, we'll need to change the formatting a little bit: <$encAdd := printf "%s.Add%s(%q,%s)" $enc (zapEncoder .Type) .Name (zapMarshaler .Type $fval)>. Not sure which is more readable, let me know what you think.

For easier comparison:

<$enc>.Add<zapEncoder .Type>("<.Name>", <zapMarshalerPtr .Type $fval>)

<$encAdd := printf "%s.Add%s(%q,%s)" $enc (zapEncoder .Type) .Name (zapMarshaler .Type $fval)>

Copy link
Contributor

@amckinney amckinney left a 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!

@amckinney
Copy link
Contributor

Please hold off on merging until we release go.uber.org/zap and add the release pin there.

@mh-park mh-park merged commit 19af15f into dev Aug 6, 2018
@mh-park mh-park deleted the minho/zap2 branch August 6, 2018 20:31
@abhinav abhinav restored the minho/zap2 branch August 6, 2018 20:35
mh-park added a commit that referenced this pull request Aug 6, 2018
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.

2 participants