GO語言zap日志庫理解和使用方法示例
1. zap日志庫介紹
目前我所了解的日志庫有g(shù)o內(nèi)置的默認(rèn)日志標(biāo)準(zhǔn)庫,這個日志包簡單,無需額外的依賴,無需安裝,缺乏日志格式化能力,無法切割日志,無法日志分級,適合小項(xiàng)目,對日志功能要求不高的場景;
然后就是logrus第三方日志庫,支持日志分級,支持結(jié)構(gòu)化日志,支持鉤子機(jī)制,但性能一般,適合不對性能要求的中小型項(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)建一個Logger。- 上面的每一個函數(shù)都將創(chuàng)建一個logger。唯一的區(qū)別在于它將記錄的信息不同。例如production logger默認(rèn)記錄調(diào)用函數(shù)信息、日期和時間等。
- 通過Logger調(diào)用Info/Error等。
- 默認(rèn)情況下日志都會打印到應(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請求
}
// 日志初始化函數(shù)
func InitLogger() {
//創(chuàng)建生產(chǎn)環(huán)境級別的日志器
//默認(rèn)輸出 JSON 格式的日志,包含時間戳、日志級別、調(diào)用位置等信息
logger, _ = zap.NewProduction()
}
// HTTP請求函數(shù)
func simpleHttpGet(url string) {
//發(fā)送HTTP GET請求
//返回值是請求響應(yīng)數(shù)據(jù)和錯誤信息
resp, err := http.Get(url)
if err != nil {
//如果請求出錯,記錄錯誤日志
logger.Error("http get error", //錯誤描述
zap.String("url", url), //記錄錯誤的URL
zap.Error(err)) //記錄錯誤的信息
} else {
//如果請求成功,記錄信息日志
logger.Info("success",
zap.String("statusCode", resp.Status), //響應(yīng)狀態(tài)碼
zap.String("url", url)) //請求的URL
err := resp.Body.Close() //關(guān)閉響應(yīng)體
if err != nil {
// 如果關(guān)閉響應(yīng)體出錯,記錄錯誤日志
logger.Error("body read error")
}
}
}
這段代碼是使用Go語言編寫的簡單HTTP請求客戶端程序,主要功能是向指定的URL發(fā)送HTTP GET 請求,并使用zap日志庫記錄請求過程中的關(guān)鍵信息。
3.2 Sugared Logger
大部分實(shí)現(xiàn)與Logger相同,唯一的區(qū)別是我們通過調(diào)用logger的Sugar()方法來獲取一個SugaredLogger,然后使用SugaredLogger以printf格式記錄語句。下面是用SugaredLogger日志記錄器實(shí)現(xiàn)上述Logger記錄器相同功能的代碼:
// 聲明一個全局日志器變量
// SugaredLogger支持格式化字符串,使用時更接近打印日志習(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ā)送請求
sugarLogger.Debugf("Tring to GET %s", url)
//發(fā)送HTTP請求
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)
//返回錯誤時,resp 變量可能為 nil(空值),
//此時調(diào)用 resp.Body.Close() 會導(dǎo)致程序 panic(崩潰)
}
}在zap日志庫中的Logger和SugaredLogger時基礎(chǔ)與封裝的關(guān)系,兩者本質(zhì)上共享一套日志核心功能,
logger(zap.Logger)是 zap 的 基礎(chǔ)核心,專注于性能和類型安全;sugarLogger(zap.SugaredLogger)是其 便捷封裝,專注于開發(fā)效率。
4. 定制logger
4.1 將日志寫入文件
我們將使用zap.New(...)方法來手動傳遞所有配置,而不是使用像zap.NewPriduction()這樣的預(yù)置方法來創(chuàng)建logger
func New(core zapcore.Core, options ...Option) *Logger
zapcore.Core需要三個配置:Encoder,WriteSyncer,LogLevel。
1. Encoder:編碼器(如何寫入日志)。我們將使用開箱即用的NewJSONEncoder(),并使用預(yù)先設(shè)置的ProductionEncoderConfig()。

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

3. Log Level:哪種級別的日志將被寫入
// 聲明一個全局日志器變量
// SugaredLogger支持格式化字符串,使用時更接近打印日志習(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)和日志級別
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ā)送請求
sugarLogger.Debugf("Tring to GET %s", url)
//發(fā)送HTTP請求
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)
//返回錯誤時,resp 變量可能為 nil(空值),
//此時調(diào)用 resp.Body.Close() 會導(dǎo)致程序 panic(崩潰)
}
}
4.2 將JSON Encoder更改為普通的Log Encoder
我們希望將編碼器從JSON Encoder更改為普通Encoder。為此,我們需要將NewJSONEncoder()更改為NewConsoleEncoder()。
return zapcore.NewConsoleEncoder(zap.NewProductionEncoderConfig())
然后打印結(jié)果(就不是JSON格式了):

