Haben Sie nützliche awk- und grep-Skripte zum Parsen von Apache-Protokollen? [geschlossen]


69

Ich kann Protokollanalysatoren verwenden, muss jedoch häufig aktuelle Webprotokolle analysieren, um zu sehen, was gerade passiert.

Ich mache manchmal Dinge wie die Ermittlung der Top-10-IPs, die eine bestimmte Datei anfordern

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

Was hast du in deiner Toolbox?


1
Ich hatte tatsächlich diese große, schöne Regex, die ich von Hand geschrieben hatte, um alle meine benutzerdefinierten Apache-Protokolle in einzelne Felder zu analysieren und an eine Datenbank zu senden. Ich mache mir einen Kick, dass ich es nicht mehr habe. Es war ein Einzeiler; gab Ihnen eine Variable für jedes Log-Element zurück - dann habe ich in MySQL eingefügt. Wenn ich es finde, werde ich es hier posten.
Kyle Hodgson

Antworten:


54

Mit awk allein können Sie so ziemlich alles mit Apache-Protokolldateien machen. Apache-Protokolldateien sind grundsätzlich durch Leerzeichen getrennt, und Sie können so tun, als ob die Anführungszeichen nicht vorhanden wären, und über die Spaltennummer auf alle gewünschten Informationen zugreifen. Dies funktioniert nur dann, wenn Sie über das kombinierte Protokollformat verfügen und sich für Benutzeragenten interessieren. Zu diesem Zeitpunkt müssen Sie Anführungszeichen (") als Trennzeichen verwenden und einen separaten Befehl awk ausführen. Im Folgenden werden die IPs von aufgeführt Jeder Benutzer, der die Indexseite anfordert, sortiert nach der Anzahl der Treffer:

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

$ 7 ist die angeforderte URL. Sie können zu Beginn beliebige Bedingungen hinzufügen. Ersetzen Sie die '$ 7 == "/" mit den gewünschten Informationen.

Wenn Sie das $ 1 in (ipcount [$ 1] ++) ersetzen, können Sie die Ergebnisse nach anderen Kriterien gruppieren. Wenn Sie $ 7 verwenden, wird angezeigt, auf welche Seiten wie oft zugegriffen wurde. Dann möchten Sie natürlich zu Beginn den Zustand ändern. Das Folgende würde zeigen, auf welche Seiten ein Benutzer von einer bestimmten IP-Adresse aus zugegriffen hat:

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

Sie können die Ausgabe auch sortieren, um die Ergebnisse in der richtigen Reihenfolge abzurufen, entweder als Teil des Shell-Befehls oder auch im awk-Skript selbst:

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

Letzteres wäre nützlich, wenn Sie das awk-Skript erweitern möchten, um andere Informationen auszudrucken. Es ist alles eine Frage dessen, was Sie herausfinden möchten. Diese sollten als Ausgangspunkt für alles dienen, woran Sie interessiert sind.


Yah, es scheint immer seltsam, verrückte lange Cat / Grep / Awk-Pipelines zu sehen. Sobald Sie in awk sind, ist das in der Regel genug. Die ersten drei Klauseln des ursprünglichen Posts könnten trivial als "awk '/ request_to_file_foo / {print $ 1}' foo.log" geschrieben werden. awk kann eine Datei als Eingabe nehmen und mithilfe von Regex wissen, um welche Zeilen es sich handelt.
Zac Thompson

Elegant und schlicht. Gut.
Olivier Dulac

Achtung, Leerzeichen im Feld "authuser" (3rd) scheinen zulässig zu sein, was alles kaputt macht, und ich persönlich denke, es sollte verboten sein, dies zuzulassen
;-)

23

Eine Sache, die ich aus Gründen, die ich mir nicht vorstellen kann, noch nie von einem anderen gesehen habe, ist, das Apache-Protokolldateiformat auf eine einfacher zu analysierende Version mit den Informationen zu ändern, die für Sie tatsächlich von Bedeutung sind.

Beispielsweise verwenden wir niemals die HTTP-Basisauthentifizierung, sodass wir diese Felder nicht protokollieren müssen. Ich interessiere mich dafür, wie lange die Bearbeitung einer Anfrage dauert, daher werden wir dies hinzufügen. Bei einem Projekt möchten wir auch wissen (auf unserem Load Balancer), ob Server Anfragen langsamer als andere bedienen, daher protokollieren wir den Namen des Servers, zu dem wir zurückkehren.

