這是一個建立於 的文章,其中的資訊可能已經有所發展或是發生改變。
log4go 的 4.0.2 版本(https://github.com/ccpaging/log4go/tree/4.0.2)發布以後,
看了看別的 go 語言記錄檔設計。發現了一篇好文:
log4go 和 logrus 的對比與分析
https://www.doraemonext.com/archives/783.html
順藤摸瓜,找了一窩關於日誌的設計。連結如下(含老的連結):
- https://github.com/alecthomas/log4go/
這是log4go項目的“鼻祖”
- https://github.com/ngmoco/timber
實現了結構化,寫檔案緩衝,熱配置等。把log4go重構的面目全非
- https://github.com/siddontang/go/tree/master/log
- https://github.com/sirupsen/logrus
- https://github.com/YoungPioneers/blog4go
- https://github.com/YoungPioneers/blog4go-benchmark 各種 go log 的benchmark對比
- 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]*Filterfunc (log Logger) AddFilter(name string, lvl Level, writer LogWriter) Logger { log[name] = NewFilter(lvl, writer) return log}
擴充程式只要做:
NewXXXLogWrite
,初始化擴充要使用的資源。
LogWrite(rec *LogRecord)
,輸出日誌記錄
- 在
Close()
中關閉或釋放資源
- 在應用程式中調用
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 配置。記錄檔的配置有三種方式:
- 在應用程式中配置
- 單獨的設定檔
- 存於主程式設定檔中
日誌系統作為一個協助工具功能,常常面臨的是第三種情況。而設定檔的格式多種多樣。例如:
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/opBenchmarkFileNotLogged-4 20000000 106 ns/opBenchmarkFileUtilLog-4 200000 10660 ns/opBenchmarkFileUtilNotLog-4 5000000 239 ns/opBenchmarkCacheFileLog-4 1000000 2191 ns/opBenchmarkCacheFileNotLogged-4 20000000 106 ns/opBenchmarkCacheFileUtilLog-4 500000 3680 ns/opBenchmarkCacheFileUtilNotLog-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