Aller au contenu principal

Erreurs, Logging et Configuration

Hiérarchie d'exceptions

# domain/exceptions.py

class DomainError(Exception):
"""Exception métier de base."""
pass

class ValidationError(DomainError):
"""Données invalides."""
pass

class NotFoundError(DomainError):
"""Resource pas trouvée."""
pass

class ConflictError(DomainError):
"""Resource existe déjà."""
pass

class PaymentError(DomainError):
"""Erreur paiement."""
pass

Mapper les erreurs métier → HTTP

# application/error_handlers.py

exception_status_map = {
ValidationError: 400,
NotFoundError: 404,
ConflictError: 409,
PaymentError: 402,
DomainError: 422, # Unprocessable Entity
}

@app.exception_handler(DomainError)
async def handle_domain_error(request, exc: DomainError):
status = exception_status_map.get(type(exc), 500)
return JSONResponse(
status_code=status,
content={"error": str(exc), "type": type(exc).__name__}
)

Logging

# infrastructure/logging.py

import logging

logger = logging.getLogger(__name__)

# Lors d'une création user
logger.info(f"User created", extra={"user_id": user.id, "email": user.email})

# Lors d'une erreur
logger.error(f"Payment failed", extra={"customer_id": cust_id, "amount": amount}, exc_info=True)

# JAMAIS : logger des mots de passe, tokens, données sensibles
logger.debug(f"User password: {password}") # ❌ MAUVAIS

# BON :
logger.debug(f"User {user.id} authenticated") # ✅ BON

Configuration avec Pydantic Settings

# config/settings.py

from pydantic_settings import BaseSettings

class Settings(BaseSettings):
# Database
DATABASE_URL: str = "postgresql://user:pass@localhost/mydb"

# API
API_KEY_STRIPE: str # Obligatoire, depuis env
API_KEY_SENDGRID: str

# Comportement
LOG_LEVEL: str = "INFO"
CORS_ORIGINS: list = ["http://localhost:3000"]

class Config:
env_file = ".env"
case_sensitive = True

settings = Settings()

# Utiliser dans l'app
@app.on_event("startup")
async def startup():
app.state.settings = settings

Variables d'env obligatoires

# .env.example (commiter ce fichier!)
DATABASE_URL=postgresql://user:pass@localhost/mydb
API_KEY_STRIPE=sk_test_xxxxx
API_KEY_SENDGRID=SG.xxxxx
LOG_LEVEL=INFO

Mauvais vs Bon

# ❌ MAUVAIS : En dur dans le code
stripe.api_key = "sk_live_xxxxx"

# ✅ BON : Depuis settings
stripe.api_key = settings.API_KEY_STRIPE

Règles pour l'IA

Erreurs et Logging :

  • Lancer DomainError ou sous-classe, jamais ValueError/RuntimeError
  • Capturer exceptions, log, relancer sous forme métier
  • Jamais logguer données sensibles (password, token, card number)
  • Log = contexte utile : IDs, statuts, pas détails techniques

Configuration :

  • Lire config depuis Settings
  • Jamais de secrets en dur dans le code
  • .env = non commité, .env.example = commité
  • Variables obligatoires = pas de valeur par défaut