I have the following logging structure:
[STDERR] 2018-07-09 11:06:16.003 INFO some_pkg/main.go:232 Logging message 1 {"pid": 8842, "process": "some_process"}
[STDERR] 2018-07-09 11:06:16.006 DEBUG some_pkg/main.go:291 Logging message 2 {"pid": 8842, "process": "other_process"}
[STDERR] 2018-07-09 11:06:16.009 INFO some_pkg/main.go:345 Logging message 3 {"pid": 8842, "process": "some_process"}
You can see there are five types of information in this logging snippet. There are date/time, log-level, occurrence, message and a JSON fields (except [STDERR] field). Which means I have five columns in my logging structure. I would like to add a new columns with key pid and process (from the JSON). How should I do this with ZAP encoders and configs? I didn't find a solution for that in the ZAP documentation.
I use the following code to add fields to the logging:
logger = logger.With(zap.Field{Key: "pid", Type: zapcore.Int64Type, Integer: int64(os.Getpid())})
But the pid field's value goes to a JSON (what you can see above) and I would like to see it in a new column. There is an easy way in ZAP to do this?
My desired structure would be the following with the previous example:
[STDERR] 2018-07-09 11:06:16.003 INFO some_pkg/main.go:232 Logging message 1 8842 some_process
[STDERR] 2018-07-09 11:06:16.006 DEBUG some_pkg/main.go:291 Logging message 2 8836 other_process
[STDERR] 2018-07-09 11:06:16.009 INFO some_pkg/main.go:345 Logging message 3 8842 some_process
There isn't a very good way to do this. This answer provides an explanation, a non-solution and a hack.
First of all let me clarify the terminology: the log "columns" you are talking about are the fields of zapcore.Entry
, i.e. the log entry. The spacing between the columns is given by the field ConsoleSeparator
of zapcore.EncoderConfig
, which defaults to \t
.
The formatting of the log entry happens in the EncodeEntry
method of the consoleEncoder
type, but you can't customize its behavior: zapcore.Entry
is a struct and there are no hooks in that implementation that you can exploit to change what and when the fields are logged. It might be worth to request this to the zap team.
The first option you are left with is to implement your own encoder and use it to construct a zapcore.Core
:
enc := &customEncoder{}
logger := zap.New(zapcore.NewCore(enc, os.Stderr, zap.NewAtomicLevelAt(zap.DebugLevel)))
The custom encoder must implement zapcore.Encoder
. This comes with a number of problems, e.g. the order of the log entry fields, the possible presence of a stack trace, the encoder's configuration options, code reuse, etc. It's not worth it to go into all the details; I'll just say that it will be very awkward to implement zapcore.Encoder
for this use case, and likely not worth the maintenance burden.
The last field of zapcore.Entry
that the default console encoder prints is the Message
. So what you can do is to pre-format the message:
pid := 8842
procname := "some_process"
msg := fmt.Sprintf("Logging message 1\t%d\t%s", pid, procname)
logger.Info(msg)
Will print:
[STDERR] 2018-07-09 11:06:16.003 INFO some_pkg/main.go:232 Logging message 1 8842 some_process
The requested logging style would result in a mix of structured logging (json) and unstructured logging (all other fields in console).
blackgreen's answer should make it possible and its a good hack, but the bigger problem here I think is that you are working against what zap has to offer (Blazing fast, structured, leveled logging in Go.).
Recommended approach would be put everything in json and parse json keys in whatever log viewer you are using. Resuling log structure looks something like:
{"timeStamp": "2018-07-09 11:06:16.003", "level": "INFO", "source": "some_pkg/main.go:232", "msg": "Logging message 1", "pid": 8842, "process": "some_process"}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With