Czy masz jakieś przydatne skrypty awk i grep do analizowania dzienników Apache? [Zamknięte]

69

Mogę używać analizatorów dzienników, ale często muszę analizować ostatnie dzienniki sieciowe, aby zobaczyć, co się w tej chwili dzieje.

Czasami robię takie rzeczy, jak znaleźć 10 najlepszych IPS, które żądają określonego pliku

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

Co masz w swoim zestawie narzędzi?

martwy programista
źródło
1
Tak naprawdę miałem ten duży, regex, który napisałem ręcznie, aby parsować wszystkie moje niestandardowe dzienniki Apache do poszczególnych pól w celu przesłania do bazy danych. Kopię się, że już go nie mam. To był jeden liniowiec; oddałem ci jedną zmienną dla każdego elementu dziennika - wtedy wstawiałem do MySQL. Jeśli go znajdę, opublikuję go tutaj.
Kyle Hodgson,

Odpowiedzi:

54

Możesz zrobić prawie wszystko z plikami dziennika apache z samym awk. Pliki dziennika Apache są zasadniczo oddzielone białymi spacjami i możesz udawać, że cytaty nie istnieją, i uzyskać dostęp do wszelkich informacji, którymi jesteś zainteresowany, według numeru kolumny. Jedyny czas, kiedy to się psuje, to jeśli masz połączony format dziennika i interesujesz się klientami użytkownika, w którym to momencie musisz użyć cudzysłowu (") jako separatora i uruchomić osobną komendę awk. Poniższe informacje pokazują adresy IP każdy użytkownik, który żąda strony indeksu posortowanej według liczby trafień:

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

Żądany adres URL to 7 USD. Możesz dodać dowolne warunki na początku. Zamień „7 $ ==” / ”na dowolne potrzebne informacje.

Jeśli zastąpisz 1 $ w (ipcount [$ 1] ++), możesz pogrupować wyniki według innych kryteriów. Użycie 7 USD pokaże, które strony były odwiedzane i jak często. Oczywiście wtedy chciałbyś zmienić stan na początku. Poniżej pokazano, które strony były dostępne dla użytkownika z określonego adresu IP:

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

Możesz również potokować dane wyjściowe przez sortowanie, aby uporządkować wyniki, albo jako część polecenia powłoki, albo również w samym skrypcie awk:

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

To drugie byłoby przydatne, jeśli zdecydujesz się rozwinąć skrypt awk, aby wydrukować inne informacje. Wszystko zależy od tego, czego chcesz się dowiedzieć. Powinny one służyć jako punkt wyjścia do wszystkiego, co Cię interesuje.

znak
źródło
Tak, zawsze wydaje się dziwne widzieć szalone długie rurociągi cat / grep / awk. Kiedy już jesteś w awk, to zwykle wystarcza. Pierwsze trzy klauzule oryginalnego postu można trywialnie zapisać jako „awk” / request_to_file_foo / {print $ 1} „foo.log”. awk może pobierać plik jako dane wejściowe i może używać wyrażenia regularnego, aby wiedzieć, które linie mają być ważne.
Zac Thompson
Elegancki i prosty. Dobry.
Olivier Dulac,
Uwaga, w polu „authuser” (3.) wydaje się, że spacje są dozwolone, co wszystko psuje, a ja osobiście uważam, że powinno to być zabronione, aby nam to umożliwić ;-)
Mandark,
23

Jedną z rzeczy, których nigdy nie widziałem, z powodów, których nie wyobrażam sobie, jest zmiana formatu pliku dziennika Apache na łatwiejszą do analizy wersję z informacjami, które są dla Ciebie ważne.

Na przykład nigdy nie używamy podstawowego uwierzytelniania HTTP, więc nie musimy rejestrować tych pól. Ja jestem ciekaw, jak długo każde żądanie zajmuje służyć, więc dodamy, że w. W jednym projekcie, chcemy też wiedzieć (na naszej równoważenia obciążenia) czy jakieś serwery służą żądania wolniej niż inni, więc zalogować nazwę serwera, do którego jesteśmy z powrotem proxy.

