You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@logging.apache.org by pk...@apache.org on 2022/04/09 21:15:45 UTC

[logging-log4j2] branch release-2.x updated: [LOG4J2-3440] Synchronize Log4j 1.x and Log4j 2.x appenders

This is an automated email from the ASF dual-hosted git repository.

pkarwasz pushed a commit to branch release-2.x
in repository https://gitbox.apache.org/repos/asf/logging-log4j2.git


The following commit(s) were added to refs/heads/release-2.x by this push:
     new dfc215713a [LOG4J2-3440] Synchronize Log4j 1.x and Log4j 2.x appenders
dfc215713a is described below

commit dfc215713abbd211e60a81074f177f49bbd9e406
Author: Piotr P. Karwasz <pi...@karwasz.org>
AuthorDate: Sun Apr 3 14:35:24 2022 +0200

    [LOG4J2-3440] Synchronize Log4j 1.x and Log4j 2.x appenders
    
    Forwards the `addAppender`, `callAppenders`, `getAllAppenders` and
    `getAppender` calls the the corresponding Log4j 2.x objects if Log4j 2.x
    Core is present.
    
    The `getAllAppenders` method only returns those appenders, which are
    attached to homonymous logger config and are native Log4j 1.x appenders.
---
 .../src/main/java/org/apache/log4j/Category.java   | 49 +++++++++----
 .../org/apache/log4j/bridge/AppenderAdapter.java   |  7 +-
 .../org/apache/log4j/legacy/core/CategoryUtil.java | 45 +++++++++++-
 .../test/java/org/apache/log4j/CategoryTest.java   | 80 +++++++++++++++++++++-
 .../test/java/org/apache/log4j/ListAppender.java   |  4 ++
 .../config/PropertiesReconfigurationTest.java      | 16 ++---
 6 files changed, 171 insertions(+), 30 deletions(-)

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 cfafe8d1ea..0ebffc9c77 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
@@ -16,13 +16,18 @@
  */
 package org.apache.log4j;
 
+import java.util.Collection;
+import java.util.Collections;
 import java.util.Enumeration;
 import java.util.Map;
 import java.util.ResourceBundle;
 import java.util.Vector;
 import java.util.concurrent.ConcurrentMap;
+import java.util.stream.Collectors;
 
+import org.apache.log4j.bridge.AppenderAdapter;
 import org.apache.log4j.bridge.AppenderWrapper;
+import org.apache.log4j.bridge.LogEventWrapper;
 import org.apache.log4j.helpers.AppenderAttachableImpl;
 import org.apache.log4j.helpers.NullEnumeration;
 import org.apache.log4j.legacy.core.CategoryUtil;
@@ -32,6 +37,7 @@ import org.apache.log4j.spi.AppenderAttachable;
 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.LoggerConfig;
 import org.apache.logging.log4j.message.LocalizedMessage;
 import org.apache.logging.log4j.message.MapMessage;
 import org.apache.logging.log4j.message.Message;
