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

feat: Custom interface{} object Encoder #1034

Closed
psrajat opened this issue Dec 10, 2021 · 4 comments · Fixed by #1039
Closed

feat: Custom interface{} object Encoder #1034

psrajat opened this issue Dec 10, 2021 · 4 comments · Fixed by #1039

Comments

@psrajat
Copy link
Contributor

psrajat commented Dec 10, 2021

Description

We log a lot of structs using zap. And zap uses json.Encoder for encoding these interface{} (or ReflectType) fields.
It would be great if we can use something else other than encoding/json lib.

For instance, json-iterator is faster than std lib and provides other customizations on top of it.

This would provide 2 benefits:

  1. Better performance
  2. Logging interface{} objects in a custom way.

For example: json-iterator provides the functionality to have custom tags instead of json.

type UserData struct {
  Name string `json:"name" log:"name"`
  Age int64 `json:"age" log:"age"`
  Email string `json:"email" log:"-"`
  Address string `json:"address" log:"-"`
}

With this feature, we can control what we want to log.
In the above example, we want to only log Name and Age but don't want to log Email and Address.
Since this is using its own instance, it won't affect other serializations/deserializations taking place somewhere else.

On Implementation

I figured one way we can do this is by replacing the type of reflectEnc variable to a newly defined ReflectTypeEncoder interface in json_encoder.go.

This interface will expose mainly 2 methods:

  1. WithBuffer(*buffer.Buffer): which will provide the encoder the Buffer where it needs to write the data
  2. Encode(interface{}): which encodes the object and writes it to the provided buffer above

The configuration can be provided while initializing Logger:

jsonIterConfig := jsoniter.Config{
  EscapeHTML:             false,
  TagKey: "log",
}

zapConfig := zap.NewProductionConfig()
zapConfig.EncoderConfig.EncodeReflectType = NewJsonIterReflectTypeEncoder(jsonIterConfig)

If not provided, we can continue using the default encoding/json one so won't be a breaking change.

LMK if there are any concerns with this approach.
Or there are other ways I can do this.
If you're okay with it, I can raise a PR.

Note: This may also solve #993

@abhinav
Copy link
Collaborator

abhinav commented Dec 14, 2021

Hey, @psrajat. This is a reasonable feature request, although I would suggest some small changes to it:
I think we'd prefer to hide the buffer.Buffer type as an implementation detail and give the encoder only an io.Writer.

So maybe something like this:

type ReflectedEncoder interface { Encode(interface{}) error }
// json.Encoder and jsoniter.Encoder both match this interface.

type EncoderConfig struct {
  // ...
  NewReflectedEncoder func(io.Writer) ReflectedEncoder
}

Zap would still own the buffer and reset it as needed.
The encoder will write to the buffer as requested, and won't be able to read from it or reset it.

@psrajat
Copy link
Contributor Author

psrajat commented Dec 14, 2021

@abhinav Makes sense.

Raised #1039 for this

abhinav added a commit that referenced this issue Dec 15, 2021
This adds support for overriding the mechanism we use to encode
`ReflectType` fields. That is, in the following,

    log.Info("foo", zap.Reflect("bar", baz))

It allows `baz` to be serialized using a third-party JSON library
by providing a custom ReflectedEncoder in the zapcore.EncoderConfig.
`encoding/json`'s Encoder type is a valid ReflectedEncoder.

Resolves #1034

Co-authored-by: Sung Yoon Whang <sungyoonwhang@gmail.com>
Co-authored-by: Abhinav Gupta <abg@uber.com>
@abhinav
Copy link
Collaborator

abhinav commented Dec 15, 2021

Thanks, @psrajat! We'll tag a new release of Zap in January.
You can pin to the master branch meanwhile, if you need to use this feature sooner.

@psrajat
Copy link
Contributor Author

psrajat commented Dec 15, 2021

Thanks a lot for helping out @abhinav :)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Development

Successfully merging a pull request may close this issue.

2 participants