package logger import ( "context" "fmt" "io" "os" "path/filepath" "runtime" "strings" "time" "github.com/google/uuid" "github.com/sirupsen/logrus" "gopkg.in/natefinch/lumberjack.v2" ) // LogConfig 日志配置 type LogConfig struct { Level string `mapstructure:"level"` Filename string `mapstructure:"filename"` MaxSize int `mapstructure:"maxSize"` MaxAge int `mapstructure:"maxAge"` MaxBackups int `mapstructure:"maxBackups"` EnableConsole bool `mapstructure:"enableConsole"` EnableFile bool `mapstructure:"enableFile"` Format string `mapstructure:"format"` EnableCaller bool `mapstructure:"enableCaller"` EnableOperation bool `mapstructure:"enableOperation"` EnablePerf bool `mapstructure:"enablePerf"` PerfThreshold int64 `mapstructure:"perfThreshold"` } var ( logConfig LogConfig ) var ( log *logrus.Logger isDevelopment bool ) // ContextKey 上下文键类型 type ContextKey string const ( // RequestIDKey 请求ID键 RequestIDKey ContextKey = "request_id" // UserIDKey 用户ID键 UserIDKey ContextKey = "user_id" // OperationKey 操作类型键 OperationKey ContextKey = "operation" // ModuleKey 模块名称键 ModuleKey ContextKey = "module" ) // CustomFormatter 自定义日志格式器 type CustomFormatter struct { TimestampFormat string Environment string } // Format 格式化日志 func (f *CustomFormatter) Format(entry *logrus.Entry) ([]byte, error) { timestamp := entry.Time.Format(f.TimestampFormat) // 获取调用者信息 caller := "" if entry.HasCaller() { caller = fmt.Sprintf("%s:%d", filepath.Base(entry.Caller.File), entry.Caller.Line) } // 开发环境:可读性更好的文本格式 var msg strings.Builder msg.WriteString(fmt.Sprintf("[%s] %s %s", timestamp, strings.ToUpper(entry.Level.String()), entry.Message)) if caller != "" { msg.WriteString(fmt.Sprintf(" (%s)", caller)) } // 添加字段信息 if len(entry.Data) > 0 { msg.WriteString(" |") for k, v := range entry.Data { msg.WriteString(fmt.Sprintf(" %s=%v", k, v)) } } msg.WriteString("\n") return []byte(msg.String()), nil } // Init 初始化日志 func Init(cfg LogConfig) { log = logrus.New() logConfig = cfg // 存储配置 // 判断是否为开发环境 isDevelopment = os.Getenv("GIN_MODE") != "release" // 设置日志级别 level, err := logrus.ParseLevel(cfg.Level) if err != nil { level = logrus.InfoLevel } log.SetLevel(level) // 设置调用者信息 log.SetReportCaller(cfg.EnableCaller) // 设置日志格式化器 var formatter logrus.Formatter if cfg.Format == "json" { // 生产环境使用JSON格式 formatter = &logrus.JSONFormatter{ TimestampFormat: "2006-01-02 15:04:05", DisableHTMLEscape: true, // 禁用HTML转义,避免特殊字符问题 FieldMap: logrus.FieldMap{ logrus.FieldKeyTime: "timestamp", logrus.FieldKeyLevel: "level", logrus.FieldKeyMsg: "message", logrus.FieldKeyFunc: "caller", }, } } else { // 开发环境使用文本格式 formatter = &CustomFormatter{ TimestampFormat: "2006-01-02 15:04:05", Environment: "development", } } log.SetFormatter(formatter) // 配置输出 var outputs []io.Writer // 控制台输出 if cfg.EnableConsole { outputs = append(outputs, os.Stdout) } // 文件输出 if cfg.EnableFile && cfg.Filename != "" { // 确保日志目录存在 logDir := filepath.Dir(cfg.Filename) if err := os.MkdirAll(logDir, 0755); err != nil { log.Fatalf("创建日志目录失败: %v", err) } // 配置lumberjack进行日志轮转 lumberjackLogger := &lumberjack.Logger{ Filename: cfg.Filename, MaxSize: cfg.MaxSize, // MB MaxAge: cfg.MaxAge, // days MaxBackups: cfg.MaxBackups, // files LocalTime: true, Compress: true, } outputs = append(outputs, lumberjackLogger) } // 设置输出 if len(outputs) > 0 { if len(outputs) == 1 { log.SetOutput(outputs[0]) } else { log.SetOutput(io.MultiWriter(outputs...)) } } } // GetLogger 获取日志实例 func GetLogger() *logrus.Logger { if log == nil { log = logrus.New() } return log } // GenerateRequestID 生成请求ID func GenerateRequestID() string { return uuid.New().String() } // WithContext 从上下文创建带有上下文信息的日志条目 func WithContext(ctx context.Context) *logrus.Entry { entry := GetLogger().WithFields(logrus.Fields{}) if requestID := ctx.Value(RequestIDKey); requestID != nil { entry = entry.WithField("request_id", requestID) } if userID := ctx.Value(UserIDKey); userID != nil { entry = entry.WithField("user_id", userID) } if operation := ctx.Value(OperationKey); operation != nil { entry = entry.WithField("operation", operation) } if module := ctx.Value(ModuleKey); module != nil { entry = entry.WithField("module", module) } return entry } // Debug 调试日志 func Debug(args ...interface{}) { GetLogger().Debug(args...) } // Debugf 格式化调试日志 func Debugf(format string, args ...interface{}) { GetLogger().Debugf(format, args...) } // DebugWithContext 带上下文的调试日志 func DebugWithContext(ctx context.Context, args ...interface{}) { WithContext(ctx).Debug(args...) } // DebugfWithContext 带上下文的格式化调试日志 func DebugfWithContext(ctx context.Context, format string, args ...interface{}) { WithContext(ctx).Debugf(format, args...) } // Info 信息日志 func Info(args ...interface{}) { GetLogger().Info(args...) } // Infof 格式化信息日志 func Infof(format string, args ...interface{}) { GetLogger().Infof(format, args...) } // InfoWithContext 带上下文的信息日志 func InfoWithContext(ctx context.Context, args ...interface{}) { WithContext(ctx).Info(args...) } // InfofWithContext 带上下文的格式化信息日志 func InfofWithContext(ctx context.Context, format string, args ...interface{}) { WithContext(ctx).Infof(format, args...) } // Warn 警告日志 func Warn(args ...interface{}) { GetLogger().Warn(args...) } // Warnf 格式化警告日志 func Warnf(format string, args ...interface{}) { GetLogger().Warnf(format, args...) } // WarnWithContext 带上下文的警告日志 func WarnWithContext(ctx context.Context, args ...interface{}) { WithContext(ctx).Warn(args...) } // WarnfWithContext 带上下文的格式化警告日志 func WarnfWithContext(ctx context.Context, format string, args ...interface{}) { WithContext(ctx).Warnf(format, args...) } // Error 错误日志 func Error(args ...interface{}) { GetLogger().Error(args...) } // Errorf 格式化错误日志 func Errorf(format string, args ...interface{}) { GetLogger().Errorf(format, args...) } // ErrorWithContext 带上下文的错误日志 func ErrorWithContext(ctx context.Context, args ...interface{}) { WithContext(ctx).Error(args...) } // ErrorfWithContext 带上下文的格式化错误日志 func ErrorfWithContext(ctx context.Context, format string, args ...interface{}) { WithContext(ctx).Errorf(format, args...) } // Fatal 致命错误日志 func Fatal(args ...interface{}) { GetLogger().Fatal(args...) } // Fatalf 格式化致命错误日志 func Fatalf(format string, args ...interface{}) { GetLogger().Fatalf(format, args...) } // WithField 添加字段 func WithField(key string, value interface{}) *logrus.Entry { return GetLogger().WithField(key, value) } // WithFields 添加多个字段 func WithFields(fields logrus.Fields) *logrus.Entry { return GetLogger().WithFields(fields) } // WithError 添加错误字段 func WithError(err error) *logrus.Entry { return GetLogger().WithError(err) } // LogRequest 记录请求日志 func LogRequest(method, path, ip, userAgent string, statusCode int, duration int64) { GetLogger().WithFields(logrus.Fields{ "type": "http_request", "method": method, "path": path, "ip": ip, "user_agent": userAgent, "status": statusCode, "duration": duration, "timestamp": time.Now().Unix(), }).Info("HTTP Request") } // LogRequestWithContext 带上下文的请求日志 func LogRequestWithContext(ctx context.Context, method, path, ip, userAgent string, statusCode int, duration int64) { WithContext(ctx).WithFields(logrus.Fields{ "type": "http_request", "method": method, "path": path, "ip": ip, "user_agent": userAgent, "status": statusCode, "duration": duration, "timestamp": time.Now().Unix(), }).Info("HTTP Request") } // LogError 记录错误日志 func LogError(err error, context map[string]interface{}) { entry := GetLogger().WithError(err).WithField("type", "application_error") // 添加错误堆栈信息 if pc, file, line, ok := runtime.Caller(1); ok { entry = entry.WithFields(logrus.Fields{ "error_file": filepath.Base(file), "error_line": line, "error_func": runtime.FuncForPC(pc).Name(), }) } if context != nil { entry = entry.WithFields(context) } entry.Error("Application Error") } // LogErrorWithContext 带上下文的错误日志 func LogErrorWithContext(ctx context.Context, err error, context map[string]interface{}) { entry := WithContext(ctx).WithError(err).WithField("type", "application_error") // 添加错误堆栈信息 if pc, file, line, ok := runtime.Caller(1); ok { entry = entry.WithFields(logrus.Fields{ "error_file": filepath.Base(file), "error_line": line, "error_func": runtime.FuncForPC(pc).Name(), }) } if context != nil { entry = entry.WithFields(context) } entry.Error("Application Error") } // LogOperation 记录操作日志 func LogOperation(userID uint, userType, operation, resource, method, path, ip string, requestData, responseData interface{}, statusCode int, duration time.Duration) { if !logConfig.EnableOperation { return } GetLogger().WithFields(logrus.Fields{ "type": "operation", "user_id": userID, "user_type": userType, "operation": operation, "resource": resource, "method": method, "path": path, "ip": ip, "request_data": requestData, "response_data": responseData, "status_code": statusCode, "duration": duration.Milliseconds(), "timestamp": time.Now().Unix(), }).Info("User Operation") } // LogOperationWithContext 带上下文的操作日志 func LogOperationWithContext(ctx context.Context, userID uint, userType, operation, resource, method, path, ip string, requestData, responseData interface{}, statusCode int, duration time.Duration) { if !logConfig.EnableOperation { return } WithContext(ctx).WithFields(logrus.Fields{ "type": "operation", "user_id": userID, "user_type": userType, "operation": operation, "resource": resource, "method": method, "path": path, "ip": ip, "request_data": requestData, "response_data": responseData, "status_code": statusCode, "duration": duration.Milliseconds(), "timestamp": time.Now().Unix(), }).Info("User Operation") } // LogPerformance 记录性能日志 func LogPerformance(operation string, duration time.Duration, details map[string]interface{}) { if !logConfig.EnablePerf { return } // 检查是否超过阈值 if duration.Milliseconds() < logConfig.PerfThreshold { return } entry := GetLogger().WithFields(logrus.Fields{ "type": "performance", "operation": operation, "duration": duration.Milliseconds(), "timestamp": time.Now().Unix(), }) if details != nil { entry = entry.WithFields(details) } // 根据耗时判断日志级别 if duration > 5*time.Second { entry.Error("Slow Operation Detected") } else if duration > 1*time.Second { entry.Warn("Performance Warning") } else { entry.Info("Performance Log") } } // LogPerformanceWithContext 带上下文的性能日志 func LogPerformanceWithContext(ctx context.Context, operation string, duration time.Duration, details map[string]interface{}) { if !logConfig.EnablePerf { return } // 检查是否超过阈值 if duration.Milliseconds() < logConfig.PerfThreshold { return } entry := WithContext(ctx).WithFields(logrus.Fields{ "type": "performance", "operation": operation, "duration": duration.Milliseconds(), "timestamp": time.Now().Unix(), }) if details != nil { entry = entry.WithFields(details) } // 根据耗时判断日志级别 if duration > 5*time.Second { entry.Error("Slow Operation Detected") } else if duration > 1*time.Second { entry.Warn("Performance Warning") } else { entry.Info("Performance Log") } } // LogDatabase 记录数据库操作日志 func LogDatabase(operation, table string, duration time.Duration, rowsAffected int64, query string) { entry := GetLogger().WithFields(logrus.Fields{ "type": "database", "operation": operation, "table": table, "duration": duration.Milliseconds(), "rows_affected": rowsAffected, "query": query, "timestamp": time.Now().Unix(), }) // 根据耗时判断日志级别 if duration > 2*time.Second { entry.Warn("Slow Database Query") } else { entry.Debug("Database Operation") } } // LogDatabaseWithContext 带上下文的数据库操作日志 func LogDatabaseWithContext(ctx context.Context, operation, table string, duration time.Duration, rowsAffected int64, query string) { entry := WithContext(ctx).WithFields(logrus.Fields{ "type": "database", "operation": operation, "table": table, "duration": duration.Milliseconds(), "rows_affected": rowsAffected, "query": query, "timestamp": time.Now().Unix(), }) // 根据耗时判断日志级别 if duration > 2*time.Second { entry.Warn("Slow Database Query") } else { entry.Debug("Database Operation") } }