Zap日志库指南

Zap是由Uber开发的高性能Go日志库,特别适合后端服务。

入门:

第一目:为什么要选择Zap?

在Go语言的日志库中,流行的有 zap、logrus...甚至还有Go自带的原生的log。

**zap:**极致性能(低延迟、低内存占用)、支持多种日志级别、偏底层、Uber官方维护并持续更新

**Logrus:**灵活易用、支持多种日志级别、高层封装支持全局调用、基本停滞更新

**原生log包:**简单易用的基础工具、学习成本低。高并发场景下性能也较低(文末有解释)

第二目:安装Zap

Go 复制代码
go get -u go.uber.org/zap

第三目:go语言简答的启动

Go 复制代码
package main

import (
	"go.uber.org/zap"
	"time"
)

func main() {
	// 创建生产环境使用的Logger(JSON格式)
	logger, _ := zap.NewProduction()
	defer logger.Sync() // 刷新缓冲区
    // Sync的作用,就是强制输出还在缓存区的内容

	// 记录不同级别的日志
	logger.Info("服务启动",
		zap.String("service", "user-api"),
		zap.Time("start_time", time.Now()),
	)

    // 打印
	logger.Warn("数据库连接缓慢",
		zap.Duration("duration", 150*time.Millisecond),
	)

	logger.Error("无法连接Redis",
		zap.String("host", "redis:6379"),
		zap.Error(err), // 假设有err变量
	)
}
输出示例
Go 复制代码
{"level":"info","ts":1690000000.1234567,"msg":"服务启动","service":"user-api","start_time":"2023-07-22T10:00:00Z"}
{"level":"warn","ts":1690000001.2345678,"msg":"数据库连接缓慢","duration":0.15}
{"level":"error","ts":1690000002.3456789,"msg":"无法连接Redis","host":"redis:6379","error":"connection refused"}

进阶:

第一目:自定义Logger配置:

Go 复制代码
func main() {
	// 创建自定义配置
	config := zap.NewProductionConfig()
	
	// 修改配置
	config.Level = zap.NewAtomicLevelAt(zap.DebugLevel) // 设置日志级别
	config.OutputPaths = []string{"stdout", "logs/app.log"} // 输出到控制台和文件
	config.EncoderConfig.TimeKey = "timestamp" // 自定义时间字段名
	config.EncoderConfig.EncodeTime = zapcore.ISO8601TimeEncoder // ISO时间格式
	
	// 构建Logger
	logger, err := config.Build()
	if err != nil {
		panic(err)
	}
	defer logger.Sync()
	
	// 使用Logger
	logger.Debug("调试信息", zap.String("module", "authentication"))
}

第二目:创建开发环境Logger(易读格式)

Go 复制代码
func main() {
	// 开发环境使用更易读的控制台输出
	logger, _ := zap.NewDevelopment()
	defer logger.Sync()
	
	logger.Info("开发模式日志",
		zap.String("url", "/api/users"),
		zap.Int("status", 200),
	)
}

// 2023-07-22T10:00:00.123Z	INFO	开发模式日志	{"url": "/api/users", "status": 200}

第三目:使用Sugar Logger(简化语法)

Sugar是zap提供的一种更灵活,更易用的日志记录接口。

格式化字符串与结构字段,语法更接近传统日志库 ,缺点就是性能略低

Go 复制代码
func main() {
	logger, _ := zap.NewProduction()
	defer logger.Sync()

	// 创建Sugar Logger
	sugar := logger.Sugar()

	// 简化语法(性能略低于标准Logger)
	sugar.Infow("用户注册成功",
		"user_id", 12345,
		"email", "user@example.com",
		"registration_time", time.Now(),
	)

	// 格式化日志
	sugar.Infof("新请求: %s %s", "GET", "/api/users")
}

{"level":"info","ts":1753187495.5488393,"caller":"zap/main.go:92","msg":"用户注册成功","user_id":12345,"email":"user@example.com","registration_time":1753187495.5488393}

第四目:封装Logger(推荐)

对zap,封装成Init与Sync两个函数,需要时,调用即可。

