golang高性能日志庫(kù)zap的使用

本文作者:陳進(jìn)堅(jiān)
個(gè)人博客:https://jian1098.github.io
CSDN博客:https://blog.csdn.net/c_jian
簡(jiǎn)書(shū):http://www.itdecent.cn/u/8ba9ac5706b6
聯(lián)系方式:jian1098@qq.com

摘要

zapUber開(kāi)發(fā)的非??斓摹⒔Y(jié)構(gòu)化的,分日志級(jí)別的Go日志庫(kù)。根據(jù)Uber-go Zap的文檔,它的性能比類(lèi)似的結(jié)構(gòu)化日志包更好,也比標(biāo)準(zhǔn)庫(kù)更快。具體的性能測(cè)試可以去github上看到。

github地址:https://github.com/uber-go/zap

創(chuàng)建實(shí)例

通過(guò)調(diào)用zap.NewProduction()/zap.NewDevelopment()或者zap.Example()創(chuàng)建一個(gè)Logger。這三個(gè)方法的區(qū)別在于它將記錄的信息不同,參數(shù)只能是string類(lèi)型

//代碼
var log *zap.Logger
log = zap.NewExample()
log, _ := zap.NewDevelopment()
log, _ := zap.NewProduction()
log.Debug("This is a DEBUG message")
log.Info("This is an INFO message")

//Example 輸出
{"level":"debug","msg":"This is a DEBUG message"}
{"level":"info","msg":"This is an INFO message"}

//Development 輸出
2018-10-30T17:14:22.459+0800    DEBUG    development/main.go:7    This is a DEBUG message
2018-10-30T17:14:22.459+0800    INFO    development/main.go:8    This is an INFO message

//Production 輸出
{"level":"info","ts":1540891173.3190675,"caller":"production/main.go:8","msg":"This is an INFO message"}
{"level":"info","ts":1540891173.3191047,"caller":"production/main.go:9","msg":"This is an INFO message with fields","region":["us-west"],"id":2}

三種創(chuàng)建方式對(duì)比:

  • ExampleProduction使用的是json格式輸出,Development使用行的形式輸出
  • Development
    • 從警告級(jí)別向上打印到堆棧中來(lái)跟蹤
    • 始終打印包/文件/行(方法)
    • 在行尾添加任何額外字段作為json字符串
    • 以大寫(xiě)形式打印級(jí)別名稱(chēng)
    • 以毫秒為單位打印ISO8601格式的時(shí)間戳
  • Production
    • 調(diào)試級(jí)別消息不記錄
    • Error,Dpanic級(jí)別的記錄,會(huì)在堆棧中跟蹤文件,Warn不會(huì)
    • 始終將調(diào)用者添加到文件中
    • 以時(shí)間戳格式打印日期
    • 以小寫(xiě)形式打印級(jí)別名稱(chēng)

格式化輸出

zap有兩種類(lèi)型,分別是*zap.Logger*zap.SugaredLogger,它們惟一的區(qū)別是,我們通過(guò)調(diào)用主logger的. Sugar()方法來(lái)獲取一個(gè)SugaredLogger,然后使用SugaredLoggerprintf格式記錄語(yǔ)句,例如

var sugarLogger *zap.SugaredLogger

func InitLogger() {
  logger, _ := zap.NewProduction()
    sugarLogger = logger.Sugar()
}

func main() {
    InitLogger()
    defer sugarLogger.Sync()
    sugarLogger.Errorf("Error fetching URL %s : Error = %s", url, err)
}

寫(xiě)入文件

默認(rèn)情況下日志都會(huì)打印到應(yīng)用程序的console界面,但是為了方便查詢,可以將日志寫(xiě)入文件,但是我們不能再使用前面創(chuàng)建實(shí)例的3個(gè)方法,而是使用zap.New()

package main

import (
    "go.uber.org/zap"
    "go.uber.org/zap/zapcore"
    "os"
)

