Aveți vreun script awk și grep util pentru analizarea jurnalelor apache? [închis] (Administrarea sistemului, Apache 2.2, Grep, Logare, Awk, Parsing)

deadprogrammer a intrebat.

Pot folosi analizatori de jurnale, dar de multe ori am nevoie să analizez jurnalele web recente pentru a vedea ce se întâmplă în acest moment.

Uneori fac lucruri cum ar fi să aflu care sunt primele 10 IP-uri care solicită un anumit fișier

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

Ce ai în setul de instrumente?

Comentarii

  • De fapt, aveam un regex mare și frumos pe care îl scrisesem de mână pentru a analiza toate jurnalele mele Apache personalizate în câmpuri individuale pentru a le introduce într-o bază de date. Mă doare în cot că nu-l mai am. Era un one liner; îți dădea înapoi o variabilă pentru fiecare element de jurnal – apoi îl introduceam în MySQL. Dacă o găsesc, o voi posta aici. –  > Por Kyle.
12 răspunsuri
Mark

Puteți face cam orice cu fișierele de jurnal apache doar cu awk. Fișierele de jurnal Apache sunt practic separate prin spații albe și poți pretinde că ghilimelele nu există și poți accesa orice informație care te interesează prin numărul de coloană. Singurul moment în care acest lucru se strică este dacă aveți formatul de jurnal combinat și sunteți interesat de agenții de utilizator, moment în care trebuie să folosiți ghilimele („) ca separator și să executați o comandă awk separată. Următorul lucru vă va arăta IP-urile fiecărui utilizator care a solicitat pagina de index, ordonate în funcție de numărul de accesări:

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

$7 este url-ul solicitat. Puteți adăuga orice condiții doriți la început. Înlocuiți ‘$7 == „/” cu orice informație pe care o doriți.

Dacă înlocuiți $1 în (ipcount[$1]++), atunci puteți grupa rezultatele după alte criterii. Folosind $7 ar arăta ce pagini au fost accesate și cât de des. Bineînțeles, atunci ar trebui să modificați condiția de la început. Următoarele ar arăta ce pagini au fost accesate de un utilizator de la un anumit IP:

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

Puteți, de asemenea, să direcționați ieșirea prin sortare pentru a obține rezultatele în ordine, fie ca parte a comenzii shell, fie în scriptul awk în sine:

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

Aceasta din urmă ar fi utilă dacă ați decide să extindeți scriptul awk pentru a imprima și alte informații. Totul depinde de ceea ce doriți să aflați. Acestea ar trebui să vă servească drept punct de plecare pentru orice vă interesează.

Comentarii

  • Da, întotdeauna pare ciudat să vezi conducte cat/grep/awk nebunește de lungi. Odată ce ai intrat în awk, de obicei este suficient. Primele trei clauze ale mesajului original ar putea fi scrise în mod trivial ca „awk ‘/request_to_file_foo/ {print $1}’ foo.log”. awk poate lua un fișier ca intrare și poate folosi regex pentru a ști de ce linii să se intereseze. –  > Por Zac Thompson.
  • Elegant și simplu. Bun. –  > Por Olivier Dulac.
  • Atenție, se pare că spațiile sunt permise în câmpul „authuser” (al treilea), asta strică totul și, personal, cred că ar trebui interzis, pentru a ne permite să facem asta 😉 –  > Por Mandark.
Dan Udey

Un lucru pe care nu am văzut pe nimeni altcineva să-l facă, din motive pe care nu mi le pot imagina, este să schimbe formatul fișierului jurnal Apache într-o versiune mai ușor de analizat, cu informațiile care contează cu adevărat pentru tine.

De exemplu, noi nu folosim niciodată HTTP basic auth, așa că nu avem nevoie să înregistrăm acele câmpuri. I sunt interesat de cât timp durează servirea fiecărei cereri, așa că vom adăuga acest lucru. Pentru un proiect, dorim, de asemenea, să știm (pe echilibrul nostru de sarcină) dacă vreun server servește cererile mai lent decât altele, așa că înregistrăm numele serverului pe care îl trimitem prin proxy.

