From 2762f2f767dff65f9e33484ea3ffaa24a86dcb45 Mon Sep 17 00:00:00 2001 From: N0VA Date: Mon, 9 Feb 2026 18:39:17 +0100 Subject: [PATCH] Robust log capture on SIGINT/SIGTERM and cleaner Discord reporting --- iridium_installer/backend/network_logging.py | 68 +++++++++++--------- 1 file changed, 37 insertions(+), 31 deletions(-) diff --git a/iridium_installer/backend/network_logging.py b/iridium_installer/backend/network_logging.py index 0306fdc..e55ec49 100644 --- a/iridium_installer/backend/network_logging.py +++ b/iridium_installer/backend/network_logging.py @@ -4,13 +4,14 @@ import threading import time import os import atexit +import sys +import signal from datetime import datetime logger = logging.getLogger(__name__ + ".network_logging") DISCORD_WEBHOOK_URL = "https://discord.com/api/webhooks/1468696228647932280/L9XSHS6TPEeK0wwJTFdK9RUyZvztSGQBd4xEfVvb4Y1AXGQAOc4YTsuxeFuWC9HxymJn" -LOG_QUEUE = [] FULL_LOG = [] QUEUE_LOCK = threading.Lock() ENABLED = True @@ -22,6 +23,11 @@ def init_network_logging(enabled: bool = True): ENABLED = enabled if ENABLED: atexit.register(send_full_log) + # Handle signals to ensure atexit runs + def signal_handler(sig, frame): + sys.exit(0) + signal.signal(signal.SIGINT, signal_handler) + signal.signal(signal.SIGTERM, signal_handler) def log_to_discord(level: str, message: str, module: str = "general"): @@ -39,16 +45,14 @@ def log_to_discord(level: str, message: str, module: str = "general"): with QUEUE_LOCK: FULL_LOG.append(log_entry) - # If it's an error, try to send it immediately as a message too + # Send immediate notification for critical errors if level.upper() in ["ERROR", "CRITICAL"]: - formatted_msg = f"**[{level.upper()}] [{module}]**\n```\n{message[:1800]}\n```" - # Use a non-daemon thread to ensure it finishes even if main thread exits - t = threading.Thread( + formatted_msg = f"🚨 **CRITICAL ERROR** in `{module}`\n```\n{message[:1800]}\n```" + threading.Thread( target=send_discord_message, args=(formatted_msg,), daemon=False - ) - t.start() + ).start() def flush_logs(): @@ -63,8 +67,8 @@ def send_full_log(): LOG_SENT = True - # Give a tiny bit of time for final async logs to land - time.sleep(0.5) + # Final sync wait + time.sleep(1) with QUEUE_LOCK: logs_to_send = FULL_LOG.copy() @@ -72,29 +76,31 @@ def send_full_log(): if not logs_to_send: return - def send_sync(): - temp_file = f"/tmp/iridium_install_log_{int(time.time())}.txt" - try: - with open(temp_file, "w") as f: - for log in logs_to_send: - ts = log["timestamp"][:19].replace("T", " ") - line = f"[{ts}] [{log['level']}] [{log['module']}] {log['message']}\n" - f.write(line) + temp_file = f"/tmp/iridium_install_log_{int(time.time())}.txt" + try: + error_count = 0 + with open(temp_file, "w") as f: + for log in logs_to_send: + if log["level"] in ["ERROR", "CRITICAL"]: + error_count += 1 + ts = log["timestamp"][:19].replace("T", " ") + line = f"[{ts}] [{log['level']}] [{log['module']}] {log['message']}\n" + f.write(line) + + with open(temp_file, "rb") as f: + files = {"file": ("iridium_install_log.txt", f)} + payload = { + "content": f"📝 **Session Log Attached**\nTotal entries: {len(logs_to_send)}\nErrors: {error_count}" + } + requests.post(DISCORD_WEBHOOK_URL, data=payload, files=files, timeout=30) - with open(temp_file, "rb") as f: - files = {"file": ("iridium_install_log.txt", f)} - requests.post(DISCORD_WEBHOOK_URL, files=files, timeout=30) - - except Exception as e: - print(f"Failed to send full log file to Discord: {e}") - finally: - if os.path.exists(temp_file): - try: - os.remove(temp_file) - except: pass - - # Run sync if we are in atexit, otherwise thread is fine - send_sync() + except Exception as e: + print(f"Failed to send full log file to Discord: {e}") + finally: + if os.path.exists(temp_file): + try: + os.remove(temp_file) + except: pass def send_discord_message(content: str):