sviluppo-web-qa.it

Hai degli utili script awk e grep per l'analisi dei log di Apache?

Posso usare gli analizzatori di log, ma spesso ho bisogno di analizzare i log web recenti per vedere cosa sta succedendo in questo momento.

A volte faccio cose come capire i 10 migliori IP che richiedono un determinato file

cat foo.log | grep request_to_file_foo | awk '{print $1}' |  sort -n | uniq -c | sort -rn | head

Cosa hai nella tua cassetta degli attrezzi?

70
deadprogrammer

Puoi fare praticamente qualsiasi cosa con i file di log di Apache solo con awk. I file di registro di Apache sono sostanzialmente separati da spazi bianchi e puoi far finta che le virgolette non esistano e accedere a qualsiasi informazione ti interessi per numero di colonna. L'unica volta che si interrompe è se si dispone del formato di registro combinato e si è interessati agli interpreti, a quel punto è necessario utilizzare virgolette (") come separatore ed eseguire un comando awk separato. Di seguito verranno mostrati gli IP di ogni utente che richiede la pagina dell'indice ordinata per numero di risultati:

awk -F'[ "]+' '$7 == "/" { ipcount[$1]++ }
    END { for (i in ipcount) {
        printf "%15s - %d\n", i, ipcount[i] } }' logfile.log

$ 7 è l'URL richiesto. Puoi aggiungere qualunque condizione desideri all'inizio. Sostituisci "$ 7 =="/"con qualsiasi informazione desideri.

Se si sostituisce $ 1 in (ipcount [$ 1] ++), è possibile raggruppare i risultati in base ad altri criteri. L'uso di $ 7 mostra quali pagine sono state accedute e con quale frequenza. Ovviamente allora vorresti cambiare la condizione all'inizio. Quanto segue mostrerebbe quali pagine hanno avuto accesso da un utente da un IP specifico:

awk -F'[ "]+' '$1 == "1.2.3.4" { pagecount[$7]++ }
    END { for (i in pagecount) {
        printf "%15s - %d\n", i, pagecount[i] } }' logfile.log

È inoltre possibile reindirizzare l'output attraverso l'ordinamento per ottenere i risultati in ordine, sia come parte del comando Shell, sia nello script awk stesso:

awk -F'[ "]+' '$7 == "/" { ipcount[$1]++ }
    END { for (i in ipcount) {
        printf "%15s - %d\n", i, ipcount[i] | sort } }' logfile.log

Quest'ultimo sarebbe utile se decidessi di espandere lo script awk per stampare altre informazioni. È tutta una questione di ciò che vuoi scoprire. Questi dovrebbero servire come punto di partenza per qualunque cosa ti interessi.

54
Mark

Una cosa che non ho mai visto fare a nessun altro, per motivi che non riesco a immaginare, è cambiare il formato del file di registro di Apache in una versione più facilmente analizzabile con le informazioni che contano davvero per te.

Ad esempio, non utilizziamo mai l'autenticazione di base HTTP, quindi non è necessario registrare tali campi. I am interessato a quanto tempo impiega ogni richiesta, quindi la aggiungeremo. Per un progetto, vogliamo anche sapere (sul nostro bilanciamento del carico) se qualche server sta servendo richieste più lente di altri, quindi registriamo il nome del server a cui stiamo eseguendo il proxy.

Ecco un estratto dalla configurazione di Apache di un server:

# We don't want to log bots, they're our friends
BrowserMatch Pingdom.com robot

# Custom log format, for testing
#
#         date          proto   ipaddr  status  time    req     referer         user-agent
LogFormat "%{%F %T}t    %p      %a      %>s     %D      %r      %{Referer}i     %{User-agent}i" standard
CustomLog /var/log/Apache2/access.log standard env=!robot

Quello che non puoi davvero dire da questo è che tra ogni campo c'è un carattere di tabulazione letterale (\ t). Ciò significa che se voglio fare qualche analisi in Python, magari per esempio mostrando stati non 200, posso fare questo:

for line in file("access.log"):
  line = line.split("\t")
  if line[3] != "200":
    print line

O se volessi fare "chi è hotlinking delle immagini?" sarebbe

if line[6] in ("","-") and "/images" in line[5]:

Per i conteggi IP in un registro di accesso, l'esempio precedente:

grep -o "[0-9]\{1,3\}\.[0-9]\{1,3\}\.[0-9]\{1,3\}\.[0-9]\{1,3\}" logfile | sort -n | uniq -c | sort -n

diventa qualcosa del genere:

cut -f 3 log | uniq -c | sort -n

