Compter le nombre de codes HTTP dans des fichiers logs avec awk
Aujourd'hui je devais sortir rapidement la liste des différents codes HTTP de tous les fichiers logs IIS. Comme expliqué dans mon post précédent j'ai un fichier par jour et environ 16 GB de fichiers logs sur une année...
Dans un fichier log pour un serveur web il y a en général un code indiquant le status de l'opération réalisée.
Ainsi dans
IIS on peut utiliser un format de log W3C qui affichera la colonne sc-status pour le Http status code ainsi que la colonne sc-substatus. Mon but est de parcourir tous les fichiers logs et de calculer le nombre de fois que l'on trouve chacun de ces status code.
On pourrait le faire avec du perl, mais je préfère aujourd'hui essayer awk présent sur toutes les bonnes distros Linux en standard.
awk ou le plus répandu et moderne gawk est un outil formidable pour traiter très rapidement des gros volumes de données au format texte. Commençons par un exemple simple :
je veux trouver toutes les lignes du log ou le code d'état HTTP est plus grand que 500 ce qui correspond à une erreur serveur. Et bien avec awk cela ce fait simplement en écrivant :
gawk '$14>500 {print}' 2007/ex070105.log
la première partie indique une règle qui est suivie entre accolade de l'action à entreprendre si cette règle est vraie.Donc dans ce cas on dit que lorsque $14 est plus grand que 500 on veut afficher la ligne courante.
Pourquoi $14 me direz vous et bien il se trouve que awk considère chaque ligne comme un enregistrement et dans chaque ligne il considère que chaque fois qu'il y a un espace c'est un nouveau champ de l'enregistrement courant.
Comme une ligne de mon log IIS est composée des champs suivants :
# date time s-sitename s-ip cs-method cs-uri-stem cs-uri-query s-port cs-username c-ip cs-version cs(User-Agent) cs(Referer) sc-status sc-substatus sc-win32-status sc-bytes cs-bytes time-taken
pour awk le $1 contiendra la date,le $2 la valeur time et ainsi de suite.
bien entendu la commande précédente peut être lancée sur un groupe de fichiers. Par exemple pour avoir la liste complète pour tout le mois de janvier il suffit de faire:
gawk '$14>500 {print}' 2007/ex0701*
Dans le cas qui m'intéresse je vais devoir stocker dans un tableau associatif une entrée par paire de status/sous-status trouvé et l'incrémenter chaque fois que je le retrouve et tout cela ce fait en écrivant :
{s[$14 ":" $15]++}
Ici la variable tableau s'appelle "s" et la clé d'indexation et composée de la valeur courante du champ $14 (sc-status) suivi d'un charactère de séparation ":" puis la valeur de $15 (sc-substatus)
Comme je ne veux pas traiter les lignes de commentaires je peux ajouter une règle avant cette action qui ne prendra que les lignes qui ne commence pas par un "#" ce qui donnera donc :
$1 !~ /^#.+$/ {s[$14 ":" $15]++}
http://www.blogger.com/img/gl.link.gifIl ne me reste plus qu'à parcourir le contenu de mon tableau associatif une fois que toutes les lignes ont été traitées et c'est la que la règle END prend tout son sens puisqu'elle correspond justement au cas que nous voulons on aura donc qqch du style:
END{for(i in s) {printf("%-9s\t%i\n",i,s[i])}}
Voici la solution complète de mon problème en une ligne, ça va donner au final qqch comme:
gawk '$1 !~ /^#.+$/ {s[$14 ":" $15]++}; END{Mysort="sort -k 1";for(i in s) {printf("%-9s\t%i\n",i,s[i])|Mysort}}' 2007/ex0701*
On peut trouver la liste des status-code et leur explication pour IIS sur le site Technet de Microsoft
Par ailleurs la documentation très complète de awk est disponible sur le site GNU
Voici un lien sur un site avec plusieurs Manuels très utile tels que bash, bison et awk
Commentaires