diff options
Diffstat (limited to 'backend/internal/middleware/error_handler.go')
-rw-r--r-- | backend/internal/middleware/error_handler.go | 119 |
1 files changed, 119 insertions, 0 deletions
diff --git a/backend/internal/middleware/error_handler.go b/backend/internal/middleware/error_handler.go new file mode 100644 index 0000000..4e7eedf --- /dev/null +++ b/backend/internal/middleware/error_handler.go @@ -0,0 +1,119 @@ +package middleware + +import ( + "finance/backend/internal/logger" + "net/http" + "runtime/debug" + "time" + + "github.com/gin-gonic/gin" +) + +// ErrorResponse represents a standardized error response +type ErrorResponse struct { + Status int `json:"status"` + Message string `json:"message"` + Error string `json:"error,omitempty"` +} + +// ErrorHandler middleware recovers from panics and logs errors +func ErrorHandler() gin.HandlerFunc { + return func(c *gin.Context) { + defer func() { + if err := recover(); err != nil { + // Get logger from the context or create a new one + log := getLoggerFromContext(c) + + // Log the stack trace + log.Errorf("PANIC RECOVERED: %v\n%s", err, debug.Stack()) + + // Return a 500 Internal Server Error response + c.JSON(http.StatusInternalServerError, ErrorResponse{ + Status: http.StatusInternalServerError, + Message: "An unexpected error occurred", + }) + + // Abort the request + c.Abort() + } + }() + + c.Next() + } +} + +// RequestLogger middleware logs information about each request +func RequestLogger(log *logger.Logger) gin.HandlerFunc { + return func(c *gin.Context) { + // Store logger in context for other middleware to use + c.Set("logger", log) + + // Start timer + startTime := time.Now() + + // Process request + c.Next() + + // Calculate latency + latency := time.Since(startTime) + + // Get request details + method := c.Request.Method + path := c.Request.URL.Path + statusCode := c.Writer.Status() + ip := c.ClientIP() + userAgent := c.Request.UserAgent() + + // Log request info based on status code + logMessage := logger.FormatRequestLog(method, path, ip, userAgent, statusCode, latency) + + if statusCode >= 500 { + log.Error(logMessage) + } else if statusCode >= 400 { + log.Warn(logMessage) + } else { + log.Info(logMessage) + } + } +} + +// NotFoundHandler handles 404 errors +func NotFoundHandler() gin.HandlerFunc { + return func(c *gin.Context) { + // Get logger from the context or create a new one + log := getLoggerFromContext(c) + + log.Warnf("404 Not Found: %s %s", c.Request.Method, c.Request.URL.Path) + c.JSON(http.StatusNotFound, ErrorResponse{ + Status: http.StatusNotFound, + Message: "The requested resource was not found", + }) + } +} + +// MethodNotAllowedHandler handles 405 errors +func MethodNotAllowedHandler() gin.HandlerFunc { + return func(c *gin.Context) { + // Get logger from the context or create a new one + log := getLoggerFromContext(c) + + log.Warnf("405 Method Not Allowed: %s %s", c.Request.Method, c.Request.URL.Path) + c.JSON(http.StatusMethodNotAllowed, ErrorResponse{ + Status: http.StatusMethodNotAllowed, + Message: "The requested method is not allowed", + }) + } +} + +// getLoggerFromContext retrieves the logger from context or creates a default one +func getLoggerFromContext(c *gin.Context) *logger.Logger { + loggerInterface, exists := c.Get("logger") + if exists { + if log, ok := loggerInterface.(*logger.Logger); ok { + return log + } + } + + // If no logger in context, create a new one + return logger.NewLogger(nil, logger.INFO) +} |