Più facile da leggere e comprendere e molto meno costoso dal punto di vista computazionale (senza regex) che, su registri da 9 GB, fa un'enorme differenza nel tempo impiegato. Quando questo diventa DAVVERO pulito è se si desidera fare la stessa cosa per gli User-agent. Se i log sono delimitati da spazi, è necessario eseguire manualmente una corrispondenza delle espressioni regolari o la ricerca di stringhe. Con questo formato, è semplice:

cut -f 8 log | uniq -c | sort -n

Esattamente come sopra. In effetti, qualsiasi riepilogo che vuoi fare è essenzialmente lo stesso.

Perché mai dovrei spendere la CPU del mio sistema in awk e grep quando il taglio farà esattamente quello che voglio ordini di grandezza più velocemente?

24
Dan Udey

Dimentica awk e grep. Dai un'occhiata asql . Perché scrivere script illeggibili quando è possibile utilizzare sql come sintassi per eseguire query sul file di registro. Per esempio.

asql v0.6 - type 'help' for help.
asql> load /home/skx/hg/engaging/logs/access.log
Loading: /home/skx/hg/engaging/logs/access.log
sasql> select COUNT(id) FROM logs
46
asql> alias hits SELECT COUNT(id) FROM logs
ALIAS hits SELECT COUNT(id) FROM logs
asql> alias ips SELECT DISTINCT(source) FROM logs;
ALIAS ips SELECT DISTINCT(source) FROM logs;
asql> hits
46
asql> alias
ALIAS hits SELECT COUNT(id) FROM logs
ALIAS ips SELECT DISTINCT(source) FROM logs;
16
Vihang D

Ecco uno script per trovare i migliori URL, i migliori referrer e i migliori useragent dalle recenti voci di registro N.

#!/bin/bash
# Usage
# ls-httpd type count
# Eg: 
# ls-httpd url 1000
# will find top URLs in the last 1000 access log entries
# ls-httpd ip 1000
# will find top IPs in the last 1000 access log entries
# ls-httpd agent 1000
# will find top user agents in the last 1000 access log entries

type=$1
length=$2

if [ "$3" == "" ]; then
  log_file="/var/log/httpd/example.com-access_log"
else
  log_file="$3"
fi

if [ "$type" = "ip" ]; then
  tail -n $length $log_file | grep -o "[0-9]\{1,3\}\.[0-9]\{1,3\}\.[0-9]\{1,3\}\.[0-9]\{1,3\}" | sort -n | uniq -c | sort -n
Elif [ "$type" = "agent" ]; then
  tail -n $length $log_file | awk -F\" '{print $6}'| sort -n | uniq -c | sort -n
Elif [ "$type" = "url" ]; then
  tail -n $length $log_file | awk -F\" '{print $2}'| sort -n | uniq -c | sort -n
fi

Source

6
anoopjohn

per i conteggi IP in un registro di accesso:

cat log | grep -o "[0-9]\{1,3\}\.[0-9]\{1,3\}\.[0-9]\{1,3\}\.[0-9]\{1,3\}" | sort -n | uniq -c | sort -n

È un po 'brutto, ma funziona. Uso anche quanto segue con netstat (per vedere le connessioni attive):

netstat -an | awk '{print $5}' | grep -o "[0-9]\{1,3\}\.[0-9]\{1,3\}\.[0-9]\{1,3\}\.[0-9]\{1,3\}" | egrep -v "(`for i in \`ip addr | grep inet |grep eth0 | cut -d/ -f1 | awk '{print $2}'\`;do echo -n "$i|"| sed 's/\./\\\./g;';done`127\.|0\.0\.0)" | sort -n | uniq -c | sort -n

Sono alcuni dei miei "one liner" preferiti :)

5
f4nt

Qui il mio esempio 'sed', legge il formato predefinito dei log di Apache e lo converte in qualcosa di più conveniente per l'elaborazione automatica. L'intera riga è definita come espressione regolare, le variabili vengono salvate e scritte nell'output con '#' come separatore.

La notazione semplificata dell'input è:% s% s% s [% s] "% s"% s% s "% s" "% s"

Riga di input di esempio: xx.xx.xx.xx - - [29/Mar/2011: 12: 33: 02 +0200] "GET /index.html HTTP/1.0" 200 9443 "-" "Mozilla/4.0"

Riga di output di esempio: xx.xx.xx.xx # - # - # 29/Mar/2011: 12: 33: 02 + 0200 # GET /index.html HTTP/1.0 # 200 # 9443 # - # Mozilla/4.0

cat access.log | \ 
  sed 's/^\(.*\) \(.*\) \(.*\) \[\(.*\)\] \"\(.*\)\" \(.*\) \(.*\) \"\(.*\)\" \"\(.*\)\"$/\1#\2#\3#\4#\5#\6#\7#\8#\9/g'