Iată un extras din configurația apache a unui 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

Ceea ce nu se poate observa este că între fiecare câmp se află un caracter de tabulare literală (t). Acest lucru înseamnă că, dacă vreau să fac o analiză în Python, poate să arăt statusuri non-200, de exemplu, pot face acest lucru:

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

Sau dacă aș vrea să fac „cine face hotlinking la imagini?” ar fi

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

Pentru numărul de IP-uri într-un jurnal de acces, exemplul anterior:

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

devine ceva de genul acesta:

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

Mai ușor de citit și de înțeles și mult mai puțin costisitor din punct de vedere al calculului (fără regex), ceea ce, în cazul unor jurnale de 9 GB, face o diferență uriașă în ceea ce privește timpul necesar. Atunci când acest lucru devine cu adevărat interesant este dacă doriți să faceți același lucru pentru User-agents. Dacă jurnalele dvs. sunt delimitate prin spațiu, trebuie să faceți manual o potrivire cu expresii regulate sau o căutare de șiruri. Cu acest format, este simplu:

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

Exact la fel ca cel de mai sus. De fapt, orice rezumat pe care doriți să îl faceți este, în esență, exact la fel.

De ce naiba aș cheltui CPU-ul sistemului meu pe awk și grep când cut va face exact ceea ce vreau cu mult mai repede?

Comentarii

  • Exemplele dvs. pentru noul format sunt de fapt încă prea complicate – numărul de IP-uri devine cut -f 3 log | uniq -c | sort -n, agenți utilizator cut -f 8 log | uniq -c | sort -n. –  > Por Creshal.
  • Aveți dreptate, este mai simplu. Am actualizat exemplele pentru a reflecta acest lucru. –  > Por Dan Udey.
  • „cat file | grep string” este inutil, de ce nu „grep string file” ? –  > Por c4f4t0r.
  • Nu am nicio scuză și am actualizat exemplul în consecință. –  > Por Dan Udey.
Vihang D

Uitați de awk și grep. Consultați asql. De ce să scrieți scripturi ilizibile când puteți folosi o sintaxă asemănătoare cu cea a sql pentru interogarea fișierului jurnal. De exemplu.

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;

Comentarii

  • Interesant, dar cred că ați putea întâmpina probleme dacă jurnalele dvs. sunt deosebit de mari. De asemenea, cât de bine se descurcă cu formatele de jurnal personalizate? –  > Por Vagnerr.
  • Îl încerc în acest moment, timpul de încărcare este atât de lent (cel puțin în versiunea 0.9). Încărcarea unui log de 200Mb durează mai mult de cinci minute… –  > Por aseques.
  • Trebuie să spun că după timpul de încărcare (a durat în jur de 15 minute), sintaxa acestui program este grozavă, poți sorta,număra și grupa după. Foarte frumos. –  > Por aseques.
  • Apache HTTPD are o metodă cu ajutorul căreia puteți trimite efectiv jurnalele către o bază de date. Da, scrierile ar putea dura mult timp, dar un proxy cu fire de execuție ar putea face exact ceea ce trebuie intercalat la mijloc. Oricum, asta va face ca interogarea jurnalelor într-o sintaxă asemănătoare cu SQL să fie mult mai rapidă. De asemenea, nu este implicată nici încărcarea – serverul bazei de date este în permanență „ON”. –  > Por nearora.
  • Habar nu aveam că există așa ceva! Pentru dezvoltatorii web care sunt deja foarte experimentați cu SQL, aceasta este o opțiune excelentă. –  > Por rinogo.
anoopjohn

Iată un script pentru a găsi top urls, top referrers și top useragents din intrările recente din N log-uri

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

Sursa

f4nt

pentru numărul de IP-uri într-un jurnal de acces:

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

Este un pic urât, dar funcționează. Folosesc, de asemenea, următoarele cu netstat (pentru a vedea conexiunile active):

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

Acestea sunt unele dintre „one liners” mele preferate 🙂

ericslaw

