zap日志的使用
性能测试
1w条
开始日志性能测试...
开始Logrus性能测试,写入10000条日志...
Logrus写入10000条日志耗时: 112.2748ms
Logrus平均每条日志耗时: 11.227µs
Logrus每秒写入日志数: 89067
开始Zap性能测试,写入10000条日志...
Zap写入10000条日志耗时: 31.3495ms
Zap平均每条日志耗时: 3.134µs
Zap每秒写入日志数: 318984
性能比较:
Zap比Logrus快 3.58x
日志写入完成!
日志文件:
- Logrus: logrus_output.log
- Zap: zap_output.log
10w条
开始日志性能测试...
开始Logrus性能测试,写入100000条日志...
Logrus写入100000条日志耗时: 996.1245ms
Logrus平均每条日志耗时: 9.961µs
Logrus每秒写入日志数: 100389
开始Zap性能测试,写入100000条日志...
Zap写入100000条日志耗时: 69.2183ms
Zap平均每条日志耗时: 692ns
Zap每秒写入日志数: 1444705
性能比较:
Zap比Logrus快 14.39x
日志写入完成!
日志文件:
- Logrus: logrus_output.log
- Zap: zap_output.log
可以看到zap的性能要比logrus快一个数量级了。
性能分析
使用预分配对象 + 自定义 encoder(如
JSONEncoder
、ConsoleEncoder
)。所有字段都被编码为强类型的键值对,避免 interface{} 类型带来的反射和额外开销。
典型写法:
go 复制编辑 logger.Info("Request received", zap.String("url", url), zap.Int("status", status), )
尽可能使用对象池(sync.Pool),避免频繁申请/释放内存。
避免 interface{},提升运行时效率。
内存使用几乎为0
基本使用
基本配置
func NewZapLogger() *ZapLogger {
config := zap.NewProductionConfig()
config.Level = zap.NewAtomicLevelAt(zap.InfoLevel)
config.OutputPaths = []string{"zap_output.log"}
config.ErrorOutputPaths = []string{"zap_output.log"}
config.EncoderConfig.TimeKey = "timestamp"
config.EncoderConfig.EncodeTime = zapcore.ISO8601TimeEncoder
logger, _ := config.Build()
return &ZapLogger{
logger: logger,
}
}
日志切割
func NewLogger() *zap.Logger {
// 日志分割配置
hook := &lumberjack.Logger{
Filename: "./logs/app.log", // 日志文件位置
MaxSize: 10, // 每个日志文件最大 10 MB
MaxBackups: 5, // 最多保留 5 个旧文件
MaxAge: 30, // 保留 30 天
Compress: true, // 是否压缩
}
writeSyncer := zapcore.AddSync(hook)
encoder := zapcore.NewJSONEncoder(zap.NewProductionEncoderConfig())
core := zapcore.NewCore(encoder, writeSyncer, zapcore.InfoLevel)
logger := zap.New(core, zap.AddCaller(), zap.AddCallerSkip(1))
return logger
}
sync方法
logger.Sync()
是 zap
中用于**刷新缓冲区、确保日志写入磁盘(或输出流)**的关键方法。
✅ Sync()
的作用:强制把日志输出的内容“冲”到目标(文件、控制台等),防止丢日志
zap
的底层使用了 io.Writer
接口(比如文件、stdout 等),而很多 Writer
是带有缓冲的。例如:
日志写入文件时,OS 会先写入内存缓冲区,之后再写入磁盘。
如果程序突然退出(或崩溃),未刷新的日志就会丢失。
调用 Sync()
就是为了:
保证缓冲区的数据全部写入底层输出。
保证关键日志(如 panic、error)能真正写到日志里。
可以在main函数中 defer 一个sync方法
使用
# 普通logger
logger, _ := zap.NewProduction()
defer logger.Sync()
logger.Info("User logged in",
zap.String("username", "alice"),
zap.Int("userID", 42),
)
# suggerlogger 性能稍微差一点 使用了反射
logger, _ := zap.NewProduction()
sugar := logger.Sugar()
defer sugar.Sync()
sugar.Infof("User %s logged in with id %d", "alice", 42)
sugar.Infow("User logged in",
"username", "alice",
"userID", 42,
)
封装实现
func (z *ZapLogger) Info(msg string, fields map[string]interface{}) {
zapFields := make([]zap.Field, 0, len(fields))
for k, v := range fields {
zapFields = append(zapFields, zap.Any(k, v))
}
z.logger.Info(msg, zapFields...)
}
func (z *ZapLogger) Error(msg string, fields map[string]interface{}) {
zapFields := make([]zap.Field, 0, len(fields))
for k, v := range fields {
zapFields = append(zapFields, zap.Any(k, v))
}
z.logger.Error(msg, zapFields...)
}
func (z *ZapLogger) Debug(msg string, fields map[string]interface{}) {
zapFields := make([]zap.Field, 0, len(fields))
for k, v := range fields {
zapFields = append(zapFields, zap.Any(k, v))
}
z.logger.Debug(msg, zapFields...)
}
func (z *ZapLogger) Warn(msg string, fields map[string]interface{}) {
zapFields := make([]zap.Field, 0, len(fields))
for k, v := range fields {
zapFields = append(zapFields, zap.Any(k, v))
}
z.logger.Warn(msg, zapFields...)
}