go的log(一):Golang 標準庫提供的log
Golang的標準庫提供了log的機制,但是該模塊的功能較爲簡單(看似簡單,其實他有他的設計思路)。不過比手寫fmt. Printxxx還是強很多的。至少在輸出的位置做了線程安全的保護。其官方手冊見Golang log 。這裏給出一個簡單使用的例子:
1 2 3 4 5 6 7 | package main import ( "log" ) func main(){ log.Fatal( "Come with fatal,exit with 1 \n" ) } |
編譯運行後,會看到程序打印了 Come with fatal,exit with 1
然後就退出了,如果用 echo $?
查看退出碼,會發現是 “1”。
一般接口
Golang's log模塊主要提供了3類接口。分別是 “Print 、Panic 、Fatal ”。當然是用前先包含log包。
1 2 3 | import ( "log" ) |
爲了方便是用,Golang和Python一樣,在提供接口時,提供一個簡單的包級別的使用接口。不同於Python,其輸出默認定位到標準錯誤 可以通過SetOutput
進行修改。
對每一類接口其提供了3中調用方式,分別是 "Xxxx 、 Xxxxln 、Xxxxf" 比如對於Print就有:
1 2 3 | log.Print log.Printf log.Println |
log.Print :表示其參數的調用方式和
fmt.Print
是類似的,即輸出對象而不用給定特別的標誌符號。log.Printf : 表示其參數的調用方式和
fmt.Printf
是類似的,即可以用C系列的格式化標誌表示輸出對象的類型,具體類型表示 可以參考fmt.Printf的文檔log.Println: 表示其調用方式和
fmt.Println
類似,其和log.Print基本一致,僅僅是在輸出的時候多輸出一個換行
這裏是以 “Print”來具體說明的,對於“Panic”和“Fatal”也是一樣的。下面再以"Print"爲例,看下調用方式:
1 2 3 4 5 6 7 8 9 10 | package main import ( "log" ) func main(){ arr := [] int { 2 , 3 } log.Print( "Print array " ,arr, "\n" ) log.Println( "Println array" ,arr) log.Printf( "Printf array with item [%d,%d]\n" ,arr[ 0 ],arr[ 1 ]) } |
會得到如下結果:
1 2 3 | 2014 / 05 / 02 12 : 27 : 19 Print array [ 2 3 ] 2014 / 05 / 02 12 : 27 : 19 Println array [ 2 3 ] 2014 / 05 / 02 12 : 27 : 19 Printf array with item [ 2 , 3 ] |
輸出中的日期和時間是默認的格式,如果直接調用簡單接口,其格式是固定的,可以通過 SetFlags
方法進行修改,同時這裏輸出 內容的(傳個log.Print的內容)前面和時間的後面是空的,這也是默認的行爲,我們可以通過添加前綴來表示其是一條"Warnning" 或者是一條"Debug"日誌。通過使用 SetPrefix
可以設置該前綴。
SetOutput
SetFlags
SetPrefix
這裏關係不大,先不解釋,留到後面介紹Logger類型中一併解釋。
看完了 log.PrintXxx
接口,我們再來看下 log.FatalXxx
接口,我們以 log.Fatal
爲例介紹其功能。如最開始看到的例子, 在調用 log.Fatal
接口後,會先將日誌內容打印到標準輸出,接着調用系統的 os.exit(1)
接口,退出程序返回狀態爲 “1”
比較複雜的是 log.PanicXxx
,看該函數的說明,其相當於再把日誌內容刷到標準錯誤後調用 panic
函數(不清楚Golan的defer-recover-panic機制可以Golang Blog去學習一下)。這裏舉個常用的例子:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | package main import ( "log" "fmt" ) func main(){ defer func(){ if e:= recover();e!= nil { fmt.Println( "Just comming recover" ) fmt.Println( "e from recover is :" ,e) fmt.Println( "After recover" ) } }() arr := [] int { 2 , 3 } log.Panic( "Print array " ,arr, "\n" ) } |
結果爲:
1 2 3 4 | 2014 / 05 / 03 13 : 52 : 42 Print array [ 2 3 ] Just comming recover e from recover is : Print array [ 2 3 ] After recover |
從結果我們可以看出,是先將日誌刷入標準輸出,然後通過defer裏面的recover進行捕獲panic的內容。
自定義Logger類型
理清了“Print 、Panic 、Fatal ”後我們就好介紹 log.Logger
類型了。該類型提供了一個New方法用來創建對象。
1 | func New(out io.Writer, prefix string, flag int ) *Logger |
其初始化條件分別是日誌寫入的位置 out ,日誌的前綴內容 prefix ,以及日誌的內容flag。可以通過上面介紹的 SetOutput
SetFlags
SetPrefix
依次對其進行設置。
輸出位置out,是一個io.Writer對象,該對象可以是一個文件也可以是實現了該接口的對象。通常我們可以用這個來指定 其輸出到哪個文件
prefix 我們在前面已經看到,就是在日誌內容前面的內容。我們可以將其置爲 "[Info]" 、 "[Warning]"等來幫助區分日誌 級別。
flags 較爲迷惑,其實際上就是一個選項,可選的值有:
Ldate =
1
<< iota
// the date: 2009/01/23 形如 2009/01/23 的日期
Ltime
// the time: 01:23:23 形如 01:23:23 的時間
Lmicroseconds
// microsecond resolution: 01:23:23.123123. 形如01:23:23.123123 的時間
Llongfile
// full file name and line number: /a/b/c/d.go:23 全路徑文件名和行號
Lshortfile
// final file name element and line number: d.go:23. overrides Llongfile 文件名和行號
LstdFlags = Ldate | Ltime
// 日期和時間
表示在日誌內容開頭,我們暫且稱之爲日誌擡頭,打印出相關內容。對於上面的默認格式就是 LstdFlags 打印出日期和時間。
該方法還定義瞭如上一些同名方法。
1 2 3 4 5 6 7 8 9 10 11 12 13 | func (l *Logger) Print(v ... interface {}) func (l *Logger) Printf(format string, v ... interface {}) func (l *Logger) Println(v ... interface {}) func (l *Logger) Fatal(v ... interface {}) func (l *Logger) Fatalf(format string, v ... interface {}) func (l *Logger) Fatalln(v ... interface {}) func (l *Logger) Panic(v ... interface {}) func (l *Logger) Panicf(format string, v ... interface {}) func (l *Logger) Panicln(v ... interface {}) func (l *Logger) Flags() int func (l *Logger) Prefix() string func (l *Logger) SetFlags(flag int ) func (l *Logger) SetPrefix(prefix string) |
其中 “Print 、Panic 、Fatal ” 系列函數和之前介紹的一樣,Flags和Prefix分別可以獲得log.Logger當前的日誌擡頭和前綴。 SetFlags ,SetPrefix 則可以用來設置日誌擡頭和前綴。
使用實例
最後我看有log模塊將debug日誌打印到文件的實例。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 | package main import ( "log" "os" ) func main(){ fileName := "xxx_debug.log" logFile,err := os.Create(fileName) defer logFile.Close() if err != nil { log.Fatalln( "open file error !" ) } debugLog := log.New(logFile, "[Debug]" ,log.Llongfile) debugLog.Println( "A debug message here" ) debugLog.SetPrefix( "[Info]" ) debugLog.Println( "A Info Message here " ) debugLog.SetFlags(debugLog.Flags() | log.LstdFlags) debugLog.Println( "A different prefix" ) } |
運行後打開日誌文件我們可以看到相應的日誌內容