美文网首页
聊聊golang的zap的NewDevelopment

聊聊golang的zap的NewDevelopment

作者: go4it | 来源:发表于2020-12-06 23:48 被阅读0次

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

    NewDevelopment

    zap@v1.16.0/logger.go

    func NewDevelopment(options ...Option) (*Logger, error) {
        return NewDevelopmentConfig().Build(options...)
    }
    

    NewDevelopment使用NewDevelopmentConfig进行build

    NewDevelopmentConfig

    zap@v1.16.0/config.go

    func NewDevelopmentConfig() Config {
        return Config{
            Level:            NewAtomicLevelAt(DebugLevel),
            Development:      true,
            Encoding:         "console",
            EncoderConfig:    NewDevelopmentEncoderConfig(),
            OutputPaths:      []string{"stderr"},
            ErrorOutputPaths: []string{"stderr"},
        }
    }
    

    NewDevelopmentConfig创建Config,其Level为NewAtomicLevelAt(DebugLevel),Development为true,Encoding为console,EncoderConfig为NewDevelopmentEncoderConfig,OutputPaths及ErrorOutputPaths均为stderr

    NewDevelopmentEncoderConfig

    zap@v1.16.0/config.go

    func NewDevelopmentEncoderConfig() zapcore.EncoderConfig {
        return zapcore.EncoderConfig{
            // Keys can be anything except the empty string.
            TimeKey:        "T",
            LevelKey:       "L",
            NameKey:        "N",
            CallerKey:      "C",
            FunctionKey:    zapcore.OmitKey,
            MessageKey:     "M",
            StacktraceKey:  "S",
            LineEnding:     zapcore.DefaultLineEnding,
            EncodeLevel:    zapcore.CapitalLevelEncoder,
            EncodeTime:     zapcore.ISO8601TimeEncoder,
            EncodeDuration: zapcore.StringDurationEncoder,
            EncodeCaller:   zapcore.ShortCallerEncoder,
        }
    }
    

    NewDevelopmentEncoderConfig创建zapcore.EncoderConfig,其LineEnding为zapcore.DefaultLineEnding,EncodeLevel为zapcore.CapitalLevelEncoder,EncodeTime为zapcore.ISO8601TimeEncoder,EncodeDuration为zapcore.StringDurationEncoder,EncodeCaller为zapcore.ShortCallerEncoder

    encoder

    zap@v1.16.0/zapcore/encoder.go

    const DefaultLineEnding = "\n"
    
    func CapitalLevelEncoder(l Level, enc PrimitiveArrayEncoder) {
        enc.AppendString(l.CapitalString())
    }
    
    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())
    }
    
    func ShortCallerEncoder(caller EntryCaller, enc PrimitiveArrayEncoder) {
        // TODO: consider using a byte-oriented API to save an allocation.
        enc.AppendString(caller.TrimmedPath())
    }
    

    encoder

    zap@v1.16.0/encoder.go

    var (
        errNoEncoderNameSpecified = errors.New("no encoder name specified")
    
        _encoderNameToConstructor = map[string]func(zapcore.EncoderConfig) (zapcore.Encoder, error){
            "console": func(encoderConfig zapcore.EncoderConfig) (zapcore.Encoder, error) {
                return zapcore.NewConsoleEncoder(encoderConfig), nil
            },
            "json": func(encoderConfig zapcore.EncoderConfig) (zapcore.Encoder, error) {
                return zapcore.NewJSONEncoder(encoderConfig), nil
            },
        }
        _encoderMutex sync.RWMutex
    )
    

    _encoderNameToConstructor内置了console、json两种encoder

    NewConsoleEncoder

    zap@v1.16.0/console_encoder.go

    func NewConsoleEncoder(cfg EncoderConfig) Encoder {
        if len(cfg.ConsoleSeparator) == 0 {
            // Use a default delimiter of '\t' for backwards compatibility
            cfg.ConsoleSeparator = "\t"
        }
        return consoleEncoder{newJSONEncoder(cfg, true)}
    }
    
    type consoleEncoder struct {
        *jsonEncoder
    }
    
    func (c consoleEncoder) EncodeEntry(ent Entry, fields []Field) (*buffer.Buffer, error) {
        line := bufferpool.Get()
    
        // We don't want the entry's metadata to be quoted and escaped (if it's
        // encoded as strings), which means that we can't use the JSON encoder. The
        // simplest option is to use the memory encoder and fmt.Fprint.
        //
        // If this ever becomes a performance bottleneck, we can implement
        // ArrayEncoder for our plain-text format.
        arr := getSliceEncoder()
        if c.TimeKey != "" && c.EncodeTime != nil {
            c.EncodeTime(ent.Time, arr)
        }
        if c.LevelKey != "" && c.EncodeLevel != nil {
            c.EncodeLevel(ent.Level, arr)
        }
        if ent.LoggerName != "" && c.NameKey != "" {
            nameEncoder := c.EncodeName
    
            if nameEncoder == nil {
                // Fall back to FullNameEncoder for backward compatibility.
                nameEncoder = FullNameEncoder
            }
    
            nameEncoder(ent.LoggerName, arr)
        }
        if ent.Caller.Defined {
            if c.CallerKey != "" && c.EncodeCaller != nil {
                c.EncodeCaller(ent.Caller, arr)
            }
            if c.FunctionKey != "" {
                arr.AppendString(ent.Caller.Function)
            }
        }
        for i := range arr.elems {
            if i > 0 {
                line.AppendString(c.ConsoleSeparator)
            }
            fmt.Fprint(line, arr.elems[i])
        }
        putSliceEncoder(arr)
    
        // Add the message itself.
        if c.MessageKey != "" {
            c.addSeparatorIfNecessary(line)
            line.AppendString(ent.Message)
        }
    
        // Add any structured context.
        c.writeContext(line, fields)
    
        // If there's no stacktrace key, honor that; this allows users to force
        // single-line output.
        if ent.Stack != "" && c.StacktraceKey != "" {
            line.AppendByte('\n')
            line.AppendString(ent.Stack)
        }
    
        if c.LineEnding != "" {
            line.AppendString(c.LineEnding)
        } else {
            line.AppendString(DefaultLineEnding)
        }
        return line, nil
    }
    

    consoleEncoder内嵌了*jsonEncoder,其EncodeEntry方法通过getSliceEncoder()获取`*sliceArrayEncoder,然后依次往arr添加time、level、loggerName、caller,最后再添加业务的message本身,对于有stacktrace还会追加stacktrace

    实例

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

    输出

    2020-12-06T23:29:08.081+0800    INFO    log-demo/zap_demo.go:17 this will be logged
    2020-12-06T23:29:08.082+0800    PANIC   log-demo/zap_demo.go:18 test panic
    main.developmentDemo
            /zap_demo.go:18
    main.main
            /zap_demo.go:10
    runtime.main
            /usr/local/go/src/runtime/proc.go:204
    panic: test panic
    
    goroutine 1 [running]:
    go.uber.org/zap/zapcore.(*CheckedEntry).Write(0xc0000f20c0, 0x0, 0x0, 0x0)
            /go/pkg/mod/go.uber.org/zap@v1.16.0/zapcore/entry.go:234 +0x585
    go.uber.org/zap.(*SugaredLogger).log(0xc0000fbed0, 0x4, 0x0, 0x0, 0xc0000fbed8, 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.developmentDemo()
            /zap_demo.go:18 +0x199
    main.main()
            /zap_demo.go:10 +0x25
    exit status 2
    

    小结

    NewDevelopmentEncoderConfig创建zapcore.EncoderConfig,其LineEnding为zapcore.DefaultLineEnding,EncodeLevel为zapcore.CapitalLevelEncoder,EncodeTime为zapcore.ISO8601TimeEncoder,EncodeDuration为zapcore.StringDurationEncoder,EncodeCaller为zapcore.ShortCallerEncoder

    doc

    相关文章

      网友评论

          本文标题:聊聊golang的zap的NewDevelopment

          本文链接:https://www.haomeiwen.com/subject/iktzwktx.html