Golang日志入門(mén)與技術(shù)選型

參考文檔:https://liwenzhou.com/posts/Go/zap/

無(wú)論是軟件開(kāi)發(fā)的調(diào)試階段還是軟件上線之后的運(yùn)行階段,日志一直都是非常重要的一個(gè)環(huán)節(jié),我們也應(yīng)該養(yǎng)成在程序中記錄日志的好習(xí)慣。
Go 語(yǔ)言內(nèi)置的log包實(shí)現(xiàn)了簡(jiǎn)單的日志服務(wù)。本文介紹了標(biāo)準(zhǔn)庫(kù)log的基本使用和第三日志庫(kù)的選型和使用。

1、原生Logger

log 包定義了 Logger 類(lèi)型,該類(lèi)型提供了一些格式化輸出的方法。本包也提供了一個(gè)預(yù)定義的 “標(biāo)準(zhǔn)”logger,可以通過(guò)調(diào)用函數(shù)Print系列(Print|Printf|Println)、Fatal系列(Fatal|Fatalf|Fatalln)、和Panic系列(Panic|Panicf|Panicln)來(lái)使用,比自行創(chuàng)建一個(gè) logger 對(duì)象更容易使用。

例如,我們可以像下面的代碼一樣直接通過(guò)log包來(lái)調(diào)用上面提到的方法,默認(rèn)它們會(huì)將日志信息打印到終端界面:

package main

import (
    "log"
)

func main() {
    log.Println("這是一條很普通的日志。")
    v := "很普通的"
    log.Printf("這是一條%s日志。\n", v)
    log.Fatalln("這是一條會(huì)觸發(fā)fatal的日志。")
    log.Panicln("這是一條會(huì)觸發(fā)panic的日志。")
}

編譯并執(zhí)行上面的代碼會(huì)得到如下輸出:

2017/06/19 14:04:17 這是一條很普通的日志。
2017/06/19 14:04:17 這是一條很普通的日志。
2017/06/19 14:04:17 這是一條會(huì)觸發(fā)fatal的日志。

logger 會(huì)打印每條日志信息的日期、時(shí)間,默認(rèn)輸出到系統(tǒng)的標(biāo)準(zhǔn)錯(cuò)誤。Fatal 系列函數(shù)會(huì)在寫(xiě)入日志信息后調(diào)用 os.Exit(1)。Panic 系列函數(shù)會(huì)在寫(xiě)入日志信息后 panic。

1.1 配置 logger配置

默認(rèn)情況下的 logger 只會(huì)提供日志的時(shí)間信息,但是很多情況下我們希望得到更多信息,比如記錄該日志的文件名和行號(hào)等。log標(biāo)準(zhǔn)庫(kù)中為我們提供了定制這些設(shè)置的方法。
log標(biāo)準(zhǔn)庫(kù)中的Flags函數(shù)會(huì)返回標(biāo)準(zhǔn) logger 的輸出配置,而SetFlags函數(shù)用來(lái)設(shè)置標(biāo)準(zhǔn) logger 的輸出配置。

func Flags() int
func SetFlags(flag int)

1.1.1 flag 選項(xiàng)

log標(biāo)準(zhǔn)庫(kù)提供了如下的 flag 選項(xiàng),它們是一系列定義好的常量。

const (
    // 控制輸出日志信息的細(xì)節(jié),不能控制輸出的順序和格式。
    // 輸出的日志在每一項(xiàng)后會(huì)有一個(gè)冒號(hào)分隔:例如2009/01/23 01:23:23.123123 /a/b/c/d.go:23: message
    Ldate         = 1 << iota     // 日期:2009/01/23
    Ltime                         // 時(shí)間:01:23:23
    Lmicroseconds                 // 微秒級(jí)別的時(shí)間:01:23:23.123123(用于增強(qiáng)Ltime位)
    Llongfile                     // 文件全路徑名+行號(hào): /a/b/c/d.go:23
    Lshortfile                    // 文件名+行號(hào):d.go:23(會(huì)覆蓋掉Llongfile)
    LUTC                          // 使用UTC時(shí)間
    LstdFlags     = Ldate | Ltime // 標(biāo)準(zhǔn)logger的初始值
)

下面我們?cè)谟涗浫罩局跋仍O(shè)置一下標(biāo)準(zhǔn) logger 的輸出選項(xiàng)如下:

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標(biāo)準(zhǔn)庫(kù)中還提供了關(guān)于日志信息前綴的兩個(gè)方法:

func Prefix() string
func SetPrefix(prefix string)

其中Prefix函數(shù)用來(lái)查看標(biāo)準(zhǔn) logger 的輸出前綴,SetPrefix函數(shù)用來(lái)設(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: 這是一條很普通的日志。

這樣我們就能夠在代碼中為我們的日志信息添加指定的前綴,方便之后對(duì)日志信息進(jìn)行檢索和處理。

1.1.3 配置日志輸出位置

func SetOutput(w io.Writer)

SetOutput函數(shù)用來(lái)設(shè)置標(biāo)準(zhǔn) logger 的輸出目的地,默認(rèn)是標(biāo)準(zhǔn)錯(cuò)誤輸出。

例如,下面的代碼會(huì)把日志輸出到同目錄下的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("這是一條很普通的日志。")
}

