veza/veza-backend-api/internal/middleware/sentry_recover.go

101 lines
2.5 KiB
Go

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 ""
}