Spring Boot 2.6.4 -> 2.6.6:记录模拟异常时 Logback 中出现奇怪的 NullPointerException

Spring Boot 2.6.4 -> 2.6.6 : strange NullPointerException within Logback when logging a mock Exception

从 Spring Boot 2.6.4 升级到 2.6.6 时,我的一项测试(用 Kotlin 编写)失败了:

    @Test
    fun shouldLogProperMessageIfNotAbleToHitAPI() {

        val configValidator = ConfigValidator(GitHubCrawlerProperties(SourceControlConfig(url = "someIncorrectURL",organizationName="someOrg")),mockRemoteSourceControl)

        `when`(mockRemoteSourceControl.validateRemoteConfig("someOrg")).thenThrow(NoReachableRepositories("problem !",mock(Exception::class.java)))

        val validationErrors=configValidator.getValidationErrors()

        assertThat(validationErrors).hasSize(1);

    }

构建通过 Spring Boot 2.6.4。当我 运行 在我的 IDE 中单独测试时,它在 Spring Boot 2.6.6 中工作,但在 Maven 构建过程中失败。

stacktrace 默认情况下不显示,但在用 try/catch 包围调用后,我能够得到它,它指向 Logback :

java.lang.NullPointerException: null
        at ch.qos.logback.classic.spi.ThrowableProxy.<init>(ThrowableProxy.java:99)
        at ch.qos.logback.classic.spi.ThrowableProxy.<init>(ThrowableProxy.java:89)
        at ch.qos.logback.classic.spi.ThrowableProxy.<init>(ThrowableProxy.java:62)
        at ch.qos.logback.classic.spi.LoggingEvent.<init>(LoggingEvent.java:119)
        at ch.qos.logback.classic.Logger.buildLoggingEventAndAppend(Logger.java:419)
        at ch.qos.logback.classic.Logger.filterAndLog_0_Or3Plus(Logger.java:383)
        at ch.qos.logback.classic.Logger.error(Logger.java:538)
        at com.societegenerale.githubcrawler.ConfigValidator.getValidationErrors(ConfigValidator.kt:48)

Logback 版本似乎没有变化,我仍然得到 v 1.2.11 。

查看 Logback 源代码,在 ThrowableProxy 中:

        if (GET_SUPPRESSED_METHOD != null) {
            // this will only execute on Java 7
            Throwable[] throwableSuppressed = extractSupressedThrowables(throwable);
            
            if (throwableSuppressed.length > 0) {
                List<ThrowableProxy> suppressedList = new ArrayList<ThrowableProxy>(throwableSuppressed.length);
                for (Throwable sup : throwableSuppressed) {
...

注意:我使用 Java 11 进行构建,因此 Logback 源代码中的注释 this will only execute on Java 7 似乎是错误的。

好像throwableSuppressed是null,调用throwableSuppressed.size的时候得到NPE

如果我没有在 NoReachableRepositories("problem !",mock(Exception::class.java)) 中使用模拟,而是使用 NoReachableRepositories("problem !",Exception())

,则测试通过

我意识到使用真正的异常可能比模拟更好,所以我的问题以某种方式解决了(在这上面花了 2 个小时之后..)。

不过,我很好奇:升级到 Spring Boot 2.6.6 后可能会导致此问题,这应该是一个小改动?

这个问题是在 logback 1.2.11 中引入的commit. It is tracked in this jira ticket

Logback 已从 spring boot 2.6.5 升级到 1.2.11,您可以参考此 changelog。所以如果你升级到 2.6.5,你会遇到同样的错误。

我们现在可以做的是通过在 build.gradle 文件中添加这一行来将 logback 的版本覆盖到 1.2.10。

ext["logback.version"] = "1.2.10"