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