Maîtrise de l'analyse des logs Nginx pour un dépannage efficace

Débloquez un dépannage efficace en maîtrisant les logs d'accès et d'erreur de Nginx. Ce guide détaille comment configurer des formats de logs personnalisés pour capturer des métriques de temps cruciales, vous permettant d'identifier les goulots d'étranglement de performance au sein de Nginx ou du serveur d'application en amont. Apprenez à diagnostiquer instantanément les problèmes critiques comme les erreurs 502 et 504 en utilisant les niveaux de gravité des logs d'erreur, et utilisez des commandes shell puissantes (`grep`, `awk`) pour filtrer, compter et analyser rapidement les schémas de trafic.

Maîtrise de l'analyse des logs Nginx pour un dépannage efficace

Les logs Nginx sont généralement le moyen le plus rapide de transformer « le site est en panne » en un problème spécifique. Le log d'accès vous indique ce que les clients ont demandé et le statut qu'ils ont reçu. Le log d'erreur vous indique ce que Nginx n'a pas pu faire : se connecter à un amont, lire un certificat, ouvrir un fichier, analyser une configuration ou attendre assez longtemps une réponse du backend.

Une bonne analyse des logs Nginx ne consiste pas à fixer les fichiers jusqu'à ce que quelque chose paraisse suspect. Il s'agit de poser une question précise, de filtrer rapidement et de corréler le log d'accès avec le log d'erreur et les logs de l'application amont. Un 502 dans le log d'accès est un symptôme. La ligne correspondante dans le log d'erreur est généralement le début de la réponse.


1. Fondamentaux des logs Nginx : Accès vs. Erreur

Nginx maintient deux types distincts de logs, chacun remplissant une fonction critique et séparée :

1.1 Le log d'accès (access.log)

Le log d'accès enregistre les détails de chaque requête que Nginx traite. Il est essentiel pour comprendre le comportement des utilisateurs, surveiller le flux de trafic et évaluer les temps de réponse.

Emplacement par défaut : Généralement /var/log/nginx/access.log

Objectif : Suivi des interactions client, des requêtes réussies, des erreurs client, des erreurs serveur renvoyées via Nginx, des octets envoyés, des agents utilisateurs et du timing des requêtes si configuré.

1.2 Le log d'erreur (error.log)

Le log d'erreur suit les problèmes internes, les échecs opérationnels et les problèmes de communication qui surviennent pendant le cycle de vie du traitement de Nginx. Ce log est la source définitive pour dépanner les problèmes de connectivité backend et les erreurs de configuration du serveur.

Emplacement par défaut : Généralement /var/log/nginx/error.log

