package logger import ( "context" "fmt" "os" "go.uber.org/zap" "go.uber.org/zap/zapcore" ) // Logger 日志接口 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 Sync() error } // ZapLogger Zap日志实现 type ZapLogger struct { logger *zap.Logger } // Config 日志配置 type Config struct { Level string Format string Output string FilePath string MaxSize int MaxBackups int MaxAge int Compress bool } // NewLogger 创建新的日志实例 func NewLogger(config Config) (Logger, error) { // 设置日志级别 level, err := zapcore.ParseLevel(config.Level) if err != nil { return nil, fmt.Errorf("无效的日志级别: %w", err) } // 配置编码器 var encoder zapcore.Encoder encoderConfig := getEncoderConfig() switch config.Format { case "json": encoder = zapcore.NewJSONEncoder(encoderConfig) case "console": encoder = zapcore.NewConsoleEncoder(encoderConfig) default: encoder = zapcore.NewJSONEncoder(encoderConfig) } // 配置输出 var writeSyncer zapcore.WriteSyncer switch config.Output { case "stdout": writeSyncer = zapcore.AddSync(os.Stdout) case "stderr": writeSyncer = zapcore.AddSync(os.Stderr) case "file": if config.FilePath == "" { config.FilePath = "logs/app.log" } // 确保目录存在 if err := os.MkdirAll("logs", 0755); err != nil { return nil, fmt.Errorf("创建日志目录失败: %w", err) } file, err := os.OpenFile(config.FilePath, os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0666) if err != nil { return nil, fmt.Errorf("打开日志文件失败: %w", err) } writeSyncer = zapcore.AddSync(file) default: writeSyncer = zapcore.AddSync(os.Stdout) } // 创建核心 core := zapcore.NewCore(encoder, writeSyncer, level) // 创建logger logger := zap.New(core, zap.AddCaller(), zap.AddStacktrace(zapcore.ErrorLevel)) return &ZapLogger{ logger: logger, }, nil } // getEncoderConfig 获取编码器配置 func getEncoderConfig() zapcore.EncoderConfig { return zapcore.EncoderConfig{ TimeKey: "timestamp", LevelKey: "level", NameKey: "logger", CallerKey: "caller", FunctionKey: zapcore.OmitKey, MessageKey: "message", StacktraceKey: "stacktrace", LineEnding: zapcore.DefaultLineEnding, EncodeLevel: zapcore.LowercaseLevelEncoder, EncodeTime: zapcore.ISO8601TimeEncoder, EncodeDuration: zapcore.StringDurationEncoder, EncodeCaller: zapcore.ShortCallerEncoder, } } // Debug 调试日志 func (l *ZapLogger) Debug(msg string, fields ...zapcore.Field) { l.logger.Debug(msg, fields...) } // Info 信息日志 func (l *ZapLogger) Info(msg string, fields ...zapcore.Field) { l.logger.Info(msg, fields...) } // Warn 警告日志 func (l *ZapLogger) Warn(msg string, fields ...zapcore.Field) { l.logger.Warn(msg, fields...) } // Error 错误日志 func (l *ZapLogger) Error(msg string, fields ...zapcore.Field) { l.logger.Error(msg, fields...) } // Fatal 致命错误日志 func (l *ZapLogger) Fatal(msg string, fields ...zapcore.Field) { l.logger.Fatal(msg, fields...) } // Panic 恐慌日志 func (l *ZapLogger) Panic(msg string, fields ...zapcore.Field) { l.logger.Panic(msg, fields...) } // With 添加字段 func (l *ZapLogger) With(fields ...zapcore.Field) Logger { return &ZapLogger{ logger: l.logger.With(fields...), } } // WithContext 从上下文添加字段 func (l *ZapLogger) WithContext(ctx context.Context) Logger { // 从上下文中提取常用字段 fields := []zapcore.Field{} if traceID := getTraceIDFromContext(ctx); traceID != "" { fields = append(fields, zap.String("trace_id", traceID)) } if userID := getUserIDFromContext(ctx); userID != "" { fields = append(fields, zap.String("user_id", userID)) } if requestID := getRequestIDFromContext(ctx); requestID != "" { fields = append(fields, zap.String("request_id", requestID)) } return l.With(fields...) } // Sync 同步日志 func (l *ZapLogger) Sync() error { return l.logger.Sync() } // GetZapLogger 获取内部的zap.Logger实例 func (l *ZapLogger) GetZapLogger() *zap.Logger { return l.logger } // getTraceIDFromContext 从上下文获取追踪ID func getTraceIDFromContext(ctx context.Context) string { if traceID := ctx.Value("trace_id"); traceID != nil { if id, ok := traceID.(string); ok { return id } } return "" } // getUserIDFromContext 从上下文获取用户ID func getUserIDFromContext(ctx context.Context) string { if userID := ctx.Value("user_id"); userID != nil { if id, ok := userID.(string); ok { return id } } return "" } // getRequestIDFromContext 从上下文获取请求ID func getRequestIDFromContext(ctx context.Context) string { if requestID := ctx.Value("request_id"); requestID != nil { if id, ok := requestID.(string); ok { return id } } return "" } // Field 创建日志字段的便捷函数 func String(key, val string) zapcore.Field { return zap.String(key, val) } func Int(key string, val int) zapcore.Field { return zap.Int(key, val) } func Int64(key string, val int64) zapcore.Field { return zap.Int64(key, val) } func Float64(key string, val float64) zapcore.Field { return zap.Float64(key, val) } func Bool(key string, val bool) zapcore.Field { return zap.Bool(key, val) } func Error(err error) zapcore.Field { return zap.Error(err) } func Any(key string, val interface{}) zapcore.Field { return zap.Any(key, val) } func Duration(key string, val interface{}) zapcore.Field { return zap.Any(key, val) }