关于golang:logrus-源码解析

47次阅读

共计 13027 个字符,预计需要花费 33 分钟才能阅读完成。

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 entry
func (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)
    }

正文完
 0