package middleware import ( "os" "strconv" "time" "github.com/gin-gonic/gin" "go.uber.org/zap" ) // RequestLogger middleware pour logger les requêtes HTTP avec contexte structuré func RequestLogger(logger *zap.Logger) gin.HandlerFunc { return func(c *gin.Context) { // Début de la requête start := time.Now() path := c.Request.URL.Path query := c.Request.URL.RawQuery // Traiter la requête c.Next() // Calculer la durée latency := time.Since(start) // Récupérer le request ID si présent requestID, exists := c.Get("request_id") if !exists { requestID = "" } // Récupérer l'user ID si présent (après authentification) userID, exists := c.Get("user_id") if !exists { userID = nil } // Préparer les champs structurés fields := []zap.Field{ zap.Int("status", c.Writer.Status()), zap.String("method", c.Request.Method), zap.String("path", path), zap.String("query", query), zap.String("ip", c.ClientIP()), zap.String("user_agent", c.Request.UserAgent()), zap.Duration("latency", latency), zap.Int("body_size", c.Writer.Size()), } // Ajouter request ID si présent if requestID != "" { fields = append(fields, zap.String("request_id", requestID.(string))) } // Ajouter user ID si présent if userID != nil { fields = append(fields, zap.Any("user_id", userID)) } // Ajouter le trace_id au logger si disponible (T0025) if traceID := GetTraceID(c); traceID != "" { fields = append(fields, zap.String("trace_id", traceID)) } // Ajouter le span_id au logger si disponible (T0025) if spanID := GetSpanID(c); spanID != "" { fields = append(fields, zap.String("span_id", spanID)) } // Ajouter les erreurs s'il y en a if len(c.Errors) > 0 { fields = append(fields, zap.Strings("errors", c.Errors.Errors())) } // FIX #9: Détecter les requêtes lentes avec seuil configurable // Seuil par défaut: 1 seconde (configurable via SLOW_REQUEST_THRESHOLD_MS) slowThresholdMs := getEnvInt("SLOW_REQUEST_THRESHOLD_MS", 1000) // 1000ms = 1s par défaut slowThreshold := time.Duration(slowThresholdMs) * time.Millisecond isSlowRequest := latency > slowThreshold // Logger selon le status code et la latence if c.Writer.Status() >= 500 { // Erreurs serveur logger.Error("Request completed", fields...) } else if c.Writer.Status() >= 400 { // Erreurs client logger.Warn("Request completed with error", fields...) } else if isSlowRequest { // FIX #9: Requêtes lentes (succès mais > seuil) logger.Warn("Slow request detected", append(fields, zap.Duration("slow_threshold", slowThreshold), zap.Bool("is_slow", true), )..., ) } else { // Succès normal logger.Info("Request completed", fields...) } } } // getEnvInt récupère une variable d'environnement entière avec une valeur par défaut // FIX #9: Helper pour lire SLOW_REQUEST_THRESHOLD_MS func getEnvInt(key string, defaultValue int) int { value := os.Getenv(key) if value == "" { return defaultValue } if intValue, err := strconv.Atoi(value); err == nil { return intValue } return defaultValue }