first commit
Some checks failed
CI/CD Pipeline / Test (push) Failing after 22m19s
CI/CD Pipeline / Security Scan (push) Failing after 5m57s
CI/CD Pipeline / Build (amd64, darwin) (push) Has been skipped
CI/CD Pipeline / Build (amd64, linux) (push) Has been skipped
CI/CD Pipeline / Build (amd64, windows) (push) Has been skipped
CI/CD Pipeline / Build (arm64, darwin) (push) Has been skipped
CI/CD Pipeline / Build (arm64, linux) (push) Has been skipped
CI/CD Pipeline / Build Docker Image (push) Has been skipped
CI/CD Pipeline / Create Release (push) Has been skipped

This commit is contained in:
Rogee
2025-09-28 10:05:07 +08:00
commit 7fcabe0225
481 changed files with 125127 additions and 0 deletions

635
internal/logging/logger.go Normal file
View File

@@ -0,0 +1,635 @@
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...)
}