Files
Unlucky-Launcher/docs/logs-location.md
T
worker-logplace 5fd5cd47dc feat(m188): launcher.log 早落盘 + 会话日志搬到 logs/game(旧家只读兼容)
用户投诉原话:「我们的启动日志放在哪?…他找半天没找到崩溃日志」「只有一个安装日志」。

启动器运行日志(.ULL/logs/launcher.log)
- presenter 构造 HostEnv 时就登记日志目录:启动早期缓冲的 200 行([probe]/[font]/[theme])
  当场刷盘,不再是「只有开过一局游戏才有日志」。
- app/** 里所有 eprintln! 诊断改走 app::diag::diag!(stderr + 文件双写,行首 [tag] 即 scope),
  含 boot/boot::fallback/state/update 各线。
- boot 那一拍补一行后端事实(request/effective/fallback)。
- 滚动上限(1 MiB → launcher.1.log,只留一份)写进 README 与 docs/logs-location.md。

会话日志搬家:data/agent/sessions → logs
- LauncherLayout: sessions_root()/game_logs() 新增,agent_sessions() 降级为只读兼容。
- host/logs 与 cli/commands/sessions 两个根合并(同 id 新根优先),event_path 回落旧家。
- 不搬不删;日志目录写一份 session-log-location.json 记录(moved=0, policy=read-only-compat)。

入口与文案
- 设置页「通用」新增「日志文件夹」行:路径 + 打开目录 + 复制路径(i18n 中英)。
- 日志页页头的「打开目录」不再是占位:走 M187 的 open_dir,失败文本带路径并送进剪贴板。
- README.md / README.zh-CN.md 新增「日志在哪」章节(含 crash-reports 只在游戏走到那一步才有),
  docs/logs-location.md 给出上限、双写判据、迁移策略与没验的部分。

守门(每条都有变异证明,见报告)
- 旧家会话仍列得出/打不开 ⇒ 只读新根变红;新会话写 logs/game ⇒ 只读旧根、写回旧家都变红。
- launcher.log 滚动 ⇒ 干掉 rename 变红。
- 打开日志文件夹指向真实目录 ⇒ 换成 cache/ 或去掉 create_dir_all 都变红。
- 日志根随三模式变化 ⇒ 把 HostEnv::logs_dir 写死成便携落点变红。
2026-11-09 00:43:45 +08:00

6.4 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(crates/app-iced/src/app/** 里打的行) 只进 stderr(本 mission scope 之外的行)
启动编排:[probe](降级重开 / 探测耗时)、[font]、[theme]、[appearance]、[gpu] 的降级与拒绝降级、[boot](含后端 request/effective)、[shot] 的通道提示 renderer::acceptance 的验收度量:[window] / [perf] / [caps] / [theme] source=…
用户动作的结果:[logs](打开日志文件夹成功/失败)、[settings] 写盘失败、[i18n] 语言没落盘、[update] 的忽略/重启、[agent] / [content] / [page] 的落点提示 renderer 自己的 [gpu] …初始化失败、[gpu] iced_wgpu: Selected、[probe] request=…(probe.rs)、[corner](platform/win32/corner.rs)
一次性、量很小的诊断(每次启动几行到几十行) 组件几何探针与测试:[feedback-probe] / [gallery] / [probe] divider…

不允许把高频行接进来:1 MiB 的上限就是为了保住「事故现场」,逐帧写会把它冲掉。

已知边界(M188 没做,已登记):renderer/**、probe.rs、platform/win32/corner.rs 里的行 仍然只走 stderr——它们各自属于别的 mission 的 scope。要全线接通,得把这些文件的 eprintln! 也换成 crate::app::diag::diag!(或把 emit 提成 lib 级函数)。

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),此时设置页显示的路径仍然正确, 但文件不存在。