Odkrywając access_log Apache
Napisał: Patryk Krawaczyński
23/08/2009 w Hacks & Scripts Brak komentarzy. (artykuł nr 135, ilość słów: 1045)
P
rzedstawiony skrypt jest tłumaczeniem „Exploring the Apache access_log” z książki „Wicked Cool Shell Scripts” autorstwa Dave’a Taylor’a udostępnionym on-line (Skrypt #84) na stronie www.intuitive.com. Do tłumaczenia zostało dodane także parę informacji od tłumacza.
Jeśli używasz serwera Apache lub podobnego serwera WWW, który także wykorzystuje Common Log Format – istnieje trochę możliwości wykonania analiz statystycznych, przy pomocy skryptu powłoki. Standardowa konfiguracja dla serwera posiada zapisywane pliki access_log oraz error_log dla strony internetowej; nawet ISP (dostawcy Internetu) udostępniają surowe dane swoim klientom, lecz jeśli posiadasz własny serwer, powinieneś definitywnie posiadać i archiwizować te cenne informacje.
Typowy wiersz w pliku access_log wygląda tak:
63.203.109.38 - - [02/Sep/2003:09:51:09 -0700] "GET /custer HTTP/1.1" 301 248 "http://search.msn.com/results.asp?RS=CHECKED&FORM=MSNH& v=1&q=%22little+big+Horn%22" "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.0)"
Tabela 1 pokazuje wartości dla formatu common log.
| Kolumna | Wartość |
| 1 | IP hosta korzystającego z serwera |
| 2 – 3 | Informacje o zabezpieczonych połączeniach https/SSL |
| 4 | Przesunięcie daty i strefy czasowej dla konkretnego żądania |
| 5 | Wywołana metoda |
| 6 | Zażądany URL |
| 7 | Użyty protokół |
| 8 | Kod wynikowy |
| 9 | Liczba przetransferowanych bajtów |
| 10 | Adres strony odsyłającej |
| 11 | Łańcuch znaków do identyfikacji przeglądarki |
Wynik kodu (pole 8) 301 wskazuje powodzenie. Adres strony odsyłającej (pole 10) wskazuje adres URL strony odwiedzonej przez internautę zaraz przez zażądaniem strony z tego serwisu: Widać, że użytkownik był na wyszukiwarce search.msn.com (MSN) i szukał wyrażenia „little big Horn”. W wynikach tego wyszukiwania znajdował się link do adresu URL /custer na tym serwerze.
Liczbę odwiedzin strony można szybko ustalić poprzez policzenie ilości słów w pliku dziennika oraz zakres dat poprzez porównanie dziesięciu pierwszych i ostatnich wierszy:
$ wc -l access_log 10991 access_log $ head -1 access_log ; tail -1 access_log 64.12.96.106 - - [13/Sep/2003:18:02:54 -0600] ... 216.93.167.154 - - [15/Sep/2003:16:30:29 -0600] ...
Z tymi wytycznymi w umyśle, oto skrypt, który generuje wiele użytecznych statystyk, zakładając, że plik access_log jest w formacie Apache.
Skrypt:
#!/bin/sh
# webaccess - analizuje plik Apache-format access_log , wyciągając
# przydatne dane statystyczne
bytes_in_gb=1048576
scriptbc="$HOME/bin/scriptbc"
nicenumber="$HOME/bin/nicenumber"
host="intuitive.com"
if [ $# -eq 0 -o ! -f "$1" ] ; then
echo "Usage: $(basename $0) logfile" >&2
exit 1
fi
firstdate="$(head -1 "$1" | awk '{print $4}' | sed 's/\[//')"
lastdate="$(tail -1 "$1" | awk '{print $4}' | sed 's/\[//')"
echo "Results of analyzing log file $1"
echo ""
echo " Start date: $(echo $firstdate|sed 's/:/ at /')"
echo " End date: $(echo $lastdate|sed 's/:/ at /')"
hits="$(wc -l < "$1" | sed 's/[^[:digit:]]//g')"
echo " Hits: $($nicenumber $hits) (total accesses)"
pages="$(grep -ivE '(.txt|.gif|.jpg|.png)' "$1" |wc -l|sed 's/[^[:digit:]]//g')"
echo " Pageviews: $($nicenumber $pages) (hits minus graphics)"
totalbytes="$(awk '{sum+=$10} END {print sum}' "$1")"
echo -n " Transferred: $($nicenumber $totalbytes) bytes "
if [ $totalbytes -gt $bytes_in_gb ] ; then
echo "($($scriptbc $totalbytes / $bytes_in_gb) GB)"
elif [ $totalbytes -gt 1024 ] ; then
echo "($($scriptbc $totalbytes / 1024) MB)"
else
echo ""
fi
# now let's scrape the log file for some useful data:
echo ""
echo "The ten most popular pages were:"
awk '{print $7}' "$1" | grep -ivE '(.gif|.jpg|.png)' | \
sed 's/\/$//g' | sort | \
uniq -c | sort -rn | head -10
echo ""
echo "The ten most common referrer URLs were:"
awk '{print $11}' "$1" | \
grep -vE "(^"-"$|/www.$host|/$host)" | \
sort | uniq -c | sort -rn | head -10
echo ""
exit 0
Jak to działa:
Skrypt ten tylko pozornie wygląda na skomplikowany. Łatwiej jest go ujrzeć patrząc na każdy blok jako odrębny, mały skrypt. Dla przykładu, pierwszych kilka wierszy wyodrębnia pierwszą i ostatnią datę poprzez proste wyciągnięcie czwartego pola z pierwszego i ostatniego wiersza pliku. Liczba trafień obliczana jest przez policzenie liczby wierszy (przy pomocy polecenia wc), a liczbę wyświetleń strony stanowi po prostu liczba trafień minus różnego rodzaju żądania obrazków czy surowych plików tekstowych (to jest plików z rozszerzeniami .gif, .jpg, .png lub .txt). Całkowita liczba przetransferowanych bajtów jest liczona poprzez podsumowanie wartości dziesiątego pola ze wszystkich wierszy, a następnie wywoływana jest funkcja nicenumber, aby przedstawić wynik w czytelny sposób. Najbardziej popularne strony, można wyłapać poprzez wyodrębnienie zażądanych stron z pliku dziennika; zasłaniając wpisy dotyczące plików graficznych; sortując za pomocą polecenia: uniq -c, aby obliczyć liczbę wystąpień każdego unikatowego wiersza; oraz ostatecznie, ponownie sortując w celu upewnienia się, że najczęściej pojawiające się wiersze były na samej górze. Kod wygląda tak:
awk '{print $7}' "$1" | grep -ivE '(.gif|.jpg|.png)' | \
sed 's/\/$//g' | sort | \
uniq -c | sort -rn | head -10
Warto zauważyć, że stosujemy w małym stopniu normalizację. Wywołanie sed wyodrębnia wszystkie slashe (/), tak aby /podkatalog/ i /podkatalog zostały policzone jako to samo żądanie.
Podobnie do sekcji wyławiającej najbardziej popularne strony, następna sekcja wyciąga informacje o stronach odsyłających:
awk '{print $11}' "$1" | \
grep -vE "(^\"-\"$|/www.$host|/$host)" | \
sort | uniq -c | sort -rn | head -10
Fragment ten wyodrębnia pole 11 z pliku dziennika, zasłaniając obydwa wpisy dotyczące bieżącego hosta, jak i wpisy w postaci „-” (wartość ta jest przesyłana, gdy przeglądarka blokuje dane o stronie odsyłającej), a następnie wprowadzając rezultat do tej samej sekwencji sort|uniq -c|sort -rn|head -10, aby uzyskać dziesięć najpopularniejszych stron odsyłających.
Uruchamianie skryptu:
W celu wykonania tego skryptu jako argument wywołania należy podać nazwę pliku dziennika Apache (lub innego w formacie Common Log Format).
Rezultaty:
Rezultaty uruchomienia tego skryptu na typowym pliku dziennika dają wiele informacji:
$ webaccess /web/logs/intuitive/access_log Results of analyzing log file /web/logs/intuitive/access_log Start date: 13/Sep/2003 at 18:02:54 End date: 15/Sep/2003 at 16:39:21 Hits: 11,015 (total accesses) Pageviews: 4,217 (hits minus graphics) Transferred: 64,091,780 bytes (61.12 GB) The ten most popular pages were: 862 /blog/index.rdf 327 /robots.txt 266 /blog/index.xml 183 115 /custer 96 /blog/styles-site.css 93 /blog 68 /cgi-local/etymologic.cgi 66 /origins 60 /coolweb The ten most common referrer URLs were: 96 "http://booktalk.intuitive.com/" 18 "http://booktalk.intuitive.com/archives/cat_html.shtml" 13 "http://search.msn.com/results.asp?FORM=MSNH&v=1&q=little+big+horn" 12 "http://www.geocities.com/capecanaveral/7420/voc1.html" 10 "http://search.msn.com/spresults.aspx?q=plains&FORM=IE4" 9 "http://www.etymologic.com/index.cgi" 8 "http://www.allwords.com/12wlinks.php" 7 "http://www.sun.com/bigadmin/docs/" 7 "http://www.google.com/search?hl=en&ie=UTF-8&oe=UTF-8&q=cool+web+pages" 6 "http://www.google.com/search?oe=UTF-8&q=html+4+entities"
Modyfikowanie skryptu:
Problem jaki występuje podczas analizowania plików dziennika serwera WWW jakim jest Apache jest taki, że czasami dwa różne adresy URL tak naprawdę mogą oznaczać tę samą stronę. Dla przykładu: /custer/ oraz /custer/index.shtml są tymi samymi stronami, więc podczas obliczania dziesięciu najpopularniejszych stron należy wziąć to pod uwagę. Konwersja przy pomocy wywołania sed powoduje, że /custer i /custer/ nie są uważane za różne wywołania, lecz poznanie domyślnej nazwy pliku dla każdego katalogu może być trochę trudniejsze. Pożytek jaki płynie z analizy dziesięciu najpopularniejszych stron odsyłających może być jeszcze większy, kiedy obetniemy ich adresy URL do podstawowej nazwy domenowej (np. slashdot.org).
Więcej informacji: Shell Script, awk –usage, man head, sort, uniq

