Go 源碼學習 - log 庫

log 包實現了一個簡單的日誌功能。

Logger 結構體作爲日誌對象,生成文本行到 io.Writer。每次記錄日誌的操作都生成一行日誌,即便是log.Printf也不用包含\n。結構體中有sync.Metex鎖,保證了在多 goroutine 情況時的順序寫入。

type Logger struct {
	mu     sync.Mutex // 確保原子寫入
	prefix string     // 日誌行首的前綴字符串
	flag   int        // 屬性標誌
	out    io.Writer  // 輸出目標
	buf    []byte     // 字節數組緩衝區,用於加速寫入
}

其中 flags 可選項如下:

const (
	Ldate         = 1 << iota     // 本地時區的日期: 2009/01/23
	Ltime                         // 本地時區的時間: 01:23:23
	Lmicroseconds                 // 毫秒粒度: 01:23:23.123123.  包含了 Ltime.
	Llongfile                     // 完整的路徑和文件名和行號: /a/b/c/d.go:23
	Lshortfile                    // 不包含路徑,僅文件名和行號: d.go:23. overrides Llongfile
	LUTC                          // 使用 UTC 而非本地時區
	LstdFlags     = Ldate | Ltime // 標準 logger 的初始 flag 值
)

New 函數用於創建 logger,三個參數是:輸出目標, 前綴字符串,屬性標誌

func New(out io.Writer, prefix string, flag int) *Logger 

logger 的 SetOutput 方法用於設置輸出目標

func (l *Logger) SetOutput(w io.Writer) 

包中提供了一個標準 logger - std,我們的程序中執行log.Print時執行的就是std.Print

var std = New(os.Stderr, "", LstdFlags)

logger 的 formatHeader 方法將前綴字符串、日期、時間、文件名和行號寫入緩衝區,它會調用itoa函數格式化文本。

func (l *Logger) formatHeader(buf *[]byte, t time.Time, file string, line int)

logger 的 Output 方法打印日誌,它處理流程如下:

func (l *Logger) Output(calldepth int, s string) error
  1. 獲取當前時間
  2. logger 加鎖
  3. 如果 flags 設置了 Lshortfile 或 Llongfile,先解鎖,再調用 runtime.Caller()函數獲取文件名,再加鎖。解鎖加鎖是因爲獲取文件名的動作消耗較大
  4. 清空 logger 的緩衝buf
  5. 調用 formatHeader 方法將時間、文件名、行號寫入緩衝buf
  6. 將日誌內容追加寫入緩衝buf
  7. 如果行尾沒有換行符就添加一個換行符
  8. 將緩衝buf寫入輸出目標

logger 的 Printf、Print、Println 調用 Output 方法打印日誌,Fatal、Fatalf、Fatalln 調用 Output 方法打印日誌後以錯誤碼1退出程序,Panic、Panicf、Panicln 調用 Output 方法打印日誌後觸發panic,panic的內容就是日誌內容。

還有其他一些設置和獲取自定義 logger 或者 std logger 的前綴字符串、屬性標誌和輸出目標的函數和方法。

logger 結構體裏面的 buf 是爲了高效寫入,除此以外,log 包裏面還有一個 itoa 函數,它會高效地把整數按位轉換爲字節數組寫入緩衝buf

func itoa(buf *[]byte, i int, wid int) {
	// Assemble decimal in reverse order.
	var b [20]byte
	bp := len(b) - 1
	for i >= 10 || wid > 1 {
		wid--
		q := i / 10
		b[bp] = byte('0' + i - q*10)
		bp--
		i = q
	}
	// i < 10
	b[bp] = byte('0' + i)
	*buf = append(*buf, b[bp:]...)
}

如果要像 python 的logging 那樣可以設置日誌旋轉的日誌功能可以使用這個包:lumberjack

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