var log *zap.Logger
func main() {
    writeSyncer, _ := os.Create("./info.log")                           //日志文件存放目錄
    encoderConfig := zap.NewProductionEncoderConfig()                    //指定時(shí)間格式
    encoderConfig.EncodeTime = zapcore.ISO8601TimeEncoder
    encoderConfig.EncodeLevel = zapcore.CapitalLevelEncoder
    encoder := zapcore.NewConsoleEncoder(encoderConfig)                 //獲取編碼器,NewJSONEncoder()輸出json格式,NewConsoleEncoder()輸出普通文本格式
    core := zapcore.NewCore(encoder, writeSyncer, zapcore.DebugLevel)    //第三個(gè)及之后的參數(shù)為寫(xiě)入文件的日志級(jí)別,ErrorLevel模式只記錄error級(jí)別的日志
    log = zap.New(core,zap.AddCaller())                                //AddCaller()為顯示文件名和行號(hào)
    log.Info("hello world")
    log.Error("hello world")
}

日志文件輸出結(jié)果:

2020-12-16T17:53:30.466+0800    INFO    geth/main.go:18 hello world
2020-12-16T17:53:30.486+0800    ERROR   geth/main.go:19 hello world

同時(shí)輸出控制臺(tái)和文件

如果需要同時(shí)輸出控制臺(tái)和文件,只需要改造一下zapcore.NewCore即可,示例:

package main

import (
    "github.com/natefinch/lumberjack"
    "go.uber.org/zap"
    "go.uber.org/zap/zapcore"
    "os"
)

var log *zap.Logger

func main() {
    //獲取編碼器,NewJSONEncoder()輸出json格式,NewConsoleEncoder()輸出普通文本格式
    encoderConfig := zap.NewProductionEncoderConfig()
    encoderConfig.EncodeTime = zapcore.ISO8601TimeEncoder //指定時(shí)間格式
    encoderConfig.EncodeLevel = zapcore.CapitalLevelEncoder
    encoder := zapcore.NewConsoleEncoder(encoderConfig)

    //文件writeSyncer
    fileWriteSyncer := zapcore.AddSync(&lumberjack.Logger{
        Filename:   "./info.log", //日志文件存放目錄
        MaxSize:    1,            //文件大小限制,單位MB
        MaxBackups: 5,            //最大保留日志文件數(shù)量
        MaxAge:     30,           //日志文件保留天數(shù)
        Compress:   false,        //是否壓縮處理
    })
    fileCore := zapcore.NewCore(encoder, zapcore.NewMultiWriteSyncer(fileWriteSyncer,zapcore.AddSync(os.Stdout)), zapcore.DebugLevel) //第三個(gè)及之后的參數(shù)為寫(xiě)入文件的日志級(jí)別,ErrorLevel模式只記錄error級(jí)別的日志

    log = zap.New(fileCore, zap.AddCaller()) //AddCaller()為顯示文件名和行號(hào)


    log.Info("hello world")
    log.Error("hello world")
}

文件切割

日志文件會(huì)隨時(shí)間越來(lái)越大,為了避免日志文件把硬盤(pán)空間占滿,需要按條件對(duì)日志文件進(jìn)行切割,zap包本身不提供文件切割的功能,但是可以用zap官方推薦的lumberjack包處理

    //文件writeSyncer
    fileWriteSyncer := zapcore.AddSync(&lumberjack.Logger{
        Filename:   "./info.log", //日志文件存放目錄,如果文件夾不存在會(huì)自動(dòng)創(chuàng)建
        MaxSize:    1,            //文件大小限制,單位MB
        MaxBackups: 5,            //最大保留日志文件數(shù)量
        MaxAge:     30,           //日志文件保留天數(shù)
        Compress:   false,        //是否壓縮處理
    })

按級(jí)別寫(xiě)入文件

為了管理人員的查詢方便,一般我們需要將低于error級(jí)別的放到info.log,error及以上嚴(yán)重級(jí)別日志存放到error.log文件中,我們只需要改造一下zapcore.NewCore方法的第3個(gè)參數(shù),然后將文件WriteSyncer拆成infoerror兩個(gè)即可,示例:

package main

import (
    "github.com/natefinch/lumberjack"
    "go.uber.org/zap"
    "go.uber.org/zap/zapcore"
    "os"
)

var log *zap.Logger

