Skip to Content

Chapitre 15.5 — Observabilité

Partie 15 : Expert — Chapitre 5/9 Version de référence : Symfony 7.4 / Monolog / Sentry / OpenTelemetry.

⏱️ TL;DR

  • Les trois piliers de l’observabilité : logs (ce qui s’est passé), métriques (combien/à quelle vitesse), traces (le chemin d’une requête à travers les services).
  • Monolog gère les logs : canaux, handlers, niveaux ; en prod, on structure les logs (JSON) pour les indexer.
  • Sentry capture les erreurs avec contexte (breadcrumbs, release, utilisateur) → on est alerté avant les utilisateurs.
  • OpenTelemetry fournit traces et métriques distribuées (utile en microservices / API + workers).
  • Un health check (/health) permet à l’orchestrateur de savoir si l’instance est saine (DB, Redis joignables).
  • Pour un dev Next.js : c’est votre stack d’observabilité habituelle (Sentry, logs structurés, tracing) — côté Symfony, intégrée.

🎯 Objectifs

  • Configurer des logs structurés avec Monolog.
  • Capturer les erreurs avec Sentry.
  • Comprendre traces/métriques (OpenTelemetry).
  • Exposer un health check.

1. Les trois piliers

  • Logs : « à 14h02, la réservation #42 a échoué à cause de X ». Pour enquêter.
  • Métriques : « 320 réservations/min, latence p95 = 180 ms ». Pour surveiller les tendances et alerter.
  • Traces : « cette requête a passé 40 ms en base, 120 ms dans l’API Stripe ». Pour localiser où le temps part, à travers plusieurs services.

Une app observable répond vite à « pourquoi c’est lent/cassé ? » — indispensable en prod.

2. Logs structurés avec Monolog

Symfony utilise Monolog. On y écrit via LoggerInterface (injecté), avec des niveaux (debug, info, warning, error, critical) et un contexte :