Oto fragment konfiguracji apache jednego serwera:

# 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

Nie można tak naprawdę powiedzieć, że między każdym polem znajduje się dosłowny znak tabulacji (\ t). Oznacza to, że jeśli chcę przeprowadzić analizę w Pythonie, być może pokażę na przykład statusy inne niż 200, mogę to zrobić:

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

Lub gdybym chciał zrobić „kto łączy obrazy na gorąco”? to byłby

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

W przypadku adresów IP w dzienniku dostępu poprzedni przykład:

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

staje się mniej więcej tak:

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

Łatwiejszy do odczytania i zrozumienia oraz znacznie mniej kosztowny obliczeniowo (bez wyrażenia regularnego), co w przypadku dzienników o pojemności 9 GB ma ogromną różnicę w zakresie czasu. Kiedy robi się to NAPRAWDĘ fajnie, jeśli chcesz zrobić to samo dla agentów użytkownika. Jeśli twoje dzienniki są rozdzielane spacjami, musisz ręcznie dopasować wyrażenie regularne lub wyszukać ciąg znaków. W tym formacie jest to proste:

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

Dokładnie tak samo jak powyżej. W rzeczywistości każde podsumowanie, które chcesz zrobić, jest dokładnie takie samo.

Dlaczego, u licha, miałbym spędzać procesor mojego systemu na awk i grep, kiedy cięcie zrobi dokładnie to, czego chcę rzędów wielkości szybciej?

Dan Udey
źródło
2
Twoje przykłady nowego formatu są w rzeczywistości nadal nadmiernie skomplikowane - liczby IP stają się cut -f 3 log | uniq -c | sort -nagentami użytkowników cut -f 8 log | uniq -c | sort -n.
Creshal,
Masz rację, to jest prostsze. Zaktualizowałem przykłady, aby to odzwierciedlić.
Dan Udey
„cat file | grep string” jest bezużyteczny, dlaczego nie „grep string file”?
c4f4t0r
2
Nie mam wymówki i odpowiednio zaktualizowałem przykład.
Dan Udey
15

Zapomnij o awk i grep. Sprawdź asql . Po co pisać nieczytelne skrypty, skoro można użyć składni podobnej do języka SQL do przeszukiwania pliku dziennika. Na przykład.

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;
Vihang D.
źródło
Ciekawe, ale może się zdarzyć, że kłody będą szczególnie duże. Jak dobrze radzi sobie z niestandardowymi formatami dzienników?
Vagnerr
Próbuję w tej chwili, czas ładowania jest tak wolny (przynajmniej w wersji 0.9). Ładowanie dziennika
200 Mb
Muszę powiedzieć, że po czasie ładowania (zajęło to około 15 minut) synstax tego programu jest świetny, możesz sortować, liczyć i grupować według. Bardzo miły.
następuje
Apache HTTPD ma metodę, dzięki której można skutecznie wysyłać dzienniki do bazy danych. Tak, zapisy mogą trwać długo, ale wątkowy serwer proxy może zrobić właściwą rzecz umieszczoną pośrodku. W każdym razie sprawi to, że dzienniki zapytań w składni typu SQL będą znacznie szybsze. Nie wymaga też ładowania - serwer bazy danych jest stale „WŁĄCZONY”.
nearora
6

Oto skrypt do wyszukiwania najlepszych adresów URL, najlepszych stron odsyłających i najlepszych użytkowników z ostatnich N wpisów w dzienniku

#!/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

Źródło

anoopjohn
źródło
4

dla adresów IP w dzienniku dostępu:

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

To trochę brzydkie, ale działa. Korzystam także z netstat (aby zobaczyć aktywne połączenia):

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

To jedne z moich ulubionych „one linerów” :)

f4nt
źródło
3

Stworzenie listy typowych pytań byłoby świetnym indeksem dla tych odpowiedzi na to pytanie. Moje typowe pytania to:

  • dlaczego zmienił się hitrate?
  • dlaczego rośnie ogólny czas reakcji? ”.

