uber-go / zap

Blazing fast, structured, leveled logging in Go.

Home Page:https://pkg.go.dev/go.uber.org/zap

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Invalid JSON output - value without key when using custom caller encoder

PossibleLlama opened this issue · comments

Describe the bug
When using a custom caller encoder and appending to the encoder, the output gives the EntryCaller value without a key, {"caller":"foo","undefined"}.
The undefined value is whatever the zapcore.EntryCaller struct is.

To Reproduce

func main() {
  z := zap.NewProductionConfig()
  z.EncoderConfig.EncodeCaller = customCallerEncoder
}

func customCallerEncoder(caller zapcore.EntryCaller, enc zapcore.PrimitiveArrayEncoder) {
  enc.AppendString("foo")
  zapcore.ShortCallerEncoder(zapcore.EntryCaller{}, enc)
}

Expected behavior
The output should be valid JSON. I'd expect the "undefined" value to be dropped if there isn't a key associated with it.

Additional context
I'm trying to customise the value of the caller function to provide a link to the file within source control. The default encoder adds a : between the filename and the line which doesn't work for the link. Being able to customise that deliminator would be great, or if there is another solution I'd be happy to learn it.

Hey, @PossibleLlama, thanks for the report.
We don't think this is a bug in Zap, but incorrect usage of the CallerEncoder hook.
A custom caller encoder should make exactly one Append* call on the given PrimitiveArrayEncoder.
The code above makes two: the "foo" and the ShortCallerEncoder call.
(We agree that the documentation could be better at clarifying this; we'll fix that.)

The ShortCallerEncoder is quite short;
To do what you want, you can use the same information it makes use of, and customize it as needed:

zap/zapcore/encoder.go

Lines 276 to 279 in f9c5b00

func ShortCallerEncoder(caller EntryCaller, enc PrimitiveArrayEncoder) {
// TODO: consider using a byte-oriented API to save an allocation.
enc.AppendString(caller.TrimmedPath())
}

Alternatively, if you need the file and line number separately, we wouldn't be opposed to a new method on EntryCaller. Maybe:

func (EntryCaller) ShortFileLine() (file string, line int)

Although, based on the use case described above, you probably want the full file path and line number, and that's already available on EntryCaller:

zap/zapcore/entry.go

Lines 70 to 74 in f9c5b00

type EntryCaller struct {
Defined bool
PC uintptr
File string
Line int

Thanks for the explanation, I wasn't aware that was equivalent to calling the encoder twice.
Thank you for the help 😄

No problem! Feel free to open another Issue/Discussion if you have any more questions.
Closing this one.