Hier ist ein Auszug aus der Apache-Konfiguration eines Servers:

# 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

Was Sie nicht wirklich erkennen können, ist, dass zwischen jedem Feld ein literales Tabulatorzeichen (\ t) steht. Das bedeutet, dass ich Folgendes tun kann, wenn ich in Python einige Analysen durchführen möchte und beispielsweise Statuswerte anzeigen möchte, die nicht 200 sind:

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

Oder wenn ich machen wollte 'wer verbindet Bilder?' es wäre

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

Für IP-Zählungen in einem Zugriffsprotokoll das vorherige Beispiel:

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

wird so etwas:

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

Leichter zu lesen und zu verstehen und weitaus weniger rechenintensiv (kein regulärer Ausdruck), was bei 9-GB-Protokollen einen großen Unterschied in der Zeitdauer macht. Wenn dies WIRKLICH ordentlich wird, ist es, wenn Sie dasselbe für Benutzeragenten tun möchten. Wenn Ihre Protokolle durch Leerzeichen getrennt sind, müssen Sie einige reguläre Ausdrücke abgleichen oder Zeichenfolgen manuell suchen. Mit diesem Format ist es einfach:

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

Genau das gleiche wie oben. Tatsächlich ist jede Zusammenfassung, die Sie machen möchten, im Wesentlichen genau dieselbe.

Warum um alles in der Welt würde ich die CPU meines Systems für awk ausgeben und grep, wenn cut genau das macht, was ich um Größenordnungen schneller will?


2
Ihre Beispiele für das neue Format sind eigentlich immer noch zu kompliziert - IP-Zählungen werden zu cut -f 3 log | uniq -c | sort -nUser Agents cut -f 8 log | uniq -c | sort -n.
Creshal

Du hast recht, das ist einfacher. Ich habe die Beispiele aktualisiert, um dies widerzuspiegeln.
Dan Udey

"cat file | grep string" ist nutzlos, warum nicht "grep string file"?
c4f4t0r

2
Ich habe keine Entschuldigung und habe das Beispiel entsprechend aktualisiert.
Dan Udey

15

Vergiss awk und grep. Check out asql . Warum nicht lesbare Skripte schreiben, wenn Sie SQL-ähnliche Syntax zum Abfragen der Protokolldatei verwenden können? Z.B.

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;

Interessant, aber Sie könnten auf Probleme stoßen, wenn Ihre Protokolle besonders groß sind, würde ich denken. Wie gut wird es auch mit benutzerdefinierten Protokollformaten fertig?
Vagnerr

Ich versuche es gerade, die Ladezeit ist so langsam (zumindest in Version 0.9). Das Laden eines 200-MB-Protokolls dauert mehr als fünf Minuten.
Folgen 15.12.11

Ich muss sagen, dass nach der Ladezeit (es dauerte ungefähr 15 Minuten) der Synstax dieses Programms großartig ist. Sie können sortieren, zählen und gruppieren nach. Wirklich nett.
Folgen

Apache HTTPD verfügt über eine Methode, mit der Sie die Protokolle effektiv an eine Datenbank senden können. Ja, das Schreiben kann lange dauern, aber ein Thread-Proxy kann genau das Richtige tun, das sich in der Mitte befindet. Auf jeden Fall beschleunigt dies das Abfragen von Protokollen in einer SQL-ähnlichen Syntax erheblich. Auch kein Laden erforderlich - der Datenbankserver ist ständig eingeschaltet.
Nearora

6

Hier finden Sie ein Skript, mit dem Sie die wichtigsten URLs, Verweise und Benutzereinträge aus den letzten N Protokolleinträgen finden können

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

Quelle


4

Für IP-Zählungen in einem Zugriffsprotokoll:

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

Es ist ein bisschen hässlich, aber es funktioniert. Ich benutze auch folgendes mit netstat (um aktive Verbindungen zu sehen):

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

Sie sind einige meiner Lieblings "Einzeiler" :)


3

Das Erstellen einer Liste mit häufig gestellten Fragen wäre ein guter Index für diese Antworten auf diese Frage. Meine häufigsten Fragen sind:

  • warum hat sich die hitrate geändert?
  • Warum steigt die Gesamtreaktionszeit?

