zap——SugaredLogger

SugaredLogger

// A SugaredLogger wraps the base Logger functionality in a slower, but less
// verbose, API. Any Logger can be converted to a SugaredLogger with its Sugar
// method.
//
// Unlike the Logger, the SugaredLogger doesn't insist on structured logging.
// For each log level, it exposes three methods: one for loosely-typed
// structured logging, one for println-style formatting, and one for
// printf-style formatting. For example, SugaredLoggers can produce InfoLevel
// output with Infow ("info with" structured context), Info, or Infof.
type SugaredLogger struct {
    base *Logger
}

先看下注釋,SugaredLogger用一個較慢但不太冗長的API包裝基本Logger功能。Logger可以使用Sugar方法轉換爲SugaredLogger。SugaredLogger可以不用結構化log。針對每個log level,提供了三個方法:格式寬鬆的結構化log,println格式化的log,printf格式化的log。例如,可以使用Infow,Info,Infof生成InfoLevel的輸出。

我們就以InfoLevel的三個方法爲例,看下具體的處理過程。

InfoLevel

// Info uses fmt.Sprint to construct and log a message.
func (s *SugaredLogger) Info(args ...interface{}) {
    s.log(InfoLevel, "", args, nil)
}

// Infof uses fmt.Sprintf to log a templated message.
func (s *SugaredLogger) Infof(template string, args ...interface{}) {
    s.log(InfoLevel, template, args, nil)
}

// Infow logs a message with some additional context. The variadic key-value
// pairs are treated as they are in With.
func (s *SugaredLogger) Infow(msg string, keysAndValues ...interface{}) {
    s.log(InfoLevel, msg, nil, keysAndValues)
}

Info、Infof、Infow均調用了log方法,區別在於傳入的參數不同,Info僅傳入args參數,Infof提供了args、template參數,Infow提供了msg和keysAndValues參數。

func (s *SugaredLogger) log(lvl zapcore.Level, template string, fmtArgs []interface{}, context []interface{}) {
    // If logging at this level is completely disabled, skip the overhead of
    // string formatting.
    if lvl < DPanicLevel && !s.base.Core().Enabled(lvl) {
        return
    }

    // Format with Sprint, Sprintf, or neither.
    msg := template
    if msg == "" && len(fmtArgs) > 0 {
        msg = fmt.Sprint(fmtArgs...)
    } else if msg != "" && len(fmtArgs) > 0 {
        msg = fmt.Sprintf(template, fmtArgs...)
    }

    if ce := s.base.Check(lvl, msg); ce != nil {
        ce.Write(s.sweetenFields(context)...)
    }
}

具體看下fmtArgs的處理過程,如果存在template,則使用fmt.Sprintf格式化fmtArgs;如果不存在,則使用fmt.Sprint格式化參數,最終對msg進行處理。SugaredLogger慢首先就體現在使用了fmt.Sprintf,Sprintf內部會使用反射處理數據。

Check的過程就是調用Logger處理的,已在之前的文章中分析過,此處不再細述。

func (s *SugaredLogger) sweetenFields(args []interface{}) []Field {
    if len(args) == 0 {
        return nil
    }

    // Allocate enough space for the worst case; if users pass only structured
    // fields, we shouldn't penalize them with extra allocations.
    //爲最壞的情況分配足夠的空間;如果只有結構化的fileds,不要用Sugar.
    fields := make([]Field, 0, len(args))
    var invalid invalidPairs

    for i := 0; i < len(args); {
        // This is a strongly-typed field. Consume it and move on.
        if f, ok := args[i].(Field); ok {
            fields = append(fields, f)
            i++
            continue
        }

        // Make sure this element isn't a dangling key.
        // 確保最後的是成對的key、value
        if i == len(args)-1 {
            s.base.DPanic(_oddNumberErrMsg, Any("ignored", args[i]))
            break
        }

        // Consume this value and the next, treating them as a key-value pair. If the
        // key isn't a string, add this pair to the slice of invalid pairs.
        // 取當前值及下一個值爲key、value。如果key是string類型,存入fileds中;否則,存入無效的fileds中。
        key, val := args[i], args[i+1]
        if keyStr, ok := key.(string); !ok {
            // Subsequent errors are likely, so allocate once up front.
            if cap(invalid) == 0 {
                invalid = make(invalidPairs, 0, len(args)/2)
            }
            invalid = append(invalid, invalidPair{i, key, val})
        } else {
            fields = append(fields, Any(keyStr, val))
        }
        i += 2
    }

    // If we encountered any invalid key-value pairs, log an error.
    // 如果存在key不爲string,DPanic
    if len(invalid) > 0 {
        s.base.DPanic(_nonStringKeyErrMsg, Array("invalid", invalid))
    }
    return fields
}

使用Infow時,keysAndValues中的key的類型必須是string,否則會panic。如果參數都是結構化的Fileds,不要使用Sugar,會造成額外的內存分配。

後續的調用與Logger內的處理一致。

總結

SugaredLogger是在Logger的基礎上,提供了額外參數的格式化方式,提供更大的便利性以格式化具體的msg,在可能帶來風險的同時,還會造成額外的內存分配。實際可以通過,Logger Info的fields參數實現類似的功能。如果我們對log格式沒有特別大的隨意性要求,強烈建議使用Logger而不是SugaredLogger。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章