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。