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/24 18:00:28 UTC

[logging-log4j2] 07/11: [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 master
in repository https://gitbox.apache.org/repos/asf/logging-log4j2.git

commit e0d25b863a1732ce4a56775b66438abce6838ecd
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.
    
    Conflicts:
            log4j-1.2-api/src/test/java/org/apache/log4j/CategoryTest.java
---
 .../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   | 100 +++++++++++++++++++--
 .../test/java/org/apache/log4j/ListAppender.java   |   4 +
 .../config/PropertiesReconfigurationTest.java      |  16 +---
 6 files changed, 183 insertions(+), 38 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 e490df3c45..e474681eb4 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
@@ -17,6 +17,23 @@
 
 package org.apache.log4j;
 
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertFalse;
+import static org.junit.jupiter.api.Assertions.assertNotNull;
+import static org.junit.jupiter.api.Assertions.assertNull;
+import static org.junit.jupiter.api.Assertions.assertSame;
+import static org.junit.jupiter.api.Assertions.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.config.ConfigurationFactory;
@@ -34,24 +51,20 @@ import org.junit.jupiter.api.BeforeAll;
 import org.junit.jupiter.api.BeforeEach;
 import org.junit.jupiter.api.Test;
 
-import java.lang.reflect.Method;
-import java.util.Collections;
-import java.util.List;
-import java.util.Map;
-import java.util.function.Consumer;
-
-import static org.junit.jupiter.api.Assertions.*;
-
 /**
  * Tests of Category.
  */
 public class CategoryTest {
 
-    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();
 
     @BeforeAll
     public static void setupClass() {
         appender.start();
+        version1Appender.setName(VERSION1_APPENDER_NAME);
         System.setProperty(ConfigurationFactory.CONFIGURATION_FACTORY_PROPERTY, BasicConfigurationFactory.class.getName());
     }
 
@@ -328,6 +341,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(++count, version1Appender.getEvents().size(), "adding at root works");
+            assertEquals(0, appender.getEvents().size(), "adding at child works");
+            // Another logger
+            logger.info("testAddLogger2");
+            assertEquals(++count, version1Appender.getEvents().size(), "adding at root works");
+            assertEquals(1, appender.getEvents().size(), "adding at child works");
+            // Call appenders
+            final LoggingEvent event = new LoggingEvent();
+            logger.callAppenders(event);
+            assertEquals(++count, version1Appender.getEvents().size(), "callAppenders");
+            assertEquals(2, appender.getEvents().size(), "callAppenders");
+        } 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(1, rootAppenders.size(), "only v1 appenders");
+            assertTrue(rootAppenders.get(0) instanceof org.apache.log4j.ListAppender, "appender is a v1 ListAppender");
+            assertEquals(VERSION1_APPENDER_NAME, rootLogger.getAppender(VERSION1_APPENDER_NAME).getName(),
+                    "explicitly named appender");
+            Appender v2ListAppender = rootLogger.getAppender(VERSION2_APPENDER_NAME);
+            assertTrue(v2ListAppender instanceof AppenderWrapper, "explicitly named appender");
+            assertTrue(((AppenderWrapper) v2ListAppender).getAppender() instanceof ListAppender,
+                    "appender is a v2 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(1, appenders.size(), "no parent appenders");
+            assertEquals(loggerAppender, appenders.get(0));
+            assertNull(logger.getAppender(VERSION1_APPENDER_NAME), "no parent appenders");
+            assertNull(logger.getAppender(VERSION2_APPENDER_NAME), "no parent appenders");
+
+            final Logger childLogger = LogManager.getLogger(CategoryTest.class.getName() + ".child");
+            final Enumeration<Appender> childAppenders = childLogger.getAllAppenders();
+            assertFalse(childAppenders.hasMoreElements(), "no parent appenders");
+            assertNull(childLogger.getAppender("appender2"), "no parent appenders");
+            assertNull(childLogger.getAppender(VERSION1_APPENDER_NAME), "no parent appenders");
+            assertNull(childLogger.getAppender(VERSION2_APPENDER_NAME), "no parent appenders");
+        } 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 ae2910e41f..c7774d4daf 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();