@@ -194,11 +200,17 @@ public class Category implements AppenderAttachable {
      */
     @Override
     public void addAppender(final Appender appender) {
-        if (aai == null) {
-            aai = new AppenderAttachableImpl();
-        }
-        aai.addAppender(appender);
         if (appender != null) {
+            if (LogManager.isLog4jCorePresent()) {
+                CategoryUtil.addAppender(logger, AppenderAdapter.adapt(appender));
+            } else {
+                synchronized (this) {
+                    if (aai == null) {
+                        aai = new AppenderAttachableImpl();
+                    }
+                    aai.addAppender(appender);
+                }
+            }
             repository.fireAddAppenderEvent(this, appender);
         }
     }
@@ -233,6 +245,10 @@ public class Category implements AppenderAttachable {
      * @param event the event to log.
      */
     public void callAppenders(final LoggingEvent event) {
+        if (LogManager.isLog4jCorePresent()) {
+            CategoryUtil.log(logger, new LogEventWrapper(event));
+            return;
+        }
         int writes = 0;
         for (Category c = this; c != null; c = c.parent) {
             // Protected against simultaneous call to addAppender, removeAppender,...
@@ -353,14 +369,23 @@ public class Category implements AppenderAttachable {
     }
 
     /**
-     * Get the appenders contained in this category as an {@link Enumeration}. If no appenders can be found, then a
-     * {@link NullEnumeration} is returned.
+     * Get all the Log4j 1.x appenders contained in this category as an
+     * {@link Enumeration}. Log4j 2.x appenders are omitted.
      *
      * @return Enumeration An enumeration of the appenders in this category.
      */
     @Override
-    @SuppressWarnings("rawtypes")
+    @SuppressWarnings("unchecked")
     public Enumeration getAllAppenders() {
+        if (LogManager.isLog4jCorePresent()) {
+            final Collection<org.apache.logging.log4j.core.Appender> appenders = CategoryUtil.getAppenders(logger)
+                    .values();
+            return Collections.enumeration(appenders.stream()
+                    // omit native Log4j 2.x appenders
+                    .filter(AppenderAdapter.Adapter.class::isInstance)
+                    .map(AppenderWrapper::adapt)
+                    .collect(Collectors.toSet()));
+        }
         return aai == null ? NullEnumeration.getInstance() : aai.getAllAppenders();
     }
 
@@ -372,14 +397,10 @@ public class Category implements AppenderAttachable {
      */
     @Override
     public Appender getAppender(final String name) {
-        Appender appender = aai != null ? aai.getAppender(name) : null;
-        if (appender == null && LogManager.isLog4jCorePresent()) {
-            final org.apache.logging.log4j.core.Appender coreAppender = CategoryUtil.getAppenders(logger).get(name);
-            if (coreAppender != null) {
-                addAppender(appender = AppenderWrapper.adapt(coreAppender));
-            }
+        if (LogManager.isLog4jCorePresent()) {
+            return AppenderWrapper.adapt(CategoryUtil.getAppenders(logger).get(name));
         }
-        return appender;
+        return aai != null ? aai.getAppender(name) : null;
     }
 
     public Priority getChainedPriority() {
diff --git a/log4j-1.2-api/src/main/java/org/apache/log4j/bridge/AppenderAdapter.java b/log4j-1.2-api/src/main/java/org/apache/log4j/bridge/AppenderAdapter.java
index f57e29deab..23931b2ee6 100644
--- a/log4j-1.2-api/src/main/java/org/apache/log4j/bridge/AppenderAdapter.java
+++ b/log4j-1.2-api/src/main/java/org/apache/log4j/bridge/AppenderAdapter.java
@@ -24,6 +24,7 @@ import org.apache.logging.log4j.core.Layout;
 import org.apache.logging.log4j.core.LogEvent;
 import org.apache.logging.log4j.core.appender.AbstractAppender;
 import org.apache.logging.log4j.core.config.Property;
+import org.apache.logging.log4j.util.Strings;
 
 /**
  * Binds a Log4j 1.x Appender to Log4j 2.
@@ -62,7 +63,11 @@ public class AppenderAdapter {
     private AppenderAdapter(Appender appender) {
         this.appender = appender;
         final org.apache.logging.log4j.core.Filter appenderFilter = FilterAdapter.adapt(appender.getFilter());
-        this.adapter = new Adapter(appender.getName(), appenderFilter, null, true, null);
+        String name = appender.getName();
+        if (Strings.isEmpty(name)) {
+            name = String.format("0x%08x", appender.hashCode());
+        }
+        this.adapter = new Adapter(name, appenderFilter, null, true, null);
     }
 
     public Adapter getAdapter() {
diff --git a/log4j-1.2-api/src/main/java/org/apache/log4j/legacy/core/CategoryUtil.java b/log4j-1.2-api/src/main/java/org/apache/log4j/legacy/core/CategoryUtil.java
index ea6b43cd0e..3e5dc2c515 100644
--- a/log4j-1.2-api/src/main/java/org/apache/log4j/legacy/core/CategoryUtil.java
+++ b/log4j-1.2-api/src/main/java/org/apache/log4j/legacy/core/CategoryUtil.java
@@ -16,14 +16,19 @@
  */
 package org.apache.log4j.legacy.core;
 
+import java.util.Collections;
 import java.util.Iterator;
 import java.util.Map;
+import java.util.Optional;
 import java.util.function.Supplier;
 
+import org.apache.log4j.bridge.AppenderAdapter;
 import org.apache.logging.log4j.Level;
 import org.apache.logging.log4j.Logger;
 import org.apache.logging.log4j.core.Appender;
 import org.apache.logging.log4j.core.Filter;
+import org.apache.logging.log4j.core.LogEvent;
+import org.apache.logging.log4j.core.config.LoggerConfig;
 import org.apache.logging.log4j.spi.LoggerContext;
 
 /**
@@ -40,13 +45,23 @@ public final class CategoryUtil {
     }
 
     /**
-     * Delegates to {@link org.apache.logging.log4j.core.Logger#getAppenders()} if appropriate.
+     * Gets the appenders attached directly to this logger.
      *
      * @param logger The target logger.
      * @return A Map containing the Appender's name as the key and the Appender as the value.
      */
     public static Map<String, Appender> getAppenders(final Logger logger) {
-        return get(logger, asCore(logger)::getAppenders, null);
+        return get(logger, () -> getDirectAppenders(logger), Collections.emptyMap());
+    }
+
+    private static Map<String, Appender> getDirectAppenders(final Logger logger) {
+        return CategoryUtil.getExactLoggerConfig(logger)
+                .map(LoggerConfig::getAppenders)
+                .orElse(Collections.emptyMap());
+    }
+
+    private static Optional<LoggerConfig> getExactLoggerConfig(final Logger logger) {
+        return Optional.of(asCore(logger).get()).filter(lc -> logger.getName().equals(lc.getName()));
     }
 
     /**
@@ -117,6 +132,32 @@ public final class CategoryUtil {
         }
     }
 
+    /**
+     * Adds an appender to the logger. This method requires a check for the presence
+     * of Log4j Core or it will cause a {@code ClassNotFoundException}.
+     * 
+     * @param logger   The target logger.
+     * @param appender A Log4j2 appender.
+     */
+    public static void addAppender(final Logger logger, final Appender appender) {
+        if (appender instanceof AppenderAdapter.Adapter) {
+            appender.start();
+        }
+        asCore(logger).addAppender(appender);
+    }
+
+    /**
+     * Sends the event to all appenders directly connected with the logger. This
+     * method requires a check for the presence of Log4j Core or it will cause a
+     * {@code ClassNotFoundException}.
+     * 
+     * @param logger The target logger.
+     * @param event  The event to send.
+     */
+    public static void log(final Logger logger, final LogEvent event) {
+        getExactLoggerConfig(logger).ifPresent(lc -> lc.log(event));
+    }
+
     private CategoryUtil() {
     }
 }
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 c2be94c073..a1b8334f4c 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
@@ -18,16 +18,21 @@
 package org.apache.log4j;
 
 import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertNotNull;
 import static org.junit.Assert.assertNull;
 import static org.junit.Assert.assertTrue;
 
 import java.lang.reflect.Method;
 import java.util.Collections;
+import java.util.Enumeration;
 import java.util.List;
 import java.util.Map;
 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.logging.log4j.core.Layout;
 import org.apache.logging.log4j.core.LogEvent;
 import org.apache.logging.log4j.core.LoggerContext;
@@ -52,11 +57,15 @@ public class CategoryTest {
 
     static ConfigurationFactory cf = new BasicConfigurationFactory();
 
-    private static ListAppender appender = new ListAppender("List");
+    private static final String VERSION1_APPENDER_NAME = "Version1List";
+    private static final String VERSION2_APPENDER_NAME = "List";
+    private static ListAppender appender = new ListAppender(VERSION2_APPENDER_NAME);
+    private static org.apache.log4j.ListAppender version1Appender = new org.apache.log4j.ListAppender();
 
     @BeforeClass
     public static void setupClass() {
         appender.start();
+        version1Appender.setName(VERSION1_APPENDER_NAME);
         ConfigurationFactory.setConfigurationFactory(cf);
         LoggerContext.getContext().reconfigure();
     }
@@ -335,6 +344,75 @@ public class CategoryTest {
 
     }
 
+    @Test
+    public void testAddAppender() {
+        try {
+            final Logger rootLogger = LogManager.getRootLogger();
+            int count = version1Appender.getEvents().size();
+            rootLogger.addAppender(version1Appender);
+            final Logger logger = LogManager.getLogger(CategoryTest.class);
+            final org.apache.log4j.ListAppender appender = new org.apache.log4j.ListAppender();
+            appender.setName("appender2");
+            logger.addAppender(appender);
+            // Root logger
+            rootLogger.info("testAddLogger");
+            assertEquals("adding at root works", ++count, version1Appender.getEvents().size());
+            assertEquals("adding at child works", 0, appender.getEvents().size());
+            // Another logger
+            logger.info("testAddLogger2");
+            assertEquals("adding at root works", ++count, version1Appender.getEvents().size());
+            assertEquals("adding at child works", 1, appender.getEvents().size());
+            // Call appenders
+            final LoggingEvent event = new LoggingEvent();
+            logger.callAppenders(event);
+            assertEquals("callAppenders", ++count, version1Appender.getEvents().size());
+            assertEquals("callAppenders", 2, appender.getEvents().size());
+        } finally {
+            LogManager.resetConfiguration();
+        }
+    }
+
+    @Test
+    public void testGetAppender() {
+        try {
+            final Logger rootLogger = LogManager.getRootLogger();
+            final org.apache.logging.log4j.core.Logger v2RootLogger = (org.apache.logging.log4j.core.Logger) rootLogger
+                    .getLogger();
+            v2RootLogger.addAppender(AppenderAdapter.adapt(version1Appender));
+            v2RootLogger.addAppender(appender);
+            final List<Appender> rootAppenders = Collections.list(rootLogger.getAllAppenders());
+            assertEquals("only v1 appenders", 1, rootAppenders.size());
+            assertTrue("appender is a v1 ListAppender", rootAppenders.get(0) instanceof org.apache.log4j.ListAppender);
+            assertEquals("explicitly named appender", VERSION1_APPENDER_NAME,
+                    rootLogger.getAppender(VERSION1_APPENDER_NAME).getName());
+            Appender v2ListAppender = rootLogger.getAppender(VERSION2_APPENDER_NAME);
+            assertTrue("explicitly named appender", v2ListAppender instanceof AppenderWrapper);
+            assertTrue("appender is a v2 ListAppender",
+                    ((AppenderWrapper) v2ListAppender).getAppender() instanceof ListAppender);
+
+            final Logger logger = LogManager.getLogger(CategoryTest.class);
+            final org.apache.logging.log4j.core.Logger v2Logger = (org.apache.logging.log4j.core.Logger) logger
+                    .getLogger();
+            final org.apache.log4j.ListAppender loggerAppender = new org.apache.log4j.ListAppender();
+            loggerAppender.setName("appender2");
+            v2Logger.addAppender(AppenderAdapter.adapt(loggerAppender));
+            final List<Appender> appenders = Collections.list(logger.getAllAppenders());
+            assertEquals("no parent appenders", 1, appenders.size());
+            assertEquals(loggerAppender, appenders.get(0));
+            assertNull("no parent appenders", logger.getAppender(VERSION1_APPENDER_NAME));
+            assertNull("no parent appenders", logger.getAppender(VERSION2_APPENDER_NAME));
+
+            final Logger childLogger = LogManager.getLogger(CategoryTest.class.getName() + ".child");
+            final Enumeration<Appender> childAppenders = childLogger.getAllAppenders();
+            assertFalse("no parent appenders", childAppenders.hasMoreElements());
+            assertNull("no parent appenders", childLogger.getAppender("appender2"));
+            assertNull("no parent appenders", childLogger.getAppender(VERSION1_APPENDER_NAME));
+            assertNull("no parent appenders", childLogger.getAppender(VERSION2_APPENDER_NAME));
+        } finally {
+            LogManager.resetConfiguration();
+        }
+    }
+
     /**
      * Derived category to check method signature of forcedLog.
      */
diff --git a/log4j-1.2-api/src/test/java/org/apache/log4j/ListAppender.java b/log4j-1.2-api/src/test/java/org/apache/log4j/ListAppender.java
index 573d98673c..c1d5a799aa 100644
--- a/log4j-1.2-api/src/test/java/org/apache/log4j/ListAppender.java
+++ b/log4j-1.2-api/src/test/java/org/apache/log4j/ListAppender.java
@@ -80,4 +80,8 @@ public class ListAppender extends AppenderSkeleton {
         }
         return getMessages();
     }
+
+    public String toString() {
+        return String.format("ListAppender[%s]", getName());
+    }
 }
diff --git a/log4j-1.2-api/src/test/java/org/apache/log4j/config/PropertiesReconfigurationTest.java b/log4j-1.2-api/src/test/java/org/apache/log4j/config/PropertiesReconfigurationTest.java
index 1b40845e01..f2abe26cd8 100644
--- a/log4j-1.2-api/src/test/java/org/apache/log4j/config/PropertiesReconfigurationTest.java
+++ b/log4j-1.2-api/src/test/java/org/apache/log4j/config/PropertiesReconfigurationTest.java
@@ -128,24 +128,15 @@ public class PropertiesReconfigurationTest {
     }
 
     private void checkCustomAppender(final String appenderName, final boolean expectBoolean, final int expectInt, final String expectString) {
-        final Logger logger = LogManager.getLogger("test");
+        final Logger logger = LogManager.getRootLogger();
         final org.apache.log4j.Appender appender = logger.getAppender(appenderName);
         assertNotNull(appender);
         assertCustomNoopAppender(appender, expectBoolean, expectInt, expectString);
         assertCustomNoopAppender(getAppenderFromContext(appenderName), expectBoolean, expectInt, expectString);
     }
 
-    private void checkCustomFileAppender(final boolean expectAppend, final Appender appender) {
-        assertNotNull(appender);
-        final FileAppender fileAppender = (FileAppender) appender;
-        @SuppressWarnings("resource")
-        final FileManager manager = fileAppender.getManager();
-        assertNotNull(manager);
-        assertEquals(expectAppend, manager.isAppend());
-    }
-
     private void checkCustomFileAppender(final String appenderName, final boolean expectBoolean, final int expectInt, final String expectString) {
-        final Logger logger = LogManager.getLogger("test");
+        final Logger logger = LogManager.getRootLogger();
         final org.apache.log4j.Appender appender = logger.getAppender(appenderName);
         assertNotNull(appender);
         assertCustomFileAppender(appender, expectBoolean, expectInt, expectString);
@@ -153,13 +144,14 @@ public class PropertiesReconfigurationTest {
     }
 
     private void checkFileAppender(final boolean expectAppend, final String appenderName) {
-        final Logger logger = LogManager.getLogger("test");
+        final Logger logger = LogManager.getRootLogger();
         final org.apache.log4j.Appender appender = logger.getAppender(appenderName);
         assertNotNull(appender);
         final AppenderWrapper appenderWrapper = (AppenderWrapper) appender;
         checkCoreFileAppender(expectAppend, appenderWrapper.getAppender());
     }
 
+    @SuppressWarnings("unchecked")
     private <T extends org.apache.log4j.Appender> T getAppenderFromContext(final String appenderName) {
         final LoggerContext context = (LoggerContext) org.apache.logging.log4j.LogManager.getContext(false);
         final LoggerConfig loggerConfig = context.getConfiguration().getRootLogger();