Hai qualche utile script awk e grep per l'analisi dei log di Apache? [chiuso]


70

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?


1
In realtà ho avuto questo bellissimo regex che avevo scritto a mano per analizzare tutti i miei log personalizzati di Apache in singoli campi per l'invio a un database. Mi sto prendendo a calci per non averlo più. Era una fodera; ti ho restituito una variabile per ogni elemento del registro - quindi stavo inserendo in MySQL. Se lo trovo lo posterò qui.
Kyle Hodgson,

Risposte:


54

Puoi fare praticamente qualsiasi cosa con i file di log di apache con solo 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 mostrerebbe a quali pagine è stato effettuato l'accesso 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

Puoi anche reindirizzare l'output attraverso l'ordinamento per ottenere i risultati in ordine, sia come parte del comando shell, sia anche nello stesso script awk:

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.


Sì, sembra sempre strano vedere pipeline lunghe pazze di gatti / grep / awk. Una volta che sei in imbarazzo, di solito è abbastanza. Le prime tre clausole del post originale potrebbero essere banalmente scritte come "awk" / request_to_file_foo / {print $ 1} 'foo.log ". awk può prendere un file come input e usare regex per sapere a quali linee preoccuparsi.
Zac Thompson,

Elegante e semplice Buono.
Olivier Dulac,

Attenzione, gli spazi sembrano consentiti nel campo "authuser" (3 °), che rompe tutto, e personalmente penso che dovrebbe essere proibito, per permetterci di farlo ;-)
Mandark,

23

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. Mi sono interessato a quanto tempo ogni richiesta prende a servire, quindi dovremo aggiungere che in. Per un progetto, vogliamo anche sapere (il nostro bilanciamento del carico) se tutti i server stanno servendo le richieste più lento rispetto ad altri, in modo da registrare 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

Ciò 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 farlo:

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 su awk e grep quando il taglio farà esattamente quello che voglio ordini di grandezza più velocemente?


2
I tuoi esempi per il nuovo formato sono in realtà ancora troppo complicati - i conteggi IP diventano cut -f 3 log | uniq -c | sort -n, agenti utente cut -f 8 log | uniq -c | sort -n.
Creshal,

Hai ragione, è più semplice. Ho aggiornato gli esempi per riflettere ciò.
Dan Udey,

"cat file | grep string" è inutile, perché non "grep string file"?
c4f4t0r,

2
Non ho scuse e ho aggiornato l'esempio di conseguenza.
Dan Udey,

15

Dimentica awk e grep. Dai un'occhiata ad 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;

Interessante, ma potresti incorrere in problemi se i tuoi registri sono particolarmente grandi, penso. Inoltre, è in grado di gestire formati di registro personalizzati?
Vagnerr,

Ci sto provando al momento, il tempo di caricamento è così lento (almeno nella versione 0.9). Il caricamento di un registro da
200 Mb

Devo dire che dopo il tempo di caricamento (ci sono voluti circa 15 minuti) la sintassi di questo programma è ottima, puoi ordinare, contare e raggruppare. Veramente bello.
aseques,

Apache HTTPD ha un metodo con cui è possibile inviare efficacemente i log a un database. Sì, le scritture potrebbero richiedere molto tempo, ma un proxy con thread potrebbe fare la cosa giusta inserita nel mezzo. Ad ogni modo, questo renderà molto più veloce l'interrogazione dei log in un SQL come la sintassi. Nessun caricamento coinvolto troppo - il server di database è sempre "ON".
vicino

6

Ecco uno script per trovare i migliori URL, i migliori referrer e i migliori useragent delle 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

fonte


4

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 :)


3

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 diversi da 200:

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

Sì, sto tradendo con quel grep, presumendo che una citazione-spazio-200-spazio corrisponda solo ai codici di stato http .... potrebbe usare awk o perl per isolare il campo, basta ricordare 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 nello script qui sotto, specialmente se non si ha familiarità con il perl.

  • legge lo stdin in modo da poter usare parti dei tuoi registri, usare tail (specialmente con tail -f), con o senza greps e altri filtri ...
  • truffa l'estrazione di data e ora 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 tuoi log (se hai più apache dietro un bilanciamento del carico) e
  • webalizer (o awstats o altro analizzatore comune).

3

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 :-)


Ciò ha reso l'elaborazione con AWK un gioco da ragazzi. Stavo cercando un modo rapido per impostare un deliminatore comune e questo l'ha inchiodato.
Citricguy,

Ho sentito il potere regex e volevo solo passare il mio tweak, che taglia "HTML / 1.1" e separa il protocollo (in un modo probabilmente non conforme agli standard) nel suo campo. Goditi: `` `cat access.log | sed 's /^(.*) (. *) (. *) [(. *)] \ "([[:: alpha:]] \ +) (. *) HTTP \ / 1 \ .1 \" ( . *) (. *) \ "(. *) \" \ "(. *) \" $ / \ 1 # \ 2 # \ 3 # \ 4 # \ 5 # \ 6 # \ 7 # \ 8 # \ 9 # \ 10 / g '' ``
Josh Rumbut,

2

Uso moltissimo awk eseguendo la coda o il cat. Ogni notte mi consegno un report Web per ciascun server. A seconda del file di registro e del LogFormat, dovrai modificare alcuni dei rivestimenti per farti funzionare.

Ecco un semplice esempio:

Se voglio codificare i log sul mio server solo per 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

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

1

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 è cercare un file di registro tra 2 volte
#Script use: 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 quei caratteri
start = $ (echo "$ 1" | sed 's / \ // \\\ // g')
stop = $ (echo "$ 2" | sed 's / \ // \\\ // g')

zipped = $ (echo "$ 3" | grep -c "gz $") #figura se il file è zippato o no

if ["$ zipped" == "1"]; quindi #Se il file è compresso, passalo attraverso zcat prima di sed
        zcat $ 3 | sed -n "/ $ start /, / $ stop / p";
altro
        sed -n "/ $ start /, / $ stop / p" $ 3; #se non è zippato, basta eseguire sed
fi

1

Sebbene non sia 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, strip dups e ordina per timestamp. Può anche eseguire ricerche DNS ed essere configurato per l'esecuzione multithread. È particolarmente utile quando si utilizza con awstats perché awstats non è in grado di aggiungere linee di registro con timestamp più vecchi del database corrente, quindi tutti devono essere aggiunti in ordine, ma è molto semplice in quanto si cancella 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 questi timestamp esatti non si verificano nel file (anche se i valori tra loro lo fanno) - L'esempio di Chris ha esattamente questo problema. Per fare ciò, ho scritto uno script PHP che riporta gli intervalli di data e ora del file di registro e può anche estrarre un blocco per intervallo di data e ora, utilizzando qualsiasi formato di data o ora desiderato (non è necessario che corrisponda al formato di data e 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 per un semplice log apache con somma byte con awk
rymo

0

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 opensource, che è in grado di fare monitoraggio dal vivo o registri di processo e ottenere statistiche (top N), molto flessibile e potente, il posto ufficiale è qui

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.