package middleware import ( "fmt" "net/http" "github.com/getsentry/sentry-go" "github.com/gin-gonic/gin" "go.uber.org/zap" ) // SentryRecover middleware pour capturer les panics et les erreurs avec Sentry func SentryRecover(logger *zap.Logger) gin.HandlerFunc { return func(c *gin.Context) { defer func() { if err := recover(); err != nil { // Capturer le panic dans Sentry hub := sentry.CurrentHub().Clone() hub.Scope().SetTag("component", "gin") hub.Scope().SetContext("request", map[string]interface{}{ "method": c.Request.Method, "path": c.Request.URL.Path, "query": c.Request.URL.RawQuery, "ip": c.ClientIP(), }) // Récupérer le request ID si présent if requestID, exists := c.Get("request_id"); exists { hub.Scope().SetTag("request_id", requestID.(string)) } // Récupérer l'user ID si présent if userID, exists := c.Get("user_id"); exists { hub.Scope().SetUser(sentry.User{ ID: toString(userID), Username: toString(userID), }) } // Capturer l'erreur if errObj, ok := err.(error); ok { hub.CaptureException(errObj) } else { hub.CaptureMessage(fmt.Sprintf("Panic: %v", err)) } // Logger l'erreur localement aussi if logger != nil { logger.Error("Panic recovered", zap.Any("error", err), zap.String("method", c.Request.Method), zap.String("path", c.Request.URL.Path), zap.String("ip", c.ClientIP()), ) } // Répondre avec une erreur générique c.JSON(http.StatusInternalServerError, gin.H{ "error": "internal server error", "message": "An unexpected error occurred", }) c.Abort() } }() c.Next() // Capturer les erreurs HTTP 5xx if c.Writer.Status() >= 500 { hub := sentry.CurrentHub().Clone() hub.Scope().SetTag("component", "gin") hub.Scope().SetTag("status_code", toString(c.Writer.Status())) hub.Scope().SetContext("request", map[string]interface{}{ "method": c.Request.Method, "path": c.Request.URL.Path, "status": c.Writer.Status(), }) // Récupérer les erreurs du contexte Gin if len(c.Errors) > 0 { for _, err := range c.Errors { hub.CaptureException(err) } } else { // Créer une erreur générique pour les 5xx sans erreur explicite hub.CaptureMessage("HTTP 5xx error without explicit error") } } } } // toString convertit une valeur en string de manière sûre func toString(v interface{}) string { if v == nil { return "" } if s, ok := v.(string); ok { return s } return "" }