Zauważyłem takie zmiany, monitorując strony statusu serwera (przez mod_status) pod kątem szybkości transmisji i przybliżonego czasu odpowiedzi dla aktywnych i ostatnio zakończonych żądań (wiedząc, że brakuje mi ogromnej ilości danych, ale próbki są wystarczająco dobre).

Korzystam z następującej dyrektywy LogFormat (% T jest naprawdę przydatny)

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

Szukam przyczyny i tego, co stało się najpierw ... zwykle na temat określonych podzbiorów wzorców w moich logach, więc muszę znać następujące informacje dla każdego wzorca / wyrażenia regularnego:

  • liczba trafień na interwał (minuta lub godzina) dla danego wzorca (adres IP lub ciąg znaków cgi lub parametry itp.)
  • histogramy przybliżonego czasu odpowiedzi (przy użyciu parametru% T)

Zwykle używam perla, ponieważ w końcu staje się wystarczająco skomplikowany, aby był opłacalny.


Przykładem innym niż Perl byłby szybki przepływ danych na minutę dla kodów stanu innych niż 200:

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

Tak, oszukuję tym grepem, zakładając, że cytat-spacja-200-spacja pasuje tylko do kodów stanu HTTP .... może użyć awk lub perl do izolacji pola, pamiętaj, że może to być niedokładne.


Bardziej złożonym przykładem w Perlu może być wizualizacja zmiany szybkości transmisji dla wzorca.

W skrypcie poniżej jest wiele do żucia, zwłaszcza jeśli nie znasz perla.

  • odczytuje stdin, abyś mógł korzystać z części dzienników, używać ogona (szczególnie z tail-f), z greps lub bez greps i innych filtrów ...
  • oszukuje ekstrakcję sygnatury czasowej epoki z hackiem wyrażenia regularnego i użyciem Date :: Manip
  • możesz go tylko nieznacznie zmodyfikować, aby wydobyć czas odpowiedzi lub inne dowolne dane

następujący kod:

#!/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
    );
}

Jeśli chcesz po prostu przetworzyć standardowe dane, sprawdź

  • „scalelog”, aby zebrać wszystkie logi razem (jeśli masz wiele podejść za modułem równoważenia obciążenia) i
  • webalizer (lub awstats lub inny popularny analizator).
ericslaw
źródło
3

Tutaj mój przykład „sed” odczytuje domyślny format dzienników Apache i konwertuje go na coś wygodniejszego do automatycznego przetwarzania. Cała linia jest zdefiniowana jako wyrażenie regularne, zmienne są zapisywane i zapisywane na wyjściu z „#” jako separatorem.

Uproszczony zapis danych wejściowych jest następujący:% s% s% s [% s] "% s"% s% s "% s" "% s"

Przykładowy wiersz wejściowy: xx.xx.xx.xx - - [29 / Mar / 2011: 12: 33: 02 +0200] „GET /index.html HTTP / 1.0” 200 9443 ”-„ „Mozilla / 4.0”

Przykładowa linia wyjściowa: 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'

Poczuj moc wyrażeń regularnych :-)

Kris
źródło
Dzięki temu przetwarzanie z AWK było dziecinnie proste. Szukał szybkiego sposobu skonfigurowania wspólnego ogranicznika i to go przybiło.
Citricguy
Czułem moc wyrażeń regularnych i chciałem tylko przekazać własne poprawki, które wycinają „HTML / 1.1” i dzielą protokół (prawdopodobnie w sposób niezgodny ze standardami) na swoje własne pole. Ciesz się: `` cat access.log | sed 's /^(.*) (. *) (. *) [(. *)] \ "([[: alpha:]] \ +) (. *) HTTP \ / 1 \ .1 \" ( . *) (. *) \ "(. *) \" \ "(. *) \" $ / \ 1 # \ 2 # \ 3 # \ 4 # \ 5 # \ 6 # \ 7 # \ 8 # \ 9 # \ 10 / g ''
Josh Rumbut
2

Często używam awk, modyfikując lub cat'ując plik. Każdej nocy dostarczam sobie raport internetowy dla każdego serwera. W zależności od pliku dziennika i LogFormat będziesz musiał edytować niektóre linijki, które będą dla Ciebie działać.

