454 lines
		
	
	
		
			11 KiB
		
	
	
	
		
			Markdown
		
	
	
	
	
	
		
		
			
		
	
	
			454 lines
		
	
	
		
			11 KiB
		
	
	
	
		
			Markdown
		
	
	
	
	
	
|  | # 🚀 Zap 官方最佳实践日志系统指南
 | |||
|  | 
 | |||
|  | ## 概述
 | |||
|  | 
 | |||
|  | 本日志系统完全基于 [Zap 官方最佳实践](https://betterstack.com/community/guides/logging/go/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()` 方法 | |||
|  | - 便于分布式系统追踪 | |||
|  | 
 | |||
|  | ## 🏗️ 架构设计
 | |||
|  | 
 | |||
|  | ### 核心接口
 | |||
|  | 
 | |||
|  | ```go | |||
|  | 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. 基础使用
 | |||
|  | 
 | |||
|  | ```go | |||
|  | 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. 依赖注入使用
 | |||
|  | 
 | |||
|  | ```go | |||
|  | 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. 上下文日志记录
 | |||
|  | 
 | |||
|  | ```go | |||
|  | 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. 结构化字段
 | |||
|  | 
 | |||
|  | ```go | |||
|  | // 使用强类型字段 | |||
|  | 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. 级别分文件日志
 | |||
|  | 
 | |||
|  | ```go | |||
|  | // 配置启用级别分文件 | |||
|  | 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            # 当前日期 | |||
|  | ``` | |||
|  | 
 | |||
|  | ## ⚙️ 配置选项
 | |||
|  | 
 | |||
|  | ### 基础配置
 | |||
|  | 
 | |||
|  | ```yaml | |||
|  | 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              # 是否使用彩色输出 | |||
|  | ``` | |||
|  | 
 | |||
|  | ### 级别配置
 | |||
|  | 
 | |||
|  | ```yaml | |||
|  | 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. **使用强类型字段**
 | |||
|  | 
 | |||
|  | ```go | |||
|  | // ✅ 推荐:使用强类型字段 | |||
|  | 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. **合理使用日志级别**
 | |||
|  | 
 | |||
|  | ```go | |||
|  | // 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. **上下文信息提取**
 | |||
|  | 
 | |||
|  | ```go | |||
|  | // 在中间件中设置上下文 | |||
|  | 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. **性能优化**
 | |||
|  | 
 | |||
|  | ```go | |||
|  | // ✅ 推荐:延迟计算 | |||
|  | if logger.Core().Enabled(zapcore.DebugLevel) { | |||
|  |     logger.Debug("调试信息", zap.String("data", expensiveOperation())) | |||
|  | } | |||
|  | 
 | |||
|  | // ❌ 避免:总是计算 | |||
|  | logger.Debug("调试信息", zap.String("data", expensiveOperation())) | |||
|  | ``` | |||
|  | 
 | |||
|  | ## 🚨 错误处理
 | |||
|  | 
 | |||
|  | ### 1. **Panic 恢复**
 | |||
|  | 
 | |||
|  | ```go | |||
|  | // 使用 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. **错误日志记录**
 | |||
|  | 
 | |||
|  | ```go | |||
|  | // 记录错误详情 | |||
|  | 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 官方基准测试](https://betterstack.com/community/guides/logging/go/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. **检查日志级别**
 | |||
|  | 
 | |||
|  | ```go | |||
|  | // 检查日志级别是否启用 | |||
|  | if logger.Core().Enabled(zapcore.DebugLevel) { | |||
|  |     logger.Debug("调试信息") | |||
|  | } | |||
|  | ``` | |||
|  | 
 | |||
|  | ### 2. **同步日志**
 | |||
|  | 
 | |||
|  | ```go | |||
|  | // 确保日志写入完成 | |||
|  | defer logger.Sync() | |||
|  | 
 | |||
|  | // 或者在应用关闭时 | |||
|  | func cleanup() { | |||
|  |     logger.Sync() | |||
|  | } | |||
|  | ``` | |||
|  | 
 | |||
|  | ### 3. **验证配置**
 | |||
|  | 
 | |||
|  | ```go | |||
|  | // 验证日志器配置 | |||
|  | 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. **全局访问**: 支持全局日志器访问 | |||
|  | 
 | |||
|  | 通过合理使用,您将获得高性能、结构化的日志系统,满足生产环境的各种需求! | |||
|  | 
 | |||
|  | ## 📚 参考资源
 | |||
|  | 
 | |||
|  | - [Zap 官方文档](https://pkg.go.dev/go.uber.org/zap) | |||
|  | - [Zap 最佳实践指南](https://betterstack.com/community/guides/logging/go/zap/) | |||
|  | - [Zap GitHub 仓库](https://github.com/uber-go/zap) |