高效簡單的 logx 日誌庫
在後端專案中,日誌系統常用來記錄程式碼執行中的關鍵資訊,如客戶端某次請求處理異常,則需記錄請求引數以便後續分析。
log 標準庫
log 標準庫提供簡潔的日誌記錄方式,如:
func main() { f, _ := os.OpenFile("service.log", os.O_CREATE|os.O_APPEND|os.O_WRONLY, os.ModePerm) log.SetOutput(f) log.Printf("invalid uid: %s", "10086") log.Fatalf("disk usage 100%") }
日誌會被記錄到 service.log
,下邊日誌檔案以此為例:
2019/03/02 11:43:23 invalid uid: 10086 2019/03/02 11:43:23 disk usage full
log 改進
日誌分級:在開發中,日誌往往需要分級,如常見的 6 個日誌等級
FINE// 最低級別,記錄所有日誌 INFO// 收集程式執行的有用資訊 DEBUG// 記錄程式除錯資訊 WARN// 輸出潛在的錯誤 ERROR// 發生的錯誤但不影響程式的執行,用易讀易定位的方式收集好錯誤資訊,方便後期排查 FATAL// 最高級別,發生嚴重錯誤時像 panic 一樣直接退出程式,如 disk full 等錯誤
其中,高級別日誌會同時輸出低級別日誌中,如 WARN 的日誌會同時寫到 DEBUG 日誌,等級寫關係如下:
切割備份:
service.log service.2019-03-02-001.log
logx 日誌庫
原始碼: wuYin/logx ,使用詳見 README.md
日誌格式
可參考 PHP $Exception 的輸出,日誌行最少需包括:
- 寫入時間
- 日誌等級
- 寫日誌的檔案行
- 格式化後的日誌資料
於是將日誌行內容抽象成結構 LogRecord
type LogRecord struct { LevelLevel// 日誌級別 Created time.Time // 記錄時間 Sourcestring// 原始檔位置 Message string// 日誌資訊 }
最後輸出日誌類似於:
[2019/03/01 16:42:20 CST] [FATL] (logx.TestLogger_DebugLog:17) System|disk full
結構關係
日誌可輸出到終端(標準輸出),日誌檔案,甚至是日誌伺服器的 socket 連線。
將寫操作抽象成介面 LogWriter
type LogWriter interface { LogWrite(rec *LogRecord) // 寫日誌 Close()// 日誌寫完畢後的資源清理工作 }
定義終端輸出結構:
type ConsoleLogWriter struct { formatstring writeCh chan *LogRecord }
定義檔案日誌輸出:
type FileLogWriter struct { formatstring fNamestring file*os.File writeCh chan *LogRecord }
核心模組
-
寫資料
如上實現了兩個
LogWriter
介面的 writer,它們都有接收LogRecord
的緩衝 channel,當 channel 中有日誌行資料時,接收寫入到對應的 io.Writer 輸出流中 -
writer 分類
程式可能需要將不同業務的日誌寫入到不同名的檔案,比如介面層寫到 handler.log,服務層寫到 service.log。這裡將每個日誌實體抽象成 Filter
type Filter struct { MinLevel Level LogWriter }
-
集中管理 Filter
filter 是實際寫入日誌的程式,集中管理成 logger
type Logger map[string]*Filter
切割備份
對於檔案型別的日誌,可定義 maxSize 和 maxLine 來限制單個日誌的最大空間和最大行數,因為每次向檔案寫入資料時寫入的位元組數是可知的,所以可監控日誌增長。
當日志空間滿或行數滿後,需要將當前日誌備份成日期-序號的命名,具體備份細節直接看原始碼和註釋即可。
總結
logx 是照著我們內部生產環境使用的日誌庫仿寫的,整體流程不是很複雜,不過也比較完善,夠用。此外,logx 在 FATAL 日誌使程式 panic 後可選 recover 等方面還有改進空間,放到 Todo list 吧,感謝關注。