4.3 更改時間編碼并添加調(diào)用者詳細(xì)信息
鑒于我們對配置所做的更改,有下面兩個問題:
- 時間是以非人類可讀的方式展示,例如1.572161051846623e+09
- 調(diào)用方函數(shù)的詳細(xì)信息沒有顯示在日志中
我們要做的第一件事是覆蓋默認(rèn)的ProductionConfig(),并進(jìn)行以下更改:
- 修改時間編碼器
- 在日志文件中使用大寫字母記錄日志級別
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ù)中添加一個Option。
logger := zap.New(core, zap.AddCaller())
4.4 將日志輸出到多個位置
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)和日志級別
//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ā)送請求
sugarLogger.Debugf("Tring to GET %s", url)
//發(fā)送HTTP請求
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)
//返回錯誤時,resp 變量可能為 nil(空值),
//此時調(diào)用 resp.Body.Close() 會導(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)和日志級別
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ā)送請求
sugarLogger.Debugf("Tring to GET %s", url)
//發(fā)送HTTP請求
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)
//返回錯誤時,resp 變量可能為 nil(空值),
//此時調(diào)用 resp.Body.Close() 會導(dǎo)致程序 panic(崩潰)
}
}
6. 日志中間件實(shí)現(xiàn)
// 聲明一個日志記錄器實(shí)例
var sugarLogger *zap.SugaredLogger
// 中間件函數(shù)
func Logger() gin.HandlerFunc {
InitLogger()
return func(c *gin.Context) {
//在請求處理前,記錄一條Debug級別的日志,包含請求的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ù)器錯誤: %d", statusCode)
} else {
sugarLogger.Infof("請求正常: %d", statusCode)
}
}
}
// 初始化日志器
func InitLogger() {
encoder := getEncoder() //獲取編碼器(定義日志格式)
writeSyncer := getLogWriter() //獲取輸出目標(biāo)(日志寫到哪里)
//創(chuàng)建一個核心日志處理器,相當(dāng)于日志處理引擎
core := zapcore.NewCore(encoder, writeSyncer, zapcore.DebugLevel)
//基于core創(chuàng)建Logger實(shí)例
// zap.AddCaller()是可選配置,作用是讓日志種自動包含調(diào)用日志的代碼位置信息(即文件名和行號)
logger := zap.New(core, zap.AddCaller())
//從一個標(biāo)準(zhǔn)的zap.Logger實(shí)例創(chuàng)建并獲取一個zap.SugaredLogger實(shí)例
sugarLogger = logger.Sugar()
}
// 使用JSON格式的編碼器,并采用開發(fā)環(huán)境的默認(rèn)配置,包括日志級別和時間戳等字段
func getEncoder() zapcore.Encoder {
config := zap.NewProductionEncoderConfig()
//修改時間格式
config.EncodeTime = func(t time.Time, enc zapcore.PrimitiveArrayEncoder) {
enc.AppendString(t.Format("2006-01-02 15:04:05"))
}
//將日志級別顯示為中文
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("錯誤")
case zapcore.DPanicLevel:
enc.AppendString("嚴(yán)重錯誤")
case zapcore.PanicLevel:
enc.AppendString("恐慌")
case zapcore.FatalLevel:
enc.AppendString("致命")
default:
enc.AppendString(level.String())
}
}
// 修改字段名為中文
config.MessageKey = "消息"
config.LevelKey = "級別"
config.TimeKey = "時間"
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", //文件路徑,不存在會自動創(chuàng)建
MaxSize: 200, //單個文件的最大大?。∕B)
MaxBackups: 10, //保留的最大備份文件數(shù)
MaxAge: 30, //日志文件的最大保存天數(shù)
Compress: true, //是否壓縮備份文件
}
return zapcore.AddSync(lumberjackLogger)
}
總結(jié)
到此這篇關(guān)于GO語言zap日志庫理解和使用方法的文章就介紹到這了,更多相關(guān)GO語言zap日志庫使用內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Go項(xiàng)目在GoLand中導(dǎo)入依賴標(biāo)紅問題的解決方案
這篇文章主要介紹了Go項(xiàng)目在GoLand中導(dǎo)入依賴標(biāo)紅問題的解決方案,文中通過代碼示例講解的非常詳細(xì),對大家的學(xué)習(xí)或工作有一定的幫助,需要的朋友可以參考下2024-06-06
實(shí)現(xiàn)像php一樣方便的go ORM數(shù)據(jù)庫操作示例詳解
這篇文章主要為大家介紹了實(shí)現(xiàn)像php一樣方便的go ORM數(shù)據(jù)庫操作示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2022-12-12
超實(shí)用的Golang通道指南之輕松實(shí)現(xiàn)并發(fā)編程
Golang?中的通道是一種高效、安全、靈活的并發(fā)機(jī)制,用于在并發(fā)環(huán)境下實(shí)現(xiàn)數(shù)據(jù)的同步和傳遞。本文主要介紹了如何利用通道輕松實(shí)現(xiàn)并發(fā)編程,需要的可以參考一下2023-04-04
Golang設(shè)計(jì)模式中抽象工廠模式詳細(xì)講解
抽象工廠模式用于生成產(chǎn)品族的工廠,所生成的對象是有關(guān)聯(lián)的。如果抽象工廠退化成生成的對象無關(guān)聯(lián)則成為工廠函數(shù)模式。比如本例子中使用RDB和XML存儲訂單信息,抽象工廠分別能生成相關(guān)的主訂單信息和訂單詳情信息2023-01-01

