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.log

head -n <count> <file> — Zeigt die ersten N Zeilen, um das Log-Format zu verstehen.

head -n 20 /var/log/syslog

tail -n <count> <file> — Zeigt die letzten N Log-Einträge.

tail -n 50 /var/log/nginx/error.log

less +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/syslog

file <logfile> — Erkennt, ob eine Log-Datei Klartext, gzip-komprimiert oder ein anderes Format ist.

file /var/log/syslog.2.gz

du -sh <file> — Prüft die Größe einer Log-Datei vor der Verarbeitung.

du -sh /var/log/nginx/access.log

Nach Muster filtern

grep '<pattern>' <file> — Findet alle Zeilen, die auf ein Muster passen.

grep 'ERROR' /var/log/app.log

grep -i '<pattern>' <file> — Mustersuche ohne Beachtung der Groß-/Kleinschreibung.

grep -i 'timeout' /var/log/app.log

grep -E 'ERROR|WARN|FATAL' <file> — Findet Zeilen, die auf eines von mehreren Mustern passen.

grep -E 'ERROR|WARN|FATAL' /var/log/app.log

grep -v '<pattern>' <file> — Schließt Zeilen aus, die auf ein Muster passen (invertierter Treffer).

grep -v 'healthcheck' access.log

grep -v -e '<a>' -e '<b>' <file> — Schließt Zeilen aus, die auf mehrere Muster passen.

grep -v -e 'bot' -e 'crawler' -e 'monitoring' access.log

grep -c '<pattern>' <file> — Zählt, wie viele Zeilen auf ein Muster passen.

grep -c '500' access.log

grep -B <n> -A <n> '<pattern>' <file> — Zeigt Kontextzeilen vor und nach jedem Treffer.

grep -B 3 -A 5 'OutOfMemoryError' app.log

Zeitbasiertes Filtern

grep '<date>' <file> — Filtert Log-Einträge für ein bestimmtes Datum.

grep '2026-02-16' /var/log/app.log

grep '<date>.*<time_prefix>' <file> — Filtert Einträge für ein bestimmtes Datum und eine bestimmte Stunde.

grep '16/Feb/2026:14' access.log

awk '$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.log

sed -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.log

awk '/^<start>/,/^<end>/' <file> — Extrahiert mit awk einen Zeilenbereich zwischen zwei passenden Mustern.

awk '/^2026-02-16 14:00/,/^2026-02-16 15:00/' app.log

journalctl --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 -20

awk '{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 -rn

grep -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 | head

awk '{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 -rn

grep -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 -rn

awk '{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 -20

awk '{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 -20

awk '$9 == <code>' <file> — Filtert Requests nach einem bestimmten HTTP-Status-Code.

awk '$9 == 404' access.log

awk '$9 >= 500' <file> — Findet alle Server-Fehler (5xx-Status-Codes).

awk '$9 >= 500' access.log

awk '{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+ | bc

awk '{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.log

awk -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 -10

awk '$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 -20

Requests 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 -c

awk '{print substr($4, 2, 14)}' <file> | uniq -c — Zählt Requests pro Stunde.

awk '{print substr($4, 2, 14)}' access.log | uniq -c

awk '{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 -10

awk '$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 -10

Echtzeit-Monitoring

tail -f <file> — Verfolgt eine Log-Datei in Echtzeit, während neue Einträge angehängt werden.

tail -f /var/log/nginx/error.log

tail -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.log

tail -F <file> — Verfolgt mit Wiederholung. Folgt dem Log auch nach einer Log-Rotation weiter.

tail -F /var/log/app.log

journalctl -f -u <service> — Verfolgt die systemd-Journal-Ausgabe für einen bestimmten Dienst.

journalctl -f -u nginx

tail -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"; done

Komprimierte & rotierte Logs

zcat <file.gz> — Zeigt den vollständigen Inhalt einer gzip-komprimierten Log-Datei.

zcat /var/log/syslog.2.gz

zgrep '<pattern>' <file.gz> — Sucht in gzip-komprimierten Log-Dateien, ohne sie zu entpacken.

zgrep 'ERROR' /var/log/app.log.*.gz

zless <file.gz> — Durchblättert eine gzip-komprimierte Log-Datei interaktiv mit einem Pager.

zless /var/log/syslog.3.gz

zcat <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 nginx

journalctl -u <service> -n <count> — Zeigt die letzten N Einträge für einen Dienst.

journalctl -u mysql -n 50

journalctl -p <priority> — Filtert nach Prioritätsstufe: emerg, alert, crit, err, warning, notice, info, debug.

journalctl -p err

journalctl -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 -100

journalctl --disk-usage — Zeigt, wie viel Speicherplatz das Journal belegt.

journalctl -k — Zeigt nur Kernel-Meldungen (entspricht dmesg).

journalctl -k -p err

Mehrzeilige 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.log

awk '/<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.log

grep -Pzo '(?s)<start>.*?<end>' <file> — Extrahiert mehrzeilige Blöcke mit Perl-kompatibler Regex.

grep -Pzo '(?s)Exception.*?\n\n' app.log

awk '/^[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.log

Feld-Extraktion mit awk

awk '{print $<n>}' <file> — Gibt ein bestimmtes Feld (eine Spalte) aus jeder Log-Zeile aus.

awk '{print $1}' access.log

awk -F'<sep>' '{print $<n>}' <file> — Teilt Zeilen an einem eigenen Feldtrenner und extrahiert ein Feld.

awk -F'|' '{print $3}' app.log

awk -F'"' '{print $2}' <file> — Extrahiert die Request-Zeile aus dem Apache-/nginx-Combined-Log-Format.

awk -F'"' '{print $2}' access.log

awk '{print $NF}' <file> — Gibt das letzte Feld jeder Zeile aus.

awk '{print $NF}' access.log

awk '{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/syslog

Sortieren & Deduplizieren

sort <file> | uniq — Entfernt doppelte Zeilen aus sortierter Ausgabe.

awk '{print $7}' access.log | sort | uniq

sort -u <file> — Sortiert und entfernt Duplikate in einem Schritt.

awk '{print $1}' access.log | sort -u

sort <file> | uniq -c | sort -rn — Zählt Vorkommen und sortiert nach Häufigkeit (absteigend).

awk '{print $7}' access.log | sort | uniq -c | sort -rn

sort <file> | uniq -d — Zeigt nur Zeilen, die mehr als einmal vorkommen (nur Duplikate).

awk '{print $1}' access.log | sort | uniq -d

sort -t'<sep>' -k<field> -rn <file> — Sortiert numerisch nach einem bestimmten Feld in umgekehrter Reihenfolge.

du -sh /var/log/*.log | sort -t'\t' -k1 -rh

Praktische 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.log

awk '{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.

Verwandte Kommandos

  • apache – Webserver, dessen Access- und Error-Logs du hier auswertest
  • caddy – moderner Webserver mit eigenem (oft JSON-)Log-Format
  • certbot – TLS-Zertifikate verwalten, deren Erneuerungen im Log auftauchen