zap——細節拾遺

前言

我們在之前的篇章中主要按照幾個核心使用點進行源碼的分析,有些細節未能覆蓋到,因此特意對使用時可能需要注意的細節,特意說明下。

1.輸出的日誌信息內容的具體順序問題

先說結論,先後順序爲:

Level-Time-LoggerName-Caller-Message-已排序的InitialFields-Stack-Fields

注意:以上部分信息可缺省。

(1)config build時對InitialFields按照key排序處理,存入logger的core.enc.buf

func (cfg Config) Build(opts ...Option) (*Logger, error) {
    ...
    log := New(
        zapcore.NewCore(enc, sink, cfg.Level),
        cfg.buildOptions(errSink)...,
    )
    ...
    return log, nil
}

func (cfg Config) buildOptions(errSink zapcore.WriteSyncer) []Option {
    opts := []Option{ErrorOutput(errSink)}
    ...
    if len(cfg.InitialFields) > 0 {
        fs := make([]Field, 0, len(cfg.InitialFields))
        keys := make([]string, 0, len(cfg.InitialFields))
        for k := range cfg.InitialFields {
            keys = append(keys, k)
        }
        sort.Strings(keys)//按key排序
        for _, k := range keys {//根據已排序的key處理對應的val
            fs = append(fs, Any(k, cfg.InitialFields[k]))
        }
        opts = append(opts, Fields(fs...))//添加fields的處理func
    }

    return opts
}

// Fields adds fields to the Logger.
func Fields(fs ...Field) Option {
    return optionFunc(func(log *Logger) {
        log.core = log.core.With(fs)
    })
}

func (c *ioCore) With(fields []Field) Core {
    clone := c.clone()
    addFields(clone.enc, fields)
    return clone
}
//fields的真正處理的func
func addFields(enc ObjectEncoder, fields []Field) {
    for i := range fields {
        fields[i].AddTo(enc)
    }
}

func (f Field) AddTo(enc ObjectEncoder) {
    var err error

    switch f.Type {
    ...
    case Int64Type:
        enc.AddInt64(f.Key, f.Integer)
    ...
    default:
        panic(fmt.Sprintf("unknown field type: %v", f))
    }

    if err != nil {
        enc.AddString(fmt.Sprintf("%sError", f.Key), err.Error())
    }
}

func (enc *jsonEncoder) AppendInt64(val int64) {
    enc.addElementSeparator()
    enc.buf.AppendInt(val)
}

//開始調用再構建core後
func New(core zapcore.Core, options ...Option) *Logger {
    if core == nil {
        return NewNop()
    }
    log := &Logger{
        core:        core,
        errorOutput: zapcore.Lock(os.Stderr),
        addStack:    zapcore.FatalLevel + 1,
    }
    return log.WithOptions(options...)//此處進行InitialFields處理的調用
}

func (log *Logger) WithOptions(opts ...Option) *Logger {
    c := log.clone()
    for _, opt := range opts {
        opt.apply(c)
    }
    return c
}

func (f optionFunc) apply(log *Logger) {
    f(log)
}

(2)Write時進行整體信息順序控制

func (log *Logger) Info(msg string, fields ...Field) {
    if ce := log.check(InfoLevel, msg); ce != nil {
        ce.Write(fields...)
    }
}

func (ce *CheckedEntry) Write(fields ...Field) {
    ...
    var err error
    for i := range ce.cores {
        err = multierr.Append(err, ce.cores[i].Write(ce.Entry, fields))
    }
    ...
}

func (c *ioCore) Write(ent Entry, fields []Field) error {
    buf, err := c.enc.EncodeEntry(ent, fields)
    ...
    return nil
}

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 && 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.MessageKey != "" {
        final.addKey(enc.MessageKey)
        final.AppendString(ent.Message)
    }
    if enc.buf.Len() > 0 {//處理InitialFields
        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
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章