极致性能
Zap 通过减少内存分配和优化编码逻辑,显著降低日志记录的开销。官方基准测试显示,Zap 的性能远超 logrus
等传统库。
zap.String
, zap.Int
)确保日志数据格式正确。结构化日志
默认输出 JSON 格式,便于与 ELK(Elasticsearch, Logstash, Kibana)等日志分析系统集成。
灵活的日志级别
支持 Debug
, Info
, Warn
, Error
, Panic
, Fatal
多级别日志,并允许动态调整级别。
go get go.uber.org/zap
package main
import (
"go.uber.org/zap"
)
func main() {
// 使用预定义的 Production 配置(JSON 格式,日志级别为 Info)
logger, _ := zap.NewProduction()
defer logger.Sync() // 确保日志刷新到输出
// 记录结构化日志
logger.Info("用户登录成功",
zap.String("username", "alice"),
zap.Int("attempts", 3),
)
}
{
"level": "info",
"ts": 1630000000,
"msg": "用户登录成功",
"username": "alice",
"attempts": 3
}
zap.Field
明确指定类型(如 zap.String
)。示例
logger.Info("订单创建成功",
zap.String("order_id", "12345"),
zap.Float64("amount", 99.99),
)
Infow
, Errorw
等链式方法。interface{}
,但牺牲了类型安全和少量性能。示例
sugar := logger.Sugar()
sugar.Infow("订单创建失败",
"order_id", "12345",
"error", "库存不足", // 类型由开发者自行保证
)
func main() {
// 配置日志级别、输出目标、时间格式等
config := zap.NewProductionConfig()
config.Level = zap.NewAtomicLevelAt(zap.DebugLevel) // 启用 Debug 级别
config.OutputPaths = []string{"stdout", "/var/log/app.log"} // 输出到控制台和文件
config.EncoderConfig.TimeKey = "timestamp" // 自定义时间字段名
config.EncoderConfig.EncodeTime = zapcore.ISO8601TimeEncoder // 时间格式为 ISO8601
logger, _ := config.Build()
defer logger.Sync()
}
Zap 本身不提供日志切割功能,但可通过 lumberjack
实现:
import (
"gopkg.in/natefinch/lumberjack.v2"
)
func main() {
// 配置日志切割(按大小和日期)
lumberjackLogger := &lumberjack.Logger{
Filename: "app.log",
MaxSize: 100, // MB
MaxBackups: 3, // 保留旧文件数
MaxAge: 28, // 保留天数
}
// 将切割器绑定到 Zap
config := zap.NewProductionConfig()
config.OutputPaths = []string{"stdout", lumberjackLogger.Filename}
logger, _ := config.Build()
}
Gin 默认的 Logger
中间件性能较低,使用 Zap 可显著提升性能。
步骤 1:安装 ginzap
中间件库
go get github.com/gin-contrib/zap
步骤 2:集成 Zap 到 Gin
package main
import (
"github.com/gin-gonic/gin"
"go.uber.org/zap"
"github.com/gin-contrib/zap"
)
func main() {
// 初始化 Zap
logger, _ := zap.NewProduction()
defer logger.Sync()
// 创建 Gin 引擎
r := gin.New()
// 使用 Zap 中间件(替换默认的 Logger 和 Recovery)
r.Use(ginzap.Ginzap(logger, time.RFC3339, true)) // 记录请求日志
r.Use(ginzap.RecoveryWithZap(logger, true)) // 处理 Panic 并记录
// 定义路由
r.GET("/ping", func(c *gin.Context) {
c.String(200, "pong")
})
r.Run(":8080")
}
在中间件中添加额外的上下文信息:
r.Use(func(c *gin.Context) {
// 记录请求处理时间
start := time.Now()
c.Next() // 处理请求
latency := time.Since(start)
// 记录日志
logger.Info("HTTP请求",
zap.String("path", c.Request.URL.Path),
zap.Int("status", c.Writer.Status()),
zap.Duration("latency", latency),
)
})
在大型项目中,日志器(Logger)通常需要在多个模块或函数中使用。如果每次都需要显式传递 Logger 实例,代码会变得冗长且难以维护。例如:
func processOrder(logger *zap.Logger, orderID string) {
logger.Info("Processing order", zap.String("orderID", orderID))
}
func main() {
logger, _ := zap.NewProduction()
processOrder(logger, "12345")
}
Zap 的全局日志器通过以下三个函数实现:
zap.ReplaceGlobals(logger *zap.Logger)
作用:
替换全局的 Logger 实例。调用此函数后,zap.L() 和 zap.S() 会基于新的 Logger 返回对应的日志器。
为什么需要调用它?
默认情况下,Zap 的全局日志器是空的(zap.L() 返回一个无操作的 Logger)。通过调用 zap.ReplaceGlobals(),你可以将自定义的 Logger 设置为全局日志器。
zap.L()
作用:
返回全局的 Logger 实例。如果未调用 zap.ReplaceGlobals(),则返回一个无操作的 Logger(即不记录任何日志)。
zap.S()
作用:
返回全局的 SugaredLogger 实例。SugaredLogger 是基于全局 Logger 创建的,因此必须先调用 zap.ReplaceGlobals() 设置全局 Logger。
func initLogger() {
logger, _ := zap.NewProduction()
zap.ReplaceGlobals(logger)
}
func main() {
initLogger()
defer zap.L().Sync()
// 在项目任何地方使用全局日志器
zap.L().Info("Application started")
}
若有错误与不足请指出,关注DPT一起进步吧!!!