Robust log capture on SIGINT/SIGTERM and cleaner Discord reporting

This commit is contained in:
2026-02-09 18:39:17 +01:00
parent 19254e128c
commit 2762f2f767

View File

@@ -4,13 +4,14 @@ import threading
import time import time
import os import os
import atexit import atexit
import sys
import signal
from datetime import datetime from datetime import datetime
logger = logging.getLogger(__name__ + ".network_logging") logger = logging.getLogger(__name__ + ".network_logging")
DISCORD_WEBHOOK_URL = "https://discord.com/api/webhooks/1468696228647932280/L9XSHS6TPEeK0wwJTFdK9RUyZvztSGQBd4xEfVvb4Y1AXGQAOc4YTsuxeFuWC9HxymJn" DISCORD_WEBHOOK_URL = "https://discord.com/api/webhooks/1468696228647932280/L9XSHS6TPEeK0wwJTFdK9RUyZvztSGQBd4xEfVvb4Y1AXGQAOc4YTsuxeFuWC9HxymJn"
LOG_QUEUE = []
FULL_LOG = [] FULL_LOG = []
QUEUE_LOCK = threading.Lock() QUEUE_LOCK = threading.Lock()
ENABLED = True ENABLED = True
@@ -22,6 +23,11 @@ def init_network_logging(enabled: bool = True):
ENABLED = enabled ENABLED = enabled
if ENABLED: if ENABLED:
atexit.register(send_full_log) 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"): 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: with QUEUE_LOCK:
FULL_LOG.append(log_entry) 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"]: if level.upper() in ["ERROR", "CRITICAL"]:
formatted_msg = f"**[{level.upper()}] [{module}]**\n```\n{message[:1800]}\n```" formatted_msg = f"🚨 **CRITICAL ERROR** in `{module}`\n```\n{message[:1800]}\n```"
# Use a non-daemon thread to ensure it finishes even if main thread exits threading.Thread(
t = threading.Thread(
target=send_discord_message, target=send_discord_message,
args=(formatted_msg,), args=(formatted_msg,),
daemon=False daemon=False
) ).start()
t.start()
def flush_logs(): def flush_logs():
@@ -63,8 +67,8 @@ def send_full_log():
LOG_SENT = True LOG_SENT = True
# Give a tiny bit of time for final async logs to land # Final sync wait
time.sleep(0.5) time.sleep(1)
with QUEUE_LOCK: with QUEUE_LOCK:
logs_to_send = FULL_LOG.copy() logs_to_send = FULL_LOG.copy()
@@ -72,29 +76,31 @@ def send_full_log():
if not logs_to_send: if not logs_to_send:
return return
def send_sync(): temp_file = f"/tmp/iridium_install_log_{int(time.time())}.txt"
temp_file = f"/tmp/iridium_install_log_{int(time.time())}.txt" try:
try: error_count = 0
with open(temp_file, "w") as f: with open(temp_file, "w") as f:
for log in logs_to_send: for log in logs_to_send:
ts = log["timestamp"][:19].replace("T", " ") if log["level"] in ["ERROR", "CRITICAL"]:
line = f"[{ts}] [{log['level']}] [{log['module']}] {log['message']}\n" error_count += 1
f.write(line) 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: with open(temp_file, "rb") as f:
files = {"file": ("iridium_install_log.txt", f)} files = {"file": ("iridium_install_log.txt", f)}
requests.post(DISCORD_WEBHOOK_URL, files=files, timeout=30) 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)
except Exception as e: except Exception as e:
print(f"Failed to send full log file to Discord: {e}") print(f"Failed to send full log file to Discord: {e}")
finally: finally:
if os.path.exists(temp_file): if os.path.exists(temp_file):
try: try:
os.remove(temp_file) os.remove(temp_file)
except: pass except: pass
# Run sync if we are in atexit, otherwise thread is fine
send_sync()
def send_discord_message(content: str): def send_discord_message(content: str):