Chapitre 14.4 — Supervision applicative
⏱️ TL;DR — La machine peut se porter à merveille — CPU au repos, disque à moitié vide,
/healthqui répond200— pendant que ton application, elle, part en vrille : une exception non gérée fait planter un paiement sur dix, le temps de réponse triple, une requête sur vingt renvoie une erreur5xx. Le monitoring système ne voit rien de tout ça. Ce chapitre monte d’un cran, au niveau de l’appli : capter les erreurs applicatives (Sentry : les exceptions front et back, avec leur contexte), mesurer les métriques applicatives (temps de réponse, taux de5xxlu dans les logs Nginx), construire un healthcheck qui vérifie vraiment les dépendances (base, services externes), survoler l’APM, et surtout corréler logs + métriques + erreurs lors d’un incident. C’est le tableau de bord minimal du dev qui opère sa prod. Et ça boucle la partie : observer, c’est dormir tranquille.
🎯 Objectifs
- Distinguer la santé de la machine de la santé de l’application.
- Capter les erreurs applicatives avec un outil de suivi d’erreurs (Sentry).
- Mesurer des métriques applicatives : temps de réponse et taux d’erreur
5xx(via les logs Nginx). - Écrire un healthcheck applicatif qui vérifie la base et les dépendances, pas seulement « le process tourne ».
- Corréler logs, métriques et erreurs pour élucider un incident — et savoir quel tableau de bord minimal tenir.
La machine va bien, l’appli va mal
Tout ce qu’on a vu jusqu’ici surveille l’infrastructure. Mais un serveur en pleine forme peut héberger une app malade. Le monitoring système est aveugle à ces symptômes-là :
Un systemctl status vert dit seulement que le process est vivant, pas qu’il fait bien son travail. Il faut donc une couche d’observation au niveau applicatif, qui répond à d’autres questions : mes utilisateurs rencontrent-ils des erreurs ? Mes pages sont-elles rapides ? Mes dépendances (base, API tierces) répondent-elles ?
⚠️ Piège — Se fier au « le service tourne » (
systemctl statusvert) comme preuve que tout va bien. Le process peut tourner tout en crachant une exception à chaque requête d’un certain type, en timeoutant sur la base, ou en renvoyant du500aux seuls utilisateurs connectés. « Le process est up » ≠ « l’app fonctionne ». Il faut mesurer ce que vivent les utilisateurs, pas seulement l’état du daemon.
Capter les erreurs applicatives : Sentry
Quand une exception non gérée survient dans ton code, elle finit — au mieux — dans un log, noyée parmi des milliers de lignes, sans contexte exploitable. Un outil de suivi d’erreurs comme Sentry change ça radicalement.
Le principe : tu intègres un SDK léger dans ton app (front et back). À chaque exception, il capture et envoie l’erreur avec tout son contexte : la stack trace complète, l’URL, le navigateur ou la version, l’utilisateur concerné (anonymisé), les données de la requête. Sentry regroupe les occurrences d’une même erreur (une ligne au lieu de mille), compte leur fréquence, signale les régressions (une erreur qu’on croyait corrigée qui revient) et alerte quand une nouvelle erreur apparaît — typiquement après un déploiement.
- Côté back (API Symfony, Node) : tu vois les exceptions serveur, celles qui produisent des
500, avec la stack trace exacte et les paramètres de la requête fautive. - Côté front (Next.js dans le navigateur) : tu vois les erreurs JavaScript que tes utilisateurs subissent — celles que tu ne reproduis jamais sur ta machine parce qu’elles dépendent d’un navigateur, d’une donnée ou d’un timing particulier.
💡 Réflexe — Après chaque déploiement, garde un œil sur ton suivi d’erreurs pendant quelques minutes. Une nouvelle erreur qui surgit ou une fréquence qui bondit juste après une mise en ligne pointe presque toujours vers ta release. C’est le complément parfait du CI/CD (Partie 13) : tu déploies, tu observes l’impact réel sur les utilisateurs, tu réagis (rollback si besoin) avant que ça dégénère.
🔒 Sécurité — Attention à ce que ton suivi d’erreurs envoie. Une stack trace ou un contexte de requête peut embarquer des données personnelles ou des secrets (token dans un header, email, mot de passe en clair dans un payload). Configure le scrubbing (filtrage) des données sensibles côté SDK, et vérifie que tu respectes le RGPD (cf. le cours RGPD) : on ne collecte pas tout, on minimise. Un outil d’observation ne doit pas devenir une fuite de données.
Mesurer les métriques applicatives
Au-delà des erreurs ponctuelles, deux métriques racontent la santé de ton app dans la durée : le temps de réponse et le taux d’erreur.
Le taux de 5xx via les logs Nginx
Souviens-toi : Nginx logue chaque requête avec son code de réponse dans access.log (chapitre 14.1). Les codes 5xx (500, 502, 503, 504) sont des erreurs serveur : ton app a échoué. En les comptant, tu obtiens un taux d’erreur — l’indicateur de santé le plus direct.
# Compter les reponses par code sur le log Nginx du jour
# ($9 = le champ "code HTTP" dans le format de log par defaut)
awk '{print $9}' /var/log/nginx/access.log | sort | uniq -c | sort -rn
# Extraire uniquement les erreurs serveur (5xx) pour les inspecter
grep -E ' 5[0-9][0-9] ' /var/log/nginx/access.log | tail -n 50- La première commande agrège les requêtes par code HTTP et les trie par fréquence : tu vois d’un coup d’œil combien de
200, de404, de500, de502. Une flambée de5xx= ton app souffre. - La seconde isole les lignes en erreur serveur pour les inspecter (quelles URL ? quel moment ?). Tu croises ensuite avec les logs du service (
journalctl) et Sentry pour la cause.
💡 Réflexe — Distingue bien les familles de codes : les
4xx(404,403…) sont des erreurs client (page inexistante, accès refusé) — souvent normales. Les5xxsont des erreurs serveur — c’est ton problème. Une hausse soudaine de5xxest l’un des signaux les plus fiables qu’une release ou une dépendance vient de casser. On peut en faire une alerte (chapitre 14.3).
Le temps de réponse
La rapidité perçue compte autant que la disponibilité : une page lente fait fuir autant qu’une page cassée. Nginx peut logger le temps de traitement de chaque requête (variable $request_time, à ajouter au format de log). Les outils d’APM (voir plus bas) le font plus finement. L’idée : suivre non pas la moyenne (trompeuse) mais les percentiles — le fameux p95 (95% des requêtes sont plus rapides que X), qui reflète l’expérience des utilisateurs les moins bien servis.
Le healthcheck qui vérifie vraiment
Au chapitre 14.3, /health répondait simplement 200. Mais un /health naïf ment : il renvoie 200 tant que le process répond… même si la base est injoignable et que toutes les vraies requêtes échouent.
Un bon healthcheck applicatif vérifie les dépendances avant de se déclarer en bonne santé :
GET /health -> l'app verifie, dans l'ordre :
1. je reponds (le process web tourne) OK
2. je peux joindre la base (une requete triviale) OK
3. mes dependances critiques repondent OK
=> 200 si TOUT est OK, sinon 503 avec le detailAinsi, quand le moniteur (Uptime Kuma) tape /health et reçoit 503, il t’alerte pour un problème réel (base tombée), pas seulement quand le serveur web est mort. On calibre : le check doit rester léger (une requête SELECT 1 sur la base, pas un scan de table) pour ne pas devenir lui-même une charge, et il ne doit pas exposer d’infos sensibles.
⚠️ Piège — Le healthcheck trop optimiste ou trop lourd. Trop optimiste (il renvoie
200sans rien vérifier), il ne détecte rien — une fausse assurance. Trop lourd (il teste tout, avec de vraies requêtes coûteuses à chaque appel toutes les 30 s), il consomme des ressources et peut lui-même provoquer la panne qu’il surveille. Le bon healthcheck vérifie l’essentiel, légèrement.
APM et traces (survol)
Un cran au-dessus, l’APM (Application Performance Monitoring) instrumente ton app pour suivre chaque requête dans le détail : combien de temps passé dans le code, dans chaque requête SQL, dans chaque appel à une API externe. Le tracing distribué relie les maillons — front → API → base → service tiers — pour voir où exactement une requête lente perd son temps.
Des outils comme Sentry (qui fait aussi de la performance), Grafana Tempo, ou des solutions managées couvrent ce besoin. C’est précieux quand tu traques une lenteur diffuse, superflu au démarrage. On le cite ; on l’adopte quand la douleur (perf) le justifie.
Corréler : l’art du diagnostic d’incident
La vraie compétence n’est pas d’avoir dix outils, c’est de les croiser quand ça brûle. Un incident se résout en recoupant les sources :
| Source | Ce qu’elle apporte dans un incident |
|---|---|
| Alerte (uptime/seuil) | Le déclencheur : « le site répond en 503 », « disque à 90% ». |
Métriques (htop, Netdata) | L’état machine : CPU/RAM/disque/I/O au moment du problème. |
Logs (journalctl, Nginx) | Le quoi/quand : le message d’erreur exact, l’heure, l’URL. |
| Erreurs (Sentry) | Le pourquoi applicatif : la stack trace, la ligne de code fautive. |
| Déploiements (CI/CD) | Le contexte : « ça a commencé pile après la release de 14 h 03 ». |
Le raisonnement type : l’alerte te réveille → les métriques disent si c’est la machine (disque plein ? OOM ?) ou pas → les logs donnent l’erreur et l’heure → Sentry pointe la ligne de code → l’historique des déploiements confirme la cause (une release). Chaque source élimine des hypothèses. C’est la même méthode « remonter la chaîne » que le cycle de vie d’une requête (chapitre 1.5), appliquée à l’observation.
📚 La doc — Les SDK Sentry (
@sentry/nextjs,sentry/sentry-symfony…) ont chacun leur guide d’intégration et d’options (scrubbing, taux d’échantillonnage des traces). Réfère-toi à la doc officielle de ta stack pour l’intégration exacte et les réglages de confidentialité, plutôt qu’à un tutoriel daté.
Le tableau de bord minimal du dev qui opère sa prod
Tu n’as pas besoin d’un NOC de multinationale. Voici le minimum vital — ce qui, réuni, te fait passer de « je subis ma prod » à « je la pilote » :
- Uptime +
/healthqui vérifie la base, avec alerte (chapitre 14.3). - Alertes disque
>85%, cert<14 jours, service down, flambée de5xx. - Suivi d’erreurs (Sentry) front + back, surveillé après chaque déploiement.
- Logs propres et tournés (
logrotate), lisibles avecjournalctl/grep. - Un dashboard ressources (Netdata) sous la main pour le diagnostic à chaud.
Observer, c’est dormir tranquille
On boucle la partie. Tu es passé d’une prod boîte noire — qui tombe sans prévenir et qu’un client t’apprend à découvrir — à une prod qui te parle : ses logs racontent ce qui se passe, ses métriques montrent comment elle se porte, son monitoring veille en continu, ses alertes te préviennent avant l’utilisateur, et sa supervision applicative te dit ce que vivent vraiment tes utilisateurs.
Ce n’est pas du luxe d’ops : c’est ce qui te permet de déployer sereinement, de dormir pendant que ta prod tourne, et d’intervenir vite et juste le jour où — inévitablement — quelque chose casse. On ne pilote que ce qu’on mesure. Observer, au fond, c’est ça : dormir tranquille.
🧭 Sur FormaCampus — Le tableau de bord de FormaCampus tient en cinq briques. Sentry capte les exceptions du front Next.js et de l’API Symfony (avec scrubbing des données personnelles, RGPD oblige) ; une alerte se déclenche à la moindre nouvelle erreur après un déploiement. Le taux de
5xxest extrait des logs Nginx et alerté au-delà d’un seuil. Le/healthde l’API vérifie Postgres avant de répondre200, si bien qu’Uptime Kuma détecte une base tombée, pas seulement un serveur web mort. Quand un incident survient, l’astreinte corrèle : alerte Telegram → Netdata (machine ?) →journalctl(l’erreur) → Sentry (la ligne) → historique des merges (la release). Le dernier gros bug, introduit un vendredi soir, a été repéré par Sentry en trois minutes et rollbacké avant le week-end. L’équipe a passé un samedi tranquille — la meilleure preuve que l’observation fonctionne.
✏️ Exercices
Exercice 1 — /health menteur. Le moniteur Uptime Kuma affiche formacampus.fr en vert (/health renvoie 200), pourtant les utilisateurs se plaignent : « impossible de charger mes cours ». En creusant, la base Postgres est tombée. Qu’est-ce qui cloche dans le healthcheck, et comment le corriger ?
✅ Solution
Le /health est naïf : il renvoie 200 dès que le process web répond, sans vérifier la base. Il ment donc — le serveur web est vivant, mais l’app ne peut rien faire d’utile sans Postgres. Correction : faire du /health un vrai healthcheck applicatif qui, avant de renvoyer 200, teste la base avec une requête triviale (SELECT 1) et les dépendances critiques ; en cas d’échec, renvoyer 503 avec le détail. Uptime Kuma détectera alors le vrai problème et alertera. On garde le check léger pour ne pas surcharger la base.
Exercice 2 — Après un déploiement, ça se dégrade. Dix minutes après une mise en ligne, les utilisateurs signalent des erreurs sporadiques. Comment croises-tu tes sources d’observation pour trouver la cause et décider quoi faire ?
✅ Solution
On corrèle. (1) Logs Nginx : compter les codes (awk '{print $9}' … | uniq -c) → une flambée de 5xx confirme des erreurs serveur, et sur quelles URL. (2) Sentry : une nouvelle erreur (ou une fréquence qui bondit) apparue juste après le déploiement, avec sa stack trace → la ligne de code fautive. (3) Métriques (Netdata/htop) : écarter une cause machine (CPU/RAM/disque OK ?). (4) Historique CI/CD : l’heure de la release coïncide avec le début des erreurs → forte présomption. Décision : si la release est en cause, rollback vers la version précédente (Partie 13, déploiement par releases + lien current), puis corriger au calme. On a diagnostiqué sans deviner, en recoupant.
🧠 Quiz de révision
1. Pourquoi un systemctl status vert ne prouve-t-il pas que l’app va bien ?
Parce qu’il atteste seulement que le process est vivant, pas qu’il fait bien son travail. L’app peut tourner tout en renvoyant des 500, en timeoutant sur la base ou en plantant sur certaines requêtes. Il faut mesurer ce que vivent les utilisateurs (erreurs, 5xx, temps de réponse), pas seulement l’état du daemon.
2. À quoi sert un outil comme Sentry ?
À capter les exceptions applicatives (front et back) avec leur contexte (stack trace, URL, version, utilisateur), à les regrouper et les compter, à signaler les régressions et à alerter sur les nouvelles erreurs — notamment juste après un déploiement. Il rend visibles les erreurs que tu ne reproduis jamais sur ta machine.
3. Comment mesurer un taux d’erreur applicatif sans outil dédié ?
En comptant les codes 5xx (erreurs serveur) dans le access.log de Nginx, qui logue chaque requête avec son code. Par exemple awk '{print $9}' access.log | sort | uniq -c agrège les requêtes par code. Une flambée de 5xx = l’app souffre. On distingue les 5xx (serveur, ton problème) des 4xx (client, souvent normal).
4. Qu’est-ce qui distingue un bon healthcheck d’un mauvais ?
Un bon healthcheck vérifie les dépendances (base joignable via un SELECT 1, services critiques) avant de renvoyer 200, et renvoie 503 en cas d’échec — tout en restant léger. Un mauvais est soit trop optimiste (renvoie 200 sans rien vérifier, il ment), soit trop lourd (teste tout avec des requêtes coûteuses et devient lui-même une charge).
5. En quoi consiste “corréler” pendant un incident ?
À recouper plusieurs sources pour élucider la cause : l’alerte (le déclencheur), les métriques (machine ou pas ?), les logs (le message et l’heure exacts), Sentry (la ligne de code), l’historique des déploiements (une release coïncide-t-elle ?). Chaque source élimine des hypothèses jusqu’à isoler la cause, sans deviner.
Fin de la Partie 14. Ta prod n’est plus une boîte noire : tu la vois, elle t’alerte, tu dors tranquille. Reste à te préparer au jour où, malgré tout, ça casse — et à grandir : Partie 15 — Sauvegardes, résilience & scaling.