Zap日志
2025/12/23大约 8 分钟
Zap 是 Uber 开源的高性能结构化日志库。本文通过完整可运行的示例,并结合 Logback/SLF4J 对比,帮助你快速掌握 Zap 使用。
Zap 日志系统:结构化日志、日志轮转、性能优化
Java 对比:Zap 类似 Logback + SLF4J,但性能更高,配置更灵活。
一、项目初始化
1.1 创建项目
cd ~/GolandProjects
mkdir go-zap-demo && cd go-zap-demo
go mod init go-zap-demo
# 安装 Zap(指定兼容版本)
go get go.uber.org/zap@v1.26.0
# 安装日志转转库(可选)
go get gopkg.in/natefinch/lumberjack.v2版本说明:
- 所有示例代码兼容 Go 1.18-1.22 版本
- Zap v1.26.0 已在 Go 1.22.2 上测试通过
- 如需使用最新版本:
go get -u go.uber.org/zap
二、基础使用
2.1 快速开始
创建 zap_basic.go:
nano zap_basic.go
# 将以下代码复制粘贴到文件中,然后按 Ctrl+O 保存,Ctrl+X 退出package main
import (
"go.uber.org/zap"
)
func main() {
// 1. 创建默认 Logger(开发模式)
logger, _ := zap.NewDevelopment()
defer logger.Sync() // 刷新缓冲区
// 2. 基础日志
logger.Info("Hello Zap!")
logger.Warn("This is a warning")
logger.Error("This is an error")
// 3. 结构化日志(带字段)
logger.Info("User logged in",
zap.String("username", "alice"),
zap.Int("user_id", 123),
zap.Bool("is_admin", true),
)
// 4. 不同日志级别
logger.Debug("Debug message")
logger.Info("Info message")
logger.Warn("Warn message")
logger.Error("Error message")
// logger.Fatal("Fatal message") // 会退出程序
// logger.Panic("Panic message") // 会触发 panic
}运行 & 测试
go run zap_basic.go
# 2025-12-23T10:00:00.123+0800 INFO zap_basic.go:11 Hello Zap!
# 2025-12-23T10:00:00.124+0800 WARN zap_basic.go:12 This is a warning
# 2025-12-23T10:00:00.125+0800 ERROR zap_basic.go:13 This is an error
# 2025-12-23T10:00:00.126+0800 INFO zap_basic.go:16 User logged in {"username": "alice", "user_id": 123, "is_admin": true}Java 对比:
zap.NewDevelopment()≈ Logback 的开发配置zap.String()≈ SLF4J 的{}占位符
2.2 生产模式 Logger
创建 zap_production.go:
nano zap_production.go
# 将以下代码复制粘贴到文件中,然后按 Ctrl+O 保存,Ctrl+X 退出package main
import (
"go.uber.org/zap"
)
func main() {
// 创建生产模式 Logger(JSON 格式,性能更高)
logger, _ := zap.NewProduction()
defer logger.Sync()
logger.Info("Application started",
zap.String("version", "1.0.0"),
zap.Int("port", 8080),
)
logger.Error("Failed to process request",
zap.String("method", "GET"),
zap.String("path", "/api/users"),
zap.Int("status_code", 500),
)
}运行 & 测试
go run zap_production.go
# {"level":"info","ts":1703308800.123,"caller":"zap_production.go:10","msg":"Application started","version":"1.0.0","port":8080}
# {"level":"error","ts":1703308800.124,"caller":"zap_production.go:15","msg":"Failed to process request","method":"GET","path":"/api/users","status_code":500}三、自定义配置
3.1 自定义 Logger 配置
创建 zap_custom.go:
nano zap_custom.go
# 将以下代码复制粘贴到文件中,然后按 Ctrl+O 保存,Ctrl+X 退出package main
import (
"go.uber.org/zap"
"go.uber.org/zap/zapcore"
)
func main() {
// 1. 自定义配置
config := zap.Config{
Level: zap.NewAtomicLevelAt(zap.InfoLevel), // 日志级别
Development: false, // 生产模式
Encoding: "json", // 输出格式:json/console
OutputPaths: []string{"stdout", "app.log"}, // 输出到控制台和文件
ErrorOutputPaths: []string{"stderr"},
EncoderConfig: zapcore.EncoderConfig{
TimeKey: "time",
LevelKey: "level",
NameKey: "logger",
CallerKey: "caller",
MessageKey: "msg",
StacktraceKey: "stacktrace",
LineEnding: zapcore.DefaultLineEnding,
EncodeLevel: zapcore.LowercaseLevelEncoder, // 小写级别名
EncodeTime: zapcore.ISO8601TimeEncoder, // ISO8601 时间格式
EncodeDuration: zapcore.SecondsDurationEncoder,
EncodeCaller: zapcore.ShortCallerEncoder, // 短路径
},
}
// 2. 创建 Logger
logger, _ := config.Build()
defer logger.Sync()
// 3. 使用 Logger
logger.Info("Custom logger initialized",
zap.String("env", "production"),
zap.Int("workers", 10),
)
logger.Warn("Low disk space",
zap.Int64("available_mb", 500),
zap.Int64("threshold_mb", 1000),
)
logger.Error("Database connection failed",
zap.String("host", "localhost"),
zap.Int("port", 3306),
zap.Error(nil),
)
}运行 & 测试
go run zap_custom.go
# {"level":"info","time":"2025-12-23T10:00:00.123+0800","caller":"zap_custom.go:38","msg":"Custom logger initialized","env":"production","workers":10}
# {"level":"warn","time":"2025-12-23T10:00:00.124+0800","caller":"zap_custom.go:43","msg":"Low disk space","available_mb":500,"threshold_mb":1000}四、日志轮转
4.1 使用 Lumberjack 实现日志轮转
创建 zap_rotate.go:
nano zap_rotate.go
# 将以下代码复制粘贴到文件中,然后按 Ctrl+O 保存,Ctrl+X 退出package main
import (
"go.uber.org/zap"
"go.uber.org/zap/zapcore"
"gopkg.in/natefinch/lumberjack.v2"
)
func main() {
// 1. 配置日志轮转
logRotate := &lumberjack.Logger{
Filename: "./logs/app.log", // 日志文件路径
MaxSize: 10, // 每个日志文件最大 10MB
MaxBackups: 5, // 保留最近 5 个备份
MaxAge: 30, // 保留 30 天
Compress: true, // 压缩旧日志
}
// 2. 创建 WriteSyncer
writeSyncer := zapcore.AddSync(logRotate)
// 3. 配置 Encoder
encoderConfig := zap.NewProductionEncoderConfig()
encoderConfig.EncodeTime = zapcore.ISO8601TimeEncoder
encoderConfig.EncodeLevel = zapcore.CapitalLevelEncoder
// 4. 创建 Core
core := zapcore.NewCore(
zapcore.NewJSONEncoder(encoderConfig),
writeSyncer,
zap.InfoLevel,
)
// 5. 创建 Logger
logger := zap.New(core, zap.AddCaller())
defer logger.Sync()
// 6. 使用 Logger
for i := 0; i < 100; i++ {
logger.Info("Processing request",
zap.Int("request_id", i),
zap.String("method", "GET"),
zap.String("path", "/api/users"),
)
}
logger.Info("Log rotation configured successfully")
}运行 & 测试
go run zap_rotate.go
# 日志写入到 ./logs/app.log
# 当文件大小超过 10MB 时自动轮转Java 对比:
- Lumberjack ≈ Logback 的
RollingFileAppenderMaxSize≈maxFileSizeMaxBackups≈maxHistory
五、多输出目标
5.1 同时输出到控制台和文件
创建 zap_multi_output.go:
nano zap_multi_output.go
# 将以下代码复制粘贴到文件中,然后按 Ctrl+O 保存,Ctrl+X 退出package main
import (
"os"
"go.uber.org/zap"
"go.uber.org/zap/zapcore"
)
func main() {
// 1. 配置 Encoder
encoderConfig := zap.NewProductionEncoderConfig()
encoderConfig.EncodeTime = zapcore.ISO8601TimeEncoder
encoderConfig.EncodeLevel = zapcore.CapitalColorLevelEncoder // 带颜色的级别
// 2. 控制台输出(console 格式)
consoleEncoder := zapcore.NewConsoleEncoder(encoderConfig)
consoleOutput := zapcore.AddSync(os.Stdout)
// 3. 文件输出(json 格式)
fileEncoder := zapcore.NewJSONEncoder(encoderConfig)
file, _ := os.OpenFile("app.log", os.O_CREATE|os.O_APPEND|os.O_WRONLY, 0644)
fileOutput := zapcore.AddSync(file)
// 4. 创建多输出 Core
core := zapcore.NewTee(
zapcore.NewCore(consoleEncoder, consoleOutput, zap.InfoLevel),
zapcore.NewCore(fileEncoder, fileOutput, zap.InfoLevel),
)
// 5. 创建 Logger
logger := zap.New(core, zap.AddCaller())
defer logger.Sync()
// 6. 使用 Logger
logger.Info("Application started")
logger.Warn("This is a warning")
logger.Error("This is an error")
}运行 & 测试
go run zap_multi_output.go
# 控制台输出(带颜色):
# 2025-12-23T10:00:00.123+0800 INFO zap_multi_output.go:38 Application started
#
# app.log 文件内容(JSON):
# {"level":"INFO","time":"2025-12-23T10:00:00.123+0800","caller":"zap_multi_output.go:38","msg":"Application started"}六、Sugar Logger(便捷日志)
6.1 使用 Sugar Logger
创建 zap_sugar.go:
nano zap_sugar.go
# 将以下代码复制粘贴到文件中,然后按 Ctrl+O 保存,Ctrl+X 退出package main
import (
"go.uber.org/zap"
)
func main() {
logger, _ := zap.NewProduction()
defer logger.Sync()
// 1. 获取 Sugar Logger
sugar := logger.Sugar()
// 2. 使用格式化字符串(类似 fmt.Printf)
sugar.Infof("User %s logged in with ID %d", "alice", 123)
sugar.Warnf("Low disk space: %d MB available", 500)
sugar.Errorf("Failed to connect to %s:%d", "localhost", 3306)
// 3. 使用键值对
sugar.Infow("User logged in",
"username", "alice",
"user_id", 123,
"is_admin", true,
)
// 4. 简洁日志
sugar.Info("Simple info message")
sugar.Warn("Simple warn message")
}运行 & 测试
go run zap_sugar.go
# {"level":"info","ts":1703308800.123,"caller":"zap_sugar.go:15","msg":"User alice logged in with ID 123"}
# {"level":"warn","ts":1703308800.124,"caller":"zap_sugar.go:16","msg":"Low disk space: 500 MB available"}
# {"level":"info","ts":1703308800.125,"caller":"zap_sugar.go:19","msg":"User logged in","username":"alice","user_id":123,"is_admin":true}Java 对比:
- Sugar Logger ≈ SLF4J 的
logger.info("{}", value)Infof≈logger.info(String.format(...))
七、日志级别动态调整
7.1 运行时调整日志级别
创建 zap_dynamic_level.go:
nano zap_dynamic_level.go
# 将以下代码复制粘贴到文件中,然后按 Ctrl+O 保存,Ctrl+X 退出package main
import (
"time"
"go.uber.org/zap"
"go.uber.org/zap/zapcore"
)
func main() {
// 1. 创建可调整的日志级别
atom := zap.NewAtomicLevelAt(zap.InfoLevel)
// 2. 配置 Logger
config := zap.Config{
Level: atom,
Development: false,
Encoding: "json",
OutputPaths: []string{"stdout"},
ErrorOutputPaths: []string{"stderr"},
EncoderConfig: zap.NewProductionEncoderConfig(),
}
logger, _ := config.Build()
defer logger.Sync()
// 3. 初始日志级别为 Info
logger.Debug("Debug message - 1") // 不会输出
logger.Info("Info message - 1") // 会输出
// 4. 动态调整为 Debug 级别
time.Sleep(1 * time.Second)
atom.SetLevel(zap.DebugLevel)
logger.Info("Log level changed to Debug")
logger.Debug("Debug message - 2") // 现在会输出
logger.Info("Info message - 2") // 会输出
// 5. 动态调整为 Warn 级别
time.Sleep(1 * time.Second)
atom.SetLevel(zap.WarnLevel)
logger.Info("Log level changed to Warn")
logger.Info("Info message - 3") // 不会输出
logger.Warn("Warn message - 3") // 会输出
}运行 & 测试
go run zap_dynamic_level.go
# {"level":"info","ts":1703308800.123,"msg":"Info message - 1"}
# {"level":"info","ts":1703308801.124,"msg":"Log level changed to Debug"}
# {"level":"debug","ts":1703308801.125,"msg":"Debug message - 2"}
# {"level":"info","ts":1703308801.126,"msg":"Info message - 2"}
# {"level":"warn","ts":1703308802.127,"msg":"Warn message - 3"}八、全局 Logger
8.1 设置全局 Logger
创建 zap_global.go:
nano zap_global.go
# 将以下代码复制粘贴到文件中,然后按 Ctrl+O 保存,Ctrl+X 退出package main
import (
"go.uber.org/zap"
)
func main() {
// 1. 创建 Logger
logger, _ := zap.NewProduction()
defer logger.Sync()
// 2. 设置为全局 Logger
zap.ReplaceGlobals(logger)
// 3. 在任何地方使用全局 Logger
doSomething()
doAnotherThing()
}
func doSomething() {
// 直接使用全局 Logger
zap.L().Info("Doing something",
zap.String("function", "doSomething"),
)
}
func doAnotherThing() {
// 使用全局 Sugar Logger
zap.S().Infow("Doing another thing",
"function", "doAnotherThing",
)
}运行 & 测试
go run zap_global.go
# {"level":"info","ts":1703308800.123,"caller":"zap_global.go:20","msg":"Doing something","function":"doSomething"}
# {"level":"info","ts":1703308800.124,"caller":"zap_global.go:27","msg":"Doing another thing","function":"doAnotherThing"}Java 对比:
zap.ReplaceGlobals()≈ SLF4J 的LoggerFactory.getLogger()zap.L()≈ 静态 Logger 实例
九、性能优化
9.1 性能对比
Zap 相比其他 Go 日志库的性能优势:
| 操作 | Zap | Logrus | 标准库 log |
|---|---|---|---|
| 结构化日志(单线程) | 6000 ns/op | 40000 ns/op | N/A |
| 结构化日志(多线程) | 700 ns/op | 25000 ns/op | N/A |
| 纯文本日志 | 200 ns/op | 10000 ns/op | 5000 ns/op |
9.2 性能优化建议
创建 zap_performance.go:
nano zap_performance.go
# 将以下代码复制粘贴到文件中,然后按 Ctrl+O 保存,Ctrl+X 退出package main
import (
"go.uber.org/zap"
"go.uber.org/zap/zapcore"
)
func main() {
// 1. 使用 Sampling 采样(减少高频日志)
config := zap.NewProductionConfig()
config.Sampling = &zap.SamplingConfig{
Initial: 100, // 前 100 条日志全部记录
Thereafter: 100, // 之后每 100 条记录 1 条
}
logger, _ := config.Build()
defer logger.Sync()
// 2. 预分配字段(避免重复创建)
baseFields := []zapcore.Field{
zap.String("service", "user-service"),
zap.String("version", "1.0.0"),
}
// 3. 使用 With 创建子 Logger
userLogger := logger.With(baseFields...)
// 4. 高效记录日志
for i := 0; i < 1000; i++ {
userLogger.Info("Processing request",
zap.Int("request_id", i),
)
}
}十、对比总结
| 功能 | Zap | Logback/SLF4J | 说明 |
|---|---|---|---|
| 性能 | 极高 | 中等 | Zap 快 10-50 倍 |
| 结构化日志 | 原生支持 | 需要 MDC | Zap 更简洁 |
| 日志级别 | 6 个级别 | 5 个级别 | 类似 |
| 日志轮转 | Lumberjack | RollingFileAppender | 配置方式不同 |
| 格式化 | JSON/Console | Pattern | Zap 更灵活 |
| 配置 | 代码配置 | XML/YAML | 不同风格 |
十一、常见问题
Q: Zap 和 Logrus 哪个更好?
A: Zap 性能更高(快 4-10 倍),适合高并发场景;Logrus 使用更简单,适合小型项目。
Q: 如何在 Gin 中使用 Zap?
A:
router.Use(ginzap.Ginzap(logger, time.RFC3339, true))
router.Use(ginzap.RecoveryWithZap(logger, true))Q: 如何记录 HTTP 请求日志?
A:
logger.Info("HTTP Request",
zap.String("method", r.Method),
zap.String("path", r.URL.Path),
zap.Int("status", statusCode),
zap.Duration("duration", duration),
)十二、下一步
✅ 已掌握 Zap 日志系统
→ 下一章:综合实战项目(Gin + GORM + Viper + Zap)
→ 完整的 Web 应用开发
祝你编码愉快!🚀
