From dcf3076824bb6a9d65ac1655c95fd272adb4f19b Mon Sep 17 00:00:00 2001 From: Junhyeok Lee Date: Wed, 9 Jul 2025 23:49:40 +0900 Subject: [PATCH] Add guards to mutator methods when compatibility mode is off --- .../main/java/org/apache/log4j/Category.java | 30 ++++++++ .../java/org/apache/log4j/LogManager.java | 12 ++++ .../java/org/apache/log4j/CategoryTest.java | 72 +++++++++++++++++++ .../java/org/apache/log4j/LogManagerTest.java | 19 +++++ .../java/org/apache/log4j/LoggerTest.java | 2 + ...3667_respect_log4j1_compatibility_flag.xml | 12 ++++ 6 files changed, 147 insertions(+) create mode 100644 src/changelog/.2.x.x/3667_respect_log4j1_compatibility_flag.xml diff --git a/log4j-1.2-api/src/main/java/org/apache/log4j/Category.java b/log4j-1.2-api/src/main/java/org/apache/log4j/Category.java index a81409c0246..0c8748232d5 100644 --- a/log4j-1.2-api/src/main/java/org/apache/log4j/Category.java +++ b/log4j-1.2-api/src/main/java/org/apache/log4j/Category.java @@ -35,6 +35,7 @@ import org.apache.log4j.spi.HierarchyEventListener; import org.apache.log4j.spi.LoggerRepository; import org.apache.log4j.spi.LoggingEvent; +import org.apache.logging.log4j.core.config.ConfigurationFactory; import org.apache.logging.log4j.message.LocalizedMessage; import org.apache.logging.log4j.message.MapMessage; import org.apache.logging.log4j.message.Message; @@ -42,6 +43,7 @@ import org.apache.logging.log4j.message.SimpleMessage; import org.apache.logging.log4j.spi.ExtendedLogger; import org.apache.logging.log4j.spi.LoggerContext; +import org.apache.logging.log4j.util.PropertiesUtil; import org.apache.logging.log4j.util.StackLocatorUtil; import org.apache.logging.log4j.util.Strings; @@ -52,6 +54,10 @@ public class Category implements AppenderAttachable { private static final String FQCN = Category.class.getName(); + private static boolean isFullCompatibilityEnabled() { + return PropertiesUtil.getProperties().getBooleanProperty(ConfigurationFactory.LOG4J1_EXPERIMENTAL); + } + /** * Tests if the named category exists (in the default hierarchy). * @@ -192,6 +198,9 @@ protected Category(final String name) { */ @Override public void addAppender(final Appender appender) { + if (!isFullCompatibilityEnabled()) { + return; + } if (appender != null) { if (LogManager.isLog4jCorePresent()) { CategoryUtil.addAppender(logger, AppenderAdapter.adapt(appender)); @@ -572,6 +581,9 @@ void maybeLog( */ @Override public void removeAllAppenders() { + if (!isFullCompatibilityEnabled()) { + return; + } if (aai != null) { final Vector appenders = new Vector(); for (final Enumeration iter = aai.getAllAppenders(); iter != null && iter.hasMoreElements(); ) { @@ -593,6 +605,9 @@ public void removeAllAppenders() { */ @Override public void removeAppender(final Appender appender) { + if (!isFullCompatibilityEnabled()) { + return; + } if (appender == null || aai == null) { return; } @@ -611,6 +626,9 @@ public void removeAppender(final Appender appender) { */ @Override public void removeAppender(final String name) { + if (!isFullCompatibilityEnabled()) { + return; + } if (name == null || aai == null) { return; } @@ -622,6 +640,9 @@ public void removeAppender(final String name) { } public void setAdditivity(final boolean additivity) { + if (!isFullCompatibilityEnabled()) { + return; + } if (LogManager.isLog4jCorePresent()) { CategoryUtil.setAdditivity(logger, additivity); } @@ -635,6 +656,9 @@ final void setHierarchy(final LoggerRepository repository) { } public void setLevel(final Level level) { + if (!isFullCompatibilityEnabled()) { + return; + } setLevel(level != null ? level.getVersion2Level() : null); } @@ -645,10 +669,16 @@ private void setLevel(final org.apache.logging.log4j.Level level) { } public void setPriority(final Priority priority) { + if (!isFullCompatibilityEnabled()) { + return; + } setLevel(priority != null ? priority.getVersion2Level() : null); } public void setResourceBundle(final ResourceBundle bundle) { + if (!isFullCompatibilityEnabled()) { + return; + } this.bundle = bundle; } diff --git a/log4j-1.2-api/src/main/java/org/apache/log4j/LogManager.java b/log4j-1.2-api/src/main/java/org/apache/log4j/LogManager.java index 933c97148ef..388d04c8f9c 100644 --- a/log4j-1.2-api/src/main/java/org/apache/log4j/LogManager.java +++ b/log4j-1.2-api/src/main/java/org/apache/log4j/LogManager.java @@ -26,7 +26,9 @@ import org.apache.log4j.spi.NOPLoggerRepository; import org.apache.log4j.spi.RepositorySelector; import org.apache.log4j.spi.RootLogger; +import org.apache.logging.log4j.core.config.ConfigurationFactory; import org.apache.logging.log4j.spi.LoggerContext; +import org.apache.logging.log4j.util.PropertiesUtil; import org.apache.logging.log4j.util.StackLocatorUtil; /** @@ -64,6 +66,10 @@ public final class LogManager { private static final boolean LOG4J_CORE_PRESENT; + private static boolean isFullCompatibilityEnabled() { + return PropertiesUtil.getProperties().getBooleanProperty(ConfigurationFactory.LOG4J1_EXPERIMENTAL); + } + static { LOG4J_CORE_PRESENT = checkLog4jCore(); // By default, we use a DefaultRepositorySelector which always returns 'hierarchy'. @@ -201,6 +207,9 @@ static void reconfigure(final ClassLoader classLoader) { } public static void resetConfiguration() { + if (!isFullCompatibilityEnabled()) { + return; + } resetConfiguration(StackLocatorUtil.getCallerClassLoader(2)); } @@ -225,6 +234,9 @@ public static void setRepositorySelector(final RepositorySelector selector, fina * Shuts down the current configuration. */ public static void shutdown() { + if (!isFullCompatibilityEnabled()) { + return; + } shutdown(StackLocatorUtil.getCallerClassLoader(2)); } diff --git a/log4j-1.2-api/src/test/java/org/apache/log4j/CategoryTest.java b/log4j-1.2-api/src/test/java/org/apache/log4j/CategoryTest.java index 6dd7305c07e..b607a2158df 100644 --- a/log4j-1.2-api/src/test/java/org/apache/log4j/CategoryTest.java +++ b/log4j-1.2-api/src/test/java/org/apache/log4j/CategoryTest.java @@ -16,6 +16,7 @@ */ package org.apache.log4j; +import static org.junit.jupiter.api.Assertions.assertDoesNotThrow; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertInstanceOf; @@ -27,11 +28,14 @@ import java.util.Collections; import java.util.Enumeration; import java.util.List; +import java.util.Locale; import java.util.Map; +import java.util.ResourceBundle; import java.util.function.Consumer; import org.apache.log4j.bridge.AppenderAdapter; import org.apache.log4j.bridge.AppenderWrapper; import org.apache.log4j.spi.LoggingEvent; +import org.apache.log4j.varia.NullAppender; import org.apache.logging.log4j.core.Layout; import org.apache.logging.log4j.core.LogEvent; import org.apache.logging.log4j.core.LoggerContext; @@ -42,6 +46,7 @@ import org.apache.logging.log4j.message.Message; import org.apache.logging.log4j.message.ObjectMessage; import org.apache.logging.log4j.message.SimpleMessage; +import org.apache.logging.log4j.test.junit.SetTestProperty; import org.apache.logging.log4j.util.Constants; import org.apache.logging.log4j.util.Strings; import org.junit.jupiter.api.AfterAll; @@ -52,6 +57,7 @@ /** * Tests of Category. */ +@SetTestProperty(key = "log4j1.compatibility", value = "true") class CategoryTest { static ConfigurationFactory cf = new BasicConfigurationFactory(); @@ -409,6 +415,72 @@ void testGetAppender() { } } + @Test + @SetTestProperty(key = "log4j1.compatibility", value = "false") + void testSetLevelCompatibilityDisabled() { + final Category category = Category.getInstance("TestCategory"); + final Level initialLevel = category.getEffectiveLevel(); + category.setLevel(Level.FATAL); + assertEquals(initialLevel, category.getEffectiveLevel(), "level should be unchanged when compatibility is off"); + } + + @Test + @SetTestProperty(key = "log4j1.compatibility", value = "false") + void testSetAdditivityCompatibilityDisabled() { + final Category category = Category.getInstance("TestCategory"); + final boolean initialAdditivity = category.getAdditivity(); + category.setAdditivity(!initialAdditivity); + assertEquals( + initialAdditivity, + category.getAdditivity(), + "additivity should be unchanged when compatibility is off"); + } + + @Test + @SetTestProperty(key = "log4j1.compatibility", value = "false") + void testAddAppenderCompatibilityDisabled() { + final Category category = Category.getInstance("TestCategory"); + category.addAppender(new NullAppender()); + assertFalse( + category.getAllAppenders().hasMoreElements(), "no appenders should be added when compatibility is off"); + } + + @Test + @SetTestProperty(key = "log4j1.compatibility", value = "false") + void testSetPriorityCompatibilityDisabled() { + final Category category = Category.getInstance("TestCategory"); + final Priority initialPriority = category.getPriority(); + category.setPriority(Level.FATAL); + assertEquals(initialPriority, category.getPriority(), "priority should be unchanged when compatibility is off"); + } + + @Test + @SetTestProperty(key = "log4j1.compatibility", value = "false") + void testSetResourceBundleCompatibilityDisabled() { + final Category category = Category.getInstance("TestCategory"); + final ResourceBundle bundle = ResourceBundle.getBundle("L7D", new Locale("en", "US")); + category.setResourceBundle(bundle); + assertNull(category.getResourceBundle(), "resource bundle should not be set when compatibility is off"); + } + + @Test + @SetTestProperty(key = "log4j1.compatibility", value = "false") + void testRemoveAppenderCompatibilityDisabled() { + final Category category = Category.getInstance("TestCategory"); + assertDoesNotThrow( + () -> category.removeAppender("TestAppender"), + "removeAppender should be a no-op and not throw exceptions when compatibility is off"); + } + + @Test + @SetTestProperty(key = "log4j1.compatibility", value = "false") + void testRemoveAllAppendersCompatibilityDisabled() { + final Category category = Category.getInstance("TestCategory"); + assertDoesNotThrow( + category::removeAllAppenders, + "removeAllAppenders should be a no-op and not throw exceptions when compatibility is off"); + } + /** * Derived category to check method signature of forcedLog. */ diff --git a/log4j-1.2-api/src/test/java/org/apache/log4j/LogManagerTest.java b/log4j-1.2-api/src/test/java/org/apache/log4j/LogManagerTest.java index 501d2c36709..b916ae1ad7d 100644 --- a/log4j-1.2-api/src/test/java/org/apache/log4j/LogManagerTest.java +++ b/log4j-1.2-api/src/test/java/org/apache/log4j/LogManagerTest.java @@ -16,17 +16,20 @@ */ package org.apache.log4j; +import static org.junit.jupiter.api.Assertions.assertDoesNotThrow; import static org.junit.jupiter.api.Assertions.assertTrue; import java.util.Collections; import java.util.Enumeration; import java.util.List; import java.util.stream.Collectors; +import org.apache.logging.log4j.test.junit.SetTestProperty; import org.junit.jupiter.api.Test; /** * Tests {@link LogManager}. */ +@SetTestProperty(key = "log4j1.compatibility", value = "true") class LogManagerTest { private static final String SIMPLE_NAME = LogManagerTest.class.getSimpleName(); @@ -47,4 +50,20 @@ void testGetCurrentLoggers() { assertTrue(names.contains(SIMPLE_NAME + ".foo")); assertTrue(names.contains(SIMPLE_NAME + ".foo.bar")); } + + @Test + @SetTestProperty(key = "log4j1.compatibility", value = "false") + void testResetConfigurationCompatibilityDisabled() { + assertDoesNotThrow( + () -> LogManager.resetConfiguration(), + "resetConfiguration should be a no-op and not throw exceptions when compatibility is off"); + } + + @Test + @SetTestProperty(key = "log4j1.compatibility", value = "false") + void testShutdownCompatibilityDisabled() { + assertDoesNotThrow( + () -> LogManager.shutdown(), + "shutdown should be a no-op and not throw exceptions when compatibility is off"); + } } diff --git a/log4j-1.2-api/src/test/java/org/apache/log4j/LoggerTest.java b/log4j-1.2-api/src/test/java/org/apache/log4j/LoggerTest.java index f1f93599d8b..b90f3b527b5 100644 --- a/log4j-1.2-api/src/test/java/org/apache/log4j/LoggerTest.java +++ b/log4j-1.2-api/src/test/java/org/apache/log4j/LoggerTest.java @@ -38,6 +38,7 @@ import org.apache.logging.log4j.core.config.Property; import org.apache.logging.log4j.core.layout.PatternLayout; import org.apache.logging.log4j.core.test.appender.ListAppender; +import org.apache.logging.log4j.test.junit.SetTestProperty; import org.junit.jupiter.api.AfterAll; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.BeforeEach; @@ -46,6 +47,7 @@ /** * Used for internal unit testing the Logger class. */ +@SetTestProperty(key = "log4j1.compatibility", value = "true") class LoggerTest { Appender a1; diff --git a/src/changelog/.2.x.x/3667_respect_log4j1_compatibility_flag.xml b/src/changelog/.2.x.x/3667_respect_log4j1_compatibility_flag.xml new file mode 100644 index 00000000000..9b5178d5194 --- /dev/null +++ b/src/changelog/.2.x.x/3667_respect_log4j1_compatibility_flag.xml @@ -0,0 +1,12 @@ + + + + + Programmatic configuration changes using the Log4j 1.x API now respect the `log4j1.compatibility` flag. + +