Construirea unei liste de întrebări comune ar fi un index grozav pentru aceste răspunsuri la această întrebare. Întrebările mele obișnuite sunt:

  • de ce s-a schimbat hitrate-ul?
  • de ce crește timpul de răspuns general?”.

Observ astfel de schimbări monitorizând paginile de stare a serverului (prin mod_status) pentruhitrate și timpul de răspuns aproximativ pentru cererile active și cele recent finalizate(știind foarte bine că îmi scapă o grămadă imensă de date, dar mostrele sunt suficient de bune).

Folosesc următoarea directivă LogFormat (%T este foarte utilă)

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

Caut cauza-efect și ce s-a întâmplat mai întâi… de obicei despre anumite subansambluri specifice de tipare din jurnalele mele, așa că am nevoie să știu următoarele pentru orice tip/ expresie regulată dată:

  • hitcounts per interval (minut sau oră) pentru un anumit model (adresă ip sau șir cgi sau parametri, etc.)
  • histograme ale timpului de răspuns aproximativ (utilizând parametrul %T)

În general, folosesc perl, pentru că, în cele din urmă, devine suficient de complex pentru a merita.


Un exemplu fără perl ar fi o rată de accesare rapidă pe minut pentru codurile de stare non-200:

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

Da, trișez cu acel grep, presupunând că un citat-spațiu-200-spațiu se potrivește numai cu codurile de stare http…. ar putea folosi awk sau perl pentru a izola câmpul, dar nu uitați că ar putea fi inexact.


Un exemplu mai complex în perl ar putea fi vizualizarea unei schimbări în rata de succes pentru un model.

Scriptul de mai jos are multe de mestecat, mai ales dacă nu sunteți familiarizați cu perl.

  • citește stdin, astfel încât să puteți folosi porțiuni din jurnalele dvs., folosește tail (în special cu tail -f),cu sau fără greps și alte filtre…
  • trișează extragerea timestamp-ului epocii cu hack-ul unui regex și utilizarea lui Date::Manip
  • ați putea să-l modificați doar puțin pentru a extrage timpul de răspuns sau alte date arbitrare

codul urmează:

#!/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
",
            $t,
            strftime("%m/%d/%Y %H:%M:%S",localtime($t)),
            $count{$t}+0,
            substr("x"x100,0,$count{$t}/$tick
    );
}

Dacă doriți doar să procesați metrici standard, verificați

  • ‘mergelog’ pentru a obține toate jurnalele împreună (dacă aveți mai multe Apache în spatele unui load balancer) și
  • webalizer (sau awstats sau un alt analizor comun).

Kris

Iată exemplul meu ‘sed’, citește formatul implicit al jurnalelor apache și îl convertește în ceva mai convenabil pentru procesarea automată. Întreaga linie este definită ca expresie regulată, variabilele sunt salvate și scrise la ieșire cu ‘#’ ca separator.

Notația simplificată a intrării este:%s %s %s %s %s [%s] „%s” %s %s %s „%s” „%s” „%s”

Exemplu de linie de intrare:xx.xx.xx.xx.xx – – [29/Mar/2011:12:33:02 +0200] „GET /index.html HTTP/1.0” 200 9443 „-” „Mozilla/4.0”

Exemplu de linie de ieșire:xx.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'

Simțiți puterea expresiilor regulate 🙂

Comentarii

  • Acest lucru a făcut ca procesarea cu AWK să fie floare la ureche. Căutam o modalitate rapidă de a configura un delimitator comun și acest lucru a bătut în cuie. –  > Por Citricguy.
  • Am simțit puterea regexurilor și am vrut doar să transmit propria mea modificare, care elimină „HTML/1.1” și separă protocolul (într-un mod probabil neconform cu standardele) în propriul câmp. Bucurați-vă: „` cat access.log | sed ‘s/^(.*) (.*) (.*) (.*) [(.*)]
    ([[[:alpha:]]+) (.*) HTTP/1.1
    (.*) (.*)
    (.*)

    (.*)
    $/1#2#3#4#5#6#7#8#9#10/g’ „` –  > Por Josh Rumbut.

Michael Steinfeld

