import logging import requests import threading import time import os import atexit 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 LOG_SENT = False def init_network_logging(enabled: bool = True): global ENABLED ENABLED = enabled if ENABLED: atexit.register(send_full_log) def log_to_discord(level: str, message: str, module: str = "general"): if not ENABLED: return timestamp = datetime.utcnow().isoformat() log_entry = { "timestamp": timestamp, "level": level.upper(), "module": module, "message": message, } with QUEUE_LOCK: FULL_LOG.append(log_entry) # If it's an error, try to send it immediately as a message too 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( target=send_discord_message, args=(formatted_msg,), daemon=False ) t.start() def flush_logs(): pass def send_full_log(): """Sends the entire session log as a text file attachment at the end.""" global LOG_SENT if not ENABLED or LOG_SENT: return LOG_SENT = True # Give a tiny bit of time for final async logs to land time.sleep(0.5) with QUEUE_LOCK: logs_to_send = FULL_LOG.copy() 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) 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() def send_discord_message(content: str): try: payload = {"content": content} requests.post(DISCORD_WEBHOOK_URL, json=payload, timeout=5) except Exception as e: print(f"Discord webhook error: {e}") class DiscordLogHandler(logging.Handler): def __init__(self, module_name: str = "general"): super().__init__() self.module_name = module_name def emit(self, record: logging.LogRecord): if record.levelno < logging.INFO: return level_map = { logging.INFO: "INFO", logging.WARNING: "WARN", logging.ERROR: "ERROR", logging.CRITICAL: "CRITICAL", } level = level_map.get(record.levelno, "INFO") message = self.format(record) module_name = ( self.module_name if self.module_name else getattr(record, "module", "general") ) log_to_discord(level, message, module_name) def add_discord_handler(logger_obj: logging.Logger, module: str = None): module_name = ( module if module else (logger_obj.name if logger_obj.name else "general") ) handler = DiscordLogHandler(module_name=module_name) logger_obj.addHandler(handler)