Chapitre 14.1 — Les logs, ta boîte noire
⏱️ TL;DR — Un serveur qui tourne écrit en permanence ce qu’il fait : chaque démarrage de service, chaque requête HTTP, chaque tentative de connexion SSH, chaque erreur. Ces logs sont ta boîte noire : quand ça casse, ils te disent quoi, quand et pourquoi. Deux mondes cohabitent sur une Ubuntu moderne : le journal systemd (interrogé avec
journalctl) pour les services, et les fichiers texte de/var/log(syslog,auth.log, les logs Nginx). Tu vas apprendre à les lire efficacement (suivre en direct, filtrer, remonter dans le temps) et — crucial — à les faire tourner aveclogrotate, parce qu’un log qu’on ne borne pas remplit le disque et fait tomber toute la machine.
🎯 Objectifs
- Interroger le journal systemd avec
journalctl: par service, en direct, par période, par niveau de gravité. - Connaître les fichiers de log clés de
/var/loget ce qu’ils contiennent. - Lire efficacement un log :
tail -f,grep,less. - Mettre en place la rotation des logs avec
logrotatepour ne jamais saturer le disque. - Comprendre les niveaux de log et survoler la centralisation.
Deux endroits où vivent les logs
Sur une Ubuntu/Debian moderne, tes logs se trouvent à deux endroits complémentaires, et il faut savoir naviguer entre les deux.
journald(le journal de systemd) collecte les sorties de tous les services gérés par systemd, plus les messages du noyau. On l’interroge avecjournalctl. C’est structuré, indexé, requêtable./var/logcontient les fichiers texte classiques, hérités d’Unix : lesysloggénéral,auth.log(authentification), et surtout les logs de tes applications tierces comme Nginx (access.log,error.log), qui écrivent directement dans leurs propres fichiers.
Le journal systemd : journalctl
C’est ton point de départ pour tout ce qui est service. Ton app Next.js, ton API Symfony, Nginx (au niveau du service), Postgres : tout ce qui tourne en unit systemd remonte ici.
🐚 Au terminal — Les incantations
journalctlque tu taperas tous les jours :
# Les logs d'UN service précis (le drapeau -u = unit)
journalctl -u formacampus-web
# Suivre en DIRECT (le drapeau -f = follow), comme tail -f
journalctl -u formacampus-web -f
# Depuis une période (le langage est naturel)
journalctl -u formacampus-web --since "1 hour ago"
journalctl -u formacampus-web --since "2026-07-15 09:00" --until "2026-07-15 10:00"
# Ne garder que les erreurs (le drapeau -p err = priorité >= error)
journalctl -u formacampus-web -p err
# Les logs depuis le DERNIER démarrage de la machine (le drapeau -b = boot)
journalctl -b
# Le boot précédent (utile après un reboot inattendu)
journalctl -b -1Décortiquons les options, car ce sont elles qui rendent journalctl puissant :
-u <service>cible un service précis. Sans ça, tu vois tout, c’est illisible. C’est ton premier réflexe : « qu’est-ce que dit ce service ? ».-f(follow) affiche les nouvelles lignes en temps réel. Tu le lances, tu recharges la page en erreur dans ton navigateur, et tu regardes l’erreur apparaître. Indispensable pour reproduire un bug.--since/--untilbornent la fenêtre temporelle. « Le site a ramé vers 9 h » → tu regardes précisément ce créneau au lieu de scroller des heures.-p errfiltre par priorité (gravité). Tu ne vois que les messages de niveau error et au-dessus, en ignorant le bruit informatif. On y revient plus bas avec les niveaux.-blimite au démarrage courant de la machine. Après un reboot surprise,journalctl -b -1te montre les derniers soubresauts avant le crash.
💡 Réflexe — Face à un service qui déraille, l’enchaînement gagnant est toujours le même :
systemctl status <service>pour l’état résumé, puisjournalctl -u <service> -p err --since "30 min ago"pour lire précisément les erreurs récentes de ce service. Deux commandes, et tu sais.
📚 La doc — Tape
man journalctl: la liste des filtres est longue (par PID, par utilisateur, par exécutable, en JSON avec-o json…). Lemanfait autorité, les options évoluent peu.
Les logs classiques de /var/log
Tout ne passe pas par journald. Beaucoup de logiciels écrivent directement dans des fichiers. Les incontournables :
| Fichier | Contenu |
|---|---|
/var/log/syslog | Le journal général du système (services, cron, divers). |
/var/log/auth.log | Les authentifications : connexions SSH, sudo, su. |
/var/log/nginx/access.log | Chaque requête HTTP reçue par Nginx (IP, URL, code, taille). |
/var/log/nginx/error.log | Les erreurs de Nginx (502, upstream injoignable, config). |
Le fichier auth.log mérite une attention particulière. Ouvre-le sur un serveur exposé depuis quelques jours et tu verras des centaines de tentatives de connexion SSH échouées, depuis le monde entier : ce sont des bots qui bruteforcent en permanence. C’est normal et ça confirme pourquoi tu as désactivé le mot de passe SSH et installé fail2ban (Partie 5).
🔒 Sécurité —
auth.logest ta caméra de surveillance de l’accès au serveur. Une rafale deFailed passworddepuis une IP, c’est un bruteforce (fail2ban devrait la bannir). Mais unAccepted publickeyque tu n’as pas initié, ou unsudoinattendu, c’est un signal d’alerte majeur. Regarderauth.logrégulièrement — ou mieux, y brancher une alerte (chapitre 14.3) — fait partie de l’hygiène de prod.
Les logs Nginx, eux, sont ta source de vérité sur le trafic : qui vient, sur quelles URL, avec quels codes de réponse. On s’en resservira au chapitre 14.4 pour calculer un taux d’erreur 5xx.
Lire efficacement un log
Un fichier de log de production fait vite des dizaines de milliers de lignes. On ne l’ouvre pas en entier. Trois outils suffisent :
# Suivre en direct les nouvelles requêtes (le -f = follow)
tail -f /var/log/nginx/access.log
# Les 100 dernières lignes seulement
tail -n 100 /var/log/nginx/error.log
# Filtrer : ne montrer que les lignes contenant "500"
grep " 500 " /var/log/nginx/access.log
# Combiner : suivre en direct MAIS seulement les erreurs 5xx
tail -f /var/log/nginx/access.log | grep " 50"
# Parcourir un gros fichier confortablement (navigation, recherche avec /)
less /var/log/nginx/error.logtail -fsuit le fichier en direct : chaque nouvelle ligne s’affiche. C’est l’équivalent, pour les fichiers texte, dujournalctl -f.grep <motif>ne garde que les lignes qui contiennent le motif. « Montre-moi les 502 », « les requêtes de cette IP », « les erreurs d’hier ». C’est ton filtre universel.lessouvre le fichier en lecture navigable (flèches,/pour chercher,Gpour la fin,qpour quitter) sans tout charger en RAM. Parfait pour explorer un gros log posément.
💡 Réflexe — Le combo
tail -f … | grep …est ton stéthoscope. Tu poses l’oreille sur le flux en direct, filtré sur ce qui t’intéresse (une IP, un code d’erreur, une URL), pendant que tu reproduis le problème dans le navigateur. L’erreur apparaît sous tes yeux, avec son horodatage exact.
La rotation des logs : logrotate
Voici le point qui sauve ta prod, et que les débutants ignorent jusqu’à la première panne.
Un log grandit sans fin. access.log prend quelques octets par requête ; multiplié par des millions de requêtes sur des mois, ça fait des gigaoctets. Si rien ne borne cette croissance, le fichier finit par remplir le disque. Et un disque plein, tu le sais depuis la Partie 1, c’est la panne totale : la base ne peut plus écrire, l’app tombe, plus rien ne fonctionne — une catastrophe qui semble « sortie de nulle part ».
La solution est logrotate : un outil (déjà présent sur Ubuntu, lancé quotidiennement) qui fait tourner les logs. Concrètement, il renomme le log courant, en ouvre un neuf, compresse les anciens et supprime les trop vieux. Tu gardes un historique utile sans croissance infinie.
⚠️ Piège — Le log qui remplit le disque en silence. C’est la panne la plus sournoise du débutant : tout marche pendant des semaines, puis un matin tout est mort, et la cause — un
access.logou un log applicatif de 40 Go — n’a aucun rapport apparent avec le symptôme (« le site ne répond plus »). Le pire ? Tes logs applicatifs maison (ton app qui écrit dans un fichier à elle) ne sont pas couverts par défaut : c’est à toi de leur écrire une règlelogrotate.
Les paquets bien faits (Nginx, etc.) posent leur config dans /etc/logrotate.d/ — un fichier par logiciel. Pour ton propre log applicatif, tu ajoutes le tien. Un exemple commenté pour un log maison :
# /etc/logrotate.d/formacampus
/var/log/formacampus/*.log {
daily # tourner tous les jours
rotate 14 # garder 14 archives (donc ~14 jours d'historique)
compress # gzip les archives (elles pesent 10x moins)
delaycompress # ne pas compresser la plus recente (encore ecrite)
missingok # pas d'erreur si le fichier n'existe pas encore
notifempty # ne pas tourner un fichier vide
copytruncate # copier puis vider le fichier en place
}daily+rotate 14: on tourne chaque jour et on conserve 14 archives. Au-delà, les plus vieilles sont supprimées automatiquement. C’est ta rétention : assez d’historique pour enquêter, pas assez pour saturer.compress: les vieux logs sont gzippés, ce qui divise leur taille par dix environ. Un log texte se compresse remarquablement bien.copytruncateest utile quand l’app garde le fichier ouvert en écriture : logrotate le copie puis le vide sur place, sans exiger que l’app rouvre son fichier. (Les daemons qui savent réouvrir sur signal utilisent plutôtcreate.)
🐚 Au terminal — Ne devine pas si ta règle marche, teste-la à sec :
# Simuler sans rien modifier (mode debug), voir ce que logrotate ferait
sudo logrotate -d /etc/logrotate.d/formacampus
# Forcer une rotation immédiate pour valider en vrai (-f = force)
sudo logrotate -f /etc/logrotate.d/formacampusLes niveaux de log
Un message de log a une gravité. La hiérarchie standard (syslog), du plus grave au plus anodin : emergency, alert, critical, error, warning, notice, info, debug. En pratique tu raisonnes surtout avec quatre d’entre eux : error (quelque chose a échoué), warning (anomalie non bloquante), info (fonctionnement normal), debug (détail verbeux, pour développer).
L’intérêt : filtrer. En prod, tu ne veux pas noyer le signal sous le debug. Le filtre -p err de journalctl ne montre que le niveau error et au-dessus. Côté application, tu règles le niveau de log de ton framework (souvent info en prod, debug seulement en cas d’enquête).
⚠️ Piège — Laisser le niveau
debugactif en production. Non seulement ça noie les vraies erreurs dans un océan de détails, mais ça remplit le disque bien plus vite. En prod :infopar défaut,debugseulement le temps d’une investigation, puis on revient àinfo.
Centralisation & persistance (survol)
Sur un seul VPS, lire les logs sur la machine suffit. Mais deux améliorations valent d’être connues.
Rendre journald persistant. Par défaut, sur certaines installations, le journal systemd est en mémoire et disparaît au reboot — gênant pour enquêter sur un crash. Pour le conserver sur disque, on crée /var/log/journal (et journald bascule en mode persistant) ; on borne alors sa taille dans /etc/systemd/journald.conf (SystemMaxUse) pour, encore une fois, ne pas remplir le disque.
Centraliser (survol). Dès que tu as plusieurs serveurs, aller lire les logs machine par machine devient pénible. On envoie alors les logs ailleurs, vers un collecteur central (des stacks comme Loki/Grafana, ou des services managés), pour tout chercher au même endroit. Sur un unique VPS FormaCampus, c’est prématuré ; on le garde en tête pour plus tard.
🧭 Sur FormaCampus — Sur le VPS de FormaCampus, l’équipe a posé une règle
logrotatepour chaque log applicatif (front Next.js et API Symfony écrivent dans/var/log/formacampus/), avecrotate 14etcompress— après s’être fait peur une fois, quand un log de debug oublié avait grignoté le disque en une nuit. Le journald est passé en persistant et borné, pour pouvoir analyser un reboot inattendu. Etauth.logest surveillé : toute rafale suspecte déclenchera une alerte (chapitre 14.3). Résultat : les logs informent sans jamais menacer la machine.
✏️ Exercices
Exercice 1 — L’API renvoie des 500 depuis 10 minutes. Ton API Symfony tourne en service systemd nommé formacampus-api. Quelle(s) commande(s) tapes-tu pour voir précisément ce qui se passe, et pourquoi ces options ?
✅ Solution
D’abord l’état résumé : systemctl status formacampus-api (le service tourne-t-il, a-t-il redémarré ?). Puis les erreurs récentes de ce service : journalctl -u formacampus-api -p err --since "15 min ago". Le -u cible l’API (on ignore le reste), -p err ne garde que les erreurs (on écarte le bruit info), --since "15 min ago" borne à la fenêtre du problème. Pour voir l’erreur se produire en direct, on reproduit la requête en gardant ouvert journalctl -u formacampus-api -f.
Exercice 2 — Un log applicatif menace le disque. Ton app écrit dans /var/log/formacampus/app.log, qui grossit vite et n’est couvert par aucune rotation. Écris une règle logrotate qui tourne le log chaque jour, garde 7 jours d’historique, compresse les archives, et fonctionne même si l’app garde le fichier ouvert.
✅ Solution
Un fichier /etc/logrotate.d/formacampus-app :
/var/log/formacampus/app.log {
daily
rotate 7
compress
delaycompress
missingok
notifempty
copytruncate
}daily + rotate 7 = rotation quotidienne, 7 archives conservées (les plus vieilles supprimées). compress gzippe les archives. copytruncate copie puis vide le fichier en place, ce qui convient à une app qui garde son log ouvert sans savoir le réouvrir. On valide avec sudo logrotate -d /etc/logrotate.d/formacampus-app (simulation) puis -f (rotation forcée).
🧠 Quiz de révision
1. Quelle est la différence entre le journal systemd et les fichiers de /var/log ?
Le journal systemd (journald, lu avec journalctl) collecte de façon structurée et requêtable les sorties des services systemd et du noyau. Les fichiers de /var/log sont des logs texte classiques écrits directement par certains logiciels (Nginx, cron, auth.log…). On lit les premiers avec journalctl, les seconds avec tail/grep/less.
2. Que font les options -u, -f et -p err de journalctl ?
-u <service> cible un service précis. -f (follow) affiche les nouvelles lignes en temps réel. -p err filtre par priorité et ne garde que le niveau error et au-dessus (on écarte le bruit informatif).
3. Pourquoi la rotation des logs est-elle vitale, et avec quel outil ?
Parce qu’un log grandit sans fin et finit par remplir le disque, ce qui provoque une panne totale (la base et l’app ne peuvent plus écrire). L’outil est logrotate : il renomme le log, en ouvre un neuf, compresse les anciens et supprime les trop vieux, bornant ainsi la taille tout en gardant un historique.
4. Que surveille-t-on dans /var/log/auth.log ?
Les authentifications : connexions SSH (réussies et échouées), usages de sudo/su. On y repère les bruteforces SSH (rafales de Failed password) et surtout tout accès inattendu (Accepted publickey ou sudo qu’on n’a pas initié), signal de sécurité majeur.
5. Pourquoi éviter le niveau debug en production ?
Parce que debug noie les vraies erreurs sous un flot de détails et remplit le disque bien plus vite. En prod, on reste en info, et on n’active debug que temporairement, le temps d’une investigation, avant de revenir à info.
Chapitre suivant : Métriques système — mesurer l’état de la machine et trouver le goulot d’étranglement.