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。

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