Frage Verfügen Sie über nützliche awk- und grep-Skripte zum Analysieren von Apache-Protokollen? [geschlossen]


Ich kann Protokollanalysatoren verwenden, aber oft muss ich die letzten Weblogs analysieren, um zu sehen, was gerade passiert.

Manchmal mache ich Dinge, um die 10 besten ips herauszufinden, 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?


63
2018-05-21 22:14


Ursprung


Ich hatte tatsächlich diesen großen schönen Regex, den ich von Hand geschrieben hatte, um alle meine Apache-Benutzer-Logs auf einzelne Felder zu analysieren, um sie in einer Datenbank einzureichen. Ich trete mich selbst, dass ich es nicht mehr habe. Es war ein Einliner; 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:


Sie können so ziemlich alles mit Apache Log-Dateien mit awk alleine machen. Apache-Protokolldateien sind im Wesentlichen durch Leerzeichen getrennt, und Sie können so tun, als ob die Anführungszeichen nicht existieren, und auf alle Informationen zugreifen, die für Sie von Interesse sind. Der einzige Zeitpunkt, an dem das Problem auftritt, ist, dass Sie das kombinierte Protokollformat haben und an Benutzeragenten interessiert sind. An diesem Punkt müssen Sie Anführungszeichen (") als Trennzeichen verwenden und einen separaten Befehl awk ausführen. Im Folgenden werden die IPs von Jeder Benutzer, der die Indexseite nach der Anzahl der Treffer anfragt:

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 am Anfang beliebige Bedingungen hinzufügen. Ersetzen Sie die $ 7 == "/" mit den gewünschten Informationen.

Wenn Sie die $ 1 in (ipcount [$ 1] ++) ersetzen, können Sie die Ergebnisse nach anderen Kriterien gruppieren. Mit $ 7 würde angezeigt, auf welche Seiten zugegriffen wurde und wie oft. Natürlich würdest du dann den Zustand am Anfang ändern wollen. Im Folgenden wird angezeigt, auf welche Seiten von einem Benutzer von einer bestimmten IP aus zugegriffen wurde:

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 über sort pipen, um die Ergebnisse der Reihe nach entweder als Teil des Shell-Befehls oder auch im awk-Skript selbst zu erhalten:

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 würden, um andere Informationen auszudrucken. Es ist alles eine Frage dessen, was Sie herausfinden wollen. Diese sollten als Ausgangspunkt für alles dienen, was Sie interessiert.


53
2018-06-03 02:21



Yah, es scheint immer seltsam, verrückte lange Cat / Grep / Awk Pipelines zu sehen. Sobald du in awk bist, ist das normalerweise genug. Die ersten drei Klauseln des ursprünglichen Beitrags könnten trivialerweise als "awk" / request_to_file_foo / {print $ 1} 'foo.log "geschrieben werden. awk kann eine Datei als Eingabe nehmen und kann Regex verwenden, um zu wissen, um welche Zeilen es sich handelt. - Zac Thompson
Elegant und einfach. Gut. - Olivier Dulac
Vorsicht, Leerzeichen scheinen im "Authuser" (3.) Feld erlaubt zu sein, das alles kaputt macht, und ich persönlich finde es verboten, uns das zu erlauben ;-) - Mandark


Eine Sache, die ich noch nie gesehen habe, ist, dass ich das Apache-Protokolldateiformat aus Gründen, die ich mir nicht vorstellen kann, in eine leichter zu analysierende Version mit den Informationen umwandeln kann, die Ihnen wirklich wichtig sind.

Zum Beispiel verwenden wir niemals die HTTP-Basisauthentifizierung, so dass diese Felder nicht protokolliert werden müssen. ich bin interessiert sich für wie lange jede Anfrage dauert, so fügen wir das in. Für ein Projekt möchten wir auch wissen (auf unserem Load Balancer), wenn Server Anfragen langsamer als andere bedienen, so protokollieren wir den Namen der Server, auf den wir uns verlassen.

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 davon nicht wirklich unterscheiden können, ist, dass zwischen jedem Feld ein Buchstabentabellenzeichen (\ t) ist. Dies bedeutet, dass ich, wenn ich eine Analyse in Python machen möchte, vielleicht nicht 200 Status anzeigen kann, kann ich dies tun:

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

Oder wenn ich "wer ist hotlinking 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

Einfacher zu lesen und zu verstehen, und viel weniger rechenintensiv (kein Regex), was bei 9-GB-Logs einen großen Unterschied macht, wie lange es dauert. Wenn das WIRKLICH ordentlich wird, wenn Sie das gleiche für User-Agenten machen wollen. Wenn Ihre Protokolle durch Leerzeichen getrennt sind, müssen Sie einige reguläre Ausdrücke suchen oder die Suche nach Zeichenfolgen manuell durchführen. 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 wollen, im Wesentlichen genau dieselbe.

Warum um alles in der Welt würde ich die CPU meines Systems für awk und grep ausgeben, wenn Cut genau das tut, was ich um Größenordnungen schneller machen möchte?


23
2018-06-05 21:46



