Vraag Heb je bruikbare awk- en grep-scripts voor het parseren van apache-logs? [Gesloten]


Ik kan loganalysators gebruiken, maar vaak moet ik recente weblogs analyseren om te zien wat er op dit moment gebeurt.

Ik doe soms dingen als het vinden van de top 10 ips die een bepaald bestand vragen

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

Wat heb je in je gereedschapskist?


63
2018-05-21 22:14


oorsprong


Ik had eigenlijk deze grote mooie regex die ik met de hand had geschreven om al mijn apache-custom logs te ontleden naar individuele velden voor verzending in een database. Ik trap mezelf dat ik het niet meer heb. Het was een one liner; gaf je één variabele terug voor elk log-element - daarna voegde ik MySQL toe. Als ik het vind, zal ik het hier plaatsen. - Kyle Hodgson


antwoorden:


Je kunt vrijwel alles doen met apache-logbestanden met awk alleen. Apache-logbestanden zijn in principe witruimte gescheiden, en u kunt doen alsof de offertes niet bestaan ​​en u toegang hebben tot welke informatie u ook bent geïnteresseerd in kolomnummer. De enige keer dat dit uitvalt, is of u het gecombineerde log-formaat hebt en geïnteresseerd bent in user-agents, op welk punt u aanhalingstekens (") als scheidingsteken moet gebruiken en een aparte awk-opdracht uitvoert. Hierna ziet u de IP's van elke gebruiker die de indexpagina opvraagt, gesorteerd op het aantal hits:

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

$ 7 is de gevraagde URL. Je kunt aan het begin alle condities toevoegen die je wilt. Vervang '$ 7 == "/" door welke informatie je maar wilt.

Als u de $ 1 in (ipcount [$ 1] ++) vervangt, kunt u de resultaten groeperen op basis van andere criteria. Als u $ 7 gebruikt, wordt weergegeven welke pagina's zijn geopend en hoe vaak. Natuurlijk zou je de conditie aan het begin willen veranderen. Het volgende zou laten zien welke pagina's werden geopend door een gebruiker van een specifiek IP:

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

U kunt de uitvoer ook via sortering pipen om de resultaten op volgorde te krijgen, hetzij als onderdeel van de shell-opdracht, of ook in het awk-script zelf:

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

Dit laatste zou handig zijn als je zou besluiten het awk-script uit te breiden om andere informatie af te drukken. Het is allemaal een kwestie van wat je wilt weten. Deze zouden als vertrekpunt moeten dienen voor alles waarin je bent geïnteresseerd.


53
2018-06-03 02:21



Yah, het lijkt altijd vreemd om gekke lange kat / grep / awk-pijpleidingen te zien. Als je eenmaal in het ongewisse bent, is dat meestal voldoende. De eerste drie clausules van de oorspronkelijke post kunnen triviaal worden geschreven als "awk" / request_to_file_foo / {print $ 1} 'foo.log'. awk kan een bestand als invoer nemen en kan regex gebruiken om te weten welke regels belangrijk zijn. - Zac Thompson
Elegant en eenvoudig. Goed. - Olivier Dulac
Pas op, ruimtes lijken toegestaan ​​in het "authuser" (3e) veld, dat alles breekt, en ik persoonlijk denk dat het verboden moet zijn, om ons dit te laten doen ;-) - Mandark


Een ding dat ik nog nooit iemand anders heb zien doen, om redenen die ik me niet kan voorstellen, is om het Apache-logbestandsformaat te veranderen in een gemakkelijker te parseren versie met de informatie die er echt toe doet.

We gebruiken bijvoorbeeld nooit HTTP Basic Auth, dus we hoeven deze velden niet te loggen. ik ben geïnteresseerd in hoe lang elk verzoek in beslag neemt om te dienen, dus we voegen dat toe. Voor één project willen we ook weten (op onze load balancer) of servers meer langzamer verzoeken doen dan andere, dus we loggen de naam van de server waar we naar terugverhuizen.

Hier is een fragment uit de apache config van één 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

Wat je hier niet echt van kunt zien, is dat tussen elk veld een letterteken (\ t) staat. Dit betekent dat als ik wat analyses in Python wil doen, misschien bijvoorbeeld niet-200 statussen laat zien, ik dit kan doen:

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

Of als ik wilde doen 'wie is hotlinking-afbeeldingen?' het zou zijn

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

Voor IP-tellingen in een toegangslogboek, het vorige voorbeeld:

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

wordt zoiets als dit:

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

Gemakkelijker te lezen en te begrijpen, en veel minder rekenkundig duur (geen regex) die, op logbestanden van 9 GB, een enorm verschil maakt in hoe lang het duurt. Wanneer dit ECHT leuk wordt is als u hetzelfde wilt doen voor User-agents. Als uw logboeken door spaties gescheiden zijn, moet u een paar reguliere expressies overeen laten komen of handmatig zoeken op tekenreeksen. Met dit formaat is het eenvoudig:

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

Precies hetzelfde als het bovenstaande. In feite is elke samenvatting die u wilt doen in essentie precies hetzelfde.

Waarom zou ik in hemelsnaam de CPU van mijn systeem besteden aan awk en grep wanneer cut precies doet wat ik sneller wil?


23
2018-06-05 21:46



Uw voorbeelden voor de nieuwe indeling zijn eigenlijk nog steeds te ingewikkeld - IP-tellingen worden cut -f 3 log | uniq -c | sort -nuser agents cut -f 8 log | uniq -c | sort -n. - Creshal
Je hebt gelijk, dat is eenvoudiger. Ik heb de voorbeelden bijgewerkt om dat weer te geven. - Dan Udey
"cat file | grep string" is nutteloos, waarom niet "grep string file"? - c4f4t0r
Ik heb geen excuus en heb het voorbeeld dienovereenkomstig bijgewerkt. - Dan Udey


