正在载入

等待时间过长时请刷新页面

编写于

最近更新

Problem 与异常

Linlang 审计与日志服务通过两种方式处理运行中的错误。
其中 Java 异常负责改变当前调用流程,Problem 负责保存稳定代码、故障现场和处理建议。

基本原则

错误类别处理办法
参数不合法,调用不能继续抛出合适的 Java 异常
故障需要留档但服务可以降级报告 Problem
故障需要留档且调用不能继续报告一次 Problem,并且抛出 Java 异常
玩家输入错误等正常结果返回业务提示

Problem 不是 Exception。报告之后代码仍会继续执行;需要终止操作时,调用方必须明确返回失败或抛出异常。

lin.linAudit().problem().report(
        "PLUGIN-CACHE-REBUILD-FAIL",
        exception,
        "cache", "reward"
);

在边界报告一次

底层方法通常只抛出带原因链的异常,由能够决定恢复、降级或终止的边界报告 Problem。例如启动器、命令调度器、异步任务入口和资源关闭流程都适合作为报告边界。

try {
    cache.reload();
} catch (IOException exception) {
    lin.linAudit().problem().report(
            "PLUGIN-CACHE-RELOAD-FAIL",
            exception,
            "file", cacheFile
    );
    throw new IllegalStateException("PLUGIN-CACHE-RELOAD-FAIL", exception);
}

不要在每一层捕获并报告同一个异常,否则控制台和问题文件会出现重复记录。

需要逐步组织上下文或缩短控制台输出时,可以使用 LinProblem 构建器:

LinProblem problem = LinProblem.builder("PLUGIN-CACHE-RELOAD-FAIL")
        .context("file", cacheFile)
        .cause(exception)
        .consoleSummary("缓存重载失败,详情见问题日志")
        .build();

lin.linAudit().problem().report(problem);

设置 consoleSummary 后,控制台只显示这条单行摘要,不附加问题堆栈;完整上下文和原因链仍写入 Problem 文件。

专用异常

Core 内部使用少量专用异常区分失败来源。这些异常只是控制流分类,不是另一套错误系统。

防止重复报告

ReportedProblemException 是公开的标记接口,表示当前异常已经在更靠近故障现场的位置报告过 Problem。运行时边界遇到这类异常时仍会停止或汇总失败,但不会再附加一个含义更宽泛的问题代码。ConfigLoadException、FileSaveException、ReloadException 以及命令组配置异常都使用这一标记。

该标记不表示异常可以忽略。业务代码仍应按照方法契约处理失败,也不应为了绕过报告而让普通业务异常实现它。

命令参数拒绝

CommandArgumentException 表示输入没有通过已声明的命令参数规则,例如参数缺失、整数越界、UUID 无效或目标玩家不存在。

这是正常的命令结果。命令服务会显示参数错误和命令用法,不生成 Problem。为了兼容旧解析器,普通 IllegalArgumentException 当前也按参数拒绝处理;其他无法归类的异常才报告 LIN-COMMAND-ARGUMENT-PARSE-FAIL。

命令规范错误

CommandSpecException 表示开发者注册的命令结构无效,例如参数名重复、可选参数后出现必填参数、根命令不一致或命令签名重复。

注册过程会报告 LIN-COMMAND-REGISTRATION-FAIL,然后把异常继续抛给调用方。该类位于 Core,不属于插件应依赖或捕获的公开 API;从 API 角度,它仍属于 IllegalArgumentException。

数据实体映射错误

DataMappingException 表示实体声明不能形成有效表结构,例如缺少 @Id、存在重复列、字段类型不受支持或自动主键类型错误。

数据库入口会报告 LIN-DATA-MAPPING-INVALID 并附带实体类,再终止仓库创建或迁移。该异常是包内实现类型,插件不应依赖其类名;调用方应通过 Problem 代码和原始原因定位声明错误。

Problem 与语言

Problem 记录只保存稳定代码、上下文和 Throwable,建立记录时不会读取语言文件。这样语言服务自身加载失败时,错误仍然能够被保存。

查询 Problem 时,ProblemDefinition 的说明和处理建议可以使用当前运行时语言:

Optional<ProblemDefinition> result =
        lin.linAudit().problem().lookup("LIN-DATA-MAPPING-INVALID");

问题语言资源不可用时自动回退到 Runtime 内建说明。代码本身不会翻译,也不应包含变化的文件名或异常文本;这些信息应放入上下文字段。

自定义问题代码

插件可以定义自己的稳定代码,例如:

RAINBOW-CONFIG-LOAD-FAIL
RAINBOW-REWARD-CALCULATE-FAIL

只需要您来维护解释文档或可解读即可。Linlang 不提供自定义的问题代码的相关服务。

讨论

请登录账号