Objectif : Suivi des erreurs côté serveur, des avertissements et des événements système (erreurs 5xx, échecs d'analyse de fichiers de configuration).

Niveaux de gravité du log d'erreur

Nginx utilise huit niveaux de gravité. Lors du dépannage, vous voulez généralement commencer au niveau error ou supérieur. Le niveau de gravité est configuré à l'aide de la directive error_log :

# Définir le niveau de gravité minimum à 'warn'
error_log /var/log/nginx/error.log warn;
Niveau Description Priorité
crit Conditions critiques, comme un échec d'exécution grave La plus élevée
error Une erreur s'est produite qui a empêché le traitement d'une requête Élevée
warn Quelque chose d'inattendu s'est produit, mais les opérations continuent Moyenne
notice Condition normale mais significative (par exemple, redémarrage du serveur) Faible
info Messages informatifs La plus faible

Il existe également les niveaux emerg, alert et debug. debug peut être extrêmement bavard et nécessite généralement une version de Nginx avec le support de débogage. Utilisez-le pour un dépannage ciblé, pas comme un paramètre de production normal.

2. Personnalisation des logs d'accès pour l'analyse des performances

Le format de log d'accès par défaut de Nginx, souvent appelé combined, est utile mais manque de variables de timing de performance cruciales. Pour dépanner efficacement les lenteurs, vous devez définir un format personnalisé qui capture combien de temps Nginx a passé à traiter la requête et combien de temps le serveur amont a pris.

2.1 Définition d'un format de log de performance

Utilisez la directive log_format (généralement définie dans nginx.conf) pour créer un format personnalisé, par exemple timing_log :

log_format timing_log '$remote_addr - $remote_user [$time_local] ' 
                    '"$request" $status $body_bytes_sent ' 
                    '"$http_referer" "$http_user_agent" ' 
                    '$request_time $upstream_response_time';

server {
    listen 80;
    server_name example.com;
    
    # Appliquer le format personnalisé ici
    access_log /var/log/nginx/timing_access.log timing_log;
    # ... reste de la configuration
}
Variable Description Valeur de dépannage
$request_time Temps total écoulé entre la réception du premier octet et l'envoi du dernier octet. Des valeurs élevées indiquent un réseau lent, un Nginx lent ou un backend lent.
$upstream_response_time Temps passé à attendre la réponse du serveur amont (par exemple, le serveur d'application). Des valeurs élevées ici identifient l'application backend comme le goulot d'étranglement.
$status Code de statut HTTP renvoyé au client. Essentiel pour filtrer les erreurs (4xx, 5xx).

Envisagez d'utiliser le formatage JSON des logs lorsque les logs sont envoyés à un système centralisé. Le JSON est plus difficile à lire à l'œil nu, mais beaucoup plus facile à analyser de manière fiable pour les outils. Si vous conservez des logs en texte brut, sachez que les numéros de champ awk peuvent être perturbés lorsque les agents utilisateurs, les chemins de requête ou les champs entre guillemets contiennent des espaces.

Envisagez également de journaliser les ID de requête. Si votre équilibreur de charge ou votre application envoie déjà un en-tête d'ID de requête, transmettez-le et journalisez-le :

log_format timing_log '$remote_addr [$time_local] '
                    '"$request" $status $body_bytes_sent '
                    'request_time=$request_time '
                    'upstream_time=$upstream_response_time '
                    'request_id=$request_id '
                    'upstream=$upstream_addr';

Un ID de requête vous permet de connecter une requête publique lente à une entrée de log d'application. Sans cela, vous devez faire correspondre par horodatage, chemin et IP client, ce qui est possible mais beaucoup moins agréable.

3. Interprétation des entrées du log d'accès

Une entrée typique utilisant le format personnalisé pourrait ressembler à ceci (avec les valeurs de timing ajoutées à la fin) :

192.168.1.10 - - [10/May/2024:14:30:05 +0000] "GET /api/data HTTP/1.1" 200 450 "-" "Mozilla/5.0" 0.534 0.528

Diagnostic :

  1. Code de statut (200) : Succès.
  2. Temps de requête (0.534s) : Le temps total est d'une demi-seconde.
  3. Temps amont (0.528s) : Presque tout le temps a été passé à attendre l'application backend (0.534 - 0.528 = 0.006s passé par la surcharge Nginx).

Diagnostic : Pour cette requête, l'application backend est la source probable de la latence de 500 ms. La surcharge Nginx semble faible.

Ne généralisez pas à partir d'une seule ligne. Examinez un échantillon de requêtes lentes. Si la plupart des requêtes lentes ont un $upstream_response_time élevé, concentrez-vous sur l'application ou le réseau amont. Si $request_time est élevé alors que $upstream_response_time est faible, le retard peut être dû au temps de téléchargement du client, au téléchargement lent du client, au comportement de mise en mémoire tampon ou au travail côté Nginx.

Dépannage à l'aide des codes de statut

Plage de codes de statut Signification Action typique / Source du log
4xx (Erreurs client) Le client a envoyé une requête invalide ou non autorisée. Vérifiez les logs d'accès pour une fréquence élevée. Recherchez 404 Not Found (fichiers manquants) ou 403 Forbidden (problèmes de permissions).
5xx (Erreurs serveur) Nginx ou un serveur amont n'a pas réussi à répondre à une requête valide. Vérifiez immédiatement le log d'erreur pour les entrées correspondantes.
502 Bad Gateway Nginx n'a pas pu obtenir de réponse de l'application amont. Le log d'erreur montrera les détails (Connexion refusée, Délai d'attente).
504 Gateway Timeout Le serveur amont a mis trop de temps à répondre dans les limites de proxy configurées. Le log d'erreur montrera des avertissements de délai d'attente. Enquêtez sur la latence du backend avant d'augmenter les délais d'attente.

Augmenter proxy_read_timeout peut masquer le symptôme alors que les utilisateurs attendent toujours trop longtemps. C'est valable pour les points de terminaison de longue durée, le streaming ou les opérations lentes connues, mais pour les requêtes API normales, cela devrait d'abord déclencher une enquête sur le backend.

4. Diagnostic des problèmes critiques dans le log d'erreur

Lorsqu'une requête entraîne une erreur 5xx, le log d'accès vous indique seulement que l'erreur s'est produite. Le log d'erreur vous indique pourquoi.

Étude de cas : 502 Bad Gateway

Une erreur 502 est l'un des problèmes les plus courants lors de l'utilisation de Nginx comme proxy inverse. Elle pointe presque toujours vers une application backend qui est en panne, surchargée ou inaccessible.

Recherchez ces messages spécifiques dans le log d'erreur :

4.1 Connexion refusée (Backend en panne)

Cela indique que Nginx a essayé de se connecter au port backend mais que rien n'écoutait, ce qui signifie que le serveur d'application (par exemple, PHP-FPM, Gunicorn) est arrêté ou mal configuré.

2024/05/10 14:35:10 [error] 12345#0: *1 connect() failed (111: Connection refused) while connecting to upstream, client: 192.168.1.10, server: example.com, request: "GET /test"
  • Action : Vérifiez si le service backend est en cours d'exécution, s'il écoute sur le port ou le socket Unix attendu, et si Nginx pointe vers la même adresse. Redémarrez seulement après avoir compris pourquoi il s'est arrêté.

4.2 Connexion amont prématurément fermée (Crash du backend)

Cela se produit lorsque Nginx établit une connexion mais que le serveur backend la termine avant d'envoyer une réponse HTTP complète. Cela suggère souvent une erreur fatale ou un crash dans le code de l'application.

2024/05/10 14:38:22 [error] 12345#0: *2 upstream prematurely closed connection while reading response header from upstream, client: 192.168.1.10, server: example.com, request: "POST /submit"
  • Action : Vérifiez les logs d'erreur natifs du serveur d'application (par exemple, logs PHP-FPM, logs Node.js) pour l'erreur fatale spécifique.

Avertissement : Si Nginx ne parvient pas à lire son fichier de configuration au démarrage, l'erreur sera souvent envoyée directement sur la sortie d'erreur standard ou dans un fichier log d'amorçage, et non à l'emplacement configuré error.log. Vérifiez toujours journalctl -xe ou les logs système si Nginx ne démarre pas.

Étude de cas : 403 Forbidden

Un 403 dans le log d'accès peut être causé par l'autorisation de l'application, les règles d'accès Nginx, les permissions du système de fichiers ou le comportement de l'index de répertoire. Le log d'accès seul ne peut pas vous dire lequel.

Recherchez dans le log d'erreur des lignes comme :

2024/05/10 15:02:01 [error] 12345#0: *12 directory index of "/var/www/site/" is forbidden

Cela signifie que Nginx a atteint un répertoire mais n'avait aucun fichier d'index à servir et que la liste des répertoires est désactivée. La solution peut être de créer le index.html attendu, d'ajuster la directive index ou de rediriger la requête vers l'application.

Pour les problèmes de permissions, vous pouvez voir :

2024/05/10 15:04:44 [error] 12345#0: *15 open() "/var/www/site/private.txt" failed (13: Permission denied)

Vérifiez la propriété du fichier, les permissions d'exécution du répertoire, la politique SELinux ou AppArmor le cas échéant, et l'utilisateur sous lequel les workers Nginx s'exécutent.

Étude de cas : 499 Client Closed Request

Le statut 499 spécifique à Nginx signifie que le client a fermé la connexion avant que Nginx ait fini de répondre. C'est courant lorsque les utilisateurs naviguent ailleurs, que les clients mobiles perdent la connectivité ou qu'un amont prend tellement de temps que le client abandonne.

Ne traitez pas chaque 499 comme un bug Nginx. Regardez le timing. Si de nombreux 499 ont un temps de requête élevé et correspondent à des amonts lents, les utilisateurs abandonnent peut-être des requêtes lentes. S'ils se produisent immédiatement à partir d'un client ou d'un réseau, il peut s'agir d'un comportement du client.

5. Commandes shell pratiques pour l'analyse des logs

Bien que des systèmes de surveillance de logs robustes soient recommandés pour la production, la ligne de commande Linux fournit des outils puissants pour un dépannage rapide en temps réel.

5.1 Surveillance en temps réel

Surveillez les logs au fur et à mesure que les requêtes arrivent (particulièrement utile après avoir déployé un correctif ou testé une nouvelle fonctionnalité) :

tail -f /var/log/nginx/access.log
# Ou, pour les erreurs uniquement
tail -f /var/log/nginx/error.log

Pour les logs pivotés et compressés, utilisez zgrep :

zgrep '" 50[0-9] ' /var/log/nginx/access.log*.gz

La rotation des logs est importante lors de l'examen d'un incident. L'erreur a peut-être eu lieu juste avant minuit ou avant qu'un travail de rotation ne compresse le fichier de la veille.

5.2 Filtrage et comptage des erreurs

Trouvez et comptez rapidement les erreurs 5xx les plus fréquentes de la dernière heure ou du dernier jour :

# Trouver toutes les requêtes 5xx
grep '" 50[0-9] ' /var/log/nginx/access.log | less

# Compter la distribution des erreurs 5xx (par exemple, combien de 502 vs. 504)
grep '" 50[0-9] ' /var/log/nginx/access.log | awk '{print $9}' | sort | uniq -c | sort -nr

Explication : awk '{print $9}' isole le code de statut HTTP (en supposant le format de log par défaut ou combiné où le statut est le 9ème champ).

Si vous utilisez un format de log personnalisé, confirmez le numéro de champ avant de faire confiance au comptage. Une vérification rapide plus sûre consiste à imprimer quelques lignes analysées :

awk '{print NR, $0; if (NR == 3) exit}' /var/log/nginx/access.log

Pour les logs JSON, utilisez jq au lieu des numéros de champ :

jq -r 'select(.status >= 500) | .status' /var/log/nginx/access.json \
  | sort | uniq -c | sort -nr

5.3 Identification des requêtes lentes (nécessite un format de log personnalisé)

Si vous avez implémenté le format timing_log (où $request_time est l'avant-dernier champ, ou le champ 16 dans notre exemple) :

# Trouver les 10 requêtes les plus lentes (par exemple, les requêtes prenant plus d'une seconde)
awk '($16 > 1.0) {print $16, $7}' /var/log/nginx/timing_access.log | sort -nr | head -10

Explication : Cette commande imprime le temps de requête et l'URI ($7) pour toute requête qui a pris plus de 1,0 seconde, triés par ordre décroissant.

Un format de timing en texte brut plus lisible utilise des valeurs nommées, comme request_time=0.534. Ensuite, vous pouvez grep pour des plages lentes de manière moins élégante mais avec moins de surprises sur les numéros de champ. Pour une analyse sérieuse, envoyez des logs structurés à un système de logs et interrogez les percentiles par route.

5.4 Identification des adresses IP les plus demandeuses

Utile pour repérer les tentatives de déni de service potentielles, les pics de trafic ou les activités suspectes :

# Trouver les 20 premières IP faisant des requêtes
awk '{print $1}' /var/log/nginx/access.log | sort | uniq -c | sort -nr | head -20

Les IP les plus fréquentes sont un point de départ, pas une preuve d'abus. Un NAT d'entreprise, un edge CDN ou un équilibreur de charge peut faire apparaître de nombreux utilisateurs comme une seule source. Si Nginx est derrière un proxy, configurez et journalisez soigneusement la véritable IP client avec real_ip_header et les plages de proxy de confiance. Ne faites jamais confiance aux en-têtes X-Forwarded-For arbitraires provenant d'Internet ouvert.

Un flux de dépannage pratique

Commencez par le symptôme de l'utilisateur et une fenêtre de temps. « Le paiement a renvoyé des 502 vers 14h35 UTC » est beaucoup plus utile que « Nginx est cassé ».

D'abord, comptez les statuts :

grep '10/May/2024:14:3' /var/log/nginx/access.log \
  | awk '{print $9}' | sort | uniq -c | sort -nr

Le filtrage par date avec des logs en texte brut est difficile, et la commande exacte dépend de votre format de log. Pour une vérification rapide d'incident, même un filtrage approximatif peut montrer si le problème était principalement des 502, 504, 403 ou 404.

Ensuite, extrayez quelques requêtes correspondantes :

grep '" 502 ' /var/log/nginx/access.log | tail -20

Notez l'horodatage, l'URI, le temps amont et l'ID de requête s'il est présent. Ensuite, recherchez dans le log d'erreur autour du même horodatage :

grep '14:35' /var/log/nginx/error.log

Si l'erreur dit connect() failed (111: Connection refused), inspectez le service amont et son port. Si elle dit upstream timed out, inspectez la latence du backend et la mise en file d'attente. Si elle dit no live upstreams, inspectez la santé de l'amont, le DNS ou la configuration de l'équilibreur de charge.

Enfin, vérifiez les logs du backend en utilisant le même ID de requête ou horodatage. Nginx vous dit souvent où le transfert a échoué, mais le log du backend vous dit pourquoi l'application s'est comportée de cette façon.

Rendre les logs utiles avant la panne

Le pire moment pour améliorer la journalisation est pendant une panne. Ajoutez le timing des requêtes, le timing amont, l'adresse amont et les ID de requête avant d'en avoir besoin. Gardez les logs d'accès et d'erreur séparés par site lorsqu'un serveur héberge plusieurs applications. Assurez-vous que la rotation conserve suffisamment d'historique pour les incidents que vous enquêtez réellement.

Quand quelque chose se casse, lisez les logs par paires : le log d'accès pour ce qui s'est passé, le log d'erreur pour ce que Nginx n'a pas pu faire, le log d'application pour ce que l'amont a fait ensuite. Cette habitude maintient le dépannage ciblé et vous mène généralement à la véritable panne plus rapidement que de modifier les délais d'attente ou de redémarrer les services au hasard.