From 3154dd5518ee79aa4245b1d95266333d9db0f1fb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E8=96=87=E8=96=87=E5=AE=89?= Date: Thu, 22 Jan 2026 23:35:35 +0800 Subject: [PATCH] a --- backend/api/supervisor_account.py | 46 +++++++++++++++++++++++++++++-- 1 file changed, 43 insertions(+), 3 deletions(-) diff --git a/backend/api/supervisor_account.py b/backend/api/supervisor_account.py index 9eb8fae..cb0d288 100644 --- a/backend/api/supervisor_account.py +++ b/backend/api/supervisor_account.py @@ -277,7 +277,8 @@ def parse_supervisor_status(raw: str) -> Tuple[bool, Optional[int], str]: def tail_supervisor(program: str, stream: str = "stderr", lines: int = 120) -> str: """ 读取 supervisor 进程最近日志(stdout/stderr)。 - supervisorctl tail - [stdout|stderr] + ⚠️ 修复:优先直接读取日志文件,避免 XML-RPC 编码错误。 + 如果 supervisorctl tail 失败(编码错误),回退到直接读取文件。 """ s = (stream or "stderr").strip().lower() if s not in {"stdout", "stderr"}: @@ -287,13 +288,38 @@ def tail_supervisor(program: str, stream: str = "stderr", lines: int = 120) -> s n = 20 if n > 500: n = 500 - return run_supervisorctl(["tail", f"-{n}", str(program), s]) + + # 优先尝试通过 supervisorctl tail(正常情况) + try: + return run_supervisorctl(["tail", f"-{n}", str(program), s]) + except Exception as e: + # 如果 supervisorctl tail 失败(可能是编码错误),尝试直接读取日志文件 + error_msg = str(e) + if "UnicodeDecodeError" in error_msg or "utf-8" in error_msg.lower() or "codec" in error_msg.lower(): + # 尝试从程序名解析 account_id(例如 auto_sys_acc4 -> 4) + try: + m = re.match(r"^auto_sys_acc(\d+)$", program) + if m: + account_id = int(m.group(1)) + project_root = _get_project_root() + log_dir, out_log, err_log = expected_trading_log_paths(project_root, account_id) + # 根据 stream 选择对应的日志文件 + log_file = out_log if s == "stdout" else err_log + if log_file.exists(): + # 直接读取文件,使用宽松的编码处理 + return _tail_text_file(log_file, lines=n) + except Exception: + pass + + # 如果所有尝试都失败,返回错误信息(但不要抛出异常,避免影响主流程) + return f"[读取日志失败: {error_msg}]" def _tail_text_file(path: Path, lines: int = 200, max_bytes: int = 64 * 1024) -> str: """ 读取文本文件末尾(用于 supervisor spawn error 等场景,program stderr 可能为空)。 尽量只读最后 max_bytes,避免大文件占用内存。 + ⚠️ 修复:使用更宽松的编码处理,支持中文等多字节字符。 """ try: p = Path(path) @@ -305,7 +331,21 @@ def _tail_text_file(path: Path, lines: int = 200, max_bytes: int = 64 * 1024) -> if size > read_size: f.seek(-read_size, os.SEEK_END) data = f.read() - text = data.decode("utf-8", errors="ignore") + + # ⚠️ 修复:尝试多种编码,优先 UTF-8,失败则尝试常见中文编码 + text = None + encodings = ["utf-8", "gbk", "gb2312", "gb18030", "latin1"] + for enc in encodings: + try: + text = data.decode(enc, errors="strict") + break + except (UnicodeDecodeError, LookupError): + continue + + # 如果所有编码都失败,使用 errors="ignore" 强制解码(会丢失部分字符但不会报错) + if text is None: + text = data.decode("utf-8", errors="ignore") + # 仅保留最后 N 行 parts = text.splitlines() if not parts: