zap 是 Uber 开源的高性能日志库,以速度快、结构化输出、低内存占用为核心优势,广泛 云原生、微服务等高性能场景,是 Go 生态中最受欢迎的日志库之一。
一、zap核心优势
- 极致性能:比 logrus 快 2-10 倍,避免反射开销,适合高并发场景;
- 结构化日志:默认输出 JSON 格式,便于日志分析工具(如 ELK)解析;
- 级别分明:支持 Debug/Info/Warn/Error/DPanic/Panic/Fatal 7 级日志;
- 灵活配置:可自定义输出格式、日志级别、输出目标(文件 / 控制台)等;
- 安全性:避免并发写日志的数据竞争问题。
二、安装与基础使用
配置代理解决网络问题:
# 设置为阿里云Go代理
go env -w GOPROXY=https://mirrors.aliyun.com/goproxy/,direct
创建项目
go mod init demo
1.安装zap
go get -u go.uber.org/zap
2. 快速入门(默认配置)
zap 提供两种日志器:
- zap.Logger:高性能,需显式指定字段类型(推荐生产环境);
- zap.SugaredLogger:语法更简洁,支持格式化输出(牺牲少量性能,适合快速开发)。
示例:基础用法
package main
import (
"errors"
"go.uber.org/zap"
)
func main() {
// 1. 创建默认日志器(生产环境推荐)
logger, _ := zap.NewProduction()
defer logger.Sync() // 退出前刷新缓冲区,确保日志写入
// 2. 输出不同级别日志(需显式指定字段类型)
logger.Debug("调试信息", zap.String("user", "admin"), zap.Int("id", 1))
logger.Info("普通信息", zap.String("action", "login"), zap.Bool("success", true))
logger.Warn("警告信息", zap.String("reason", "权限不足"))
err := errors.New("数据库连接失败")
logger.Error("错误信息", zap.Error(err))
// 3. 创建 SugaredLogger(简化语法)
sugar := logger.Sugar()
defer sugar.Sync()
// 4. 格式化输出(类似 fmt.Printf)
sugar.Debugf("调试:用户 %s 尝试访问 %s", "test", "/api")
sugar.Infof("信息:请求耗时 %d ms", 123)
sugar.Warnf("警告:磁盘使用率 %.2f%%", 90.5)
sugar.Errorf("错误:处理 %s 失败,缘由:%v", "订单123", "库存不足")
}
三、核心配置:自定义日志行为
通过 zap.Config 或 zap.NewDevelopment()/zap.NewProduction() 预设配置,自定义日志输出。
1. 关键配置项
|
配置项 |
作用 |
常用值示例 |
|
Level |
日志级别(低于此级别的日志不输出) |
zap.InfoLevel(默认) |
|
Encoding |
输出格式(json 或 console) |
json(结构化)、console(人类可读) |
|
OutputPaths |
输出目标(文件路径或 stdout/stderr) |
[“stdout”, “app.log”](同时输出到控制台和文件) |
|
EncoderConfig |
编码器配置(自定义字段、时间格式等) |
见下文示例 |
2. 自定义配置示例
package main
import (
"go.uber.org/zap"
"go.uber.org/zap/zapcore"
)
func main() {
// 1. 定义日志级别(调试级别,输出所有日志)
level := zap.NewAtomicLevelAt(zap.DebugLevel)
// 2. 配置编码器(自定义输出格式)
encoderConfig := zapcore.EncoderConfig{
TimeKey: "time", // 时间字段名
LevelKey: "level", // 级别字段名
NameKey: "logger", // 日志器名称字段名
CallerKey: "caller", // 调用者(文件名:行号)字段名
MessageKey: "msg", // 消息字段名
StacktraceKey: "stacktrace", // 堆栈跟踪字段名
LineEnding: zapcore.DefaultLineEnding, // 行结束符
EncodeLevel: zapcore.CapitalLevelEncoder, // 级别格式(INFO/ERROR)
EncodeTime: zapcore.ISO8601TimeEncoder, // 时间格式(ISO8601)
EncodeDuration: zapcore.StringDurationEncoder, // duration 格式
EncodeCaller: zapcore.ShortCallerEncoder, // 调用者格式(短文件名)
}
// 3. 构建配置
cfg := zap.Config{
Level: level,
Development: true, // 开发模式(启用堆栈跟踪等)
Encoding: "console", // 控制台格式(非 JSON)
EncoderConfig: encoderConfig,
OutputPaths: []string{"stdout", "app.log"}, // 输出到控制台和文件
ErrorOutputPaths: []string{"stderr"}, // 错误日志输出到 stderr
}
// 4. 创建日志器
logger, err := cfg.Build()
if err != nil {
panic(err)
}
defer logger.Sync()
// 5. 使用日志器
logger.Info("自定义配置日志",
zap.String("module", "user"),
zap.Int("count", 100)
)
logger.Error("操作失败",
zap.String("operation", "delete"),
zap.Error(zap.Error("记录不存在"))
)
}
四、高级特性
1. 字段复用(zap.Fields)
对重复出现的字段(如 request_id),可预先定义并复用,减少代码冗余:
// 定义通用字段
commonFields := zap.Fields(
zap.String("service", "user-service"),
zap.String("env", "production"),
)
// 创建带通用字段的日志器
loggerWithFields := logger.With(commonFields)
// 输出时自动包含通用字段
loggerWithFields.Info("用户注册")
// 输出包含 "service":"user-service", "env":"production"
2. 日志轮转(配合lumberjack)
安装lumberjack:
go get gopkg.in/natefinch/lumberjack.v2
或者
# 使用 GitHub 地址直接安装
go get github.com/natefinch/lumberjack
zap 本身不支持日志轮转,需配合 lumberjack 实现按大小 / 时间分割日志:
package main
import (
"go.uber.org/zap"
"go.uber.org/zap/zapcore"
"gopkg.in/natefinch/lumberjack.v2"
)
func main() {
// 1. 配置 lumberjack 日志轮转
hook := &lumberjack.Logger{
Filename: "app.log", // 日志文件路径
MaxSize: 100, // 单个文件最大大小(MB)
MaxBackups: 30, // 最大备份文件数
MaxAge: 7, // 最大保留天数
Compress: true, // 是否压缩备份文件
}
defer hook.Close()
// 2. 将 lumberjack 作为输出目标
core := zapcore.NewCore(
zapcore.NewJSONEncoder(zap.NewProductionEncoderConfig()), // JSON 编码器
zapcore.AddSync(hook), // 输出到文件
zap.InfoLevel // 日志级别
)
// 3. 创建日志器
logger := zap.New(core, zap.AddCaller()) // 启用调用者信息
defer logger.Sync()
logger.Info("带轮转功能的日志")
}
3. 上下文日志(zap.Logger传递)
在请求链路中传递日志器,附加上下文信息(如 trace_id),便于追踪全链路日志:
// 处理请求的函数
func handleRequest(logger *zap.Logger, traceID string) {
// 附加 trace_id 到日志器
reqLogger := logger.With(zap.String("trace_id", traceID))
reqLogger.Info("开始处理请求")
// 传递给子函数
processData(reqLogger)
}
func processData(logger *zap.Logger) {
logger.Debug("处理数据")
}
五、最佳实践
- 优先使用 zap.Logger:生产环境追求性能,避免 SugaredLogger 的格式化开销;
- 设置合理日志级别:开发环境用 DebugLevel,生产环境用 InfoLevel 或 WarnLevel,减少日志量;
- 日志轮转必配置:避免单文件过大,结合 lumberjack 按大小 / 时间分割;
- 包含关键上下文:每条日志应包含 service、env、trace_id 等标识,便于问题定位;
- 错误日志附加堆栈:Error 及以上级别日志提议包含堆栈跟踪(zap.AddStacktrace(zap.ErrorLevel));
- ** defer logger.Sync ()**:确保程序退出时日志缓冲区数据写入文件。
六、总结
zap 以高性能和结构化输出为核心,通过灵活配置满足不同场景需求,尤其适合对性能敏感的云原生、微服务等场景。入门只需掌握基础日志器创建、级别输出和自定义配置,进阶可结合日志轮转和上下文传递,实现生产级日志方案。