zap日志的使用

0

性能测试

  • 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(如 JSONEncoderConsoleEncoder)。

  • 所有字段都被编码为强类型的键值对,避免 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...)
}