11 KiB
11 KiB
🚀 Zap 官方最佳实践日志系统指南
概述
本日志系统完全基于 Zap 官方最佳实践 设计,使用 zap.NewProduction() 和 zap.NewDevelopment() 预设,提供高性能、结构化的日志记录。
✨ 核心特性
1. 基于 Zap 官方预设
- 使用
zap.NewProduction()生产环境预设 - 使用
zap.NewDevelopment()开发环境预设 - 自动添加调用者信息、堆栈跟踪等
2. 全局日志器支持
- 支持
zap.ReplaceGlobals()全局替换 - 提供
logger.L()和logger.GetGlobalLogger()访问 - 符合 Zap 官方推荐的使用方式
3. 强类型字段支持
- 使用
zap.String(),zap.Int(),zap.Error()等强类型字段 - 避免运行时类型错误
- 提供最佳性能
4. 上下文日志记录
- 自动从上下文提取
request_id,user_id,trace_id - 支持
WithContext()方法 - 便于分布式系统追踪
🏗️ 架构设计
核心接口
type Logger interface {
// 基础日志方法
Debug(msg string, fields ...zapcore.Field)
Info(msg string, fields ...zapcore.Field)
Warn(msg string, fields ...zapcore.Field)
Error(msg string, fields ...zapcore.Field)
Fatal(msg string, fields ...zapcore.Field)
Panic(msg string, fields ...zapcore.Field)
// 结构化日志方法
With(fields ...zapcore.Field) Logger
WithContext(ctx context.Context) Logger
Named(name string) Logger
// 同步和清理
Sync() error
Core() zapcore.Core
// 获取原生 Zap Logger
GetZapLogger() *zap.Logger
}
实现类型
- ZapLogger: 标准日志器,基于 Zap 官方预设
- LevelLogger: 级别分文件日志器,支持按级别分离
- 全局日志器: 通过
zap.ReplaceGlobals()提供全局访问
🚀 使用方法
1. 基础使用
package main
import (
"go.uber.org/zap"
"tyapi-server/internal/shared/logger"
)
func main() {
// 初始化全局日志器
config := logger.Config{
Development: true,
Output: "stdout",
Format: "console",
}
if err := logger.InitGlobalLogger(config); err != nil {
panic(err)
}
// 使用全局日志器
logger.L().Info("应用启动成功")
// 或者获取全局日志器
globalLogger := logger.GetGlobalLogger()
globalLogger.Info("使用全局日志器")
}
2. 依赖注入使用
type ProductService struct {
logger logger.Logger
}
func NewProductService(logger logger.Logger) *ProductService {
return &ProductService{logger: logger}
}
func (s *ProductService) CreateProduct(ctx context.Context, product *Product) error {
// 记录操作日志
s.logger.Info("创建产品",
zap.String("product_id", product.ID),
zap.String("product_name", product.Name),
zap.String("user_id", product.CreatedBy),
)
// 业务逻辑...
return nil
}
3. 上下文日志记录
func (s *ProductService) GetProduct(ctx context.Context, id string) (*Product, error) {
// 自动从上下文提取字段
logger := s.logger.WithContext(ctx)
logger.Info("获取产品信息",
zap.String("product_id", id),
zap.String("operation", "get_product"),
)
// 业务逻辑...
return product, nil
}
4. 结构化字段
// 使用强类型字段
s.logger.Info("用户登录",
zap.String("username", "john_doe"),
zap.Int("user_id", 12345),
zap.String("ip_address", "192.168.1.100"),
zap.String("user_agent", r.UserAgent()),
zap.Time("login_time", time.Now()),
)
// 记录错误
if err != nil {
s.logger.Error("数据库操作失败",
zap.Error(err),
zap.String("operation", "create_user"),
zap.String("table", "users"),
)
}
5. 级别分文件日志
// 配置启用级别分文件
config := logger.Config{
EnableLevelSeparation: true,
Output: "file",
LogDir: "logs",
UseDaily: true,
LevelConfigs: map[string]interface{}{
"debug": map[string]interface{}{
"max_size": 50,
"max_backups": 3,
"max_age": 7,
},
"error": map[string]interface{}{
"max_size": 200,
"max_backups": 10,
"max_age": 90,
},
},
}
// 创建级别分文件日志器
levelLogger, err := logger.NewLevelLogger(logger.LevelLoggerConfig{
BaseConfig: config,
EnableLevelSeparation: true,
LevelConfigs: convertLevelConfigs(config.LevelConfigs),
})
📁 日志文件结构
按级别分文件
logs/
├── 2024-01-01/
│ ├── debug.log # 调试日志
│ ├── info.log # 信息日志
│ ├── warn.log # 警告日志
│ ├── error.log # 错误日志
│ ├── fatal.log # 致命错误日志
│ └── panic.log # 恐慌错误日志
└── app.log # 主日志文件
按日期分包
logs/
├── 2024-01-01/
│ ├── app.log
│ └── error.log
├── 2024-01-02/
│ ├── app.log
│ └── error.log
└── app.log # 当前日期
⚙️ 配置选项
基础配置
logger:
# 环境配置
development: true # 是否为开发环境
# 输出配置
output: "file" # 输出方式: stdout, stderr, file
format: "json" # 输出格式: json, console
log_dir: "logs" # 日志目录
# 文件配置
max_size: 100 # 单个文件最大大小(MB)
max_backups: 5 # 最大备份文件数
max_age: 30 # 最大保留天数
compress: true # 是否压缩
# 高级功能
use_daily: true # 是否按日分包
enable_level_separation: true # 是否启用按级别分文件
use_color: false # 是否使用彩色输出
级别配置
logger:
level_configs:
debug:
max_size: 50 # 50MB
max_backups: 3 # 3个备份
max_age: 7 # 7天
compress: true
info:
max_size: 100 # 100MB
max_backups: 5 # 5个备份
max_age: 30 # 30天
compress: true
error:
max_size: 200 # 200MB
max_backups: 10 # 10个备份
max_age: 90 # 90天
compress: true
🔧 最佳实践
1. 使用强类型字段
// ✅ 推荐:使用强类型字段
logger.Info("用户操作",
zap.String("user_id", userID),
zap.String("action", "login"),
zap.Time("timestamp", time.Now()),
)
// ❌ 避免:使用 Any 字段
logger.Info("用户操作",
zap.Any("user_id", userID),
zap.Any("action", "login"),
zap.Any("timestamp", time.Now()),
)
2. 合理使用日志级别
// Debug: 详细的调试信息
logger.Debug("SQL查询", zap.String("query", sql))
// Info: 重要的业务事件
logger.Info("用户注册成功", zap.String("user_id", userID))
// Warn: 警告信息,不影响功能
logger.Warn("数据库连接池使用率过高", zap.Int("usage", 85))
// Error: 错误信息,功能受影响
logger.Error("数据库连接失败", zap.Error(err))
// Fatal: 致命错误,应用无法继续
logger.Fatal("配置文件加载失败", zap.Error(err))
3. 上下文信息提取
// 在中间件中设置上下文
func LoggingMiddleware(logger logger.Logger) gin.HandlerFunc {
return func(c *gin.Context) {
// 生成请求ID
requestID := uuid.New().String()
// 设置上下文
ctx := context.WithValue(c.Request.Context(), "request_id", requestID)
ctx = context.WithValue(ctx, "user_id", getUserID(c))
ctx = context.WithValue(ctx, "trace_id", getTraceID(c))
c.Request = c.Request.WithContext(ctx)
// 记录请求日志
logger.WithContext(ctx).Info("收到请求",
zap.String("method", c.Request.Method),
zap.String("path", c.Request.URL.Path),
zap.String("client_ip", c.ClientIP()),
)
c.Next()
}
}
4. 性能优化
// ✅ 推荐:延迟计算
if logger.Core().Enabled(zapcore.DebugLevel) {
logger.Debug("调试信息", zap.String("data", expensiveOperation()))
}
// ❌ 避免:总是计算
logger.Debug("调试信息", zap.String("data", expensiveOperation()))
🚨 错误处理
1. Panic 恢复
// 使用 panic 恢复中间件
func PanicRecoveryMiddleware(logger *zap.Logger) gin.HandlerFunc {
return gin.RecoveryWithWriter(&panicLogger{logger: logger})
}
type panicLogger struct {
logger *zap.Logger
}
func (pl *panicLogger) Write(p []byte) (n int, err error) {
pl.logger.Error("系统发生严重错误",
zap.String("error_type", "panic"),
zap.String("stack_trace", string(p)),
zap.String("timestamp", time.Now().Format("2006-01-02 15:04:05")),
)
return len(p), nil
}
2. 错误日志记录
// 记录错误详情
if err != nil {
logger.Error("操作失败",
zap.Error(err),
zap.String("operation", "create_user"),
zap.String("user_id", userID),
zap.String("stack_trace", string(debug.Stack())),
)
return err
}
📊 性能基准
基于 Zap 官方基准测试:
| 包 | 时间 | 相对于 Zap | 内存分配 |
|---|---|---|---|
| zap | 193 ns/op | +0% | 0 allocs/op |
| zap (sugared) | 227 ns/op | +18% | 1 allocs/op |
| zerolog | 81 ns/op | -58% | 0 allocs/op |
| slog | 322 ns/op | +67% | 0 allocs/op |
🔍 调试和故障排除
1. 检查日志级别
// 检查日志级别是否启用
if logger.Core().Enabled(zapcore.DebugLevel) {
logger.Debug("调试信息")
}
2. 同步日志
// 确保日志写入完成
defer logger.Sync()
// 或者在应用关闭时
func cleanup() {
logger.Sync()
}
3. 验证配置
// 验证日志器配置
config := logger.Config{
Development: true,
Output: "stdout",
Format: "console",
}
logger, err := logger.NewLogger(config)
if err != nil {
log.Fatalf("创建日志器失败: %v", err)
}
🎯 总结
本日志系统完全基于 Zap 官方最佳实践设计,具有以下优势:
- 高性能: 基于 Zap 的高性能实现
- 官方推荐: 使用
zap.NewProduction()和zap.NewDevelopment()预设 - 强类型: 支持强类型字段,避免运行时错误
- 结构化: 支持结构化日志记录
- 上下文: 自动提取上下文信息
- 灵活配置: 支持文件输出、级别分离、按日分包等
- 全局访问: 支持全局日志器访问
通过合理使用,您将获得高性能、结构化的日志系统,满足生产环境的各种需求!