Analyse de fichiers Log en perl
Quand il faut analyser de grandes quantité de données sous format texte tel que des fichiers logs, Perl est d'une aide inappréciable.
Voici un joli "perl oneliner" qui va calculer le nombre de hits par user login dans un log IIS
Analysons un peu cette ligne de commande:
#Software: Microsoft Internet Information Services 6.0
#Version: 1.0
#Date: 2007-01-07 11:33:26
#Fields: 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
C'est à cause de ces lignes que j'ai le unless du point 2 et vous voyez que la quantité de champ que vous pouvez analysez est plutôt vaste alors bonne analyse.
Voici un joli "perl oneliner" qui va calculer le nombre de hits par user login dans un log IIS
perl -ne 'unless (/^#.*$/) {@e=split(/ /,$_); $Login{uc($e[8])}++;} END{foreach $key (sort keys %Login){printf(qq(%-25s %i\n),$key,$Login{$key}); } }' 2007/ex070106.log 2007/ex070107.log |sort -k 2nr >DistinctsLogins200701.txt
Analysons un peu cette ligne de commande:
- perl -ne : on lance perl avec les option -n qui rajoute une boucle while (<>) {...} implicite et l'option -e qui permet justement d'exécuter une commande cf doc perl en ligne
- 'unless
(/^#.*$/) { :
viens maintenant le début du programme perl, en l'occurrence on dit que tant qu'on est pas dans une ligne de commentaire on va faire quelque chose se trouvant après l'accolade ouvrante. En effet dans les fichiers logs une ligne commentaire commence par un dièse # d'ou l'expression régulière /^#.*$/ qui signifie si la ligne commence par (c'est le chapeau ^) un dièse (# ) suivi de n'importe quel caractère (.) qui se répète zéro ou plusieurs fois (*) jusqu'à la fin de la ligne ($) - @e=split(/ /,$_); :on stocke dans le tableau @e tous les éléments résultants de l'éclatement de la ligne courante ($_) en réalisant cet "éclatement" en partageant la ligne en plusieurs morceaux séparés par un espace (/ /). On aurait pu aussi bien utiliser une autre expression régulière que celle-ci qui est vraiment triviale, mais il se trouve que dans les fichiers logs de IIS, les champs de chaque lignes sont bel et bien séparés par un espace.
- $Login{uc($e[8])}++; : ici on incrémente la valeur stockeé dans un tableau associatif (ou un dictionnaire si vous préférez) $Login dont la clé est justement le nom du login trouvé à l'emplacement 9 $e[8] que l'on passe en majuscules (avec la fonction uc). C'est assez concis par rapport à d'autres langages... ainsi si on trouve sur la ligne courante on trouve un utilisateur du nom de toto on va créer une nouvelle entrée dans notre tableau associatif $Login{"toto"} qui sera incrémentée de 1 à l'aide de l'opérateur ++. Quand on retombera plus loin sur une autre ligne avec l'utilisateur toto et bien l'entrée du tableau associatif qu'on avait créé au stade d'avant va simplement s'incrémenter à deux.
- } END { : voila on est à la fin du code qui s'éxécute pour chaque ligne du fichier sauf si c'est un commentaire. Et on va passer grâce au "END {...}" au bloc de code qui doit s'éxécuter une fois que toutes les lignes de tous les fichiers passé en paramètre aurons été traités. Cette syntaxe sympa est tirée de l'outil awk dont il faudra que je vous parle d'ailleurs
- foreach $key (sort keys %Login){ : On veut parcourir notre tableau alors on a pour chaque clé ($key) de notre tableau associatif (%Login) trié au préalable par les valeurs de ces clés (sort keys). La phrase est un peu alambiquée mais c'est assez simple
- printf(qq(%-25s %i\n),$key,$Login{$key}); : Ici on faire un affichage formaté avec la fonction printf d'une chaine entre guillemet (la fonction qq(coucou) équivaut au traditionnel "coucou" mais elle est beaucoup plus digeste dans une ligne de commande). le %-25s revient à dire affiche sur 25 colonnes aligné à gauche la chaine de caractère que je vais te donner. Il est suivi du %i\n pour afficher le nombre de hits trouvé pour cet utilisateur suivi d'un saut de ligne. Enfin on passe les deux valeurs à savoir $key qui contient le nom de l'utilisateur courant et $Login{$key} son nombre de hits.
- } }' : Et voila ! on ferme le bloc du foreach et on ferme le bloc de code du END{ et nous voici au bout de notre commande perl ... plus long à expliquer qu'à taper...
- 2007/ex070106.log 2007/ex070107.log : sont deux fichiers de logs que je veux traiter. J'aurais pu aussi bien dire 2007/*.log pour traiter d'un coup tous mes fichiers logs de l'année. Ou encore 2007/ex0701* pour traiter uniquement les fichiers du mois de janvier.
- |sort -k 2nr >DistinctsLogins200701.txt : Finalement je "pipe" avec | les résultats dans le filtre sort avec les options -k 2nr ce qui a pour effet d'effectuer un tri numérique descendant basé sur la deuxième colonne du fichier. Au final je redirige le tout dans le fichier DistinctsLogins200701.txt.
#Software: Microsoft Internet Information Services 6.0
#Version: 1.0
#Date: 2007-01-07 11:33:26
#Fields: 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
C'est à cause de ces lignes que j'ai le unless du point 2 et vous voyez que la quantité de champ que vous pouvez analysez est plutôt vaste alors bonne analyse.
Commentaires