Oto prosty przykład:

Jeśli chcę dostosować dzienniki na moim serwerze tylko dla kodów stanu 404/500, zrobiłbym to:

# $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>

Michael Steinfeld
źródło
2

Kto łączy Twoje zdjęcia na gorąco:

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

Rzeczą, którą zwykle robię, jest czytanie sekcji dziennika na podstawie czasu, więc napisałem następujący skrypt za pomocą sed, aby wyciągnąć okres, który mnie interesuje, działa na każdym pliku dziennika, do którego przyszedłem w poprzek i może również obsługiwać zarchiwizowane dzienniki.

#! / bin / bash
# Ten skrypt powinien zwrócić zestaw linii między 2 wartościami, głównym celem jest przeszukanie pliku dziennika między 2 razy
# Użycie skryptu: plik „start” „stop” logship.sh

#Jeśli plik zawiera dowolne „/” w zakresie dat, następujące 2 wiersze dodają znak zmiany znaczenia, aby można było wyszukać te znaki
start = $ (echo "$ 1" | sed 's / \ // \\\ // g')
stop = $ (echo "$ 2" | sed 's / \ // \\\ // g')

zipped = $ (echo "$ 3" | grep -c "gz $") # konfiguruje, czy plik jest skompresowany, czy nie

if [„$ zipped” == „1”]; następnie #Jeśli plik jest skompresowany, prześlij go przez Zcat przed sedem
        zcat 3 USD | sed -n "/ $ start /, / $ stop / p";
jeszcze
        sed -n "/ $ start /, / $ stop / p" 3 $; # jeśli nie jest spakowany, po prostu uruchom sed
fi
Chris
źródło
1

Chociaż nie jestem sedem ani awk, są dwie rzeczy, które uważam za przydatne do obsługi plików dziennika apache i icecast.

AWStats ma bardzo przydatny skrypt o nazwie logresolvemerge.pl, który będzie łączyć wiele skompresowanych lub nieskompresowanych plików dziennika, usuwać duplikaty i sortować według datownika. Może także wyszukiwać DNS i być skonfigurowany do uruchamiania wielowątkowego. Jest to szczególnie przydatne podczas korzystania z awstats, ponieważ awstats nie może dodawać wierszy dziennika ze znacznikami czasu starszymi niż bieżąca baza danych, więc wszystkie muszą być dodawane w kolejności, ale to bardzo proste, ponieważ po prostu cucasz wszystko na logresolvemerge.pl i wszystko ładnie wyskakuje.

sed i awk są dość kiepskie w obchodzeniu się z datami, ponieważ ogólnie traktują je jak łańcuchy. awk ma pewne funkcje związane z czasem i datą, ale nie są zbyt duże. Na przykład wyodrębnienie zakresu linii między dwoma znacznikami czasu jest trudne, jeśli te dokładne znaczniki czasu nie występują w pliku (nawet jeśli wartości między nimi) - przykład Chrisa ma właśnie ten problem. Aby temu zaradzić, napisałem skrypt PHP, który zgłasza zakresy znaczników czasu pliku dziennika, a także może wyodrębnić porcję według zakresu znaczników czasu, używając dowolnego formatu daty lub godziny (nie musi być zgodny z formatem znacznika czasu pliku dziennika).

Aby zachować ten temat, oto kilka przydatnych awkism: Pobierz całkowitą liczbę bajtów obsługiwanych z dziennika apache lub icecast:

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

Uzyskaj całkowitą liczbę sekund połączonych z dziennika Icecast:

cat access.log | awk '{ sum += $13 } END { print sum }'
Synchro
źródło
+1 za prosty dziennik apache sumujący bajty z awk
rymo
0

Odzyskiwanie tego starego wątku, po rezygnacji z asql dla dużych plików dziennika, szukałem rozwiązania againg, również w przypadku awarii serwera, znalazłem tutaj o wtopie , jest to narzędzie typu open source, które może wykonywać monitorowanie na żywo lub dzienniki procesów i uzyskiwać statystyki (na górę N), bardzo elastyczny i potężny, oficjalne miejsce jest tutaj

konsekwencje
źródło