logrus 剖析之 hook


logrus 通過實現 Hook接口擴展 hook 機制,可以根據需求將日志分發到任意的存儲介質, 比如 es, mq 或者監控報警系統,及時獲取異常日志。可以說極大的提高了日志系統的可擴展性。

hook 內部實現

Hook 接口定義如下:

type Hook interface {
  // 定義哪些等級的日志觸發 hook 機制
	Levels() []Level
  // hook 觸發器的具體執行操作
  // 如果 Fire 執行失敗,錯誤日志會重定向到標准錯誤流
	Fire(*Entry) error
}

logrus的內部是怎么實現觸發的呢, logrus中有個內部結構LevelHooks用來存儲所有定義的 hook 函數。

// 存儲全局 hooks, 以日志等級為鍵聚合存儲
type LevelHooks map[Level][]Hook
// 添加 hooks
func (hooks LevelHooks) Add(hook Hook) {
	for _, level := range hook.Levels() {
		hooks[level] = append(hooks[level], hook)
	}
}

// 根據日志等級觸發 hooks
func (hooks LevelHooks) Fire(level Level, entry *Entry) error {
	for _, hook := range hooks[level] {
		if err := hook.Fire(entry); err != nil {
			return err
		}
	}

	return nil
}

在打印日志時, entry會調用 fireHooks()函數,該函數會觸發所有對應的日志等級 的 hook 邏輯。

// 觸發 hooks
func (entry *Entry) fireHooks() {
	entry.Logger.mu.Lock()
	defer entry.Logger.mu.Unlock()
	err := entry.Logger.Hooks.Fire(entry.Level, entry)
	if err != nil {
		fmt.Fprintf(os.Stderr, "Failed to fire hook: %v\n", err)
	}
}

自定義 hook

說了這么多,我們寫一個簡單的自定義 hook 的例子。在這個例子中我們希望當系統發生error或者panic的時候,將錯誤日志打印到單獨的 err.log 文件中便於我們排查錯誤(實際開發中不會這么做)

// MyHook ...
type MyHook struct {
}

// Levels 只定義 error 和 panic 等級的日志,其他日志等級不會觸發 hook
func (h *MyHook) Levels() []log.Level {
	return []log.Level{
		log.ErrorLevel,
		log.PanicLevel,
	}
}

// Fire 將異常日志寫入到指定日志文件中
func (h *MyHook) Fire(entry *log.Entry) error {
	f, err := os.OpenFile("err.log", os.O_RDWR|os.O_CREATE|os.O_APPEND, 0644)
	if err != nil {
		return err
	}
	if _, err := f.Write([]byte(entry.Message)); err != nil {
		return err
	}
	return nil
}

func main() {
	log.AddHook(&MyHook{})
	log.Error("some errors\n")
	log.Panic("some panic\n")
	log.Print("hello world\n")
}

運行后會創建一個 err.log 文件,文件中存儲了:

some errors
some panic

結構符合我們的預期,至此一個自定義的logrus hook就完成了。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM