diff --git a/scripts/apache-log-survey.py b/scripts/apache-log-survey.py index 15056de..9bc83ad 100644 --- a/scripts/apache-log-survey.py +++ b/scripts/apache-log-survey.py @@ -17,6 +17,7 @@ import glob import os import re import sys +import time from collections import Counter, defaultdict from datetime import datetime, timezone @@ -68,11 +69,54 @@ def parse_ts(raw): def main(): + # ---------------------------------------------------------------------- + # FORTSCHRITT SICHTBAR MACHEN — sonst sieht der Scan wie ein Haenger aus. + # + # Real passiert (x22): der Aufruf war `python3 survey.py 2>&1 | tee bestand.txt`. + # Das Skript gibt ALLES erst am Ende aus (alle print() stehen hinter der + # Scan-Schleife), und Python puffert stdout in eine PIPE blockweise (4-8 KB, + # nicht zeilenweise). Die Gesamtausgabe ist aber nur ~1 KB — sie landet also + # erst beim Prozessende in der Datei. Bei 2 Mio Zeilen gemessen: 41 s lang + # 0 Bytes, dann alles auf einmal. Auf einem KeyHelp-Host mit tausenden + # Logdateien dauert das Minuten und liest sich wie ein Absturz. + # + # Drei Gegenmaßnahmen: + # 1. Zeilenpufferung erzwingen (reconfigure), damit jede Zeile sofort raus ist + # 2. einen Startbanner VOR dem Scan ausgeben + # 3. pro Datei eine Fortschrittszeile (mit flush) + # ---------------------------------------------------------------------- + try: + sys.stdout.reconfigure(line_buffering=True) + sys.stderr.reconfigure(line_buffering=True) + except (AttributeError, ValueError): + pass # aeltere Python-Version: dann eben gepuffert + files = sorted({f for pat in PATTERNS for f in glob.glob(pat)}) if not files: print("Keine Apache-Logs gefunden. Laeuft Apache? (apache2ctl -v)") return 1 + total_bytes = 0 + for _f in files: + try: + total_bytes += os.path.getsize(_f) + except OSError: + pass + + def human(n): + for u in ("B", "KB", "MB", "GB"): + if n < 1024: + return f"{n:.1f} {u}" + n /= 1024 + return f"{n:.1f} TB" + + print("=" * 72) + print("APACHE-LOG BESTANDSAUFNAHME") + print("=" * 72) + print(f"Scanne {len(files)} Dateien ({human(total_bytes)} auf Platte) …") + print("(grosse Bestaende dauern; dieser Fortschritt laeuft mit)") + print() + total_lines = 0 total_parsed = 0 total_err = 0 @@ -88,14 +132,17 @@ def main(): day_domain = set() per_file = [] - for path in files: + t0 = time.time() + for _idx, path in enumerate(files, 1): fh = open_maybe_gz(path) if fh is None: + print(f" [{_idx}/{len(files)}] NICHT LESBAR: {path}", flush=True) continue try: - bytes_on_disk += os.path.getsize(path) + _sz = os.path.getsize(path) + bytes_on_disk += _sz except OSError: - pass + _sz = 0 domain = os.path.basename(os.path.dirname(path)) or os.path.basename(path) n_lines = n_ok = n_err = 0 f_first = f_last = None @@ -137,18 +184,14 @@ def main(): total_parsed += n_ok total_err += n_err per_file.append((path, n_lines, n_ok, n_err, f_first, f_last)) + # Fortschritt pro Datei — und bei grossen Dateien auch bei jeder Zeile + # die Marke, damit lange Einzeldateien nicht stumm wirken. + print(f" [{_idx}/{len(files)}] {n_lines:>10,} Zeilen " + f"{human(_sz):>9} {path}", flush=True) - def human(n): - for u in ("B", "KB", "MB", "GB"): - if n < 1024: - return f"{n:.1f} {u}" - n /= 1024 - return f"{n:.1f} TB" - - print("=" * 72) - print("APACHE-LOG BESTANDSAUFNAHME") - print("=" * 72) - print(f"Dateien: {len(files)} ({human(bytes_on_disk)} auf Platte)") + print() + print(f"Scan fertig in {time.time() - t0:.1f} s.") + print() print(f"Zeilen total: {total_lines:,}") print(f" Zugriffe: {total_parsed:,}") print(f" Fehlerlog: {total_err:,}")