無論是軟件開發(fā)的調(diào)試階段還是軟件上線之后的運行階段肺素,日志一直都是非常重要的一個環(huán)節(jié)恨锚,我們也應該養(yǎng)成在程序中記錄日志的好習慣。
Go 語言內(nèi)置的log
包實現(xiàn)了簡單的日志服務倍靡。本文介紹了標準庫log
的基本使用和第三日志庫的選型和使用猴伶。
1、原生Logger
log 包定義了 Logger 類型,該類型提供了一些格式化輸出的方法他挎。本包也提供了一個預定義的 “標準”logger筝尾,可以通過調(diào)用函數(shù)Print系列
(Print|Printf|Println)、Fatal系列
(Fatal|Fatalf|Fatalln)办桨、和Panic系列
(Panic|Panicf|Panicln)來使用筹淫,比自行創(chuàng)建一個 logger 對象更容易使用。
例如呢撞,我們可以像下面的代碼一樣直接通過log
包來調(diào)用上面提到的方法损姜,默認它們會將日志信息打印到終端界面:
package main
import (
"log"
)
func main() {
log.Println("這是一條很普通的日志。")
v := "很普通的"
log.Printf("這是一條%s日志殊霞。\n", v)
log.Fatalln("這是一條會觸發(fā)fatal的日志摧阅。")
log.Panicln("這是一條會觸發(fā)panic的日志。")
}
編譯并執(zhí)行上面的代碼會得到如下輸出:
2017/06/19 14:04:17 這是一條很普通的日志绷蹲。
2017/06/19 14:04:17 這是一條很普通的日志棒卷。
2017/06/19 14:04:17 這是一條會觸發(fā)fatal的日志。
logger 會打印每條日志信息的日期祝钢、時間比规,默認輸出到系統(tǒng)的標準錯誤。Fatal 系列函數(shù)會在寫入日志信息后調(diào)用 os.Exit(1)太颤。Panic 系列函數(shù)會在寫入日志信息后 panic苞俘。
1.1 配置 logger配置
默認情況下的 logger 只會提供日志的時間信息盹沈,但是很多情況下我們希望得到更多信息龄章,比如記錄該日志的文件名和行號等。log
標準庫中為我們提供了定制這些設置的方法乞封。
log
標準庫中的Flags
函數(shù)會返回標準 logger 的輸出配置做裙,而SetFlags
函數(shù)用來設置標準 logger 的輸出配置。
func Flags() int
func SetFlags(flag int)
1.1.1 flag 選項
log
標準庫提供了如下的 flag 選項肃晚,它們是一系列定義好的常量锚贱。
const (
// 控制輸出日志信息的細節(jié),不能控制輸出的順序和格式关串。
// 輸出的日志在每一項后會有一個冒號分隔:例如2009/01/23 01:23:23.123123 /a/b/c/d.go:23: message
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(會覆蓋掉Llongfile)
LUTC // 使用UTC時間
LstdFlags = Ldate | Ltime // 標準logger的初始值
)
下面我們在記錄日志之前先設置一下標準 logger 的輸出選項如下:
func main() {
log.SetFlags(log.Llongfile | log.Lmicroseconds | log.Ldate)
log.Println("這是一條很普通的日志拧廊。")
}
編譯執(zhí)行后得到的輸出結(jié)果如下:
2017/06/19 14:05:17.494943 .../log_demo/main.go:11: 這是一條很普通的日志。
1.1.2 配置日志前綴
log
標準庫中還提供了關(guān)于日志信息前綴的兩個方法:
func Prefix() string
func SetPrefix(prefix string)
其中Prefix
函數(shù)用來查看標準 logger 的輸出前綴晋修,SetPrefix
函數(shù)用來設置輸出前綴吧碾。
func main() {
log.SetFlags(log.Llongfile | log.Lmicroseconds | log.Ldate)
log.Println("這是一條很普通的日志。")
log.SetPrefix("[小王子]")
log.Println("這是一條很普通的日志墓卦。")
}
上面的代碼輸出如下:
[小王子]2017/06/19 14:05:57.940542 .../log_demo/main.go:13: 這是一條很普通的日志倦春。
這樣我們就能夠在代碼中為我們的日志信息添加指定的前綴,方便之后對日志信息進行檢索和處理。
1.1.3 配置日志輸出位置
func SetOutput(w io.Writer)
SetOutput
函數(shù)用來設置標準 logger 的輸出目的地睁本,默認是標準錯誤輸出尿庐。
例如,下面的代碼會把日志輸出到同目錄下的xx.log
文件中呢堰。
func main() {
logFile, err := os.OpenFile("./xx.log", os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0644)
if err != nil {
fmt.Println("open log file failed, err:", err)
return
}
log.SetOutput(logFile)
log.SetFlags(log.Llongfile | log.Lmicroseconds | log.Ldate)
log.Println("這是一條很普通的日志抄瑟。")
log.SetPrefix("[小王子]")
log.Println("這是一條很普通的日志。")
}
如果你要使用標準的 logger暮胧,我們通常會把上面的配置操作寫到init
函數(shù)中锐借。
func init() {
logFile, err := os.OpenFile("./xx.log", os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0644)
if err != nil {
fmt.Println("open log file failed, err:", err)
return
}
log.SetOutput(logFile)
log.SetFlags(log.Llongfile | log.Lmicroseconds | log.Ldate)
}
1.2 創(chuàng)建 logger
log
標準庫中還提供了一個創(chuàng)建新 logger 對象的構(gòu)造函數(shù)–New
,支持我們創(chuàng)建自己的 logger 示例往衷。New
函數(shù)的簽名如下:
func New(out io.Writer, prefix string, flag int) *Logger
New 創(chuàng)建一個 Logger 對象钞翔。其中,參數(shù) out 設置日志信息寫入的目的地席舍。參數(shù) prefix 會添加到生成的每一條日志前面布轿。參數(shù) flag 定義日志的屬性(時間、文件等等)来颤。
舉個例子:
func main() {
logger := log.New(os.Stdout, "<New>", log.Lshortfile|log.Ldate|log.Ltime)
logger.Println("這是自定義的logger記錄的日志汰扭。")
}
將上面的代碼編譯執(zhí)行之后,得到結(jié)果如下:
<New>2017/06/19 14:06:51 main.go:34: 這是自定義的logger記錄的日志福铅。
1.3 總結(jié)
Go 內(nèi)置的 log 庫功能有限萝毛,例如無法滿足記錄不同級別日志的情況,我們在實際的項目中根據(jù)自己的需要選擇使用第三方的日志庫滑黔,如 logrus笆包、zap 等。
2略荡、第三方日志庫
2.1 日志選型需求整理
- 日志寫入性能
- 日志級別分離庵佣,并且可分離成多個日志文件
- 可讀性與結(jié)構(gòu)化,Json格式或有分隔符汛兜,方便后續(xù)的日志采集巴粪、監(jiān)控等
- 能夠打印基本信息,如調(diào)用文件 / 函數(shù)名和行號粥谬,日志時間等
- 日志書寫友好肛根,支持通過context自動log trace等
- 文件切割,可按小時漏策、天進行日志拆分派哲,或者按文件大小
- 文件定時刪除
- 開源性,與其他開源框架支持較好
- 多輸出 - 同時支持標準輸出哟玷,文件等
2.2 日志比對
2.2.1 功能比對
參考文檔:
搜看的許多日志框架狮辽,最后剩下兩款目前明顯性能比較好的Uber開源的Zap和ZeroLog一也,參考github中開源項目日志引用情況和日志周邊框架支持最終選用Zap。
需求點 | go.uber.org/zap(國內(nèi)一些開源項目見得比較多喉脖、性能也不錯椰苟、推薦) | github.com/rs/zerolog |
---|---|---|
日志寫入性能 | 較高 | 高 |
日志級別分離 | 支持 | 支持 |
可讀性 (Json格式或有分隔符,方便后續(xù)的日志采集树叽、監(jiān)控等) | json格式 | json格式 |
易用性:接入方便舆蝴,書寫方便(格式化),可Hook注入trace_id等 | 自動接入時間题诵、代碼信息洁仗、日志級別,支持Hook | 可支持接入時間性锭、代碼信息赠潦、日志級別等,支持Hook |
文件切割 (可按時間草冈、文件大小日志拆分) | 不支持她奥,可通過lumberjack實現(xiàn) | 支持 |
定時刪除 | 支持 | 支持 |
多輸出 - 同時支持標準輸出,文件等 | 支持 | 支持 |
2.2.3 性能數(shù)據(jù)比對
根據(jù)Uber-go Zap的文檔怎棱,它的性能比類似的結(jié)構(gòu)化日志包更好——也比標準庫更快哩俭。 以下是Zap發(fā)布的基準測試信息
記錄一個靜態(tài)字符串,沒有任何上下文或printf風格的模板:
下一篇我們會來講講高性能日志框架Zap的使用拳恋,以及如何滿足我們對于日志框架豐富的使用需求凡资,我們下期見,Peace??
我是簡凡谬运,一個勵志用最簡單的語言隙赁,描述最復雜問題的新時代農(nóng)民工判帮。求點贊,求關(guān)注凹炸,如果你對此篇文章有什么疑惑耐亏,歡迎在我的微信公眾號中留言,我還可以為你提供以下幫助:
- 幫助建立自己的知識體系
- 互聯(lián)網(wǎng)真實高并發(fā)場景實戰(zhàn)講解
- 不定期分享Golang妇萄、Java相關(guān)業(yè)內(nèi)的經(jīng)典場景實踐
我的博客:https://besthpt.github.io/
我的微信:bestbear666
微信公眾號:"簡凡丶"