package middleware import ( "log" "net/http" "time" "github.com/gin-gonic/gin" ) // RequestLoggerConfig holds the configuration for the request logger type RequestLoggerConfig struct { SkipPaths []string Logger *log.Logger TimeFormat string TimeZone string } // DefaultRequestLoggerConfig returns the default request logger configuration func DefaultRequestLoggerConfig() *RequestLoggerConfig { return &RequestLoggerConfig{ SkipPaths: []string{"/health", "/ready", "/metrics"}, Logger: log.Default(), TimeFormat: "2006/01/02 - 15:04:05", TimeZone: "UTC", } } // Logger returns a Gin middleware handler for logging requests func Logger() gin.HandlerFunc { return LoggerWithConfig(DefaultRequestLoggerConfig()) } // LoggerWithConfig returns a Gin middleware handler for logging requests with custom configuration func LoggerWithConfig(config *RequestLoggerConfig) gin.HandlerFunc { return func(c *gin.Context) { // Skip logging for certain paths path := c.Request.URL.Path skip := false for _, skipPath := range config.SkipPaths { if path == skipPath { skip = true break } } start := time.Now() raw := c.Request.URL.RawQuery c.Next() if skip { return } // Calculate latency latency := time.Since(start) // Get the client IP clientIP := c.ClientIP() // Get the request method method := c.Request.Method // Get the status code statusCode := c.Writer.Status() // Log the request if raw != "" { raw = "?" + raw } config.Logger.Printf( "[%s] %s %s%s | %d | %v | %s", clientIP, method, path, raw, statusCode, latency, c.Errors.ByType(gin.ErrorTypePrivate).String(), ) } } // Recovery returns a Gin middleware handler for recovering from panics func Recovery() gin.HandlerFunc { return RecoveryWithConfig(DefaultRecoveryConfig()) } // RecoveryConfig holds the configuration for the recovery middleware type RecoveryConfig struct { Logger *log.Logger TimeFormat string TimeZone string SkipStackTrace bool StackTraceSize int StackAll bool } // DefaultRecoveryConfig returns the default recovery configuration func DefaultRecoveryConfig() *RecoveryConfig { return &RecoveryConfig{ Logger: log.Default(), TimeFormat: "2006/01/02 - 15:04:05", TimeZone: "UTC", SkipStackTrace: false, StackTraceSize: 1024, StackAll: false, } } // RecoveryWithConfig returns a Gin middleware handler for recovering from panics with custom configuration func RecoveryWithConfig(config *RecoveryConfig) gin.HandlerFunc { return func(c *gin.Context) { defer func() { if err := recover(); err != nil { // Log the panic config.Logger.Printf("[PANIC] %v", err) // Abort with a 500 error c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{ "error": "internal server error", }) } }() c.Next() } }