Ihre Beispiele für das neue Format sind eigentlich noch zu kompliziert - IP-Adressen werden cut -f 3 log | uniq -c | sort -n, Benutzeragenten cut -f 8 log | uniq -c | sort -n. - Creshal
Du hast Recht, das ist einfacher. Ich habe die Beispiele aktualisiert, um das zu reflektieren. - Dan Udey
"cat file | grep string" ist nutzlos, warum nicht "grep string file"? - c4f4t0r
Ich habe keine Entschuldigung und habe das Beispiel entsprechend aktualisiert. - Dan Udey


Vergiss awk und grep. Auschecken asql. Warum sollten Sie nicht lesbare Skripte schreiben, wenn Sie eine SQL-ähnliche Syntax zum Abfragen der Logdatei 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;

14
2018-06-05 02:22



Interessant, aber Sie könnten Probleme bekommen, wenn Ihre Protokolle besonders groß sind, würde ich denken. Wie gut bewältigt es auch benutzerdefinierte Protokollformate? - Vagnerr
Ich versuche es gerade, die Ladezeit ist so langsam (zumindest in Version 0.9). Laden eines 200Mb-Protokolls dauert mehr als fünf Minuten. - Aseques
Ich muss sagen, dass nach der Ladezeit (es dauerte ungefähr 15 Minuten) der synstax dieses Programms großartig ist, man kann sortieren, zählen und gruppieren nach. Wirklich nett. - Aseques
Apache HTTPD verfügt über eine Methode, mit der Sie die Protokolle effektiv an eine Datenbank senden können. Ja, Schreibvorgänge können lange dauern, aber ein Threaded-Proxy kann genau das Richtige in der Mitte machen. Wie auch immer, das macht das Abfragen von Protokollen in einer SQL-ähnlichen Syntax viel schneller. Auch das Laden ist nicht involviert - der Datenbankserver ist ständig "ON". - nearora


Hier finden Sie ein Skript, um Top-URLs, Top-Referrer und Top-User-Agenten aus den letzten N Log-Einträgen zu finden

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


6
2017-11-27 14:03





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 verwende 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 "One Liners" :)


4
2018-05-22 02:19





Eine Liste mit häufigen Fragen zu erstellen 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, indem ich die Server-Statusseiten (über mod_status) für Antwortzeit für aktive und kürzlich abgeschlossene Anfragen schätzen und approximieren (Wissend, ich vermisse einen riesigen Datenstapel, aber die Proben sind gut genug).

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

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

Ich suche Ursache-Wirkung und was ist zuerst passiert ... in der Regel über bestimmte Teilmengen von Mustern in meinen Protokollen, also muss ich folgendes für jedes Muster / jeden regulären Ausdruck wissen:

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

Ich benutze normalerweise Perl, weil es irgendwann komplex genug wird, um sich zu lohnen.


Ein Nicht-Perl-Beispiel wäre ein Quickie-Treffer 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, vorausgesetzt, ein Quote-Space-200-Space passt nur http-Status-Codes .... könnte awk oder perl verwenden, um das Feld zu isolieren, nur daran denken, es könnte ungenau sein.


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

Es gibt eine Menge zu kauen in dem Skript unten, vor allem, wenn Sie mit Perl nicht vertraut sind.

  • liest stdin, so dass Sie Teile Ihrer Protokolle verwenden können, verwenden Sie Schwanz (vor allem mit Schwanz-f), mit oder ohne Greps und andere Filter ...
  • Cheats Epochen Timestamp Extraktion mit Hack einer 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 Standardmesswerte verarbeiten möchten, gehen Sie zur Kasse

  • 'mergelog', um alle Ihre Logs zusammen zu bekommen (wenn Sie mehrere Apachen hinter einem Load Balancer haben) und
  • Webalizer (oder AWSTATS oder andere übliche Analysator).

3
2018-06-05 21:34





Hier mein 'sed' Beispiel, es liest das Standardformat von Apache Logs und konvertiert es in etwas bequemeres für die automatische Verarbeitung. Die gesamte Zeile ist als regulärer Ausdruck definiert, Variablen werden gespeichert und zur Ausgabe mit '#' als Trennzeichen geschrieben.

Die vereinfachte Schreibweise der Eingabe ist: % s% s% s [% s] "% s"% s% s "% s" "% s"

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

Beispiel Ausgangszeile: xx.xx.xx.xx # - # - # 29 / Mrz / 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'

Spüre die Kraft regulärer Ausdrücke :-)


3
2018-03-29 15:40



Dies machte die Verarbeitung mit AWK ein Kinderspiel. War auf der Suche nach einem schnellen Weg, um einen gemeinsamen Begrenzer einzurichten und dies genagelt. - Citricguy
Ich habe die Regex-Macht gespürt und wollte nur meinen eigenen Tweak weitergeben, der "HTML / 1.1" ausschneidet und das Protokoll (in einer wahrscheinlich nicht standardkonformen Weise) in ein eigenes Feld aufteilt. Genieße: `` `cat access.log | sed 's /^(.*) (. *) (. *) [(. *)] \ "([[: alpha:]] \ +) (. *) HTTP \ / 1 \ .1 \" ( . *) (. *) \ "(. *) \" \ "(. *) \" $ / \ 1 # \ 2 # \ 3 # \ 4 # \ 5 # \ 6 # \ 7 # \ 8 # \ 9 # \ 10 / g '`` ` - Josh Rumbut