package logger import ( "context" "path/filepath" "time" "go.uber.org/zap" "go.uber.org/zap/zapcore" "gopkg.in/natefinch/lumberjack.v2" ) // Logger 日志器接口 - 基于 Zap 官方推荐 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 } // Config 日志配置 - 基于 Zap 官方配置结构 type Config struct { // 基础配置 Level string `mapstructure:"level"` // 日志级别 Format string `mapstructure:"format"` // 输出格式 (json/console) Output string `mapstructure:"output"` // 输出方式 (stdout/stderr/file) LogDir string `mapstructure:"log_dir"` // 日志目录 UseDaily bool `mapstructure:"use_daily"` // 是否按日分包 UseColor bool `mapstructure:"use_color"` // 是否使用彩色输出(仅console格式) // 文件配置 MaxSize int `mapstructure:"max_size"` // 单个文件最大大小(MB) MaxBackups int `mapstructure:"max_backups"` // 最大备份文件数 MaxAge int `mapstructure:"max_age"` // 最大保留天数 Compress bool `mapstructure:"compress"` // 是否压缩 // 高级功能 EnableLevelSeparation bool `mapstructure:"enable_level_separation"` // 是否启用按级别分文件 LevelConfigs map[string]interface{} `mapstructure:"level_configs"` // 各级别配置(使用 interface{} 避免循环依赖) EnableRequestLogging bool `mapstructure:"enable_request_logging"` // 是否启用请求日志 EnablePerformanceLog bool `mapstructure:"enable_performance_log"` // 是否启用性能日志 // 开发环境配置 Development bool `mapstructure:"development"` // 是否为开发环境 Sampling bool `mapstructure:"sampling"` // 是否启用采样 } // ZapLogger Zap日志实现 - 基于官方推荐 type ZapLogger struct { logger *zap.Logger } // NewLogger 创建新的日志实例 - 使用 Zap 官方推荐的方式 func NewLogger(config Config) (Logger, error) { var logger *zap.Logger var err error // 根据环境创建合适的日志器 if config.Development { logger, err = zap.NewDevelopment( zap.AddCaller(), zap.AddCallerSkip(1), zap.AddStacktrace(zapcore.ErrorLevel), ) } else { logger, err = zap.NewProduction( zap.AddCaller(), zap.AddCallerSkip(1), zap.AddStacktrace(zapcore.ErrorLevel), ) } if err != nil { return nil, err } // 如果配置为文件输出,需要手动设置 Core if config.Output == "file" { writeSyncer, err := createFileWriteSyncer(config) if err != nil { return nil, err } // 创建新的 Core 并替换 encoder := getEncoder(config.Format, config) level := getLogLevel(config.Level) core := zapcore.NewCore(encoder, writeSyncer, level) logger = zap.New(core, zap.AddCaller(), zap.AddCallerSkip(1), zap.AddStacktrace(zapcore.ErrorLevel)) } return &ZapLogger{ logger: logger, }, nil } // 实现 Logger 接口 func (z *ZapLogger) Debug(msg string, fields ...zapcore.Field) { z.logger.Debug(msg, fields...) } func (z *ZapLogger) Info(msg string, fields ...zapcore.Field) { z.logger.Info(msg, fields...) } func (z *ZapLogger) Warn(msg string, fields ...zapcore.Field) { z.logger.Warn(msg, fields...) } func (z *ZapLogger) Error(msg string, fields ...zapcore.Field) { z.logger.Error(msg, fields...) } func (z *ZapLogger) Fatal(msg string, fields ...zapcore.Field) { z.logger.Fatal(msg, fields...) } func (z *ZapLogger) Panic(msg string, fields ...zapcore.Field) { z.logger.Panic(msg, fields...) } func (z *ZapLogger) With(fields ...zapcore.Field) Logger { return &ZapLogger{logger: z.logger.With(fields...)} } func (z *ZapLogger) WithContext(ctx context.Context) Logger { // 从上下文提取字段 fields := extractFieldsFromContext(ctx) return &ZapLogger{logger: z.logger.With(fields...)} } func (z *ZapLogger) Named(name string) Logger { return &ZapLogger{logger: z.logger.Named(name)} } func (z *ZapLogger) Sync() error { return z.logger.Sync() } func (z *ZapLogger) Core() zapcore.Core { return z.logger.Core() } func (z *ZapLogger) GetZapLogger() *zap.Logger { return z.logger } // 全局日志器 - 基于 Zap 官方推荐 var globalLogger *zap.Logger // InitGlobalLogger 初始化全局日志器 func InitGlobalLogger(config Config) error { var logger *zap.Logger var err error // 根据环境创建合适的日志器 if config.Development { logger, err = zap.NewDevelopment( zap.AddCaller(), zap.AddCallerSkip(1), zap.AddStacktrace(zapcore.ErrorLevel), ) } else { logger, err = zap.NewProduction( zap.AddCaller(), zap.AddCallerSkip(1), zap.AddStacktrace(zapcore.ErrorLevel), ) } if err != nil { return err } // 如果配置为文件输出,需要手动设置 Core if config.Output == "file" { writeSyncer, err := createFileWriteSyncer(config) if err != nil { return err } // 创建新的 Core 并替换 encoder := getEncoder(config.Format, config) level := getLogLevel(config.Level) core := zapcore.NewCore(encoder, writeSyncer, level) logger = zap.New(core, zap.AddCaller(), zap.AddCallerSkip(1), zap.AddStacktrace(zapcore.ErrorLevel)) } // 替换全局日志器 zap.ReplaceGlobals(logger) globalLogger = logger return nil } // GetGlobalLogger 获取全局日志器 func GetGlobalLogger() *zap.Logger { if globalLogger == nil { // 如果没有初始化,使用默认的生产环境配置 globalLogger = zap.Must(zap.NewProduction()) } return globalLogger } // L 获取全局日志器(Zap 官方推荐的方式) func L() *zap.Logger { return zap.L() } // 辅助函数 func getLogLevel(level string) zapcore.Level { switch level { case "debug": return zapcore.DebugLevel case "info": return zapcore.InfoLevel case "warn": return zapcore.WarnLevel case "error": return zapcore.ErrorLevel case "fatal": return zapcore.FatalLevel case "panic": return zapcore.PanicLevel default: return zapcore.InfoLevel } } func getEncoder(format string, config Config) zapcore.Encoder { encoderConfig := getEncoderConfig(config) if format == "console" { return zapcore.NewConsoleEncoder(encoderConfig) } return zapcore.NewJSONEncoder(encoderConfig) } func getEncoderConfig(config Config) zapcore.EncoderConfig { encoderConfig := zap.NewProductionEncoderConfig() if config.Development { encoderConfig = zap.NewDevelopmentEncoderConfig() } // 自定义时间格式 encoderConfig.TimeKey = "timestamp" encoderConfig.EncodeTime = zapcore.ISO8601TimeEncoder // 自定义级别格式 encoderConfig.EncodeLevel = zapcore.CapitalLevelEncoder // 自定义调用者格式 encoderConfig.EncodeCaller = zapcore.ShortCallerEncoder return encoderConfig } func createFileWriteSyncer(config Config) (zapcore.WriteSyncer, error) { // 使用 lumberjack 进行日志轮转 rotator := &lumberjack.Logger{ Filename: getLogFilePath(config), MaxSize: config.MaxSize, MaxBackups: config.MaxBackups, MaxAge: config.MaxAge, Compress: config.Compress, } return zapcore.AddSync(rotator), nil } func getLogFilePath(config Config) string { if config.UseDaily { // 按日期分包 date := time.Now().Format("2006-01-02") return filepath.Join(config.LogDir, date, "app.log") } return filepath.Join(config.LogDir, "app.log") } func extractFieldsFromContext(ctx context.Context) []zapcore.Field { var fields []zapcore.Field // 提取请求ID if requestID := ctx.Value("request_id"); requestID != nil { if id, ok := requestID.(string); ok { fields = append(fields, zap.String("request_id", id)) } } // 提取用户ID if userID := ctx.Value("user_id"); userID != nil { if id, ok := userID.(string); ok { fields = append(fields, zap.String("user_id", id)) } } // 提取跟踪ID if traceID := ctx.Value("trace_id"); traceID != nil { if id, ok := traceID.(string); ok { fields = append(fields, zap.String("trace_id", id)) } } return fields }