command line perl e analisi dei log

Tempo fa abbiamo visto come utilizzare il perl come tool di linea di comando. Vediamo oggi come utilizzarlo in un caso un poco più complesso: per contare le occorrenze di un dato vhost in un access.log di apache e magari al tempo stesso avere le statistiche di quante volte apache da una risposta corretta (return code 200) o qualche altro errore.

Osserviamo per prima cosa una riga di log del nostro sito preferito

xx.xx.xx.xx linuxandcompany.it - [06/Apr/2014:23:54:13 +0200] "GET /2014/01/squid-come-reverse-proxy/ HTTP/1.1" 200 10177 "-" "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_8_4) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/27.0.1453.110 Safari/537.36"

Questo tipo di analisi si riesce a fare direttamente in linea con il perl. Iniziamo prima da una cosa cosa semplice: contiamo il numero di occorrenze dei vari return code per un dato sito:

cat linuxandcompany.it-08-04-2014.log|grep "linuxandcompany.it" |perl -ne '{@fields = split(/ /); $stat{$fields[8]}++;}END{while( ($key1, $value1) = each(%stat) ){print $key1. "\t" .$value1. "\n"}}'

che darà un risultato della forma:

304	16
200	397
302	4
301	2
404	2

Il comando inizia con un cat che passa ogni riga del file di log in stdout. Lo stdout viene intercettato da un grep che seleziona le righe del mio sito.
Le righe selezionate vengono poi passate al comando per che, come visto, con le opzioni ne applica il comando che segue ad ogni riga.
Il comando per è costituito da due blocchi: quello ordinario nella prima coppia di parentesi graffe e un secondo blocco identificato da END{…} che viene eseguito solo a conclusione del ciclo.

Il primo blocco inizia con uno split che separa la stringa in stdin in un array con le varie parole per elementi

@fields = split(/ /);

Tra gli slash è riportato il carattere scelto per spezzare la stringa.
Lo split avrebbe potuto anche essere scritto come

@fields = split(/ /, $_);

Utilizzo poi i campi per incrementare un hash di contatori che ha per chiavi i return code.

$stat{$fields[8]}++;

Il blocco finale si occuperà poi di visualizzare il risultato:

while( ($key1, $value1) = each(%stat) )
   {
   print $key1. "\t" .$value1. "\n"
   }

ed è costituito da un ciclo sulle coppie chiave, valore dell’hash.

In una situazione reale questo comando funziona però fino ad un certo punto perché le stringhe possono contenere spazi anche se non è frequentissimo. Un risultato molto migliore si ottiene combinando due split: un primo basato sulle virgolette, che racchiudono le stringe significative, ed un secondo basato sugli spazi da applicare alle sottostringhe:

cat linuxandcompany.it-08-04-2014.log|grep "linuxandcompany.it" |perl -ne '{@fields = split(/"/); @sub_fields = split(/ /, $fields[2]); $stat{$sub_fields[1]}++;}END{while( ($key1, $value1) = each(%stat) ){print $key1. "\t" .$value1. "\n"}}'

Si può poi complicare a piacere andando a fare la stessa statistica per ogni sito presente nel log. Sotto faccio un doppio report (per sito e complessivo).

cat linuxandcompany.it-08-04-2014.log |perl -ne '{@fields = split(/"/); @sub_fields1 = split(/ /, $fields[0]); @sub_fields2 = split(/ /, $fields[2]); $stat1{$sub_fields1[1]}{$sub_fields2[1]}++; $stat2{$sub_fields2[1]}++}END{while(($key1, $value1) = each(%stat1)){ while( ($key2,$value2) = each(%$value1) ){print $key1. "\t" . $key2."\t".$value2. "\n"}}; print "\n\n"; while( ($key1, $value1) = each(%stat2) ){print $key1. "\t" .$value1. "\n"}}'

Rimane evidente che questo è al limite di quello che ha senso fare con una linea di comando e non scrivendo un piccolo script.

Comandi analoghi si possono utilizzare ad esempio per contare le occorrenze delle pagine nel log.

Lascia un commento

Il tuo indirizzo email non sarà pubblicato. I campi obbligatori sono contrassegnati *