# -*- coding: utf-8 -*- """统一调试日志 + 调试器窗口控制协议(纯文件通信,与主窗口 UI 零耦合) 日志文件: data/debug_session.log (环境变量 HAOCODE_DEBUG_LOG 可覆盖) 控制文件: data/debug_window.cmd (环境变量 HAOCODE_DEBUG_CMD 可覆盖) 三方写入协议: [USER] 用户在调试窗口输入框手输的观察情况 [AGENT] 代理(外部脚本/命令行)注入的指令与备注 [APP] 应用自身事件(上下文标签变化/usage/发送/完成/报错/压缩) [SYS] 调试窗口自身的开关事件 行格式: [YYYY-MM-DD HH:MM:SS.mmm] [TAG] 内容 控制协议: 代理往 debug_window.cmd 写入 "show" 或 "hide"(一行), app 侧 2s QTimer 轮询并消费(读完即删)。 """ import os import threading import time _ROOT = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) _DATA_DIR = os.path.join(_ROOT, "data") DEBUG_LOG_PATH = (os.environ.get("HAOCODE_DEBUG_LOG") or os.path.join(_DATA_DIR, "debug_session.log")) DEBUG_CMD_PATH = (os.environ.get("HAOCODE_DEBUG_CMD") or os.path.join(_DATA_DIR, "debug_window.cmd")) _lock = threading.Lock() def _stamp() -> str: t = time.time() return (f"{time.strftime('%Y-%m-%d %H:%M:%S', time.localtime(t))}" f".{int(t * 1000) % 1000:03d}") def debug_log(msg: str, tag: str = "APP") -> None: """线程安全追加一条日志(worker 线程亦可调用)。 静默吞掉一切异常——本模块绝不影响主流程。""" try: with _lock: with open(DEBUG_LOG_PATH, "a", encoding="utf-8") as f: f.write(f"[{_stamp()}] [{tag}] {msg}\n") except Exception: pass def poll_debug_cmd(): """消费控制文件,返回 'show' / 'hide' / None。由主线程 QTimer 周期调用。""" try: if os.path.exists(DEBUG_CMD_PATH): with open(DEBUG_CMD_PATH, "r", encoding="utf-8") as f: action = (f.read() or "").strip().lower() try: os.remove(DEBUG_CMD_PATH) except Exception: pass if action in ("show", "hide"): return action except Exception: pass return None def autostart_debug_window(cfg: dict) -> bool: """调试窗口随程序启动:cfg["debug_window_autostart"] 为 true(缺省也是 true) 时向控制文件写 "show",主窗口事件循环启动后 2s 轮询即开窗。 纯文件操作(不依赖 Qt),返回是否写入。""" try: if not bool(cfg.get("debug_window_autostart", True)): return False with open(DEBUG_CMD_PATH, "w", encoding="utf-8") as f: f.write("show\n") return True except Exception: return False