?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 函數的請求日志。
3、Gin 框架使用 zap 日志庫
Zap 支持兩種模式,分別是 SugaredLogger? 和 Logger?,其中 SugaredLogger? 模式比 Logger 模式執行速度更快。
SugaredLogger 模式
使用 Zap 日志庫,首先需要使用 New? 函數創建一個 Logger,代碼如下:
func New(core zapcore.Core, options ...Option) *Logger
使用 New? 函數,接收一個 zapcore.Core? 類型的參數和一個 Option? 類型的可選參數,返回一個 *Logger。
其中 zap.Core? 類型的參數,可以使用 NewCore? 函數創建,接收三個參數,分別是 zapcore.Encoder? 類型,zapcore.WriteSyncer? 類型和 zapcore.LevelEnabler 類型,分別用于指定日志格式、日志路徑和日志級別。
func NewCore(enc Encoder, ws WriteSyncer, enab LevelEnabler) Core
其中 zapcore.Encoder? 類型的參數,可以使用 NewProductionEncoderConfig 函數創建,返回一個用于生產環境的固定日志編碼配置。
// 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? 類型的參數,可以使用 AddSync? 函數創建,該函數接收一個 io.Writer 類型的參數。
func AddSync(w io.Writer) WriteSyncer
其中 zapcore.LevelEnabler? 類型的參數,可以使用 zapcore? 包定義的常量 zapcore.DebugLevel?,該常量是 zapcore.Level? 類型,并且 zapcore.Level? 類型實現了 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
}
運行程序,執行 curl http://127.0.0.1:8080/ping。
可以看到,生成的日志文件 xxx.log?,文件中是 json 格式的日志內容,我們可以根據實際需求修改為其他格式。
開發中,可能我們希望日志可以同時輸出到日志文件和終端中,可以使用函數 NewMultiWriteSyncer,代碼如下:
func wsV2() zapcore.WriteSyncer {
return zapcore.NewMultiWriteSyncer(ws(), zapcore.AddSync(os.Stdout))
}
除了使用 zap.New()? 創建 Logger? 之外,Zap 還提供了開箱即用的三種創建 Logger? 的方式,分別是函數 NewProduction,NewDevelopment? 和 Example(),感興趣的讀者朋友們,可以試用一下。
Logger 模式
接下來,我們簡單介紹一下 Logger? 模式,它主要用于性能和類型安全比較重要的場景中,但是,它沒有 SugaredLogger 模式簡單易用,我們可以根據實際場景選擇使用哪種模式。
我們修改一下現有代碼,新創建 InitLoggerV2? 函數,其中 enc,ws? 和 enab? 函數的代碼與 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)
}
閱讀上面這段代碼,我們可以發現,在使用 zap 記錄日志時,我們需要顯示指定數據類型,一般用于性能和類型安全比較重要的場景中。
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 保留的日志文件數量
- MaxAge 日志文件的最長保留時間,單位是天
- Compress 日志文件是否需要壓縮
5、總結
本文我們通過在 Gin 構建的應用中,使用 Zap 記錄請求日志,介紹了 Zap 的使用方式,最后還通過 lumberjack 日志切割庫進行切割日志。
參考資料:
- https://github.com/uber-go/zap
- https://pkg.go.dev/go.uber.org/zap