$this->logger->info('Réservation créée', [ 'bookingId' => $booking->getId(), 'userId' => $user->getId(), ]); $this->logger->error('Échec paiement Stripe', [ 'bookingId' => $id, 'exception' => $e, // Monolog capture la trace ]);

En production, on structure les logs en JSON (un objet par ligne) pour les indexer dans un agrégateur (Elastic/Loki/Datadog) et les requêter :

# config/packages/prod/monolog.yaml (extrait) monolog: handlers: main: type: stream path: 'php://stderr' # les logs vont sur stderr (capté par Docker/l'infra) level: info formatter: monolog.formatter.json # format JSON structuré

Les canaux (channels) séparent les logs par domaine (app, security, doctrine, messenger) — pratique pour filtrer.

⚠️ Piège : ne loggez jamais de secrets ni de données personnelles en clair (mots de passe, tokens, numéros de carte, emails en masse). Les logs sont souvent conservés et accessibles à plus de monde que la base. Loggez des identifiants (bookingId), pas des payloads sensibles. C’est un enjeu RGPD et de sécurité.

3. Sentry : la capture d’erreurs

Un log d’erreur perdu dans des millions de lignes ne sert à rien. Sentry capture les exceptions, les regroupe, les enrichit (utilisateur, requête, release, breadcrumbs = fil des événements avant l’erreur) et alerte (email/Slack).

composer require sentry/sentry-symfony

Configuré, Sentry remonte automatiquement les exceptions non gérées, avec leur contexte. On est prévenu d’un bug de prod avant que les utilisateurs ne se plaignent — et on a la trace exacte pour le corriger. On peut aussi associer un release (le SHA du déploiement) pour savoir quelle version a introduit un bug.

4. OpenTelemetry : traces et métriques

Pour une architecture distribuée (API Symfony + workers + services externes + front Next.js), OpenTelemetry (OTel) fournit un standard de traces (le parcours d’une requête à travers les composants) et de métriques, exportables vers de nombreux backends (Jaeger, Datadog, Grafana Tempo). Une trace relie, par un identifiant de corrélation, tous les segments d’une même opération — on voit que « la lenteur vient de l’appel à Moodle, pas de la base ».

Pour BookLab, OTel devient utile dès qu’on a plusieurs processus (API + workers Messenger + hub Mercure) et qu’on veut suivre une opération de bout en bout.

5. Health checks

Un health check est un endpoint que l’orchestrateur (Kubernetes, load balancer) interroge pour savoir si l’instance est saine :

#[Route('/health', name: 'health', methods: ['GET'])] public function health(Connection $db): JsonResponse { try { $db->executeQuery('SELECT 1'); // la base répond-elle ? return $this->json(['status' => 'ok']); } catch (\Throwable $e) { return $this->json(['status' => 'degraded'], 503); } }
  • Liveness : « le process est-il vivant ? » (redémarrer si non).
  • Readiness : « est-il prêt à recevoir du trafic ? » (dépendances joignables : base, Redis). L’orchestrateur n’envoie du trafic qu’aux instances prêtes (essentiel pour le zéro-downtime, chapitre 15.4).

💡 Pour un dev Next.js : vous connaissez Sentry (il marche côté front et back — on peut corréler une erreur front Next.js à l’appel API Symfony qui l’a causée). Les logs structurés + traces sont votre stack d’observabilité habituelle. L’atout d’une architecture BookLab bien instrumentée : une trace unique qui suit une réservation du clic Next.js → API Symfony → worker → Stripe. Quand un client dit « ça a planté », vous retrouvez exactement la requête.

✏️ Exercices

Exercice 1. Pourquoi structurer les logs en JSON en production ?

✅ Solution

Pour les rendre indexables et requêtables par un agrégateur de logs (Elastic, Loki, Datadog…) : un objet JSON par ligne, avec des champs (bookingId, level, channel) sur lesquels on peut filtrer/agréger (« toutes les erreurs de paiement de la dernière heure »). Des logs texte libres sont bien plus difficiles à exploiter à grande échelle. Le JSON structuré transforme les logs en données exploitables.

Exercice 2. Qu’apporte Sentry qu’un simple log d’erreur n’apporte pas ?

✅ Solution

Sentry capture, regroupe, enrichit et alerte : il agrège les occurrences d’une même erreur (au lieu de les noyer dans les logs), ajoute du contexte (utilisateur, requête, release, breadcrumbs), et notifie (Slack/email) — on est prévenu avant les utilisateurs, avec la trace exacte et la version responsable. Un log d’erreur brut, lui, reste passif et dispersé.

Exercice 3. À quoi sert le readiness check pour un déploiement sans coupure ?

✅ Solution

Le readiness indique si une instance est prête à recevoir du trafic (ses dépendances — base, Redis — sont joignables et le cache est chaud). Lors d’un déploiement rolling, l’orchestrateur n’envoie du trafic à une nouvelle instance que lorsqu’elle répond « prête » — évitant d’acheminer des requêtes vers une instance encore en démarrage (qui échouerait). C’est un maillon clé du zéro-downtime (chapitre 15.4).

🧠 Quiz de révision

1. Quels sont les trois piliers de l’observabilité ?

Logs (événements), métriques (compteurs/latences), traces (parcours d’une requête à travers les services).

2. Comment logge-t-on en production ?

Avec Monolog, en JSON structuré (canaux, niveaux), envoyé vers un agrégateur — sans jamais logger de secrets/données personnelles.

3. Que fait Sentry ?

Il capture les erreurs avec contexte (utilisateur, release, breadcrumbs), les regroupe et alerte — on est prévenu avant les utilisateurs.

4. À quoi sert OpenTelemetry ?

À fournir traces et métriques distribuées (parcours d’une requête à travers plusieurs services), utile en architecture API + workers + externes.

5. Différence liveness / readiness ?

Liveness : le process est-il vivant (à redémarrer sinon) ? Readiness : est-il prêt à recevoir du trafic (dépendances joignables) ? Clé du zéro-downtime.


Prochain chapitre : 15.6 — Durcissement production — rate limiting, en-têtes, secrets, OWASP.