Zap - Go 高性能日志库
Zap 是 Uber 开源的高性能日志库,以极致的性能和丰富的功能著称。掌握 Zap 是开发生产级 Go 应用的基础,特别适用于高并发、高性能场景。
📌 核心概念
⚡
高性能
零分配日志
10x 性能
📝
结构化
JSON/Console 输出
Structured
🎯
日志级别
Debug 到 Fatal
LogLevel
🔧
字段添加
类型安全字段
Fields
快速开始
📝 基础使用
package main
import (
"go.uber.org/zap"
)
func main() {
// 方式 1: 快速使用 (适合原型开发)
logger, _ := zap.NewProduction()
defer logger.Sync() // 刷新缓冲
logger.Info("Server started",
zap.String("host", "localhost"),
zap.Int("port", 8080),
)
logger.Error("Failed to connect",
zap.String("error", "connection refused"),
)
// 方式 2: 便捷 SugaredLogger (性能略低)
sugar := logger.Sugar()
sugar.Infow("Request received",
"method", "GET",
"path", "/api/users",
)
sugar.Errorw("Invalid input", "error", "missing field")
}
💡 Zap 要点
- Logger: 类型安全,性能最高
- SugaredLogger: 使用灵活,性能略低
- Sync(): 刷新缓冲,确保日志写入
- 字段类型: zap.String/Int/Bool 等类型安全
日志级别
📝 日志级别使用
package main
import (
"go.uber.org/zap"
"go.uber.org/zap/zapcore"
)
func main() {
logger, _ := zap.NewProduction()
defer logger.Sync()
// Debug: 调试信息
logger.Debug("Debug message", zap.String("detail", "verbose"))
// Info: 一般信息
logger.Info("Server started", zap.Int("port", 8080))
// Warn: 警告信息
logger.Warn("Deprecated API used", zap.String("api", "v1"))
// Error: 错误信息
logger.Error("Database connection failed", zap.Error(err))
// DPanic: 开发环境 panic
logger.DPanic("Unexpected state")
// Panic: 记录日志并 panic
// logger.Panic("Critical error")
// Fatal: 记录日志并退出程序
// logger.Fatal("Unrecoverable error")
// 检查级别再记录 (避免不必要的参数计算)
if logger.Core().Enabled(zapcore.DebugLevel) {
logger.Debug("Expensive debug", zap.Any("data", compute()))
}
}
func compute() string {
return "computed"
}
配置日志
📝 自定义配置
package main
import (
"go.uber.org/zap"
"go.uber.org/zap/zapcore"
)
func NewLogger() (*zap.Logger, error) {
// 配置结构
config := zap.Config{
Level: zap.NewAtomicLevelAt(zapcore.InfoLevel),
Development: false,
Encoding: "json", // json 或 console
EncoderConfig: zapcore.EncoderConfig{
TimeKey: "time",
LevelKey: "level",
NameKey: "logger",
CallerKey: "caller",
MessageKey: "msg",
StacktraceKey: "stacktrace",
LineEnding: zapcore.DefaultLineEnding,
EncodeLevel: zapcore.LowercaseLevelEncoder,
EncodeTime: zapcore.ISO8601TimeEncoder,
EncodeDuration: zapcore.SecondsDurationEncoder,
EncodeCaller: zapcore.ShortCallerEncoder,
},
OutputPaths: []string{"stdout", "./logs/app.log"},
ErrorOutputPaths: []string{"stderr"},
}
return config.Build()
}
func main() {
logger, err := NewLogger()
if err != nil {
panic(err)
}
defer logger.Sync()
logger.Info("Application started")
}
📝 开发环境配置
func NewDevLogger() (*zap.Logger, error) {
config := zap.NewDevelopmentConfig()
// 自定义输出
config.OutputPaths = []string{"stdout"}
// 使用 console 编码器
config.Encoding = "console"
// 启用调用者信息
config.EncoderConfig.CallerKey = "caller"
return config.Build()
}
字段添加
📝 类型安全字段
func handleRequest(logger *zap.Logger) {
// 基本类型字段
logger.Info("Request",
zap.String("method", "GET"),
zap.String("path", "/api/users"),
zap.Int("status", 200),
zap.Int64("duration_ms", 150),
zap.Bool("success", true),
zap.Float64("size_kb", 1.5),
)
// 复杂类型字段
logger.Info("User data",
zap.Any("user", user),
zap.Strings("tags", []string{"vip", "active"}),
zap.Ints("ids", []int{1, 2, 3}),
zap.Errors([]error{err1, err2}),
)
// 错误字段
if err != nil {
logger.Error("Operation failed", zap.Error(err))
}
// 命名空间 (字段分组)
logger.Info("Request",
zap.Namespace("request"),
zap.String("method", "POST"),
zap.Namespace("response"),
zap.Int("status", 201),
)
}
Logger 组合
📝 With 和 Named
func main() {
logger, _ := zap.NewProduction()
defer logger.Sync()
// With: 添加公共字段
requestLogger := logger.With(
zap.String("request_id", "abc123"),
zap.String("client_ip", "192.168.1.1"),
)
requestLogger.Info("Request started")
requestLogger.Info("Request completed")
// Named: 添加 logger 名称
dbLogger := logger.Named("database")
dbLogger.Info("Connected to database")
// 组合使用
userServiceLogger := logger.Named("service").With(
zap.String("service", "user"),
)
userServiceLogger.Info("User created", zap.Int64("user_id", 123))
}
Hook 和扩展
📝 日志 Hook
package main
import (
"go.uber.org/zap"
"go.uber.org/zap/zapcore"
)
// 自定义 Hook: 错误日志发送告警
type ErrorHook struct{}
func (h *ErrorHook) Write(p []byte) (int, error) {
// 发送告警通知
sendAlert(string(p))
return len(p), nil
}
func NewLoggerWithHook() (*zap.Logger, error) {
config := zap.NewProductionConfig()
// 添加 Hook: Error 级别以上触发
config.Hooks = []zapcore.Core{
zapcore.NewCore(
zapcore.NewJSONEncoder(config.EncoderConfig),
&ErrorHook{},
zapcore.ErrorLevel,
),
}
return config.Build()
}
func sendAlert(msg string) {
// 发送告警到钉钉/企业微信/Slack 等
}
与 Gin 集成
📝 Gin + Zap 中间件
package middleware
import (
"github.com/gin-gonic/gin"
"go.uber.org/zap"
"time"
)
func Logger(logger *zap.Logger) gin.HandlerFunc {
return func(c *gin.Context) {
start := time.Now()
// 处理请求
c.Next()
// 记录日志
logger.Info("Request",
zap.String("method", c.Request.Method),
zap.String("path", c.Request.URL.Path),
zap.Int("status", c.Writer.Status()),
zap.String("client_ip", c.ClientIP()),
zap.Duration("duration", time.Since(start)),
)
}
}
func Recovery(logger *zap.Logger) gin.HandlerFunc {
return func(c *gin.Context) {
defer func() {
if err := recover(); err != nil {
logger.Error("Panic recovered",
zap.Any("error", err),
zap.String("path", c.Request.URL.Path),
)
c.AbortWithStatus(500)
}
}()
c.Next()
}
}
最佳实践
✅ Zap 使用建议
- 全局 Logger: 使用全局变量或注入到 Context
- 延迟 Sync: 使用 defer logger.Sync()
- 类型安全: 优先使用 Logger 而非 SugaredLogger
- 字段复用: 使用 With 添加公共字段
- 级别检查: 使用 Enabled 避免不必要的计算
- 结构化输出: 生产环境使用 JSON 格式
🚨 常见陷阱
- 忘记 Sync: 日志可能丢失,程序退出前必须 Sync
- 过度使用 Any: 失去类型安全,性能下降
- Debug 生产: 生产环境应设置为 Info 或 Warn
- 敏感信息: 避免记录密码、token 等敏感数据
- 高频日志: 避免在循环中记录大量日志