Go 复制代码
// logger/logger.go
package logger

import (
	"go.uber.org/zap"
)

var Log *zap.Logger

func Init(debug bool) {
	var err error
	if debug {
		Log, err = zap.NewDevelopment()
	} else {
		Log, err = zap.NewProduction()
	}
	
	if err != nil {
		panic(err)
	}
	
	// 添加全局字段(服务名、版本等)
	Log = Log.With(
		zap.String("service", "user-service"),
		zap.String("version", "1.0.0"),
	)
}

func Sync() {
	_ = Log.Sync()
}

第五目:在项目中如何调用

Go 复制代码
// main.go
package main

import (
	"your-project/logger"
	"net/http"
)

func main() {
	// 初始化Logger
	logger.Init(true) // 开发模式
	defer logger.Sync()
	
	http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
		logger.Log.Info("处理请求",
			zap.String("path", r.URL.Path),
			zap.String("method", r.Method),
		)
		
		w.Write([]byte("Hello, World!"))
	})
	
	logger.Log.Info("启动HTTP服务", zap.String("port", "8080"))
	http.ListenAndServe(":8080", nil)
}

第六目:日志切割

一、安装Lumberjack:

bash 复制代码
go get gopkg.in/natefinch/lumberjack.v2

二、配置日志切割

Go 复制代码
import (
	"go.uber.org/zap"
	"go.uber.org/zap/zapcore"
	"gopkg.in/natefinch/lumberjack.v2"
)

func getLogWriter() zapcore.WriteSyncer {
	lumberJackLogger := &lumberjack.Logger{
		Filename:   "logs/app.log", // 日志文件路径
		MaxSize:    100,           // 单个文件最大大小(MB)
		MaxBackups: 5,             // 保留旧文件的最大个数
		MaxAge:     30,            // 保留旧文件的最大天数
		Compress:   true,          // 是否压缩/归档旧文件
	}
	return zapcore.AddSync(lumberJackLogger)
}

func main() {
	encoder := getEncoder()
	writer := getLogWriter()
	core := zapcore.NewCore(encoder, writer, zapcore.DebugLevel)
	
	logger := zap.New(core)
	defer logger.Sync()
	
	// 使用logger...
}

func getEncoder() zapcore.Encoder {
	encoderConfig := zap.NewProductionEncoderConfig()
	encoderConfig.EncodeTime = zapcore.ISO8601TimeEncoder
	return zapcore.NewJSONEncoder(encoderConfig)
}

项目中,常用的就这些。

后续会补充。


日志级别的合理使用:

  • DEBUG:调试信息

  • INFO:重要流程信息

  • WARN:预期之外但可恢复的情况

  • ERROR:需要立即关注的错误

  • DPANIC/ PANIC/FATAL:严重错误(谨慎使用)


注:

  • Zap 使用 无反射设计 (通过预定义字段类型避免运行时反射),且 内存预分配 减少 GC 压力。
  • 原生 log 依赖 fmt.Sprintf() 进行字符串拼接,在高并发下性能损耗显著。
相关推荐
爷_5 小时前
字节跳动震撼开源Coze平台!手把手教你本地搭建AI智能体开发环境
前端·人工智能·后端
不过普通话一乙不改名8 小时前
第一章:Go语言基础入门之函数
开发语言·后端·golang
豌豆花下猫9 小时前
Python 潮流周刊#112:欢迎 AI 时代的编程新人
后端·python·ai
Electrolux9 小时前
你敢信,不会点算法没准你赛尔号都玩不明白
前端·后端·算法
whhhhhhhhhw9 小时前
Go语言-fmt包中Print、Println与Printf的区别
开发语言·后端·golang
Spliceㅤ10 小时前
Spring框架
java·服务器·后端·spring·servlet·java-ee·tomcat
IguoChan10 小时前
10. Redis Operator (3) —— 监控配置
后端
ん贤12 小时前
GoWeb必备理论
go·web
Micro麦可乐12 小时前
前端与 Spring Boot 后端无感 Token 刷新 - 从原理到全栈实践
前端·spring boot·后端·jwt·refresh token·无感token刷新