01
介绍
我们在之前的文章中介绍过标准库 log 包的使用方式,它虽然使用方便,但是它支持的功能比较简单。
本文我们介绍 uber 开源的日志库 zap,首先使用 Gin 框架构建一个 Web 应用,然后通过在该 Web 应用中记录日志,来介绍 zap 的使用方式。
最后,我们再使用开源的日志切割库 lumberjack,进行日志切割。
使用 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 函数的请求日志。
03
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 记录日志时,我们需要显示指定数据类型,一般用于性能和类型安全比较重要的场景中。
04
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 日志文件是否需要压缩
05
总结
本文我们通过在 Gin 构建的应用中,使用 Zap 记录请求日志,介绍了 Zap 的使用方式,最后还通过 lumberjack 日志切割库进行切割日志。
更多关于 Zap 的内容,感兴趣的读者朋友们可以阅读官方文档或源码。
推荐阅读:
参考资料: