If I update my project from Log4J 2.24.1 to 2.25.0 (no other changes), I no longer get logging output.

Spring Boot 3.5.1, gradlew :subprojects:app:run.

Diff: https://github.com/sdkotlin/sd-kotlin-spring-talks/commit/b15de4858a0c10ab9c267489b1b5a98cf1a9e10d.

Log4J 2.24.1 https://github.com/sdkotlin/sd-kotlin-spring-talks/tree/8d40d0c54e700adbc7c7609eb2b04a16223869ab

> Task :subprojects:app:run

  .   ____          _            __ _ _
 /\\ / ___'_ __ _ _(_)_ __  __ _ \ \ \ \
( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
 \\/  ___)| |_)| | | | | || (_| |  ) ) ) )
  '  |____| .__|_| |_|_| |_\__, | / / / /
 =========|_|==============|___/=/_/_/_/

 :: Spring Boot ::                (v3.5.1)

2025-06-19T10:14:37.901-07:00  INFO 65736 --- [           main] o.s.s.SpringBootAppKt                    : Starting SpringBootAppKt using Java 21.0.7 with PID 65736 (/Users/ianbrandt/Development/SDKotlin/sd-kotlin-spring-talks/subprojects/app/build/classes/kotlin/main started by ianbrandt in /Users/ianbrandt/Development/SDKotlin/sd-kotlin-spring-talks/subprojects/app)
2025-06-19T10:14:37.903-07:00  INFO 65736 --- [           main] o.s.s.SpringBootAppKt                    : No active profile set, falling back to 1 default profile: "default"
2025-06-19T10:14:38.107-07:00  INFO 65736 --- [           main] k.r.KClass                               : Config path: /Users/ianbrandt/Development/SDKotlin/sd-kotlin-spring-talks/subprojects/app/config
2025-06-19T10:14:38.107-07:00  INFO 65736 --- [atcher-worker-1] k.r.KClass                               : The current time is 2025-06-19T17:14:38.107189Z
2025-06-19T10:14:38.107-07:00  INFO 65736 --- [           main] k.r.KClass                               : Log path: /Users/ianbrandt/Development/SDKotlin/sd-kotlin-spring-talks/subprojects/app/logs
You've been scanned.
2025-06-19T10:14:38.128-07:00  INFO 65736 --- [           main] o.s.s.SpringBootAppKt                    : Started SpringBootAppKt in 0.395 seconds (process running for 0.625)
2025-06-19T10:14:38.129-07:00  INFO 65736 --- [           main] o.s.s.ResourcePrinter                    : classpath:/native-resource.txt content:
Testing native resources on MacOS.

2025-06-19T10:14:38.130-07:00  INFO 65736 --- [           main] o.s.s.ResourcePrinter                    : classpath:/custom-resource.txt content:
Testing custom resources.


BUILD SUCCESSFUL in 9s

Log4J 2.25.0 https://github.com/sdkotlin/sd-kotlin-spring-talks/tree/b15de4858a0c10ab9c267489b1b5a98cf1a9e10d

> Task :subprojects:app:run

  .   ____          _            __ _ _
 /\\ / ___'_ __ _ _(_)_ __  __ _ \ \ \ \
( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
 \\/  ___)| |_)| | | | | || (_| |  ) ) ) )
  '  |____| .__|_| |_|_| |_\__, | / / / /
 =========|_|==============|___/=/_/_/_/

 :: Spring Boot ::                (v3.5.1)

You've been scanned.

BUILD SUCCESSFUL in 1s

That one output "You've been scanned" line is a plain old println(): https://github.com/sdkotlin/sd-kotlin-spring-talks/blob/b15de4858a0c10ab9c267489b1b5a98cf1a9e10d/subprojects/app/src/main/kotlin/org/sdkotlin/springdemo/SpringBootApp.kt#L45.

Comment From: wilkinsona

Thanks for the report. Given that the only change you've made is to the Log4j2 version, it would suggest that the cause lies in that project. As such, I'm curious why you've opened an issue here rather than there. If you've done some analysis that points to a problem caused by Spring Boot, please share it with us

Comment From: wilkinsona

https://github.com/apache/logging-log4j2/commit/b23e9a56a9cc9eed8c48241555256db8647e917a is a breaking change from Spring Boot's perspective. We call LoggerContext.start(Configuration) on a LoggerContext in STARTED state. This used to result in setConfiguration(Configuration) being called. Since these changes in Log4j2, that no longer happens so Boot's configuration isn't applied. Perhaps we can do something differently that will work with both 2.25.0 and earlier versions but I think this should be investigated by the Log4j2 team in the first instance. I'll close this one for now, we can re-open it if that investigation identifies a change that could be made in Boot to tolerate the change in Log4j2's behavior.