Senti il ​​potere delle espressioni regolari :-)

3
Kris

Costruire un elenco di domande comuni sarebbe un ottimo indice per queste risposte a questa domanda. Le mie domande comuni sono:

  • perché è cambiato l'itrato?
  • perché il tempo di risposta complessivo aumenta? '.

Ho notato tali cambiamenti monitorando le pagine di stato del server (tramite mod_status) per i tempi di risposta approssimativi e approssimativi per le richieste attive e completate di recente (sapendo benissimo che mi manca un enorme mucchio di dati, ma i campioni sono abbastanza buoni).

Uso la seguente direttiva LogFormat (% T è davvero utile)

LogFormat "%h %l %u %t \"%r\" %>s %b 
    \"%{Referer}i\" \"%{User-Agent}i\" %T" custom

Sto cercando causa-effetto e cosa è successo prima ... di solito su specifici sottoinsiemi di schemi nei miei registri, quindi ho bisogno di sapere quanto segue per ogni dato schema/espressione regolare:

  • hitcount per intervallo (minuti o ore) per un dato pattern (indirizzo ip o stringa cgi o parametri, ecc.)
  • istogrammi del tempo di risposta approssimativo (usando il parametro% T)

In genere uso Perl, perché alla fine diventa abbastanza complesso da valerne la pena.


Un esempio non Perl potrebbe essere un nitrato veloce al minuto per codici di stato non 200:

tail -9000 access_log | grep -v '" 200 ' | cut -d: -f2,3 | uniq -c

Sì, sto tradendo con quel grep, presumendo che una quota-spazio-200-spazio corrisponda solo ai codici di stato http .... potrei usare awk o Perl per isolare il campo solo tenendo presente che potrebbe essere impreciso.


Un esempio più complesso in Perl potrebbe essere quello di visualizzare un cambiamento di nitrato per un modello.

C'è molto da masticare nella sceneggiatura qui sotto, specialmente se non hai familiarità con Perl.

  • legge lo stdin in modo da poter usare porzioni dei tuoi registri, usare tail (specialmente con tail -f), con o senza greps e altri filtri ...
  • tradisce l'estrazione di timestamp di Epoca con l'hack di una regex e l'uso di Date :: Manip
  • è possibile modificarlo solo leggermente per estrarre i tempi di risposta o altri dati arbitrari

il codice segue:

#!/usr/bin/Perl
# script to show changes in hitrates for any regex pattern
# results displayed with arbitrary intervals
# and ascii indication of frequency
# gaps are also displayed properly
use Date::Manip;
use POSIX qw(strftime);
$pattern=shift || ".";
$ival=shift || 60;
$tick=shift || 10;
$minb=undef;
while (<>){
    next unless /$pattern/;
    $stamp="$1 $2" if m[(../.../....):(..:..:..)];
    $Epoch = UnixDate(ParseDate($stamp),"%s");
    $bucket= int($Epoch/$ival)*$ival;
    $minb=$bucket if $bucket<$minb || !defined($minb);
    $maxb=$bucket if $bucket>$maxb;
    $count{$bucket}++;
}
# loop thru the min/max range to expose any gaps
for($t=$minb;$t<=$maxb;$t+=$ival){
    printf "%s %s %4d %s\n",
            $t,
            strftime("%m/%d/%Y %H:%M:%S",localtime($t)),
            $count{$t}+0,
            substr("x"x100,0,$count{$t}/$tick
    );
}

Se vuoi solo elaborare metriche standard, verifica

  • 'mergelog' per riunire tutti i log (se si hanno più apache dietro un bilanciamento del carico) e
  • webalizer (o awstats o altro analizzatore comune).
3
ericslaw

Chi collega a caldo le tue immagini:

awk -F\" '($2 ~ /\.(jpg|gif)/ && $4 !~ /^http:\/\/www\.mydomain\.com/){print $4}' access_log | sort | uniq -c | sort
2
rkthkr

Uso moltissimo awk eseguendo la coda o il cat. Ogni sera mi consegno un report web per ciascun server. A seconda del file di registro e di LogFormat, dovrai modificare alcune delle fodere che funzionano per te.

Ecco un semplice esempio:

Se voglio codificare i log sul mio server per soli codici di stato 404/500, farei questo:

# $6 is the status code in my log file

tail -f ${Apache_LOG} |  awk  '$8 ~ /(404|500)/ {print $6}'

<snip>

echo ""
#echo  "Hits by source IP:"
echo "======================================================================"

awk '{print $2}' "$1" | grep -ivE "(127.0.0.1|192.168.100.)" | sort | uniq -c | sort -rn | head -25

