This commit is contained in:
薇薇安 2026-01-22 23:35:35 +08:00
parent fc128f98b4
commit 3154dd5518

View File

@ -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 -<n> <program> [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: