Go編程快閃之 logrus日誌庫

戰術臥倒

golang中常見的日誌包是logrus, 根據logrus的胚子和我們的生產要求,給出一個生產可用的logrus實踐姿勢。

主謂賓定狀補

logrus是一個結構化的、可插拔的、兼容golang標準log api的日誌庫。

快速過一下能力

  • 支持對output=TTY增加關鍵字顏色
  • 內置JSONFormatter和TextFormatter(默認)兩種Formatter
  • 支持輸出logger所在的函數行位置 log.SetReportCaller(true)
  • 可以兼容golang內置的標準log庫, 建議無腦替換
  • 鼓勵輸出可解析的日誌字段,而不是大段的無法結構化的文本日誌
log.WithFields(log.Fields{
 "event": event,
 "topic": topic,
 "key": key,
}).Fatal("Failed to send event")

基於現狀,湊了6個錢包上生產,下面給出一些自己的生產實踐。

添磚加瓦

1. logrus不支持滾動日誌

好馬配好鞍 https://github.com/lestrrat-go/file-rotatelogs 讓你下雨天不再哭泣。

它會根據配置自動按照時間切分日誌,並滾動清理日誌(不用配磁盤報警,不用擔心磁盤滿故障)。

	logf, err := rotatelogs.New(
  	cfg.Log.LogDir+logName+".%Y%m%d%H%M",
  	rotatelogs.WithLinkName(cfg.Log.LogDir+logName),
  	rotatelogs.WithMaxAge(24*time.Hour),
  	rotatelogs.WithRotationTime(time.Hour),
  )
  if err != nil {
  	stdLog.Printf("failed to create rotatelogs: %s", err)
  	return
  }

2. 日誌格式化

java生態默認日誌輸出格式:

11:44:44.827 WARN [93ef3E0120160803114444] [main] [ClassPathXmlApplicationContext] Exception encountered during context initialization - cancelling refresh attempt

在公司中javaer佔據主流,故java的默認格式就成了公司集中式日誌的"標準"格式。

很明顯,logrus默認的兩種Formatter都不匹配。

github.com/antonfisher/nested-logrus-formatter 讓你柳暗花明。

log.SetFormatter(&nested.Formatter{ // 嵌套日誌兼容skynet日誌格式
		HideKeys:        true,
		FieldsOrder:     []string{"region", "node", "topic"},
		TimestampFormat: "2006-01-02 15:04:05.000", // 顯示ms
	})

3. 自定義Hook用法:輸出默認字段

寫本文的時候,發現logrus官方本身支持輸出默認日誌字段。

requestLogger := log.WithFields(log.Fields{"request_id": request_id, "user_ip": user_ip})
requestLogger.Warn("something not great happened")

Hook: 通常 鉤子函數用於在觸發某種事件時附帶一些動作。

logrus的Hook定義:logEntry滿足指定的logLevel日誌時, 你想要做的動作(你甚至可以不設置output直接在hook輸出日誌, 這就是內置write hook的實現)。

type Hook interface {
	Levels() []Level
	Fire(*Entry) error
}

示例代碼爲logLevel>=info的logEntry,固定了2個日誌字段。

type FixedFieldHook struct {
	LogLevels  []logrus.Level
	FixedField map[string]string
}

// Fire will be called when some logging function is called with current hook
// It will format log entry to string and write it to appropriate writer
func (hook *FixedFieldHook) Fire(entry *logrus.Entry) error {
	for k, v := range hook.FixedField {
		entry.Data[k] = v
	}
	return nil
}

log.AddHook(&FixedFieldHook{ // Set fixed field
		FixedField: map[string]string{"region": cfg.LocalRegion, "node": ip},
		LogLevels: []logrus.Level{
			logrus.InfoLevel,
			logrus.ErrorLevel,
			logrus.WarnLevel,
			logrus.FatalLevel,
		},
	})

拋磚引玉,戰術臥倒。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章