fix(survey): Fortschritt sichtbar — der leere bestand.txt war kein Fehler, sondern ein laufender Scan
BEFUND (x22, gemessen und reproduziert): Der Aufruf war 'python3 apache-log-survey.py 2>&1 | tee bestand.txt'. Das Skript gab ALLES erst nach der Scan-Schleife aus (alle print() standen dahinter), und Python puffert stdout in eine PIPE blockweise (4-8 KB). Die Gesamtausgabe ist aber nur ~1 KB -> sie landete erst beim Prozessende in der Datei. Messung mit 2 Mio Zeilen (140 MB): mit tee: nach 5 s = 0 Bytes, fertig nach 41 s -> dann 1028 Bytes ohne Pipe: nach 1 s = 0 Bytes python3 -u: nach 1 s = 0 Bytes => Die Datei war leer, weil der Scan LIEF. Auf einem KeyHelp-Host mit tausenden Logdateien dauert das Minuten und liest sich wie ein Haenger oder Absturz. FIX (drei Massnahmen, damit das nie wieder passiert): 1. sys.stdout.reconfigure(line_buffering=True) — jede Zeile geht sofort raus, auch durch eine Pipe. 2. Startbanner VOR dem Scan: Anzahl und Gesamtgroesse der Dateien. 3. Fortschrittszeile pro Datei (Nummer/Gesamtzahl, Zeilen, Groesse, Pfad) plus eine Zeile fuer nicht lesbare Dateien. Verifiziert mit 1,4 Mio Zeilen durch eine Pipe: nach 2 s steht das Banner, drei Fortschrittszeilen erscheinen waehrend des Scans. Gegenprobe mit dem alten Code: nach 3 s 16 Bytes, alles erst am Ende.
This commit is contained in:
1 parent
194fa98f97
commit
df2ee94613
1 file changed
+57
-14
@@ -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:,}")
|
||||
|
||||
Reference in new issue
Block a user