Folosesc foarte mult awk prin tailing sau cat’ing fișierul. În fiecare seară îmi livrez un raport web pentru fiecare server. În funcție de fișierul de jurnal și de LogFormat, va trebui să editați unele dintre rândurile unice pentru a funcționa pentru dumneavoastră… .

Iată un exemplu simplu:

Dacă vreau să urmăresc în jurnalele de pe serverul meu doar codurile de stare 404/500, trebuie să fac astfel:

# $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
", secs / 60,req )}' | sort -rn | head -50

exit 0

< /snip>

rkthkr

Cine face hot-linking pentru imaginile tale:

awk -F" '($2 ~ /.(jpg|gif)/ && $4 !~ /^http://www.mydomain.com/){print $4}' access_log | sort | uniq -c | sort

Chris

Lucrul pe care tind să îl fac cel mai adesea este să citesc secțiuni ale unui jurnal în funcție de timp, așa că am scris următorul script folosind sed pentru a extrage perioada care mă interesează, funcționează pe fiecare fișier de jurnal pe care l-am întâlnit și se poate ocupa și de jurnalele arhivate.

#!/bin/bash
#This script should return a set of lines between 2 values, the main purpose is for searching a log file between 2 times
#Script usage: logship.sh "start" "stop" file

#If the file contains any "/" in the date range the following 2 lines add the escape character so that the search can be performed for those characters
start=$(echo "$1" | sed 's///\//g')
stop=$(echo "$2" | sed 's///\//g')

zipped=$(echo "$3" | grep -c "gz$")     #figures out if the file is zipped or not

if [ "$zipped" ==  "1" ]; then          #If the file is zipped then pass it through zcat before sed
        zcat $3 | sed -n "/$start/,/$stop/p";
else
        sed -n "/$start/,/$stop/p" $3;  #if it's not zipped just run sed
fi

Synchro

Deși nu este vorba de sed sau awk, există două lucruri pe care le-am găsit utile pentru gestionarea fișierelor de jurnal apache și icecast.

AWStats are un script foarte util numit logresolvemerge.pl care combină mai multe fișiere de jurnal comprimate sau necomprimate, elimină dublurile și le sortează în funcție de data și ora. De asemenea, poate face căutări DNS și poate fi configurat pentru a rula în mai multe fire. Este deosebit de util atunci când este utilizat cu awstats, deoarece awstats nu poate adăuga linii de jurnal cu timestamp-uri mai vechi decât baza de date curentă, astfel încât toate trebuie adăugate în ordine, dar acest lucru este foarte ușor, deoarece trebuie doar să se cronometreze totul la logresolvemerge.pl și totul apare frumos.

sed și awk sunt destul de proaste în ceea ce privește manipularea datelor deoarece, în general, le tratează ca pe niște șiruri de caractere. awk dispune de câteva funcții de timp și dată, dar nu sunt la înălțimea așteptărilor. De exemplu, extragerea unui interval de linii între două marcaje de timp este dificilă dacă acele marcaje de timp exacte nu apar în fișier (chiar dacă valorile dintre ele apar) – exemplul lui Chris are exact această problemă. Pentru a rezolva această problemă, am scris un script PHP care raportează intervale de timestamp-uri din fișierul de jurnal și care poate extrage, de asemenea, o bucată în funcție de intervalul de timestamp-uri, folosind orice format de dată sau oră doriți (nu trebuie să corespundă cu formatul timestamp-ului din fișierul de jurnal).

Pentru a rămâne în legătură cu subiectul, iată câteva awkisme utile: Obțineți numărul total de octeți serviți din jurnalul apache sau icecast:

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

Obțineți numărul total de secunde de conectare dintr-un jurnal icecast:

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

Comentarii

  • +1 pentru o simplă însumare de octeți a jurnalului apache cu awk –  > Por rymo.
aseques

Recuperând acest thread vechi, după ce am renunțat la asql pentru fișiere de log mari, am căutat o soluție againg, tot în serverfault, am găsit despre wtop aici este un instrument opensource, care este capabil să facă monitorizare live sau să proceseze log-uri și să obțină statistici (top N), foarte flexibil și puternic, locul oficial este aici