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

本文涉及的产品
日志服务 SLS,月写入数据量 50GB 1个月
简介: Go 语言怎么使用 zap 日志库?

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 支持两种模式,分别是 SugaredLoggerLogger,其中 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 的方式,分别是函数 NewProductionNewDevelopmentExample(),感兴趣的读者朋友们,可以试用一下。

Logger 模式

接下来,我们简单介绍一下 Logger 模式,它主要用于性能和类型安全比较重要的场景中,但是,它没有 SugaredLogger 模式简单易用,我们可以根据实际场景选择使用哪种模式。

我们修改一下现有代码,新创建 InitLoggerV2 函数,其中 encwsenab 函数的代码与 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 的内容,感兴趣的读者朋友们可以阅读官方文档或源码。

推荐阅读:

参考资料:

  1. https://github.com/uber-go/zap
  2. https://pkg.go.dev/go.uber.org/zap


相关实践学习
日志服务之使用Nginx模式采集日志
本文介绍如何通过日志服务控制台创建Nginx模式的Logtail配置快速采集Nginx日志并进行多维度分析。
目录
相关文章
|
9天前
|
程序员 Go PHP
为什么大部分的 PHP 程序员转不了 Go 语言?
【9月更文挑战第8天】大部分 PHP 程序员难以转向 Go 语言,主要因为:一、编程习惯与思维方式差异,如语法风格和编程范式;二、学习成本高,需掌握新知识体系且面临项目压力;三、职业发展考量,现有技能价值及市场需求不确定性。学习新语言虽有挑战,但对拓宽职业道路至关重要。
39 10
|
7天前
|
Go API 开发者
深入探讨:使用Go语言构建高性能RESTful API服务
在本文中,我们将探索Go语言在构建高效、可靠的RESTful API服务中的独特优势。通过实际案例分析,我们将展示Go如何通过其并发模型、简洁的语法和内置的http包,成为现代后端服务开发的有力工具。
|
9天前
|
算法 程序员 Go
PHP 程序员学会了 Go 语言就能唬住面试官吗?
【9月更文挑战第8天】学会Go语言可提升PHP程序员的面试印象,但不足以 solely “唬住” 面试官。学习新语言能展现学习能力、拓宽技术视野,并增加就业机会。然而,实际项目经验、深入理解语言特性和综合能力更为关键。全面展示这些方面才能真正提升面试成功率。
31 10
|
9天前
|
编译器 Go
go语言学习记录(关于一些奇怪的疑问)有别于其他编程语言
本文探讨了Go语言中的常量概念,特别是特殊常量iota的使用方法及其自动递增特性。同时,文中还提到了在声明常量时,后续常量可沿用前一个值的特点,以及在遍历map时可能遇到的非顺序打印问题。
|
6天前
|
存储 监控 数据可视化
Go 语言打造公司监控电脑的思路
在现代企业管理中,监控公司电脑系统对保障信息安全和提升工作效率至关重要。Go 语言凭借其高效性和简洁性,成为构建监控系统的理想选择。本文介绍了使用 Go 语言监控系统资源(如 CPU、内存)和网络活动的方法,并探讨了整合监控数据、设置告警机制及构建可视化界面的策略,以满足企业需求。
24 1
|
13天前
|
安全 大数据 Go
深入探索Go语言并发编程:Goroutines与Channels的实战应用
在当今高性能、高并发的应用需求下,Go语言以其独特的并发模型——Goroutines和Channels,成为了众多开发者眼中的璀璨明星。本文不仅阐述了Goroutines作为轻量级线程的优势,还深入剖析了Channels作为Goroutines间通信的桥梁,如何优雅地解决并发编程中的复杂问题。通过实战案例,我们将展示如何利用这些特性构建高效、可扩展的并发系统,同时探讨并发编程中常见的陷阱与最佳实践,为读者打开Go语言并发编程的广阔视野。
|
10天前
|
存储 Shell Go
Go语言结构体和元组全面解析
Go语言结构体和元组全面解析
|
25天前
|
Kubernetes Ubuntu Windows
【Azure K8S | AKS】分享从AKS集群的Node中查看日志的方法(/var/log)
【Azure K8S | AKS】分享从AKS集群的Node中查看日志的方法(/var/log)
|
7天前
|
Java
日志框架log4j打印异常堆栈信息携带traceId,方便接口异常排查
日常项目运行日志,异常栈打印是不带traceId,导致排查问题查找异常栈很麻烦。
|
17天前
|
存储 监控 数据可视化
SLS 虽然不是直接使用 OSS 作为底层存储,但它凭借自身独特的存储架构和功能,为用户提供了一种专业、高效的日志服务解决方案。
【9月更文挑战第2天】SLS 虽然不是直接使用 OSS 作为底层存储,但它凭借自身独特的存储架构和功能,为用户提供了一种专业、高效的日志服务解决方案。
49 9