Files
Unlucky-Launcher/docs/logs-location.md
T
worker-logplace 8c5bebf3e3 fix(m188): r1 复审 P2-1——后端启动失败的具名原因进 launcher.log
复审结论 p2-1/1 item + fix-then-merge:主线成立,但「启动器自己因 GPU / 渲染后端起不来」的
事故原因仍只进 stderr —— 双击启动没有 stderr,那条路径上等于没有日志。

- renderer/mod.rs::report_failure 的两行(`[gpu] <backend> 初始化失败(具名)` /
  `[gpu] 失败详情(具名)`)改走 diag!;
- renderer/log_bridge.rs 转出的 wgpu 日志(含 `…Selected: AdapterInfo…`,GPU 真生效的唯一证据
  行)改走 diag!;
- renderer/preflight.rs 的 `[gpu-preflight]`(ULL_PREFLIGHT=1)改走 diag!;
- probe.rs::apply_with_preflight 的三行 `[probe]` 改走 diag!。
- app/diag.rs 模块文档 + docs/logs-location.md §3 的判据改准:
  「逐帧度量不接;显式取证开关与一次性事故行可接」,并修掉 `[corner]` 两处自相矛盾
  (唯一打印点在 app/update/appearance.rs,走 diag! ⇒ 进日志;corner.rs 只 format_line)。
- 新守门 renderer::tests::a_backend_startup_failure_lands_in_the_launcher_log:
  断言那两行真的落进 launcher.log;变异(改回纯 eprintln)→ 红。
- README 两语的 launcher.log 行补上「渲染后端起不来的具名原因」。
2026-11-09 01:05:40 +08:00

7.1 KiB
Raw Blame History

日志在哪(M188)

一句话:启动器自己写的东西全在 <数据根>/logs/ 一个目录里,游戏自己的东西在实例运行目录里。 设置页那行「日志文件夹」显示的就是前者的绝对路径,旁边有 打开文件夹 与 复制路径;日志页页头 也有 打开文件夹。两个入口走同一个落点(crates/app-iced/src/app/logs_folder.rs),失败时输出的 文本带完整路径。

1. 三个数据模式下的绝对路径

<数据根> 由 core::platform::data_dir 的三模式解析(便携 / 标准 / 自定义):

模式 启动器数据目录(= <数据根>) 日志根
portable <exe 目录>/.ULL <exe 目录>/.ULL/logs
standard Windows %APPDATA%\ULL · macOS ~/Library/Application Support/ULL · Linux $XDG_DATA_HOME/ULL(缺省 ~/.local/share/ULL) <上者>/logs
custom <自定义根>/.ULL <自定义根>/.ULL/logs

GUI 的 --data-dir <dir> 是 custom 模式的一种入口:那时日志根是 <dir>/.ULL/logs。设置页显示的 路径与 launcher.log 的实际落点取的是同一份解析结果(SettingsBridge → <home>/config/launcher.json → <home>),所以两者不会各说各话。

2. 目录里有什么

路径 写者 是什么
logs/launcher.log core::launch::launcher_log 启动器自己的运行日志:启动拍(主题 / 语言 / 路由 / 窗口)、后端探测与降级、字体链、每一次启动的命令行事实 / spawn 结果 / 退出码、打开日志目录等用户动作的结果。
logs/launcher.1.log 同上(滚动) 上一份日志。只留一份历史。
logs/game/<session-id>.jsonl core::launch::session 每次启动一份 JSONL 事件流(命令行已脱敏、每行 stdout/stderr、退出、诊断)。
logs/game/index.jsonl core::launch::session_index 一会话一行摘要的索引,日志页左栏读它。
logs/install.log presenter::host::install_journal 安装流水,每阶段一行。
logs/session-log-location.json presenter::host::logs::note_location 会话日志现在在哪 / 以前在哪 / 搬没搬(moved: 0),每次启动重写。
data/agent/sessions/game/… 旧版本 只读兼容,见 §4。

上限与滚动

  • 单文件上限 launcher_log::MAX_BYTES = 1 MiB;每次追加前检查,超过就把 launcher.log 改名成 launcher.1.log(旧的那份被替换),新文件从 0 开始。
  • 保留份数:1。所以 logs/ 里启动器日志的稳态占用 ≤ 约 2 MiB(+ 同目录的会话 jsonl)。
  • 为什么不做按日期滚动:事故复现往往在「刚刚那一次」,份数比历史长度更重要;而无限份数会让「导出 诊断包」变成搬砖。

