logrus 源码剖析

logrus 个性

  1. 齐全兼容Go规范库日志模块。logrus领有六种日志级别:debug、info、warn、error、fatal和panic,这是Go规范库日志模块的API的超集。如果你的我的项目应用规范库日志模块,齐全能够用最低的代价迁徙到logrus上。
  2. 可扩大的Hook机制。容许使用者通过hook形式,将日志散发到任意中央,如本地文件系统、规范输入等。
  3. 可选的日志输入格局。logrus内置了两种日志格局,JSONFormatter和TextFormatter。如果这两个格局不满足需要,能够本人入手实现接口Formatter,来定义本人的日志格局。
  4. Field机制。logrus激励通过Field机制进行精细化、结构化的日志记录,而不是通过简短的音讯来记录日志。
  5. logrus是一个可插拔的、结构化的日志框架。

logrus 源码构造

logrus 对外提供实现的接口(exported.go), 提供的接口默认应用的stderr输入,TextFormater格局。

var (    // std is the name of the standard logger in stdlib `log`    std = New())// SetOutput sets the standard logger output.func SetOutput(out io.Writer) {    std.SetOutput(out)}// SetFormatter sets the standard logger formatter.func SetFormatter(formatter Formatter) {    std.SetFormatter(formatter)}// SetReportCaller sets whether the standard logger will include the calling// method as a field.func SetReportCaller(include bool) {    std.SetReportCaller(include)}// SetLevel sets the standard logger level.func SetLevel(level Level) {    std.SetLevel(level)}// GetLevel returns the standard logger level.func GetLevel() Level {    return std.GetLevel()}func IsLevelEnabled(level Level) bool {    return std.IsLevelEnabled(level)}// AddHook adds a hook to the standard logger hooks.func AddHook(hook Hook) {    std.AddHook(hook)}// WithError creates an entry from the standard logger and adds an error to it, using the value defined in ErrorKey as key.func WithError(err error) *Entry {    return std.WithField(ErrorKey, err)}// WithContext creates an entry from the standard logger and adds a context to it.func WithContext(ctx context.Context) *Entry {    return std.WithContext(ctx)}// WithField creates an entry from the standard logger and adds a field to// it. If you want multiple fields, use `WithFields`.//// Note that it doesn't log until you call Debug, Print, Info, Warn, Fatal// or Panic on the Entry it returns.func WithField(key string, value interface{}) *Entry {    return std.WithField(key, value)}// WithFields creates an entry from the standard logger and adds multiple// fields to it. This is simply a helper for `WithField`, invoking it// once for each field.//// Note that it doesn't log until you call Debug, Print, Info, Warn, Fatal// or Panic on the Entry it returns.func WithFields(fields Fields) *Entry {    return std.WithFields(fields)}// WithTime creates an entry from the standard logger and overrides the time of// logs generated with it.//// Note that it doesn't log until you call Debug, Print, Info, Warn, Fatal// or Panic on the Entry it returns.func WithTime(t time.Time) *Entry {    return std.WithTime(t)}// Trace logs a message at level Trace on the standard logger.func Trace(args ...interface{}) {    std.Trace(args...)}// Debug logs a message at level Debug on the standard logger.func Debug(args ...interface{}) {    std.Debug(args...)}// Print logs a message at level Info on the standard logger.func Print(args ...interface{}) {    std.Print(args...)}// Info logs a message at level Info on the standard logger.func Info(args ...interface{}) {    std.Info(args...)}// Warn logs a message at level Warn on the standard logger.func Warn(args ...interface{}) {    std.Warn(args...)}// Warning logs a message at level Warn on the standard logger.func Warning(args ...interface{}) {    std.Warning(args...)}// Error logs a message at level Error on the standard logger.func Error(args ...interface{}) {    std.Error(args...)}// Panic logs a message at level Panic on the standard logger.func Panic(args ...interface{}) {    std.Panic(args...)}// Fatal logs a message at level Fatal on the standard logger then the process will exit with status set to 1.func Fatal(args ...interface{}) {    std.Fatal(args...)}// TraceFn logs a message from a func at level Trace on the standard logger.func TraceFn(fn LogFunction) {    std.TraceFn(fn)}// DebugFn logs a message from a func at level Debug on the standard logger.func DebugFn(fn LogFunction) {    std.DebugFn(fn)}// PrintFn logs a message from a func at level Info on the standard logger.func PrintFn(fn LogFunction) {    std.PrintFn(fn)}// InfoFn logs a message from a func at level Info on the standard logger.func InfoFn(fn LogFunction) {    std.InfoFn(fn)}// WarnFn logs a message from a func at level Warn on the standard logger.func WarnFn(fn LogFunction) {    std.WarnFn(fn)}// WarningFn logs a message from a func at level Warn on the standard logger.func WarningFn(fn LogFunction) {    std.WarningFn(fn)}// ErrorFn logs a message from a func at level Error on the standard logger.func ErrorFn(fn LogFunction) {    std.ErrorFn(fn)}// PanicFn logs a message from a func at level Panic on the standard logger.func PanicFn(fn LogFunction) {    std.PanicFn(fn)}// FatalFn logs a message from a func at level Fatal on the standard logger then the process will exit with status set to 1.func FatalFn(fn LogFunction) {    std.FatalFn(fn)}// Tracef logs a message at level Trace on the standard logger.func Tracef(format string, args ...interface{}) {    std.Tracef(format, args...)}// Debugf logs a message at level Debug on the standard logger.func Debugf(format string, args ...interface{}) {    std.Debugf(format, args...)}// Printf logs a message at level Info on the standard logger.func Printf(format string, args ...interface{}) {    std.Printf(format, args...)}// Infof logs a message at level Info on the standard logger.func Infof(format string, args ...interface{}) {    std.Infof(format, args...)}// Warnf logs a message at level Warn on the standard logger.func Warnf(format string, args ...interface{}) {    std.Warnf(format, args...)}// Warningf logs a message at level Warn on the standard logger.func Warningf(format string, args ...interface{}) {    std.Warningf(format, args...)}// Errorf logs a message at level Error on the standard logger.func Errorf(format string, args ...interface{}) {    std.Errorf(format, args...)}// Panicf logs a message at level Panic on the standard logger.func Panicf(format string, args ...interface{}) {    std.Panicf(format, args...)}// Fatalf logs a message at level Fatal on the standard logger then the process will exit with status set to 1.func Fatalf(format string, args ...interface{}) {    std.Fatalf(format, args...)}// Traceln logs a message at level Trace on the standard logger.func Traceln(args ...interface{}) {    std.Traceln(args...)}// Debugln logs a message at level Debug on the standard logger.func Debugln(args ...interface{}) {    std.Debugln(args...)}// Println logs a message at level Info on the standard logger.func Println(args ...interface{}) {    std.Println(args...)}// Infoln logs a message at level Info on the standard logger.func Infoln(args ...interface{}) {    std.Infoln(args...)}// Warnln logs a message at level Warn on the standard logger.func Warnln(args ...interface{}) {    std.Warnln(args...)}// Warningln logs a message at level Warn on the standard logger.func Warningln(args ...interface{}) {    std.Warningln(args...)}// Errorln logs a message at level Error on the standard logger.func Errorln(args ...interface{}) {    std.Errorln(args...)}func Panicln(args ...interface{}) {    std.Panicln(args...)}func Fatalln(args ...interface{}) {    std.Fatalln(args...)}

如果想要输入日志到文件须要SetOutput 设置文件输入file。接下来次要解析三个接口:

  1. logrus.Info("hello logrus")
  2. logrus.WithFields(logrus.Fields{     "age": 12, }).Info("hello logrus")
  3. logrus.Infof("hello %s", "logrus")

1. WithFields

Fields 是一个map类型的构造: type Fields map[string]interface{}
依据Fields类型封装成Entry构造体返回。

func (logger *Logger) WithFields(fields Fields) *Entry {    entry := logger.newEntry() // 通过sync.Pool获取Entry构造体    defer logger.releaseEntry(entry) // 把entry 放回sync.Pool    return entry.WithFields(fields) }func (logger *Logger) newEntry() *Entry {    entry, ok := logger.entryPool.Get().(*Entry)    if ok {        return entry    }    return NewEntry(logger)}func (entry *Entry) WithFields(fields Fields) *Entry {    data := make(Fields, len(entry.Data)+len(fields))    for k, v := range entry.Data {        data[k] = v    }    fieldErr := entry.err    for k, v := range fields {        isErrField := false        if t := reflect.TypeOf(v); t != nil {            switch {            case t.Kind() == reflect.Func, t.Kind() == reflect.Ptr && t.Elem().Kind() == reflect.Func:                isErrField = true            }        }        if isErrField {            tmp := fmt.Sprintf("can not add field %q", k)            if fieldErr != "" {                fieldErr = entry.err + ", " + tmp            } else {                fieldErr = tmp            }        } else {            data[k] = v        }    }    return &Entry{Logger: entry.Logger, Data: data, Time: entry.Time, err: fieldErr, Context: entry.Context}}

2. Info

func (logger *Logger) Info(args ...interface{}) {    logger.Log(InfoLevel, args...)}func (logger *Logger) Log(level Level, args ...interface{}) {    if logger.IsLevelEnabled(level) { // 判断是否能够打印        entry := logger.newEntry() // 获取新的Entry        entry.Log(level, args...)  // entry 去打印        logger.releaseEntry(entry)    }}func (entry *Entry) Log(level Level, args ...interface{}) {    if entry.Logger.IsLevelEnabled(level) { // entry里再判断是否能够打印        entry.log(level, fmt.Sprint(args...)) //格式化args 成string    } }func (entry *Entry) log(level Level, msg string) {    var buffer *bytes.Buffer //用来接管字节    newEntry := entry.Dup()    if newEntry.Time.IsZero() {        newEntry.Time = time.Now()    }    newEntry.Level = level    newEntry.Message = msg    newEntry.Logger.mu.Lock()    reportCaller := newEntry.Logger.ReportCaller    bufPool := newEntry.getBufferPool()    newEntry.Logger.mu.Unlock()    if reportCaller {        newEntry.Caller = getCaller()    }    newEntry.fireHooks()    buffer = bufPool.Get()    defer func() {        newEntry.Buffer = nil        buffer.Reset()        bufPool.Put(buffer)    }()    buffer.Reset()    newEntry.Buffer = buffer    newEntry.write() // 写入数据    newEntry.Buffer = nil    // To avoid Entry#log() returning a value that only would make sense for    // panic() to use in Entry#Panic(), we avoid the allocation by checking    // directly here.    if level <= PanicLevel {        panic(newEntry)    }}func (entry *Entry) write() {    serialized, err := entry.Logger.Formatter.Format(entry) //格式化entry    if err != nil {        fmt.Fprintf(os.Stderr, "Failed to obtain reader, %v\n", err)        return    }    entry.Logger.mu.Lock()    defer entry.Logger.mu.Unlock()    if _, err := entry.Logger.Out.Write(serialized); err != nil { // 写入数据        fmt.Fprintf(os.Stderr, "Failed to write to log, %v\n", err)    }}

格式化:text_format和 json_format

TextFormatter:

// Format renders a single log entryfunc (f *TextFormatter) Format(entry *Entry) ([]byte, error) {    data := make(Fields)    for k, v := range entry.Data {        data[k] = v    }    prefixFieldClashes(data, f.FieldMap, entry.HasCaller())    keys := make([]string, 0, len(data))    for k := range data {        keys = append(keys, k)    }    var funcVal, fileVal string    fixedKeys := make([]string, 0, 4+len(data))    if !f.DisableTimestamp {        fixedKeys = append(fixedKeys, f.FieldMap.resolve(FieldKeyTime))    }    fixedKeys = append(fixedKeys, f.FieldMap.resolve(FieldKeyLevel))    if entry.Message != "" {        fixedKeys = append(fixedKeys, f.FieldMap.resolve(FieldKeyMsg))    }    if entry.err != "" {        fixedKeys = append(fixedKeys, f.FieldMap.resolve(FieldKeyLogrusError))    }    if entry.HasCaller() {        if f.CallerPrettyfier != nil {            funcVal, fileVal = f.CallerPrettyfier(entry.Caller)        } else {            funcVal = entry.Caller.Function            fileVal = fmt.Sprintf("%s:%d", entry.Caller.File, entry.Caller.Line)        }        if funcVal != "" {            fixedKeys = append(fixedKeys, f.FieldMap.resolve(FieldKeyFunc))        }        if fileVal != "" {            fixedKeys = append(fixedKeys, f.FieldMap.resolve(FieldKeyFile))        }    }    if !f.DisableSorting {        if f.SortingFunc == nil {            sort.Strings(keys)            fixedKeys = append(fixedKeys, keys...)        } else {            if !f.isColored() {                fixedKeys = append(fixedKeys, keys...)                f.SortingFunc(fixedKeys)            } else {                f.SortingFunc(keys)            }        }    } else {        fixedKeys = append(fixedKeys, keys...)    }    var b *bytes.Buffer    if entry.Buffer != nil {        b = entry.Buffer    } else {        b = &bytes.Buffer{}    }    f.terminalInitOnce.Do(func() { f.init(entry) })    timestampFormat := f.TimestampFormat    if timestampFormat == "" {        timestampFormat = defaultTimestampFormat    }    if f.isColored() {        f.printColored(b, entry, keys, data, timestampFormat)    } else {        for _, key := range fixedKeys {            var value interface{}            switch {            case key == f.FieldMap.resolve(FieldKeyTime):                value = entry.Time.Format(timestampFormat)            case key == f.FieldMap.resolve(FieldKeyLevel):                value = entry.Level.String()            case key == f.FieldMap.resolve(FieldKeyMsg):                value = entry.Message            case key == f.FieldMap.resolve(FieldKeyLogrusError):                value = entry.err            case key == f.FieldMap.resolve(FieldKeyFunc) && entry.HasCaller():                value = funcVal            case key == f.FieldMap.resolve(FieldKeyFile) && entry.HasCaller():                value = fileVal            default:                value = data[key]            }            f.appendKeyValue(b, key, value) // key 和 value 写入b 字节        }    }    b.WriteByte('\n')    return b.Bytes(), nil}

json_format

func (f *JSONFormatter) Format(entry *Entry) ([]byte, error) {    data := make(Fields, len(entry.Data)+4)    for k, v := range entry.Data {        switch v := v.(type) {        case error:            // Otherwise errors are ignored by `encoding/json`            // https://github.com/sirupsen/logrus/issues/137            data[k] = v.Error()        default:            data[k] = v        }    }    if f.DataKey != "" {        newData := make(Fields, 4)        newData[f.DataKey] = data        data = newData    }    prefixFieldClashes(data, f.FieldMap, entry.HasCaller())    timestampFormat := f.TimestampFormat    if timestampFormat == "" {        timestampFormat = defaultTimestampFormat    }    if entry.err != "" {        data[f.FieldMap.resolve(FieldKeyLogrusError)] = entry.err    }    if !f.DisableTimestamp {        data[f.FieldMap.resolve(FieldKeyTime)] = entry.Time.Format(timestampFormat)    }    data[f.FieldMap.resolve(FieldKeyMsg)] = entry.Message    data[f.FieldMap.resolve(FieldKeyLevel)] = entry.Level.String()    if entry.HasCaller() {        funcVal := entry.Caller.Function        fileVal := fmt.Sprintf("%s:%d", entry.Caller.File, entry.Caller.Line)        if f.CallerPrettyfier != nil {            funcVal, fileVal = f.CallerPrettyfier(entry.Caller)        }        if funcVal != "" {            data[f.FieldMap.resolve(FieldKeyFunc)] = funcVal        }        if fileVal != "" {            data[f.FieldMap.resolve(FieldKeyFile)] = fileVal        }    }    var b *bytes.Buffer    if entry.Buffer != nil {        b = entry.Buffer    } else {        b = &bytes.Buffer{}    }    encoder := json.NewEncoder(b) //应用Json库序列化    encoder.SetEscapeHTML(!f.DisableHTMLEscape)    if f.PrettyPrint {        encoder.SetIndent("", "  ")    }    if err := encoder.Encode(data); err != nil {        return nil, fmt.Errorf("failed to marshal fields to JSON, %w", err)    }    return b.Bytes(), nil}

3. Infof

和Info底层统一,下层应用fmt.Sprintf格式化。

func (logger *Logger) Infof(format string, args ...interface{}) {    logger.Logf(InfoLevel, format, args...)}func (logger *Logger) Logf(level Level, format string, args ...interface{}) {    if logger.IsLevelEnabled(level) {        entry := logger.newEntry()        entry.Logf(level, format, args...)        logger.releaseEntry(entry)    }}func (entry *Entry) Logf(level Level, format string, args ...interface{}) {    if entry.Logger.IsLevelEnabled(level) {        entry.Log(level, fmt.Sprintf(format, args...))    }}

小结

和zap库比拟

  1. logrus 对外提供的接口绝对比拟零散而且如果日志输入到文件时,文件治理还须要本人做。zap提供了一些规范的接口,通过配置来治理,文件只须要提供文件名即可,不必另外本人治理。
  2. logrus的json格局应用的规范库json. zap的json格局是本人封装的,性能比json库强。
  3. logrus 应用sync.Pool时,如果没有Get到会本人生成,zap并没有。示例如下:
    logrus:

    func (logger *Logger) newEntry() *Entry { entry, ok := logger.entryPool.Get().(*Entry) if ok {     return entry } return NewEntry(logger)}