Home > Back-end >  Does Zap logger support escape character '\n' and '\t' to print new line error
Does Zap logger support escape character '\n' and '\t' to print new line error

Time:10-27

func InitLogger() {
    loggerMgr, err := zap.NewProduction()
    if err != nil {
        log.Println("error")
    }
    defer loggerMgr.Sync()
    logger := loggerMgr.Sugar()
    logger.Error("START!")
}

The result

{"level":"error","ts":1635248463.347698,"caller":"cgw-go-utils/main.go:36","msg":"START!","stacktrace":"main.raiseError\n\t/Users/I053322/dev/repo/cgw-go-utils/main.go:36\nmain.main\n\t/Users/I053322/dev/repo/cgw-go-utils/main.go:22\nruntime.main\n\t/usr/local/opt/go/libexec/src/runtime/proc.go:225"}

I want to get the result with escape of

{"level":"error","ts":1635248463.347698,"caller":"cgw-go-utils/main.go:36","msg":"START!","stacktrace":"main.raiseError
    /Users/I053322/dev/repo/cgw-go-utils/main.go:36
main.main
    /Users/I053322/dev/repo/cgw-go-utils/main.go:22
runtime.main
    /usr/local/opt/go/libexec/src/runtime/proc.go:225"}

CodePudding user response:

No.

zap.NewProduction() returns a logger with JSON encoding, and escape sequences as \n and \t are encoded verbatim into JSON. If it didn't, it wouldn't be valid JSON.

If you really must, and you are okay with producing invalid JSON, you might implement your own encoder by decorating the existing zap JSON encoder.

Demonstrative code:

package main

import (
    "bytes"
    "go.uber.org/zap"
    "go.uber.org/zap/buffer"
    "go.uber.org/zap/zapcore"
    "os"
)

func main() {
    core := zapcore.NewCore(
        &EscapeSeqJSONEncoder{ Encoder: zapcore.NewJSONEncoder(zap.NewProductionEncoderConfig()) },
        os.Stdout,
        zapcore.InfoLevel,
    )
    logger := zap.New(core)
    logger.Info("foo", zap.String("some_field", "foo\nbar"))
}

type EscapeSeqJSONEncoder struct {
    zapcore.Encoder
}

func (enc *EscapeSeqJSONEncoder) Clone() zapcore.Encoder {
    return enc // TODO: change me
}

func (enc *EscapeSeqJSONEncoder) EncodeEntry(entry zapcore.Entry, fields []zapcore.Field) (*buffer.Buffer, error) {
    // call EncodeEntry on the embedded interface to get the 
    // original output
    b, err := enc.Encoder.EncodeEntry(entry, fields)
    if err != nil {
        return nil, err
    }
    newb := buffer.NewPool().Get()

    // then manipulate that output into what you need it to be
    newb.Write(bytes.Replace(b.Bytes(), []byte("\\n"), []byte("\n"), -1))
    return newb, nil
}

Outputs:

{"level":"info","ts":1635257984.618096,"msg":"foo","some_field":"foo
bar"}

Notes: the function zapcore.NewCore takes a zapcore.Encoder argument, which is an interface. This interface is very troublesome to implement. The idea is to embed it in your custom struct, so that you get all the methods for free.

  • Related