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"}
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.