3. 哪些行进 launcher.log、哪些只进 stderr

判据(复审后收紧成这一句):逐帧度量不接;显式取证开关与一次性事故行可接。实现是 crates/app-iced/src/app/diag.rs 的 diag! 宏(stderr + 文件双写,行首 [tag] 就是日志里的 scope)。

进 launcher.log 只进 stderr
启动编排:[probe](request=… -> ICED_BACKEND=…、降级重开、探测耗时)、[font]、[theme]、[appearance]、[gpu] 的降级与拒绝降级、[boot](含后端 request/effective)、[shot] 的通道提示 验收/出图矩阵的逐帧度量:[window] / [perf] / [caps] / [theme] source=…(renderer::acceptance/**,一次启动上百行,会冲掉事故现场)
一次性事故行:[gpu] <backend> 初始化失败(具名) 与 [gpu] 失败详情(具名)(renderer::report_failure,复审 P2-1)、[gpu] <target>: …Selected: AdapterInfo…(renderer/log_bridge.rs 转出的 wgpu 日志)、[gpu-preflight](ULL_PREFLIGHT=1) 组件几何探针的断言输出(components/** 的 [feedback-probe]、gallery 页面里的 [gallery])
用户动作的结果:[logs](打开日志文件夹成功/失败)、[settings] 写盘失败、[i18n] 语言没落盘、[update] 的忽略/重启、[agent] / [content] / [page] 的落点提示 每条日志行的正文里再套的括号(不是标签)
显式开关下的一次性行:[tick](ULL_TICK_TRACE=1,≈1 行/秒,约 4.8 小时才滚一次)、[corner](app/update/appearance.rs 打,platform/win32/corner.rs 只负责 format_line)、[appearance] / [theme] 的切换 —

[corner] 到底在哪一侧,看打印点:唯一打印点是 app/update/appearance.rs(走 diag!), platform/win32/corner.rs::format_line 只拼字符串、不打印 ⇒ 它进 launcher.log。

3.1 还没接进日志的行(本 mission 之外)

  • renderer::acceptance/** 的 [window] / [perf] / [caps]:逐帧/矩阵取证,按上面的判据故意不接。
  • components/** 的几何探针([feedback-probe] / [gallery]):测试通道,不进产品日志。

(renderer/mod.rs 的 report_failure、renderer/log_bridge.rs、renderer/preflight.rs、 probe.rs:apply_with_preflight 已在 M188 复审后接上。)

4. 会话日志搬家:策略是不搬

M188 把新会话从 data/agent/sessions/game/ 挪到 logs/game/(目录名里的 agent 是历史遗留, 它从来只装游戏会话)。旧目录的处理方式:

  • 原样保留、继续读:presenter::host::logs 与 cli 的会话读取都是两个根合并,同一个 session_id 同时存在时以 logs/ 那条为准;
  • 不搬、不删:搬运用户已有数据是不可逆动作,中途失败会留下半份会话,崩溃弹窗里的会话关联也会断;
  • 留记录:logs/session-log-location.json 写明两个根、旧根在不在、旧根里几个文件、moved: 0、 policy: "read-only-compat"。

5. 游戏自己的日志(不归启动器写)

在实例的运行目录里(隔离实例是 <游戏目录>/versions/<隔离名>/…,共享实例直接是 .minecraft):

  • logs/latest.log、logs/<日期>-<序号>.log.gz —— 游戏自己的日志;
  • crash-reports/crash-*.txt —— 只在 JVM 活到崩溃处理器那一步时才有;
  • hs_err_pid*.log —— JVM 自己崩(native crash)时才写。

模组 / 缺库 / Java 选错把游戏弄死在 Minecraft 初始化之前时,上面三个都不会出现——那不是启动器 丢了日志,而是游戏没机会写。那种时候的证据是 logs/game/<session-id>.jsonl 与 logs/launcher.log。

6. 没验的部分

  • 真机「双击启动后日志确实在」:本轮只在开发机上用 cargo test 与本地进程验证,Windows 桌面 双击启动需要用户实测。
  • macOS / Linux 的日志根:路径由同一条解析链给出(standard_launcher_home 有单测覆盖三个平台), 但「Mac 上点打开文件夹真的会开 Finder」「Linux 上没有 xdg-open 时会怎样」没有真机验证。
  • 只读数据目录:日志写不进去时按设计静默放过(best-effort),此时设置页显示的路径仍然正确, 但文件不存在。