func main() {
    var coreArr []zapcore.Core

    //獲取編碼器
    encoderConfig := zap.NewProductionEncoderConfig()               //NewJSONEncoder()輸出json格式,NewConsoleEncoder()輸出普通文本格式
    encoderConfig.EncodeTime = zapcore.ISO8601TimeEncoder           //指定時(shí)間格式
    encoderConfig.EncodeLevel = zapcore.CapitalColorLevelEncoder    //按級(jí)別顯示不同顏色,不需要的話取值z(mì)apcore.CapitalLevelEncoder就可以了
    //encoderConfig.EncodeCaller = zapcore.FullCallerEncoder        //顯示完整文件路徑
    encoder := zapcore.NewConsoleEncoder(encoderConfig)

    //日志級(jí)別
    highPriority := zap.LevelEnablerFunc(func(lev zapcore.Level) bool{  //error級(jí)別
        return lev >= zap.ErrorLevel
    })
    lowPriority := zap.LevelEnablerFunc(func(lev zapcore.Level) bool {  //info和debug級(jí)別,debug級(jí)別是最低的
        return lev < zap.ErrorLevel && lev >= zap.DebugLevel
    })

    //info文件writeSyncer
    infoFileWriteSyncer := zapcore.AddSync(&lumberjack.Logger{
        Filename:   "./log/info.log",   //日志文件存放目錄,如果文件夾不存在會(huì)自動(dòng)創(chuàng)建
        MaxSize:    1,                  //文件大小限制,單位MB
        MaxBackups: 5,                  //最大保留日志文件數(shù)量
        MaxAge:     30,                 //日志文件保留天數(shù)
        Compress:   false,              //是否壓縮處理
    })
    infoFileCore := zapcore.NewCore(encoder, zapcore.NewMultiWriteSyncer(infoFileWriteSyncer,zapcore.AddSync(os.Stdout)), lowPriority) //第三個(gè)及之后的參數(shù)為寫(xiě)入文件的日志級(jí)別,ErrorLevel模式只記錄error級(jí)別的日志
    //error文件writeSyncer
    errorFileWriteSyncer := zapcore.AddSync(&lumberjack.Logger{
        Filename:   "./log/error.log",      //日志文件存放目錄
        MaxSize:    1,                      //文件大小限制,單位MB
        MaxBackups: 5,                      //最大保留日志文件數(shù)量
        MaxAge:     30,                     //日志文件保留天數(shù)
        Compress:   false,                  //是否壓縮處理
    })
    errorFileCore := zapcore.NewCore(encoder, zapcore.NewMultiWriteSyncer(errorFileWriteSyncer,zapcore.AddSync(os.Stdout)), highPriority) //第三個(gè)及之后的參數(shù)為寫(xiě)入文件的日志級(jí)別,ErrorLevel模式只記錄error級(jí)別的日志

    coreArr = append(coreArr, infoFileCore)
    coreArr = append(coreArr, errorFileCore)
    log = zap.New(zapcore.NewTee(coreArr...), zap.AddCaller()) //zap.AddCaller()為顯示文件名和行號(hào),可省略

    log.Info("hello info")
    log.Debug("hello debug")
    log.Error("hello error")
}

這樣修改之后,infodebug級(jí)別的日志就存放到info.log,error級(jí)別的日志單獨(dú)放到error.log文件中了

控制臺(tái)按級(jí)別顯示顏色

指定編碼器的EncodeLevel即可,

    //獲取編碼器
    encoderConfig := zap.NewProductionEncoderConfig()               //NewJSONEncoder()輸出json格式,NewConsoleEncoder()輸出普通文本格式
    encoderConfig.EncodeTime = zapcore.ISO8601TimeEncoder           //指定時(shí)間格式
    encoderConfig.EncodeLevel = zapcore.CapitalColorLevelEncoder    //按級(jí)別顯示不同顏色,不需要的話取值z(mì)apcore.CapitalLevelEncoder就可以了
    encoder := zapcore.NewConsoleEncoder(encoderConfig)

顯示文件路徑和行號(hào)

