Files
CNWei dd7612bc74 feat: 编辑器实时探测并显示已关联虚拟环境
fix: 按系统 ANSI 代码页解码子进程输出,修复中文日志乱码
2026-10-24 22:30:10 +08:00

33 lines
2.3 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# ADR-0004:运行日志的输出编码
- 状态:已接受
- 日期:2026-10-24
## 背景
任务日志原先按 UTF-8 无损方式(`String::from_utf8_lossy`)解码子进程输出。中文 Windows 上多数程序并不输出 UTF-8:Python 默认使用系统 ANSI 代码页(简体中文为 GBK/936),`cmd.exe`、部分 CLI 使用控制台代码页。结果日志里出现成片的 `����`,例如 `release/TaskPilot/logs/copyparty/1.log` 中真实的 `copyparty ������ (PID: 22056)`。
同一处还有两个次要问题:读取日志按 64 KiB 切块时可能切开多字节字符;终态任务只要日志超过 64 KiB,前端拿到首块就停止轮询,导致尾部丢失。
## 决策
1. 在写入侧解码:子进程每读完一行就解码为 UTF-8 再落盘,日志文件因此始终是合法 UTF-8,字节 offset 语义不变。
2. 默认 `auto`:先按 UTF-8 严格解码,失败时回退到系统 ANSI 代码页(`GetACP`),并按代码页映射到 `encoding_rs` 编码(936→GBK、950→Big5、932→Shift_JIS、949→EUC-KR、125x→windows-125x)。
3. 允许用 `TASK_PILOT_LOG_ENCODING` 指定编码标签(如 `gbk`、`utf-8`、`windows-1251`);显式指定时不再嗅探,行为完全确定。
4. HTTP 任务响应体沿用同一解码器。
5. 读取侧只做 UTF-8 边界对齐:块尾落在多字节字符中间时,把不完整字节留给下一次读取;只有真正非法的字节才替换为 `U+FFFD`。
6. `complete` 改为"运行已结束且已读到文件末尾",修复长日志被截断的问题。
## 原因
- 解码放在写入侧,只需处理一次,SSE、增量读取、下载、`grep` 得到的内容完全一致。
- 先 UTF-8 再回退,是因为 UTF-8 合法序列有强约束,误判概率低;反过来(先 GBK)会把大量 UTF-8 日志解坏。
- 使用系统代码页而不是硬编码 GBK,保证非中文 Windows 上的行为同样正确。
- 逐行解码避免多字节字符被缓冲区边界切开,且不受行尾差异影响。
## 边界
- 逐行启发式无法覆盖"一行内混用多种编码",也无法识别恰好构成合法 UTF-8 的 ANSI 字节序列;这两种情况需要显式设置 `TASK_PILOT_LOG_ENCODING`。
- 修复前写入的历史日志已经是替换字符,不会恢复。
- 非 Windows 平台回退编码为 UTF-8,不做代码页探测。