2

Go 语言怎么使用 Zap 日志库?

 1 year ago
source link: https://www.51cto.com/article/746276.html
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

Go 语言怎么使用 Zap 日志库?

作者:frank 2023-02-13 00:24:37
本文我们通过在 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 日志文件是否需要压缩

本文我们通过在 Gin 构建的应用中,使用 Zap 记录请求日志,介绍了 Zap 的使用方式,最后还通过 lumberjack 日志切割库进行切割日志。

参考资料:

  1. https://github.com/uber-go/zap
  2. https://pkg.go.dev/go.uber.org/zap
责任编辑:武晓燕 来源: Golang语言开发栈

About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK