package logging import ( "fmt" "os" "path/filepath" "runtime" "strings" "time" "github.com/sirupsen/logrus" ) // Logger 封装了 logrus.Logger,提供结构化日志功能 // 该结构体实现了统一的日志记录接口,支持多种输出格式和级别 type Logger struct { *logrus.Logger config *LoggingConfig entryPool chan *LogEntry closed bool } // LoggingConfig 日志配置 type LoggingConfig struct { Level string `yaml:"level" json:"level"` // 日志级别: debug, info, warn, error, fatal Format string `yaml:"format" json:"format"` // 输出格式: json, text Output string `yaml:"output" json:"output"` // 输出目标: stdout, file, both FilePath string `yaml:"file_path" json:"file_path"` // 日志文件路径 MaxSize int `yaml:"max_size_mb" json:"max_size_mb"` // 单个文件最大大小(MB) MaxBackups int `yaml:"max_backups" json:"max_backups"` // 最大备份文件数 MaxAge int `yaml:"max_age_days" json:"max_age_days"` // 最大保存天数 Compress bool `yaml:"compress" json:"compress"` // 是否压缩旧日志 ReportCaller bool `yaml:"report_caller" json:"report_caller"` // 是否记录调用者信息 EnableColor bool `yaml:"enable_color" json:"enable_color"` // 是否启用颜色输出 } // NewLogger 创建新的日志记录器 // 返回配置好的 Logger 实例 func NewLogger(config *LoggingConfig) (*Logger, error) { if config == nil { config = &LoggingConfig{ Level: "info", Format: "text", Output: "stdout", MaxSize: 100, MaxBackups: 3, MaxAge: 30, Compress: true, ReportCaller: false, EnableColor: true, } } // 创建 logrus 实例 logger := logrus.New() // 设置日志级别 level, err := logrus.ParseLevel(config.Level) if err != nil { return nil, fmt.Errorf("invalid log level %s: %v", config.Level, err) } logger.SetLevel(level) // 设置格式化器 if err := setFormatter(logger, config); err != nil { return nil, fmt.Errorf("failed to set formatter: %v", err) } // 设置输出目标 if err := setOutput(logger, config); err != nil { return nil, fmt.Errorf("failed to set output: %v", err) } // 设置调用者报告 logger.SetReportCaller(config.ReportCaller) // 创建 Logger 包装器 wrappedLogger := &Logger{ Logger: logger, config: config, entryPool: make(chan *LogEntry, 100), closed: false, } return wrappedLogger, nil } // NewDefaultLogger 创建使用默认配置的日志记录器 // 返回使用默认配置的 Logger 实例 func NewDefaultLogger() (*Logger, error) { config := &LoggingConfig{ Level: "info", Format: "text", Output: "stdout", MaxSize: 100, MaxBackups: 3, MaxAge: 30, Compress: true, ReportCaller: false, EnableColor: true, } return NewLogger(config) } // WithFields 创建带有字段的日志记录器 // 返回新的 logrus.Entry 实例 func (l *Logger) WithFields(fields map[string]interface{}) *logrus.Entry { return l.Logger.WithFields(fields) } // WithField 创建带有单个字段的日志记录器 // 返回新的 logrus.Entry 实例 func (l *Logger) WithField(key string, value interface{}) *logrus.Entry { return l.Logger.WithField(key, value) } // WithError 创建带有错误信息的日志记录器 // 返回新的 logrus.Entry 实例 func (l *Logger) WithError(err error) *logrus.Entry { return l.Logger.WithError(err) } // WithTime 创建带有特定时间的日志记录器 // 返回新的 logrus.Entry 实例 func (l *Logger) WithTime(t time.Time) *logrus.Entry { return l.Logger.WithTime(t) } // Log 使用 LogEntry 记录结构化日志 // 根据 LogEntry 的配置记录日志 func (l *Logger) Log(entry *LogEntry) error { if entry == nil { return fmt.Errorf("log entry cannot be nil") } // 转换 LogLevel 到 logrus.Level level, err := l.convertLogLevel(entry.Level) if err != nil { return fmt.Errorf("invalid log level: %v", err) } // 创建 logrus.Entry logrusEntry := l.Logger.WithFields(entry.Fields) if entry.Error != nil { logrusEntry = logrusEntry.WithError(entry.Error) } // 添加性能指标 if entry.Duration > 0 { logrusEntry = logrusEntry.WithField("duration_ms", entry.Duration.Milliseconds()) } if entry.MemoryUsage > 0 { logrusEntry = logrusEntry.WithField("memory_bytes", entry.MemoryUsage) } if entry.CPUUsage > 0 { logrusEntry = logrusEntry.WithField("cpu_usage_percent", entry.CPUUsage) } // 添加请求信息 if entry.RequestID != "" { logrusEntry = logrusEntry.WithField("request_id", entry.RequestID) } if entry.UserID != "" { logrusEntry = logrusEntry.WithField("user_id", entry.UserID) } if entry.SessionID != "" { logrusEntry = logrusEntry.WithField("session_id", entry.SessionID) } // 添加操作信息 if entry.Operation != "" { logrusEntry = logrusEntry.WithField("operation", entry.Operation) } if entry.Category != "" { logrusEntry = logrusEntry.WithField("category", entry.Category) } if entry.Subsystem != "" { logrusEntry = logrusEntry.WithField("subsystem", entry.Subsystem) } // 添加网络信息 if entry.IPAddress != "" { logrusEntry = logrusEntry.WithField("ip_address", entry.IPAddress) } if entry.Method != "" { logrusEntry = logrusEntry.WithField("method", entry.Method) } if entry.Path != "" { logrusEntry = logrusEntry.WithField("path", entry.Path) } if entry.StatusCode > 0 { logrusEntry = logrusEntry.WithField("status_code", entry.StatusCode) } // 添加标签 if len(entry.Tags) > 0 { logrusEntry = logrusEntry.WithField("tags", entry.Tags) } // 添加自定义字段 if len(entry.Custom) > 0 { for k, v := range entry.Custom { logrusEntry = logrusEntry.WithField(k, v) } } // 根据级别记录日志 switch level { case logrus.PanicLevel: logrusEntry.Panic(entry.Message) case logrus.FatalLevel: logrusEntry.Fatal(entry.Message) case logrus.ErrorLevel: logrusEntry.Error(entry.Message) case logrus.WarnLevel: logrusEntry.Warn(entry.Message) case logrus.InfoLevel: logrusEntry.Info(entry.Message) case logrus.DebugLevel: logrusEntry.Debug(entry.Message) default: logrusEntry.Info(entry.Message) } return nil } // Debug 记录调试级别日志 func (l *Logger) Debug(args ...interface{}) { l.Logger.Debug(args...) } // Debugf 记录格式化调试级别日志 func (l *Logger) Debugf(format string, args ...interface{}) { l.Logger.Debugf(format, args...) } // Info 记录信息级别日志 func (l *Logger) Info(args ...interface{}) { l.Logger.Info(args...) } // Infof 记录格式化信息级别日志 func (l *Logger) Infof(format string, args ...interface{}) { l.Logger.Infof(format, args...) } // Warn 记录警告级别日志 func (l *Logger) Warn(args ...interface{}) { l.Logger.Warn(args...) } // Warnf 记录格式化警告级别日志 func (l *Logger) Warnf(format string, args ...interface{}) { l.Logger.Warnf(format, args...) } // Error 记录错误级别日志 func (l *Logger) Error(args ...interface{}) { l.Logger.Error(args...) } // Errorf 记录格式化错误级别日志 func (l *Logger) Errorf(format string, args ...interface{}) { l.Logger.Errorf(format, args...) } // Fatal 记录致命错误级别日志并退出程序 func (l *Logger) Fatal(args ...interface{}) { l.Logger.Fatal(args...) } // Fatalf 记录格式化致命错误级别日志并退出程序 func (l *Logger) Fatalf(format string, args ...interface{}) { l.Logger.Fatalf(format, args...) } // Panic 记录 panic 级别日志并触发 panic func (l *Logger) Panic(args ...interface{}) { l.Logger.Panic(args...) } // Panicf 记录格式化 panic 级别日志并触发 panic func (l *Logger) Panicf(format string, args ...interface{}) { l.Logger.Panicf(format, args...) } // LogOperation 记录操作日志,包含开始和结束时间 // 返回操作结束记录函数 func (l *Logger) LogOperation(operation string, fields map[string]interface{}) func() { start := time.Now() entryFields := make(map[string]interface{}) for k, v := range fields { entryFields[k] = v } entryFields["operation"] = operation entryFields["status"] = "started" l.WithFields(entryFields).Info("Operation started") return func() { duration := time.Since(start) entryFields["duration_ms"] = duration.Milliseconds() entryFields["status"] = "completed" l.WithFields(entryFields).Infof("Operation completed in %v", duration) } } // LogHTTPRequest 记录HTTP请求日志 func (l *Logger) LogHTTPRequest(method, path string, statusCode int, duration time.Duration, fields map[string]interface{}) { logFields := map[string]interface{}{ "method": method, "path": path, "status_code": statusCode, "duration_ms": duration.Milliseconds(), } for k, v := range fields { logFields[k] = v } message := fmt.Sprintf("%s %s - %d", method, path, statusCode) if statusCode >= 500 { l.WithFields(logFields).Error(message) } else if statusCode >= 400 { l.WithFields(logFields).Warn(message) } else { l.WithFields(logFields).Info(message) } } // LogConversion 记录转换操作日志 func (l *Logger) LogConversion(targetFormat, sourceURL string, nodeCount int, duration time.Duration, success bool) { fields := map[string]interface{}{ "target_format": targetFormat, "source_url": sourceURL, "node_count": nodeCount, "duration_ms": duration.Milliseconds(), "success": success, } message := fmt.Sprintf("Conversion %s -> %s (%d nodes, %v)", sourceURL, targetFormat, nodeCount, duration) if success { l.WithFields(fields).Info(message) } else { l.WithFields(fields).Error(message) } } // LogProxyValidation 记录代理验证日志 func (l *Logger) LogProxyValidation(proxyName, proxyType string, isValid bool, pingTime int, err error) { fields := map[string]interface{}{ "proxy_name": proxyName, "proxy_type": proxyType, "is_valid": isValid, "ping_ms": pingTime, } message := fmt.Sprintf("Proxy validation: %s (%s)", proxyName, proxyType) if !isValid { if err != nil { fields["error"] = err.Error() l.WithFields(fields).Error(message) } else { l.WithFields(fields).Warn(message) } } else { l.WithFields(fields).Info(message) } } // GetConfig 获取当前日志配置 // 返回日志配置的副本 func (l *Logger) GetConfig() *LoggingConfig { if l.config == nil { return nil } // 返回配置的深拷贝 config := *l.config return &config } // UpdateConfig 更新日志配置 // 动态更新日志记录器的配置 func (l *Logger) UpdateConfig(config *LoggingConfig) error { if config == nil { return fmt.Errorf("config cannot be nil") } // 更新日志级别 if config.Level != "" { level, err := logrus.ParseLevel(config.Level) if err != nil { return fmt.Errorf("invalid log level %s: %v", config.Level, err) } l.SetLevel(level) } // 更新格式化器 if config.Format != "" { if err := setFormatter(l.Logger, config); err != nil { return fmt.Errorf("failed to update formatter: %v", err) } } // 更新输出目标 if config.Output != "" { if err := setOutput(l.Logger, config); err != nil { return fmt.Errorf("failed to update output: %v", err) } } // 更新其他配置 l.config = config l.SetReportCaller(config.ReportCaller) return nil } // Close 关闭日志记录器,清理资源 func (l *Logger) Close() error { if l.closed { return nil } // 关闭 entry pool close(l.entryPool) l.closed = true return nil } // convertLogLevel 转换 LogLevel 到 logrus.Level func (l *Logger) convertLogLevel(level LogLevel) (logrus.Level, error) { switch level { case DEBUG: return logrus.DebugLevel, nil case INFO: return logrus.InfoLevel, nil case WARN: return logrus.WarnLevel, nil case ERROR: return logrus.ErrorLevel, nil case FATAL: return logrus.FatalLevel, nil default: return logrus.InfoLevel, fmt.Errorf("unsupported log level: %d", level) } } // setFormatter 设置日志格式化器 func setFormatter(logger *logrus.Logger, config *LoggingConfig) error { switch strings.ToLower(config.Format) { case "json": logger.SetFormatter(&logrus.JSONFormatter{ TimestampFormat: "2006-01-02 15:04:05", FieldMap: logrus.FieldMap{ logrus.FieldKeyTime: "timestamp", logrus.FieldKeyLevel: "level", logrus.FieldKeyMsg: "message", }, }) case "text": logger.SetFormatter(&logrus.TextFormatter{ FullTimestamp: true, TimestampFormat: "2006-01-02 15:04:05", DisableColors: !config.EnableColor, ForceColors: config.EnableColor, }) default: return fmt.Errorf("unsupported log format: %s", config.Format) } return nil } // setOutput 设置日志输出目标 func setOutput(logger *logrus.Logger, config *LoggingConfig) error { switch strings.ToLower(config.Output) { case "stdout": logger.SetOutput(os.Stdout) case "file": if config.FilePath == "" { config.FilePath = "logs/subconverter-go.log" } if err := ensureLogDirectory(config.FilePath); err != nil { return fmt.Errorf("failed to create log directory: %v", err) } file, err := os.OpenFile(config.FilePath, os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0666) if err != nil { return fmt.Errorf("failed to open log file: %v", err) } logger.SetOutput(file) case "both": // 同时输出到控制台和文件 if config.FilePath == "" { config.FilePath = "logs/subconverter-go.log" } if err := ensureLogDirectory(config.FilePath); err != nil { return fmt.Errorf("failed to create log directory: %v", err) } file, err := os.OpenFile(config.FilePath, os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0666) if err != nil { return fmt.Errorf("failed to open log file: %v", err) } logger.SetOutput(file) // 暂时只输出到文件,实际项目中可以使用 MultiWriter default: return fmt.Errorf("unsupported log output: %s", config.Output) } return nil } // ensureLogDirectory 确保日志目录存在 func ensureLogDirectory(filePath string) error { dir := filepath.Dir(filePath) if dir == "" { return nil } return os.MkdirAll(dir, 0755) } // GetCallerInfo 获取调用者信息 func GetCallerInfo() (string, int, string) { _, file, line, ok := runtime.Caller(2) if !ok { return "unknown", 0, "unknown" } // 获取函数名 fn := runtime.FuncForPC(runtimeCaller(2)) var funcName string if fn != nil { funcName = fn.Name() // 简化函数名 if idx := strings.LastIndex(funcName, "/"); idx != -1 { funcName = funcName[idx+1:] } if idx := strings.LastIndex(funcName, "."); idx != -1 { funcName = funcName[idx+1:] } } return filepath.Base(file), line, funcName } // runtimeCaller 获取调用者的程序计数器 func runtimeCaller(skip int) uintptr { pc, _, _, ok := runtime.Caller(skip) if !ok { return 0 } return pc } // 全局日志记录器实例 var defaultLogger *Logger // GetDefaultLogger 获取全局默认日志记录器 // 如果未初始化则创建默认实例 func GetDefaultLogger() *Logger { if defaultLogger == nil { logger, err := NewDefaultLogger() if err != nil { // 如果创建失败,使用最简单的配置 logrusLogger := logrus.New() logrusLogger.SetLevel(logrus.InfoLevel) logrusLogger.SetFormatter(&logrus.TextFormatter{ DisableColors: true, }) defaultLogger = &Logger{Logger: logrusLogger} } else { defaultLogger = logger } } return defaultLogger } // SetDefaultLogger 设置全局默认日志记录器 func SetDefaultLogger(logger *Logger) { defaultLogger = logger } // 便捷的全局日志函数 func Debug(args ...interface{}) { GetDefaultLogger().Debug(args...) } func Debugf(format string, args ...interface{}) { GetDefaultLogger().Debugf(format, args...) } func Info(args ...interface{}) { GetDefaultLogger().Info(args...) } func Infof(format string, args ...interface{}) { GetDefaultLogger().Infof(format, args...) } func Warn(args ...interface{}) { GetDefaultLogger().Warn(args...) } func Warnf(format string, args ...interface{}) { GetDefaultLogger().Warnf(format, args...) } func Error(args ...interface{}) { GetDefaultLogger().Error(args...) } func Errorf(format string, args ...interface{}) { GetDefaultLogger().Errorf(format, args...) } func Fatal(args ...interface{}) { GetDefaultLogger().Fatal(args...) } func Fatalf(format string, args ...interface{}) { GetDefaultLogger().Fatalf(format, args...) } func Panic(args ...interface{}) { GetDefaultLogger().Panic(args...) } func Panicf(format string, args ...interface{}) { GetDefaultLogger().Panicf(format, args...) }