前面說(shuō)到要顯示文件路徑和行號(hào),只需要zap.New方法添加參數(shù)zap.AddCaller()即可,如果要顯示完整的路徑,需要在編碼器配置中指定

    //獲取編碼器
    encoderConfig := zap.NewProductionEncoderConfig()               //NewJSONEncoder()輸出json格式,NewConsoleEncoder()輸出普通文本格式
    encoderConfig.EncodeTime = zapcore.ISO8601TimeEncoder           //指定時(shí)間格式
    encoderConfig.EncodeLevel = zapcore.CapitalColorLevelEncoder    //按級(jí)別顯示不同顏色,不需要的話取值z(mì)apcore.CapitalLevelEncoder就可以了
    encoderConfig.EncodeCaller = zapcore.FullCallerEncoder          //顯示完整文件路徑
    encoder := zapcore.NewConsoleEncoder(encoderConfig)

完整代碼

package main

import (
    "github.com/natefinch/lumberjack"
    "go.uber.org/zap"
    "go.uber.org/zap/zapcore"
    "os"
)

var log *zap.Logger

func main() {
    var coreArr []zapcore.Core

    //獲取編碼器
    encoderConfig := zap.NewProductionEncoderConfig()               //NewJSONEncoder()輸出json格式,NewConsoleEncoder()輸出普通文本格式
    encoderConfig.EncodeTime = zapcore.ISO8601TimeEncoder           //指定時(shí)間格式
    encoderConfig.EncodeLevel = zapcore.CapitalColorLevelEncoder    //按級(jí)別顯示不同顏色,不需要的話取值z(mì)apcore.CapitalLevelEncoder就可以了
    //encoderConfig.EncodeCaller = zapcore.FullCallerEncoder        //顯示完整文件路徑
    encoder := zapcore.NewConsoleEncoder(encoderConfig)

    //日志級(jí)別
    highPriority := zap.LevelEnablerFunc(func(lev zapcore.Level) bool{  //error級(jí)別
        return lev >= zap.ErrorLevel
    })
    lowPriority := zap.LevelEnablerFunc(func(lev zapcore.Level) bool {  //info和debug級(jí)別,debug級(jí)別是最低的
        return lev < zap.ErrorLevel && lev >= zap.DebugLevel
    })

    //info文件writeSyncer
    infoFileWriteSyncer := zapcore.AddSync(&lumberjack.Logger{
        Filename:   "./log/info.log",   //日志文件存放目錄,如果文件夾不存在會(huì)自動(dòng)創(chuàng)建
        MaxSize:    2,                  //文件大小限制,單位MB
        MaxBackups: 100,                //最大保留日志文件數(shù)量
        MaxAge:     30,                 //日志文件保留天數(shù)
        Compress:   false,              //是否壓縮處理
    })
    infoFileCore := zapcore.NewCore(encoder, zapcore.NewMultiWriteSyncer(infoFileWriteSyncer,zapcore.AddSync(os.Stdout)), lowPriority) //第三個(gè)及之后的參數(shù)為寫(xiě)入文件的日志級(jí)別,ErrorLevel模式只記錄error級(jí)別的日志
    //error文件writeSyncer
    errorFileWriteSyncer := zapcore.AddSync(&lumberjack.Logger{
        Filename:   "./log/error.log",      //日志文件存放目錄
        MaxSize:    1,                      //文件大小限制,單位MB
        MaxBackups: 5,                      //最大保留日志文件數(shù)量
        MaxAge:     30,                     //日志文件保留天數(shù)
        Compress:   false,                  //是否壓縮處理
    })
    errorFileCore := zapcore.NewCore(encoder, zapcore.NewMultiWriteSyncer(errorFileWriteSyncer,zapcore.AddSync(os.Stdout)), highPriority) //第三個(gè)及之后的參數(shù)為寫(xiě)入文件的日志級(jí)別,ErrorLevel模式只記錄error級(jí)別的日志

    coreArr = append(coreArr, infoFileCore)
    coreArr = append(coreArr, errorFileCore)
    log = zap.New(zapcore.NewTee(coreArr...), zap.AddCaller()) //zap.AddCaller()為顯示文件名和行號(hào),可省略


    log.Info("hello info")
    log.Debug("hello debug")
    log.Error("hello error")
}

參考文章

在Go語(yǔ)言項(xiàng)目中使用Zap日志庫(kù) - 知乎 (zhihu.com)

golang高性能日志庫(kù)zap配置示例 - ExplorerMan - 博客園 (cnblogs.com)

?著作權(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)容