Comment From: ianbrandt

Thanks for the report. Given that the only change you've made is to the Log4j2 version, it would suggest that the cause lies in that project. As such, I'm curious why you've opened an issue here rather than there.

You know, that's a good question. I'll have to blame it on a moment of absent-mindedness. Sorry about that, and thanks for looking into this. I've filed https://github.com/apache/logging-log4j2/issues/3770.

Comment From: ppkarwasz

Hi @wilkinsona,

We call LoggerContext.start(Configuration) on a LoggerContext in STARTED state. This used to result in setConfiguration(Configuration) being called. Since these changes in Log4j2, that no longer happens so Boot's configuration isn't applied. Perhaps we can do something differently that will work with both 2.25.0 and earlier versions but I think this should be investigated by the Log4j2 team in the first instance.

Thank you for investigating and pinpointing the issue — we’ll restore backward compatibility in Log4j 2.25.1.

That said, I believe the use of LoggerContext.start(Configuration) in this scenario may not be the most appropriate approach. In practice, this call is never actually starting the context — the context has already been initialized by the commons-logging bridge when Spring Boot starts logging. The intent here is to reconfigure an already started context, so I’d suggest replacing it with LoggerContext.reconfigure(Configuration).

While I realize this usage of start(Configuration) dates back to Spring Boot 1.2.0 (and yes, we even copied it to our own Log4j2CloudConfigLoggingSystem 😈), the semantics of start vs. reconfigure align more closely with the distinction between Configurator.initialize() and Configurator.reconfigure():

  • start(Configuration) replaces the configuration only if the context hasn't already been started
  • reconfigure(Configuration) always applies the new configuration

Note: There is one exception where using start is appropriate: when a Spring Boot application is running inside an application server that shares a global LoggerContext across multiple deployments. In that case, reconfiguring a shared context is risky — it can lead to classloader leaks, logging interference between applications, and other side effects (as seen in apache/logging-log4j2#1430).

Unfortunately, there’s currently no reliable API for detecting whether a LoggerContext is shared or isolated per application. So in most cases, using reconfigure(Configuration) is the safer and more semantically correct choice — especially for standalone Spring Boot apps.

Comment From: wilkinsona

Thank you, @ppkarwasz. Re-opening to consider switching from start to reconfigure in Boot 4.0 when Log4j2 2.25 (or later) will become our default version.

Comment From: ppkarwasz

Hi @wilkinsona,

Thank you!

Do you have an estimated timeline for Spring Boot 4.0? We’re currently working on several open issues and planned subprojects that could help improve and decentralize the implementation of LoggingSystem. Here are a few areas where collaboration might be especially valuable:

  • Configuration File Locations
    With the modularization of Log4j Core 3, the current logic in Spring Boot for detecting supported configuration formats won’t be compatible with the 3.x series. We’re exploring the possibility of moving the logic for AbstractLoggingSystem#getStandardConfigLocations upstream (see apache/logging-log4j2#3775) to better support dynamic discovery.

  • Forwarding JUL Log Events
    Because the java.util.logging.manager system property must be set very early in the JVM lifecycle, Spring Boot currently uses java.util.logging.Handlers to forward JUL events to the active backend — which is less efficient. We're working on a universal java.util.logging.LogManager implementation (apache/logging-jdk#20) that could work with both Logback and Log4j Core. This may offer a more reliable and efficient bridging mechanism.

  • Logging Configuration API
    Users frequently want to programmatically adjust logging levels, but the ecosystem currently lacks a unified, backend-agnostic API for this. We're prototyping a small, standalone API to address this. One key challenge is defining an opt-in locking mechanism that prevents conflicts when multiple libraries (e.g., Spring Boot and another dependency) attempt to manipulate log configuration simultaneously (apache/logging-admin#1). Your input here would be particularly appreciated.

  • Miscellaneous Improvements
    I’ve proposed several enhancements to our Log4j2SpringBootLoggingSystem in this thread on the dev@logging.apache.org mailing list. Some of them may be relevant or beneficial to Spring Boot’s own Log4j2LoggingSystem.

Unfortunately, we’re quite short-handed at the moment, so I’m not sure when we’ll be able to fully implement these improvements — most likely not before the end of the year. Still, we’d love to coordinate if any of these align with Spring Boot 4.0’s roadmap.

Comment From: wilkinsona

We'll release Boot 4.0 in November. RC1 (the last chance for significant changes/new features) is due in October. You can find the exact details on the milestone page.