feat: add error_summary() helper to replace verbose exception tracebacks with concise German error messages
Add error_summary() function to format exceptions as single-line log messages with 240-char limit and special handling for requests.RequestException as "Dienst nicht erreichbar". Replace log.exception() calls with log.error() using error_summary() across service_monitor, scan_datastore_storage, sab_downloads, scan_one, start_scan, index, downloads, system, and datastore endpoints. Update user-facing error messages to German "momentan nicht verfügbar/
This commit is contained in:
@@ -50,6 +50,13 @@ LOG_RETENTION_HOURS = max(1, int(os.environ.get("LOG_RETENTION_HOURS", "6")))
|
||||
|
||||
logging.basicConfig(level=os.environ.get("LOG_LEVEL", "INFO").upper(), format="%(asctime)s %(levelname)s %(message)s")
|
||||
log = logging.getLogger("media-max")
|
||||
|
||||
|
||||
def error_summary(exc):
|
||||
"""Return one short, useful log line instead of a connection traceback."""
|
||||
if isinstance(exc, requests.RequestException):
|
||||
return f"{exc.__class__.__name__}: Dienst nicht erreichbar"
|
||||
return " ".join(str(exc).split())[:240] or exc.__class__.__name__
|
||||
try:
|
||||
file_handler = TimedRotatingFileHandler(LOG_FILE, when="H", interval=LOG_RETENTION_HOURS, backupCount=1, encoding="utf-8", delay=True, utc=True)
|
||||
file_handler.setFormatter(logging.Formatter("%(asctime)s %(levelname)s %(message)s"))
|
||||
@@ -147,8 +154,8 @@ def service_monitor():
|
||||
while True:
|
||||
try:
|
||||
refresh_services()
|
||||
except Exception:
|
||||
log.exception("Dienststatus konnte nicht aktualisiert werden")
|
||||
except Exception as exc:
|
||||
log.error("Dienststatus konnte nicht aktualisiert werden: %s", error_summary(exc))
|
||||
time.sleep(max(5, SERVICE_REFRESH_SECONDS))
|
||||
|
||||
threading.Thread(target=service_monitor, name="service-monitor", daemon=True).start()
|
||||
@@ -237,8 +244,8 @@ def scan_datastore_storage():
|
||||
category["files"] += 1
|
||||
usage = shutil.disk_usage(root)
|
||||
except OSError as exc:
|
||||
log.exception("Datastore konnte nicht gelesen werden: %s", DATASTORE_PATH)
|
||||
return {"error": f"Datastore konnte nicht gelesen werden: {exc}", "path": DATASTORE_PATH, "categories": list(categories.values())}
|
||||
log.error("Datastore konnte nicht gelesen werden (%s): %s", DATASTORE_PATH, error_summary(exc))
|
||||
return {"error": "Datastore momentan nicht verfügbar", "path": DATASTORE_PATH, "categories": list(categories.values())}
|
||||
used = usage.used
|
||||
values = []
|
||||
for category in categories.values():
|
||||
@@ -552,8 +559,8 @@ def sab_downloads():
|
||||
try:
|
||||
if RADARR_API_KEY: wanted.extend({"title": x.get("movie", {}).get("title") or x.get("title", ""), "source": "Radarr"} for x in api_get(RADARR_URL, RADARR_API_KEY, "queue?includeUnknownMovieItems=true").get("records", []))
|
||||
if SONARR_URL and SONARR_API_KEY: wanted.extend({"title": x.get("title", ""), "source": "Sonarr"} for x in api_get(SONARR_URL, SONARR_API_KEY, "queue?includeUnknownSeriesItems=true").get("records", []))
|
||||
except Exception:
|
||||
log.warning("Radarr/Sonarr-Queue konnte für SAB-Zuordnung nicht geladen werden", exc_info=True)
|
||||
except Exception as exc:
|
||||
log.warning("Radarr/Sonarr-Queue konnte für SAB-Zuordnung nicht geladen werden: %s", error_summary(exc))
|
||||
|
||||
def make_item(item, completed=False):
|
||||
title = item.get("name") or item.get("filename") or item.get("nzb_name") or "—"
|
||||
@@ -649,7 +656,7 @@ def scan_one(path):
|
||||
if not p.exists(): data = {"error": f"Datei nicht gefunden: {p}"}
|
||||
else:
|
||||
try: data = run_ffprobe(str(p)); log.info("Gescannt: %s", p)
|
||||
except Exception as exc: data = {"error": str(exc)}; log.exception("Scanfehler für %s", p)
|
||||
except Exception as exc: data = {"error": str(exc)}; log.error("Scanfehler für %s: %s", p, error_summary(exc))
|
||||
mtime = p.stat().st_mtime
|
||||
con = db(); con.execute("INSERT INTO media_cache(path,mtime,data,scanned_at) VALUES(?,?,?,?) ON CONFLICT(path) DO UPDATE SET mtime=excluded.mtime,data=excluded.data,scanned_at=excluded.scanned_at", (str(p), mtime, json.dumps(data), datetime.utcnow().isoformat())); con.commit(); con.close()
|
||||
return data
|
||||
@@ -751,7 +758,7 @@ def start_scan(source, force=False):
|
||||
with job_lock: jobs[source]["done"] += 1
|
||||
with job_lock: jobs[source]["state"] = "done"
|
||||
except Exception as exc:
|
||||
log.exception("%s-Scan fehlgeschlagen", source); jobs[source]["error"] = str(exc); jobs[source]["state"] = "error"
|
||||
log.error("%s-Scan fehlgeschlagen: %s", source, error_summary(exc)); jobs[source]["error"] = str(exc); jobs[source]["state"] = "error"
|
||||
executor.submit(work)
|
||||
|
||||
@app.route("/")
|
||||
@@ -765,7 +772,7 @@ def index():
|
||||
try:
|
||||
series = sonarr_groups()
|
||||
if any(row["pending"] for group in series for row in group["episodes"]): start_scan("sonarr")
|
||||
except Exception as exc: log.exception("Sonarr nicht erreichbar"); error = f"{error + ' | ' if error else ''}Sonarr: {exc}"
|
||||
except Exception as exc: log.error("Sonarr nicht erreichbar: %s", error_summary(exc)); error = f"{error + ' | ' if error else ''}Sonarr nicht erreichbar"
|
||||
missing_german_movies = [row for row in rows if is_german_review_candidate(row) and not has_german_track(row)]
|
||||
missing_german_series = []
|
||||
for group in series:
|
||||
@@ -821,24 +828,24 @@ def downloads():
|
||||
try:
|
||||
return jsonify(sab_downloads())
|
||||
except Exception as exc:
|
||||
log.exception("SABnzbd konnte nicht abgefragt werden")
|
||||
return jsonify({"items": [], "queue_status": "Fehler", "speed": "—", "paused": False, "active_count": 0, "history_count": 0, "remaining": "—", "error": str(exc)}), 502
|
||||
log.error("SABnzbd konnte nicht abgefragt werden: %s", error_summary(exc))
|
||||
return jsonify({"items": [], "queue_status": "Nicht erreichbar", "speed": "—", "paused": False, "active_count": 0, "history_count": 0, "remaining": "—", "error": "SABnzbd ist momentan nicht erreichbar."}), 502
|
||||
|
||||
@app.get("/api/system")
|
||||
def system():
|
||||
try:
|
||||
return jsonify(system_metrics())
|
||||
except Exception as exc:
|
||||
log.exception("Systemmetriken konnten nicht gelesen werden")
|
||||
return jsonify({"error": str(exc), "cpu_percent": 0, "memory": {}, "disks": []}), 500
|
||||
log.error("Systemmetriken konnten nicht gelesen werden: %s", error_summary(exc))
|
||||
return jsonify({"error": "Systemmetriken momentan nicht verfügbar", "cpu_percent": 0, "memory": {}, "disks": []}), 500
|
||||
|
||||
@app.get("/api/datastore")
|
||||
def datastore():
|
||||
try:
|
||||
return jsonify(datastore_snapshot())
|
||||
except Exception as exc:
|
||||
log.exception("Datastore konnte nicht abgefragt werden")
|
||||
return jsonify({"storage": {"error": str(exc), "path": DATASTORE_PATH, "categories": []}, "raid": {"status": "failed", "label": "FAILED", "error": str(exc)}}), 500
|
||||
log.error("Datastore konnte nicht abgefragt werden: %s", error_summary(exc))
|
||||
return jsonify({"storage": {"error": "Datastore momentan nicht verfügbar", "path": DATASTORE_PATH, "categories": []}, "raid": {"status": "failed", "label": "FAILED", "error": "RAID-Status momentan nicht verfügbar"}}), 500
|
||||
|
||||
@app.get("/health")
|
||||
def health(): return jsonify({"ok":True, "radarr_url":RADARR_URL, "sonarr_enabled":bool(SONARR_URL and SONARR_API_KEY), "sab_enabled":bool(SAB_URL and SAB_API_KEY)})
|
||||
|
||||
Reference in New Issue
Block a user