Using Zap - Creating custom encoders

The various implementations of field encoders provided in zap can sometimes feel inadequate. For example, you might want the logging output to be similar to that in syslog or other common log formats. You might want the timestamps in the log to ignore seconds, or the log level to be wrapped within square brackets.

To have your own custom formatters for the metadata fields you need to write custom encoders.


Contents:

This post is third in a series of posts showing different ways of using zap. The other posts are Simple use cases, Creating custom loggers and Working With Global Loggers

This documentation was written for zap v1.8.

The full source for the code extracts in the rest of the post is here

1. Performance considerations

You can use custom encoders for formatting time, level, caller, etc. One caveat is that you need these encoders to be as efficient as possible so as to not negate the memory/speed advantages of zap itself. After all, these functions are called for every line of log to be emitted! So avoid creating temporary variables, or doing any expensive calculations.

That said, the examples below are just a demonstration. I am not claiming at all that they are efficient replacements of the default functionality. 😄

2. Customizing timestamp formatting

Here is an implementation which uses the syslog format often found in the wild.

func SyslogTimeEncoder(t time.Time, enc zapcore.PrimitiveArrayEncoder) {
	enc.AppendString(t.Format("Jan  2 15:04:05"))
}

...

cfg.EncoderConfig.EncodeTime = SyslogTimeEncoder

logger, _ = cfg.Build()
logger.Info("This should have a syslog style timestamp")

Output:

May  2 18:54:55 INFO    This should have a syslog style timestamp

If you noticed in the implementation that the encoder is supposed to append primitives to an array like object. zap uses this array to efficiently encode output with minimal memory allocations.

3. Customizing level formatting

func CustomLevelEncoder(level zapcore.Level, enc zapcore.PrimitiveArrayEncoder) {
	enc.AppendString("[" + level.CapitalString() + "]")
}
...
cfg.EncoderConfig.EncodeLevel = CustomLevelEncoder

logger, _ = cfg.Build()
logger.Info("This should have a bracketed level name")

Output:

May  2 18:54:55 [INFO]  This should have a bracketed level name
I am creating a single string from multiple substrings and appending it to the array. This might cause temporary memory allocations. I had to do this because if I appended the substrings separately, it seemed to me that zap was considering them as separate fields in the output and spacing them out with whitespace.

Similar customization can be done for other metadata fields as well. You can look at the zap source to find the general pattern of these implementations.

techgolang
Using Zap - Creating custom loggers Using Zap - Working With Global Loggers