Ich bemerke solche Änderungen durch Überwachen der Serverstatus-Seiten (über mod_status) auf die Zugriffsrate und die ungefähre Antwortzeit für aktive und kürzlich abgeschlossene Anforderungen (wohl wissend, dass ich einen großen Datenstapel vermisse, aber Beispiele sind gut genug).

Ich verwende die folgende LogFormat-Direktive (% T ist wirklich nützlich)

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

Ich suche nach Ursache-Wirkung und was zuerst passiert ist ... normalerweise über bestimmte Untergruppen von Mustern in meinen Protokollen, daher muss ich für ein bestimmtes Muster / einen regulären Ausdruck Folgendes wissen:

  • Zugriffszahlen pro Intervall (Minute oder Stunde) für ein bestimmtes Muster (IP-Adresse oder CGI-Zeichenfolge oder Parameter usw.)
  • Histogramme der ungefähren Reaktionszeit (unter Verwendung des% T-Parameters)

Ich benutze normalerweise Perl, weil es mit der Zeit so komplex wird, dass es sich lohnt.


Ein Nicht-Perl-Beispiel wäre eine schnelle Trefferquote pro Minute für Nicht-200-Statuscodes:

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

Ja, ich betrüge mit diesem Grep und gehe davon aus, dass ein Anführungszeichen-Leerzeichen-200-Leerzeichen nur mit http-Statuscodes übereinstimmt. Sie könnten awk oder perl verwenden, um das Feld zu isolieren.


Ein komplexeres Beispiel in Perl könnte darin bestehen, eine Änderung der Trefferquote für ein Muster zu visualisieren.

In dem folgenden Skript gibt es viel zu kauen, besonders wenn Sie mit Perl nicht vertraut sind.

  • Liest stdin, damit Sie Teile Ihrer Protokolle verwenden können. Verwenden Sie tail (insbesondere mit tail -f), mit oder ohne greps und andere Filter.
  • betrügt Epoche Timestamp Extraktion mit Hack eines Regex und Verwendung von Date :: Manip
  • Sie können es nur geringfügig ändern, um die Antwortzeit oder andere beliebige Daten zu extrahieren

Code folgt:

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

Wenn Sie nur Standardmetriken verarbeiten möchten, gehen Sie zur Kasse

  • 'mergelog', um alle Ihre Protokolle zusammen zu bekommen (wenn Sie mehrere Apaches hinter einem Load Balancer haben) und
  • Webalizer (oder awstats oder andere gängige Analyzer).

3

In meinem Beispiel 'sed' liest es das Standardformat von Apache-Protokollen und konvertiert es in etwas, das für die automatische Verarbeitung bequemer ist. Die gesamte Zeile wird als regulärer Ausdruck definiert, Variablen werden gespeichert und mit '#' als Trennzeichen ausgegeben.

Die vereinfachte Notation der Eingabe lautet:% s% s% s [% s] "% s"% s% s "% s" "% s"

Beispiel für eine Eingabezeile: xx.xx.xx.xx - - [29 / Mar / 2011: 12: 33: 02 +0200] "GET /index.html HTTP / 1.0" 200 9443 - "Mozilla / 4.0"

Beispiel für eine Ausgabezeile: 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'

Fühle die Kraft regulärer Ausdrücke :-)


Dies machte die Bearbeitung mit AWK zum Kinderspiel. Ich war auf der Suche nach einem schnellen Weg, einen gemeinsamen Trenner einzurichten, und dieser hat es geschafft.
Citricguy

Ich habe die Regex-Kraft gespürt und wollte nur meine eigene Optimierung weitergeben, die das "HTML / 1.1" ausschneidet und das Protokoll (auf eine wahrscheinlich nicht standardkonforme Weise) in ein eigenes Feld aufteilt. Viel Spaß mit: `` `cat access.log | sed's /^(.*) (. *) (. *) [(. *)] "([[: alpha:]] \ +) (. *) HTTP \ / 1 \ .1 \" ( . *) (. *) "(. *)" (. *) "$ / \ 1 # \ 2 # \ 3 # \ 4 # \ 5 # \ 6 # \ 7 # \ 8 # \ 9 # \ 10 / g '`` `
Josh Rumbut

2

Ich benutze awk oft, indem ich die Datei beschreibe oder beschreibe. Jede Nacht liefere ich mir einen Webbericht für jeden Server. Abhängig von Ihrer Protokolldatei und Ihrem LogFormat müssen Sie einige der einen Liner bearbeiten, um für Sie zu arbeiten.

