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

Source: https://www.jpkc.com/db/cheatsheets/web-servers/log-analysis/

<!-- PROSE:intro -->
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.
<!-- PROSE:intro:end -->

## Schneller Überblick

`wc -l <file>` — Zählt die Gesamtzahl der Zeilen in einer Log-Datei.

```bash
wc -l access.log
```

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

```bash
head -n 20 /var/log/syslog
```

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

```bash
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.

```bash
less +F /var/log/syslog
```

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

```bash
file /var/log/syslog.2.gz
```

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

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

## Nach Muster filtern

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

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

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

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

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

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

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

```bash
grep -v 'healthcheck' access.log
```

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

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

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

```bash
grep -c '500' access.log
```

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

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

## Zeitbasiertes Filtern

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

```bash
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.

```bash
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).

```bash
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.

```bash
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.

```bash
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.

```bash
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.

```bash
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).

```bash
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.

```bash
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).

```bash
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.

```bash
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).

```bash
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.

```bash
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.

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

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

```bash
awk '$9 == 404' access.log
```

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

```bash
awk '$9 >= 500' access.log
```

`awk '{print $10}' <file> | paste -sd+ | bc` — Berechnet die gesamte übertragene Byte-Menge (Feld 10 im Combined Log Format).

```bash
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.

```bash
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).

```bash
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.

```bash
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).

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

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

```bash
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).

```bash
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.

```bash
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.

```bash
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.

```bash
tail -f /var/log/app.log | grep --line-buffered 'ERROR'
```

`tail -f <file1> <file2>` — Verfolgt mehrere Log-Dateien gleichzeitig mit Dateinamen-Überschriften.

```bash
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.

```bash
tail -F /var/log/app.log
```

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

```bash
journalctl -f -u nginx
```

`tail -f <file> | awk '$9 >= 400 {print}'` — Überwacht das Access-Log und zeigt in Echtzeit nur Fehler-Antworten.

```bash
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.

```bash
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.

```bash
zcat /var/log/syslog.2.gz
```

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

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

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

```bash
zless /var/log/syslog.3.gz
```

`zcat <files.gz> | grep '<pattern>'` — Sucht über mehrere komprimierte Log-Dateien hinweg.

```bash
zcat /var/log/nginx/access.log.*.gz | grep '500'
```

`cat <current> <(zcat <rotated.gz>) | grep '<pattern>'` — Sucht über aktuelle und rotierte komprimierte Logs gleichzeitig.

```bash
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.

```bash
journalctl -u nginx
```

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

```bash
journalctl -u mysql -n 50
```

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

```bash
journalctl -p err
```

`journalctl -p <priority> --since '<time>'` — Zeigt Einträge einer bestimmten Priorität seit einem gegebenen Zeitpunkt.

```bash
journalctl -p warning --since '1 hour ago'
```

`journalctl --since '<start>' --until '<end>'` — Fragt Einträge innerhalb eines Zeitfensters ab.

```bash
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.

```bash
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).

```bash
journalctl -k -p err
```

## Mehrzeilige Log-Einträge

`grep -A <n> '<pattern>' <file>` — Zeigt eine feste Anzahl Zeilen nach jedem Treffer (z. B. Stacktraces).

```bash
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.

```bash
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.

```bash
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.

```bash
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.

```bash
awk '{print $1}' access.log
```

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

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

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

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

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

```bash
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).

```bash
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.

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

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

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

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

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

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

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

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

```bash
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.

```bash
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.

```bash
awk '$NF > 5.0' access.log
```

<!-- PROSE:outro -->
## 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](https://goaccess.io/) – interaktiver Echtzeit-Log-Analyzer für Apache und nginx (englisch)
- [GNU-Awk-Handbuch](https://www.gnu.org/software/gawk/manual/) – vollständige Referenz zu awk (englisch)
- [grep(1) – Manpage](https://www.gnu.org/software/grep/manual/grep.html) – alle Optionen von GNU grep (englisch)
<!-- PROSE:outro:end -->

## Verwandte Kommandos

- [apache](https://www.jpkc.com/db/cheatsheets/web-servers/apache/) – Webserver, dessen Access- und Error-Logs du hier auswertest
- [caddy](https://www.jpkc.com/db/cheatsheets/web-servers/caddy/) – moderner Webserver mit eigenem (oft JSON-)Log-Format
- [certbot](https://www.jpkc.com/db/cheatsheets/web-servers/certbot/) – TLS-Zertifikate verwalten, deren Erneuerungen im Log auftauchen