echo ""
echo ""
#echo "The 25 most popular pages:"
echo "======================================================================"

awk '{print $6}' "$1" | grep -ivE '(mod_status|favico|crossdomain|alive.txt)' | grep -ivE '(.gif|.jpg|.png)' | \
 sed 's/\/$//g' | sort | \
 uniq -c | sort -rn | head -25

echo ""    
echo ""
echo "The 25 most popular pages (no js or css):"
echo "======================================================================"

awk '{print $6}' "$1" | grep -ivE '(mod_status|favico|crossdomain|alive.txt)' | grep -ivE '(.gif|.jpg|.png|.js|.css)' | \
 sed 's/\/$//g' | sort | \
   uniq -c | sort -rn | head -25

   echo ""


#echo "The 25 most common referrer URLs:"
echo "======================================================================"

awk '{print $11}' "$1" | \
 grep -vE "(^"-"$|/www.$Host|/$Host)" | \
 sort | uniq -c | sort -rn | head -25

echo ""

#echo "Longest running requests"
echo "======================================================================"

awk  '{print $10,$6}' "$1" | grep -ivE '(.gif|.jpg|.png|.css|.js)'  | awk '{secs=0.000001*$1;req=$2;printf("%.2f minutes req time for %s\n", secs / 60,req )}' | sort -rn | head -50

exit 0

</ snip>

2

Pur non essendo sed o awk, ci sono due cose che ho trovato utili per gestire i file di log di Apache e icecast.

AWStats ha uno script molto utile chiamato logresolvemerge.pl che combina più file di registro compressi o non compressi, rimuove i duplicati e ordina per timestamp. Può anche eseguire ricerche DNS ed essere configurato per l'esecuzione multithread. È particolarmente utile quando si usa con awstats perché awstats non può aggiungere linee di registro con timestamp più vecchi del database corrente, quindi tutti devono essere aggiunti in ordine, ma è molto facile come semplicemente lanciare tutto su logresolvemerge.pl e tutto si apre bene.

sed e awk sono piuttosto cattivi nel gestire le date perché generalmente le trattano come stringhe. awk ha alcune funzioni di data e ora, ma non sono molte. Ad esempio, estrarre un intervallo di righe tra due timestamp è difficile se tali timestamp esatti non si verificano nel file (anche se i valori tra di loro lo fanno) - L'esempio di Chris ha esattamente questo problema. Per far ciò, ho scritto a PHP script che riporta gli intervalli di data e ora del file di registro e può anche estrarre un blocco per intervallo di data e ora, usando qualsiasi formato di data o ora che ti piace (non è necessario che corrisponda al formato data/ora del file di registro).

Per mantenere questo argomento, ecco un paio di utili awkismi: Ottieni il numero totale di byte serviti da Apache o dal registro icecast:

cat access.log | awk '{ sum += $10 } END { print sum }'

Ottieni il numero totale di secondi collegati da un registro icecast:

cat access.log | awk '{ sum += $13 } END { print sum }'
1
Synchro

La cosa che tendo a fare la maggior parte del tempo è leggere sezioni di un registro in base al tempo, quindi ho scritto il seguente script usando sed per estrarre il periodo che mi interessa, funziona su ogni file di registro che sono venuto attraverso e in grado di gestire anche i registri archiviati.

 #!/bin/bash 
 # Questo script dovrebbe restituire un set di righe tra 2 valori, lo scopo principale è di cercare un file di registro tra 2 volte 
 # Uso dello script: logship.sh "start" "stop" file 
 
 # Se il file contiene "/" nell'intervallo di date, le seguenti 2 righe aggiungono il carattere di escape in modo che la ricerca possa essere eseguita per quelle caratteri 
 inizio = $ (echo "$ 1" | sed 's/\ // \\\ // g') 
 stop = $ (echo "$ 2" | sed 's/\//\\\//g')[.____.[[.____.[zipped=$(echo "$ 3" | grep -c "gz $") #figura se il file è compresso o meno 
 
 se ["$ zipped" == "1"]; quindi #Se il file è compresso, passalo attraverso zcat prima di sed 
 zcat $ 3 | sed -n "/$start/,/$stop/p";[.____.[else
 sed -n"/$ start /,/$ stop/p "$ 3; #se non è zippato, basta eseguire sed 
 fi 
1
Chris

Recuperando questo vecchio thread, dopo aver rinunciato ad asql per i file di log di grandi dimensioni, ho cercato una soluzione againg, anche in serverfault, ho scoperto di wtop qui è uno strumento open source, in grado di eseguire il monitoraggio live o registri di processo e statistiche (in alto N), molto flessibile e potente, il posto ufficiale è qui

0
aseques