GO語言zap日志庫理解和使用方法示例
1. zap日志庫介紹
目前我所了解的日志庫有g(shù)o內(nèi)置的默認(rèn)日志標(biāo)準(zhǔn)庫,這個(gè)日志包簡(jiǎn)單,無需額外的依賴,無需安裝,缺乏日志格式化能力,無法切割日志,無法日志分級(jí),適合小項(xiàng)目,對(duì)日志功能要求不高的場(chǎng)景;
然后就是logrus第三方日志庫,支持日志分級(jí),支持結(jié)構(gòu)化日志,支持鉤子機(jī)制,但性能一般,適合不對(duì)性能要求的中小型項(xiàng)目;
最后就是zap日志庫,相比于logrus有極致的性能,同樣結(jié)構(gòu)化日志,類型安全,靈活配置,但學(xué)習(xí)成本高,配置復(fù)雜,適合高性能要求的生產(chǎn)環(huán)境。
2.安裝zap庫
go get -u go.uber.org/zap
3.配置日志記錄器
Zap提供了兩種類型的日志記錄器:Suared Logger和Logger。
在性能很好但不是很關(guān)鍵·1的上下文中,使用SugaredLogger。它比其他結(jié)構(gòu)化日志記錄包快4-10倍,并且支持結(jié)構(gòu)化和printf風(fēng)格的日志記錄。
在每一微秒和每一次內(nèi)存分配都很重要的上下文中,使用Logger。它比SugaredLogger更快,內(nèi)存分配次數(shù)也更少,但它只支持強(qiáng)類型的結(jié)構(gòu)化日志記錄。
3.1 Logger
- 通過調(diào)用
zap.NewProduction()/zap.NewDevelopment()或者zap.Example()創(chuàng)建一個(gè)Logger。- 上面的每一個(gè)函數(shù)都將創(chuàng)建一個(gè)logger。唯一的區(qū)別在于它將記錄的信息不同。例如production logger默認(rèn)記錄調(diào)用函數(shù)信息、日期和時(shí)間等。
- 通過Logger調(diào)用Info/Error等。
- 默認(rèn)情況下日志都會(huì)打印到應(yīng)用程序的console界面。
package main
import (
"go.uber.org/zap"
"net/http"
)
var logger *zap.Logger //聲明全局的zap日志記錄器變量
func main() {
InitLogger()
defer logger.Sync() //程序退出前刷新日志緩沖區(qū),確保緩沖區(qū)中的信息刷新到輸出
simpleHttpGet("https://www.baidu.com") //自定義函數(shù),用于發(fā)送HTTP請(qǐng)求
}
// 日志初始化函數(shù)
func InitLogger() {
//創(chuàng)建生產(chǎn)環(huán)境級(jí)別的日志器
//默認(rèn)輸出 JSON 格式的日志,包含時(shí)間戳、日志級(jí)別、調(diào)用位置等信息
logger, _ = zap.NewProduction()
}
// HTTP請(qǐng)求函數(shù)
func simpleHttpGet(url string) {
//發(fā)送HTTP GET請(qǐng)求
//返回值是請(qǐng)求響應(yīng)數(shù)據(jù)和錯(cuò)誤信息
resp, err := http.Get(url)
if err != nil {
//如果請(qǐng)求出錯(cuò),記錄錯(cuò)誤日志
logger.Error("http get error", //錯(cuò)誤描述
zap.String("url", url), //記錄錯(cuò)誤的URL
zap.Error(err)) //記錄錯(cuò)誤的信息
} else {
//如果請(qǐng)求成功,記錄信息日志
logger.Info("success",
zap.String("statusCode", resp.Status), //響應(yīng)狀態(tài)碼
zap.String("url", url)) //請(qǐng)求的URL
err := resp.Body.Close() //關(guān)閉響應(yīng)體
if err != nil {
// 如果關(guān)閉響應(yīng)體出錯(cuò),記錄錯(cuò)誤日志
logger.Error("body read error")
}
}
}
這段代碼是使用Go語言編寫的簡(jiǎn)單HTTP請(qǐng)求客戶端程序,主要功能是向指定的URL發(fā)送HTTP GET 請(qǐng)求,并使用zap日志庫記錄請(qǐng)求過程中的關(guān)鍵信息。
3.2 Sugared Logger
大部分實(shí)現(xiàn)與Logger相同,唯一的區(qū)別是我們通過調(diào)用logger的Sugar()方法來獲取一個(gè)SugaredLogger,然后使用SugaredLogger以printf格式記錄語句。下面是用SugaredLogger日志記錄器實(shí)現(xiàn)上述Logger記錄器相同功能的代碼:
// 聲明一個(gè)全局日志器變量
// SugaredLogger支持格式化字符串,使用時(shí)更接近打印日志習(xí)慣
// 比基礎(chǔ)的 zap.Logger 更易用(但性能略低)
var sugarLogger *zap.SugaredLogger
func main() {
InitLoger() //初始化日志器
defer sugarLogger.Sync() //程序退出前刷新日志緩沖區(qū)
simpleHttpGet("https://www.baidu.com")
}
func InitLoger() {
logger, _ := zap.NewProduction() //創(chuàng)建生產(chǎn)環(huán)境日志器(JSON格式,包含元數(shù)據(jù))
sugarLogger = logger.Sugar() //轉(zhuǎn)換為SugaredLogger,支持格式化日志
}
func simpleHttpGet(url string) {
//記錄調(diào)試日志:表示開始嘗試發(fā)送請(qǐng)求
sugarLogger.Debugf("Tring to GET %s", url)
//發(fā)送HTTP請(qǐng)求
resp, err := http.Get(url)
if err != nil {
sugarLogger.Errorf("Failed to GET %s: %s", url, err)
} else {
sugarLogger.Infof("Successfully GET %s", url)
resp.Body.Close()
//sugarLogger.Errorf("Failed to GET %s: %s", url, err)
//后面不需要執(zhí)行 resp.Body.Close(),核心原因是:當(dāng) http.Get(url)
//返回錯(cuò)誤時(shí),resp 變量可能為 nil(空值),
//此時(shí)調(diào)用 resp.Body.Close() 會(huì)導(dǎo)致程序 panic(崩潰)
}
}在zap日志庫中的Logger和SugaredLogger時(shí)基礎(chǔ)與封裝的關(guān)系,兩者本質(zhì)上共享一套日志核心功能,
logger(zap.Logger)是 zap 的 基礎(chǔ)核心,專注于性能和類型安全;sugarLogger(zap.SugaredLogger)是其 便捷封裝,專注于開發(fā)效率。
4. 定制logger
4.1 將日志寫入文件
我們將使用zap.New(...)方法來手動(dòng)傳遞所有配置,而不是使用像zap.NewPriduction()這樣的預(yù)置方法來創(chuàng)建logger
func New(core zapcore.Core, options ...Option) *Logger
zapcore.Core需要三個(gè)配置:Encoder,WriteSyncer,LogLevel。
1. Encoder:編碼器(如何寫入日志)。我們將使用開箱即用的NewJSONEncoder(),并使用預(yù)先設(shè)置的ProductionEncoderConfig()。

