When upgrading to Spring Boot 3.4.2 our logging stopped completely working.

After narrowing down I figured the problem happens with Spring Boot 3.4.2 when using Log4j2 together with BlockHound.

Here is a minimum reproducer repo: https://github.com/micopiira/spring-boot-log4j2-issue

Spring Boot 3.4.1 with exact same Log4j2 version and BlockHound version works.

Spring Boot 3.4.1:

Image

Spring Boot 3.4.2:

Image

Comment From: wilkinsona

Thanks for the report and for the minimal sample. I assume that something has changed in Spring Boot 3.4.2 that is causing BlockHound to break Log4j2 in some way. Unfortunately, given that this works fine without BlockHound and the way in which BlockHound works, I don't think there's anything we can do about this in Spring Boot. Please raise a BlockHound issue so that they can investigate further.

Comment From: wilkinsona

Re-opening. Thanks to @violetagg's analysis, this has been tracked down to an assumption in StatusConsoleListener#closeNonSystemStream that is faulty when BlockHound is involved. It's only started causing a problem due to https://github.com/spring-projects/spring-boot/issues/43578.

Comment From: nosan

Thanks for the sample, @micopiira.

StandardOutputIntegration.applyTo(...) replaces the standard OUT and ERR streams with PrintStreamDelegate. During initialization, Log4J2LoggingSystem attempts to reset the StatusLogger.FallbackListener stream (which was previously replaced by BlockHound). This results in PrintStreamDelegate being closed, which in turn delegates the close call to the standard OUT and ERR streams.

One of the options is to revert https://github.com/spring-projects/spring-boot/commit/b6b9237f2c0f8b3767c04d017bfd0a5e5778d18d and fix SampleLog4j2StructuredLoggingApplicationTests. https://github.com/spring-projects/spring-boot/compare/main...nosan:spring-boot:43963

Meanwhile, you can use this solution https://github.com/reactor/BlockHound/issues/469#issuecomment-2621801396

Comment From: violetagg

Thanks for the sample, @micopiira.

StandardOutputIntegration.applyTo(...) replaces the standard OUT and ERR streams with PrintStreamDelegate. During initialization, Log4J2LoggingSystem attempts to reset the StatusLogger.FallbackListener stream (which was previously replaced by BlockHound). This results in PrintStreamDelegate being closed, which in turn delegates the close call to the standard OUT and ERR streams.

@nosan Please check the screenshot below -> it is not the delegator that closes but the real system stream

Image

One of the options is to revert b6b9237 and fix SampleLog4j2StructuredLoggingApplicationTests. main...nosan:spring-boot:43963

Meanwhile, you can use this solution reactor/BlockHound#469 (comment)

Comment From: nosan

@violetagg

Oh, you're right, my mistake.



// stream = original System.out
// System.out = BlockHound.PrintStreamDelegate

    private static void closeNonSystemStream(final OutputStream stream) {
        // Close only non-system streams
        if (stream != System.out && stream != System.err) {
            try {
                stream.close();
            } catch (IOException error) {
                // We are at the lowest level of the system.
                // Hence, there is nothing better we can do but dumping the failure.
                error.printStackTrace(System.err);
            }
        }
    }

so Log42J closes the original stream.

Comment From: eichingertim

I noticed a similar problem in IntelliJ:

When I upgraded to 3.4.2 and executed a SpringBootTest with OutputCaptureExtension (Log Framework Log4J) the test is marked as "Not started" after some time and produces no log output. However when the tests are run via maven it works fine.

When I downgraded again to 3.4.1 the behavior is back to normal. Can anybody else validate this?

Comment From: nosan

@eichingertim

I've tried the following test with IntelliJ IDEA and 3.4.2:



@SpringBootTest
@ExtendWith(OutputCaptureExtension.class)
class SpringBootLog4j2DemoApplicationTests {

    @Test
    void contextLoads(CapturedOutput output) {
        Assertions.assertThat(output).isNotEmpty();
    }

}

And indeed it does not work for me as well.

Image

This fix https://github.com/spring-projects/spring-boot/compare/main...nosan:spring-boot:gh-43963 resolves both issues.