DEBUG.WIKI
京东云;让 AI 进入真实业务;零售 · 物流 · 客服 · 硬件;查看方案 →
V.PS:让项目靠近用户,亚太、欧洲、北美 KVM VPS
腾讯云;让你的想法跑起来;轻量服务器 · 建站 / Docker / API;查看产品 →

Gradle 9.8.0 测试日志突然为空,自定义 JUL Handler 可能被移除了

Gradle 9.8.0 捕获 JUL 时移除了自定义根 handler,影响 QuarkusMainTest。检查同一测试 JVM 的处理器,9.8.1 已明确修复。

应用正常运行,升级 Gradle Wrapper 到 9.8.0 后,@QuarkusMainTest 却拿不到日志输出,基于 LaunchResult 的断言开始失败。若测试 JVM 使用 JBoss LogManager,要检查根 JUL logger 上的自定义 handler 是否在 Gradle 开始捕获日志后消失。

这个回归不局限于 Quarkus。只要自定义 LogManager 提前装入根 handler,而测试依赖它接收日志,就可能受影响。Gradle 9.8.1 的发行说明已经明确列入修复。

调高日志级别为什么没用

9.8.0 改变了 JUL 捕获的安装方式:从由 LogManager 执行 reset,变成直接移除根 logger 的所有 handler,再挂入 Gradle 的 SLF4J bridge。默认 JDK LogManager 与某些自定义实现的 reset 行为不同,后者可以特意保留自己的 handler;新逻辑绕开了这层决定。

Quarkus 的 JBoss LogManager 会安装 QuarkusDelayedHandler,应用的其他日志处理器经由它接收记录。这个入口被拿掉后,继续修改 quarkus.log.* 级别,并不能让它重新收到日志。

先在同一测试 JVM 的原失败位置检查配置与根 handler 类型。以下片段只读状态,不替换 logger:

import java.util.logging.Handler;
import java.util.logging.LogManager;
import java.util.logging.Logger;

System.out.println(System.getProperty("java.util.logging.manager"));
System.out.println(LogManager.getLogManager().getClass().getName());
Logger root = Logger.getLogger("");
for (Handler handler : root.getHandlers()) {
    System.out.println(handler.getClass().getName());
}

片段放在可执行的方法里,输出应保留到测试诊断,而不是业务日志中。如果目标 handler 本来就不存在,先检查测试启动配置。若存在但收不到记录,再核对级别、过滤器和应用初始化;不能只看 Wrapper 版本就认定同一原因。

更新捕获实现,保留 Quarkus 日志管理器

使用兼容项目插件和 JDK、且包含修复的正式 Gradle 版本,例如已核证的 9.8.1。新实现只管理默认 ConsoleHandler 和 SLF4J bridge,把其他根 handler 留在原处,并在捕获结束时恢复自己管理的那部分。

按项目原有流程更新完整 Wrapper 文件与校验,保存原构建基线。暂时无法升级时,兼容条件允许可恢复此前可用的 9.7.1 组合,再安排维护升级。不要直接把 JVM 的 java.util.logging.manager 改回默认值来凑过 Quarkus 测试;这会改变框架日志基础配置。

在测试中手工再挂一个 handler 也要谨慎:捕获生命周期和热重载可能使它重复附着,产生重复消息或泄漏资源。这里优先修复 Gradle 的捕获边界,不在每个用例里修补根 logger。

验收应回到原 @QuarkusMainTest 和日志断言,确认预期记录进入 LaunchResult,内容与数量正确;另外检查普通测试输出没有重复。仅控制台重新出现文字,还不足以证明框架捕获接口恢复。若更新引出其他插件问题,恢复配套 Wrapper 和 JVM 设置,保留已经收集的 handler 状态继续排查。