Log4j 1.2 API initialization does not respect `log4j1.compatibility` setting
Description
I've come across a bug where a call to org.apache.log4j.LogManager.getLogger() exhibits apparently unintended side effects on the configuration (particularly log levels) of other loggers that were created previously. The below code prints two INFO-level log messages on Log4j 2.22.1 and earlier, but since 2.23.0 only the first message is emitted:
Logger logger = (Logger) LogManager.getLogger(App.class);
TestAppender testAppender = new TestAppender();
logger.addAppender(testAppender);
logger.setLevel(Level.INFO);
testAppender.start();
logger.info("Initializing Log4j 1.2 API...");
org.apache.log4j.LogManager.getLogger("org.example.OtherClass");
logger.info("Log4j 1.2 API initialized.");
System.out.printf("Recorded %,d events (expected 2)%n", testAppender.getLogEvents());
Configuration
Version: 2.23.0 and later
JDK: Corretto 17.0.15
Logs
[0] ~/log4j-repro # ./run.sh
Testing 2.20.0
19:12:51.494 [main] INFO org.example.App - Initializing Log4j 1.2 API...
19:12:51.502 [main] INFO org.example.App - Log4j 1.2 API initialized.
Recorded 2 events (expected 2)
Testing 2.21.0
19:12:53.067 [main] INFO org.example.App - Initializing Log4j 1.2 API...
19:12:53.075 [main] INFO org.example.App - Log4j 1.2 API initialized.
Recorded 2 events (expected 2)
Testing 2.21.1
19:12:54.209 [main] INFO org.example.App - Initializing Log4j 1.2 API...
19:12:54.217 [main] INFO org.example.App - Log4j 1.2 API initialized.
Recorded 2 events (expected 2)
Testing 2.22.0
19:12:55.348 [main] INFO org.example.App - Initializing Log4j 1.2 API...
19:12:55.356 [main] INFO org.example.App - Log4j 1.2 API initialized.
Recorded 2 events (expected 2)
Testing 2.22.1
19:12:56.534 [main] INFO org.example.App - Initializing Log4j 1.2 API...
19:12:56.544 [main] INFO org.example.App - Log4j 1.2 API initialized.
Recorded 2 events (expected 2)
Testing 2.23.0
19:12:58.049 [main] INFO org.example.App - Initializing Log4j 1.2 API...
Recorded 1 events (expected 2)
Testing 2.23.1
19:12:59.140 [main] INFO org.example.App - Initializing Log4j 1.2 API...
Recorded 1 events (expected 2)
Testing 2.24.0
19:13:00.573 [main] INFO org.example.App - Initializing Log4j 1.2 API...
Recorded 1 events (expected 2)
Testing 2.24.1
19:13:01.686 [main] INFO org.example.App - Initializing Log4j 1.2 API...
Recorded 1 events (expected 2)
Testing 2.24.2
19:13:02.762 [main] INFO org.example.App - Initializing Log4j 1.2 API...
Recorded 1 events (expected 2)
Testing 2.24.2
19:13:03.657 [main] INFO org.example.App - Initializing Log4j 1.2 API...
Recorded 1 events (expected 2)
Testing 2.25.0-SNAPSHOT
2025-05-16T02:13:04.911900Z main INFO Initializing Log4j 1.2 API...Recorded 1 events (expected 2)
Reproduction
I made a standalone reproducer here. The run.sh script will run it against the last dozen releases of Log4j2, including the 2.25.0-SNAPSHOT release in the local Maven repository (in other words, the bug appears to be present as of the latest commit on 2.x).
Related? https://github.com/apache/logging-log4j2/pull/2282
@rschmitt,
The method o.a.l.l.Logger.setLevel is intended for testing purposes only, as noted in the JavaDoc:
This method is not exposed through the public API and is provided primarily for unit testing.
It temporarily sets the level on a specific Logger instance, but this change is not persisted and may be overridden when the logger configuration is re-synchronized with the Configuration object.
If you're looking to programmatically change the logging level in a supported and persistent way, you should use Configurator.setLevel:
Configurator.setLevel(logger, Level.INFO);
Note that we're currently developing a lightweight Logger Admin API that enables logger level manipulation in a way that's independent of the underlying logging implementation.
Logging APIs like SLF4J and the Log4j API deliberately avoid exposing this functionality to prevent libraries from silently altering the logging configuration—often without the application developer’s knowledge. To address this concern, we're exploring a signaling mechanism to prevent such misuse (apache/logging-admin#1).
You're welcome to try out the Logger Admin API and share your feedback—we’d greatly appreciate it.
Related? #2282
Yes, you're right — that pull request is indeed responsible for the behavior change you're seeing. We were previously using the incorrect Logger.setLevel method internally instead of Configurator.setLevel. After that change, o.a.l.Logger.setLevel began properly re-synchronizing the cached levels of o.a.l.l.Logger instances with the current Configuration.
Following #2778, the log4j-1.2-api module is no longer supposed to modify the Log4j Core configuration by default. However, it looks like we missed a few methods that still allow this. As an answer to this bug report, we should update log4j-1.2-api to prevent any modifications to the Core configuration unless log4j1.compatibility is explicitly set to true.
The method
o.a.l.l.Logger.setLevelis intended for testing purposes only, as noted in the JavaDoc
Yes, I found this behavior debugging a failing unit test.