Files
tyapi-server/docs/Zap官方最佳实践日志系统指南.md
2025-08-25 15:44:06 +08:00

11 KiB
Raw Blame History

🚀 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
}

实现类型

  1. ZapLogger: 标准日志器,基于 Zap 官方预设
  2. LevelLogger: 级别分文件日志器,支持按级别分离
  3. 全局日志器: 通过 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 官方最佳实践设计,具有以下优势:

  1. 高性能: 基于 Zap 的高性能实现
  2. 官方推荐: 使用 zap.NewProduction()zap.NewDevelopment() 预设
  3. 强类型: 支持强类型字段,避免运行时错误
  4. 结构化: 支持结构化日志记录
  5. 上下文: 自动提取上下文信息
  6. 灵活配置: 支持文件输出、级别分离、按日分包等
  7. 全局访问: 支持全局日志器访问

通过合理使用,您将获得高性能、结构化的日志系统,满足生产环境的各种需求!

📚 参考资源