跳到正文

项目日志治理 ​

本文件是 lol_audio_unpack 的长期开发者日志规范。它面向仓库维护者与自动化代理,约束日志语义、布点位置、桥接边界与异常趋势处理方式。

完整目标是:在写代码时就把日志布点、等级和失败可见性设计好,而不是等排错时临时补洞。

目标 ​

  • 统一控制台、界面、后台任务的日志语义。
  • 明确区分仓库内部业务日志与第三方桥接日志。
  • 让阶段边界、风险决策、失败趋势和异常处理保持稳定可见。
  • 在保持 loguru 原生能力的前提下,减少日志风格漂移。

非目标 ​

  • 不重写底层 loguru。
  • 不引入一套强制全仓使用的新日志框架。
  • 不要求所有日志都经过统一包装层。
  • 不为了“统一”牺牲 caller attribution。
  • 不把 Qt 或 pyvgmstream 的上游输出硬改造成业务阶段事件。
  • 不在第一轮就全仓扫荡式重写所有历史日志。

双轨模型 ​

项目日志治理采用双轨模型。两条轨道可以出现在同一个观察面上,但职责不同,不能互相替代。

轨道 A:仓库内部业务日志 ​

内部业务日志用于表达项目自身的运行语义,负责以下信息:

  • 阶段开始、完成、跳过、中止。
  • 关键模式切换、用户动作结果、批次进度。
  • fallback、重试、自动回退、降级等风险决策。
  • 单项失败、累计失败与阶段摘要。

这条轨道是强语义轨道,等级选择和布点位置需要遵守本文件的硬约束。

轨道 B:第三方桥接日志 ​

第三方桥接日志用于消费 Qt、pyvgmstream 等外部来源的输出,职责只有三类:

  • 接入外部日志。
  • 做最小必要的等级映射。
  • 保留来源标签和少量必要上下文。

这条轨道不承担业务阶段语义,也不替代业务层自己的日志。如果第三方输出对用户真正重要,应在业务边界额外补一条内部业务日志,而不是把桥接日志直接抬升成阶段事件。

等级语义 ​

以下语义对仓库内部业务日志构成强约束。

控制台、界面与后台任务共用面向用户的控制台输出。等级首先按受众判断:用户需要知道的任务进展、 结果与问题正常展示;仅供开发排障的预览读取、预加载、缓存和耗时信息放在 DEBUG。 界面内部加载不等于用户任务阶段,记录 worker 生命周期并不要求使用 INFO。

INFO ​

用于操作者应该稳定看到的阶段事件,例如:

  • 阶段开始、完成、跳过。
  • 关键模式切换。
  • 用户动作结果。
  • 批次级进度。

SUCCESS ​

用于顶层完成或少量关键完成摘要。

  • 不要把普通过程提示都写成 SUCCESS。
  • 不要把 SUCCESS 当成“更好看的 INFO”。

DEBUG ​

用于决策快照与实现细节,例如:

  • fallback 原因。
  • 分支选择原因。
  • 参数裁剪、路径选择、数量统计。
  • 重试原因、桥接策略细节。
  • 实体预览等界面内部加载的启动、完成和耗时;频繁切换实体不应刷屏。影响用户的加载失败仍按风险使用 WARNING/ERROR。

TRACE ​

用于深度排障与细粒度节点,例如:

  • worker 或 thread 生命周期细节点。
  • 阶段内部的细粒度步骤。
  • 候选路径筛选过程。

TRACE 必须有组织地使用,不能把高频业务阶段可见性下沉到这里。

WARNING ​

用于可恢复异常与降级行为,例如:

  • 单项失败但整体继续。
  • fallback 生效。
  • 自动回退。
  • 结果可能打折但流程未中断。

ERROR ​

用于当前单元失败、阶段关键步骤失败,或错误趋势已经升级到必须显式暴露的程度。

CRITICAL ​

用于当前流程无法继续、必须停止或强制终止的情况。

必须布点的位置 ​

以下位置默认必须主动考虑日志,而不是等报错后再补:

  • 阶段边界:开始、完成、跳过、中止。
  • 风险决策点:fallback、重试、自动准备、自动回退、忽略输入、分支选择。
  • 错误吞掉点:except ...: continue、记录错误后继续、单项失败但整体继续。
  • worker / thread 边界:启动、完成、失败,以及主线程接收结果并切阶段。
  • 外部系统边界:本地文件、manifest、WAD / BNK、第三方桥接入口。
  • 读取入口边界:如果方法约定以 None / 空字典 / falsey 结果表示失败,异常日志必须在方法体内显式补齐。

第三方桥接原则 ​

