日志

Zap 是 Uber 开源的高性能结构化日志库,支持日志分级、JSON 输出、强类型字段和自定义编码器。

go get go.uber.org/zap

Zap 提供两种日志器:

类型特点适用场景
Logger使用强类型字段,分配更少、性能更高生产环境和高频日志
SugaredLogger支持键值对和 printf 风格 API,使用更方便业务代码和低频日志

Logger

zap.NewProduction 默认输出 JSON,适合由日志系统采集和检索。

logger, _ := zap.NewProduction()
defer logger.Sync()

logger.Info("request completed",
    zap.String("method", "GET"),
    zap.String("path", "/users/1"),
    zap.Int("status", 200),
    zap.Duration("latency", 35*time.Millisecond),
)

字段会作为独立属性写入 JSON,比把所有信息拼接到消息字符串中更容易查询。

SugaredLogger

SugarLogger 转换为语法更灵活的 SugaredLogger

sugar := logger.Sugar()

sugar.Infow("request completed",
    "method", "GET",
    "path", "/users/1",
    "status", 200,
)
sugar.Infof("user %d logged in", 1)

Infow 的键值参数应成对出现,键应使用字符串。需要更好的类型安全和性能时,优先使用 Logger

日志级别

Zap 提供 DebugInfoWarnErrorDPanicPanicFatal 等级别。

logger.Debug("cache missed", zap.String("key", "user:1"))
logger.Info("server started", zap.Int("port", 8080))
logger.Warn("retrying request", zap.Int("attempt", 2))
logger.Error("query failed", zap.Error(err))

Panic 会在写入日志后触发 panic,Fatal 会在写入日志后调用 os.Exit(1),常规错误处理不应使用它们。

子日志器

With 可以创建带有固定上下文的子日志器,避免在每条日志中重复传入字段。

requestLogger := logger.With(
    zap.String("request_id", "req-123"),
    zap.Int64("user_id", 1),
)

requestLogger.Info("request started")
requestLogger.Info("request completed", zap.Int("status", 200))

开发与生产配置

devLogger, _ := zap.NewDevelopment() // 易读的控制台输出,默认开启 Debug
prodLogger, _ := zap.NewProduction() // JSON 输出,默认从 Info 开始

需要调整级别、时间格式或输出位置时,可以修改 zap.Config 后再构建日志器:

config := zap.NewProductionConfig()
config.Level = zap.NewAtomicLevelAt(zap.WarnLevel)
config.OutputPaths = []string{"stdout", "./app.log"}
config.EncoderConfig.TimeKey = "time"
config.EncoderConfig.EncodeTime = zapcore.ISO8601TimeEncoder

logger, _ := config.Build()
defer logger.Sync()

实践建议

  • 生产环境使用 JSON 输出,并使用稳定的字段名。
  • 传递 error 时使用 zap.Error(err),不要只记录 err.Error()
  • 在请求入口创建带 request_id 的子日志器,再向下传递。
  • 程序退出前调用 Sync 刷新缓冲区;不要在每次记录后调用。
  • 日志中不应记录密码、Token 或完整的个人敏感信息。