?1、介紹
我們在之前的文章中介紹過標準庫 log 包的使用方式,它雖然使用方便,但是它支持的功能比較簡單。
本文我們介紹 uber 開源的日志庫 zap?,首先使用 Gin 框架構建一個 Web 應用,然后通過在該 Web 應用中記錄日志,來介紹 zap 的使用方式。
最后,我們再使用開源的日志切割庫 lumberjack,進行日志切割。
2、使用 Gin 構建一個 Web 應用
本文重點不是介紹 gin 框架的使用方式,所以我們僅使用 gin 框架構建一個簡單的 Web 應用,代碼如下:
func main() {
r := gin.Default()
r.GET("/ping", ping)
r.Run()
}
func ping(c *gin.Context) {
c.JSON(http.StatusOK, gin.H{
"message": "pong",
})
}
閱讀上面這段代碼,訪問 http://127.0.0.1:8080/ping?,返回結果是 {"message":"pong"}。
然后,我們使用 zap? 記錄 ping 函數(shù)的請求日志。
3、Gin 框架使用 zap 日志庫
Zap 支持兩種模式,分別是 SugaredLogger? 和 Logger?,其中 SugaredLogger? 模式比 Logger 模式執(zhí)行速度更快。
SugaredLogger 模式
使用 Zap 日志庫,首先需要使用 New? 函數(shù)創(chuàng)建一個 Logger,代碼如下:
func New(core zapcore.Core, options ...Option) *Logger
使用 New? 函數(shù),接收一個 zapcore.Core? 類型的參數(shù)和一個 Option? 類型的可選參數(shù),返回一個 *Logger。
其中 zap.Core? 類型的參數(shù),可以使用 NewCore? 函數(shù)創(chuàng)建,接收三個參數(shù),分別是 zapcore.Encoder? 類型,zapcore.WriteSyncer? 類型和 zapcore.LevelEnabler 類型,分別用于指定日志格式、日志路徑和日志級別。
func NewCore(enc Encoder, ws WriteSyncer, enab LevelEnabler) Core
其中 zapcore.Encoder? 類型的參數(shù),可以使用 NewProductionEncoderConfig 函數(shù)創(chuàng)建,返回一個用于生產(chǎn)環(huán)境的固定日志編碼配置。
// NewProductionEncoderConfig returns an opinionated EncoderConfig for
// production environments.
func NewProductionEncoderConfig() zapcore.EncoderConfig {
return zapcore.EncoderConfig{
TimeKey: "ts",
LevelKey: "level",
NameKey: "logger",
CallerKey: "caller",
FunctionKey: zapcore.OmitKey,
MessageKey: "msg",
StacktraceKey: "stacktrace",
LineEnding: zapcore.DefaultLineEnding,
EncodeLevel: zapcore.LowercaseLevelEncoder,
EncodeTime: zapcore.EpochTimeEncoder,
EncodeDuration: zapcore.SecondsDurationEncoder,
EncodeCaller: zapcore.ShortCallerEncoder,
}
}
我們可以修改任意配置選項的值。
其中 zapcore.WriteSyncer? 類型的參數(shù),可以使用 AddSync? 函數(shù)創(chuàng)建,該函數(shù)接收一個 io.Writer 類型的參數(shù)。
func AddSync(w io.Writer) WriteSyncer
其中 zapcore.LevelEnabler? 類型的參數(shù),可以使用 zapcore? 包定義的常量 zapcore.DebugLevel?,該常量是 zapcore.Level? 類型,并且 zapcore.Level? 類型實現(xiàn)了 zapcore.LevelEnabler 接口。
完整代碼:
var sugaredLogger *zap.SugaredLogger
func main() {
InitLogger()
defer sugaredLogger.Sync()
r := gin.Default()
r.GET("/ping", ping)
r.Run()
}
func ping(c *gin.Context) {
sugaredLogger.Debug("call func ping")
c.JSON(http.StatusOK, gin.H{
"message": "pong",
})
}
func InitLogger() {
core := zapcore.NewCore(enc(), ws(), enab())
logger := zap.New(core)
sugaredLogger = logger.Sugar()
}
func enc() zapcore.Encoder {
cfg := zap.NewProductionEncoderConfig()
cfg.TimeKey = "time"
cfg.EncodeTime = zapcore.TimeEncoderOfLayout("2006-01-02 15:04:05")
return zapcore.NewJSONEncoder(cfg)
}
func ws() zapcore.WriteSyncer {
logFileName := fmt.Sprintf("./%v.log", time.Now().Format("2006-01-02"))
logFile, err := os.Create(logFileName)
if err != nil {
log.Fatal(err)
}
return zapcore.AddSync(logFile)
}
func enab() zapcore.LevelEnabler {
return zapcore.DebugLevel
}
運行程序,執(zhí)行 curl http://127.0.0.1:8080/ping。
可以看到,生成的日志文件 xxx.log?,文件中是 json 格式的日志內(nèi)容,我們可以根據(jù)實際需求修改為其他格式。
開發(fā)中,可能我們希望日志可以同時輸出到日志文件和終端中,可以使用函數(shù) NewMultiWriteSyncer,代碼如下:
func wsV2() zapcore.WriteSyncer {
return zapcore.NewMultiWriteSyncer(ws(), zapcore.AddSync(os.Stdout))
}
除了使用 zap.New()? 創(chuàng)建 Logger? 之外,Zap 還提供了開箱即用的三種創(chuàng)建 Logger? 的方式,分別是函數(shù) NewProduction,NewDevelopment? 和 Example(),感興趣的讀者朋友們,可以試用一下。
Logger 模式
接下來,我們簡單介紹一下 Logger? 模式,它主要用于性能和類型安全比較重要的場景中,但是,它沒有 SugaredLogger 模式簡單易用,我們可以根據(jù)實際場景選擇使用哪種模式。
我們修改一下現(xiàn)有代碼,新創(chuàng)建 InitLoggerV2? 函數(shù),其中 enc,ws? 和 enab? 函數(shù)的代碼與 SugaredLogger 模式保持一致。
var loggerV2 *zap.Logger
func main() {
InitLoggerV2()
defer loggerV2.Sync()
r := gin.Default()
r.GET("/ping", ping)
r.Run()
}
func ping(c *gin.Context) {
loggerV2.Debug("call func ping", zap.Int("code", 200))
c.JSON(http.StatusOK, gin.H{
"message": "pong",
})
}
func InitLoggerV2() {
core := zapcore.NewCore(enc(), ws(), enab())
loggerV2 = zap.New(core)
}
閱讀上面這段代碼,我們可以發(fā)現(xiàn),在使用 zap 記錄日志時,我們需要顯示指定數(shù)據(jù)類型,一般用于性能和類型安全比較重要的場景中。
4、zap 日志庫使用 lumberjack 庫進行日志切割
Zap 日志庫也不支持日志切割的功能,我們可以使用 lumberjack? 日志切割庫進行日志切割,關于 lumberjack 庫的使用方式,我們在之前的文章介紹過,此處不再重復介紹,直接上代碼:
func wsV3() zapcore.WriteSyncer {
logFileName := fmt.Sprintf("./%v.log", time.Now().Format("2006-01-02"))
lumberjackLogger := &lumberjack.Logger{
Filename: logFileName,
MaxSize: 1,
MaxBackups: 3,
MaxAge: 28,
Compress: false,
}
return zapcore.AddSync(lumberjackLogger)
}
lumberjack.Logger 的字段含義:
- Filename 日志保存文件路徑
- MaxSize 日志文件大小,單位是 MB
- MaxBackups 保留的日志文件數(shù)量
- MaxAge 日志文件的最長保留時間,單位是天
- Compress 日志文件是否需要壓縮
5、總結
本文我們通過在 Gin 構建的應用中,使用 Zap 記錄請求日志,介紹了 Zap 的使用方式,最后還通過 lumberjack 日志切割庫進行切割日志。
參考資料:
- https://github.com/uber-go/zap
- https://pkg.go.dev/go.uber.org/zap