2. WriteSyncer:指定日志將寫到哪里去。我們使用zapcore.AddSync()函數(shù)并且將打開的文件句柄傳進(jìn)去。

3. Log Level:哪種級(jí)別的日志將被寫入
// 聲明一個(gè)全局日志器變量
// SugaredLogger支持格式化字符串,使用時(shí)更接近打印日志習(xí)慣
// 比基礎(chǔ)的 zap.Logger 更易用(但性能略低)
var sugarLogger *zap.SugaredLogger
func main() {
InitLoger() //初始化日志器
defer sugarLogger.Sync() //程序退出前刷新日志緩沖區(qū)
simpleHttpGet("https://www.baidu.com")
}
func InitLoger() {
writeSyncer := getLogWriter() //獲取輸出目標(biāo)
encoder := getEncoder() //獲取日志格式編碼器(JSON格式)
//創(chuàng)建日志核心組件,關(guān)聯(lián)編碼器,輸出目標(biāo)和日志級(jí)別
core := zapcore.NewCore(encoder, writeSyncer, zapcore.DebugLevel)
logger := zap.New(core) //基于核心組件創(chuàng)建基礎(chǔ)日志器
sugarLogger = logger.Sugar() //轉(zhuǎn)換為SugaredLogger,支持格式化日志
}
func getEncoder() zapcore.Encoder {
// 使用生產(chǎn)環(huán)境的編碼器配置,返回 JSON 格式編碼器
return zapcore.NewJSONEncoder(zap.NewProductionEncoderConfig())
}
func getLogWriter() zapcore.WriteSyncer {
file, _ := os.Create("./zap.log")
return zapcore.AddSync(file) //將文件轉(zhuǎn)換為zap支持的同步寫入器
}
func simpleHttpGet(url string) {
//記錄調(diào)試日志:表示開始嘗試發(fā)送請(qǐng)求
sugarLogger.Debugf("Tring to GET %s", url)
//發(fā)送HTTP請(qǐng)求
resp, err := http.Get(url)
if err != nil {
sugarLogger.Errorf("Failed to GET %s: %s", url, err)
} else {
sugarLogger.Infof("Successfully GET %s", url)
resp.Body.Close()
//sugarLogger.Errorf("Failed to GET %s: %s", url, err)
//后面不需要執(zhí)行 resp.Body.Close(),核心原因是:當(dāng) http.Get(url)
//返回錯(cuò)誤時(shí),resp 變量可能為 nil(空值),
//此時(shí)調(diào)用 resp.Body.Close() 會(huì)導(dǎo)致程序 panic(崩潰)
}
}
4.2 將JSON Encoder更改為普通的Log Encoder
我們希望將編碼器從JSON Encoder更改為普通Encoder。為此,我們需要將NewJSONEncoder()更改為NewConsoleEncoder()。
return zapcore.NewConsoleEncoder(zap.NewProductionEncoderConfig())
然后打印結(jié)果(就不是JSON格式了):

