项目日志治理
本文件是 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 -> DEBUGQtInfoMsg -> DEBUGQtWarningMsg -> WARNINGQtCriticalMsg -> ERRORQtFatalMsg -> 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。