如果你要使用標(biāo)準(zhǔn)的 logger,我們通常會(huì)把上面的配置操作寫(xiě)到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標(biāo)準(zhǔn)庫(kù)中還提供了一個(gè)創(chuàng)建新 logger 對(duì)象的構(gòu)造函數(shù)–New,支持我們創(chuàng)建自己的 logger 示例。New函數(shù)的簽名如下:

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

New 創(chuàng)建一個(gè) Logger 對(duì)象。其中,參數(shù) out 設(shè)置日志信息寫(xiě)入的目的地。參數(shù) prefix 會(huì)添加到生成的每一條日志前面。參數(shù) flag 定義日志的屬性(時(shí)間、文件等等)。

舉個(gè)例子:

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 庫(kù)功能有限,例如無(wú)法滿足記錄不同級(jí)別日志的情況,我們?cè)趯?shí)際的項(xiàng)目中根據(jù)自己的需要選擇使用第三方的日志庫(kù),如 logrus、zap 等。

2、第三方日志庫(kù)

2.1 日志選型需求整理

  1. 日志寫(xiě)入性能
  2. 日志級(jí)別分離,并且可分離成多個(gè)日志文件
  3. 可讀性與結(jié)構(gòu)化,Json格式或有分隔符,方便后續(xù)的日志采集、監(jiān)控等
  4. 能夠打印基本信息,如調(diào)用文件 / 函數(shù)名和行號(hào),日志時(shí)間等
  5. 日志書(shū)寫(xiě)友好,支持通過(guò)context自動(dòng)log trace等
  6. 文件切割,可按小時(shí)、天進(jìn)行日志拆分,或者按文件大小
  7. 文件定時(shí)刪除
  8. 開(kāi)源性,與其他開(kāi)源框架支持較好
  9. 多輸出 - 同時(shí)支持標(biāo)準(zhǔn)輸出,文件等

2.2 日志比對(duì)

2.2.1 功能比對(duì)

參考文檔:

搜看的許多日志框架,最后剩下兩款目前明顯性能比較好的Uber開(kāi)源的Zap和ZeroLog,參考github中開(kāi)源項(xiàng)目日志引用情況和日志周邊框架支持最終選用Zap。

需求點(diǎn) go.uber.org/zap(國(guó)內(nèi)一些開(kāi)源項(xiàng)目見(jiàn)得比較多、性能也不錯(cuò)、推薦) github.com/rs/zerolog
日志寫(xiě)入性能 較高
日志級(jí)別分離 支持 支持
可讀性 (Json格式或有分隔符,方便后續(xù)的日志采集、監(jiān)控等) json格式 json格式
易用性:接入方便,書(shū)寫(xiě)方便(格式化),可Hook注入trace_id等 自動(dòng)接入時(shí)間、代碼信息、日志級(jí)別,支持Hook 可支持接入時(shí)間、代碼信息、日志級(jí)別等,支持Hook
文件切割 (可按時(shí)間、文件大小日志拆分) 不支持,可通過(guò)lumberjack實(shí)現(xiàn) 支持
定時(shí)刪除 支持 支持
多輸出 - 同時(shí)支持標(biāo)準(zhǔn)輸出,文件等 支持 支持

2.2.3 性能數(shù)據(jù)比對(duì)

根據(jù)Uber-go Zap的文檔,它的性能比類(lèi)似的結(jié)構(gòu)化日志包更好——也比標(biāo)準(zhǔn)庫(kù)更快。 以下是Zap發(fā)布的基準(zhǔn)測(cè)試信息

記錄一條消息和10個(gè)字段:
image.png

記錄一個(gè)靜態(tài)字符串,沒(méi)有任何上下文或printf風(fēng)格的模板:
image.png

下一篇我們會(huì)來(lái)講講高性能日志框架Zap的使用,以及如何滿足我們對(duì)于日志框架豐富的使用需求,我們下期見(jiàn),Peace??

我是簡(jiǎn)凡,一個(gè)勵(lì)志用最簡(jiǎn)單的語(yǔ)言,描述最復(fù)雜問(wèn)題的新時(shí)代農(nóng)民工。求點(diǎn)贊,求關(guān)注,如果你對(duì)此篇文章有什么疑惑,歡迎在我的微信公眾號(hào)中留言,我還可以為你提供以下幫助:

  • 幫助建立自己的知識(shí)體系
  • 互聯(lián)網(wǎng)真實(shí)高并發(fā)場(chǎng)景實(shí)戰(zhàn)講解
  • 不定期分享Golang、Java相關(guān)業(yè)內(nèi)的經(jīng)典場(chǎng)景實(shí)踐

我的博客:https://besthpt.github.io/
我的微信:bestbear666
微信公眾號(hào):"簡(jiǎn)凡丶"

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容