Go 用 zap 搭建生产级结构化日志:字段、分级、采样与日志轮转

test12026-11-011 次阅读

标准库够用,但生产不够快

Go 标准库的 log 没有结构化字段,slog 解决了结构但默认实现偏简单。在高频服务里,zap 的零分配 JSON 编码和级别采样能明显压低日志开销。下面搭一个能直接用在生产的结构化日志。

核心:Encoder、Writer、Level 三件套

package main

import (
  "os"

  "go.uber.org/zap"
  "go.uber.org/zap/zapcore"
  "gopkg.in/natefinch/lumberjack.v2"
)

func newLogger() *zap.Logger {
  writer := zapcore.AddSync(&lumberjack.Logger{
    Filename:   "logs/app.log",
    MaxSize:    100, // MB
    MaxBackups: 3,
    MaxAge:     7, // 天
  })

  cfg := zap.NewProductionEncoderConfig()
  cfg.EncodeTime = zapcore.ISO8601TimeEncoder
  cfg.EncodeLevel = zapcore.CapitalLevelEncoder

  core := zapcore.NewCore(
    zapcore.NewJSONEncoder(cfg),
    zapcore.NewMultiWriteSyncer(zapcore.AddSync(os.Stdout), writer),
    zap.NewAtomicLevelAt(zapcore.InfoLevel),
  )
  return zap.New(core, zap.AddCaller())
}

带字段地记录,而不是拼字符串

结构化字段让日志可被采集系统按字段检索,而不是靠正则去抠文本。

var logger = newLogger()
defer logger.Sync()

logger.Info("request handled",
  zap.String("path", "/api/users"),
  zap.Int("status", 200),
  zap.Duration("cost", cost),
)

// 需要格式化时再用 SugaredLogger
sugar := logger.Sugar()
sugar.Infow("order created", "order_id", 123, "amount", 99.9)

采样与级别

热点路径上同一个错误可能一秒打几千次,用 Sampler 把高频日志抽样,既保留现场又不压垮磁盘。级别用 AtomicLevel 还能运行时动态调整。

core = zapcore.NewCore(
  enc,
  ws,
  zap.NewAtomicLevelAt(zapcore.InfoLevel),
)
// 高频 Debug 抽样:每 100 条记 1 条
sampled := zapcore.NewSamplerWithOptions(
  core, true, 100, 1,
)

落地要点

  • JSON 编码 + 字段,方便 ELK / Loki 检索。
  • lumberjack 做按大小滚动与过期清理,别让日志把磁盘写满。
  • 生产用 Info,排查时把 AtomicLevel 调到 Debug,不重启也能细看。
T

test1

文章作者

标准库够用,但生产不够快 Go 标准库的 log 没有结构化字段,slog 解决了结构但默认实现偏简单。在高频服务里,...

分类
技术
发布时间
2026-11-01
字数
约 1509 字
阅读
1 次