Vergeet awk en grep. Uitchecken asql. Waarom onleesbare scripts schrijven wanneer u sql-achtige syntaxis kunt gebruiken voor het opvragen van het logbestand. Bijv.

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;

14
2018-06-05 02:22



Interessant, maar u kunt tegen problemen aanlopen als uw logs bijzonder groot zijn, zou ik denken. En hoe goed gaat het met aangepaste log-formaten? - Vagnerr
Ik probeer het op dit moment, de laadtijd is zo traag (tenminste in versie 0.9). Het laden van een 200Mb log duurt meer dan vijf minuten .. - Aseques
Ik moet zeggen dat na de laadtijd (het duurde ongeveer 15 minuten) de synstax van dit programma geweldig is, je kunt sorteren, tellen en groeperen. Echt heel leuk. - Aseques
Apache HTTPD heeft een methode waarmee u de logbestanden effectief naar een database kunt verzenden. Ja, schrijven duurt misschien lang, maar een proxy met threads kan precies het juiste ding doen dat in het midden is ingeklemd. Hoe dan ook, dat maakt het loggen van zoekopdrachten een SQL-achtige syntax veel sneller. Ook laden is niet nodig - de databaseserver is voortdurend "AAN". - nearora


Hier is een script om top urls, top verwijzingen en top useragents te vinden uit de recente N log entries

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

Bron


6
2017-11-27 14:03





voor IP-tellingen in een toegangslog:

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

Het is een beetje lelijk, maar het werkt. Ik gebruik ook het volgende met netstat (om actieve verbindingen te zien):

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

Ze zijn enkele van mijn favoriete "one liners" :)


4
2018-05-22 02:19





Het samenstellen van een lijst met veelgestelde vragen zou een goede index zijn voor dit antwoord op deze vraag. Mijn veelgestelde vragen zijn:

  • waarom is de hitrate gewijzigd?
  • waarom stijgt de totale reactietijd? '.

Ik merk dergelijke wijzigingen op door de serverstatuspagina's (via mod_status) voor te bewaken hitrate en geschatte responstijd voor actieve en recent voltooide aanvragen (ik weet heel goed dat ik een enorme stapel gegevens mis, maar steekproeven zijn goed genoeg).

Ik gebruik de volgende LogFormat-instructie (de% T is echt handig)

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

Ik ben op zoek naar oorzaakeffect en wat er eerst is gebeurd ... meestal over specifieke subsets van patronen in mijn logs, dus ik moet het volgende weten voor elk patroon / reguliere expressie:

  • hitcounts per interval (minuut of uur) voor een bepaald patroon (ip-adres of cgi-tekenreeks of parameters, enz.)
  • histogrammen van geschatte responstijd (met behulp van% T parameter)

Ik gebruik meestal perl, omdat het uiteindelijk complex genoeg wordt om de moeite waard te zijn.


Een niet-perl-voorbeeld zou een vlugge hitrate per minuut zijn voor niet-200 statuscodes:

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

Ja, ik val aan met die grep, veronderstellend dat een quote-spatie-200-spatie alleen overeenkomt met http-statuscodes .... kon awk of perl gebruiken om het veld te isoleren, houd er rekening mee dat het onnauwkeurig zou kunnen zijn.


Een complexer voorbeeld in perl zou kunnen zijn om een ​​verandering in de hitrate voor een patroon te visualiseren.

Er is veel om op te kauwen in het onderstaande script, vooral als je niet bekend bent met perl.

  • leest stdin zodat je delen van je logs kunt gebruiken, gebruik tail (vooral met tail -f), met of zonder greps en andere filtering ...
  • cheats epoch timestamp extractie met hack van een regex en gebruik van Date :: Manip
  • je zou het slechts in geringe mate kunnen wijzigen om reactietijd of andere willekeurige gegevens te extraheren

code volgt:

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

Als u alleen standaardstatistieken wilt verwerken, moet u afrekenen

  • 'mergelog' om al uw logs bij elkaar te krijgen (als u meerdere apaches hebt achter een load-balancer) en
  • webalizer (of awstats of andere gebruikelijke analyser).

3
2018-06-05 21:34





Hier, mijn 'sed' voorbeeld, leest het het standaard formaat van apache logs en converteert het naar iets handiger voor automatische verwerking. De hele regel wordt gedefinieerd als reguliere expressie, variabelen worden opgeslagen en geschreven naar uitvoer met '#' als scheidingsteken.

De vereenvoudigde notatie van de invoer is: % s% s% s [% s] "% s"% s% s "% s" "% s"

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

Voorbeelduitvoerregel: xx.xx.xx.xx # - # - # 29 / maart / 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'

Voel de kracht van reguliere expressies :-)


3
2018-03-29 15:40



Dit maakte het verwerken met AWK een fluitje van een cent. Was op zoek naar een snelle manier om een ​​gemeenschappelijke deliminator in te stellen en dit was het. - Citricguy
Ik heb de regex-kracht gevoeld en wilde gewoon mijn eigen tweak doorgeven, waardoor de "HTML / 1.1" wegvalt en het protocol scheidt (op een waarschijnlijk niet-standaard compliant manier) in zijn eigen veld. Geniet van: `` `cat access.log | sed 's /^(.*) (. *) (. *) [(. *)] \ "([[: alpha:]] \ +) (. *) HTTP \ / 1 \ .1 \" ( . *) (. *) \ "(. *) \" \ "(. *) \" $ / \ 1 # \ 2 # \ 3 # \ 4 # \ 5 # \ 6 # \ 7 # \ 8 # \ 9 # \ 10 / g '`` ` - Josh Rumbut