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 os
|
||||||
import re
|
import re
|
||||||
import sys
|
import sys
|
||||||
|
import time
|
||||||
from collections import Counter, defaultdict
|
from collections import Counter, defaultdict
|
||||||
from datetime import datetime, timezone
|
from datetime import datetime, timezone
|
||||||
|
|
||||||
@@ -68,11 +69,54 @@ def parse_ts(raw):
|
|||||||
|
|
||||||
|
|
||||||
def main():
|
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)})
|
files = sorted({f for pat in PATTERNS for f in glob.glob(pat)})
|
||||||
if not files:
|
if not files:
|
||||||
print("Keine Apache-Logs gefunden. Laeuft Apache? (apache2ctl -v)")
|
print("Keine Apache-Logs gefunden. Laeuft Apache? (apache2ctl -v)")
|
||||||
return 1
|
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_lines = 0
|
||||||
total_parsed = 0
|
total_parsed = 0
|
||||||
total_err = 0
|
total_err = 0
|
||||||
@@ -88,14 +132,17 @@ def main():
|
|||||||
day_domain = set()
|
day_domain = set()
|
||||||
per_file = []
|
per_file = []
|
||||||
|
|
||||||
for path in files:
|
t0 = time.time()
|
||||||
|
for _idx, path in enumerate(files, 1):
|
||||||
fh = open_maybe_gz(path)
|
fh = open_maybe_gz(path)
|
||||||
if fh is None:
|
if fh is None:
|
||||||
|
print(f" [{_idx}/{len(files)}] NICHT LESBAR: {path}", flush=True)
|
||||||
continue
|
continue
|
||||||
try:
|
try:
|
||||||
bytes_on_disk += os.path.getsize(path)
|
_sz = os.path.getsize(path)
|
||||||
|
bytes_on_disk += _sz
|
||||||
except OSError:
|
except OSError:
|
||||||
pass
|
_sz = 0
|
||||||
domain = os.path.basename(os.path.dirname(path)) or os.path.basename(path)
|
domain = os.path.basename(os.path.dirname(path)) or os.path.basename(path)
|
||||||
n_lines = n_ok = n_err = 0
|
n_lines = n_ok = n_err = 0
|
||||||
f_first = f_last = None
|
f_first = f_last = None
|
||||||
@@ -137,18 +184,14 @@ def main():
|
|||||||
total_parsed += n_ok
|
total_parsed += n_ok
|
||||||
total_err += n_err
|
total_err += n_err
|
||||||
per_file.append((path, n_lines, n_ok, n_err, f_first, f_last))
|
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):
|
print()
|
||||||
for u in ("B", "KB", "MB", "GB"):
|
print(f"Scan fertig in {time.time() - t0:.1f} s.")
|
||||||
if n < 1024:
|
print()
|
||||||
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(f"Zeilen total: {total_lines:,}")
|
print(f"Zeilen total: {total_lines:,}")
|
||||||
print(f" Zugriffe: {total_parsed:,}")
|
print(f" Zugriffe: {total_parsed:,}")
|
||||||
print(f" Fehlerlog: {total_err:,}")
|
print(f" Fehlerlog: {total_err:,}")
|
||||||
|
|||||||
Reference in new issue
Block a user