logrus 剖析之 formatter

2019/11/08 14:30
阅读数 914

使用

logrus 通过 formatter 来定义输出日志的格式,具体例子如下:

package main

import (
	log "github.com/Sirupsen/logrus"
)

func main() {
	formatter := &log.TextFormatter{
    // 不需要彩色日志
		DisableColors:   true, 
    // 定义时间戳格式
		TimestampFormat: "2006-01-02 15:04:05",
	}
	log.SetFormatter(formatter)
	log.Printf("hello world")
}

打印的日志内容如下:

time="2019-11-07 17:41:20" level=info msg="hello world"

说明:

  • time: 日志的打印时间
  • level: 日志的等级
  • msg: 日志内容

分析

本身 formatter 是接口类型,只要实现该结构我们就可以自定义日志输出格式:

// Any additional fields added with `WithField` or `WithFields` are also in
// `entry.Data`. Format is expected to return an array of bytes which are then
// logged to `logger.Out`.
type Formatter interface {
	Format(*Entry) ([]byte, error)
}

logrus 提供了两种默认的日志输出格式, TextFormatterJSONFormatter.上面的示例使用的就是TextFormatter.

TextFormatter

type TextFormatter struct {
	// Set to true to bypass checking for a TTY before outputting colors.
	ForceColors bool
  //不使用彩色日志
	DisableColors bool
	// Override coloring based on CLICOLOR and CLICOLOR_FORCE. - https://bixense.com/clicolors/
	EnvironmentOverrideColors bool
  // 是否打印时间戳
	DisableTimestamp bool
	// Enable logging the full timestamp when a TTY is attached instead of just
  // 是否按照时间戳格式打印,置为false则只打印从程序启动到打印日志的时间差(单位:秒)
	FullTimestamp bool
  // 时间戳格式
	TimestampFormat string
  // 设置 fields 不排序
	DisableSorting bool
  // 设置 fields 的排序规则
	SortingFunc func([]string)
	// Disables the truncation of the level text to 4 characters.
	DisableLevelTruncation bool
	// QuoteEmptyFields will wrap empty fields in quotes if true
	QuoteEmptyFields bool
  // 是否打印日志到终端
	isTerminal bool
  // fieldMap
	// As an example:
	// formatter := &TextFormatter{
	//     FieldMap: FieldMap{
	//         FieldKeyTime:  "@timestamp",
	//         FieldKeyLevel: "@level",
	//         FieldKeyMsg:   "@message"}}
	FieldMap FieldMap
  // 设置 func 和 file 这两个 field的输出格式
	CallerPrettyfier func(*runtime.Frame) (function string, file string)
	terminalInitOnce sync.Once
}

TextFormatter 实现 Formatter接口

// 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 删除默认的 4 个 field,防止冲突(msg, level, time, logrus_error)
	prefixFieldClashes(data, f.FieldMap, entry.HasCaller())
	keys := make([]string, 0, len(data))
	for k := range data {
		keys = append(keys, k)
	}
// 拼接待打印日志的各个元素
	var funcVal, fileVal string
// 拼接元素过程省略 ...
	f.terminalInitOnce.Do(func() { f.init(entry) })
// 修改时间戳
	timestampFormat := f.TimestampFormat
	if timestampFormat == "" {
		timestampFormat = defaultTimestampFormat
	}
	// 以下省略 ...
  // 打印换行符
	b.WriteByte('\n')
	return b.Bytes(), nil
}

JSONFormatter

// JSONFormatter formats logs into parsable json
type JSONFormatter struct {
	// TimestampFormat sets the format used for marshaling timestamps.
	TimestampFormat string

	// DisableTimestamp allows disabling automatic timestamps in output
	DisableTimestamp bool

	// DataKey allows users to put all the log entry parameters into a nested dictionary at a given key.
	DataKey string

	// FieldMap allows users to customize the names of keys for default fields.
	// As an example:
	// formatter := &JSONFormatter{
	//   	FieldMap: FieldMap{
	// 		 FieldKeyTime:  "@timestamp",
	// 		 FieldKeyLevel: "@level",
	// 		 FieldKeyMsg:   "@message",
	// 		 FieldKeyFunc:  "@caller",
	//    },
	// }
	FieldMap FieldMap

	// CallerPrettyfier can be set by the user to modify the content
	// of the function and file keys in the json data when ReportCaller is
	// activated. If any of the returned value is the empty string the
	// corresponding key will be removed from json fields.
	CallerPrettyfier func(*runtime.Frame) (function string, file string)

	// PrettyPrint will indent all json logs
	PrettyPrint bool
}

JSONFormatter的实现方式与TextFormatter类同,这里就不再赘述。总之logrusformatter实现比较简单,常用参数也在注释里进行了详细的说明。

自定义 Formatter

这里我们自定义一个 formatter,这个 formatter会在打印的日志前后分别加上前缀和后缀。

package main

import (
	"bytes"
	"fmt"

	log "github.com/Sirupsen/logrus"
)

func main() {
  // 初始化自定义 formatter
	formatter := &MyFormatter{
		Prefix: "prefix",
		Suffix: "suffix",
	}
	log.SetFormatter(formatter)
	log.Infoln("hello world")
}

// MyFormatter 自定义 formatter
type MyFormatter struct {
	Prefix string
	Suffix string
}

// Format implement the Formatter interface
func (mf *MyFormatter) Format(entry *log.Entry) ([]byte, error) {
	var b *bytes.Buffer
	if entry.Buffer != nil {
		b = entry.Buffer
	} else {
		b = &bytes.Buffer{}
	}
// entry.Message 就是需要打印的日志
	b.WriteString(fmt.Sprintf("%s - %s - %s", mf.Prefix, entry.Message, mf.Suffix))
	return b.Bytes(), nil
}

打印结果如下:

prefix - hello world - suffix
展开阅读全文
打赏
0
0 收藏
分享
加载中
更多评论
打赏
0 评论
0 收藏
0
分享
返回顶部
顶部