本文我们通过在 Gin 构建的应用中,使用 Zap 记录请求日志,介绍了 Zap 的使用方式,最后还通过 lumberjack 日志切割库进行切割日志。
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
©本文为清一色官方代发,观点仅代表作者本人,与清一色无关。清一色对文中陈述、观点判断保持中立,不对所包含内容的准确性、可靠性或完整性提供任何明示或暗示的保证。本文不作为投资理财建议,请读者仅作参考,并请自行承担全部责任。文中部分文字/图片/视频/音频等来源于网络,如侵犯到著作权人的权利,请与我们联系(微信/QQ:1074760229)。转载请注明出处:清一色财经