log4go的一些改進設想


log4go 的 4.0.2 版本(https://github.com/ccpaging/log4go/tree/4.0.2)發布以后,
看了看別的 go 語言日志文件設計。發現了一篇好文:

log4go 和 logrus 的對比與分析
https://www.doraemonext.com/archives/783.html

順藤摸瓜,找了一窩關於日志的設計。鏈接如下(含老的鏈接):

  1. https://github.com/alecthomas/log4go/
    這是log4go項目的“鼻祖”
  2. https://github.com/ngmoco/timber
    實現了結構化,寫文件緩沖,熱配置等。把log4go重構的面目全非
  3. https://github.com/siddontang/go/tree/master/log
  4. https://github.com/sirupsen/logrus
  5. https://github.com/YoungPioneers/blog4go
  6. https://github.com/YoungPioneers/blog4go-benchmark 各種 go log 的benchmark對比
  7. https://github.com/cihub/seelog

異步寫入日志

log4go 的特點之一是異步寫入。格式化日志記錄、寫入文件、轉儲日志等,都會消耗 CPU 的時間,並可能因為錯誤處理而阻塞主線程。
但日志系統僅僅是一個輔助功能,所以,保證主線程的高效運行是首先要達到的設計要求。異步寫入是可行的方案之一。

自擴展日志接口

其實,log4go 是支持類似 logrus 的擴展特性的。

正好糾結於 color text term log 的設計如何處理的問題……因為這個功能使用了第三方包。放在log4go里增加了它的依賴性。但這確實又是我特別特別喜歡的一個功能。

不如把 color text term log 做成擴展日志接口。說干就干……

先搞清楚 log4go 中可用的擴展接口:

type LogWriter interface {
	LogWrite(rec *LogRecord)

	// This should clean up anything lingering about the LogWriter, as it is called before
	// the LogWriter is removed.  LogWrite should not be called after Close.
	Close()
}

type Filter struct {
	Level Level
	rec 	chan *LogRecord	// write queue
	closed 	bool	// true if Socket was closed at API level
	LogWriter
}

type Logger map[string]*Filter

func (log Logger) AddFilter(name string, lvl Level, writer LogWriter) Logger {
	log[name] = NewFilter(lvl, writer)
	return log
}

擴展程序只要做:

  1. NewXXXLogWrite,初始化擴展要使用的資源。
  2. LogWrite(rec *LogRecord),輸出日志記錄
  3. Close()中關閉或釋放資源
  4. 在應用程序中調用 AddFilter 把新的日志擴展加入到log4go日志結構中

大功告成了。

其中,Add filter name 是 Logger map 的索引關鍵字,log4go 使用了:

"stdout", "file", "syslog"

如果新加的 Filter 的關鍵字已存在,log4go(4.0.2以后的版本)將自動關閉原來的,再增加新的。代碼如下:

func (log Logger) AddFilter(name string, lvl Level, writer LogWriter) Logger {
	if filt, isExist := log[name]; isExist {
		filt.Close()
		delete(log, name)
	}
	log[name] = NewFilter(lvl, writer)
	return log
}

借助擴展接口,log4go的日志記錄可以采用任何你希望的封裝格式,例如 xml 和 json,這是已經實現的。
以后還可以擴展csv(使日志文件導入到Excel中)或者json封裝的message。

可擴展的日志接口包括:

  • Send error messages as a mail

  • Make TCP/UDP server and let client pull the messages

  • websocket

  • nanomsg pub/sub

  • Store log messages in MySQL

自擴展日志配置接口

log4go 4.0.2 支持 xml 和 json 配置。日志文件的配置有三種方式:

  1. 在應用程序中配置
  2. 單獨的配置文件
  3. 存於主程序配置文件中

日志系統作為一個輔助功能,常常面臨的是第三種情況。而配置文件的格式多種多樣。例如:

windows ini, linux config, json, xml ...

郁悶。log4go 不應當去支持所有的配置文件格式,而是提供接口,讓用戶可以根據自己的主程序的設計需要,自行擴展。

也許應該把 xml 和 json 配置文件支持都以擴展配置文件接口的方式實現,而不是跟 log4go 的主程序捆綁在一起。

文件日志的寫緩沖

已經測試了兩層緩沖寫文件。

第一層是格式化日志記錄,一個單獨的go routine,另一個寫文件,邊格式化記錄邊寫文件,消耗降低了40%。

第二層是用bufio。達到一定的緩沖數量如4k、8k,一次寫文件。消耗降低了80%。

通過判斷Channel中的記錄長度來決定系統何時空閑。當長度為0時,后續沒有新的日志記錄,做一次Flush()。
這種方案簡單。

另外加上 rotate 的優化,效率提高了5倍。

BenchmarkFileLog-4                        200000             10675 ns/op
BenchmarkFileNotLogged-4                20000000               106 ns/op
BenchmarkFileUtilLog-4                    200000             10660 ns/op
BenchmarkFileUtilNotLog-4                5000000               239 ns/op
BenchmarkCacheFileLog-4                  1000000              2191 ns/op
BenchmarkCacheFileNotLogged-4           20000000               106 ns/op
BenchmarkCacheFileUtilLog-4               500000              3680 ns/op
BenchmarkCacheFileUtilNotLog-4           5000000               240 ns/op

Rotate 的改進設想

log4go 自帶 rotate。

linux 系統本來是有 logrotate 的,用 cron 定時執行。非常棒的設計。
簡單說,就是寫日志文件歸寫日志文件,不要去做任何轉儲的判斷。程序員可根據系統的實際運行情況,
自行設置轉儲的時間間隔。轉儲時:

  • 加鎖。使 log4go 暫時停止寫日志,這可能是在 linux 系統中 log4go 沒有使用 logrotate 的原因之一。

  • 當前日志文件進行處理。

  • 解鎖。盡快恢復 log4go,繼續寫日志到當前日志文件。

  • 另開 go routine 對歷史日志文件進行處理。


好吧。暫時就想到這么多了。很多有趣的工作正在進行……

再次感謝 doraemonext@gmail.com 童鞋的好文:log4go 和 logrus 的對比與分析

請關注:

https://github.com/ccpaging/log4go


免責聲明!

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



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