共计 13027 个字符,预计需要花费 33 分钟才能阅读完成。
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 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 库比拟
- 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) }
正文完