Hier ist ein einfaches Beispiel:

Wenn ich die Protokolle auf meinem Server nur auf 404/500 Statuscodes beschränken möchte, gehe ich folgendermaßen vor:

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


2

Wer verlinkt deine Bilder ?:

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

1

Meistens lese ich Abschnitte eines Protokolls nach Zeitangaben. Daher habe ich das folgende Skript mit sed geschrieben, um die Zeitspanne, an der ich interessiert bin, zu ermitteln. Es funktioniert für jede Protokolldatei, die ich erhalten habe across und kann auch die archivierten Protokolle verarbeiten.

#! / bin / bash
#Dieses Skript sollte einen Satz von Zeilen zwischen 2 Werten zurückgeben. Der Hauptzweck ist das zweimalige Durchsuchen einer Protokolldatei
Verwendung von #Script: logship.sh Datei "start" "stop"

#Wenn die Datei ein "/" im Datumsbereich enthält, fügen Sie in den folgenden 2 Zeilen das Escape-Zeichen hinzu, damit nach diesen Zeichen gesucht werden kann
start = $ (echo "$ 1" | sed 's / \ // \\\ // g')
stop = $ (Echo "$ 2" | sed 's / \ // \\\ // g')

zipped = $ (echo "$ 3" | grep -c "gz $") # legt fest, ob die Datei gezippt wird oder nicht

if ["$ zipped" == "1"]; Wenn die Datei komprimiert ist, leiten Sie sie durch zcat, bevor sed
        zcat $ 3 | sed -n "/ $ start /, / $ stop / p";
sonst
        sed -n "/ $ start /, / $ stop / p" $ 3; #wenn es nicht gepackt ist, renn einfach sed
fi

1

Obwohl ich nicht sed oder awk bin, gibt es zwei Dinge, die ich für den Umgang mit Apache- und Icecast-Protokolldateien als nützlich empfunden habe.

AWStats hat ein sehr nützliches Skript namens logresolvemerge.pl , das mehrere komprimierte oder unkomprimierte Protokolldateien kombiniert, Dupes entfernt und nach Zeitstempel sortiert. Es kann auch DNS-Lookups durchführen und so konfiguriert werden, dass Multithreading ausgeführt wird. Es ist besonders nützlich , wenn sie mit awstats verwenden , da awstats nicht Protokollzeilen mit Zeitstempeln älter als die aktuelle Datenbank hinzufügen kann, so dass alles in Ordnung hinzugefügt werden muß, aber das ist sehr einfach , da Sie nur Futter alles bei logresolvemerge.pl und alles erscheint schön aus.

sed und awk sind ziemlich schlecht im Umgang mit Daten, weil sie sie im Allgemeinen als Zeichenfolgen behandeln. awk hat einige Zeit- und Datumsfunktionen, aber sie sind nicht zu viel. Zum Beispiel ist es schwierig, einen Zeilenbereich zwischen zwei Zeitstempeln zu extrahieren, wenn diese genauen Zeitstempel nicht in der Datei vorkommen (auch wenn Werte zwischen ihnen vorkommen) - Chris 'Beispiel hat genau dieses Problem. Um dies zu beheben, habe ich ein PHP-Skript geschrieben , das Zeitstempelbereiche der Protokolldatei meldet und auch einen Block nach Zeitstempelbereich extrahieren kann, wobei ein beliebiges Datums- oder Zeitformat verwendet wird (es muss nicht mit dem Zeitstempelformat der Protokolldatei übereinstimmen).

Um dieses Thema auf dem Laufenden zu halten, folgen einige nützliche Tipps: Ermitteln Sie die Gesamtanzahl der aus Apache- oder Icecast-Protokollen gelieferten Bytes:

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

Ermitteln Sie die Gesamtanzahl der verbundenen Sekunden aus einem Icecast-Protokoll:

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

+1 für einfaches
bytesummierendes

0

Nach der Wiederherstellung dieses alten Threads, nachdem ich auf asql für große Protokolldateien verzichtet hatte, suchte ich nach einer Lösung, die auch im Serverfehler steckte. Ich fand , dass wtop hier ein OpenSource-Tool ist, das in der Lage ist, Protokolle live zu überwachen oder zu verarbeiten und Statistiken zu erhalten (oben) N), sehr flexibel und mächtig, der offizielle Ort ist hier

Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.