Log-Analyse — Webserver-Logs mit Bordmitteln auswerten
Praxis-Rezepte zur Analyse von Apache- und nginx-Logs mit grep, awk, sort und uniq — Top-IPs, Status-Codes, Bandbreite und Echtzeit-Monitoring.
Bevor du für eine schnelle Auswertung gleich GoAccess oder einen kompletten Log-Stack startest, kommst du mit den Bordmitteln deines Systems oft schon ans Ziel: grep, awk, sort, uniq und tail -f beantworten die meisten Fragen zu Apache- und nginx-Logs in Sekunden. Diese Rezept-Sammlung zeigt dir, wie du Top-IPs ermittelst, die Verteilung der Status-Codes prüfst, die meistgefragten URLs findest, Bandbreite summierst und Fehler in Echtzeit mitliest. Behalte im Hinterkopf, dass die Feld-Indizes ($1, $7, $9 …) vom konkreten Log-Format abhängen — die Beispiele gehen vom Combined Log Format aus. Für Ad-hoc-Analysen reichen diese Einzeiler völlig; erst bei wiederkehrenden Reports oder einem Dashboard lohnt der Griff zu spezialisierten Werkzeugen.
Schneller Überblick
wc -l <file> — Zählt die Gesamtzahl der Zeilen in einer Log-Datei.
wc -l access.loghead -n <count> <file> — Zeigt die ersten N Zeilen, um das Log-Format zu verstehen.
head -n 20 /var/log/syslogtail -n <count> <file> — Zeigt die letzten N Log-Einträge.
tail -n 50 /var/log/nginx/error.logless +F <file> — Öffnet eine Log-Datei im Follow-Modus (wie tail -f, aber mit Scrollback). Strg+C zum Scrollen, F zum Weiterverfolgen.
less +F /var/log/syslogfile <logfile> — Erkennt, ob eine Log-Datei Klartext, gzip-komprimiert oder ein anderes Format ist.
file /var/log/syslog.2.gzdu -sh <file> — Prüft die Größe einer Log-Datei vor der Verarbeitung.
du -sh /var/log/nginx/access.logNach Muster filtern
grep '<pattern>' <file> — Findet alle Zeilen, die auf ein Muster passen.
grep 'ERROR' /var/log/app.loggrep -i '<pattern>' <file> — Mustersuche ohne Beachtung der Groß-/Kleinschreibung.
grep -i 'timeout' /var/log/app.loggrep -E 'ERROR|WARN|FATAL' <file> — Findet Zeilen, die auf eines von mehreren Mustern passen.
grep -E 'ERROR|WARN|FATAL' /var/log/app.loggrep -v '<pattern>' <file> — Schließt Zeilen aus, die auf ein Muster passen (invertierter Treffer).
grep -v 'healthcheck' access.loggrep -v -e '<a>' -e '<b>' <file> — Schließt Zeilen aus, die auf mehrere Muster passen.
grep -v -e 'bot' -e 'crawler' -e 'monitoring' access.loggrep -c '<pattern>' <file> — Zählt, wie viele Zeilen auf ein Muster passen.
grep -c '500' access.loggrep -B <n> -A <n> '<pattern>' <file> — Zeigt Kontextzeilen vor und nach jedem Treffer.
grep -B 3 -A 5 'OutOfMemoryError' app.logZeitbasiertes Filtern
grep '<date>' <file> — Filtert Log-Einträge für ein bestimmtes Datum.
grep '2026-02-16' /var/log/app.loggrep '<date>.*<time_prefix>' <file> — Filtert Einträge für ein bestimmtes Datum und eine bestimmte Stunde.
grep '16/Feb/2026:14' access.logawk '$0 >= "<start>" && $0 <= "<end>"' <file> — Extrahiert Log-Einträge innerhalb eines Zeitfensters (funktioniert mit ISO-Zeitstempeln am Zeilenanfang).
awk '$0 >= "2026-02-16 10:00" && $0 <= "2026-02-16 12:00"' app.logsed -n '/<start>/,/<end>/p' <file> — Extrahiert alle Zeilen zwischen zwei Zeitstempel-Mustern.
sed -n '/2026-02-16 10:00/,/2026-02-16 12:00/p' app.logawk '/^<start>/,/^<end>/' <file> — Extrahiert mit awk einen Zeilenbereich zwischen zwei passenden Mustern.
awk '/^2026-02-16 14:00/,/^2026-02-16 15:00/' app.logjournalctl --since '<start>' --until '<end>' — Fragt das systemd-Journal für ein bestimmtes Zeitfenster ab.
journalctl --since '2026-02-16 10:00' --until '2026-02-16 12:00'Häufigkeit & Zählen
sort <file> | uniq -c | sort -rn | head -<n> — Zählt und sortiert die häufigsten Zeilen.
awk '{print $1}' access.log | sort | uniq -c | sort -rn | head -20awk '{print $<field>}' <file> | sort | uniq -c | sort -rn — Zählt das Vorkommen eines bestimmten Felds (einer Spalte).
awk '{print $9}' access.log | sort | uniq -c | sort -rngrep -oE '<pattern>' <file> | sort | uniq -c | sort -rn — Extrahiert bestimmte Muster aus Log-Zeilen und zählt sie.
grep -oE '\b[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+\b' access.log | sort | uniq -c | sort -rn | headawk '{count[$<field>]++} END {for (k in count) print count[k], k}' <file> | sort -rn — Zählt Feld-Vorkommen mit awk (schneller bei großen Dateien, ein einziger Durchlauf).
awk '{count[$9]++} END {for (k in count) print count[k], k}' access.log | sort -rngrep -c '<pattern>' <file1> <file2> <file3> — Zählt Treffer pro Datei über mehrere Log-Dateien hinweg.
grep -c 'ERROR' /var/log/app.log.*Apache-/nginx-Access-Logs
awk '{print $9}' <file> | sort | uniq -c | sort -rn — Zählt HTTP-Status-Codes (Feld 9 im Combined Log Format).
awk '{print $9}' access.log | sort | uniq -c | sort -rnawk '{print $1}' <file> | sort | uniq -c | sort -rn | head -<n> — Findet die Top-N-Client-IP-Adressen nach Request-Anzahl.
awk '{print $1}' access.log | sort | uniq -c | sort -rn | head -20awk '{print $7}' <file> | sort | uniq -c | sort -rn | head -<n> — Findet die meistgefragten URLs.
awk '{print $7}' access.log | sort | uniq -c | sort -rn | head -20awk '$9 == <code>' <file> — Filtert Requests nach einem bestimmten HTTP-Status-Code.
awk '$9 == 404' access.logawk '$9 >= 500' <file> — Findet alle Server-Fehler (5xx-Status-Codes).
awk '$9 >= 500' access.logawk '{print $10}' <file> | paste -sd+ | bc — Berechnet die gesamte übertragene Byte-Menge (Feld 10 im Combined Log Format).
awk '$9 == 200 {print $10}' access.log | paste -sd+ | bcawk '{sum += $10} END {printf "%.2f GB\n", sum/1073741824}' <file> — Berechnet die Gesamt-Bandbreite in lesbarer Form.
awk '{sum += $10} END {printf "%.2f GB\n", sum/1073741824}' access.logawk -F'"' '{print $6}' <file> | sort | uniq -c | sort -rn | head -<n> — Findet die häufigsten User-Agent-Strings (Feld 6 beim Aufteilen an Anführungszeichen).
awk -F'"' '{print $6}' access.log | sort | uniq -c | sort -rn | head -10awk '$9 == 404 {print $7}' <file> | sort | uniq -c | sort -rn | head -<n> — Findet die häufigsten 404-URLs.
awk '$9 == 404 {print $7}' access.log | sort | uniq -c | sort -rn | head -20Requests pro Zeitraum
awk '{print substr($4, 2, 17)}' <file> | uniq -c — Zählt Requests pro Minute (Apache-/nginx-Combined-Log-Format).
awk '{print substr($4, 2, 17)}' access.log | uniq -cawk '{print substr($4, 2, 14)}' <file> | uniq -c — Zählt Requests pro Stunde.
awk '{print substr($4, 2, 14)}' access.log | uniq -cawk '{print substr($4, 2, 17)}' <file> | uniq -c | sort -rn | head -<n> — Findet die geschäftigsten Minuten nach Request-Volumen (Traffic-Spitzen).
awk '{print substr($4, 2, 17)}' access.log | uniq -c | sort -rn | head -10awk '$9 >= 500 {print substr($4, 2, 17)}' <file> | uniq -c — Zählt Server-Fehler pro Minute, um Fehler-Spitzen zu erkennen.
awk '$9 >= 500 {print substr($4, 2, 17)}' access.log | uniq -c | sort -rn | head -10Echtzeit-Monitoring
tail -f <file> — Verfolgt eine Log-Datei in Echtzeit, während neue Einträge angehängt werden.
tail -f /var/log/nginx/error.logtail -f <file> | grep --line-buffered '<pattern>' — Verfolgt ein Log und filtert in Echtzeit nach einem bestimmten Muster.
tail -f /var/log/app.log | grep --line-buffered 'ERROR'tail -f <file1> <file2> — Verfolgt mehrere Log-Dateien gleichzeitig mit Dateinamen-Überschriften.
tail -f /var/log/nginx/access.log /var/log/nginx/error.logtail -F <file> — Verfolgt mit Wiederholung. Folgt dem Log auch nach einer Log-Rotation weiter.
tail -F /var/log/app.logjournalctl -f -u <service> — Verfolgt die systemd-Journal-Ausgabe für einen bestimmten Dienst.
journalctl -f -u nginxtail -f <file> | awk '$9 >= 400 {print}' — Überwacht das Access-Log und zeigt in Echtzeit nur Fehler-Antworten.
tail -f access.log | awk '$9 >= 400 {print}'tail -f <file> | while read line; do echo "$(date +%T) $line"; done — Verfolgt ein Log und stellt jeder Zeile einen Wanduhr-Zeitstempel voran.
tail -f app.log | while read line; do echo "$(date +%T) $line"; doneKomprimierte & rotierte Logs
zcat <file.gz> — Zeigt den vollständigen Inhalt einer gzip-komprimierten Log-Datei.
zcat /var/log/syslog.2.gzzgrep '<pattern>' <file.gz> — Sucht in gzip-komprimierten Log-Dateien, ohne sie zu entpacken.
zgrep 'ERROR' /var/log/app.log.*.gzzless <file.gz> — Durchblättert eine gzip-komprimierte Log-Datei interaktiv mit einem Pager.
zless /var/log/syslog.3.gzzcat <files.gz> | grep '<pattern>' — Sucht über mehrere komprimierte Log-Dateien hinweg.
zcat /var/log/nginx/access.log.*.gz | grep '500'cat <current> <(zcat <rotated.gz>) | grep '<pattern>' — Sucht über aktuelle und rotierte komprimierte Logs gleichzeitig.
cat access.log <(zcat access.log.*.gz) | grep 'POST /api/login'Systemd-Journal (journalctl)
journalctl -u <service> — Zeigt alle Journal-Einträge für einen bestimmten systemd-Dienst.
journalctl -u nginxjournalctl -u <service> -n <count> — Zeigt die letzten N Einträge für einen Dienst.
journalctl -u mysql -n 50journalctl -p <priority> — Filtert nach Prioritätsstufe: emerg, alert, crit, err, warning, notice, info, debug.
journalctl -p errjournalctl -p <priority> --since '<time>' — Zeigt Einträge einer bestimmten Priorität seit einem gegebenen Zeitpunkt.
journalctl -p warning --since '1 hour ago'journalctl --since '<start>' --until '<end>' — Fragt Einträge innerhalb eines Zeitfensters ab.
journalctl --since '2026-02-16 08:00' --until '2026-02-16 12:00'journalctl -u <service> --no-pager -o json-pretty — Gibt Journal-Einträge als formatiertes JSON zur Weiterverarbeitung aus.
journalctl -u nginx --no-pager -o json-pretty | head -100journalctl --disk-usage — Zeigt, wie viel Speicherplatz das Journal belegt.
journalctl -k — Zeigt nur Kernel-Meldungen (entspricht dmesg).
journalctl -k -p errMehrzeilige Log-Einträge
grep -A <n> '<pattern>' <file> — Zeigt eine feste Anzahl Zeilen nach jedem Treffer (z. B. Stacktraces).
grep -A 20 'Exception' /var/log/app.logawk '/<start>/{found=1} found; /<end>/{found=0}' <file> — Extrahiert Blöcke zwischen einem Start- und einem End-Muster.
awk '/BEGIN STACKTRACE/{found=1} found; /END STACKTRACE/{found=0}' app.loggrep -Pzo '(?s)<start>.*?<end>' <file> — Extrahiert mehrzeilige Blöcke mit Perl-kompatibler Regex.
grep -Pzo '(?s)Exception.*?\n\n' app.logawk '/^[0-9]{4}-/{if(buf && buf ~ /<pattern>/) print buf; buf=$0; next} {buf=buf ORS $0} END {if(buf ~ /<pattern>/) print buf}' <file> — Sammelt mehrzeilige Einträge, die mit einem Zeitstempel beginnen, und filtert nach Muster.
awk '/^[0-9]{4}-/{if(buf && buf ~ /ERROR/) print buf; buf=$0; next} {buf=buf ORS $0} END {if(buf ~ /ERROR/) print buf}' app.logFeld-Extraktion mit awk
awk '{print $<n>}' <file> — Gibt ein bestimmtes Feld (eine Spalte) aus jeder Log-Zeile aus.
awk '{print $1}' access.logawk -F'<sep>' '{print $<n>}' <file> — Teilt Zeilen an einem eigenen Feldtrenner und extrahiert ein Feld.
awk -F'|' '{print $3}' app.logawk -F'"' '{print $2}' <file> — Extrahiert die Request-Zeile aus dem Apache-/nginx-Combined-Log-Format.
awk -F'"' '{print $2}' access.logawk '{print $NF}' <file> — Gibt das letzte Feld jeder Zeile aus.
awk '{print $NF}' access.logawk '{for(i=<start>;i<=NF;i++) printf "%s ", $i; print ""}' <file> — Gibt alle Felder von Feld N bis zum Zeilenende aus (führende Spalten überspringen).
awk '{for(i=4;i<=NF;i++) printf "%s ", $i; print ""}' /var/log/syslogSortieren & Deduplizieren
sort <file> | uniq — Entfernt doppelte Zeilen aus sortierter Ausgabe.
awk '{print $7}' access.log | sort | uniqsort -u <file> — Sortiert und entfernt Duplikate in einem Schritt.
awk '{print $1}' access.log | sort -usort <file> | uniq -c | sort -rn — Zählt Vorkommen und sortiert nach Häufigkeit (absteigend).
awk '{print $7}' access.log | sort | uniq -c | sort -rnsort <file> | uniq -d — Zeigt nur Zeilen, die mehr als einmal vorkommen (nur Duplikate).
awk '{print $1}' access.log | sort | uniq -dsort -t'<sep>' -k<field> -rn <file> — Sortiert numerisch nach einem bestimmten Feld in umgekehrter Reihenfolge.
du -sh /var/log/*.log | sort -t'\t' -k1 -rhPraktische Rezepte
awk '{ip[$1]++} END {for (k in ip) if (ip[k]>100) print ip[k], k}' access.log — Findet IP-Adressen mit mehr als 100 Requests (möglicher Missbrauch).
awk '$9 == 404 {print $7}' access.log | sort | uniq -c | sort -rn | head -20 — Top-20-URLs, die 404-Fehler liefern.
awk -F'"' '$2 ~ /POST/ {print $2}' access.log | sort | uniq -c | sort -rn — Zählt POST-Requests gruppiert nach URL.
grep 'ERROR' app.log | awk '{print $1, $2}' | cut -d: -f1,2 | uniq -c — Zählt Fehler gruppiert nach Stunde für die Trend-Analyse.
diff <(grep 'ERROR' app.log.1) <(grep 'ERROR' app.log) — Vergleicht Fehlermuster zwischen dem gestrigen und dem heutigen Log.
awk 'NR==1{start=$0} END{print "First:", start; print "Last:", $0}' <file> — Zeigt den ersten und letzten Log-Eintrag, um den Zeitraum einer Log-Datei zu bestimmen.
awk 'NR==1{start=$0} END{print "First:", start; print "Last:", $0}' access.logawk '{total+=$10; count++} END {printf "Avg: %.0f bytes (%d requests)\n", total/count, count}' access.log — Berechnet die durchschnittliche Antwortgröße über alle Requests.
awk '$NF > <seconds>' <file> — Findet langsame Requests, bei denen das letzte Feld die Antwortzeit enthält.
awk '$NF > 5.0' access.log Fazit
Für Ad-hoc-Auswertungen brauchst du keinen schweren Werkzeugkasten: grep, awk und die übliche sort | uniq -c | sort -rn-Kette decken Top-IPs, Status-Codes, URLs und Bandbreite zuverlässig ab, und tail -f macht daraus ein Live-Monitoring. Sobald die gleichen Auswertungen aber täglich anfallen oder ein Dashboard her soll, lohnt der Umstieg auf ein spezialisiertes Werkzeug wie GoAccess. Denk in jedem Fall daran, die Feld-Indizes an dein tatsächliches Log-Format anzupassen.
Weiterführende Links
- GoAccess – interaktiver Echtzeit-Log-Analyzer für Apache und nginx (englisch)
- GNU-Awk-Handbuch – vollständige Referenz zu awk (englisch)
- grep(1) – Manpage – alle Optionen von GNU grep (englisch)