logrus 源码剖析
logrus 个性
- 齐全兼容Go规范库日志模块。logrus领有六种日志级别:debug、info、warn、error、fatal和panic,这是Go规范库日志模块的API的超集。如果你的我的项目应用规范库日志模块,齐全能够用最低的代价迁徙到logrus上。
- 可扩大的Hook机制。容许使用者通过hook形式,将日志散发到任意中央,如本地文件系统、规范输入等。
- 可选的日志输入格局。logrus内置了两种日志格局,JSONFormatter和TextFormatter。如果这两个格局不满足需要,能够本人入手实现接口Formatter,来定义本人的日志格局。
- Field机制。logrus激励通过Field机制进行精细化、结构化的日志记录,而不是通过简短的音讯来记录日志。
- 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。接下来次要解析三个接口:
logrus.Info("hello logrus")
logrus.WithFields(logrus.Fields{ "age": 12, }).Info("hello logrus")
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库比拟
- logrus 对外提供的接口绝对比拟零散而且如果日志输入到文件时,文件治理还须要本人做。zap提供了一些规范的接口,通过配置来治理,文件只须要提供文件名即可,不必另外本人治理。
- logrus的json格局应用的规范库json. zap的json格局是本人封装的,性能比json库强。
logrus 应用sync.Pool时,如果没有Get到会本人生成,zap并没有。示例如下:
logrus:func (logger *Logger) newEntry() *Entry { entry, ok := logger.entryPool.Get().(*Entry) if ok { return entry } return NewEntry(logger)}