4.3 更改時(shí)間編碼并添加調(diào)用者詳細(xì)信息
鑒于我們對(duì)配置所做的更改,有下面兩個(gè)問題:
- 時(shí)間是以非人類可讀的方式展示,例如1.572161051846623e+09
- 調(diào)用方函數(shù)的詳細(xì)信息沒有顯示在日志中
我們要做的第一件事是覆蓋默認(rèn)的ProductionConfig(),并進(jìn)行以下更改:
- 修改時(shí)間編碼器
- 在日志文件中使用大寫字母記錄日志級(jí)別
func getEncoder() zapcore.Encoder {
encoderConfig := zap.NewProductionEncoderConfig()
encoderConfig.EncodeTime = zapcore.ISO8601TimeEncoder
encoderConfig.EncodeLevel = zapcore.CapitalLevelEncoder
return zapcore.NewConsoleEncoder(encoderConfig)
}接下來,我們將修改zap logger代碼,添加將調(diào)用函數(shù)信息記錄到日志中的功能。為此,我們將在zap.New(..)函數(shù)中添加一個(gè)Option。
logger := zap.New(core, zap.AddCaller())
4.4 將日志輸出到多個(gè)位置
var sugarLogger *zap.SugaredLogger
func main() {
InitLoger() //初始化日志器
defer sugarLogger.Sync() //程序退出前刷新日志緩沖區(qū)
simpleHttpGet("https://www.baidu.com")
}
func InitLoger() {
encoder := getEncoder() //獲取日志格式編碼器(JSON格式)
core := getLogWriter(encoder) //獲取輸出目標(biāo)
//創(chuàng)建日志核心組件,關(guān)聯(lián)編碼器,輸出目標(biāo)和日志級(jí)別
//core := zapcore.NewCore(encoder, writeSyncer, zapcore.DebugLevel)
logger := zap.New(core, zap.AddCaller()) //基于核心組件創(chuàng)建基礎(chǔ)日志器
sugarLogger = logger.Sugar() //轉(zhuǎn)換為SugaredLogger,支持格式化日志
}
func getEncoder() zapcore.Encoder {
encoderConfig := zap.NewProductionEncoderConfig()
encoderConfig.EncodeTime = zapcore.ISO8601TimeEncoder
encoderConfig.EncodeLevel = zapcore.CapitalLevelEncoder
return zapcore.NewJSONEncoder(encoderConfig)
}
func getLogWriter(encoder zapcore.Encoder) zapcore.Core {
file01, _ := os.Create("./zap.log")
c1 := zapcore.NewCore(encoder, zapcore.AddSync(file01), zapcore.DebugLevel)
file02, _ := os.Create("./zap_error.log")
c2 := zapcore.NewCore(encoder, zapcore.AddSync(file02), zapcore.ErrorLevel)
return zapcore.NewTee(c1, c2)
}
func simpleHttpGet(url string) {
//記錄調(diào)試日志:表示開始嘗試發(fā)送請(qǐng)求
sugarLogger.Debugf("Tring to GET %s", url)
//發(fā)送HTTP請(qǐng)求
resp, err := http.Get(url)
if err != nil {
sugarLogger.Errorf("Failed to GET %s: %s", url, err)
} else {
sugarLogger.Infof("Successfully GET %s", url)
resp.Body.Close()
//sugarLogger.Errorf("Failed to GET %s: %s", url, err)
//后面不需要執(zhí)行 resp.Body.Close(),核心原因是:當(dāng) http.Get(url)
//返回錯(cuò)誤時(shí),resp 變量可能為 nil(空值),
//此時(shí)調(diào)用 resp.Body.Close() 會(huì)導(dǎo)致程序 panic(崩潰)
}
}
5. 使用Lumberjack進(jìn)行日志切割文檔
5.1 安裝
go get gopkg.in/natefinch/lumberjack.v2
5.2 zap logger種加入Lumberjack
要在zap中加入Lumberjack支持,我們需要修改WriteSyncer代碼。我們將按照下面的代碼修改getLogWriter()函數(shù):
var sugarLogger *zap.SugaredLogger
func main() {
InitLoger() //初始化日志器
defer sugarLogger.Sync() //程序退出前刷新日志緩沖區(qū)
simpleHttpGet("https://www.baidu.com")
}
func InitLoger() {
writeSyncer := getLogWriter() //獲取輸出目標(biāo)
encoder := getEncoder() //獲取日志格式編碼器(JSON格式)
//創(chuàng)建日志核心組件,關(guān)聯(lián)編碼器,輸出目標(biāo)和日志級(jí)別
core := zapcore.NewCore(encoder, writeSyncer, zapcore.DebugLevel)
logger := zap.New(core) //基于核心組件創(chuàng)建基礎(chǔ)日志器
sugarLogger = logger.Sugar() //轉(zhuǎn)換為SugaredLogger,支持格式化日志
}
func getEncoder() zapcore.Encoder {
// 使用生產(chǎn)環(huán)境的編碼器配置,返回 JSON 格式編碼器
return zapcore.NewJSONEncoder(zap.NewProductionEncoderConfig())
}
func getLogWriter() zapcore.WriteSyncer {
lumberjackLogger := &lumberjack.Logger{
Filename: "./log/sugar.log",
MaxSize: 10,
MaxBackups: 10,
MaxAge: 30,
Compress: true,
}
return zapcore.AddSync(lumberjackLogger) //將文件轉(zhuǎn)換為zap支持的同步寫入器
}
func simpleHttpGet(url string) {
//記錄調(diào)試日志:表示開始嘗試發(fā)送請(qǐng)求
sugarLogger.Debugf("Tring to GET %s", url)
//發(fā)送HTTP請(qǐng)求
resp, err := http.Get(url)
if err != nil {
sugarLogger.Errorf("Failed to GET %s: %s", url, err)
} else {
sugarLogger.Infof("Successfully GET %s", url)
resp.Body.Close()
//sugarLogger.Errorf("Failed to GET %s: %s", url, err)
//后面不需要執(zhí)行 resp.Body.Close(),核心原因是:當(dāng) http.Get(url)
//返回錯(cuò)誤時(shí),resp 變量可能為 nil(空值),
//此時(shí)調(diào)用 resp.Body.Close() 會(huì)導(dǎo)致程序 panic(崩潰)
}
}
6. 日志中間件實(shí)現(xiàn)
// 聲明一個(gè)日志記錄器實(shí)例
var sugarLogger *zap.SugaredLogger
// 中間件函數(shù)
func Logger() gin.HandlerFunc {
InitLogger()
return func(c *gin.Context) {
//在請(qǐng)求處理前,記錄一條Debug級(jí)別的日志,包含請(qǐng)求的URL路徑
sugarLogger.Debugf("Trying to access logger %s", c.Request.URL.Path)
c.Next() //執(zhí)行后續(xù)中間件和路由處理函數(shù)
statusCode := c.Writer.Status() //獲取響應(yīng)狀態(tài)碼
if len(c.Errors) > 0 {
sugarLogger.Error(c.Errors.String())
}
if statusCode >= 400 {
sugarLogger.Warnf("客戶端警告: %d", statusCode)
} else if statusCode >= 500 {
sugarLogger.Errorf("服務(wù)器錯(cuò)誤: %d", statusCode)
} else {
sugarLogger.Infof("請(qǐng)求正常: %d", statusCode)
}
}
}
// 初始化日志器
func InitLogger() {
encoder := getEncoder() //獲取編碼器(定義日志格式)
writeSyncer := getLogWriter() //獲取輸出目標(biāo)(日志寫到哪里)
//創(chuàng)建一個(gè)核心日志處理器,相當(dāng)于日志處理引擎
core := zapcore.NewCore(encoder, writeSyncer, zapcore.DebugLevel)
//基于core創(chuàng)建Logger實(shí)例
// zap.AddCaller()是可選配置,作用是讓日志種自動(dòng)包含調(diào)用日志的代碼位置信息(即文件名和行號(hào))
logger := zap.New(core, zap.AddCaller())
//從一個(gè)標(biāo)準(zhǔn)的zap.Logger實(shí)例創(chuàng)建并獲取一個(gè)zap.SugaredLogger實(shí)例
sugarLogger = logger.Sugar()
}
// 使用JSON格式的編碼器,并采用開發(fā)環(huán)境的默認(rèn)配置,包括日志級(jí)別和時(shí)間戳等字段
func getEncoder() zapcore.Encoder {
config := zap.NewProductionEncoderConfig()
//修改時(shí)間格式
config.EncodeTime = func(t time.Time, enc zapcore.PrimitiveArrayEncoder) {
enc.AppendString(t.Format("2006-01-02 15:04:05"))
}
//將日志級(jí)別顯示為中文
config.EncodeLevel = func(level zapcore.Level, enc zapcore.PrimitiveArrayEncoder) {
switch level {
case zapcore.DebugLevel:
enc.AppendString("調(diào)試")
case zapcore.InfoLevel:
enc.AppendString("信息")
case zapcore.WarnLevel:
enc.AppendString("警告")
case zapcore.ErrorLevel:
enc.AppendString("錯(cuò)誤")
case zapcore.DPanicLevel:
enc.AppendString("嚴(yán)重錯(cuò)誤")
case zapcore.PanicLevel:
enc.AppendString("恐慌")
case zapcore.FatalLevel:
enc.AppendString("致命")
default:
enc.AppendString(level.String())
}
}
// 修改字段名為中文
config.MessageKey = "消息"
config.LevelKey = "級(jí)別"
config.TimeKey = "時(shí)間"
config.CallerKey = "位置"
config.StacktraceKey = "堆棧"
return zapcore.NewJSONEncoder(config)
}
// 日志輸出配置
func getLogWriter() zapcore.WriteSyncer {
//創(chuàng)建日志文件輪轉(zhuǎn)管理器實(shí)例
//使用lumberjack庫實(shí)現(xiàn)日志輪轉(zhuǎn)
lumberjackLogger := &lumberjack.Logger{
Filename: "./logs/gin.log", //文件路徑,不存在會(huì)自動(dòng)創(chuàng)建
MaxSize: 200, //單個(gè)文件的最大大?。∕B)
MaxBackups: 10, //保留的最大備份文件數(shù)
MaxAge: 30, //日志文件的最大保存天數(shù)
Compress: true, //是否壓縮備份文件
}
return zapcore.AddSync(lumberjackLogger)
}
總結(jié)
到此這篇關(guān)于GO語言zap日志庫理解和使用方法的文章就介紹到這了,更多相關(guān)GO語言zap日志庫使用內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Go?gRPC進(jìn)階教程gRPC轉(zhuǎn)換HTTP
這篇文章主要為大家介紹了Go?gRPC進(jìn)階教程gRPC轉(zhuǎn)換HTTP教程示例,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2022-06-06
go build -tags構(gòu)建約束試驗(yàn)示例解析
這篇文章主要為大家介紹了go build -tags構(gòu)建約束試驗(yàn)示例解析,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-09-09
成功安裝vscode中g(shù)o的相關(guān)插件(詳細(xì)教程)
這篇文章主要介紹了成功安裝vscode中g(shù)o的相關(guān)插件的詳細(xì)教程,本文給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2020-05-05
Golang連接并操作PostgreSQL數(shù)據(jù)庫基本操作
PostgreSQL是常見的免費(fèi)的大型關(guān)系型數(shù)據(jù)庫,具有豐富的數(shù)據(jù)類型,也是軟件項(xiàng)目常用的數(shù)據(jù)庫之一,下面這篇文章主要給大家介紹了關(guān)于Golang連接并操作PostgreSQL數(shù)據(jù)庫基本操作的相關(guān)資料,需要的朋友可以參考下2022-09-09
Golang實(shí)現(xiàn)按行讀取文件的方法小結(jié)
按行讀取文件相較于一次性載入,有著很多優(yōu)勢(shì),如內(nèi)存效率高、處理速度快、實(shí)時(shí)性高等,本文主要介紹了Golang按行讀取文件的相關(guān)方法,希望對(duì)大家有所幫助2024-02-02
Golang程序漏洞檢測(cè)器govulncheck的安裝和使用
govulncheck 是一個(gè)命令行工具,可以幫助 Golang 開發(fā)者快速找到項(xiàng)目代碼和依賴的模塊中的安全漏洞,該工具可以分析源代碼和二進(jìn)制文件,識(shí)別代碼中對(duì)這些漏洞的任何直接或間接調(diào)用,本文就給大家介紹一下govulncheck安裝和使用,需要的朋友可以參考下2023-09-09
服務(wù)器端Go程序?qū)﹂L(zhǎng)短鏈接的處理及運(yùn)行參數(shù)的保存
這篇文章主要介紹了服務(wù)器端Go程序?qū)﹂L(zhǎng)短鏈接的處理及運(yùn)行參數(shù)的保存,這里針對(duì)使用Go語言編寫的Socket服務(wù)器進(jìn)行實(shí)例說明,需要的朋友可以參考下2016-03-03

