1

本文主要研究一下golang的zap的NewExample

NewExample

zap@v1.16.0/logger.go

func NewExample(options ...Option) *Logger {
    encoderCfg := zapcore.EncoderConfig{
        MessageKey:     "msg",
        LevelKey:       "level",
        NameKey:        "logger",
        EncodeLevel:    zapcore.LowercaseLevelEncoder,
        EncodeTime:     zapcore.ISO8601TimeEncoder,
        EncodeDuration: zapcore.StringDurationEncoder,
    }
    core := zapcore.NewCore(zapcore.NewJSONEncoder(encoderCfg), os.Stdout, DebugLevel)
    return New(core).WithOptions(options...)
}
NewExample使用通过core来创建Logger,其中core使用的Encoder为JSONEncoder,WriteSyncer使用的是os.Stdout,LevelEnabler使用的是DebugLevel;使用的JSONEncoder的zapcore.EncoderConfig其EncodeLevel为zapcore.LowercaseLevelEncoder,其TimeEncoder为zapcore.ISO8601TimeEncoder,,其DurationEncoder为zapcore.StringDurationEncoder

encoder

zap@v1.16.0/zapcore/encoder.go

func LowercaseLevelEncoder(l Level, enc PrimitiveArrayEncoder) {
    enc.AppendString(l.String())
}

func ISO8601TimeEncoder(t time.Time, enc PrimitiveArrayEncoder) {
    encodeTimeLayout(t, "2006-01-02T15:04:05.000Z0700", enc)
}

func StringDurationEncoder(d time.Duration, enc PrimitiveArrayEncoder) {
    enc.AppendString(d.String())
}
LowercaseLevelEncoder使用小写打印log level;ISO8601TimeEncoder使用2006-01-02T15:04:05.000Z0700格式打印时间;StringDurationEncoder使用Duration内置的String()打印time.Duration

NewJSONEncoder

zap@v1.16.0/zapcore/json_encoder.go

func NewJSONEncoder(cfg EncoderConfig) Encoder {
    return newJSONEncoder(cfg, false)
}

func newJSONEncoder(cfg EncoderConfig, spaced bool) *jsonEncoder {
    return &jsonEncoder{
        EncoderConfig: &cfg,
        buf:           bufferpool.Get(),
        spaced:        spaced,
    }
}

func (enc *jsonEncoder) EncodeEntry(ent Entry, fields []Field) (*buffer.Buffer, error) {
    final := enc.clone()
    final.buf.AppendByte('{')

    if final.LevelKey != "" {
        final.addKey(final.LevelKey)
        cur := final.buf.Len()
        final.EncodeLevel(ent.Level, final)
        if cur == final.buf.Len() {
            // User-supplied EncodeLevel was a no-op. Fall back to strings to keep
            // output JSON valid.
            final.AppendString(ent.Level.String())
        }
    }
    if final.TimeKey != "" {
        final.AddTime(final.TimeKey, ent.Time)
    }
    if ent.LoggerName != "" && final.NameKey != "" {
        final.addKey(final.NameKey)
        cur := final.buf.Len()
        nameEncoder := final.EncodeName

        // if no name encoder provided, fall back to FullNameEncoder for backwards
        // compatibility
        if nameEncoder == nil {
            nameEncoder = FullNameEncoder
        }

        nameEncoder(ent.LoggerName, final)
        if cur == final.buf.Len() {
            // User-supplied EncodeName was a no-op. Fall back to strings to
            // keep output JSON valid.
            final.AppendString(ent.LoggerName)
        }
    }
    if ent.Caller.Defined {
        if final.CallerKey != "" {
            final.addKey(final.CallerKey)
            cur := final.buf.Len()
            final.EncodeCaller(ent.Caller, final)
            if cur == final.buf.Len() {
                // User-supplied EncodeCaller was a no-op. Fall back to strings to
                // keep output JSON valid.
                final.AppendString(ent.Caller.String())
            }
        }
        if final.FunctionKey != "" {
            final.addKey(final.FunctionKey)
            final.AppendString(ent.Caller.Function)
        }
    }
    if final.MessageKey != "" {
        final.addKey(enc.MessageKey)
        final.AppendString(ent.Message)
    }
    if enc.buf.Len() > 0 {
        final.addElementSeparator()
        final.buf.Write(enc.buf.Bytes())
    }
    addFields(final, fields)
    final.closeOpenNamespaces()
    if ent.Stack != "" && final.StacktraceKey != "" {
        final.AddString(final.StacktraceKey, ent.Stack)
    }
    final.buf.AppendByte('}')
    if final.LineEnding != "" {
        final.buf.AppendString(final.LineEnding)
    } else {
        final.buf.AppendString(DefaultLineEnding)
    }

    ret := final.buf
    putJSONEncoder(final)
    return ret, nil
}
NewJSONEncoder创建的是jsonEncoder;其EncodeEntry方法一次打印LevelKey、TimeKey、LoggerName、Caller,最后才是业务的message本身,然后结束json打印,对于有stacktrace还会以非json的形式(普通堆栈形式)打印stacktrace

实例

func exampleDemo() {
    logger := zap.NewExample()
    defer logger.Sync() // flushes buffer, if any
    sugar := logger.Sugar()
    sugar.Info("this will be logged")
    sugar.Info("time=", time.Now())
    sugar.Panic("test panic")
}

输出

{"level":"info","msg":"this will be logged"}
{"level":"info","msg":"time=2020-12-07 23:14:48.50402 +0800 CST m=+0.000602110"}
{"level":"panic","msg":"test panic"}
panic: test panic

goroutine 1 [running]:
go.uber.org/zap/zapcore.(*CheckedEntry).Write(0xc0001680c0, 0x0, 0x0, 0x0)
        /go/pkg/mod/go.uber.org/zap@v1.16.0/zapcore/entry.go:234 +0x585
go.uber.org/zap.(*SugaredLogger).log(0xc000179e98, 0xc000054e04, 0x0, 0x0, 0xc000179ea0, 0x1, 0x1, 0x0, 0x0, 0x0)
        /go/pkg/mod/go.uber.org/zap@v1.16.0/sugar.go:234 +0xf6
go.uber.org/zap.(*SugaredLogger).Panic(...)
        /go/pkg/mod/go.uber.org/zap@v1.16.0/sugar.go:123
main.exampleDemo()
        /zap_demo.go:19 +0x277
main.main()
        /zap_demo.go:10 +0x25
exit status 2

小结

NewExample使用通过core来创建Logger,其中core使用的Encoder为JSONEncoder,WriteSyncer使用的是os.Stdout,LevelEnabler使用的是DebugLevel;NewJSONEncoder创建的是jsonEncoder;其EncodeEntry方法一次打印LevelKey、TimeKey、LoggerName、Caller,最后才是业务的message本身,然后结束json打印,对于有stacktrace还会以非json的形式(普通堆栈形式)打印stacktrace

doc


codecraft
11.9k 声望2k 粉丝

当一个代码的工匠回首往事时,不因虚度年华而悔恨,也不因碌碌无为而羞愧,这样,当他老的时候,可以很自豪告诉世人,我曾经将代码注入生命去打造互联网的浪潮之巅,那是个很疯狂的时代,我在一波波的浪潮上留下...