Qt 与 pyvgmstream 默认归入第三方桥接轨道。桥接层应尽量轻干预,重点是“接入”和“可消费”,不是替代业务层表达阶段语义。

Qt 桥接 ​

  • 保留原始 message,不额外改写成业务阶段文本。
  • 保留来源标签,便于区分 Qt 输出与仓库内部日志。
  • 做最小等级映射,优先把 Qt 的 Info 视为桥接调试信息,而不是业务 INFO。
  • 如果某条 Qt 输出对用户具有直接业务意义,应在业务边界补内部业务日志。

第一轮建议映射:

  • QtDebugMsg -> DEBUG
  • QtInfoMsg -> DEBUG
  • QtWarningMsg -> WARNING
  • QtCriticalMsg -> ERROR
  • QtFatalMsg -> CRITICAL

pyvgmstream 桥接 ​

  • 保留上游 message 与来源标签。
  • 默认把上游 INFO 消费为桥接 DEBUG,避免占用业务层的阶段观察面。
  • 更低级别输出继续停留在 DEBUG/TRACE。
  • 只有在明确表明不可继续、输入损坏或结果已不可信时,才升级到 WARNING/ERROR。

如果 pyvgmstream 的输出需要让用户直接知道当前业务发生了什么,应在任务或控制器边界补一条内部业务日志,而不是把桥接层改造成阶段播报器。

最小规范示例 ​

以下示例只用于说明最容易出错的边界,不作为完整写法模板。

示例 1:桥接日志之外何时补业务日志 ​

  • 桥接层已有日志:[pyvgmstream] decoder init failed
  • 如果这会影响当前任务行为,业务层仍要补一条日志,例如:WARNING 试听流初始化失败,已回退到静音预览

这里的桥接日志负责保留外部来源;业务日志负责说明当前阶段发生了什么、对用户有什么影响。

示例 2:单项失败但批次继续的最小日志集合 ​

  • INFO 批次开始,例如:开始处理 20 个英雄语音任务
  • WARNING 单项失败但继续,例如:英雄 Ahri 处理失败,已继续后续任务
  • INFO 或 SUCCESS 阶段摘要,例如:批次处理完成,成功 19 个,失败 1 个

最小集合至少要同时覆盖阶段开始、单项失败继续、最终摘要,避免只留下孤立报错。

异常与失败趋势 ​

异常记录 ​

  • 优先使用 logger.exception(...) 或 logger.opt(exception=True)...。
  • 不要同一异常既打一份完整栈,又手动再拼一份重复 traceback 文本。
  • 如果阶段内选择继续执行,重点是把“继续执行的理由”“影响范围”和“失败趋势”记录清楚。
  • 若方法对外通过 None / 空结果继续维持兼容语义,不要把异常边界藏在 @logger.catch 这类外层 decorator 中;应在方法体内显式记录错误并返回约定的空结果。

失败趋势 ​

单项失败可以继续,但趋势不能隐藏:

  • 单项失败要有明确日志。
  • 累计失败达到阈值时要升级提示。
  • 阶段结束时必须给出摘要。
  • 全部失败时必须强烈暴露,不允许被普通噪音淹没。

阶段摘要 ​

阶段摘要优先表达业务结果,而不是依赖第三方桥接日志堆出语义。顶层完成、部分失败、全部失败都需要在业务层有清晰收口。

日志不是结果事实源 ​

业务层使用 EntityResult、StageResult 和 RunResult 表达权威状态。控制台退出码和 界面终态必须读取 typed result,不能通过是否出现 SUCCESS 日志、异常文案或最后一条消息反推。 日志可以保留 traceback、重试和性能细节;公共结果只保留稳定状态、计数、错误摘要与已确认落盘路径。

当前已验证的收口模式 ​

  • 配置加载默认缺失文件若属于正常缺省路径,可降到 DEBUG,避免长期占用 WARNING 观察面。
  • 本地源预检、自动准备和阶段切换提示保留在业务 INFO;具体资源诊断按风险使用 WARNING 或 ERROR。
  • 忽略未知配置项属于风险决策,仍应保留 WARNING 可见性。
  • 读取入口若约定“读取失败返回空值并继续”,优先在方法体内显式 try/except,记录带上下文的异常日志,再返回约定空值。

开发时的默认判断 ​

  • 先判断这条日志属于内部业务轨道还是第三方桥接轨道。
  • 先定布点和等级,再决定是否需要极薄 helper。
  • 默认优先直接使用 logger.xxx(...)。
  • 只有在高频重复、能减少漂移、且不破坏 caller attribution 时,才考虑新增薄封装。

与执行动作直接相关的硬规则请查看仓库根目录的 AGENTS.project.md。