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/03/29 14:05:20 UTC

[logging-log4j2] branch master updated (b63d490 -> b0a74d7)

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

pkarwasz pushed a change to branch master
in repository https://gitbox.apache.org/repos/asf/logging-log4j2.git.


    from b63d490  Refactor common DOM parsing code.
     new 936d6e2  Don't use deprecated methods.
     new ef2851f  Remove unused import.
     new 807e845  Remove unused constants.
     new 859f5c7  Remove unused imports.
     new fc3cf56  Remove unused exceptions from test method signatures.
     new b08d466  Log4j 1.2 bridge now supports SocketAppender.
     new 9742cc5  Add TODO.
     new 20f18e6  Convert AtomicReferences to Holders
     new 2dacaea  Log4j 1.2 bridge missing DefaultThrowableRenderer.
     new 2f430ad  Log4j 1.2 bridge missing some ThrowableInformation constructors.
     new f29e900  Add ThrowableInformationTest.
     new 7b7a12c  Format class.
     new c4ce19f  Log4j 1.2 bridge missing some LocationInfo constructors.
     new 0df43d9  Update for Carter's comments.
     new 7d55af7  Format tweaks.
     new a129b24  Add missing util classes in org.apache.log4j.pattern.
     new e619cb0  Better error reporting.
     new 06fe59a  Add test that show we can configure a Log4j 1 properties file with a socket appender using an XML layout.
     new 68afef6  Assert XML layout presence.
     new adda476  Log4j 1.2 bridge missing UtilLoggingLevel.
     new c5d1c6c  Add missing tests from Log4j 1.2.17.
     new 83c5193  Add more missing code and tests from Log4j 1.2.17.
     new 3ff6b86  Issues with multiple Log4j 1.x filters (#753)
     new f24c834  Catch all FileAppenderTest asynchronous errors
     new b0a74d7  FileAppenderTest: increase thread pool termination timeout

The 25 revisions listed above as "new" are entirely new to this
repository and will be described in separate emails.  The revisions
listed as "add" were already present in the repository and have only
been added to this reference.


Summary of changes:
 .../java/org/apache/log4j/AppenderSkeleton.java    |   5 +-
 .../org/apache/log4j/DefaultThrowableRenderer.java |  84 +++
 .../main/java/org/apache/log4j/PatternLayout.java  |  76 ++-
 .../org/apache/log4j/PropertyConfigurator.java     |   5 +-
 .../org/apache/log4j/bridge/AppenderAdapter.java   |  16 +-
 .../org/apache/log4j/bridge/AppenderWrapper.java   |   6 +-
 .../org/apache/log4j/bridge/FilterAdapter.java     |  43 +-
 .../org/apache/log4j/builders/AbstractBuilder.java |  38 +-
 .../org/apache/log4j/builders/BuilderManager.java  |  10 +-
 .../builders/appender/AsyncAppenderBuilder.java    |  39 +-
 .../builders/appender/ConsoleAppenderBuilder.java  |  17 +-
 .../appender/DailyRollingFileAppenderBuilder.java  |   3 +-
 .../builders/appender/FileAppenderBuilder.java     |   2 +-
 .../builders/appender/RewriteAppenderBuilder.java  |   2 +-
 .../appender/RollingFileAppenderBuilder.java       |   2 +-
 .../builders/appender/SocketAppenderBuilder.java   | 153 ++++++
 .../builders/appender/SyslogAppenderBuilder.java   |   2 +-
 .../builders/filter/DenyAllFilterBuilder.java      |   8 +-
 .../builders/filter/LevelMatchFilterBuilder.java   |   3 -
 .../log4j/builders/layout/SimpleLayoutBuilder.java |   8 +-
 .../log4j}/builders/layout/XmlLayoutBuilder.java   |  35 +-
 .../log4j/config/PropertiesConfiguration.java      |  28 +-
 .../FormattingInfo.java}                           |  27 +-
 .../org/apache/log4j/helpers/OptionConverter.java  |   3 +-
 .../org/apache/log4j/helpers/PatternConverter.java | 111 ++++
 .../org/apache/log4j/helpers/PatternParser.java    | 570 +++++++++++++++++++++
 .../org/apache/log4j/helpers/UtilLoggingLevel.java | 236 +++++++++
 .../org/apache/log4j/layout/Log4j1XmlLayout.java   |   2 +-
 .../org/apache/log4j/pattern/FormattingInfo.java   | 128 +++++
 .../org/apache/log4j/pattern/NameAbbreviator.java  | 345 +++++++++++++
 .../src/main/java/org/apache/log4j/spi/Filter.java |  12 -
 .../java/org/apache/log4j/spi/LocationInfo.java    | 112 +++-
 .../org/apache/log4j/spi/ThrowableInformation.java |  59 ++-
 .../java/org/apache/log4j/xml/DOMConfigurator.java |   3 +-
 .../org/apache/log4j/xml/XmlConfiguration.java     |  48 +-
 .../org/apache/log4j/CallerInformationTest.java    |   4 +-
 .../test/java/org/apache/log4j/CategoryTest.java   |   2 +-
 .../src/test/java/org/apache/log4j/LayoutTest.java | 168 ++++++
 .../test/java/org/apache/log4j/LogWithMDCTest.java |   2 +-
 .../java/org/apache/log4j/LogWithRouteTest.java    |   2 +-
 .../test/java/org/apache/log4j/MDCTestCase.java    |   4 +-
 .../org/apache/log4j/PropertyConfiguratorTest.java |   2 +-
 .../log4j/builders/Log4j2ListAppenderBuilder.java  |  98 ++++
 .../config/AbstractLog4j1ConfigurationTest.java    | 124 ++++-
 .../config/Log4j1ConfigurationFactoryTest.java     |  15 +-
 .../log4j/config/MapRewriteAppenderTest.java       |   2 +-
 .../config/PropertiesConfigurationFactoryTest.java |   2 +-
 .../log4j/config/PropertiesConfigurationTest.java  |  12 +-
 .../apache/log4j/config/RewriteAppenderTest.java   |   2 +-
 ...t.java => SocketAppenderConfigurationTest.java} |  53 +-
 ...ralFilterFixture.java => StartsWithFilter.java} |  70 +--
 .../config/SyslogAppenderConfigurationTest.java    |   2 -
 .../apache/log4j/config/SyslogAppenderTest.java    |   1 +
 .../log4j/config/XmlConfigurationFactoryTest.java  |   2 +-
 .../apache/log4j/config/XmlConfigurationTest.java  |   6 +
 .../apache/log4j/helpers/BoundedFIFOTestCase.java  | 233 +++++++++
 .../apache/log4j/helpers/CyclicBufferTestCase.java | 159 ++++++
 .../org/apache/log4j/helpers/DateLayoutTest.java   | 288 +++++++++++
 .../java/org/apache/log4j/helpers/LogLogTest.java} |  40 +-
 .../log4j/helpers/OptionConverterTestCase.java     | 177 +++++++
 .../log4j/helpers/PatternParserTestCase.java       | 131 +++++
 .../log4j/helpers/UtilLoggingLevelTest.java}       |  33 +-
 .../apache/log4j/pattern/FormattingInfoTest.java   |  92 ++++
 .../apache/log4j/pattern/NameAbbreviatorTest.java  | 329 ++++++++++++
 .../org/apache/log4j/spi/LocationInfoTest.java     |  82 +++
 .../apache/log4j/spi/ThrowableInformationTest.java | 338 ++++++++++++
 .../test/java/org/apache/log4j/util/Compare.java   | 150 ++++++
 .../src/test/java/org/apache/log4j/xml/XLevel.java |  74 +++
 .../resources/log4j-multipleFilters.properties     |  69 +++
 .../src/test/resources/log4j-multipleFilters.xml   |  92 ++++
 ...perties => log4j1-socket-xml-layout.properties} |   9 +-
 ...default.properties => log4j1-socket.properties} |  16 +-
 .../{log4j1-syslog.xml => log4j1-socket.xml}       |  10 +-
 .../log4j/core/appender/FileAppenderTest.java      |  61 ++-
 .../log4j/core/filter/CompositeFilterTest.java     |  49 ++
 .../logging/log4j/core/filter/CompositeFilter.java |   4 +-
 src/changes/changes.xml                            |  33 +-
 77 files changed, 4882 insertions(+), 401 deletions(-)
 create mode 100644 log4j-1.2-api/src/main/java/org/apache/log4j/DefaultThrowableRenderer.java
 create mode 100644 log4j-1.2-api/src/main/java/org/apache/log4j/builders/appender/SocketAppenderBuilder.java
 copy {log4j-layout-jackson-xml/src/main/java/org/apache/logging/log4j/jackson/xml => log4j-1.2-api/src/main/java/org/apache/log4j}/builders/layout/XmlLayoutBuilder.java (78%)
 copy log4j-1.2-api/src/main/java/org/apache/log4j/{or/DefaultRenderer.java => helpers/FormattingInfo.java} (63%)
 create mode 100644 log4j-1.2-api/src/main/java/org/apache/log4j/helpers/PatternConverter.java
 create mode 100644 log4j-1.2-api/src/main/java/org/apache/log4j/helpers/PatternParser.java
 create mode 100644 log4j-1.2-api/src/main/java/org/apache/log4j/helpers/UtilLoggingLevel.java
 create mode 100644 log4j-1.2-api/src/main/java/org/apache/log4j/pattern/FormattingInfo.java
 create mode 100644 log4j-1.2-api/src/main/java/org/apache/log4j/pattern/NameAbbreviator.java
 create mode 100644 log4j-1.2-api/src/test/java/org/apache/log4j/LayoutTest.java
 create mode 100644 log4j-1.2-api/src/test/java/org/apache/log4j/builders/Log4j2ListAppenderBuilder.java
 copy log4j-1.2-api/src/test/java/org/apache/log4j/config/{SyslogAppenderConfigurationTest.java => SocketAppenderConfigurationTest.java} (59%)
 copy log4j-1.2-api/src/test/java/org/apache/log4j/config/{NeutralFilterFixture.java => StartsWithFilter.java} (75%)
 create mode 100644 log4j-1.2-api/src/test/java/org/apache/log4j/helpers/BoundedFIFOTestCase.java
 create mode 100644 log4j-1.2-api/src/test/java/org/apache/log4j/helpers/CyclicBufferTestCase.java
 create mode 100644 log4j-1.2-api/src/test/java/org/apache/log4j/helpers/DateLayoutTest.java
 copy log4j-1.2-api/src/{main/java/org/apache/log4j/spi/ThrowableRendererSupport.java => test/java/org/apache/log4j/helpers/LogLogTest.java} (55%)
 create mode 100644 log4j-1.2-api/src/test/java/org/apache/log4j/helpers/OptionConverterTestCase.java
 create mode 100644 log4j-1.2-api/src/test/java/org/apache/log4j/helpers/PatternParserTestCase.java
 copy log4j-1.2-api/src/{main/java/org/apache/log4j/spi/ThrowableRendererSupport.java => test/java/org/apache/log4j/helpers/UtilLoggingLevelTest.java} (64%)
 create mode 100644 log4j-1.2-api/src/test/java/org/apache/log4j/pattern/FormattingInfoTest.java
 create mode 100644 log4j-1.2-api/src/test/java/org/apache/log4j/pattern/NameAbbreviatorTest.java
 create mode 100644 log4j-1.2-api/src/test/java/org/apache/log4j/spi/LocationInfoTest.java
 create mode 100644 log4j-1.2-api/src/test/java/org/apache/log4j/spi/ThrowableInformationTest.java
 create mode 100644 log4j-1.2-api/src/test/java/org/apache/log4j/util/Compare.java
 create mode 100644 log4j-1.2-api/src/test/java/org/apache/log4j/xml/XLevel.java
 create mode 100644 log4j-1.2-api/src/test/resources/log4j-multipleFilters.properties
 create mode 100644 log4j-1.2-api/src/test/resources/log4j-multipleFilters.xml
 copy log4j-1.2-api/src/test/resources/{L7D_fr_CH.properties => log4j1-socket-xml-layout.properties} (72%)
 copy log4j-1.2-api/src/test/resources/{log4j1-syslog-protocol-default.properties => log4j1-socket.properties} (68%)
 copy log4j-1.2-api/src/test/resources/{log4j1-syslog.xml => log4j1-socket.xml} (84%)
 create mode 100644 log4j-core-test/src/test/java/org/apache/logging/log4j/core/filter/CompositeFilterTest.java

[logging-log4j2] 14/25: Update for Carter's comments.

Posted by pk...@apache.org.
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 0df43d9853de75f95e6d71db2ad8d465c5426e66
Author: Gary Gregory <ga...@gmail.com>
AuthorDate: Sat Feb 5 16:16:10 2022 -0500

    Update for Carter's comments.
---
 .../org/apache/log4j/spi/ThrowableInformation.java | 29 ++++++++++++----------
 1 file changed, 16 insertions(+), 13 deletions(-)

diff --git a/log4j-1.2-api/src/main/java/org/apache/log4j/spi/ThrowableInformation.java b/log4j-1.2-api/src/main/java/org/apache/log4j/spi/ThrowableInformation.java
index de81a1a..cf0002c 100644
--- a/log4j-1.2-api/src/main/java/org/apache/log4j/spi/ThrowableInformation.java
+++ b/log4j-1.2-api/src/main/java/org/apache/log4j/spi/ThrowableInformation.java
@@ -16,6 +16,7 @@
  */
 package org.apache.log4j.spi;
 
+import java.io.Serializable;
 import java.lang.reflect.Method;
 import java.util.List;
 
@@ -25,14 +26,25 @@ import org.apache.logging.log4j.core.util.Throwables;
 /**
  * Log4j's internal representation of throwables.
  */
-public class ThrowableInformation implements java.io.Serializable {
+public class ThrowableInformation implements Serializable {
 
     static final long serialVersionUID = -4748765566864322735L;
 
     private transient Throwable throwable;
     private transient Category category;
     private String[] rep;
-    private Method toStringList;
+    private static final Method TO_STRING_LIST;
+
+    static {
+        Method method = null;
+        try {
+            final Class<?> throwables = Class.forName("org.apache.logging.log4j.core.util.Throwables");
+            method = throwables.getMethod("toStringList", Throwable.class);
+        } catch (ClassNotFoundException | NoSuchMethodException ex) {
+            // Ignore the exception if Log4j-core is not present.
+        }
+        TO_STRING_LIST = method;
+    }
 
     /**
      * Constructs new instance.
@@ -49,14 +61,6 @@ public class ThrowableInformation implements java.io.Serializable {
      */
     public ThrowableInformation(Throwable throwable) {
         this.throwable = throwable;
-        Method method = null;
-        try {
-            final Class<?> throwables = Class.forName("org.apache.logging.log4j.core.util.Throwables");
-            method = throwables.getMethod("toStringList", Throwable.class);
-        } catch (ClassNotFoundException | NoSuchMethodException ex) {
-            // Ignore the exception if Log4j-core is not present.
-        }
-        this.toStringList = method;
     }
 
     /**
@@ -78,11 +82,10 @@ public class ThrowableInformation implements java.io.Serializable {
     }
 
     public synchronized String[] getThrowableStrRep() {
-        if (toStringList != null && throwable != null) {
+        if (TO_STRING_LIST != null && throwable != null) {
             try {
                 @SuppressWarnings("unchecked")
-                final
-                List<String> elements = (List<String>) toStringList.invoke(null, throwable);
+                final List<String> elements = (List<String>) TO_STRING_LIST.invoke(null, throwable);
                 if (elements != null) {
                     return elements.toArray(new String[0]);
                 }

[logging-log4j2] 05/25: Remove unused exceptions from test method signatures.

Posted by pk...@apache.org.
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 fc3cf56bb3aeba289d24577f907ff0d39594e69f
Author: Gary Gregory <ga...@gmail.com>
AuthorDate: Sat Feb 5 08:41:46 2022 -0500

    Remove unused exceptions from test method signatures.
    
    Conflicts:
    	log4j-1.2-api/src/test/java/org/apache/log4j/LogWithRouteTest.java
    	log4j-1.2-api/src/test/java/org/apache/log4j/config/MapRewriteAppenderTest.java
    	log4j-1.2-api/src/test/java/org/apache/log4j/config/RewriteAppenderTest.java
---
 log4j-1.2-api/src/main/java/org/apache/log4j/xml/DOMConfigurator.java | 3 +--
 .../src/test/java/org/apache/log4j/CallerInformationTest.java         | 4 ++--
 log4j-1.2-api/src/test/java/org/apache/log4j/CategoryTest.java        | 2 +-
 log4j-1.2-api/src/test/java/org/apache/log4j/LogWithMDCTest.java      | 2 +-
 log4j-1.2-api/src/test/java/org/apache/log4j/LogWithRouteTest.java    | 2 +-
 log4j-1.2-api/src/test/java/org/apache/log4j/MDCTestCase.java         | 4 ++--
 .../java/org/apache/log4j/config/AbstractLog4j1ConfigurationTest.java | 2 +-
 .../src/test/java/org/apache/log4j/config/MapRewriteAppenderTest.java | 2 +-
 .../org/apache/log4j/config/PropertiesConfigurationFactoryTest.java   | 2 +-
 .../src/test/java/org/apache/log4j/config/RewriteAppenderTest.java    | 2 +-
 .../java/org/apache/log4j/config/XmlConfigurationFactoryTest.java     | 2 +-
 11 files changed, 13 insertions(+), 14 deletions(-)

diff --git a/log4j-1.2-api/src/main/java/org/apache/log4j/xml/DOMConfigurator.java b/log4j-1.2-api/src/main/java/org/apache/log4j/xml/DOMConfigurator.java
index 04a4555..494208e 100644
--- a/log4j-1.2-api/src/main/java/org/apache/log4j/xml/DOMConfigurator.java
+++ b/log4j-1.2-api/src/main/java/org/apache/log4j/xml/DOMConfigurator.java
@@ -73,8 +73,7 @@ public class DOMConfigurator {
     }
 
     public static Object parseElement(final Element element, final Properties props,
-                                      @SuppressWarnings("rawtypes") final Class expectedClass)
-        throws Exception {
+                                      @SuppressWarnings("rawtypes") final Class expectedClass) {
         return null;
     }
 }
diff --git a/log4j-1.2-api/src/test/java/org/apache/log4j/CallerInformationTest.java b/log4j-1.2-api/src/test/java/org/apache/log4j/CallerInformationTest.java
index 64794a3..fd35e4a 100644
--- a/log4j-1.2-api/src/test/java/org/apache/log4j/CallerInformationTest.java
+++ b/log4j-1.2-api/src/test/java/org/apache/log4j/CallerInformationTest.java
@@ -34,7 +34,7 @@ public class CallerInformationTest {
     public static final LoggerContextRule ctx = new LoggerContextRule(CONFIG);
 
     @Test
-    public void testClassLogger() throws Exception {
+    public void testClassLogger() {
         final ListAppender app = ctx.getListAppender("Class").clear();
         final Logger logger = Logger.getLogger("ClassLogger");
         logger.info("Ignored message contents.");
@@ -48,7 +48,7 @@ public class CallerInformationTest {
     }
 
     @Test
-    public void testMethodLogger() throws Exception {
+    public void testMethodLogger() {
         final ListAppender app = ctx.getListAppender("Method").clear();
         final Logger logger = Logger.getLogger("MethodLogger");
         logger.info("More messages.");
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 abaac59..700f572 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
@@ -66,7 +66,7 @@ public class CategoryTest {
     }
 
     @Test
-    public void testExist() throws Exception {
+    public void testExist() {
         assertNull(Category.exists("Does not exist for sure"));
     }
 
diff --git a/log4j-1.2-api/src/test/java/org/apache/log4j/LogWithMDCTest.java b/log4j-1.2-api/src/test/java/org/apache/log4j/LogWithMDCTest.java
index 5e0cadb..0cc7a33 100644
--- a/log4j-1.2-api/src/test/java/org/apache/log4j/LogWithMDCTest.java
+++ b/log4j-1.2-api/src/test/java/org/apache/log4j/LogWithMDCTest.java
@@ -36,7 +36,7 @@ public class LogWithMDCTest {
     public static final LoggerContextRule CTX = new LoggerContextRule(CONFIG);
 
     @Test
-    public void testMDC() throws Exception {
+    public void testMDC() {
         MDC.put("Key1", "John");
         MDC.put("Key2", "Smith");
         try {
diff --git a/log4j-1.2-api/src/test/java/org/apache/log4j/LogWithRouteTest.java b/log4j-1.2-api/src/test/java/org/apache/log4j/LogWithRouteTest.java
index 04f448c..2770124 100644
--- a/log4j-1.2-api/src/test/java/org/apache/log4j/LogWithRouteTest.java
+++ b/log4j-1.2-api/src/test/java/org/apache/log4j/LogWithRouteTest.java
@@ -36,7 +36,7 @@ public class LogWithRouteTest {
 
     @Test
     @LoggerContextSource(CONFIG)
-    public void testMDC(final Configuration configuration) throws Exception {
+    public void testMDC(final Configuration configuration) {
         MDC.put("Type", "Service");
         MDC.put("Name", "John Smith");
         try {
diff --git a/log4j-1.2-api/src/test/java/org/apache/log4j/MDCTestCase.java b/log4j-1.2-api/src/test/java/org/apache/log4j/MDCTestCase.java
index 97bf1ff..f86a6e6 100644
--- a/log4j-1.2-api/src/test/java/org/apache/log4j/MDCTestCase.java
+++ b/log4j-1.2-api/src/test/java/org/apache/log4j/MDCTestCase.java
@@ -34,14 +34,14 @@ public class MDCTestCase {
     }
 
     @Test
-    public void testPut() throws Exception {
+    public void testPut() {
         MDC.put("key", "some value");
         Assert.assertEquals("some value", MDC.get("key"));
         Assert.assertEquals(1, MDC.getContext().size());
     }
 
     @Test
-    public void testRemoveLastKey() throws Exception {
+    public void testRemoveLastKey() {
         MDC.put("key", "some value");
         MDC.remove("key");
     }
diff --git a/log4j-1.2-api/src/test/java/org/apache/log4j/config/AbstractLog4j1ConfigurationTest.java b/log4j-1.2-api/src/test/java/org/apache/log4j/config/AbstractLog4j1ConfigurationTest.java
index 8088708..21c9b7e 100644
--- a/log4j-1.2-api/src/test/java/org/apache/log4j/config/AbstractLog4j1ConfigurationTest.java
+++ b/log4j-1.2-api/src/test/java/org/apache/log4j/config/AbstractLog4j1ConfigurationTest.java
@@ -314,7 +314,7 @@ public abstract class AbstractLog4j1ConfigurationTest {
         return (OutputStream) getOutputStream.invoke(manager);
     }
 
-    private Layout<?> testLayout(final Configuration config, final String appenderName) throws Exception {
+    private Layout<?> testLayout(final Configuration config, final String appenderName) {
         final ConsoleAppender appender = config.getAppender(appenderName);
         assertNotNull("Missing appender '" + appenderName + "' in configuration " + config.getConfigurationSource(), appender);
         return appender.getLayout();
diff --git a/log4j-1.2-api/src/test/java/org/apache/log4j/config/MapRewriteAppenderTest.java b/log4j-1.2-api/src/test/java/org/apache/log4j/config/MapRewriteAppenderTest.java
index 1f74161..e0399e7 100644
--- a/log4j-1.2-api/src/test/java/org/apache/log4j/config/MapRewriteAppenderTest.java
+++ b/log4j-1.2-api/src/test/java/org/apache/log4j/config/MapRewriteAppenderTest.java
@@ -42,7 +42,7 @@ public class MapRewriteAppenderTest {
 
     @Test
     @LoggerContextSource(value = "log4j1-mapRewrite.xml", v1config = true)
-    public void testRewrite() throws Exception {
+    public void testRewrite() {
         Logger logger = LogManager.getLogger("test");
         Map<String, String> map = new HashMap<>();
         map.put("message", "This is a test");
diff --git a/log4j-1.2-api/src/test/java/org/apache/log4j/config/PropertiesConfigurationFactoryTest.java b/log4j-1.2-api/src/test/java/org/apache/log4j/config/PropertiesConfigurationFactoryTest.java
index de81b63..27fce5d 100644
--- a/log4j-1.2-api/src/test/java/org/apache/log4j/config/PropertiesConfigurationFactoryTest.java
+++ b/log4j-1.2-api/src/test/java/org/apache/log4j/config/PropertiesConfigurationFactoryTest.java
@@ -38,7 +38,7 @@ public class PropertiesConfigurationFactoryTest {
     }
 
     @Test
-    public void testProperties() throws Exception {
+    public void testProperties() {
         Logger logger = LogManager.getLogger("test");
         logger.debug("This is a test of the root logger");
         File file = new File("target/temp.A1");
diff --git a/log4j-1.2-api/src/test/java/org/apache/log4j/config/RewriteAppenderTest.java b/log4j-1.2-api/src/test/java/org/apache/log4j/config/RewriteAppenderTest.java
index f0ba257..cb92229 100644
--- a/log4j-1.2-api/src/test/java/org/apache/log4j/config/RewriteAppenderTest.java
+++ b/log4j-1.2-api/src/test/java/org/apache/log4j/config/RewriteAppenderTest.java
@@ -42,7 +42,7 @@ public class RewriteAppenderTest {
 
     @Test
     @LoggerContextSource(value = "log4j1-rewrite.xml", v1config = true)
-    public void testRewrite(final LoggerContext context) throws Exception {
+    public void testRewrite(final LoggerContext context) {
         Logger logger = LogManager.getLogger("test");
         ThreadContext.put("key1", "This is a test");
         ThreadContext.put("hello", "world");
diff --git a/log4j-1.2-api/src/test/java/org/apache/log4j/config/XmlConfigurationFactoryTest.java b/log4j-1.2-api/src/test/java/org/apache/log4j/config/XmlConfigurationFactoryTest.java
index 65b8d47..8967e6d 100644
--- a/log4j-1.2-api/src/test/java/org/apache/log4j/config/XmlConfigurationFactoryTest.java
+++ b/log4j-1.2-api/src/test/java/org/apache/log4j/config/XmlConfigurationFactoryTest.java
@@ -41,7 +41,7 @@ public class XmlConfigurationFactoryTest {
         System.setProperty(ConfigurationFactory.LOG4J1_CONFIGURATION_FILE_PROPERTY, "target/test-classes/log4j1-file.xml");
     }
     @Test
-    public void testXML() throws Exception {
+    public void testXML() {
         Logger logger = LogManager.getLogger("test");
         logger.debug("This is a test of the root logger");
         File file = new File("target/temp.A1");

[logging-log4j2] 01/25: Don't use deprecated methods.

Posted by pk...@apache.org.
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 936d6e270157eff5964f61a71774ff908c3d9408
Author: Gary Gregory <ga...@gmail.com>
AuthorDate: Sat Feb 5 08:34:20 2022 -0500

    Don't use deprecated methods.
    
    Conflicts:
    	log4j-1.2-api/src/main/java/org/apache/log4j/builders/appender/DailyRollingFileAppenderBuilder.java
    	log4j-1.2-api/src/main/java/org/apache/log4j/builders/appender/FileAppenderBuilder.java
    	log4j-1.2-api/src/main/java/org/apache/log4j/builders/appender/RollingFileAppenderBuilder.java
---
 .../apache/log4j/builders/appender/DailyRollingFileAppenderBuilder.java  | 1 +
 1 file changed, 1 insertion(+)

diff --git a/log4j-1.2-api/src/main/java/org/apache/log4j/builders/appender/DailyRollingFileAppenderBuilder.java b/log4j-1.2-api/src/main/java/org/apache/log4j/builders/appender/DailyRollingFileAppenderBuilder.java
index 2ccc38c..576ccab 100644
--- a/log4j-1.2-api/src/main/java/org/apache/log4j/builders/appender/DailyRollingFileAppenderBuilder.java
+++ b/log4j-1.2-api/src/main/java/org/apache/log4j/builders/appender/DailyRollingFileAppenderBuilder.java
@@ -165,6 +165,7 @@ public class DailyRollingFileAppenderBuilder extends AbstractBuilder implements
                 .setLayout(fileLayout)
                 .setFilter(fileFilter)
                 .setFileName(fileName)
+                .setAppend(append)
                 .setBufferedIo(bufferedIo)
                 .setBufferSize(bufferSize)
                 .setImmediateFlush(immediateFlush)

[logging-log4j2] 13/25: Log4j 1.2 bridge missing some LocationInfo constructors.

Posted by pk...@apache.org.
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 c4ce19f4f6134ce36551ace8c9c7d65b70ea4d4f
Author: Gary Gregory <ga...@gmail.com>
AuthorDate: Sat Feb 5 15:53:20 2022 -0500

    Log4j 1.2 bridge missing some LocationInfo constructors.
    
    Conflicts:
    	log4j-1.2-api/src/main/java/org/apache/log4j/spi/LocationInfo.java
---
 .../java/org/apache/log4j/spi/LocationInfo.java    | 96 +++++++++++++++++-----
 .../org/apache/log4j/spi/LocationInfoTest.java     | 82 ++++++++++++++++++
 src/changes/changes.xml                            |  3 +
 3 files changed, 162 insertions(+), 19 deletions(-)

diff --git a/log4j-1.2-api/src/main/java/org/apache/log4j/spi/LocationInfo.java b/log4j-1.2-api/src/main/java/org/apache/log4j/spi/LocationInfo.java
index c5e5b5e..a3f2fd1 100644
--- a/log4j-1.2-api/src/main/java/org/apache/log4j/spi/LocationInfo.java
+++ b/log4j-1.2-api/src/main/java/org/apache/log4j/spi/LocationInfo.java
@@ -16,62 +16,120 @@
  */
 package org.apache.log4j.spi;
 
+import java.io.Serializable;
+import java.util.Objects;
+
 /**
  * The internal representation of caller location information.
- * 
+ *
  * @since 0.8.3
  */
-public class LocationInfo implements java.io.Serializable {
+public class LocationInfo implements Serializable {
+
+    /**
+     * When location information is not available the constant <code>NA</code> is returned. Current value of this string
+     * constant is <b>?</b>.
+     */
+    public final static String NA = "?";
+
+    static final long serialVersionUID = -1325822038990805636L;
 
-    private final StackTraceElement element;
+    private final StackTraceElement stackTraceElement;
 
     public String fullInfo;
 
     /**
-     * Constructor for LocationInfo.
-     * @param element The StackTraceElement representing the caller.
+     * Constructs a new instance.
      */
-    public LocationInfo(StackTraceElement element) {
-        this.element = element;
+    public LocationInfo(final StackTraceElement stackTraceElement) {
+        this.stackTraceElement = Objects.requireNonNull(stackTraceElement, "stackTraceElement");
+        this.fullInfo = stackTraceElement.toString();
     }
 
     /**
-     * When location information is not available the constant <code>NA</code> is returned. Current value of this string
-     * constant is <b>?</b>.
+     * Constructs a new instance.
+     *
+     * @param file source file name
+     * @param declaringClass class name
+     * @param methodName method
+     * @param line source line number
+     *
+     * @since 1.2.15
      */
-    public final static String NA = "?";
+    public LocationInfo(final String file, final String declaringClass, final String methodName, final String line) {
+        this(new StackTraceElement(declaringClass, methodName, file, Integer.parseInt(line)));
+    }
 
-    static final long serialVersionUID = -1325822038990805636L;
+    /**
+     * Constructs a new instance.
+     */
+    public LocationInfo(final Throwable throwable, final String fqnOfCallingClass) {
+        String declaringClass = null, methodName = null, file = null, line = null;
+        if (throwable != null && fqnOfCallingClass != null) {
+            final StackTraceElement[] elements = throwable.getStackTrace();
+            String prevClass = NA;
+            for (int i = elements.length - 1; i >= 0; i--) {
+                final String thisClass = elements[i].getClassName();
+                if (fqnOfCallingClass.equals(thisClass)) {
+                    final int caller = i + 1;
+                    if (caller < elements.length) {
+                        declaringClass = prevClass;
+                        methodName = elements[caller].getMethodName();
+                        file = elements[caller].getFileName();
+                        if (file == null) {
+                            file = NA;
+                        }
+                        final int lineNo = elements[caller].getLineNumber();
+                        if (lineNo < 0) {
+                            line = NA;
+                        } else {
+                            line = String.valueOf(lineNo);
+                        }
+                        final StringBuilder builder = new StringBuilder();
+                        builder.append(declaringClass);
+                        builder.append(".");
+                        builder.append(methodName);
+                        builder.append("(");
+                        builder.append(file);
+                        builder.append(":");
+                        builder.append(line);
+                        builder.append(")");
+                        this.fullInfo = builder.toString();
+                    }
+                    break;
+                }
+                prevClass = thisClass;
+            }
+        }
+        this.stackTraceElement = new StackTraceElement(declaringClass, methodName, file, Integer.parseInt(line));
+        this.fullInfo = stackTraceElement.toString();
+    }
 
     /**
      * Return the fully qualified class name of the caller making the logging request.
-     @return The class name.
      */
     public String getClassName() {
-        return element.getClassName();
+        return stackTraceElement.getClassName();
     }
 
     /**
      * Return the file name of the caller.
-     @return the file name.
      */
     public String getFileName() {
-        return element.getFileName();
+        return stackTraceElement.getFileName();
     }
 
     /**
      * Returns the line number of the caller.
-     @return The line number.
      */
     public String getLineNumber() {
-        return Integer.toString(element.getLineNumber());
+        return Integer.toString(stackTraceElement.getLineNumber());
     }
 
     /**
      * Returns the method name of the caller.
-     @return The method name.
      */
     public String getMethodName() {
-        return element.getMethodName();
+        return stackTraceElement.getMethodName();
     }
 }
diff --git a/log4j-1.2-api/src/test/java/org/apache/log4j/spi/LocationInfoTest.java b/log4j-1.2-api/src/test/java/org/apache/log4j/spi/LocationInfoTest.java
new file mode 100644
index 0000000..a6daefc
--- /dev/null
+++ b/log4j-1.2-api/src/test/java/org/apache/log4j/spi/LocationInfoTest.java
@@ -0,0 +1,82 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.log4j.spi;
+
+import junit.framework.TestCase;
+
+/**
+ * Tests for LocationInfo.
+ */
+public class LocationInfoTest extends TestCase {
+
+    /**
+     * Tests four parameter constructor.
+     */
+    public void testFourParamConstructor() {
+        final String className = LocationInfoTest.class.getName();
+        final String methodName = "testFourParamConstructor";
+        final String fileName = "LocationInfoTest.java";
+        final String lineNumber = "41";
+        LocationInfo li = new LocationInfo(fileName, className, methodName, lineNumber);
+        assertEquals(className, li.getClassName());
+        assertEquals(methodName, li.getMethodName());
+        assertEquals(fileName, li.getFileName());
+        assertEquals(lineNumber, li.getLineNumber());
+        assertEquals(className + "." + methodName + "(" + fileName + ":" + lineNumber + ")", li.fullInfo);
+    }
+
+    /**
+     * Class with name that is a substring of its caller.
+     */
+    private static class NameSubstring {
+        /**
+         * Construct a LocationInfo. Location should be immediate caller of this method.
+         * 
+         * @return location info.
+         */
+        public static LocationInfo getInfo() {
+            return new LocationInfo(new Throwable(), NameSubstring.class.getName());
+
+        }
+    }
+
+    /**
+     * Class whose name is contains the name of the class that obtains the LocationInfo.
+     */
+    private static class NameSubstringCaller {
+        /**
+         * Construct a locationInfo. Location should be this location.
+         * 
+         * @return location info.
+         */
+        public static LocationInfo getInfo() {
+            return NameSubstring.getInfo();
+        }
+
+    }
+
+    /**
+     * Tests creation of location info when the logger class name is a substring of one of the other classes in the stack
+     * trace. See bug 44888.
+     */
+    public void testLocationInfo() {
+        LocationInfo li = NameSubstringCaller.getInfo();
+        assertEquals(NameSubstringCaller.class.getName(), li.getClassName());
+        assertEquals("getInfo", li.getMethodName());
+    }
+
+}
diff --git a/src/changes/changes.xml b/src/changes/changes.xml
index 7d288e7..0226b41 100644
--- a/src/changes/changes.xml
+++ b/src/changes/changes.xml
@@ -331,6 +331,9 @@
       <action dev="ggregory" type="fix" due-to="Gary Gregory">
         Log4j 1.2 bridge missing some ThrowableInformation constructors.
       </action>
+      <action dev="ggregory" type="fix" due-to="Gary Gregory">
+        Log4j 1.2 bridge missing some LocationInfo constructors.
+      </action>
       <action dev="ggregory" type="fix">
         JndiManager reverts to 2.17.0 behavior: Read the system property for each call.
       </action>

[logging-log4j2] 15/25: Format tweaks.

Posted by pk...@apache.org.
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 7d55af76bf437cad72a43df21d31d88721267869
Author: Gary Gregory <ga...@gmail.com>
AuthorDate: Sat Feb 5 16:16:35 2022 -0500

    Format tweaks.
    
    Conflicts:
    	log4j-1.2-api/src/main/java/org/apache/log4j/layout/Log4j1XmlLayout.java
---
 .../src/main/java/org/apache/log4j/layout/Log4j1XmlLayout.java          | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/log4j-1.2-api/src/main/java/org/apache/log4j/layout/Log4j1XmlLayout.java b/log4j-1.2-api/src/main/java/org/apache/log4j/layout/Log4j1XmlLayout.java
index 12bb405..420c455 100644
--- a/log4j-1.2-api/src/main/java/org/apache/log4j/layout/Log4j1XmlLayout.java
+++ b/log4j-1.2-api/src/main/java/org/apache/log4j/layout/Log4j1XmlLayout.java
@@ -109,7 +109,7 @@ public final class Log4j1XmlLayout extends AbstractStringLayout {
         }
 
         @SuppressWarnings("ThrowableResultOfMethodCallIgnored")
-		final Throwable thrown = event.getThrown();
+        final Throwable thrown = event.getThrown();
         if (thrown != null) {
             buf.append("<log4j:throwable><![CDATA[");
             final StringWriter w = new StringWriter();

[logging-log4j2] 08/25: Convert AtomicReferences to Holders

Posted by pk...@apache.org.
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 20f18e6ab68ef45ca7c98294885766075ef6f3f7
Author: Piotr P. Karwasz <pi...@karwasz.org>
AuthorDate: Tue Mar 29 09:05:00 2022 +0200

    Convert AtomicReferences to Holders
---
 .../builders/appender/SocketAppenderBuilder.java   | 37 +++++++++++-----------
 1 file changed, 18 insertions(+), 19 deletions(-)

diff --git a/log4j-1.2-api/src/main/java/org/apache/log4j/builders/appender/SocketAppenderBuilder.java b/log4j-1.2-api/src/main/java/org/apache/log4j/builders/appender/SocketAppenderBuilder.java
index e16e140..9034f05 100644
--- a/log4j-1.2-api/src/main/java/org/apache/log4j/builders/appender/SocketAppenderBuilder.java
+++ b/log4j-1.2-api/src/main/java/org/apache/log4j/builders/appender/SocketAppenderBuilder.java
@@ -26,9 +26,6 @@ import static org.apache.log4j.xml.XmlConfiguration.forEachElement;
 import java.util.ArrayList;
 import java.util.List;
 import java.util.Properties;
-import java.util.concurrent.atomic.AtomicBoolean;
-import java.util.concurrent.atomic.AtomicInteger;
-import java.util.concurrent.atomic.AtomicReference;
 
 import org.apache.log4j.Appender;
 import org.apache.log4j.Layout;
@@ -36,13 +33,15 @@ import org.apache.log4j.bridge.AppenderWrapper;
 import org.apache.log4j.bridge.LayoutAdapter;
 import org.apache.log4j.bridge.LayoutWrapper;
 import org.apache.log4j.builders.AbstractBuilder;
+import org.apache.log4j.builders.BooleanHolder;
+import org.apache.log4j.builders.Holder;
 import org.apache.log4j.config.Log4j1Configuration;
 import org.apache.log4j.config.PropertiesConfiguration;
 import org.apache.log4j.spi.Filter;
 import org.apache.log4j.xml.XmlConfiguration;
 import org.apache.logging.log4j.Logger;
 import org.apache.logging.log4j.core.appender.SocketAppender;
-import org.apache.logging.log4j.core.config.plugins.Plugin;
+import org.apache.logging.log4j.plugins.Plugin;
 import org.apache.logging.log4j.status.StatusLogger;
 import org.w3c.dom.Element;
 
@@ -82,9 +81,9 @@ public class SocketAppenderBuilder extends AbstractBuilder implements AppenderBu
         final org.apache.logging.log4j.core.Filter actualFilter = buildFilters(level, filter);
         // @formatter:off
         return new AppenderWrapper(SocketAppender.newBuilder()
-            .withHost(host)
-            .withPort(port)
-            .withReconnectDelayMillis(reconnectDelayMillis)
+            .setHost(host)
+            .setPort(port)
+            .setReconnectDelayMillis(reconnectDelayMillis)
             .setName(name)
             .setLayout(actualLayout)
             .setFilter(actualFilter)
@@ -97,13 +96,13 @@ public class SocketAppenderBuilder extends AbstractBuilder implements AppenderBu
     @Override
     public Appender parseAppender(final Element appenderElement, final XmlConfiguration config) {
         final String name = getNameAttribute(appenderElement);
-        final AtomicReference<String> host = new AtomicReference<>("localhost");
-        final AtomicInteger port = new AtomicInteger(DEFAULT_PORT);
-        final AtomicInteger reconnectDelay = new AtomicInteger(DEFAULT_RECONNECTION_DELAY);
-        final AtomicReference<Layout> layout = new AtomicReference<>();
-        final AtomicReference<List<Filter>> filters = new AtomicReference<>(new ArrayList<>());
-        final AtomicReference<String> level = new AtomicReference<>();
-        final AtomicBoolean immediateFlush = new AtomicBoolean(true);
+        final Holder<String> host = new Holder<>("localhost");
+        final Holder<Integer> port = new Holder<>(DEFAULT_PORT);
+        final Holder<Integer> reconnectDelay = new Holder<>(DEFAULT_RECONNECTION_DELAY);
+        final Holder<Layout> layout = new Holder<>();
+        final Holder<List<Filter>> filters = new Holder<>(new ArrayList<>());
+        final Holder<String> level = new Holder<>();
+        final Holder<Boolean> immediateFlush = new BooleanHolder(true);
         forEachElement(appenderElement.getChildNodes(), currentElement -> {
             switch (currentElement.getTagName()) {
             case LAYOUT_TAG:
@@ -115,19 +114,19 @@ public class SocketAppenderBuilder extends AbstractBuilder implements AppenderBu
             case PARAM_TAG:
                 switch (getNameAttributeKey(currentElement)) {
                 case HOST_PARAM:
-                    set(HOST_PARAM, currentElement, host);
+                    setString(HOST_PARAM, currentElement, host);
                     break;
                 case PORT_PARAM:
-                    set(PORT_PARAM, currentElement, port);
+                    setInteger(PORT_PARAM, currentElement, port);
                     break;
                 case RECONNECTION_DELAY_PARAM:
-                    set(RECONNECTION_DELAY_PARAM, currentElement, reconnectDelay);
+                    setInteger(RECONNECTION_DELAY_PARAM, currentElement, reconnectDelay);
                     break;
                 case THRESHOLD_PARAM:
-                    set(THRESHOLD_PARAM, currentElement, level);
+                    setString(THRESHOLD_PARAM, currentElement, level);
                     break;
                 case IMMEDIATE_FLUSH_PARAM:
-                    set(IMMEDIATE_FLUSH_PARAM, currentElement, immediateFlush);
+                    setBoolean(IMMEDIATE_FLUSH_PARAM, currentElement, immediateFlush);
                     break;
                 }
                 break;

[logging-log4j2] 02/25: Remove unused import.

Posted by pk...@apache.org.
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 ef2851f802da3dd6c350a6f3c336996ceaeff017
Author: Gary Gregory <ga...@gmail.com>
AuthorDate: Sat Feb 5 08:34:41 2022 -0500

    Remove unused import.
    
    Conflicts:
    	log4j-1.2-api/src/main/java/org/apache/log4j/builders/BuilderManager.java
---
 .../main/java/org/apache/log4j/builders/BuilderManager.java    | 10 +++++-----
 1 file changed, 5 insertions(+), 5 deletions(-)

diff --git a/log4j-1.2-api/src/main/java/org/apache/log4j/builders/BuilderManager.java b/log4j-1.2-api/src/main/java/org/apache/log4j/builders/BuilderManager.java
index 5cb5999..c331c46 100644
--- a/log4j-1.2-api/src/main/java/org/apache/log4j/builders/BuilderManager.java
+++ b/log4j-1.2-api/src/main/java/org/apache/log4j/builders/BuilderManager.java
@@ -16,6 +16,11 @@
  */
 package org.apache.log4j.builders;
 
+import java.lang.reflect.Constructor;
+import java.util.Map;
+import java.util.Objects;
+import java.util.Properties;
+
 import org.apache.log4j.Appender;
 import org.apache.log4j.Layout;
 import org.apache.log4j.builders.appender.AppenderBuilder;
@@ -33,11 +38,6 @@ import org.apache.logging.log4j.status.StatusLogger;
 import org.apache.logging.log4j.util.LoaderUtil;
 import org.w3c.dom.Element;
 
-import java.lang.reflect.Constructor;
-import java.util.Map;
-import java.util.Objects;
-import java.util.Properties;
-
 /**
  *
  */

[logging-log4j2] 06/25: Log4j 1.2 bridge now supports SocketAppender.

Posted by pk...@apache.org.
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 b08d46636c0029ddf83ee5b843901621d3dec8c0
Author: Gary Gregory <ga...@gmail.com>
AuthorDate: Sat Feb 5 09:09:42 2022 -0500

    Log4j 1.2 bridge now supports SocketAppender.
    
    First commit.
    TODO: multicast support.
---
 .../builders/appender/SocketAppenderBuilder.java   | 164 +++++++++++++++++++++
 .../config/SocketAppenderConfigurationTest.java    |  80 ++++++++++
 .../src/test/resources/log4j1-socket.properties    |  25 ++++
 log4j-1.2-api/src/test/resources/log4j1-socket.xml |  36 +++++
 src/changes/changes.xml                            |   3 +
 5 files changed, 308 insertions(+)

diff --git a/log4j-1.2-api/src/main/java/org/apache/log4j/builders/appender/SocketAppenderBuilder.java b/log4j-1.2-api/src/main/java/org/apache/log4j/builders/appender/SocketAppenderBuilder.java
new file mode 100644
index 0000000..e16e140
--- /dev/null
+++ b/log4j-1.2-api/src/main/java/org/apache/log4j/builders/appender/SocketAppenderBuilder.java
@@ -0,0 +1,164 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache license, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the license for the specific language governing permissions and
+ * limitations under the license.
+ */
+package org.apache.log4j.builders.appender;
+
+import static org.apache.log4j.builders.BuilderManager.CATEGORY;
+import static org.apache.log4j.config.Log4j1Configuration.THRESHOLD_PARAM;
+import static org.apache.log4j.xml.XmlConfiguration.FILTER_TAG;
+import static org.apache.log4j.xml.XmlConfiguration.LAYOUT_TAG;
+import static org.apache.log4j.xml.XmlConfiguration.PARAM_TAG;
+import static org.apache.log4j.xml.XmlConfiguration.forEachElement;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Properties;
+import java.util.concurrent.atomic.AtomicBoolean;
+import java.util.concurrent.atomic.AtomicInteger;
+import java.util.concurrent.atomic.AtomicReference;
+
+import org.apache.log4j.Appender;
+import org.apache.log4j.Layout;
+import org.apache.log4j.bridge.AppenderWrapper;
+import org.apache.log4j.bridge.LayoutAdapter;
+import org.apache.log4j.bridge.LayoutWrapper;
+import org.apache.log4j.builders.AbstractBuilder;
+import org.apache.log4j.config.Log4j1Configuration;
+import org.apache.log4j.config.PropertiesConfiguration;
+import org.apache.log4j.spi.Filter;
+import org.apache.log4j.xml.XmlConfiguration;
+import org.apache.logging.log4j.Logger;
+import org.apache.logging.log4j.core.appender.SocketAppender;
+import org.apache.logging.log4j.core.config.plugins.Plugin;
+import org.apache.logging.log4j.status.StatusLogger;
+import org.w3c.dom.Element;
+
+/**
+ * Build a Console Appender
+ */
+@Plugin(name = "org.apache.log4j.net.SocketAppender", category = CATEGORY)
+public class SocketAppenderBuilder extends AbstractBuilder implements AppenderBuilder {
+
+    private static final String HOST_PARAM = "RemoteHost";
+    private static final String PORT_PARAM = "Port";
+    private static final String RECONNECTION_DELAY_PARAM = "ReconnectionDelay";
+    private static final int DEFAULT_PORT = 4560;
+
+    /**
+     * The default reconnection delay (30000 milliseconds or 30 seconds).
+     */
+    private static final int DEFAULT_RECONNECTION_DELAY = 30_000;
+
+    public static final Logger LOGGER = StatusLogger.getLogger();
+
+    public SocketAppenderBuilder() {
+    }
+
+    public SocketAppenderBuilder(final String prefix, final Properties props) {
+        super(prefix, props);
+    }
+
+    private <T extends Log4j1Configuration> Appender createAppender(final String name, final String host, final int port, final Layout layout,
+        final Filter filter, final String level, final boolean immediateFlush, final int reconnectDelayMillis, final T configuration) {
+        org.apache.logging.log4j.core.Layout<?> actualLayout = null;
+        if (layout instanceof LayoutWrapper) {
+            actualLayout = ((LayoutWrapper) layout).getLayout();
+        } else if (layout != null) {
+            actualLayout = new LayoutAdapter(layout);
+        }
+        final org.apache.logging.log4j.core.Filter actualFilter = buildFilters(level, filter);
+        // @formatter:off
+        return new AppenderWrapper(SocketAppender.newBuilder()
+            .withHost(host)
+            .withPort(port)
+            .withReconnectDelayMillis(reconnectDelayMillis)
+            .setName(name)
+            .setLayout(actualLayout)
+            .setFilter(actualFilter)
+            .setConfiguration(configuration)
+            .setImmediateFlush(immediateFlush)
+            .build());
+        // @formatter:on
+    }
+
+    @Override
+    public Appender parseAppender(final Element appenderElement, final XmlConfiguration config) {
+        final String name = getNameAttribute(appenderElement);
+        final AtomicReference<String> host = new AtomicReference<>("localhost");
+        final AtomicInteger port = new AtomicInteger(DEFAULT_PORT);
+        final AtomicInteger reconnectDelay = new AtomicInteger(DEFAULT_RECONNECTION_DELAY);
+        final AtomicReference<Layout> layout = new AtomicReference<>();
+        final AtomicReference<List<Filter>> filters = new AtomicReference<>(new ArrayList<>());
+        final AtomicReference<String> level = new AtomicReference<>();
+        final AtomicBoolean immediateFlush = new AtomicBoolean(true);
+        forEachElement(appenderElement.getChildNodes(), currentElement -> {
+            switch (currentElement.getTagName()) {
+            case LAYOUT_TAG:
+                layout.set(config.parseLayout(currentElement));
+                break;
+            case FILTER_TAG:
+                filters.get().add(config.parseFilters(currentElement));
+                break;
+            case PARAM_TAG:
+                switch (getNameAttributeKey(currentElement)) {
+                case HOST_PARAM:
+                    set(HOST_PARAM, currentElement, host);
+                    break;
+                case PORT_PARAM:
+                    set(PORT_PARAM, currentElement, port);
+                    break;
+                case RECONNECTION_DELAY_PARAM:
+                    set(RECONNECTION_DELAY_PARAM, currentElement, reconnectDelay);
+                    break;
+                case THRESHOLD_PARAM:
+                    set(THRESHOLD_PARAM, currentElement, level);
+                    break;
+                case IMMEDIATE_FLUSH_PARAM:
+                    set(IMMEDIATE_FLUSH_PARAM, currentElement, immediateFlush);
+                    break;
+                }
+                break;
+            }
+        });
+        Filter head = null;
+        Filter current = null;
+        for (final Filter f : filters.get()) {
+            if (head == null) {
+                head = f;
+            } else {
+                current.next = f;
+            }
+            current = f;
+        }
+        return createAppender(name, host.get(), port.get(), layout.get(), head, level.get(), immediateFlush.get(), reconnectDelay.get(), config);
+    }
+
+    @Override
+    public Appender parseAppender(final String name, final String appenderPrefix, final String layoutPrefix, final String filterPrefix, final Properties props,
+        final PropertiesConfiguration configuration) {
+        // @formatter:off
+        return createAppender(name,
+            getProperty(HOST_PARAM),
+            getIntegerProperty(PORT_PARAM, DEFAULT_PORT),
+            configuration.parseLayout(layoutPrefix, name, props),
+            configuration.parseAppenderFilters(props, filterPrefix, name),
+            getProperty(THRESHOLD_PARAM),
+            getBooleanProperty(IMMEDIATE_FLUSH_PARAM),
+            getIntegerProperty(RECONNECTION_DELAY_PARAM, DEFAULT_RECONNECTION_DELAY),
+            configuration);
+        // @formatter:on
+    }
+}
diff --git a/log4j-1.2-api/src/test/java/org/apache/log4j/config/SocketAppenderConfigurationTest.java b/log4j-1.2-api/src/test/java/org/apache/log4j/config/SocketAppenderConfigurationTest.java
new file mode 100644
index 0000000..5fbaf30
--- /dev/null
+++ b/log4j-1.2-api/src/test/java/org/apache/log4j/config/SocketAppenderConfigurationTest.java
@@ -0,0 +1,80 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache license, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the license for the specific language governing permissions and
+ * limitations under the license.
+ */
+package org.apache.log4j.config;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertNotNull;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+import java.io.IOException;
+import java.util.Map;
+
+import org.apache.logging.log4j.Level;
+import org.apache.logging.log4j.core.Appender;
+import org.apache.logging.log4j.core.appender.SocketAppender;
+import org.apache.logging.log4j.core.config.Configuration;
+import org.apache.logging.log4j.core.filter.ThresholdFilter;
+import org.apache.logging.log4j.core.net.Protocol;
+import org.apache.logging.log4j.core.net.TcpSocketManager;
+import org.junit.Test;
+
+/**
+ * Tests configuring a Syslog appender.
+ */
+public class SocketAppenderConfigurationTest {
+
+    private void check(final Protocol expected, final Configuration configuration) {
+        final Map<String, Appender> appenders = configuration.getAppenders();
+        assertNotNull(appenders);
+        final String appenderName = "socket";
+        final Appender appender = appenders.get(appenderName);
+        assertNotNull(appender, "Missing appender " + appenderName);
+        final SocketAppender syslogAppender = (SocketAppender) appender;
+        @SuppressWarnings("resource")
+        final TcpSocketManager manager = (TcpSocketManager) syslogAppender.getManager();
+        final String prefix = expected + ":";
+        assertTrue(manager.getName().startsWith(prefix), () -> String.format("'%s' does not start with '%s'", manager.getName(), prefix));
+        // Threshold
+        final ThresholdFilter filter = (ThresholdFilter) syslogAppender.getFilter();
+        assertEquals(Level.DEBUG, filter.getLevel());
+        // Host
+        assertEquals("localhost", manager.getHost());
+        // Port
+        assertEquals(9999, manager.getPort());
+        // Port
+        assertEquals(100, manager.getReconnectionDelayMillis());
+    }
+
+    private void checkProtocolPropertiesConfig(final Protocol expected, final String xmlPath) throws IOException {
+        check(expected, TestConfigurator.configure(xmlPath).getConfiguration());
+    }
+
+    private void checkProtocolXmlConfig(final Protocol expected, final String xmlPath) throws IOException {
+        check(expected, TestConfigurator.configure(xmlPath).getConfiguration());
+    }
+
+    @Test
+    public void testProperties() throws Exception {
+        checkProtocolXmlConfig(Protocol.TCP, "target/test-classes/log4j1-socket.properties");
+    }
+
+    @Test
+    public void testXml() throws Exception {
+        checkProtocolXmlConfig(Protocol.TCP, "target/test-classes/log4j1-socket.xml");
+    }
+
+}
diff --git a/log4j-1.2-api/src/test/resources/log4j1-socket.properties b/log4j-1.2-api/src/test/resources/log4j1-socket.properties
new file mode 100644
index 0000000..3072b46
--- /dev/null
+++ b/log4j-1.2-api/src/test/resources/log4j1-socket.properties
@@ -0,0 +1,25 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one or more
+# contributor license agreements.  See the NOTICE file distributed with
+# this work for additional information regarding copyright ownership.
+# The ASF licenses this file to You under the Apache License, Version 2.0
+# (the "License"); you may not use this file except in compliance with
+# the License.  You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+log4j.rootLogger=DEBUG,socket
+log4j.appender.socket=org.apache.log4j.net.SocketAppender
+log4j.appender.socket.remoteHost=localhost
+log4j.appender.socket.port=9999
+log4j.appender.socket.reconnectionDelay=100
+log4j.appender.socket.layout=org.apache.log4j.PatternLayout
+log4j.appender.socket.layout.conversionPattern=Main[%pid] :%t: %c %-4p - %m\n
+log4j.appender.socket.Threshold=DEBUG
diff --git a/log4j-1.2-api/src/test/resources/log4j1-socket.xml b/log4j-1.2-api/src/test/resources/log4j1-socket.xml
new file mode 100644
index 0000000..eca3a0f
--- /dev/null
+++ b/log4j-1.2-api/src/test/resources/log4j1-socket.xml
@@ -0,0 +1,36 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<!--
+  Licensed to the Apache Software Foundation (ASF) under one or more
+  contributor license agreements.  See the NOTICE file distributed with
+  this work for additional information regarding copyright ownership.
+  The ASF licenses this file to You under the Apache License, Version 2.0
+  (the "License"); you may not use this file except in compliance with
+  the License.  You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  See the License for the specific language governing permissions and
+  limitations under the License.
+-->
+<!DOCTYPE log4j:configuration SYSTEM "log4j.dtd">
+
+<log4j:configuration xmlns:log4j="http://jakarta.apache.org/log4j/">
+  <appender name="socket" class="org.apache.log4j.net.SocketAppender">
+    <param name="RemoteHost" value="localhost"/>
+    <param name="Port" value="9999"/>
+    <param name="ReconnectionDelay" value="100"/>
+    <param name="Threshold" value="DEBUG"/>
+    <layout class="org.apache.log4j.PatternLayout">
+      <param name="ConversionPattern" value="%d{yyyy-MM-dd HH:mm:ss} %-5p %c{1}:%L - %m%n" />
+    </layout>
+  </appender>
+
+  <root>
+    <priority value ="trace" />
+    <appender-ref ref="syslog" />
+  </root>
+
+</log4j:configuration>
diff --git a/src/changes/changes.xml b/src/changes/changes.xml
index 2dc1337..c7e29b0 100644
--- a/src/changes/changes.xml
+++ b/src/changes/changes.xml
@@ -322,6 +322,9 @@
       <action dev="ggregory" type="fix" due-to="Piotr P. Karwasz">
         Log4j 1.2 bridge uses some incorrect default property values in some appenders.
       </action>
+      <action dev="ggregory" type="fix" due-to="Gary Gregory">
+        Log4j 1.2 bridge supports the SocketAppender.
+      </action>
       <action dev="ggregory" type="fix" due-to="Gary Gregory, Piotr P. Karwasz">
       </action>
       <action dev="ggregory" type="fix">

[logging-log4j2] 24/25: Catch all FileAppenderTest asynchronous errors

Posted by pk...@apache.org.
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 f24c83447a15e0d8508336a8fe702ae70f052455
Author: Piotr P. Karwasz <pi...@karwasz.org>
AuthorDate: Sun Mar 13 21:19:34 2022 +0100

    Catch all FileAppenderTest asynchronous errors
    
    Assertions are not exceptions, so they were not passed on to the Junit
    thread.
    # Conflicts:
    #	log4j-core/src/test/java/org/apache/logging/log4j/core/appender/FileAppenderTest.java
---
 .../log4j/core/appender/FileAppenderTest.java      | 51 ++++++++++++++--------
 1 file changed, 32 insertions(+), 19 deletions(-)

diff --git a/log4j-core-test/src/test/java/org/apache/logging/log4j/core/appender/FileAppenderTest.java b/log4j-core-test/src/test/java/org/apache/logging/log4j/core/appender/FileAppenderTest.java
index 3927458..b2667be 100644
--- a/log4j-core-test/src/test/java/org/apache/logging/log4j/core/appender/FileAppenderTest.java
+++ b/log4j-core-test/src/test/java/org/apache/logging/log4j/core/appender/FileAppenderTest.java
@@ -26,6 +26,7 @@ import java.nio.file.Paths;
 import java.util.concurrent.ExecutorService;
 import java.util.concurrent.Executors;
 import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicReference;
 import java.util.regex.Matcher;
 import java.util.regex.Pattern;
 
@@ -34,6 +35,7 @@ import org.apache.logging.log4j.core.Layout;
 import org.apache.logging.log4j.core.LogEvent;
 import org.apache.logging.log4j.core.impl.Log4jLogEvent;
 import org.apache.logging.log4j.core.layout.PatternLayout;
+import org.apache.logging.log4j.core.util.Integers;
 import org.apache.logging.log4j.core.util.Throwables;
 import org.apache.logging.log4j.test.junit.CleanUpFiles;
 import org.apache.logging.log4j.message.SimpleMessage;
@@ -124,10 +126,16 @@ public class FileAppenderTest {
             long prevLen = curLen;
             assertEquals(0, curLen, "File length: " + curLen);
             for (int i = 0; i < 100; ++i) {
-                final LogEvent event = Log4jLogEvent.newBuilder().setLoggerName("TestLogger") //
-                        .setLoggerFqcn(FileAppenderTest.class.getName()).setLevel(Level.INFO) //
-                        .setMessage(new SimpleMessage("Test")).setThreadName(this.getClass().getSimpleName()) //
-                        .setTimeMillis(System.currentTimeMillis()).build();
+                // @formatter:off
+                final LogEvent event = Log4jLogEvent.newBuilder()
+                        .setLoggerName("TestLogger")
+                        .setLoggerFqcn(FileAppenderTest.class.getName())
+                        .setLevel(Level.INFO)
+                        .setMessage(new SimpleMessage("Test"))
+                        .setThreadName(this.getClass().getSimpleName())
+                        .setTimeMillis(System.currentTimeMillis())
+                        .build();
+                // @formatter:on
                 appender.append(event);
                 curLen = file.length();
                 assertTrue(curLen > prevLen, "File length: " + curLen);
@@ -158,17 +166,17 @@ public class FileAppenderTest {
     private void testMultipleLockingAppenderThreads(final boolean lock, final int threadCount, boolean createOnDemand)
             throws InterruptedException, Exception {
         final ExecutorService threadPool = Executors.newFixedThreadPool(threadCount);
-        final Exception[] exceptionRef = new Exception[1];
+        final AtomicReference<Throwable> throwableRef = new AtomicReference<>();
         final int logEventCount = 100;
-        final Runnable runnable = new FileWriterRunnable(createOnDemand, lock, logEventCount, exceptionRef);
+        final Runnable runnable = new FileWriterRunnable(createOnDemand, lock, logEventCount, throwableRef);
         for (int i = 0; i < threadCount; ++i) {
             threadPool.execute(runnable);
         }
         threadPool.shutdown();
         assertTrue(
                 threadPool.awaitTermination(10, TimeUnit.SECONDS), "The thread pool has not shutdown: " + threadPool);
-        if (exceptionRef[0] != null) {
-            throw exceptionRef[0];
+        if (throwableRef.get() != null) {
+            Throwables.rethrow(throwableRef.get());
         }
         verifyFile(threadCount * logEventCount);
     }
@@ -240,10 +248,16 @@ public class FileAppenderTest {
                 assertNotEquals(createOnDemand, exists, msg);
             }
             for (int i = 0; i < logEventCount; ++i) {
-                final LogEvent logEvent = Log4jLogEvent.newBuilder().setLoggerName("TestLogger")
-                        .setLoggerFqcn(FileAppenderTest.class.getName()).setLevel(Level.INFO)
-                        .setMessage(new SimpleMessage("Test")).setThreadName(name)
-                        .setTimeMillis(System.currentTimeMillis()).build();
+                // @formatter:off
+                final LogEvent logEvent = Log4jLogEvent.newBuilder()
+                        .setLoggerName("TestLogger")
+                        .setLoggerFqcn(FileAppenderTest.class.getName())
+                        .setLevel(Level.INFO)
+                        .setMessage(new SimpleMessage("Test"))
+                        .setThreadName(name)
+                        .setTimeMillis(System.currentTimeMillis())
+                        .build();
+                // @formatter:on
                 appender.append(logEvent);
                 Thread.sleep(25); // Give up control long enough for another thread/process to occasionally do something.
             }
@@ -275,14 +289,14 @@ public class FileAppenderTest {
         private final boolean createOnDemand;
         private final boolean lock;
         private final int logEventCount;
-        private final Exception[] exceptionRef;
+        private final AtomicReference<Throwable> throwableRef;
 
         public FileWriterRunnable(
-                boolean createOnDemand, final boolean lock, final int logEventCount, final Exception[] exceptionRef) {
+                boolean createOnDemand, final boolean lock, final int logEventCount, final AtomicReference<Throwable> throwableRef) {
             this.createOnDemand = createOnDemand;
             this.lock = lock;
             this.logEventCount = logEventCount;
-            this.exceptionRef = exceptionRef;
+            this.throwableRef = throwableRef;
         }
 
         @Override
@@ -291,9 +305,8 @@ public class FileAppenderTest {
 
             try {
                 writer(lock, logEventCount, thread.getName(), createOnDemand, true);
-            } catch (final Exception e) {
-                exceptionRef[0] = e;
-                Throwables.rethrow(e);
+            } catch (final Throwable e) {
+                throwableRef.set(e);
             }
         }
     }
@@ -308,7 +321,7 @@ public class FileAppenderTest {
             }
             final String id = args[0];
 
-            final int count = Integer.parseInt(args[1]);
+            final int count = Integers.parseInt(args[1]);
 
             if (count <= 0) {
                 System.out.println("Invalid count value: " + args[1]);

[logging-log4j2] 04/25: Remove unused imports.

Posted by pk...@apache.org.
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 859f5c7f6864e8fccc04e36d184de5a0dec11149
Author: Gary Gregory <ga...@gmail.com>
AuthorDate: Sat Feb 5 08:37:02 2022 -0500

    Remove unused imports.
    
    Conflicts:
    	log4j-1.2-api/src/test/java/org/apache/log4j/config/Log4j1ConfigurationFactoryTest.java
---
 .../log4j/config/Log4j1ConfigurationFactoryTest.java      | 15 +++++++++------
 .../log4j/config/SyslogAppenderConfigurationTest.java     |  2 --
 2 files changed, 9 insertions(+), 8 deletions(-)

diff --git a/log4j-1.2-api/src/test/java/org/apache/log4j/config/Log4j1ConfigurationFactoryTest.java b/log4j-1.2-api/src/test/java/org/apache/log4j/config/Log4j1ConfigurationFactoryTest.java
index 95f6226..e2638ec 100644
--- a/log4j-1.2-api/src/test/java/org/apache/log4j/config/Log4j1ConfigurationFactoryTest.java
+++ b/log4j-1.2-api/src/test/java/org/apache/log4j/config/Log4j1ConfigurationFactoryTest.java
@@ -16,6 +16,14 @@
  */
 package org.apache.log4j.config;
 
+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.assertTrue;
+
+import java.net.URISyntaxException;
+import java.net.URL;
+
 import org.apache.log4j.layout.Log4j1XmlLayout;
 import org.apache.logging.log4j.Level;
 import org.apache.logging.log4j.core.Layout;
@@ -23,12 +31,7 @@ import org.apache.logging.log4j.core.appender.ConsoleAppender;
 import org.apache.logging.log4j.core.appender.ConsoleAppender.Target;
 import org.apache.logging.log4j.core.config.Configuration;
 import org.apache.logging.log4j.core.config.LoggerConfig;
-import org.junit.jupiter.api.Test;
-
-import java.net.URISyntaxException;
-import java.net.URL;
-
-import static org.junit.jupiter.api.Assertions.*;
+import org.junit.Test;
 
 public class Log4j1ConfigurationFactoryTest extends AbstractLog4j1ConfigurationTest {
 
diff --git a/log4j-1.2-api/src/test/java/org/apache/log4j/config/SyslogAppenderConfigurationTest.java b/log4j-1.2-api/src/test/java/org/apache/log4j/config/SyslogAppenderConfigurationTest.java
index a63d808..cdf9b2f 100644
--- a/log4j-1.2-api/src/test/java/org/apache/log4j/config/SyslogAppenderConfigurationTest.java
+++ b/log4j-1.2-api/src/test/java/org/apache/log4j/config/SyslogAppenderConfigurationTest.java
@@ -28,9 +28,7 @@ import org.apache.logging.log4j.core.Appender;
 import org.apache.logging.log4j.core.appender.SyslogAppender;
 import org.apache.logging.log4j.core.config.Configuration;
 import org.apache.logging.log4j.core.filter.ThresholdFilter;
-import org.apache.logging.log4j.core.layout.Rfc5424Layout;
 import org.apache.logging.log4j.core.net.AbstractSocketManager;
-import org.apache.logging.log4j.core.net.Facility;
 import org.apache.logging.log4j.core.net.Protocol;
 import org.junit.Test;
 

[logging-log4j2] 22/25: Add more missing code and tests from Log4j 1.2.17.

Posted by pk...@apache.org.
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 83c5193ab4b1e50263e6436cec2b651723e0670d
Author: Gary Gregory <ga...@gmail.com>
AuthorDate: Sun Feb 6 09:27:07 2022 -0500

    Add more missing code and tests from Log4j 1.2.17.
    
    2 tests are ignored for now.
---
 .../main/java/org/apache/log4j/PatternLayout.java  |  76 ++-
 .../org/apache/log4j/helpers/FormattingInfo.java   |  39 ++
 .../org/apache/log4j/helpers/OptionConverter.java  |   3 +-
 .../org/apache/log4j/helpers/PatternConverter.java | 111 ++++
 .../org/apache/log4j/helpers/PatternParser.java    | 570 +++++++++++++++++++++
 .../src/test/java/org/apache/log4j/LayoutTest.java | 168 ++++++
 .../org/apache/log4j/PropertyConfiguratorTest.java |   2 +-
 .../org/apache/log4j/helpers/DateLayoutTest.java   | 288 +++++++++++
 .../log4j/helpers/OptionConverterTestCase.java     | 177 +++++++
 .../log4j/helpers/PatternParserTestCase.java       | 131 +++++
 .../test/java/org/apache/log4j/util/Compare.java   | 150 ++++++
 .../src/test/java/org/apache/log4j/xml/XLevel.java |  74 +++
 src/changes/changes.xml                            |   9 +
 13 files changed, 1794 insertions(+), 4 deletions(-)

diff --git a/log4j-1.2-api/src/main/java/org/apache/log4j/PatternLayout.java b/log4j-1.2-api/src/main/java/org/apache/log4j/PatternLayout.java
index e250700..da6f87f 100644
--- a/log4j-1.2-api/src/main/java/org/apache/log4j/PatternLayout.java
+++ b/log4j-1.2-api/src/main/java/org/apache/log4j/PatternLayout.java
@@ -16,8 +16,9 @@
  */
 package org.apache.log4j;
 
+import org.apache.log4j.helpers.PatternConverter;
+import org.apache.log4j.helpers.PatternParser;
 import org.apache.log4j.spi.LoggingEvent;
-import org.apache.logging.log4j.util.Strings;
 
 /**
  *
@@ -35,6 +36,17 @@ public class PatternLayout extends Layout {
      */
     public final static String TTCC_CONVERSION_PATTERN = "%r [%t] %p %c %x - %m%n";
 
+    protected final int BUF_SIZE = 256;
+
+    protected final int MAX_CAPACITY = 1024;
+
+    // output buffer appended to when format() is invoked
+    private StringBuffer sbuf = new StringBuffer(BUF_SIZE);
+
+    private String pattern;
+
+    private PatternConverter head;
+
     /**
      * Constructs a PatternLayout using the DEFAULT_LAYOUT_PATTERN.
      *
@@ -44,17 +56,77 @@ public class PatternLayout extends Layout {
         this(DEFAULT_CONVERSION_PATTERN);
     }
 
+    /**
+     * Constructs a PatternLayout using the supplied conversion pattern.
+     */
     public PatternLayout(final String pattern) {
+        this.pattern = pattern;
+        head = createPatternParser((pattern == null) ? DEFAULT_CONVERSION_PATTERN : pattern).parse();
+    }
 
+    /**
+     * Does not do anything as options become effective
+     */
+    public void activateOptions() {
+        // nothing to do.
+    }
+
+    /**
+     * Returns PatternParser used to parse the conversion string. Subclasses may override this to return a subclass of
+     * PatternParser which recognize custom conversion characters.
+     *
+     * @since 0.9.0
+     */
+    protected PatternParser createPatternParser(final String pattern) {
+        return new PatternParser(pattern);
     }
 
+    /**
+     * Produces a formatted string as specified by the conversion pattern.
+     */
     @Override
     public String format(final LoggingEvent event) {
-        return Strings.EMPTY;
+        // Reset working stringbuffer
+        if (sbuf.capacity() > MAX_CAPACITY) {
+            sbuf = new StringBuffer(BUF_SIZE);
+        } else {
+            sbuf.setLength(0);
+        }
+
+        PatternConverter c = head;
+
+        while (c != null) {
+            c.format(sbuf, event);
+            c = c.next;
+        }
+        return sbuf.toString();
+    }
+
+    /**
+     * Returns the value of the <b>ConversionPattern</b> option.
+     */
+    public String getConversionPattern() {
+        return pattern;
     }
 
+    /**
+     * The PatternLayout does not handle the throwable contained within {@link LoggingEvent LoggingEvents}. Thus, it returns
+     * <code>true</code>.
+     *
+     * @since 0.8.4
+     */
     @Override
     public boolean ignoresThrowable() {
         return true;
     }
+
+    /**
+     * Set the <b>ConversionPattern</b> option. This is the string which controls formatting and consists of a mix of
+     * literal content and conversion specifiers.
+     */
+    public void setConversionPattern(final String conversionPattern) {
+        pattern = conversionPattern;
+        head = createPatternParser(conversionPattern).parse();
+    }
+
 }
diff --git a/log4j-1.2-api/src/main/java/org/apache/log4j/helpers/FormattingInfo.java b/log4j-1.2-api/src/main/java/org/apache/log4j/helpers/FormattingInfo.java
new file mode 100644
index 0000000..7b62660
--- /dev/null
+++ b/log4j-1.2-api/src/main/java/org/apache/log4j/helpers/FormattingInfo.java
@@ -0,0 +1,39 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.log4j.helpers;
+
+/**
+ * FormattingInfo instances contain the information obtained when parsing formatting modifiers in conversion modifiers.
+ * 
+ * @since 0.8.2
+ */
+public class FormattingInfo {
+    int min = -1;
+    int max = 0x7FFFFFFF;
+    boolean leftAlign = false;
+
+    void dump() {
+        LogLog.debug("min=" + min + ", max=" + max + ", leftAlign=" + leftAlign);
+    }
+
+    void reset() {
+        min = -1;
+        max = 0x7FFFFFFF;
+        leftAlign = false;
+    }
+}
diff --git a/log4j-1.2-api/src/main/java/org/apache/log4j/helpers/OptionConverter.java b/log4j-1.2-api/src/main/java/org/apache/log4j/helpers/OptionConverter.java
index 14f19f8..9888979 100644
--- a/log4j-1.2-api/src/main/java/org/apache/log4j/helpers/OptionConverter.java
+++ b/log4j-1.2-api/src/main/java/org/apache/log4j/helpers/OptionConverter.java
@@ -30,6 +30,7 @@ import org.apache.log4j.spi.Configurator;
 import org.apache.log4j.spi.LoggerRepository;
 import org.apache.logging.log4j.LogManager;
 import org.apache.logging.log4j.Logger;
+import org.apache.logging.log4j.status.StatusLogger;
 import org.apache.logging.log4j.util.LoaderUtil;
 import org.apache.logging.log4j.util.PropertiesUtil;
 import org.apache.logging.log4j.util.Strings;
@@ -52,7 +53,7 @@ public class OptionConverter {
     static char DELIM_STOP = '}';
     static int DELIM_START_LEN = 2;
     static int DELIM_STOP_LEN = 1;
-    private static final Logger LOGGER = LogManager.getLogger(OptionConverter.class);
+    private static final Logger LOGGER = StatusLogger.getLogger();
 
     private static final CharMap[] charMap = new CharMap[] {
             new CharMap('n', '\n'),
diff --git a/log4j-1.2-api/src/main/java/org/apache/log4j/helpers/PatternConverter.java b/log4j-1.2-api/src/main/java/org/apache/log4j/helpers/PatternConverter.java
new file mode 100644
index 0000000..2b46db7
--- /dev/null
+++ b/log4j-1.2-api/src/main/java/org/apache/log4j/helpers/PatternConverter.java
@@ -0,0 +1,111 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.log4j.helpers;
+
+import org.apache.log4j.spi.LoggingEvent;
+
+/**
+
+   <p>PatternConverter is an abtract class that provides the
+   formatting functionality that derived classes need.
+
+   <p>Conversion specifiers in a conversion patterns are parsed to
+   individual PatternConverters. Each of which is responsible for
+   converting a logging event in a converter specific manner.
+
+   @author <a href="mailto:cakalijp@Maritz.com">James P. Cakalic</a>
+   @author Ceki G&uuml;lc&uuml;
+
+   @since 0.8.2
+ */
+public abstract class PatternConverter {
+  public PatternConverter next;
+  int min = -1;
+  int max = 0x7FFFFFFF;
+  boolean leftAlign = false;
+
+  protected
+  PatternConverter() {  }
+  
+  protected
+  PatternConverter(FormattingInfo fi) {
+    min = fi.min;
+    max = fi.max;
+    leftAlign = fi.leftAlign;
+  }
+
+  /**
+     Derived pattern converters must override this method in order to
+     convert conversion specifiers in the correct way.
+  */
+  abstract
+  protected
+  String convert(LoggingEvent event);
+
+  /**
+     A template method for formatting in a converter specific way.
+   */
+  public
+  void format(StringBuffer sbuf, LoggingEvent e) {
+    String s = convert(e);
+
+    if(s == null) {
+      if(0 < min)
+	spacePad(sbuf, min);
+      return;
+    }
+
+    int len = s.length();
+
+    if(len > max)
+      sbuf.append(s.substring(len-max));
+    else if(len < min) {
+      if(leftAlign) {	
+	sbuf.append(s);
+	spacePad(sbuf, min-len);
+      }
+      else {
+	spacePad(sbuf, min-len);
+	sbuf.append(s);
+      }
+    }
+    else
+      sbuf.append(s);
+  }	
+
+  static String[] SPACES = {" ", "  ", "    ", "        ", //1,2,4,8 spaces
+			    "                ", // 16 spaces
+			    "                                " }; // 32 spaces
+
+  /**
+     Fast space padding method.
+  */
+  public
+  void spacePad(StringBuffer sbuf, int length) {
+    while(length >= 32) {
+      sbuf.append(SPACES[5]);
+      length -= 32;
+    }
+    
+    for(int i = 4; i >= 0; i--) {	
+      if((length & (1<<i)) != 0) {
+	sbuf.append(SPACES[i]);
+      }
+    }
+  }
+}
diff --git a/log4j-1.2-api/src/main/java/org/apache/log4j/helpers/PatternParser.java b/log4j-1.2-api/src/main/java/org/apache/log4j/helpers/PatternParser.java
new file mode 100644
index 0000000..0d3ead6
--- /dev/null
+++ b/log4j-1.2-api/src/main/java/org/apache/log4j/helpers/PatternParser.java
@@ -0,0 +1,570 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.log4j.helpers;
+
+import org.apache.log4j.Layout;
+import org.apache.log4j.spi.LoggingEvent;
+import org.apache.log4j.spi.LocationInfo;
+import java.text.DateFormat;
+import java.text.SimpleDateFormat;
+import java.util.Date;
+import java.util.Map;
+import java.util.Arrays;
+
+// Contributors:   Nelson Minar <(n...@monkey.org>
+//                 Igor E. Poteryaev <ja...@mail.ru>
+//                 Reinhard Deschler <re...@web.de>
+
+/**
+   Most of the work of the {@link org.apache.log4j.PatternLayout} class
+   is delegated to the PatternParser class.
+
+   <p>It is this class that parses conversion patterns and creates
+   a chained list of {@link OptionConverter OptionConverters}.
+
+   @author <a href=mailto:"cakalijp@Maritz.com">James P. Cakalic</a>
+   @author Ceki G&uuml;lc&uuml;
+   @author Anders Kristensen
+
+   @since 0.8.2
+*/
+public class PatternParser {
+
+  private static final char ESCAPE_CHAR = '%';
+
+  private static final int LITERAL_STATE = 0;
+  private static final int CONVERTER_STATE = 1;
+  private static final int DOT_STATE = 3;
+  private static final int MIN_STATE = 4;
+  private static final int MAX_STATE = 5;
+
+  static final int FULL_LOCATION_CONVERTER = 1000;
+  static final int METHOD_LOCATION_CONVERTER = 1001;
+  static final int CLASS_LOCATION_CONVERTER = 1002;
+  static final int LINE_LOCATION_CONVERTER = 1003;
+  static final int FILE_LOCATION_CONVERTER = 1004;
+
+  static final int RELATIVE_TIME_CONVERTER = 2000;
+  static final int THREAD_CONVERTER = 2001;
+  static final int LEVEL_CONVERTER = 2002;
+  static final int NDC_CONVERTER = 2003;
+  static final int MESSAGE_CONVERTER = 2004;
+
+  int state;
+  protected StringBuffer currentLiteral = new StringBuffer(32);
+  protected int patternLength;
+  protected int i;
+  PatternConverter head;
+  PatternConverter tail;
+  protected FormattingInfo formattingInfo = new FormattingInfo();
+  protected String pattern;
+
+  public
+  PatternParser(String pattern) {
+    this.pattern = pattern;
+    patternLength =  pattern.length();
+    state = LITERAL_STATE;
+  }
+
+  private
+  void  addToList(PatternConverter pc) {
+    if(head == null) {
+      head = tail = pc;
+    } else {
+      tail.next = pc;
+      tail = pc;
+    }
+  }
+
+  protected
+  String extractOption() {
+    if((i < patternLength) && (pattern.charAt(i) == '{')) {
+      int end = pattern.indexOf('}', i);
+      if (end > i) {
+	String r = pattern.substring(i + 1, end);
+	i = end+1;
+	return r;
+      }
+    }
+    return null;
+  }
+
+
+  /**
+     The option is expected to be in decimal and positive. In case of
+     error, zero is returned.  */
+  protected
+  int extractPrecisionOption() {
+    String opt = extractOption();
+    int r = 0;
+    if(opt != null) {
+      try {
+	r = Integer.parseInt(opt);
+	if(r <= 0) {
+	    LogLog.error(
+	        "Precision option (" + opt + ") isn't a positive integer.");
+	    r = 0;
+	}
+      }
+      catch (NumberFormatException e) {
+	LogLog.error("Category option \""+opt+"\" not a decimal integer.", e);
+      }
+    }
+    return r;
+  }
+
+  public
+  PatternConverter parse() {
+    char c;
+    i = 0;
+    while(i < patternLength) {
+      c = pattern.charAt(i++);
+      switch(state) {
+      case LITERAL_STATE:
+        // In literal state, the last char is always a literal.
+        if(i == patternLength) {
+          currentLiteral.append(c);
+          continue;
+        }
+        if(c == ESCAPE_CHAR) {
+          // peek at the next char.
+          switch(pattern.charAt(i)) {
+          case ESCAPE_CHAR:
+            currentLiteral.append(c);
+            i++; // move pointer
+            break;
+          case 'n':
+            currentLiteral.append(Layout.LINE_SEP);
+            i++; // move pointer
+            break;
+          default:
+            if(currentLiteral.length() != 0) {
+              addToList(new LiteralPatternConverter(
+                                                  currentLiteral.toString()));
+              //LogLog.debug("Parsed LITERAL converter: \""
+              //           +currentLiteral+"\".");
+            }
+            currentLiteral.setLength(0);
+            currentLiteral.append(c); // append %
+            state = CONVERTER_STATE;
+            formattingInfo.reset();
+          }
+        }
+        else {
+          currentLiteral.append(c);
+        }
+        break;
+      case CONVERTER_STATE:
+	currentLiteral.append(c);
+	switch(c) {
+	case '-':
+	  formattingInfo.leftAlign = true;
+	  break;
+	case '.':
+	  state = DOT_STATE;
+	  break;
+	default:
+	  if(c >= '0' && c <= '9') {
+	    formattingInfo.min = c - '0';
+	    state = MIN_STATE;
+	  }
+	  else
+	    finalizeConverter(c);
+	} // switch
+	break;
+      case MIN_STATE:
+	currentLiteral.append(c);
+	if(c >= '0' && c <= '9')
+	  formattingInfo.min = formattingInfo.min*10 + (c - '0');
+	else if(c == '.')
+	  state = DOT_STATE;
+	else {
+	  finalizeConverter(c);
+	}
+	break;
+      case DOT_STATE:
+	currentLiteral.append(c);
+	if(c >= '0' && c <= '9') {
+	  formattingInfo.max = c - '0';
+	   state = MAX_STATE;
+	}
+	else {
+	  LogLog.error("Error occured in position "+i
+		     +".\n Was expecting digit, instead got char \""+c+"\".");
+	  state = LITERAL_STATE;
+	}
+	break;
+      case MAX_STATE:
+	currentLiteral.append(c);
+	if(c >= '0' && c <= '9')
+	  formattingInfo.max = formattingInfo.max*10 + (c - '0');
+	else {
+	  finalizeConverter(c);
+	  state = LITERAL_STATE;
+	}
+	break;
+      } // switch
+    } // while
+    if(currentLiteral.length() != 0) {
+      addToList(new LiteralPatternConverter(currentLiteral.toString()));
+      //LogLog.debug("Parsed LITERAL converter: \""+currentLiteral+"\".");
+    }
+    return head;
+  }
+
+  protected
+  void finalizeConverter(char c) {
+    PatternConverter pc = null;
+    switch(c) {
+    case 'c':
+      pc = new CategoryPatternConverter(formattingInfo,
+					extractPrecisionOption());
+      //LogLog.debug("CATEGORY converter.");
+      //formattingInfo.dump();
+      currentLiteral.setLength(0);
+      break;
+    case 'C':
+      pc = new ClassNamePatternConverter(formattingInfo,
+					 extractPrecisionOption());
+      //LogLog.debug("CLASS_NAME converter.");
+      //formattingInfo.dump();
+      currentLiteral.setLength(0);
+      break;
+    case 'd':
+      String dateFormatStr = AbsoluteTimeDateFormat.ISO8601_DATE_FORMAT;
+      DateFormat df;
+      String dOpt = extractOption();
+      if(dOpt != null)
+	dateFormatStr = dOpt;
+
+      if(dateFormatStr.equalsIgnoreCase(
+                                    AbsoluteTimeDateFormat.ISO8601_DATE_FORMAT))
+	df = new  ISO8601DateFormat();
+      else if(dateFormatStr.equalsIgnoreCase(
+                                   AbsoluteTimeDateFormat.ABS_TIME_DATE_FORMAT))
+	df = new AbsoluteTimeDateFormat();
+      else if(dateFormatStr.equalsIgnoreCase(
+                              AbsoluteTimeDateFormat.DATE_AND_TIME_DATE_FORMAT))
+	df = new DateTimeDateFormat();
+      else {
+	try {
+	  df = new SimpleDateFormat(dateFormatStr);
+	}
+	catch (IllegalArgumentException e) {
+	  LogLog.error("Could not instantiate SimpleDateFormat with " +
+		       dateFormatStr, e);
+	  df = (DateFormat) OptionConverter.instantiateByClassName(
+			           "org.apache.log4j.helpers.ISO8601DateFormat",
+				   DateFormat.class, null);
+	}
+      }
+      pc = new DatePatternConverter(formattingInfo, df);
+      //LogLog.debug("DATE converter {"+dateFormatStr+"}.");
+      //formattingInfo.dump();
+      currentLiteral.setLength(0);
+      break;
+    case 'F':
+      pc = new LocationPatternConverter(formattingInfo,
+					FILE_LOCATION_CONVERTER);
+      //LogLog.debug("File name converter.");
+      //formattingInfo.dump();
+      currentLiteral.setLength(0);
+      break;
+    case 'l':
+      pc = new LocationPatternConverter(formattingInfo,
+					FULL_LOCATION_CONVERTER);
+      //LogLog.debug("Location converter.");
+      //formattingInfo.dump();
+      currentLiteral.setLength(0);
+      break;
+    case 'L':
+      pc = new LocationPatternConverter(formattingInfo,
+					LINE_LOCATION_CONVERTER);
+      //LogLog.debug("LINE NUMBER converter.");
+      //formattingInfo.dump();
+      currentLiteral.setLength(0);
+      break;
+    case 'm':
+      pc = new BasicPatternConverter(formattingInfo, MESSAGE_CONVERTER);
+      //LogLog.debug("MESSAGE converter.");
+      //formattingInfo.dump();
+      currentLiteral.setLength(0);
+      break;
+    case 'M':
+      pc = new LocationPatternConverter(formattingInfo,
+					METHOD_LOCATION_CONVERTER);
+      //LogLog.debug("METHOD converter.");
+      //formattingInfo.dump();
+      currentLiteral.setLength(0);
+      break;
+    case 'p':
+      pc = new BasicPatternConverter(formattingInfo, LEVEL_CONVERTER);
+      //LogLog.debug("LEVEL converter.");
+      //formattingInfo.dump();
+      currentLiteral.setLength(0);
+      break;
+    case 'r':
+      pc = new BasicPatternConverter(formattingInfo,
+					 RELATIVE_TIME_CONVERTER);
+      //LogLog.debug("RELATIVE time converter.");
+      //formattingInfo.dump();
+      currentLiteral.setLength(0);
+      break;
+    case 't':
+      pc = new BasicPatternConverter(formattingInfo, THREAD_CONVERTER);
+      //LogLog.debug("THREAD converter.");
+      //formattingInfo.dump();
+      currentLiteral.setLength(0);
+      break;
+      /*case 'u':
+      if(i < patternLength) {
+	char cNext = pattern.charAt(i);
+	if(cNext >= '0' && cNext <= '9') {
+	  pc = new UserFieldPatternConverter(formattingInfo, cNext - '0');
+	  LogLog.debug("USER converter ["+cNext+"].");
+	  formattingInfo.dump();
+	  currentLiteral.setLength(0);
+	  i++;
+	}
+	else
+	  LogLog.error("Unexpected char" +cNext+" at position "+i);
+      }
+      break;*/
+    case 'x':
+      pc = new BasicPatternConverter(formattingInfo, NDC_CONVERTER);
+      //LogLog.debug("NDC converter.");
+      currentLiteral.setLength(0);
+      break;
+    case 'X':
+      String xOpt = extractOption();
+      pc = new MDCPatternConverter(formattingInfo, xOpt);
+      currentLiteral.setLength(0);
+      break;
+    default:
+      LogLog.error("Unexpected char [" +c+"] at position "+i
+		   +" in conversion patterrn.");
+      pc = new LiteralPatternConverter(currentLiteral.toString());
+      currentLiteral.setLength(0);
+    }
+
+    addConverter(pc);
+  }
+
+  protected
+  void addConverter(PatternConverter pc) {
+    currentLiteral.setLength(0);
+    // Add the pattern converter to the list.
+    addToList(pc);
+    // Next pattern is assumed to be a literal.
+    state = LITERAL_STATE;
+    // Reset formatting info
+    formattingInfo.reset();
+  }
+
+  // ---------------------------------------------------------------------
+  //                      PatternConverters
+  // ---------------------------------------------------------------------
+
+  private static class BasicPatternConverter extends PatternConverter {
+    int type;
+
+    BasicPatternConverter(FormattingInfo formattingInfo, int type) {
+      super(formattingInfo);
+      this.type = type;
+    }
+
+    public
+    String convert(LoggingEvent event) {
+      switch(type) {
+      case RELATIVE_TIME_CONVERTER:
+	return (Long.toString(event.timeStamp - LoggingEvent.getStartTime()));
+      case THREAD_CONVERTER:
+	return event.getThreadName();
+      case LEVEL_CONVERTER:
+	return event.getLevel().toString();
+      case NDC_CONVERTER:
+	return event.getNDC();
+      case MESSAGE_CONVERTER: {
+	return event.getRenderedMessage();
+      }
+      default: return null;
+      }
+    }
+  }
+
+  private static class LiteralPatternConverter extends PatternConverter {
+    private String literal;
+
+    LiteralPatternConverter(String value) {
+      literal = value;
+    }
+
+    public
+    final
+    void format(StringBuffer sbuf, LoggingEvent event) {
+      sbuf.append(literal);
+    }
+
+    public
+    String convert(LoggingEvent event) {
+      return literal;
+    }
+  }
+
+  private static class DatePatternConverter extends PatternConverter {
+    private DateFormat df;
+    private Date date;
+
+    DatePatternConverter(FormattingInfo formattingInfo, DateFormat df) {
+      super(formattingInfo);
+      date = new Date();
+      this.df = df;
+    }
+
+    public
+    String convert(LoggingEvent event) {
+      date.setTime(event.timeStamp);
+      String converted = null;
+      try {
+        converted = df.format(date);
+      }
+      catch (Exception ex) {
+        LogLog.error("Error occured while converting date.", ex);
+      }
+      return converted;
+    }
+  }
+
+  private static class MDCPatternConverter extends PatternConverter {
+    private String key;
+
+    MDCPatternConverter(FormattingInfo formattingInfo, String key) {
+      super(formattingInfo);
+      this.key = key;
+    }
+
+    public
+    String convert(LoggingEvent event) {
+      if (key == null) {
+          StringBuffer buf = new StringBuffer("{");
+          Map properties = event.getProperties();
+          if (properties.size() > 0) {
+            Object[] keys = properties.keySet().toArray();
+            Arrays.sort(keys);
+            for (int i = 0; i < keys.length; i++) {
+                buf.append('{');
+                buf.append(keys[i]);
+                buf.append(',');
+                buf.append(properties.get(keys[i]));
+                buf.append('}');
+            }
+          }
+          buf.append('}');
+          return buf.toString();
+      } else {
+        Object val = event.getMDC(key);
+        if(val == null) {
+	        return null;
+        } else {
+	        return val.toString();
+        }
+      }
+    }
+  }
+
+
+  private class LocationPatternConverter extends PatternConverter {
+    int type;
+
+    LocationPatternConverter(FormattingInfo formattingInfo, int type) {
+      super(formattingInfo);
+      this.type = type;
+    }
+
+    public
+    String convert(LoggingEvent event) {
+      LocationInfo locationInfo = event.getLocationInformation();
+      switch(type) {
+      case FULL_LOCATION_CONVERTER:
+	return locationInfo.fullInfo;
+      case METHOD_LOCATION_CONVERTER:
+	return locationInfo.getMethodName();
+      case LINE_LOCATION_CONVERTER:
+	return locationInfo.getLineNumber();
+      case FILE_LOCATION_CONVERTER:
+	return locationInfo.getFileName();
+      default: return null;
+      }
+    }
+  }
+
+  private static abstract class NamedPatternConverter extends PatternConverter {
+    int precision;
+
+    NamedPatternConverter(FormattingInfo formattingInfo, int precision) {
+      super(formattingInfo);
+      this.precision =  precision;
+    }
+
+    abstract
+    String getFullyQualifiedName(LoggingEvent event);
+
+    public
+    String convert(LoggingEvent event) {
+      String n = getFullyQualifiedName(event);
+      if(precision <= 0)
+	return n;
+      else {
+	int len = n.length();
+
+	// We substract 1 from 'len' when assigning to 'end' to avoid out of
+	// bounds exception in return r.substring(end+1, len). This can happen if
+	// precision is 1 and the category name ends with a dot.
+	int end = len -1 ;
+	for(int i = precision; i > 0; i--) {
+	  end = n.lastIndexOf('.', end-1);
+	  if(end == -1)
+	    return n;
+	}
+	return n.substring(end+1, len);
+      }
+    }
+  }
+
+  private class ClassNamePatternConverter extends NamedPatternConverter {
+
+    ClassNamePatternConverter(FormattingInfo formattingInfo, int precision) {
+      super(formattingInfo, precision);
+    }
+
+    String getFullyQualifiedName(LoggingEvent event) {
+      return event.getLocationInformation().getClassName();
+    }
+  }
+
+  private class CategoryPatternConverter extends NamedPatternConverter {
+
+    CategoryPatternConverter(FormattingInfo formattingInfo, int precision) {
+      super(formattingInfo, precision);
+    }
+
+    String getFullyQualifiedName(LoggingEvent event) {
+      return event.getLoggerName();
+    }
+  }
+}
+
diff --git a/log4j-1.2-api/src/test/java/org/apache/log4j/LayoutTest.java b/log4j-1.2-api/src/test/java/org/apache/log4j/LayoutTest.java
new file mode 100644
index 0000000..991c3e9
--- /dev/null
+++ b/log4j-1.2-api/src/test/java/org/apache/log4j/LayoutTest.java
@@ -0,0 +1,168 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.log4j;
+
+import org.apache.log4j.spi.LoggingEvent;
+
+import junit.framework.TestCase;
+
+/**
+ * Tests for Layout.
+ *
+ */
+public class LayoutTest extends TestCase {
+
+    /**
+     * Concrete Layout class for tests.
+     */
+    private static final class MockLayout extends Layout {
+        /**
+         * @{inheritDoc}
+         */
+        public void activateOptions() {
+        }
+
+        /**
+         * @{inheritDoc}
+         */
+        public String format(final LoggingEvent event) {
+            return "Mock";
+        }
+
+        /**
+         * @{inheritDoc}
+         */
+        public boolean ignoresThrowable() {
+            return true;
+        }
+    }
+
+    /**
+     * Expected content type.
+     */
+    private final String contentType;
+
+    /**
+     * Expected value for ignoresThrowable.
+     */
+    private final boolean ignoresThrowable;
+
+    /**
+     * Expected value for header.
+     */
+    private final String header;
+
+    /**
+     * Expected value for footer.
+     */
+    private final String footer;
+
+    /**
+     * Construct a new instance of LayoutTest.
+     * 
+     * @param testName test name.
+     */
+    public LayoutTest(final String testName) {
+        super(testName);
+        contentType = "text/plain";
+        ignoresThrowable = true;
+        header = null;
+        footer = null;
+    }
+
+    /**
+     * Constructor for use by derived tests.
+     * 
+     * @param testName name of test.
+     * @param expectedContentType expected value for getContentType().
+     * @param expectedIgnoresThrowable expected value for ignoresThrowable().
+     * @param expectedHeader expected value for getHeader().
+     * @param expectedFooter expected value for getFooter().
+     */
+    protected LayoutTest(final String testName, final String expectedContentType, final boolean expectedIgnoresThrowable, final String expectedHeader,
+        final String expectedFooter) {
+        super(testName);
+        contentType = expectedContentType;
+        ignoresThrowable = expectedIgnoresThrowable;
+        header = expectedHeader;
+        footer = expectedFooter;
+    }
+
+    /**
+     * Creates layout for test.
+     * 
+     * @return new instance of Layout.
+     */
+    protected Layout createLayout() {
+        return new MockLayout();
+    }
+
+    /**
+     * Tests format.
+     * 
+     * @throws Exception derived tests, particular XMLLayoutTest, may throw exceptions.
+     */
+    public void testFormat() throws Exception {
+        Logger logger = Logger.getLogger("org.apache.log4j.LayoutTest");
+        LoggingEvent event = new LoggingEvent("org.apache.log4j.Logger", logger, Level.INFO, "Hello, World", null);
+        String result = createLayout().format(event);
+        assertEquals("Mock", result);
+    }
+
+    /**
+     * Tests getContentType.
+     */
+    public void testGetContentType() {
+        assertEquals(contentType, createLayout().getContentType());
+    }
+
+    /**
+     * Tests getFooter.
+     */
+    public void testGetFooter() {
+        assertEquals(footer, createLayout().getFooter());
+    }
+
+    /**
+     * Tests getHeader.
+     */
+    public void testGetHeader() {
+        assertEquals(header, createLayout().getHeader());
+    }
+
+    /**
+     * Tests ignoresThrowable.
+     */
+    public void testIgnoresThrowable() {
+        assertEquals(ignoresThrowable, createLayout().ignoresThrowable());
+    }
+
+    /**
+     * Tests Layout.LINE_SEP.
+     */
+    public void testLineSep() {
+        assertEquals(System.getProperty("line.separator"), Layout.LINE_SEP);
+    }
+
+    /**
+     * Tests Layout.LINE_SEP.
+     */
+    public void testLineSepLen() {
+        assertEquals(Layout.LINE_SEP.length(), Layout.LINE_SEP_LEN);
+    }
+}
diff --git a/log4j-1.2-api/src/test/java/org/apache/log4j/PropertyConfiguratorTest.java b/log4j-1.2-api/src/test/java/org/apache/log4j/PropertyConfiguratorTest.java
index 26812ec..347bfd0 100644
--- a/log4j-1.2-api/src/test/java/org/apache/log4j/PropertyConfiguratorTest.java
+++ b/log4j-1.2-api/src/test/java/org/apache/log4j/PropertyConfiguratorTest.java
@@ -406,7 +406,7 @@ public class PropertyConfiguratorTest {
         assertFalse(file.exists());
     }
 
-    void validateNested() {
+    public void validateNested() {
         final Logger logger = Logger.getLogger("org.apache.log4j.PropertyConfiguratorTest");
         final String appenderName = "ROLLING";
         // Appender OK
diff --git a/log4j-1.2-api/src/test/java/org/apache/log4j/helpers/DateLayoutTest.java b/log4j-1.2-api/src/test/java/org/apache/log4j/helpers/DateLayoutTest.java
new file mode 100644
index 0000000..0573e78
--- /dev/null
+++ b/log4j-1.2-api/src/test/java/org/apache/log4j/helpers/DateLayoutTest.java
@@ -0,0 +1,288 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.log4j.helpers;
+
+import org.apache.log4j.Layout;
+import org.apache.log4j.LayoutTest;
+import org.apache.log4j.spi.LoggingEvent;
+
+import java.text.DateFormat;
+import java.text.SimpleDateFormat;
+
+import java.util.TimeZone;
+import java.util.Calendar;
+
+/**
+ * Tests {@link DateLayout}.
+ */
+public class DateLayoutTest extends LayoutTest {
+
+    /**
+     * Construct a new instance of LayoutTest.
+     * 
+     * @param testName test name.
+     */
+    public DateLayoutTest(final String testName) {
+        super(testName);
+    }
+
+    /**
+     * Constructor for use by derived tests.
+     * 
+     * @param testName name of test.
+     * @param expectedContentType expected value for getContentType().
+     * @param expectedIgnoresThrowable expected value for ignoresThrowable().
+     * @param expectedHeader expected value for getHeader().
+     * @param expectedFooter expected value for getFooter().
+     */
+    protected DateLayoutTest(final String testName, final String expectedContentType, final boolean expectedIgnoresThrowable, final String expectedHeader,
+        final String expectedFooter) {
+        super(testName, expectedContentType, expectedIgnoresThrowable, expectedHeader, expectedFooter);
+    }
+
+    /**
+     * @{inheritDoc}
+     */
+    protected Layout createLayout() {
+        return new MockLayout();
+    }
+
+    /**
+     * Tests DateLayout.NULL_DATE_FORMAT constant.
+     */
+    public void testNullDateFormat() {
+        assertEquals("NULL", DateLayout.NULL_DATE_FORMAT);
+    }
+
+    /**
+     * Tests DateLayout.RELATIVE constant.
+     */
+    public void testRelativeTimeDateFormat() {
+        assertEquals("RELATIVE", DateLayout.RELATIVE_TIME_DATE_FORMAT);
+    }
+
+    /**
+     * Tests DateLayout.DATE_FORMAT_OPTION constant.
+     * 
+     * @deprecated since constant is deprecated
+     */
+    public void testDateFormatOption() {
+        assertEquals("DateFormat", DateLayout.DATE_FORMAT_OPTION);
+    }
+
+    /**
+     * Tests DateLayout.TIMEZONE_OPTION constant.
+     * 
+     * @deprecated since constant is deprecated
+     */
+    public void testTimeZoneOption() {
+        assertEquals("TimeZone", DateLayout.TIMEZONE_OPTION);
+    }
+
+    /**
+     * Tests getOptionStrings().
+     * 
+     * @deprecated since getOptionStrings is deprecated.
+     *
+     */
+    public void testGetOptionStrings() {
+        String[] options = ((DateLayout) createLayout()).getOptionStrings();
+        assertEquals(2, options.length);
+    }
+
+    /**
+     * Tests setting DateFormat through setOption method.
+     * 
+     * @deprecated since setOption is deprecated.
+     */
+    public void testSetOptionDateFormat() {
+        DateLayout layout = (DateLayout) createLayout();
+        layout.setOption("dAtefOrmat", "foobar");
+        assertEquals("FOOBAR", layout.getDateFormat());
+    }
+
+    /**
+     * Tests setting TimeZone through setOption method.
+     * 
+     * @deprecated since setOption is deprecated.
+     */
+    public void testSetOptionTimeZone() {
+        DateLayout layout = (DateLayout) createLayout();
+        layout.setOption("tImezOne", "+05:00");
+        assertEquals("+05:00", layout.getTimeZone());
+    }
+
+    /**
+     * Tests setDateFormat.
+     */
+    public void testSetDateFormat() {
+        DateLayout layout = (DateLayout) createLayout();
+        layout.setDateFormat("ABSOLUTE");
+        assertEquals("ABSOLUTE", layout.getDateFormat());
+    }
+
+    /**
+     * Tests setTimeZone.
+     */
+    public void testSetTimeZone() {
+        DateLayout layout = (DateLayout) createLayout();
+        layout.setTimeZone("+05:00");
+        assertEquals("+05:00", layout.getTimeZone());
+    }
+
+    /**
+     * Tests 2 parameter setDateFormat with null.
+     */
+    public void testSetDateFormatNull() {
+        DateLayout layout = (DateLayout) createLayout();
+        layout.setDateFormat((String) null, null);
+    }
+
+    /**
+     * Tests 2 parameter setDateFormat with "NULL".
+     */
+    public void testSetDateFormatNullString() {
+        DateLayout layout = (DateLayout) createLayout();
+        layout.setDateFormat("NuLL", null);
+    }
+
+    /**
+     * Tests 2 parameter setDateFormat with "RELATIVE".
+     */
+    public void testSetDateFormatRelative() {
+        DateLayout layout = (DateLayout) createLayout();
+        layout.setDateFormat("rElatIve", TimeZone.getDefault());
+    }
+
+    /**
+     * Tests 2 parameter setDateFormat with "ABSOLUTE".
+     */
+    public void testSetDateFormatAbsolute() {
+        DateLayout layout = (DateLayout) createLayout();
+        layout.setDateFormat("aBsolUte", TimeZone.getDefault());
+    }
+
+    /**
+     * Tests 2 parameter setDateFormat with "DATETIME".
+     */
+    public void testSetDateFormatDateTime() {
+        DateLayout layout = (DateLayout) createLayout();
+        layout.setDateFormat("dAte", TimeZone.getDefault());
+    }
+
+    /**
+     * Tests 2 parameter setDateFormat with "ISO8601".
+     */
+    public void testSetDateFormatISO8601() {
+        DateLayout layout = (DateLayout) createLayout();
+        layout.setDateFormat("iSo8601", TimeZone.getDefault());
+    }
+
+    /**
+     * Tests 2 parameter setDateFormat with "HH:mm:ss".
+     */
+    public void testSetDateFormatSimple() {
+        DateLayout layout = (DateLayout) createLayout();
+        layout.setDateFormat("HH:mm:ss", TimeZone.getDefault());
+    }
+
+    /**
+     * Tests activateOptions.
+     */
+    public void testActivateOptions() {
+        DateLayout layout = (DateLayout) createLayout();
+        layout.setDateFormat("HH:mm:ss");
+        layout.setTimeZone("+05:00");
+        layout.activateOptions();
+    }
+
+    /**
+     * Tests setDateFormat(DateFormat, TimeZone).
+     */
+    public void testSetDateFormatWithFormat() {
+        DateFormat format = new SimpleDateFormat("HH:mm");
+        DateLayout layout = (DateLayout) createLayout();
+        layout.setDateFormat(format, TimeZone.getDefault());
+    }
+
+    /**
+     * Tests IS08601DateFormat class.
+     * 
+     * @deprecated since ISO8601DateFormat is deprecated
+     */
+    public void testISO8601Format() {
+        DateFormat format = new ISO8601DateFormat();
+        Calendar calendar = Calendar.getInstance();
+        calendar.clear();
+        calendar.set(1970, 0, 1, 0, 0, 0);
+        String actual = format.format(calendar.getTime());
+        assertEquals("1970-01-01 00:00:00,000", actual);
+    }
+
+    /**
+     * Tests DateTimeDateFormat class.
+     * 
+     * @deprecated since DateTimeDateFormat is deprecated
+     */
+    public void testDateTimeFormat() {
+        DateFormat format = new DateTimeDateFormat();
+        Calendar calendar = Calendar.getInstance();
+        calendar.clear();
+        calendar.set(1970, 0, 1, 0, 0, 0);
+        String actual = format.format(calendar.getTime());
+        SimpleDateFormat df = new SimpleDateFormat("dd MMM yyyy HH:mm:ss,SSS");
+        String expected = df.format(calendar.getTime());
+        assertEquals(expected, actual);
+    }
+
+    /**
+     * Concrete Layout class for tests.
+     */
+    private static final class MockLayout extends DateLayout {
+        /**
+         * Create new instance of MockLayout.
+         */
+        public MockLayout() {
+            //
+            // checks that protected fields are properly initialized
+            assertNotNull(pos);
+            assertNotNull(date);
+            assertNull(dateFormat);
+        }
+
+        /**
+         * @{inheritDoc}
+         */
+        public String format(final LoggingEvent event) {
+            return "Mock";
+        }
+
+        /**
+         * @{inheritDoc}
+         */
+        public void activateOptions() {
+        }
+
+        /**
+         * @{inheritDoc}
+         */
+        public boolean ignoresThrowable() {
+            return true;
+        }
+    }
+}
diff --git a/log4j-1.2-api/src/test/java/org/apache/log4j/helpers/OptionConverterTestCase.java b/log4j-1.2-api/src/test/java/org/apache/log4j/helpers/OptionConverterTestCase.java
new file mode 100644
index 0000000..83ded20
--- /dev/null
+++ b/log4j-1.2-api/src/test/java/org/apache/log4j/helpers/OptionConverterTestCase.java
@@ -0,0 +1,177 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+// Log4j uses the JUnit framework for internal unit testing. JUnit
+// is available from "http://www.junit.org".
+
+package org.apache.log4j.helpers;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.util.Properties;
+
+import org.apache.log4j.Level;
+import org.apache.log4j.LogManager;
+import org.apache.log4j.PropertyConfiguratorTest;
+import org.apache.log4j.xml.XLevel;
+import org.junit.Ignore;
+
+import junit.framework.Test;
+import junit.framework.TestCase;
+import junit.framework.TestSuite;
+
+/**
+ * Test variable substitution code.
+ * 
+ * @since 1.0
+ */
+@Ignore("WIP")
+public class OptionConverterTestCase extends TestCase {
+
+    Properties props;
+
+    public OptionConverterTestCase(String name) {
+        super(name);
+    }
+
+    public void setUp() {
+        props = new Properties();
+        props.put("TOTO", "wonderful");
+        props.put("key1", "value1");
+        props.put("key2", "value2");
+        // Log4J will NPE without this:
+        props.put("line.separator", System.getProperty("line.separator"));
+        // Log4J will throw an Error without this:
+        props.put("java.home", System.getProperty("java.home"));
+        System.setProperties(props);
+
+    }
+
+    public void tearDown() {
+        props = null;
+        LogManager.resetConfiguration();
+    }
+
+    public void varSubstTest1() {
+        String r;
+
+        r = OptionConverter.substVars("hello world.", null);
+        assertEquals("hello world.", r);
+
+        r = OptionConverter.substVars("hello ${TOTO} world.", null);
+
+        assertEquals("hello wonderful world.", r);
+    }
+
+    public void varSubstTest2() {
+        String r;
+
+        r = OptionConverter.substVars("Test2 ${key1} mid ${key2} end.", null);
+        assertEquals("Test2 value1 mid value2 end.", r);
+    }
+
+    public void varSubstTest3() {
+        String r;
+
+        r = OptionConverter.substVars("Test3 ${unset} mid ${key1} end.", null);
+        assertEquals("Test3  mid value1 end.", r);
+    }
+
+    public void varSubstTest4() {
+        String val = "Test4 ${incomplete ";
+        try {
+            OptionConverter.substVars(val, null);
+        } catch (IllegalArgumentException e) {
+            String errorMsg = e.getMessage();
+            // System.out.println('['+errorMsg+']');
+            assertEquals('"' + val + "\" has no closing brace. Opening brace at position 6.", errorMsg);
+        }
+    }
+
+    public void varSubstTest5() {
+        Properties props = new Properties();
+        props.put("p1", "x1");
+        props.put("p2", "${p1}");
+        String res = OptionConverter.substVars("${p2}", props);
+        System.out.println("Result is [" + res + "].");
+        assertEquals(res, "x1");
+    }
+
+    /**
+     * Tests configuring Log4J from an InputStream.
+     * 
+     * @since 1.2.17
+     */
+    public void testInputStream() throws IOException {
+        File file = new File("src/test/resources/log4j1-1.2.17/input/filter1.properties");
+        assertTrue(file.exists());
+        try (FileInputStream inputStream = new FileInputStream(file)) {
+            OptionConverter.selectAndConfigure(inputStream, null, LogManager.getLoggerRepository());
+        }
+        new PropertyConfiguratorTest().validateNested();
+    }
+
+    public void toLevelTest1() {
+        String val = "INFO";
+        Level p = OptionConverter.toLevel(val, null);
+        assertEquals(p, Level.INFO);
+    }
+
+    public void toLevelTest2() {
+        String val = "INFO#org.apache.log4j.xml.XLevel";
+        Level p = OptionConverter.toLevel(val, null);
+        assertEquals(p, Level.INFO);
+    }
+
+    public void toLevelTest3() {
+        String val = "TRACE#org.apache.log4j.xml.XLevel";
+        Level p = OptionConverter.toLevel(val, null);
+        assertEquals(p, XLevel.TRACE);
+    }
+
+    public void toLevelTest4() {
+        String val = "TR#org.apache.log4j.xml.XLevel";
+        Level p = OptionConverter.toLevel(val, null);
+        assertEquals(p, null);
+    }
+
+    public void toLevelTest5() {
+        String val = "INFO#org.apache.log4j.xml.TOTO";
+        Level p = OptionConverter.toLevel(val, null);
+        assertEquals(p, null);
+    }
+
+    public static Test suite() {
+        TestSuite suite = new TestSuite();
+        suite.addTest(new OptionConverterTestCase("varSubstTest5"));
+        suite.addTest(new OptionConverterTestCase("varSubstTest1"));
+        suite.addTest(new OptionConverterTestCase("varSubstTest2"));
+        suite.addTest(new OptionConverterTestCase("varSubstTest3"));
+        suite.addTest(new OptionConverterTestCase("varSubstTest4"));
+
+        suite.addTest(new OptionConverterTestCase("testInputStream"));
+
+        suite.addTest(new OptionConverterTestCase("toLevelTest1"));
+        suite.addTest(new OptionConverterTestCase("toLevelTest2"));
+        suite.addTest(new OptionConverterTestCase("toLevelTest3"));
+        suite.addTest(new OptionConverterTestCase("toLevelTest4"));
+        suite.addTest(new OptionConverterTestCase("toLevelTest5"));
+        return suite;
+    }
+
+}
diff --git a/log4j-1.2-api/src/test/java/org/apache/log4j/helpers/PatternParserTestCase.java b/log4j-1.2-api/src/test/java/org/apache/log4j/helpers/PatternParserTestCase.java
new file mode 100644
index 0000000..e91cc0f
--- /dev/null
+++ b/log4j-1.2-api/src/test/java/org/apache/log4j/helpers/PatternParserTestCase.java
@@ -0,0 +1,131 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.log4j.helpers;
+
+import org.apache.log4j.Appender;
+import org.apache.log4j.FileAppender;
+import org.apache.log4j.Level;
+import org.apache.log4j.Logger;
+import org.apache.log4j.MDC;
+import org.apache.log4j.PatternLayout;
+import org.apache.log4j.util.Compare;
+import org.junit.Ignore;
+
+import junit.framework.Test;
+import junit.framework.TestCase;
+import junit.framework.TestSuite;
+
+/**
+ * Test case for helpers/PatternParser.java. Tests the various conversion patterns supported by PatternParser. This test
+ * class tests PatternParser via the PatternLayout class which uses it.
+ */
+@Ignore("WIP")
+public class PatternParserTestCase extends TestCase {
+
+    static String OUTPUT_FILE = "target/PatternParser";
+    static String WITNESS_FILE = "target/witness/PatternParser";
+
+    static String msgPattern = "%m%n";
+
+    public static Test suite() {
+        TestSuite suite = new TestSuite();
+        suite.addTest(new PatternParserTestCase("mdcPattern"));
+        return suite;
+    }
+    Logger root;
+
+    Logger logger;
+
+    public PatternParserTestCase(String name) {
+        super(name);
+    }
+
+    /**
+     * Test case for MDC conversion pattern.
+     */
+    public void mdcPattern() throws Exception {
+
+        String mdcMsgPattern1 = "%m : %X%n";
+        String mdcMsgPattern2 = "%m : %X{key1}%n";
+        String mdcMsgPattern3 = "%m : %X{key2}%n";
+        String mdcMsgPattern4 = "%m : %X{key3}%n";
+        String mdcMsgPattern5 = "%m : %X{key1},%X{key2},%X{key3}%n";
+
+        // set up appender
+        PatternLayout layout = new PatternLayout(msgPattern);
+        Appender appender = new FileAppender(layout, OUTPUT_FILE + "_mdc", false);
+
+        // set appender on root and set level to debug
+        root.addAppender(appender);
+        root.setLevel(Level.DEBUG);
+
+        // output starting message
+        root.debug("starting mdc pattern test");
+
+        layout.setConversionPattern(mdcMsgPattern1);
+        root.debug("empty mdc, no key specified in pattern");
+
+        layout.setConversionPattern(mdcMsgPattern2);
+        root.debug("empty mdc, key1 in pattern");
+
+        layout.setConversionPattern(mdcMsgPattern3);
+        root.debug("empty mdc, key2 in pattern");
+
+        layout.setConversionPattern(mdcMsgPattern4);
+        root.debug("empty mdc, key3 in pattern");
+
+        layout.setConversionPattern(mdcMsgPattern5);
+        root.debug("empty mdc, key1, key2, and key3 in pattern");
+
+        MDC.put("key1", "value1");
+        MDC.put("key2", "value2");
+
+        layout.setConversionPattern(mdcMsgPattern1);
+        root.debug("filled mdc, no key specified in pattern");
+
+        layout.setConversionPattern(mdcMsgPattern2);
+        root.debug("filled mdc, key1 in pattern");
+
+        layout.setConversionPattern(mdcMsgPattern3);
+        root.debug("filled mdc, key2 in pattern");
+
+        layout.setConversionPattern(mdcMsgPattern4);
+        root.debug("filled mdc, key3 in pattern");
+
+        layout.setConversionPattern(mdcMsgPattern5);
+        root.debug("filled mdc, key1, key2, and key3 in pattern");
+
+        MDC.remove("key1");
+        MDC.remove("key2");
+
+        layout.setConversionPattern(msgPattern);
+        root.debug("finished mdc pattern test");
+
+        assertTrue(Compare.compare(OUTPUT_FILE + "_mdc", WITNESS_FILE + "_mdc"));
+    }
+
+    public void setUp() {
+        root = Logger.getRootLogger();
+        root.removeAllAppenders();
+    }
+
+    public void tearDown() {
+        root.getLoggerRepository().resetConfiguration();
+    }
+
+}
diff --git a/log4j-1.2-api/src/test/java/org/apache/log4j/util/Compare.java b/log4j-1.2-api/src/test/java/org/apache/log4j/util/Compare.java
new file mode 100644
index 0000000..b65b9e9
--- /dev/null
+++ b/log4j-1.2-api/src/test/java/org/apache/log4j/util/Compare.java
@@ -0,0 +1,150 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.log4j.util;
+
+import java.io.BufferedReader;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.FileReader;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+
+public class Compare {
+
+    static final int B1_NULL = -1;
+    static final int B2_NULL = -2;
+
+    public static boolean compare(final Class testClass, final String file1, final String file2) throws IOException {
+        try (final BufferedReader in1 = new BufferedReader(new FileReader(file1));
+            final BufferedReader in2 = new BufferedReader(new InputStreamReader(open(testClass, file2)))) {
+            return compare(testClass, file1, file2, in1, in2);
+        }
+    }
+
+    public static boolean compare(final Class testClass, final String file1, final String file2, final BufferedReader in1, final BufferedReader in2)
+        throws IOException {
+
+        String s1;
+        int lineCounter = 0;
+
+        while ((s1 = in1.readLine()) != null) {
+            lineCounter++;
+
+            final String s2 = in2.readLine();
+
+            if (!s1.equals(s2)) {
+                System.out.println("Files [" + file1 + "] and [" + file2 + "] differ on line " + lineCounter);
+                System.out.println("One reads:  [" + s1 + "].");
+                System.out.println("Other reads:[" + s2 + "].");
+                outputFile(testClass, file1);
+                outputFile(testClass, file2);
+
+                return false;
+            }
+        }
+
+        // the second file is longer
+        if (in2.read() != -1) {
+            System.out.println("File [" + file2 + "] longer than file [" + file1 + "].");
+            outputFile(testClass, file1);
+            outputFile(testClass, file2);
+
+            return false;
+        }
+
+        return true;
+    }
+
+    static public boolean compare(final String file1, final String file2) throws FileNotFoundException, IOException {
+        try (final BufferedReader in1 = new BufferedReader(new FileReader(file1)); final BufferedReader in2 = new BufferedReader(new FileReader(file2))) {
+
+            String s1;
+            int lineCounter = 0;
+            while ((s1 = in1.readLine()) != null) {
+                lineCounter++;
+                final String s2 = in2.readLine();
+                if (!s1.equals(s2)) {
+                    System.out.println("Files [" + file1 + "] and [" + file2 + "] differ on line " + lineCounter);
+                    System.out.println("One reads:  [" + s1 + "].");
+                    System.out.println("Other reads:[" + s2 + "].");
+                    return false;
+                }
+            }
+
+            // the second file is longer
+            if (in2.read() != -1) {
+                System.out.println("File [" + file2 + "] longer than file [" + file1 + "].");
+                return false;
+            }
+
+            return true;
+        }
+    }
+
+    private static final InputStream open(final Class testClass, final String fileName) throws IOException {
+        String resourceName = fileName;
+        if (fileName.startsWith("witness/")) {
+            resourceName = fileName.substring(fileName.lastIndexOf('/') + 1);
+        }
+        InputStream is = testClass.getResourceAsStream(resourceName);
+        if (is == null) {
+            final File file = new File(fileName);
+            if (file.exists()) {
+                is = new FileInputStream(file);
+            } else {
+                throw new FileNotFoundException("Resource " + resourceName + " not found");
+            }
+        }
+        return is;
+    }
+
+    /**
+     *
+     * Prints file on the console.
+     *
+     */
+    private static void outputFile(final Class testClass, final String file) throws IOException {
+        try (final InputStream is = open(testClass, file); final BufferedReader in1 = new BufferedReader(new InputStreamReader(is))) {
+
+            String s1;
+            int lineCounter = 0;
+            System.out.println("--------------------------------");
+            System.out.println("Contents of " + file + ":");
+
+            while ((s1 = in1.readLine()) != null) {
+                lineCounter++;
+                System.out.print(lineCounter);
+
+                if (lineCounter < 10) {
+                    System.out.print("   : ");
+                } else if (lineCounter < 100) {
+                    System.out.print("  : ");
+                } else if (lineCounter < 1000) {
+                    System.out.print(" : ");
+                } else {
+                    System.out.print(": ");
+                }
+
+                System.out.println(s1);
+            }
+        }
+    }
+
+}
diff --git a/log4j-1.2-api/src/test/java/org/apache/log4j/xml/XLevel.java b/log4j-1.2-api/src/test/java/org/apache/log4j/xml/XLevel.java
new file mode 100644
index 0000000..b0503b7
--- /dev/null
+++ b/log4j-1.2-api/src/test/java/org/apache/log4j/xml/XLevel.java
@@ -0,0 +1,74 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.log4j.xml;
+
+import org.apache.log4j.Level;
+
+/**
+ * This class introduces a new level level called TRACE. TRACE has lower level than DEBUG.
+ */
+public class XLevel extends Level {
+    private static final long serialVersionUID = 7288304330257085144L;
+
+    public static final int TRACE_INT = Level.DEBUG_INT - 1;
+    public static final int LETHAL_INT = Level.FATAL_INT + 1;
+
+    private static String TRACE_STR = "TRACE";
+    private static String LETHAL_STR = "LETHAL";
+
+    public static final XLevel TRACE = new XLevel(TRACE_INT, TRACE_STR, 7);
+    public static final XLevel LETHAL = new XLevel(LETHAL_INT, LETHAL_STR, 0);
+
+    public static Level toLevel(final int i) throws IllegalArgumentException {
+        switch (i) {
+        case TRACE_INT:
+            return XLevel.TRACE;
+        case LETHAL_INT:
+            return XLevel.LETHAL;
+        }
+        return Level.toLevel(i);
+    }
+
+    /**
+     * Convert the string passed as argument to a level. If the conversion fails, then this method returns {@link #TRACE}.
+     */
+    public static Level toLevel(final String sArg) {
+        return toLevel(sArg, XLevel.TRACE);
+    }
+
+    public static Level toLevel(final String sArg, final Level defaultValue) {
+
+        if (sArg == null) {
+            return defaultValue;
+        }
+        final String stringVal = sArg.toUpperCase();
+
+        if (stringVal.equals(TRACE_STR)) {
+            return XLevel.TRACE;
+        } else if (stringVal.equals(LETHAL_STR)) {
+            return XLevel.LETHAL;
+        }
+
+        return Level.toLevel(sArg, defaultValue);
+    }
+
+    protected XLevel(final int level, final String strLevel, final int syslogEquiv) {
+        super(level, strLevel, syslogEquiv);
+    }
+
+}
diff --git a/src/changes/changes.xml b/src/changes/changes.xml
index a8722bd..c0e3c09 100644
--- a/src/changes/changes.xml
+++ b/src/changes/changes.xml
@@ -346,6 +346,15 @@
       <action dev="ggregory" type="fix" due-to="Gary Gregory">
         Log4j 1.2 bridge missing UtilLoggingLevel.
       </action>
+      <action dev="ggregory" type="fix" due-to="Gary Gregory">
+        Log4j 1.2 bridge missing FormattingInfo.
+      </action>
+      <action dev="ggregory" type="fix" due-to="Gary Gregory">
+        Log4j 1.2 bridge missing PatternConverter.
+      </action>
+      <action dev="ggregory" type="fix" due-to="Gary Gregory">
+        Log4j 1.2 bridge missing PatternParser.
+      </action>
       <action dev="ggregory" type="fix">
         JndiManager reverts to 2.17.0 behavior: Read the system property for each call.
       </action>

[logging-log4j2] 09/25: Log4j 1.2 bridge missing DefaultThrowableRenderer.

Posted by pk...@apache.org.
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 2dacaea29975b9b5f294aa2af0734610eb86e2d7
Author: Gary Gregory <ga...@gmail.com>
AuthorDate: Sat Feb 5 14:48:17 2022 -0500

    Log4j 1.2 bridge missing DefaultThrowableRenderer.
---
 .../org/apache/log4j/DefaultThrowableRenderer.java | 84 ++++++++++++++++++++++
 src/changes/changes.xml                            |  3 +-
 2 files changed, 86 insertions(+), 1 deletion(-)

diff --git a/log4j-1.2-api/src/main/java/org/apache/log4j/DefaultThrowableRenderer.java b/log4j-1.2-api/src/main/java/org/apache/log4j/DefaultThrowableRenderer.java
new file mode 100644
index 0000000..1913f9c
--- /dev/null
+++ b/log4j-1.2-api/src/main/java/org/apache/log4j/DefaultThrowableRenderer.java
@@ -0,0 +1,84 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.log4j;
+
+import java.io.IOException;
+import java.io.InterruptedIOException;
+import java.io.LineNumberReader;
+import java.io.PrintWriter;
+import java.io.StringReader;
+import java.io.StringWriter;
+import java.util.ArrayList;
+
+import org.apache.log4j.spi.ThrowableRenderer;
+
+/**
+ * Default implementation of {@link ThrowableRenderer} using {@link Throwable#printStackTrace(PrintWriter)}.
+ *
+ * @since 1.2.16
+ */
+public final class DefaultThrowableRenderer implements ThrowableRenderer {
+
+    /**
+     * Render throwable using Throwable.printStackTrace.
+     *
+     * @param throwable throwable, may not be null.
+     * @return string representation.
+     */
+    public static String[] render(final Throwable throwable) {
+        final StringWriter sw = new StringWriter();
+        final PrintWriter pw = new PrintWriter(sw);
+        try {
+            throwable.printStackTrace(pw);
+        } catch (final RuntimeException ex) {
+            // ignore
+        }
+        pw.flush();
+        final LineNumberReader reader = new LineNumberReader(new StringReader(sw.toString()));
+        final ArrayList<String> lines = new ArrayList<>();
+        try {
+            String line = reader.readLine();
+            while (line != null) {
+                lines.add(line);
+                line = reader.readLine();
+            }
+        } catch (final IOException ex) {
+            if (ex instanceof InterruptedIOException) {
+                Thread.currentThread().interrupt();
+            }
+            lines.add(ex.toString());
+        }
+        final String[] tempRep = new String[lines.size()];
+        lines.toArray(tempRep);
+        return tempRep;
+    }
+
+    /**
+     * Construct new instance.
+     */
+    public DefaultThrowableRenderer() {
+        // empty
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public String[] doRender(final Throwable throwable) {
+        return render(throwable);
+    }
+}
diff --git a/src/changes/changes.xml b/src/changes/changes.xml
index c7e29b0..276677f 100644
--- a/src/changes/changes.xml
+++ b/src/changes/changes.xml
@@ -325,7 +325,8 @@
       <action dev="ggregory" type="fix" due-to="Gary Gregory">
         Log4j 1.2 bridge supports the SocketAppender.
       </action>
-      <action dev="ggregory" type="fix" due-to="Gary Gregory, Piotr P. Karwasz">
+      <action dev="ggregory" type="fix" due-to="Gary Gregory">
+        Log4j 1.2 bridge missing DefaultThrowableRenderer.
       </action>
       <action dev="ggregory" type="fix">
         JndiManager reverts to 2.17.0 behavior: Read the system property for each call.

[logging-log4j2] 12/25: Format class.

Posted by pk...@apache.org.
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 7b7a12cc28547b8dcc806f36184aa863b2de0001
Author: Gary Gregory <ga...@gmail.com>
AuthorDate: Sat Feb 5 15:12:46 2022 -0500

    Format class.
    
    Conflicts:
    	log4j-1.2-api/src/main/java/org/apache/log4j/spi/LocationInfo.java
---
 .../java/org/apache/log4j/spi/LocationInfo.java    | 22 ++++++++++------------
 1 file changed, 10 insertions(+), 12 deletions(-)

diff --git a/log4j-1.2-api/src/main/java/org/apache/log4j/spi/LocationInfo.java b/log4j-1.2-api/src/main/java/org/apache/log4j/spi/LocationInfo.java
index df49fa9..c5e5b5e 100644
--- a/log4j-1.2-api/src/main/java/org/apache/log4j/spi/LocationInfo.java
+++ b/log4j-1.2-api/src/main/java/org/apache/log4j/spi/LocationInfo.java
@@ -17,9 +17,9 @@
 package org.apache.log4j.spi;
 
 /**
- The internal representation of caller location information.
-
- @since 0.8.3
+ * The internal representation of caller location information.
+ * 
+ * @since 0.8.3
  */
 public class LocationInfo implements java.io.Serializable {
 
@@ -36,17 +36,15 @@ public class LocationInfo implements java.io.Serializable {
     }
 
     /**
-     When location information is not available the constant
-     <code>NA</code> is returned. Current value of this string
-     constant is <b>?</b>.  */
+     * When location information is not available the constant <code>NA</code> is returned. Current value of this string
+     * constant is <b>?</b>.
+     */
     public final static String NA = "?";
 
     static final long serialVersionUID = -1325822038990805636L;
 
-
     /**
-     Return the fully qualified class name of the caller making the
-     logging request.
+     * Return the fully qualified class name of the caller making the logging request.
      @return The class name.
      */
     public String getClassName() {
@@ -54,7 +52,7 @@ public class LocationInfo implements java.io.Serializable {
     }
 
     /**
-     Return the file name of the caller.
+     * Return the file name of the caller.
      @return the file name.
      */
     public String getFileName() {
@@ -62,7 +60,7 @@ public class LocationInfo implements java.io.Serializable {
     }
 
     /**
-     Returns the line number of the caller.
+     * Returns the line number of the caller.
      @return The line number.
      */
     public String getLineNumber() {
@@ -70,7 +68,7 @@ public class LocationInfo implements java.io.Serializable {
     }
 
     /**
-     Returns the method name of the caller.
+     * Returns the method name of the caller.
      @return The method name.
      */
     public String getMethodName() {

[logging-log4j2] 11/25: Add ThrowableInformationTest.

Posted by pk...@apache.org.
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 f29e900edced22ea900af87b15d9d558dc2eb568
Author: Gary Gregory <ga...@gmail.com>
AuthorDate: Sat Feb 5 15:12:09 2022 -0500

    Add ThrowableInformationTest.
---
 .../apache/log4j/spi/ThrowableInformationTest.java | 338 +++++++++++++++++++++
 1 file changed, 338 insertions(+)

diff --git a/log4j-1.2-api/src/test/java/org/apache/log4j/spi/ThrowableInformationTest.java b/log4j-1.2-api/src/test/java/org/apache/log4j/spi/ThrowableInformationTest.java
new file mode 100644
index 0000000..e032bd7
--- /dev/null
+++ b/log4j-1.2-api/src/test/java/org/apache/log4j/spi/ThrowableInformationTest.java
@@ -0,0 +1,338 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.log4j.spi;
+
+import java.io.PrintWriter;
+
+import junit.framework.TestCase;
+
+/**
+ * Tests {@link ThrowableInformation}.
+ */
+public class ThrowableInformationTest extends TestCase {
+    
+    /**
+     * Create ThrowableInformationTest.
+     *
+     * @param name test name.
+     */
+    public ThrowableInformationTest(final String name) {
+        super(name);
+    }
+
+    /**
+     * Custom throwable that only calls methods overridden by VectorWriter in log4j 1.2.14 and earlier.
+     */
+    private static final class OverriddenThrowable extends Throwable {
+        private static final long serialVersionUID = 1L;
+
+        /**
+         * Create new instance.
+         */
+        public OverriddenThrowable() {
+        }
+
+        /**
+         * Print stack trace.
+         *
+         * @param s print writer.
+         */
+        public void printStackTrace(final PrintWriter s) {
+            s.print((Object) "print(Object)");
+            s.print("print(char[])".toCharArray());
+            s.print("print(String)");
+            s.println((Object) "println(Object)");
+            s.println("println(char[])".toCharArray());
+            s.println("println(String)");
+            s.write("write(char[])".toCharArray());
+            s.write("write(char[], int, int)".toCharArray(), 2, 8);
+            s.write("write(String, int, int)", 2, 8);
+        }
+    }
+
+    /**
+     * Test capturing stack trace from a throwable that only uses the PrintWriter methods overridden in log4j 1.2.14 and
+     * earlier.
+     */
+    public void testOverriddenBehavior() {
+        ThrowableInformation ti = new ThrowableInformation(new OverriddenThrowable());
+        String[] rep = ti.getThrowableStrRep();
+        assertEquals(4, rep.length);
+        assertEquals("print(Object)print(char[])print(String)println(Object)", rep[0]);
+        assertEquals("println(char[])", rep[1]);
+        assertEquals("println(String)", rep[2]);
+        assertEquals("write(char[])ite(charite(Stri", rep[3]);
+    }
+
+    /**
+     * Custom throwable that calls methods not overridden by VectorWriter in log4j 1.2.14 and earlier.
+     */
+    private static final class NotOverriddenThrowable extends Throwable {
+        private static final long serialVersionUID = 1L;
+
+        /**
+         * Create new instance.
+         */
+        public NotOverriddenThrowable() {
+        }
+
+        /**
+         * Print stack trace.
+         *
+         * @param s print writer.
+         */
+        public void printStackTrace(final PrintWriter s) {
+            s.print(true);
+            s.print('a');
+            s.print(1);
+            s.print(2L);
+            s.print(Float.MAX_VALUE);
+            s.print(Double.MIN_VALUE);
+            s.println(true);
+            s.println('a');
+            s.println(1);
+            s.println(2L);
+            s.println(Float.MAX_VALUE);
+            s.println(Double.MIN_VALUE);
+            s.write('C');
+        }
+    }
+
+    /**
+     * Test capturing stack trace from a throwable that uses the PrintWriter methods not overridden in log4j 1.2.14 and
+     * earlier.
+     */
+    public void testNotOverriddenBehavior() {
+        ThrowableInformation ti = new ThrowableInformation(new NotOverriddenThrowable());
+        String[] rep = ti.getThrowableStrRep();
+        assertEquals(7, rep.length);
+        StringBuffer buf = new StringBuffer(String.valueOf(true));
+        buf.append('a');
+        buf.append(String.valueOf(1));
+        buf.append(String.valueOf(2L));
+        buf.append(String.valueOf(Float.MAX_VALUE));
+        buf.append(String.valueOf(Double.MIN_VALUE));
+        buf.append(String.valueOf(true));
+        assertEquals(buf.toString(), rep[0]);
+        assertEquals("a", rep[1]);
+        assertEquals(String.valueOf(1), rep[2]);
+        assertEquals(String.valueOf(2L), rep[3]);
+        assertEquals(String.valueOf(Float.MAX_VALUE), rep[4]);
+        assertEquals(String.valueOf(Double.MIN_VALUE), rep[5]);
+        assertEquals("C", rep[6]);
+    }
+
+    /**
+     * Custom throwable that calls methods of VectorWriter with null.
+     */
+    private static final class NullThrowable extends Throwable {
+        private static final long serialVersionUID = 1L;
+
+        /**
+         * Create new instance.
+         */
+        public NullThrowable() {
+        }
+
+        /**
+         * Print stack trace.
+         *
+         * @param s print writer.
+         */
+        public void printStackTrace(final PrintWriter s) {
+            s.print((Object) null);
+            s.print((String) null);
+            s.println((Object) null);
+            s.println((String) null);
+        }
+    }
+
+    /**
+     * Test capturing stack trace from a throwable that passes null to PrintWriter methods.
+     */
+
+    public void testNull() {
+        ThrowableInformation ti = new ThrowableInformation(new NullThrowable());
+        String[] rep = ti.getThrowableStrRep();
+        assertEquals(2, rep.length);
+        String nullStr = String.valueOf((Object) null);
+        assertEquals(nullStr + nullStr + nullStr, rep[0]);
+        assertEquals(nullStr, rep[1]);
+    }
+
+    /**
+     * Custom throwable that does nothing in printStackTrace.
+     */
+    private static final class EmptyThrowable extends Throwable {
+        private static final long serialVersionUID = 1L;
+
+        /**
+         * Create new instance.
+         */
+        public EmptyThrowable() {
+        }
+
+        /**
+         * Print stack trace.
+         *
+         * @param s print writer.
+         */
+        public void printStackTrace(final PrintWriter s) {
+        }
+    }
+
+    /**
+     * Test capturing stack trace from a throwable that does nothing on a call to printStackTrace.
+     */
+
+    public void testEmpty() {
+        ThrowableInformation ti = new ThrowableInformation(new EmptyThrowable());
+        String[] rep = ti.getThrowableStrRep();
+        assertEquals(0, rep.length);
+    }
+
+    /**
+     * Custom throwable that emits a specified string in printStackTrace.
+     */
+    private static final class StringThrowable extends Throwable {
+        private static final long serialVersionUID = 1L;
+        /**
+         * Stack trace.
+         */
+        private final String stackTrace;
+
+        /**
+         * Create new instance.
+         * 
+         * @param trace stack trace.
+         */
+        public StringThrowable(final String trace) {
+            stackTrace = trace;
+        }
+
+        /**
+         * Print stack trace.
+         *
+         * @param s print writer.
+         */
+        public void printStackTrace(final PrintWriter s) {
+            s.print(stackTrace);
+        }
+    }
+
+    /**
+     * Test capturing stack trace from throwable that just has a line feed.
+     */
+    public void testLineFeed() {
+        ThrowableInformation ti = new ThrowableInformation(new StringThrowable("\n"));
+        String[] rep = ti.getThrowableStrRep();
+        assertEquals(1, rep.length);
+        assertEquals("", rep[0]);
+    }
+
+    /**
+     * Test capturing stack trace from throwable that just has a carriage return.
+     */
+    public void testCarriageReturn() {
+        ThrowableInformation ti = new ThrowableInformation(new StringThrowable("\r"));
+        String[] rep = ti.getThrowableStrRep();
+        assertEquals(1, rep.length);
+        assertEquals("", rep[0]);
+    }
+
+    /**
+     * Test parsing of line breaks.
+     */
+    public void testParsing() {
+        ThrowableInformation ti = new ThrowableInformation(new StringThrowable("Line1\rLine2\nLine3\r\nLine4\n\rLine6"));
+        String[] rep = ti.getThrowableStrRep();
+        assertEquals(6, rep.length);
+        assertEquals("Line1", rep[0]);
+        assertEquals("Line2", rep[1]);
+        assertEquals("Line3", rep[2]);
+        assertEquals("Line4", rep[3]);
+        assertEquals("", rep[4]);
+        assertEquals("Line6", rep[5]);
+    }
+
+    /**
+     * Test capturing stack trace from throwable that a line feed followed by blank.
+     */
+    public void testLineFeedBlank() {
+        ThrowableInformation ti = new ThrowableInformation(new StringThrowable("\n "));
+        String[] rep = ti.getThrowableStrRep();
+        assertEquals(2, rep.length);
+        assertEquals("", rep[0]);
+        assertEquals(" ", rep[1]);
+    }
+
+    /**
+     * Test that getThrowable returns the throwable provided to the constructor.
+     */
+    public void testGetThrowable() {
+        Throwable t = new StringThrowable("Hello, World");
+        ThrowableInformation ti = new ThrowableInformation(t);
+        assertSame(t, ti.getThrowable());
+    }
+
+    /**
+     * Tests isolation of returned string representation from internal state of ThrowableInformation. log4j 1.2.15 and
+     * earlier did not isolate initial call. See bug 44032.
+     */
+    public void testIsolation() {
+        ThrowableInformation ti = new ThrowableInformation(new StringThrowable("Hello, World"));
+        String[] rep = ti.getThrowableStrRep();
+        assertEquals("Hello, World", rep[0]);
+        rep[0] = "Bonjour, Monde";
+        String[] rep2 = ti.getThrowableStrRep();
+        assertEquals("Hello, World", rep2[0]);
+    }
+
+    /**
+     * Custom throwable that throws a runtime exception when printStackTrace is called.
+     */
+    private static final class NastyThrowable extends Throwable {
+        private static final long serialVersionUID = 1L;
+
+        /**
+         * Create new instance.
+         */
+        public NastyThrowable() {
+        }
+
+        /**
+         * Print stack trace.
+         *
+         * @param s print writer.
+         */
+        public void printStackTrace(final PrintWriter s) {
+            s.print("NastyException");
+            throw new RuntimeException("Intentional exception");
+        }
+    }
+
+    /**
+     * Tests that a failure in printStackTrace does not percolate out of getThrowableStrRep().
+     *
+     */
+    public void testNastyException() {
+        ThrowableInformation ti = new ThrowableInformation(new NastyThrowable());
+        String[] rep = ti.getThrowableStrRep();
+        assertEquals("NastyException", rep[0]);
+    }
+
+}

[logging-log4j2] 10/25: Log4j 1.2 bridge missing some ThrowableInformation constructors.

Posted by pk...@apache.org.
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 2f430add5a645bd263e0f345aeae1eb8a0f52d93
Author: Gary Gregory <ga...@gmail.com>
AuthorDate: Sat Feb 5 15:09:08 2022 -0500

    Log4j 1.2 bridge missing some ThrowableInformation constructors.
    
    Conflicts:
    	log4j-1.2-api/src/main/java/org/apache/log4j/spi/ThrowableInformation.java
---
 .../org/apache/log4j/spi/ThrowableInformation.java | 46 +++++++++++++++++-----
 src/changes/changes.xml                            |  3 ++
 2 files changed, 39 insertions(+), 10 deletions(-)

diff --git a/log4j-1.2-api/src/main/java/org/apache/log4j/spi/ThrowableInformation.java b/log4j-1.2-api/src/main/java/org/apache/log4j/spi/ThrowableInformation.java
index e9c9776..de81a1a 100644
--- a/log4j-1.2-api/src/main/java/org/apache/log4j/spi/ThrowableInformation.java
+++ b/log4j-1.2-api/src/main/java/org/apache/log4j/spi/ThrowableInformation.java
@@ -16,29 +16,42 @@
  */
 package org.apache.log4j.spi;
 
-import java.lang.reflect.InvocationTargetException;
 import java.lang.reflect.Method;
 import java.util.List;
 
+import org.apache.log4j.Category;
 import org.apache.logging.log4j.core.util.Throwables;
 
 /**
- * Class Description goes here.
+ * Log4j's internal representation of throwables.
  */
 public class ThrowableInformation implements java.io.Serializable {
 
     static final long serialVersionUID = -4748765566864322735L;
 
     private transient Throwable throwable;
-    private final Method toStringList;
+    private transient Category category;
+    private String[] rep;
+    private Method toStringList;
 
-    @SuppressWarnings("unchecked")
-    public
-    ThrowableInformation(Throwable throwable) {
+    /**
+     * Constructs new instance.
+     *
+     * @since 1.2.15
+     * @param r String representation of throwable.
+     */
+    public ThrowableInformation(final String[] r) {
+        this.rep = rep != null ? r.clone() : null;
+    }
+
+    /**
+     * Constructs new instance.
+     */
+    public ThrowableInformation(Throwable throwable) {
         this.throwable = throwable;
         Method method = null;
         try {
-            Class throwables = Class.forName("org.apache.logging.log4j.core.util.Throwables");
+            final Class<?> throwables = Class.forName("org.apache.logging.log4j.core.util.Throwables");
             method = throwables.getMethod("toStringList", Throwable.class);
         } catch (ClassNotFoundException | NoSuchMethodException ex) {
             // Ignore the exception if Log4j-core is not present.
@@ -46,6 +59,19 @@ public class ThrowableInformation implements java.io.Serializable {
         this.toStringList = method;
     }
 
+    /**
+     * Constructs a new instance.
+     *
+     * @param throwable throwable, may not be null.
+     * @param category category used to obtain ThrowableRenderer, may be null.
+     * @since 1.2.16
+     */
+    public ThrowableInformation(final Throwable throwable, final Category category) {
+        this(throwable);
+        this.category = category;
+        this.rep = null;
+    }
+
     public
     Throwable getThrowable() {
         return throwable;
@@ -55,15 +81,15 @@ public class ThrowableInformation implements java.io.Serializable {
         if (toStringList != null && throwable != null) {
             try {
                 @SuppressWarnings("unchecked")
+                final
                 List<String> elements = (List<String>) toStringList.invoke(null, throwable);
                 if (elements != null) {
                     return elements.toArray(new String[0]);
                 }
-            } catch (IllegalAccessException | InvocationTargetException ex) {
+            } catch (final ReflectiveOperationException ex) {
                 // Ignore the exception.
             }
         }
-        return null;
+        return rep;
     }
 }
-
diff --git a/src/changes/changes.xml b/src/changes/changes.xml
index 276677f..7d288e7 100644
--- a/src/changes/changes.xml
+++ b/src/changes/changes.xml
@@ -328,6 +328,9 @@
       <action dev="ggregory" type="fix" due-to="Gary Gregory">
         Log4j 1.2 bridge missing DefaultThrowableRenderer.
       </action>
+      <action dev="ggregory" type="fix" due-to="Gary Gregory">
+        Log4j 1.2 bridge missing some ThrowableInformation constructors.
+      </action>
       <action dev="ggregory" type="fix">
         JndiManager reverts to 2.17.0 behavior: Read the system property for each call.
       </action>

[logging-log4j2] 07/25: Add TODO.

Posted by pk...@apache.org.
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 9742cc5251aee488e5a159a34349aaa0575374cf
Author: Gary Gregory <ga...@gmail.com>
AuthorDate: Sat Feb 5 09:09:54 2022 -0500

    Add TODO.
---
 .../src/test/java/org/apache/log4j/config/SyslogAppenderTest.java        | 1 +
 1 file changed, 1 insertion(+)

diff --git a/log4j-1.2-api/src/test/java/org/apache/log4j/config/SyslogAppenderTest.java b/log4j-1.2-api/src/test/java/org/apache/log4j/config/SyslogAppenderTest.java
index 3b75155..b6108e6 100644
--- a/log4j-1.2-api/src/test/java/org/apache/log4j/config/SyslogAppenderTest.java
+++ b/log4j-1.2-api/src/test/java/org/apache/log4j/config/SyslogAppenderTest.java
@@ -38,6 +38,7 @@ import static org.junit.Assert.assertNotNull;
  */
 public class SyslogAppenderTest {
 
+    // TODO Use an ephemeral port, save it in a sys prop, and update test config files.
     private static final int PORTNUM = 9999;
     private MockSyslogServer syslogServer;
 

[logging-log4j2] 16/25: Add missing util classes in org.apache.log4j.pattern.

Posted by pk...@apache.org.
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 a129b245a1a1507b82170d667a29c559b522025b
Author: Gary Gregory <ga...@gmail.com>
AuthorDate: Sat Feb 5 16:29:12 2022 -0500

    Add missing util classes in org.apache.log4j.pattern.
    
    - Log4j 1.2 bridge missed org.apache.log4j.pattern.FormattingInfo.
    - Log4j 1.2 bridge missed org.apache.log4j.pattern.NameAbbreviator.
---
 .../org/apache/log4j/pattern/FormattingInfo.java   | 128 ++++++++
 .../org/apache/log4j/pattern/NameAbbreviator.java  | 345 +++++++++++++++++++++
 .../apache/log4j/pattern/FormattingInfoTest.java   |  92 ++++++
 .../apache/log4j/pattern/NameAbbreviatorTest.java  | 329 ++++++++++++++++++++
 src/changes/changes.xml                            |   9 +
 5 files changed, 903 insertions(+)

diff --git a/log4j-1.2-api/src/main/java/org/apache/log4j/pattern/FormattingInfo.java b/log4j-1.2-api/src/main/java/org/apache/log4j/pattern/FormattingInfo.java
new file mode 100644
index 0000000..d8faaf4
--- /dev/null
+++ b/log4j-1.2-api/src/main/java/org/apache/log4j/pattern/FormattingInfo.java
@@ -0,0 +1,128 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.log4j.pattern;
+
+/**
+ * Modifies the output of a pattern converter for a specified minimum and maximum width and alignment.
+ */
+public final class FormattingInfo {
+    /**
+     * Array of spaces.
+     */
+    private static final char[] SPACES = new char[] {' ', ' ', ' ', ' ', ' ', ' ', ' ', ' '};
+
+    /**
+     * Default instance.
+     */
+    private static final FormattingInfo DEFAULT = new FormattingInfo(false, 0, Integer.MAX_VALUE);
+
+    /**
+     * Gets default instance.
+     *
+     * @return default instance.
+     */
+    public static FormattingInfo getDefault() {
+        return DEFAULT;
+    }
+
+    /**
+     * Minimum length.
+     */
+    private final int minLength;
+
+    /**
+     * Maximum length.
+     */
+    private final int maxLength;
+
+    /**
+     * Alignment.
+     */
+    private final boolean leftAlign;
+
+    /**
+     * Creates new instance.
+     *
+     * @param leftAlign left align if true.
+     * @param minLength minimum length.
+     * @param maxLength maximum length.
+     */
+    public FormattingInfo(final boolean leftAlign, final int minLength, final int maxLength) {
+        this.leftAlign = leftAlign;
+        this.minLength = minLength;
+        this.maxLength = maxLength;
+    }
+
+    /**
+     * Adjust the content of the buffer based on the specified lengths and alignment.
+     *
+     * @param fieldStart start of field in buffer.
+     * @param buffer buffer to be modified.
+     */
+    public void format(final int fieldStart, final StringBuffer buffer) {
+        final int rawLength = buffer.length() - fieldStart;
+
+        if (rawLength > maxLength) {
+            buffer.delete(fieldStart, buffer.length() - maxLength);
+        } else if (rawLength < minLength) {
+            if (leftAlign) {
+                final int fieldEnd = buffer.length();
+                buffer.setLength(fieldStart + minLength);
+
+                for (int i = fieldEnd; i < buffer.length(); i++) {
+                    buffer.setCharAt(i, ' ');
+                }
+            } else {
+                int padLength = minLength - rawLength;
+
+                for (; padLength > 8; padLength -= 8) {
+                    buffer.insert(fieldStart, SPACES);
+                }
+
+                buffer.insert(fieldStart, SPACES, 0, padLength);
+            }
+        }
+    }
+
+    /**
+     * Get maximum length.
+     *
+     * @return maximum length.
+     */
+    public int getMaxLength() {
+        return maxLength;
+    }
+
+    /**
+     * Get minimum length.
+     *
+     * @return minimum length.
+     */
+    public int getMinLength() {
+        return minLength;
+    }
+
+    /**
+     * Determine if left aligned.
+     *
+     * @return true if left aligned.
+     */
+    public boolean isLeftAligned() {
+        return leftAlign;
+    }
+}
diff --git a/log4j-1.2-api/src/main/java/org/apache/log4j/pattern/NameAbbreviator.java b/log4j-1.2-api/src/main/java/org/apache/log4j/pattern/NameAbbreviator.java
new file mode 100644
index 0000000..8905ecb
--- /dev/null
+++ b/log4j-1.2-api/src/main/java/org/apache/log4j/pattern/NameAbbreviator.java
@@ -0,0 +1,345 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.log4j.pattern;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * NameAbbreviator generates abbreviated logger and class names.
+ */
+public abstract class NameAbbreviator {
+
+    /**
+     * Abbreviator that drops starting path elements.
+     */
+    private static class DropElementAbbreviator extends NameAbbreviator {
+        /**
+         * Maximum number of path elements to output.
+         */
+        private final int count;
+
+        /**
+         * Create new instance.
+         *
+         * @param count maximum number of path elements to output.
+         */
+        public DropElementAbbreviator(final int count) {
+            this.count = count;
+        }
+
+        /**
+         * Abbreviate name.
+         *
+         * @param buf buffer to append abbreviation.
+         * @param nameStart start of name to abbreviate.
+         */
+        @Override
+        public void abbreviate(final int nameStart, final StringBuffer buf) {
+            int i = count;
+            for (int pos = buf.indexOf(".", nameStart); pos != -1; pos = buf.indexOf(".", pos + 1)) {
+                if (--i == 0) {
+                    buf.delete(nameStart, pos + 1);
+                    break;
+                }
+            }
+        }
+    }
+
+    /**
+     * Abbreviator that drops starting path elements.
+     */
+    private static class MaxElementAbbreviator extends NameAbbreviator {
+        /**
+         * Maximum number of path elements to output.
+         */
+        private final int count;
+
+        /**
+         * Create new instance.
+         *
+         * @param count maximum number of path elements to output.
+         */
+        public MaxElementAbbreviator(final int count) {
+            this.count = count;
+        }
+
+        /**
+         * Abbreviate name.
+         *
+         * @param buf buffer to append abbreviation.
+         * @param nameStart start of name to abbreviate.
+         */
+        @Override
+        public void abbreviate(final int nameStart, final StringBuffer buf) {
+            // We substract 1 from 'len' when assigning to 'end' to avoid out of
+            // bounds exception in return r.substring(end+1, len). This can happen if
+            // precision is 1 and the category name ends with a dot.
+            int end = buf.length() - 1;
+
+            final String bufString = buf.toString();
+            for (int i = count; i > 0; i--) {
+                end = bufString.lastIndexOf(".", end - 1);
+
+                if ((end == -1) || (end < nameStart)) {
+                    return;
+                }
+            }
+
+            buf.delete(nameStart, end + 1);
+        }
+    }
+
+    /**
+     * Abbreviator that simply appends full name to buffer.
+     */
+    private static class NOPAbbreviator extends NameAbbreviator {
+        /**
+         * Constructor.
+         */
+        public NOPAbbreviator() {
+        }
+
+        /**
+         * {@inheritDoc}
+         */
+        @Override
+        public void abbreviate(final int nameStart, final StringBuffer buf) {
+        }
+    }
+
+    /**
+     * Pattern abbreviator.
+     *
+     *
+     */
+    private static class PatternAbbreviator extends NameAbbreviator {
+        /**
+         * Element abbreviation patterns.
+         */
+        private final PatternAbbreviatorFragment[] fragments;
+
+        /**
+         * Create PatternAbbreviator.
+         *
+         * @param fragments element abbreviation patterns.
+         */
+        public PatternAbbreviator(final List fragments) {
+            if (fragments.size() == 0) {
+                throw new IllegalArgumentException("fragments must have at least one element");
+            }
+
+            this.fragments = new PatternAbbreviatorFragment[fragments.size()];
+            fragments.toArray(this.fragments);
+        }
+
+        /**
+         * Abbreviate name.
+         *
+         * @param buf buffer that abbreviated name is appended.
+         * @param nameStart start of name.
+         */
+        @Override
+        public void abbreviate(final int nameStart, final StringBuffer buf) {
+            //
+            // all non-terminal patterns are executed once
+            //
+            int pos = nameStart;
+
+            for (int i = 0; (i < (fragments.length - 1)) && (pos < buf.length()); i++) {
+                pos = fragments[i].abbreviate(buf, pos);
+            }
+
+            //
+            // last pattern in executed repeatedly
+            //
+            final PatternAbbreviatorFragment terminalFragment = fragments[fragments.length - 1];
+
+            while ((pos < buf.length()) && (pos >= 0)) {
+                pos = terminalFragment.abbreviate(buf, pos);
+            }
+        }
+    }
+
+    /**
+     * Fragment of an pattern abbreviator.
+     *
+     */
+    private static class PatternAbbreviatorFragment {
+        /**
+         * Count of initial characters of element to output.
+         */
+        private final int charCount;
+
+        /**
+         * Character used to represent dropped characters. '\0' indicates no representation of dropped characters.
+         */
+        private final char ellipsis;
+
+        /**
+         * Creates a PatternAbbreviatorFragment.
+         *
+         * @param charCount number of initial characters to preserve.
+         * @param ellipsis character to represent elimination of characters, '\0' if no ellipsis is desired.
+         */
+        public PatternAbbreviatorFragment(final int charCount, final char ellipsis) {
+            this.charCount = charCount;
+            this.ellipsis = ellipsis;
+        }
+
+        /**
+         * Abbreviate element of name.
+         *
+         * @param buf buffer to receive element.
+         * @param startPos starting index of name element.
+         * @return starting index of next element.
+         */
+        public int abbreviate(final StringBuffer buf, final int startPos) {
+            int nextDot = buf.toString().indexOf(".", startPos);
+
+            if (nextDot != -1) {
+                if ((nextDot - startPos) > charCount) {
+                    buf.delete(startPos + charCount, nextDot);
+                    nextDot = startPos + charCount;
+
+                    if (ellipsis != '\0') {
+                        buf.insert(nextDot, ellipsis);
+                        nextDot++;
+                    }
+                }
+
+                nextDot++;
+            }
+
+            return nextDot;
+        }
+    }
+
+    /**
+     * Default (no abbreviation) abbreviator.
+     */
+    private static final NameAbbreviator DEFAULT = new NOPAbbreviator();
+
+    /**
+     * Gets an abbreviator.
+     *
+     * For example, "%logger{2}" will output only 2 elements of the logger name, %logger{-2} will drop 2 elements from the
+     * logger name, "%logger{1.}" will output only the first character of the non-final elements in the name,
+     * "%logger{1~.2~} will output the first character of the first element, two characters of the second and subsequent
+     * elements and will use a tilde to indicate abbreviated characters.
+     *
+     * @param pattern abbreviation pattern.
+     * @return abbreviator, will not be null.
+     */
+    public static NameAbbreviator getAbbreviator(final String pattern) {
+        if (pattern.length() > 0) {
+            // if pattern is just spaces and numbers then
+            // use MaxElementAbbreviator
+            final String trimmed = pattern.trim();
+
+            if (trimmed.length() == 0) {
+                return DEFAULT;
+            }
+
+            int i = 0;
+            if (trimmed.length() > 0) {
+                if (trimmed.charAt(0) == '-') {
+                    i++;
+                }
+                for (; (i < trimmed.length()) && (trimmed.charAt(i) >= '0') && (trimmed.charAt(i) <= '9'); i++) {
+                }
+            }
+
+            //
+            // if all blanks and digits
+            //
+            if (i == trimmed.length()) {
+                final int elements = Integer.parseInt(trimmed);
+                if (elements >= 0) {
+                    return new MaxElementAbbreviator(elements);
+                } else {
+                    return new DropElementAbbreviator(-elements);
+                }
+            }
+
+            final ArrayList fragments = new ArrayList(5);
+            char ellipsis;
+            int charCount;
+            int pos = 0;
+
+            while ((pos < trimmed.length()) && (pos >= 0)) {
+                int ellipsisPos = pos;
+
+                if (trimmed.charAt(pos) == '*') {
+                    charCount = Integer.MAX_VALUE;
+                    ellipsisPos++;
+                } else {
+                    if ((trimmed.charAt(pos) >= '0') && (trimmed.charAt(pos) <= '9')) {
+                        charCount = trimmed.charAt(pos) - '0';
+                        ellipsisPos++;
+                    } else {
+                        charCount = 0;
+                    }
+                }
+
+                ellipsis = '\0';
+
+                if (ellipsisPos < trimmed.length()) {
+                    ellipsis = trimmed.charAt(ellipsisPos);
+
+                    if (ellipsis == '.') {
+                        ellipsis = '\0';
+                    }
+                }
+
+                fragments.add(new PatternAbbreviatorFragment(charCount, ellipsis));
+                pos = trimmed.indexOf(".", pos);
+
+                if (pos == -1) {
+                    break;
+                }
+
+                pos++;
+            }
+
+            return new PatternAbbreviator(fragments);
+        }
+
+        //
+        // no matching abbreviation, return defaultAbbreviator
+        //
+        return DEFAULT;
+    }
+
+    /**
+     * Gets default abbreviator.
+     *
+     * @return default abbreviator.
+     */
+    public static NameAbbreviator getDefaultAbbreviator() {
+        return DEFAULT;
+    }
+
+    /**
+     * Abbreviates a name in a StringBuffer.
+     *
+     * @param nameStart starting position of name in buf.
+     * @param buf buffer, may not be null.
+     */
+    public abstract void abbreviate(final int nameStart, final StringBuffer buf);
+}
diff --git a/log4j-1.2-api/src/test/java/org/apache/log4j/pattern/FormattingInfoTest.java b/log4j-1.2-api/src/test/java/org/apache/log4j/pattern/FormattingInfoTest.java
new file mode 100644
index 0000000..d4a396d
--- /dev/null
+++ b/log4j-1.2-api/src/test/java/org/apache/log4j/pattern/FormattingInfoTest.java
@@ -0,0 +1,92 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.log4j.pattern;
+
+import junit.framework.TestCase;
+
+/**
+ * Tests for FormattingInfo.
+ *
+ * @author Curt Arnold
+ *
+ */
+public class FormattingInfoTest extends TestCase {
+    /**
+     * Create a new instance.
+     *
+     * @param name test name
+     */
+    public FormattingInfoTest(final String name) {
+        super(name);
+    }
+
+    /**
+     * Check constructor
+     *
+     */
+    public void testConstructor() {
+        FormattingInfo field = new FormattingInfo(true, 3, 6);
+        assertNotNull(field);
+        assertEquals(3, field.getMinLength());
+        assertEquals(6, field.getMaxLength());
+        assertEquals(true, field.isLeftAligned());
+    }
+
+    /**
+     * Check that getDefault does not return null.
+     *
+     */
+    public void testGetDefault() {
+        FormattingInfo field = FormattingInfo.getDefault();
+        assertNotNull(field);
+        assertEquals(0, field.getMinLength());
+        assertEquals(Integer.MAX_VALUE, field.getMaxLength());
+        assertEquals(false, field.isLeftAligned());
+    }
+
+    /**
+     * Add padding to left since field is not minimum width.
+     */
+    public void testPadLeft() {
+        StringBuffer buf = new StringBuffer("foobar");
+        FormattingInfo field = new FormattingInfo(false, 5, 10);
+        field.format(2, buf);
+        assertEquals("fo obar", buf.toString());
+    }
+
+    /**
+     * Add padding to right since field is not minimum width.
+     */
+    public void testPadRight() {
+        StringBuffer buf = new StringBuffer("foobar");
+        FormattingInfo field = new FormattingInfo(true, 5, 10);
+        field.format(2, buf);
+        assertEquals("foobar ", buf.toString());
+    }
+
+    /**
+     * Field exceeds maximum width
+     */
+    public void testTruncate() {
+        StringBuffer buf = new StringBuffer("foobar");
+        FormattingInfo field = new FormattingInfo(true, 0, 3);
+        field.format(2, buf);
+        assertEquals("fobar", buf.toString());
+    }
+
+}
diff --git a/log4j-1.2-api/src/test/java/org/apache/log4j/pattern/NameAbbreviatorTest.java b/log4j-1.2-api/src/test/java/org/apache/log4j/pattern/NameAbbreviatorTest.java
new file mode 100644
index 0000000..5494a59
--- /dev/null
+++ b/log4j-1.2-api/src/test/java/org/apache/log4j/pattern/NameAbbreviatorTest.java
@@ -0,0 +1,329 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.log4j.pattern;
+
+import junit.framework.TestCase;
+
+/**
+ * Tests for NameAbbrevator.
+ *
+ */
+public class NameAbbreviatorTest extends TestCase {
+    /**
+     * Create a new instance.
+     *
+     * @param name test name
+     */
+    public NameAbbreviatorTest(final String name) {
+        super(name);
+    }
+
+    /**
+     * Check that getAbbreviator(" ") returns default abbreviator.
+     *
+     */
+    public void testBlank() {
+        NameAbbreviator abbrev = NameAbbreviator.getAbbreviator("   ");
+        NameAbbreviator defaultAbbrev = NameAbbreviator.getDefaultAbbreviator();
+        assertTrue(abbrev == defaultAbbrev);
+    }
+
+    /**
+     * Check that blanks are trimmed in evaluating abbreviation pattern.
+     */
+    public void testBlankOne() {
+        NameAbbreviator abbrev = NameAbbreviator.getAbbreviator(" 1 ");
+        StringBuffer buf = new StringBuffer("DEBUG - ");
+        int fieldStart = buf.length();
+        buf.append("org.example.foo.bar");
+        abbrev.abbreviate(fieldStart, buf);
+        assertEquals("DEBUG - bar", buf.toString());
+
+        buf.setLength(0);
+        buf.append("DEBUG - ");
+        fieldStart = buf.length();
+        buf.append("bar");
+        abbrev.abbreviate(fieldStart, buf);
+        assertEquals("DEBUG - bar", buf.toString());
+
+        buf.setLength(0);
+        buf.append("DEBUG - ");
+        fieldStart = buf.length();
+        abbrev.abbreviate(fieldStart, buf);
+        assertEquals("DEBUG - ", buf.toString());
+    }
+
+    /**
+     * Check that getDefaultAbbreviator does not return null.
+     *
+     */
+    public void testGetDefault() {
+        NameAbbreviator abbrev = NameAbbreviator.getDefaultAbbreviator();
+        assertNotNull(abbrev);
+    }
+
+    /**
+     * Check that getAbbreviator("-1").abbreviate() drops first name element.
+     *
+     */
+    public void testMinusOne() {
+        NameAbbreviator abbrev = NameAbbreviator.getAbbreviator("-1");
+        StringBuffer buf = new StringBuffer("DEBUG - ");
+        int fieldStart = buf.length();
+        buf.append("org.example.foo.bar");
+        abbrev.abbreviate(fieldStart, buf);
+        assertEquals("DEBUG - example.foo.bar", buf.toString());
+
+        buf.setLength(0);
+        buf.append("DEBUG - ");
+        fieldStart = buf.length();
+        buf.append("bar");
+        abbrev.abbreviate(fieldStart, buf);
+        assertEquals("DEBUG - bar", buf.toString());
+
+        buf.setLength(0);
+        buf.append("DEBUG - ");
+        fieldStart = buf.length();
+        abbrev.abbreviate(fieldStart, buf);
+        assertEquals("DEBUG - ", buf.toString());
+
+        buf.setLength(0);
+        buf.append("DEBUG - ");
+        fieldStart = buf.length();
+        buf.append(".");
+        abbrev.abbreviate(fieldStart, buf);
+        assertEquals("DEBUG - ", buf.toString());
+
+    }
+
+    /**
+     * Check that getAbbreviator("1.*.2").abbreviate drops all but the first character from the first element, uses all of
+     * the second element and drops all but the first two characters of the rest of the non-final elements.
+     *
+     */
+    public void testMulti() {
+        NameAbbreviator abbrev = NameAbbreviator.getAbbreviator("1.*.2");
+        StringBuffer buf = new StringBuffer("DEBUG - ");
+        int fieldStart = buf.length();
+        buf.append("org.example.foo.bar");
+        abbrev.abbreviate(fieldStart, buf);
+        assertEquals("DEBUG - o.example.fo.bar", buf.toString());
+
+        buf.setLength(0);
+        buf.append("DEBUG - ");
+        fieldStart = buf.length();
+        buf.append("org.example.foo.");
+        abbrev.abbreviate(fieldStart, buf);
+        assertEquals("DEBUG - o.example.fo.", buf.toString());
+
+        buf.setLength(0);
+        buf.append("DEBUG - ");
+        fieldStart = buf.length();
+        buf.append("foo.bar");
+        abbrev.abbreviate(fieldStart, buf);
+        assertEquals("DEBUG - f.bar", buf.toString());
+
+        buf.setLength(0);
+        buf.append("DEBUG - ");
+        fieldStart = buf.length();
+        buf.append("bar");
+        abbrev.abbreviate(fieldStart, buf);
+        assertEquals("DEBUG - bar", buf.toString());
+
+        buf.setLength(0);
+        buf.append("DEBUG - ");
+        fieldStart = buf.length();
+        abbrev.abbreviate(fieldStart, buf);
+        assertEquals("DEBUG - ", buf.toString());
+
+        buf.setLength(0);
+        buf.append("DEBUG - ");
+        fieldStart = buf.length();
+        buf.append(".");
+        abbrev.abbreviate(fieldStart, buf);
+        assertEquals("DEBUG - .", buf.toString());
+    }
+
+    /**
+     * Check that getAbbreviator("1").abbreviate() drops all but the final name element.
+     *
+     */
+    public void testOne() {
+        NameAbbreviator abbrev = NameAbbreviator.getAbbreviator("1");
+        StringBuffer buf = new StringBuffer("DEBUG - ");
+        int fieldStart = buf.length();
+        buf.append("org.example.foo.bar");
+        abbrev.abbreviate(fieldStart, buf);
+        assertEquals("DEBUG - bar", buf.toString());
+
+        buf.setLength(0);
+        buf.append("DEBUG - ");
+        fieldStart = buf.length();
+        buf.append("bar");
+        abbrev.abbreviate(fieldStart, buf);
+        assertEquals("DEBUG - bar", buf.toString());
+
+        buf.setLength(0);
+        buf.append("DEBUG - ");
+        fieldStart = buf.length();
+        abbrev.abbreviate(fieldStart, buf);
+        assertEquals("DEBUG - ", buf.toString());
+    }
+
+    /**
+     * Check that getAbbreviator("1.").abbreviate abbreviates non-final elements to one character.
+     *
+     */
+    public void testOneDot() {
+        NameAbbreviator abbrev = NameAbbreviator.getAbbreviator("1.");
+        StringBuffer buf = new StringBuffer("DEBUG - ");
+        int fieldStart = buf.length();
+        buf.append("org.example.foo.bar");
+        abbrev.abbreviate(fieldStart, buf);
+        assertEquals("DEBUG - o.e.f.bar", buf.toString());
+
+        buf.setLength(0);
+        buf.append("DEBUG - ");
+        fieldStart = buf.length();
+        buf.append("org.example.foo.");
+        abbrev.abbreviate(fieldStart, buf);
+        assertEquals("DEBUG - o.e.f.", buf.toString());
+
+        buf.setLength(0);
+        buf.append("DEBUG - ");
+        fieldStart = buf.length();
+        buf.append("foo.bar");
+        abbrev.abbreviate(fieldStart, buf);
+        assertEquals("DEBUG - f.bar", buf.toString());
+
+        buf.setLength(0);
+        buf.append("DEBUG - ");
+        fieldStart = buf.length();
+        buf.append("bar");
+        abbrev.abbreviate(fieldStart, buf);
+        assertEquals("DEBUG - bar", buf.toString());
+
+        buf.setLength(0);
+        buf.append("DEBUG - ");
+        fieldStart = buf.length();
+        abbrev.abbreviate(fieldStart, buf);
+        assertEquals("DEBUG - ", buf.toString());
+
+        buf.setLength(0);
+        buf.append("DEBUG - ");
+        fieldStart = buf.length();
+        buf.append(".");
+        abbrev.abbreviate(fieldStart, buf);
+        assertEquals("DEBUG - .", buf.toString());
+    }
+
+    /**
+     * Check that getAbbreviator("1~.").abbreviate abbreviates non-final elements to one character and a tilde.
+     *
+     */
+    public void testOneTildeDot() {
+        NameAbbreviator abbrev = NameAbbreviator.getAbbreviator("1~.");
+        StringBuffer buf = new StringBuffer("DEBUG - ");
+        int fieldStart = buf.length();
+        buf.append("org.example.foo.bar");
+        abbrev.abbreviate(fieldStart, buf);
+        assertEquals("DEBUG - o~.e~.f~.bar", buf.toString());
+
+        buf.setLength(0);
+        buf.append("DEBUG - ");
+        fieldStart = buf.length();
+        buf.append("org.example.foo.");
+        abbrev.abbreviate(fieldStart, buf);
+        assertEquals("DEBUG - o~.e~.f~.", buf.toString());
+
+        buf.setLength(0);
+        buf.append("DEBUG - ");
+        fieldStart = buf.length();
+        buf.append("foo.bar");
+        abbrev.abbreviate(fieldStart, buf);
+        assertEquals("DEBUG - f~.bar", buf.toString());
+
+        buf.setLength(0);
+        buf.append("DEBUG - ");
+        fieldStart = buf.length();
+        buf.append("bar");
+        abbrev.abbreviate(fieldStart, buf);
+        assertEquals("DEBUG - bar", buf.toString());
+
+        buf.setLength(0);
+        buf.append("DEBUG - ");
+        fieldStart = buf.length();
+        abbrev.abbreviate(fieldStart, buf);
+        assertEquals("DEBUG - ", buf.toString());
+
+        buf.setLength(0);
+        buf.append("DEBUG - ");
+        fieldStart = buf.length();
+        buf.append(".");
+        abbrev.abbreviate(fieldStart, buf);
+        assertEquals("DEBUG - .", buf.toString());
+
+        buf.setLength(0);
+        buf.append("DEBUG - ");
+        fieldStart = buf.length();
+        buf.append("o.e.f.bar");
+        abbrev.abbreviate(fieldStart, buf);
+        assertEquals("DEBUG - o.e.f.bar", buf.toString());
+    }
+
+    /**
+     * Check that getAbbreviator("2").abbreviate drops all but the last two elements.
+     *
+     */
+    public void testTwo() {
+        NameAbbreviator abbrev = NameAbbreviator.getAbbreviator("2");
+        StringBuffer buf = new StringBuffer("DEBUG - ");
+        int fieldStart = buf.length();
+        buf.append("org.example.foo.bar");
+        abbrev.abbreviate(fieldStart, buf);
+        assertEquals("DEBUG - foo.bar", buf.toString());
+
+        buf.setLength(0);
+        buf.append("DEBUG - ");
+        fieldStart = buf.length();
+        buf.append("foo.bar");
+        abbrev.abbreviate(fieldStart, buf);
+        assertEquals("DEBUG - foo.bar", buf.toString());
+
+        buf.setLength(0);
+        buf.append("DEBUG - ");
+        fieldStart = buf.length();
+        buf.append("bar");
+        abbrev.abbreviate(fieldStart, buf);
+        assertEquals("DEBUG - bar", buf.toString());
+    }
+
+    /**
+     * Check that "0" drops all name content.
+     *
+     */
+    public void testZero() {
+        NameAbbreviator abbrev = NameAbbreviator.getAbbreviator("0");
+        StringBuffer buf = new StringBuffer("DEBUG - ");
+        int fieldStart = buf.length();
+        buf.append("org.example.foo.bar");
+        abbrev.abbreviate(fieldStart, buf);
+        assertEquals("DEBUG - ", buf.toString());
+    }
+
+}
diff --git a/src/changes/changes.xml b/src/changes/changes.xml
index 0226b41..61b691b 100644
--- a/src/changes/changes.xml
+++ b/src/changes/changes.xml
@@ -334,6 +334,15 @@
       <action dev="ggregory" type="fix" due-to="Gary Gregory">
         Log4j 1.2 bridge missing some LocationInfo constructors.
       </action>
+      <action dev="ggregory" type="fix" due-to="Gary Gregory">
+        Log4j 1.2 bridge missed 
+      </action>
+      <action dev="ggregory" type="fix" due-to="Gary Gregory">
+        Log4j 1.2 bridge missed org.apache.log4j.pattern.FormattingInfo.
+      </action>
+      <action dev="ggregory" type="fix" due-to="Gary Gregory">
+        Log4j 1.2 bridge missed org.apache.log4j.pattern.NameAbbreviator.
+      </action>
       <action dev="ggregory" type="fix">
         JndiManager reverts to 2.17.0 behavior: Read the system property for each call.
       </action>

[logging-log4j2] 19/25: Assert XML layout presence.

Posted by pk...@apache.org.
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 68afef68bba9a467ed1d1f4b3885ae9cf4fd5534
Author: Gary Gregory <ga...@gmail.com>
AuthorDate: Sun Feb 6 07:57:45 2022 -0500

    Assert XML layout presence.
---
 .../log4j/config/SocketAppenderConfigurationTest.java   | 17 ++++++++++-------
 1 file changed, 10 insertions(+), 7 deletions(-)

diff --git a/log4j-1.2-api/src/test/java/org/apache/log4j/config/SocketAppenderConfigurationTest.java b/log4j-1.2-api/src/test/java/org/apache/log4j/config/SocketAppenderConfigurationTest.java
index 76dee05..9434168 100644
--- a/log4j-1.2-api/src/test/java/org/apache/log4j/config/SocketAppenderConfigurationTest.java
+++ b/log4j-1.2-api/src/test/java/org/apache/log4j/config/SocketAppenderConfigurationTest.java
@@ -23,6 +23,7 @@ import static org.junit.jupiter.api.Assertions.assertTrue;
 import java.io.IOException;
 import java.util.Map;
 
+import org.apache.log4j.layout.Log4j1XmlLayout;
 import org.apache.logging.log4j.Level;
 import org.apache.logging.log4j.core.Appender;
 import org.apache.logging.log4j.core.appender.SocketAppender;
@@ -37,19 +38,19 @@ import org.junit.Test;
  */
 public class SocketAppenderConfigurationTest {
 
-    private void check(final Protocol expected, final Configuration configuration) {
+    private SocketAppender check(final Protocol expected, final Configuration configuration) {
         final Map<String, Appender> appenders = configuration.getAppenders();
         assertNotNull(appenders);
         final String appenderName = "socket";
         final Appender appender = appenders.get(appenderName);
         assertNotNull(appender, "Missing appender " + appenderName);
-        final SocketAppender syslogAppender = (SocketAppender) appender;
+        final SocketAppender socketAppender = (SocketAppender) appender;
         @SuppressWarnings("resource")
-        final TcpSocketManager manager = (TcpSocketManager) syslogAppender.getManager();
+        final TcpSocketManager manager = (TcpSocketManager) socketAppender.getManager();
         final String prefix = expected + ":";
         assertTrue(manager.getName().startsWith(prefix), () -> String.format("'%s' does not start with '%s'", manager.getName(), prefix));
         // Threshold
-        final ThresholdFilter filter = (ThresholdFilter) syslogAppender.getFilter();
+        final ThresholdFilter filter = (ThresholdFilter) socketAppender.getFilter();
         assertEquals(Level.DEBUG, filter.getLevel());
         // Host
         assertEquals("localhost", manager.getHost());
@@ -57,14 +58,15 @@ public class SocketAppenderConfigurationTest {
         assertEquals(9999, manager.getPort());
         // Port
         assertEquals(100, manager.getReconnectionDelayMillis());
+        return socketAppender;
     }
 
     private void checkProtocolPropertiesConfig(final Protocol expected, final String xmlPath) throws IOException {
         check(expected, TestConfigurator.configure(xmlPath).getConfiguration());
     }
 
-    private void checkProtocolXmlConfig(final Protocol expected, final String xmlPath) throws IOException {
-        check(expected, TestConfigurator.configure(xmlPath).getConfiguration());
+    private SocketAppender checkProtocolXmlConfig(final Protocol expected, final String xmlPath) throws IOException {
+        return check(expected, TestConfigurator.configure(xmlPath).getConfiguration());
     }
 
     @Test
@@ -74,7 +76,8 @@ public class SocketAppenderConfigurationTest {
 
     @Test
     public void testPropertiesXmlLayout() throws Exception {
-        checkProtocolXmlConfig(Protocol.TCP, "target/test-classes/log4j1-socket-xml-layout.properties");
+        final SocketAppender socketAppender = checkProtocolXmlConfig(Protocol.TCP, "target/test-classes/log4j1-socket-xml-layout.properties");
+        assertTrue(socketAppender.getLayout() instanceof Log4j1XmlLayout);
     }
 
     @Test

[logging-log4j2] 21/25: Add missing tests from Log4j 1.2.17.

Posted by pk...@apache.org.
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 c5d1c6c3a639a521c203bee218cc74afd7124043
Author: Gary Gregory <ga...@gmail.com>
AuthorDate: Sun Feb 6 08:49:47 2022 -0500

    Add missing tests from Log4j 1.2.17.
---
 .../apache/log4j/helpers/BoundedFIFOTestCase.java  | 233 +++++++++++++++++++++
 .../apache/log4j/helpers/CyclicBufferTestCase.java | 159 ++++++++++++++
 .../java/org/apache/log4j/helpers/LogLogTest.java  |  52 +++++
 3 files changed, 444 insertions(+)

diff --git a/log4j-1.2-api/src/test/java/org/apache/log4j/helpers/BoundedFIFOTestCase.java b/log4j-1.2-api/src/test/java/org/apache/log4j/helpers/BoundedFIFOTestCase.java
new file mode 100644
index 0000000..c5ff717
--- /dev/null
+++ b/log4j-1.2-api/src/test/java/org/apache/log4j/helpers/BoundedFIFOTestCase.java
@@ -0,0 +1,233 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.log4j.helpers;
+
+import org.apache.log4j.Level;
+import org.apache.log4j.Logger;
+import org.apache.log4j.spi.LoggingEvent;
+
+import junit.framework.Test;
+import junit.framework.TestCase;
+import junit.framework.TestSuite;
+
+/**
+ * Test {@link BoundedFIFO}.
+ *
+ * @since 0.9.1
+ */
+public class BoundedFIFOTestCase extends TestCase {
+    static Logger cat = Logger.getLogger("x");
+
+    static int MAX = 1000;
+
+    static LoggingEvent[] e = new LoggingEvent[MAX];
+
+    public static Test suite() {
+        final TestSuite suite = new TestSuite();
+        suite.addTest(new BoundedFIFOTestCase("test1"));
+        suite.addTest(new BoundedFIFOTestCase("test2"));
+        suite.addTest(new BoundedFIFOTestCase("testResize1"));
+        suite.addTest(new BoundedFIFOTestCase("testResize2"));
+        suite.addTest(new BoundedFIFOTestCase("testResize3"));
+        return suite;
+    }
+
+    {
+        for (int i = 0; i < MAX; i++) {
+            e[i] = new LoggingEvent("", cat, Level.DEBUG, "e" + i, null);
+        }
+    }
+
+    public BoundedFIFOTestCase(final String name) {
+        super(name);
+    }
+
+    int min(final int a, final int b) {
+        return a < b ? a : b;
+    }
+
+    @Override
+    public void setUp() {
+
+    }
+
+    /**
+     * Pattern: +++++..-----..
+     */
+    public void test1() {
+        for (int size = 1; size <= 128; size *= 2) {
+            final BoundedFIFO bf = new BoundedFIFO(size);
+
+            assertEquals(bf.getMaxSize(), size);
+            assertNull(bf.get());
+
+            int i;
+            int j;
+            int k;
+
+            for (i = 1; i < 2 * size; i++) {
+                for (j = 0; j < i; j++) {
+                    // System.out.println("Putting "+e[j]);
+                    bf.put(e[j]);
+                    assertEquals(bf.length(), j < size ? j + 1 : size);
+                }
+                final int max = size < j ? size : j;
+                j--;
+                for (k = 0; k <= j; k++) {
+                    // System.out.println("max="+max+", j="+j+", k="+k);
+                    assertEquals(bf.length(), max - k > 0 ? max - k : 0);
+                    final Object r = bf.get();
+                    // System.out.println("Got "+r);
+                    if (k >= size) {
+                        assertNull(r);
+                    } else {
+                        assertEquals(r, e[k]);
+                    }
+                }
+            }
+            // System.out.println("Passed size="+size);
+        }
+    }
+
+    /**
+     * Pattern: ++++--++--++
+     */
+    public void test2() {
+        final int size = 3;
+        final BoundedFIFO bf = new BoundedFIFO(size);
+
+        bf.put(e[0]);
+        assertEquals(bf.get(), e[0]);
+        assertNull(bf.get());
+
+        bf.put(e[1]);
+        assertEquals(bf.length(), 1);
+        bf.put(e[2]);
+        assertEquals(bf.length(), 2);
+        bf.put(e[3]);
+        assertEquals(bf.length(), 3);
+        assertEquals(bf.get(), e[1]);
+        assertEquals(bf.length(), 2);
+        assertEquals(bf.get(), e[2]);
+        assertEquals(bf.length(), 1);
+        assertEquals(bf.get(), e[3]);
+        assertEquals(bf.length(), 0);
+        assertNull(bf.get());
+        assertEquals(bf.length(), 0);
+    }
+
+    /**
+     * Pattern ++++++++++++++++++++ (insert only);
+     */
+    public void testResize1() {
+        final int size = 10;
+
+        for (int n = 1; n < size * 2; n++) {
+            for (int i = 0; i < size * 2; i++) {
+
+                final BoundedFIFO bf = new BoundedFIFO(size);
+                for (int f = 0; f < i; f++) {
+                    bf.put(e[f]);
+                }
+
+                bf.resize(n);
+                final int expectedSize = min(n, min(i, size));
+                assertEquals(bf.length(), expectedSize);
+                for (int c = 0; c < expectedSize; c++) {
+                    assertEquals(bf.get(), e[c]);
+                }
+            }
+        }
+    }
+
+    /**
+     * Pattern ++...+ --...-
+     */
+    public void testResize2() {
+        final int size = 10;
+
+        for (int n = 1; n < size * 2; n++) {
+            for (int i = 0; i < size * 2; i++) {
+                for (int d = 0; d < min(i, size); d++) {
+
+                    final BoundedFIFO bf = new BoundedFIFO(size);
+                    for (int p = 0; p < i; p++) {
+                        bf.put(e[p]);
+                    }
+
+                    for (int g = 0; g < d; g++) {
+                        bf.get();
+                    }
+
+                    // x = the number of elems in
+                    final int x = bf.length();
+
+                    bf.resize(n);
+
+                    final int expectedSize = min(n, x);
+                    assertEquals(bf.length(), expectedSize);
+
+                    for (int c = 0; c < expectedSize; c++) {
+                        assertEquals(bf.get(), e[c + d]);
+                    }
+                    assertNull(bf.get());
+                }
+            }
+        }
+    }
+
+    /**
+     * Pattern: i inserts, d deletes, r inserts
+     */
+    public void testResize3() {
+        final int size = 10;
+
+        for (int n = 1; n < size * 2; n++) {
+            for (int i = 0; i < size; i++) {
+                for (int d = 0; d < i; d++) {
+                    for (int r = 0; r < d; r++) {
+
+                        final BoundedFIFO bf = new BoundedFIFO(size);
+                        for (int p0 = 0; p0 < i; p0++) {
+                            bf.put(e[p0]);
+                        }
+
+                        for (int g = 0; g < d; g++) {
+                            bf.get();
+                        }
+                        for (int p1 = 0; p1 < r; p1++) {
+                            bf.put(e[i + p1]);
+                        }
+
+                        final int x = bf.length();
+
+                        bf.resize(n);
+
+                        final int expectedSize = min(n, x);
+                        assertEquals(bf.length(), expectedSize);
+
+                        for (int c = 0; c < expectedSize; c++) {
+                            assertEquals(bf.get(), e[c + d]);
+                        }
+                        // assertNull(bf.get());
+                    }
+                }
+            }
+        }
+    }
+}
diff --git a/log4j-1.2-api/src/test/java/org/apache/log4j/helpers/CyclicBufferTestCase.java b/log4j-1.2-api/src/test/java/org/apache/log4j/helpers/CyclicBufferTestCase.java
new file mode 100644
index 0000000..bb294d1
--- /dev/null
+++ b/log4j-1.2-api/src/test/java/org/apache/log4j/helpers/CyclicBufferTestCase.java
@@ -0,0 +1,159 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+//
+// Log4j uses the JUnit framework for internal unit testing. JUnit
+// available from
+//
+//     http://www.junit.org
+
+package org.apache.log4j.helpers;
+
+import org.apache.log4j.Level;
+import org.apache.log4j.Logger;
+import org.apache.log4j.spi.LoggingEvent;
+
+import junit.framework.Test;
+import junit.framework.TestCase;
+import junit.framework.TestSuite;
+
+/**
+ * Tests {@link CyclicBuffer}.
+ */
+public class CyclicBufferTestCase extends TestCase {
+
+    static Logger cat = Logger.getLogger("x");
+
+    static int MAX = 1000;
+
+    static LoggingEvent[] e = new LoggingEvent[MAX];
+
+    public static Test suite() {
+        final TestSuite suite = new TestSuite();
+        suite.addTest(new CyclicBufferTestCase("test0"));
+        suite.addTest(new CyclicBufferTestCase("test1"));
+        suite.addTest(new CyclicBufferTestCase("testResize"));
+        return suite;
+    }
+
+    {
+        for (int i = 0; i < MAX; i++) {
+            e[i] = new LoggingEvent("", cat, Level.DEBUG, "e" + i, null);
+        }
+    }
+
+    public CyclicBufferTestCase(final String name) {
+        super(name);
+    }
+
+    void doTest1(final int size) {
+        // System.out.println("Doing test with size = "+size);
+        final CyclicBuffer cb = new CyclicBuffer(size);
+
+        assertEquals(cb.getMaxSize(), size);
+
+        for (int i = -(size + 10); i < (size + 10); i++) {
+            assertNull(cb.get(i));
+        }
+
+        for (int i = 0; i < MAX; i++) {
+            cb.add(e[i]);
+            final int limit = i < size - 1 ? i : size - 1;
+
+            // System.out.println("\nLimit is " + limit + ", i="+i);
+
+            for (int j = limit; j >= 0; j--) {
+                // System.out.println("i= "+i+", j="+j);
+                assertEquals(cb.get(j), e[i - (limit - j)]);
+            }
+            assertNull(cb.get(-1));
+            assertNull(cb.get(limit + 1));
+        }
+    }
+
+    void doTestResize(final int initialSize, final int numberOfAdds, final int newSize) {
+        // System.out.println("initialSize = "+initialSize+", numberOfAdds="
+        // +numberOfAdds+", newSize="+newSize);
+        final CyclicBuffer cb = new CyclicBuffer(initialSize);
+        for (int i = 0; i < numberOfAdds; i++) {
+            cb.add(e[i]);
+        }
+        cb.resize(newSize);
+
+        int offset = numberOfAdds - initialSize;
+        if (offset < 0) {
+            offset = 0;
+        }
+
+        int len = newSize < numberOfAdds ? newSize : numberOfAdds;
+        len = len < initialSize ? len : initialSize;
+        // System.out.println("Len = "+len+", offset="+offset);
+        for (int j = 0; j < len; j++) {
+            assertEquals(cb.get(j), e[offset + j]);
+        }
+
+    }
+
+    @Override
+    public void setUp() {
+
+    }
+
+    public void test0() {
+        final int size = 2;
+
+        CyclicBuffer cb = new CyclicBuffer(size);
+        assertEquals(cb.getMaxSize(), size);
+
+        cb.add(e[0]);
+        assertEquals(cb.length(), 1);
+        assertEquals(cb.get(), e[0]);
+        assertEquals(cb.length(), 0);
+        assertNull(cb.get());
+        assertEquals(cb.length(), 0);
+
+        cb = new CyclicBuffer(size);
+        cb.add(e[0]);
+        cb.add(e[1]);
+        assertEquals(cb.length(), 2);
+        assertEquals(cb.get(), e[0]);
+        assertEquals(cb.length(), 1);
+        assertEquals(cb.get(), e[1]);
+        assertEquals(cb.length(), 0);
+        assertNull(cb.get());
+        assertEquals(cb.length(), 0);
+
+    }
+
+    /**
+     * Test a buffer of size 1,2,4,8,..,128
+     */
+    public void test1() {
+        for (int bufSize = 1; bufSize <= 128; bufSize *= 2) {
+            doTest1(bufSize);
+        }
+    }
+
+    public void testResize() {
+        for (int isize = 1; isize <= 128; isize *= 2) {
+            doTestResize(isize, isize / 2 + 1, isize / 2 + 1);
+            doTestResize(isize, isize / 2 + 1, isize + 10);
+            doTestResize(isize, isize + 10, isize / 2 + 1);
+            doTestResize(isize, isize + 10, isize + 10);
+        }
+    }
+}
diff --git a/log4j-1.2-api/src/test/java/org/apache/log4j/helpers/LogLogTest.java b/log4j-1.2-api/src/test/java/org/apache/log4j/helpers/LogLogTest.java
new file mode 100644
index 0000000..5341e1d
--- /dev/null
+++ b/log4j-1.2-api/src/test/java/org/apache/log4j/helpers/LogLogTest.java
@@ -0,0 +1,52 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.log4j.helpers;
+
+import junit.framework.TestCase;
+
+/**
+ * Tests {@link LogLog}.
+ */
+public class LogLogTest extends TestCase {
+
+    /**
+     * Create new instance of LogLogTest.
+     *
+     * @param testName test name
+     */
+    public LogLogTest(final String testName) {
+        super(testName);
+    }
+
+    /**
+     * Check value of CONFIG_DEBUG_KEY.
+     *
+     * @deprecated since constant is deprecated
+     */
+    @Deprecated
+    public void testConfigDebugKey() {
+        assertEquals("log4j.configDebug", LogLog.CONFIG_DEBUG_KEY);
+    }
+
+    /**
+     * Check value of DEBUG_KEY.
+     */
+    public void testDebugKey() {
+        assertEquals("log4j.debug", LogLog.DEBUG_KEY);
+    }
+}

[logging-log4j2] 18/25: Add test that show we can configure a Log4j 1 properties file with a socket appender using an XML layout.

Posted by pk...@apache.org.
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 06fe59a6273595f07a18c681bbacd775a82977df
Author: Gary Gregory <ga...@gmail.com>
AuthorDate: Sat Feb 5 18:17:42 2022 -0500

    Add test that show we can configure a Log4j 1 properties file with a
    socket appender using an XML layout.
    
    Conflicts:
    	log4j-1.2-api/src/main/java/org/apache/log4j/builders/layout/XmlLayoutBuilder.java
---
 .../log4j/builders/layout/XmlLayoutBuilder.java    | 76 ++++++++++++++++++++++
 .../config/SocketAppenderConfigurationTest.java    |  5 ++
 .../resources/log4j1-socket-xml-layout.properties  | 24 +++++++
 3 files changed, 105 insertions(+)

diff --git a/log4j-1.2-api/src/main/java/org/apache/log4j/builders/layout/XmlLayoutBuilder.java b/log4j-1.2-api/src/main/java/org/apache/log4j/builders/layout/XmlLayoutBuilder.java
new file mode 100644
index 0000000..6577a52
--- /dev/null
+++ b/log4j-1.2-api/src/main/java/org/apache/log4j/builders/layout/XmlLayoutBuilder.java
@@ -0,0 +1,76 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache license, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the license for the specific language governing permissions and
+ * limitations under the license.
+ */
+package org.apache.log4j.builders.layout;
+
+import static org.apache.log4j.builders.BuilderManager.CATEGORY;
+import static org.apache.log4j.xml.XmlConfiguration.PARAM_TAG;
+import static org.apache.log4j.xml.XmlConfiguration.forEachElement;
+
+import java.util.Properties;
+import java.util.concurrent.atomic.AtomicBoolean;
+
+import org.apache.log4j.Layout;
+import org.apache.log4j.bridge.LayoutWrapper;
+import org.apache.log4j.builders.AbstractBuilder;
+import org.apache.log4j.config.PropertiesConfiguration;
+import org.apache.log4j.layout.Log4j1XmlLayout;
+import org.apache.log4j.xml.XmlConfiguration;
+import org.apache.logging.log4j.plugins.Plugin;
+import org.w3c.dom.Element;
+
+/**
+ * Build an XML Layout
+ */
+@Plugin(name = "org.apache.log4j.xml.XMLLayout", category = CATEGORY)
+public class XmlLayoutBuilder extends AbstractBuilder implements LayoutBuilder {
+
+    private static final String LOCATION_INFO = "LocationInfo";
+    private static final String PROPERTIES = "Properties";
+
+    public XmlLayoutBuilder() {
+    }
+
+    public XmlLayoutBuilder(String prefix, Properties props) {
+        super(prefix, props);
+    }
+
+
+    @Override
+    public Layout parseLayout(Element layoutElement, XmlConfiguration config) {
+        final AtomicBoolean properties = new AtomicBoolean();
+        final AtomicBoolean locationInfo = new AtomicBoolean();
+        forEachElement(layoutElement.getElementsByTagName(PARAM_TAG), currentElement -> {
+            if (PROPERTIES.equalsIgnoreCase(currentElement.getAttribute("name"))) {
+                properties.set(Boolean.parseBoolean(currentElement.getAttribute("value")));
+            } else if (LOCATION_INFO.equalsIgnoreCase(currentElement.getAttribute("name"))) {
+                locationInfo.set(Boolean.parseBoolean(currentElement.getAttribute("value")));
+            }
+        });
+        return createLayout(properties.get(), locationInfo.get());
+    }
+
+    @Override
+    public Layout parseLayout(PropertiesConfiguration config) {
+        boolean properties = getBooleanProperty(PROPERTIES);
+        boolean locationInfo = getBooleanProperty(LOCATION_INFO);
+        return createLayout(properties, locationInfo);
+    }
+
+    private Layout createLayout(boolean properties, boolean locationInfo) {
+        return new LayoutWrapper(Log4j1XmlLayout.createLayout(locationInfo, properties));
+    }
+}
diff --git a/log4j-1.2-api/src/test/java/org/apache/log4j/config/SocketAppenderConfigurationTest.java b/log4j-1.2-api/src/test/java/org/apache/log4j/config/SocketAppenderConfigurationTest.java
index 5fbaf30..76dee05 100644
--- a/log4j-1.2-api/src/test/java/org/apache/log4j/config/SocketAppenderConfigurationTest.java
+++ b/log4j-1.2-api/src/test/java/org/apache/log4j/config/SocketAppenderConfigurationTest.java
@@ -73,6 +73,11 @@ public class SocketAppenderConfigurationTest {
     }
 
     @Test
+    public void testPropertiesXmlLayout() throws Exception {
+        checkProtocolXmlConfig(Protocol.TCP, "target/test-classes/log4j1-socket-xml-layout.properties");
+    }
+
+    @Test
     public void testXml() throws Exception {
         checkProtocolXmlConfig(Protocol.TCP, "target/test-classes/log4j1-socket.xml");
     }
diff --git a/log4j-1.2-api/src/test/resources/log4j1-socket-xml-layout.properties b/log4j-1.2-api/src/test/resources/log4j1-socket-xml-layout.properties
new file mode 100644
index 0000000..35ceb97
--- /dev/null
+++ b/log4j-1.2-api/src/test/resources/log4j1-socket-xml-layout.properties
@@ -0,0 +1,24 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one or more
+# contributor license agreements.  See the NOTICE file distributed with
+# this work for additional information regarding copyright ownership.
+# The ASF licenses this file to You under the Apache License, Version 2.0
+# (the "License"); you may not use this file except in compliance with
+# the License.  You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+log4j.rootLogger=DEBUG,socket
+log4j.appender.socket=org.apache.log4j.net.SocketAppender
+log4j.appender.socket.remoteHost=localhost
+log4j.appender.socket.port=9999
+log4j.appender.socket.reconnectionDelay=100
+log4j.appender.socket.layout=org.apache.log4j.xml.XMLLayout
+log4j.appender.socket.Threshold=DEBUG

[logging-log4j2] 23/25: Issues with multiple Log4j 1.x filters (#753)

Posted by pk...@apache.org.
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 3ff6b86ab225365321df7d2a6daae4fd32ac8e62
Author: ppkarwasz <pi...@karwasz.org>
AuthorDate: Sat Feb 19 17:05:20 2022 +0100

    Issues with multiple Log4j 1.x filters (#753)
    
    * Fix issues with multiple Log4j 1.x filters
    
    This PR fixes a couple of issues concerning filters in the Log4j 1.x
    bridge:
    
     - there is an endless loop in `FilterAdapter#filter` that hangs the
    program if more than one filter is configured,
     - filters are executed multiple times per log message.
    
    The second problem comes from chains of Log4j 1.x filters: a filter
    chain is split into a `CompositeFilter` of `FilterAdapter`s (one for
    each filter). Each `FilterAdapter` executes its own filter and all those
    that come later in the chain.
    
    In order to preserve the behavior of `FilterAdapter` the following
    restrictions have been applied to the generated filters:
    
     1. no chains of Log4j 1.x filters are generated by the configuration; a
    list of Log4j 1.x filters is represented as a wrapped `CompositeFilter`
    of `FilterAdapter`s,
     2. the factories don't generate any `FilterWrapper`s of
    `FilterAdapter`s nor `FilterAdapter`s of `FilterWrapper`s.
    
    * Import and whitespace fixes
    
    * More unused import fixes
    
    * Adds regression test for CompositeFilter
    
    Conflicts:
    	log4j-1.2-api/src/main/java/org/apache/log4j/bridge/AppenderAdapter.java
    	log4j-1.2-api/src/main/java/org/apache/log4j/builders/AbstractBuilder.java
    	log4j-1.2-api/src/main/java/org/apache/log4j/builders/appender/AsyncAppenderBuilder.java
    	log4j-1.2-api/src/main/java/org/apache/log4j/builders/appender/ConsoleAppenderBuilder.java
    	log4j-1.2-api/src/main/java/org/apache/log4j/builders/appender/SocketAppenderBuilder.java
    	log4j-1.2-api/src/main/java/org/apache/log4j/config/PropertiesConfiguration.java
    	log4j-1.2-api/src/main/java/org/apache/log4j/xml/XmlConfiguration.java
    	log4j-1.2-api/src/test/java/org/apache/log4j/config/AbstractLog4j1ConfigurationTest.java
---
 .../java/org/apache/log4j/AppenderSkeleton.java    |   5 +-
 .../org/apache/log4j/PropertyConfigurator.java     |   5 +-
 .../org/apache/log4j/bridge/AppenderAdapter.java   |  16 +--
 .../org/apache/log4j/bridge/AppenderWrapper.java   |   6 +-
 .../org/apache/log4j/bridge/FilterAdapter.java     |  43 +++++++-
 .../org/apache/log4j/builders/AbstractBuilder.java |  38 ++-----
 .../builders/appender/AsyncAppenderBuilder.java    |  39 +++----
 .../builders/appender/ConsoleAppenderBuilder.java  |  17 +--
 .../appender/DailyRollingFileAppenderBuilder.java  |   2 +-
 .../builders/appender/FileAppenderBuilder.java     |   2 +-
 .../builders/appender/RewriteAppenderBuilder.java  |   2 +-
 .../appender/RollingFileAppenderBuilder.java       |   2 +-
 .../builders/appender/SocketAppenderBuilder.java   |  16 +--
 .../builders/appender/SyslogAppenderBuilder.java   |   2 +-
 .../log4j/config/PropertiesConfiguration.java      |  26 +----
 .../src/main/java/org/apache/log4j/spi/Filter.java |  12 --
 .../org/apache/log4j/xml/XmlConfiguration.java     |  48 ++++++--
 .../log4j/builders/Log4j2ListAppenderBuilder.java  |  98 +++++++++++++++++
 .../config/AbstractLog4j1ConfigurationTest.java    | 122 +++++++++++++++++++++
 .../log4j/config/PropertiesConfigurationTest.java  |  12 +-
 .../org/apache/log4j/config/StartsWithFilter.java  |  38 +++++++
 .../apache/log4j/config/XmlConfigurationTest.java  |   6 +
 .../resources/log4j-multipleFilters.properties     |  69 ++++++++++++
 .../src/test/resources/log4j-multipleFilters.xml   |  92 ++++++++++++++++
 .../log4j/core/filter/CompositeFilterTest.java     |  49 +++++++++
 .../logging/log4j/core/filter/CompositeFilter.java |   4 +-
 26 files changed, 611 insertions(+), 160 deletions(-)

diff --git a/log4j-1.2-api/src/main/java/org/apache/log4j/AppenderSkeleton.java b/log4j-1.2-api/src/main/java/org/apache/log4j/AppenderSkeleton.java
index 1353dae..3660a19 100644
--- a/log4j-1.2-api/src/main/java/org/apache/log4j/AppenderSkeleton.java
+++ b/log4j-1.2-api/src/main/java/org/apache/log4j/AppenderSkeleton.java
@@ -108,12 +108,9 @@ public abstract class AppenderSkeleton implements Appender, OptionHandler {
         return ((threshold == null) || priority.isGreaterOrEqual(threshold));
     }
 
-    /**
-     * This method is never going to be called in Log4j 2 so there isn't much point in having any code in it.
-     * @param event The LoggingEvent.
-     */
     @Override
     public void doAppend(final LoggingEvent event) {
+        // Threshold checks and filtering is performed by the AppenderWrapper.
         append(event);
     }
 
diff --git a/log4j-1.2-api/src/main/java/org/apache/log4j/PropertyConfigurator.java b/log4j-1.2-api/src/main/java/org/apache/log4j/PropertyConfigurator.java
index d522d57..483096e 100644
--- a/log4j-1.2-api/src/main/java/org/apache/log4j/PropertyConfigurator.java
+++ b/log4j-1.2-api/src/main/java/org/apache/log4j/PropertyConfigurator.java
@@ -30,6 +30,7 @@ import java.util.Properties;
 import java.util.StringTokenizer;
 import java.util.Vector;
 
+import org.apache.log4j.bridge.FilterAdapter;
 import org.apache.log4j.config.PropertiesConfiguration;
 import org.apache.log4j.config.PropertySetter;
 import org.apache.log4j.helpers.FileWatchdog;
@@ -520,6 +521,7 @@ public class PropertyConfigurator implements Configurator {
         // sort filters by IDs, insantiate filters, set filter options,
         // add filters to the appender
         final Enumeration g = new SortedKeyEnumeration(filters);
+        Filter head = null;
         while (g.hasMoreElements()) {
             final String key = (String) g.nextElement();
             final String clazz = properties.getProperty(key);
@@ -536,12 +538,13 @@ public class PropertyConfigurator implements Configurator {
                     }
                     propSetter.activate();
                     LogLog.debug("Adding filter of type [" + filter.getClass() + "] to appender named [" + appender.getName() + "].");
-                    appender.addFilter(filter);
+                    head = FilterAdapter.addFilter(head, filter);
                 }
             } else {
                 LogLog.warn("Missing class definition for filter: [" + key + "]");
             }
         }
+        appender.addFilter(head);
     }
 
     /**
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 1ae0369..3334535 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
@@ -26,7 +26,6 @@ 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.core.filter.CompositeFilter;
 
 /**
  * Binds a Log4j 1.x Appender to Log4j 2.
@@ -42,20 +41,7 @@ public class AppenderAdapter {
      */
     public AppenderAdapter(Appender appender) {
         this.appender = appender;
-        org.apache.logging.log4j.core.Filter appenderFilter = null;
-        if (appender.getFilter() != null) {
-            if (appender.getFilter().getNext() != null) {
-                org.apache.log4j.spi.Filter filter = appender.getFilter();
-                List<org.apache.logging.log4j.core.Filter> filters = new ArrayList<>();
-                while (filter != null) {
-                    filters.add(new FilterAdapter(filter));
-                    filter = filter.getNext();
-                }
-                appenderFilter = CompositeFilter.createFilters(filters.toArray(new Filter[0]));
-            } else {
-                appenderFilter = new FilterAdapter(appender.getFilter());
-            }
-        }
+        final org.apache.logging.log4j.core.Filter appenderFilter = FilterAdapter.convertFilter(appender.getFilter());
         this.adapter = new Adapter(appender.getName(), appenderFilter, null, true, null);
     }
 
diff --git a/log4j-1.2-api/src/main/java/org/apache/log4j/bridge/AppenderWrapper.java b/log4j-1.2-api/src/main/java/org/apache/log4j/bridge/AppenderWrapper.java
index d26c878..3d28fd7 100644
--- a/log4j-1.2-api/src/main/java/org/apache/log4j/bridge/AppenderWrapper.java
+++ b/log4j-1.2-api/src/main/java/org/apache/log4j/bridge/AppenderWrapper.java
@@ -55,11 +55,7 @@ public class AppenderWrapper implements Appender {
     @Override
     public void addFilter(Filter newFilter) {
         if (appender instanceof AbstractFilterable) {
-            if (newFilter instanceof FilterWrapper) {
-                ((AbstractFilterable) appender).addFilter(((FilterWrapper) newFilter).getFilter());
-            } else {
-                ((AbstractFilterable) appender).addFilter(new FilterAdapter(newFilter));
-            }
+            ((AbstractFilterable) appender).addFilter(FilterAdapter.convertFilter(newFilter));
         } else {
             LOGGER.warn("Unable to add filter to appender {}, it does not support filters", appender.getName());
         }
diff --git a/log4j-1.2-api/src/main/java/org/apache/log4j/bridge/FilterAdapter.java b/log4j-1.2-api/src/main/java/org/apache/log4j/bridge/FilterAdapter.java
index 4851125..5ce599e 100644
--- a/log4j-1.2-api/src/main/java/org/apache/log4j/bridge/FilterAdapter.java
+++ b/log4j-1.2-api/src/main/java/org/apache/log4j/bridge/FilterAdapter.java
@@ -20,6 +20,7 @@ import org.apache.log4j.spi.Filter;
 import org.apache.log4j.spi.LoggingEvent;
 import org.apache.logging.log4j.core.LogEvent;
 import org.apache.logging.log4j.core.filter.AbstractFilter;
+import org.apache.logging.log4j.core.filter.CompositeFilter;
 
 /**
  * Binds a Log4j 1.x Filter with Log4j 2.
@@ -28,6 +29,44 @@ public class FilterAdapter extends AbstractFilter {
 
     private final Filter filter;
 
+    /**
+     * Converts a Log4j 1.x filter into a Log4j 2.x filter.
+     * 
+     * @param filter
+     *            a Log4j 1.x filter
+     * @return a Log4j 2.x filter
+     */
+    public static org.apache.logging.log4j.core.Filter convertFilter(Filter filter) {
+        if (filter instanceof FilterWrapper && filter.getNext() == null) {
+            return ((FilterWrapper) filter).getFilter();
+        } else if (filter != null) {
+            return new FilterAdapter(filter);
+        }
+        return null;
+    }
+
+    /**
+     * Appends one filter to another using Log4j 2.x concatenation utilities.
+     * @param first
+     * @param second
+     * @return
+     */
+    public static Filter addFilter(Filter first, Filter second) {
+        if (first == null) {
+            return second;
+        }
+        if (second == null) {
+            return first;
+        }
+        final CompositeFilter composite;
+        if (first instanceof FilterWrapper && ((FilterWrapper) first).getFilter() instanceof CompositeFilter) {
+            composite = (CompositeFilter) ((FilterWrapper) first).getFilter();
+        } else {
+            composite = CompositeFilter.createFilters(new org.apache.logging.log4j.core.Filter[] {convertFilter(first)});
+        }
+        return new FilterWrapper(composite.addFilter(convertFilter(second)));
+    }
+
     public FilterAdapter(Filter filter) {
         this.filter = filter;
     }
@@ -46,14 +85,14 @@ public class FilterAdapter extends AbstractFilter {
         LoggingEvent loggingEvent = new LogEventAdapter(event);
         Filter next = filter;
         while (next != null) {
-            switch (filter.decide(loggingEvent)) {
+            switch (next.decide(loggingEvent)) {
                 case Filter.ACCEPT:
                     return Result.ACCEPT;
                 case Filter.DENY:
                     return Result.DENY;
                 default:
             }
-            next = filter.getNext();
+            next = next.getNext();
         }
         return Result.NEUTRAL;
     }
diff --git a/log4j-1.2-api/src/main/java/org/apache/log4j/builders/AbstractBuilder.java b/log4j-1.2-api/src/main/java/org/apache/log4j/builders/AbstractBuilder.java
index 04b439c..b73fff4 100644
--- a/log4j-1.2-api/src/main/java/org/apache/log4j/builders/AbstractBuilder.java
+++ b/log4j-1.2-api/src/main/java/org/apache/log4j/builders/AbstractBuilder.java
@@ -19,14 +19,9 @@ package org.apache.log4j.builders;
 import static org.apache.log4j.xml.XmlConfiguration.NAME_ATTR;
 import static org.apache.log4j.xml.XmlConfiguration.VALUE_ATTR;
 
-import java.util.ArrayList;
 import java.util.HashMap;
-import java.util.List;
 import java.util.Map;
 import java.util.Properties;
-import java.util.concurrent.atomic.AtomicBoolean;
-import java.util.concurrent.atomic.AtomicInteger;
-import java.util.concurrent.atomic.AtomicReference;
 
 import org.apache.log4j.bridge.FilterAdapter;
 import org.apache.log4j.bridge.FilterWrapper;
@@ -34,7 +29,6 @@ import org.apache.log4j.helpers.OptionConverter;
 import org.apache.log4j.spi.Filter;
 import org.apache.logging.log4j.Level;
 import org.apache.logging.log4j.Logger;
-import org.apache.logging.log4j.core.filter.CompositeFilter;
 import org.apache.logging.log4j.core.filter.ThresholdFilter;
 import org.apache.logging.log4j.status.StatusLogger;
 import org.apache.logging.log4j.util.Strings;
@@ -74,33 +68,17 @@ public abstract class AbstractBuilder implements Builder {
         props.entrySet().forEach(e -> this.properties.put(toBeanKey(e.getKey().toString()), e.getValue()));
     }
 
-    protected org.apache.logging.log4j.core.Filter buildFilters(final String level, final Filter filter) {
-        if (level != null && filter != null) {
-            final List<org.apache.logging.log4j.core.Filter> filterList = new ArrayList<>();
+    protected static org.apache.logging.log4j.core.Filter buildFilters(final String level, final Filter filter) {
+        Filter head = null;
+        if (level != null) {
             final org.apache.logging.log4j.core.Filter thresholdFilter = ThresholdFilter.createFilter(OptionConverter.convertLevel(level, Level.TRACE),
                 org.apache.logging.log4j.core.Filter.Result.NEUTRAL, org.apache.logging.log4j.core.Filter.Result.DENY);
-            filterList.add(thresholdFilter);
-            Filter f = filter;
-            while (f != null) {
-                if (filter instanceof FilterWrapper) {
-                    filterList.add(((FilterWrapper) f).getFilter());
-                } else {
-                    filterList.add(new FilterAdapter(f));
-                }
-                f = f.getNext();
-            }
-            return CompositeFilter.createFilters(filterList.toArray(org.apache.logging.log4j.core.Filter.EMPTY_ARRAY));
-        } else if (level != null) {
-            return ThresholdFilter.createFilter(OptionConverter.convertLevel(level, Level.TRACE), org.apache.logging.log4j.core.Filter.Result.NEUTRAL,
-                org.apache.logging.log4j.core.Filter.Result.DENY);
-        } else if (filter != null) {
-            if (filter instanceof FilterWrapper) {
-                return ((FilterWrapper) filter).getFilter();
-            } else {
-                return new FilterAdapter(filter);
-            }
+            head = new FilterWrapper(thresholdFilter);
+        }
+        if (filter != null) {
+            head = FilterAdapter.addFilter(head, filter);
         }
-        return null;
+        return FilterAdapter.convertFilter(head);
     }
 
     private String capitalize(final String value) {
diff --git a/log4j-1.2-api/src/main/java/org/apache/log4j/builders/appender/AsyncAppenderBuilder.java b/log4j-1.2-api/src/main/java/org/apache/log4j/builders/appender/AsyncAppenderBuilder.java
index 4b8b17e..ca1aa80 100644
--- a/log4j-1.2-api/src/main/java/org/apache/log4j/builders/appender/AsyncAppenderBuilder.java
+++ b/log4j-1.2-api/src/main/java/org/apache/log4j/builders/appender/AsyncAppenderBuilder.java
@@ -19,45 +19,34 @@ package org.apache.log4j.builders.appender;
 import static org.apache.log4j.builders.BuilderManager.CATEGORY;
 import static org.apache.log4j.config.Log4j1Configuration.APPENDER_REF_TAG;
 import static org.apache.log4j.config.Log4j1Configuration.THRESHOLD_PARAM;
+import static org.apache.log4j.xml.XmlConfiguration.FILTER_TAG;
 import static org.apache.log4j.xml.XmlConfiguration.PARAM_TAG;
 import static org.apache.log4j.xml.XmlConfiguration.forEachElement;
 
 import java.util.ArrayList;
 import java.util.List;
 import java.util.Properties;
-import java.util.concurrent.atomic.AtomicBoolean;
-import java.util.concurrent.atomic.AtomicInteger;
-import java.util.concurrent.atomic.AtomicReference;
 
 import org.apache.log4j.Appender;
 import org.apache.log4j.bridge.AppenderWrapper;
+import org.apache.log4j.bridge.FilterAdapter;
 import org.apache.log4j.builders.AbstractBuilder;
 import org.apache.log4j.builders.BooleanHolder;
 import org.apache.log4j.builders.Holder;
 import org.apache.log4j.config.Log4j1Configuration;
 import org.apache.log4j.config.PropertiesConfiguration;
 import org.apache.log4j.helpers.OptionConverter;
+import org.apache.log4j.spi.Filter;
 import org.apache.log4j.xml.XmlConfiguration;
 import org.apache.logging.log4j.Logger;
 import org.apache.logging.log4j.core.appender.AsyncAppender;
+import org.apache.logging.log4j.core.appender.AsyncAppender.Builder;
 import org.apache.logging.log4j.core.config.AppenderRef;
 import org.apache.logging.log4j.plugins.Plugin;
 import org.apache.logging.log4j.status.StatusLogger;
+import org.apache.logging.log4j.util.Strings;
 import org.w3c.dom.Element;
 
-import java.util.ArrayList;
-import java.util.List;
-import java.util.Properties;
-
-import static org.apache.log4j.builders.BuilderManager.CATEGORY;
-import static org.apache.log4j.xml.XmlConfiguration.NAME_ATTR;
-import static org.apache.log4j.xml.XmlConfiguration.PARAM_TAG;
-import static org.apache.log4j.xml.XmlConfiguration.VALUE_ATTR;
-import static org.apache.log4j.xml.XmlConfiguration.forEachElement;
-import static org.apache.log4j.config.Log4j1Configuration.APPENDER_REF_TAG;
-import static org.apache.log4j.config.Log4j1Configuration.THRESHOLD_PARAM;
-
-
 /**
  * Build an Async Appender
  */
@@ -83,6 +72,7 @@ public class AsyncAppenderBuilder extends AbstractBuilder implements AppenderBui
         Holder<Boolean> includeLocation = new BooleanHolder();
         Holder<String> level = new Holder<>("trace");
         Holder<Integer> bufferSize = new Holder<>(1024);
+        Holder<Filter> filter = new Holder<>();
         forEachElement(appenderElement.getChildNodes(), (currentElement) -> {
             switch (currentElement.getTagName()) {
                 case APPENDER_REF_TAG:
@@ -91,6 +81,9 @@ public class AsyncAppenderBuilder extends AbstractBuilder implements AppenderBui
                         appenderRefs.get().add(appender.getName());
                     }
                     break;
+                case FILTER_TAG:
+                    config.addFilter(filter, currentElement);
+                    break;
                 case PARAM_TAG: {
                     switch (getNameAttributeKey(currentElement)) {
                         case BUFFER_SIZE_PARAM:
@@ -110,14 +103,15 @@ public class AsyncAppenderBuilder extends AbstractBuilder implements AppenderBui
                 }
             }
         });
-        return createAppender(name, level.get(), appenderRefs.get().toArray(new String[0]), blocking.get(),
-                bufferSize.get(), includeLocation.get(), config);
+        return createAppender(name, level.get(), appenderRefs.get().toArray(Strings.EMPTY_ARRAY), blocking.get(),
+                bufferSize.get(), includeLocation.get(), filter.get(), config);
     }
 
     @Override
     public Appender parseAppender(final String name, final String appenderPrefix, final String layoutPrefix,
             final String filterPrefix, final Properties props, final PropertiesConfiguration configuration) {
         final String appenderRef = getProperty(APPENDER_REF_TAG);
+        final Filter filter = configuration.parseAppenderFilters(props, filterPrefix, name);
         final boolean blocking = getBooleanProperty(BLOCKING_PARAM);
         final boolean includeLocation = getBooleanProperty(INCLUDE_LOCATION_PARAM);
         final String level = getProperty(THRESHOLD_PARAM);
@@ -131,13 +125,13 @@ public class AsyncAppenderBuilder extends AbstractBuilder implements AppenderBui
             LOGGER.warn("Cannot locate Appender {}", appenderRef);
             return null;
         }
-        return createAppender(name, level, new String[] {appenderRef}, blocking, bufferSize, includeLocation,
+        return createAppender(name, level, new String[]{appenderRef}, blocking, bufferSize, includeLocation, filter,
                 configuration);
     }
 
     private <T extends Log4j1Configuration> Appender createAppender(final String name, final String level,
             final String[] appenderRefs, final boolean blocking, final int bufferSize, final boolean includeLocation,
-            final T configuration) {
+            final Filter filter, final T configuration) {
         final org.apache.logging.log4j.Level logLevel = OptionConverter.convertLevel(level,
                 org.apache.logging.log4j.Level.TRACE);
         final AppenderRef[] refs = new AppenderRef[appenderRefs.length];
@@ -145,8 +139,9 @@ public class AsyncAppenderBuilder extends AbstractBuilder implements AppenderBui
         for (final String appenderRef : appenderRefs) {
             refs[index++] = AppenderRef.createAppenderRef(appenderRef, logLevel, null);
         }
-        return new AppenderWrapper(AsyncAppender.newBuilder()
-                .setName(name)
+        Builder builder = AsyncAppender.newBuilder();
+        builder.setFilter(FilterAdapter.convertFilter(filter));
+        return new AppenderWrapper(builder.setName(name)
                 .setAppenderRefs(refs)
                 .setBlocking(blocking)
                 .setBufferSize(bufferSize)
diff --git a/log4j-1.2-api/src/main/java/org/apache/log4j/builders/appender/ConsoleAppenderBuilder.java b/log4j-1.2-api/src/main/java/org/apache/log4j/builders/appender/ConsoleAppenderBuilder.java
index aa8056e..1214452 100644
--- a/log4j-1.2-api/src/main/java/org/apache/log4j/builders/appender/ConsoleAppenderBuilder.java
+++ b/log4j-1.2-api/src/main/java/org/apache/log4j/builders/appender/ConsoleAppenderBuilder.java
@@ -71,7 +71,7 @@ public class ConsoleAppenderBuilder extends AbstractBuilder implements AppenderB
         String name = getNameAttribute(appenderElement);
         Holder<String> target = new Holder<>(SYSTEM_OUT);
         Holder<Layout> layout = new Holder<>();
-        Holder<List<Filter>> filters = new Holder<>(new ArrayList<>());
+        Holder<Filter> filter = new Holder<>();
         Holder<String> level = new Holder<>();
         Holder<Boolean> follow = new BooleanHolder();
         Holder<Boolean> immediateFlush = new BooleanHolder(true);
@@ -81,7 +81,7 @@ public class ConsoleAppenderBuilder extends AbstractBuilder implements AppenderB
                     layout.set(config.parseLayout(currentElement));
                     break;
                 case FILTER_TAG:
-                    filters.get().add(config.parseFilters(currentElement));
+                    config.addFilter(filter, currentElement);
                     break;
                 case PARAM_TAG: {
                     switch (getNameAttributeKey(currentElement)) {
@@ -116,18 +116,7 @@ public class ConsoleAppenderBuilder extends AbstractBuilder implements AppenderB
                 }
             }
         });
-        Filter head = null;
-        Filter current = null;
-        for (final Filter f : filters.get()) {
-            if (head == null) {
-                head = f;
-                current = f;
-            } else {
-                current.next = f;
-                current = f;
-            }
-        }
-        return createAppender(name, layout.get(), head, level.get(), target.get(), immediateFlush.get(), follow.get(), config);
+        return createAppender(name, layout.get(), filter.get(), level.get(), target.get(), immediateFlush.get(), follow.get(), config);
     }
 
     @Override
diff --git a/log4j-1.2-api/src/main/java/org/apache/log4j/builders/appender/DailyRollingFileAppenderBuilder.java b/log4j-1.2-api/src/main/java/org/apache/log4j/builders/appender/DailyRollingFileAppenderBuilder.java
index 576ccab..78ade8c 100644
--- a/log4j-1.2-api/src/main/java/org/apache/log4j/builders/appender/DailyRollingFileAppenderBuilder.java
+++ b/log4j-1.2-api/src/main/java/org/apache/log4j/builders/appender/DailyRollingFileAppenderBuilder.java
@@ -82,7 +82,7 @@ public class DailyRollingFileAppenderBuilder extends AbstractBuilder implements
                     layout.set(config.parseLayout(currentElement));
                     break;
                 case FILTER_TAG:
-                    filter.set(config.parseFilters(currentElement));
+                    config.addFilter(filter, currentElement);
                     break;
                 case PARAM_TAG:
                     switch (getNameAttributeKey(currentElement)) {
diff --git a/log4j-1.2-api/src/main/java/org/apache/log4j/builders/appender/FileAppenderBuilder.java b/log4j-1.2-api/src/main/java/org/apache/log4j/builders/appender/FileAppenderBuilder.java
index 96d2f90..8ead958 100644
--- a/log4j-1.2-api/src/main/java/org/apache/log4j/builders/appender/FileAppenderBuilder.java
+++ b/log4j-1.2-api/src/main/java/org/apache/log4j/builders/appender/FileAppenderBuilder.java
@@ -77,7 +77,7 @@ public class FileAppenderBuilder extends AbstractBuilder implements AppenderBuil
                     layout.set(config.parseLayout(currentElement));
                     break;
                 case FILTER_TAG:
-                    filter.set(config.parseFilters(currentElement));
+                    config.addFilter(filter, currentElement);
                     break;
                 case PARAM_TAG:
                     switch (getNameAttributeKey(currentElement)) {
diff --git a/log4j-1.2-api/src/main/java/org/apache/log4j/builders/appender/RewriteAppenderBuilder.java b/log4j-1.2-api/src/main/java/org/apache/log4j/builders/appender/RewriteAppenderBuilder.java
index cac02ee..4398995 100644
--- a/log4j-1.2-api/src/main/java/org/apache/log4j/builders/appender/RewriteAppenderBuilder.java
+++ b/log4j-1.2-api/src/main/java/org/apache/log4j/builders/appender/RewriteAppenderBuilder.java
@@ -87,7 +87,7 @@ public class RewriteAppenderBuilder extends AbstractBuilder implements AppenderB
                     }
                     break;
                 case FILTER_TAG:
-                    filter.set(config.parseFilters(currentElement));
+                    config.addFilter(filter, currentElement);
                     break;
                 case PARAM_TAG:
                     if (getNameAttributeKey(currentElement).equalsIgnoreCase(THRESHOLD_PARAM)) {
diff --git a/log4j-1.2-api/src/main/java/org/apache/log4j/builders/appender/RollingFileAppenderBuilder.java b/log4j-1.2-api/src/main/java/org/apache/log4j/builders/appender/RollingFileAppenderBuilder.java
index 3a74245..3ab8086 100644
--- a/log4j-1.2-api/src/main/java/org/apache/log4j/builders/appender/RollingFileAppenderBuilder.java
+++ b/log4j-1.2-api/src/main/java/org/apache/log4j/builders/appender/RollingFileAppenderBuilder.java
@@ -84,7 +84,7 @@ public class RollingFileAppenderBuilder extends AbstractBuilder implements Appen
                     layout.set(config.parseLayout(currentElement));
                     break;
                 case FILTER_TAG:
-                    filter.set(config.parseFilters(currentElement));
+                    config.addFilter(filter, currentElement);
                     break;
                 case PARAM_TAG:
                     switch (getNameAttributeKey(currentElement)) {
diff --git a/log4j-1.2-api/src/main/java/org/apache/log4j/builders/appender/SocketAppenderBuilder.java b/log4j-1.2-api/src/main/java/org/apache/log4j/builders/appender/SocketAppenderBuilder.java
index 9034f05..a2997e4 100644
--- a/log4j-1.2-api/src/main/java/org/apache/log4j/builders/appender/SocketAppenderBuilder.java
+++ b/log4j-1.2-api/src/main/java/org/apache/log4j/builders/appender/SocketAppenderBuilder.java
@@ -100,7 +100,7 @@ public class SocketAppenderBuilder extends AbstractBuilder implements AppenderBu
         final Holder<Integer> port = new Holder<>(DEFAULT_PORT);
         final Holder<Integer> reconnectDelay = new Holder<>(DEFAULT_RECONNECTION_DELAY);
         final Holder<Layout> layout = new Holder<>();
-        final Holder<List<Filter>> filters = new Holder<>(new ArrayList<>());
+        final Holder<Filter> filter = new Holder<>();
         final Holder<String> level = new Holder<>();
         final Holder<Boolean> immediateFlush = new BooleanHolder(true);
         forEachElement(appenderElement.getChildNodes(), currentElement -> {
@@ -109,7 +109,7 @@ public class SocketAppenderBuilder extends AbstractBuilder implements AppenderBu
                 layout.set(config.parseLayout(currentElement));
                 break;
             case FILTER_TAG:
-                filters.get().add(config.parseFilters(currentElement));
+                config.addFilter(filter, currentElement);
                 break;
             case PARAM_TAG:
                 switch (getNameAttributeKey(currentElement)) {
@@ -132,17 +132,7 @@ public class SocketAppenderBuilder extends AbstractBuilder implements AppenderBu
                 break;
             }
         });
-        Filter head = null;
-        Filter current = null;
-        for (final Filter f : filters.get()) {
-            if (head == null) {
-                head = f;
-            } else {
-                current.next = f;
-            }
-            current = f;
-        }
-        return createAppender(name, host.get(), port.get(), layout.get(), head, level.get(), immediateFlush.get(), reconnectDelay.get(), config);
+        return createAppender(name, host.get(), port.get(), layout.get(), filter.get(), level.get(), immediateFlush.get(), reconnectDelay.get(), config);
     }
 
     @Override
diff --git a/log4j-1.2-api/src/main/java/org/apache/log4j/builders/appender/SyslogAppenderBuilder.java b/log4j-1.2-api/src/main/java/org/apache/log4j/builders/appender/SyslogAppenderBuilder.java
index fe6ee59..ffbdedf 100644
--- a/log4j-1.2-api/src/main/java/org/apache/log4j/builders/appender/SyslogAppenderBuilder.java
+++ b/log4j-1.2-api/src/main/java/org/apache/log4j/builders/appender/SyslogAppenderBuilder.java
@@ -85,7 +85,7 @@ public class SyslogAppenderBuilder extends AbstractBuilder implements AppenderBu
                     layout.set(config.parseLayout(currentElement));
                     break;
                 case FILTER_TAG:
-                    filter.set(config.parseFilters(currentElement));
+                    config.addFilter(filter, currentElement);
                     break;
                 case PARAM_TAG: {
                     switch (getNameAttributeKey(currentElement)) {
diff --git a/log4j-1.2-api/src/main/java/org/apache/log4j/config/PropertiesConfiguration.java b/log4j-1.2-api/src/main/java/org/apache/log4j/config/PropertiesConfiguration.java
index 405bd4b..32a523d 100644
--- a/log4j-1.2-api/src/main/java/org/apache/log4j/config/PropertiesConfiguration.java
+++ b/log4j-1.2-api/src/main/java/org/apache/log4j/config/PropertiesConfiguration.java
@@ -35,7 +35,7 @@ import org.apache.log4j.LogManager;
 import org.apache.log4j.PatternLayout;
 import org.apache.log4j.bridge.AppenderAdapter;
 import org.apache.log4j.bridge.AppenderWrapper;
-import org.apache.log4j.builders.BuilderManager;
+import org.apache.log4j.bridge.FilterAdapter;
 import org.apache.log4j.helpers.OptionConverter;
 import org.apache.log4j.spi.ErrorHandler;
 import org.apache.log4j.spi.Filter;
@@ -44,22 +44,8 @@ import org.apache.logging.log4j.core.config.Configuration;
 import org.apache.logging.log4j.core.config.ConfigurationSource;
 import org.apache.logging.log4j.core.config.LoggerConfig;
 import org.apache.logging.log4j.core.config.status.StatusConfiguration;
-import org.apache.logging.log4j.core.filter.AbstractFilterable;
 import org.apache.logging.log4j.util.LoaderUtil;
 
-import java.io.IOException;
-import java.io.InputStream;
-import java.lang.reflect.InvocationTargetException;
-import java.util.ArrayList;
-import java.util.Enumeration;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
-import java.util.Properties;
-import java.util.SortedMap;
-import java.util.StringTokenizer;
-import java.util.TreeMap;
-
 /**
  * Constructs a configuration based on Log4j 1 properties.
  */
@@ -570,7 +556,6 @@ public class PropertiesConfiguration extends Log4j1Configuration {
         }
 
         Filter head = null;
-        Filter next = null;
         for (final Map.Entry<String, List<NameValue>> entry : filters.entrySet()) {
             final String clazz = props.getProperty(entry.getKey());
             Filter filter = null;
@@ -581,14 +566,7 @@ public class PropertiesConfiguration extends Log4j1Configuration {
                     filter = buildFilter(clazz, appenderName, entry.getValue());
                 }
             }
-            if (filter != null) {
-                if (head == null) {
-                    head = filter;
-                } else {
-                    next.setNext(filter);
-                }
-                next = filter;
-            }
+            head = FilterAdapter.addFilter(head, filter);
         }
         return head;
     }
diff --git a/log4j-1.2-api/src/main/java/org/apache/log4j/spi/Filter.java b/log4j-1.2-api/src/main/java/org/apache/log4j/spi/Filter.java
index 17a15d6..91917ab 100644
--- a/log4j-1.2-api/src/main/java/org/apache/log4j/spi/Filter.java
+++ b/log4j-1.2-api/src/main/java/org/apache/log4j/spi/Filter.java
@@ -16,8 +16,6 @@
  */
 package org.apache.log4j.spi;
 
-import org.apache.log4j.bridge.FilterAdapter;
-
 /**
  * @since 0.9.0
  */
@@ -32,16 +30,6 @@ public abstract class Filter {
         }
         isCorePresent = temp;
     }
-    
-    // TODO Unused?
-    private final FilterAdapter adapter;
-
-    /**
-     * Constructs a new instance.
-     */
-    public Filter() {
-        this.adapter = isCorePresent ? new FilterAdapter(this) : null;
-    }
 
     /**
      * The log event must be dropped immediately without consulting
diff --git a/log4j-1.2-api/src/main/java/org/apache/log4j/xml/XmlConfiguration.java b/log4j-1.2-api/src/main/java/org/apache/log4j/xml/XmlConfiguration.java
index 65b6a2c..1b05d57 100644
--- a/log4j-1.2-api/src/main/java/org/apache/log4j/xml/XmlConfiguration.java
+++ b/log4j-1.2-api/src/main/java/org/apache/log4j/xml/XmlConfiguration.java
@@ -21,6 +21,8 @@ import org.apache.log4j.Layout;
 import org.apache.log4j.Level;
 import org.apache.log4j.bridge.AppenderAdapter;
 import org.apache.log4j.bridge.AppenderWrapper;
+import org.apache.log4j.bridge.FilterAdapter;
+import org.apache.log4j.builders.Holder;
 import org.apache.log4j.config.Log4j1Configuration;
 import org.apache.log4j.config.PropertySetter;
 import org.apache.log4j.helpers.OptionConverter;
@@ -375,7 +377,8 @@ public class XmlConfiguration extends Log4j1Configuration {
             PropertySetter propSetter = new PropertySetter(appender);
 
             appender.setName(subst(appenderElement.getAttribute(NAME_ATTR)));
-            forEachElement(appenderElement.getChildNodes(), (currentElement) -> {
+            final Holder<Filter> filterChain = new Holder<>();
+            forEachElement(appenderElement.getChildNodes(), currentElement -> {
                 // Parse appender parameters
                 switch (currentElement.getTagName()) {
                     case PARAM_TAG:
@@ -385,12 +388,7 @@ public class XmlConfiguration extends Log4j1Configuration {
                         appender.setLayout(parseLayout(currentElement));
                         break;
                     case FILTER_TAG:
-                        Filter filter = parseFilters(currentElement);
-                        if (filter != null) {
-                            LOGGER.debug("Adding filter of type [{}] to appender named [{}]",
-                                    filter.getClass(), appender.getName());
-                            appender.addFilter(filter);
-                        }
+                        addFilter(filterChain, currentElement);
                         break;
                     case ERROR_HANDLER_TAG:
                         parseErrorHandler(currentElement, appender);
@@ -417,6 +415,10 @@ public class XmlConfiguration extends Log4j1Configuration {
                         }
                 }
             });
+            final Filter head = filterChain.get();
+            if (head != null) {
+                appender.addFilter(head);
+            }
             propSetter.activate();
             return appender;
         } catch (ConsumerException ex) {
@@ -502,12 +504,30 @@ public class XmlConfiguration extends Log4j1Configuration {
      * @param filterElement The Filter Element.
      * @return The Filter.
      */
+    public void addFilter(final Holder<Filter> ref, final Element filterElement) {
+        final Filter filter = parseFilters(filterElement);
+        final Filter newFilter = FilterAdapter.addFilter(ref.get(), filter);
+        ref.set(newFilter);
+    }
+
+    /**
+     * Used internally to parse a filter element.
+     */
     public Filter parseFilters(Element filterElement) {
         String className = subst(filterElement.getAttribute(CLASS_ATTR));
         LOGGER.debug("Class name: [" + className + ']');
         Filter filter = manager.parseFilter(className, filterElement, this);
         if (filter == null) {
+            filter = buildFilter(className, filterElement);
+        }
+        return filter;
+    }
+
+    private Filter buildFilter(final String className, final Element filterElement) {
+        try {
+            Filter filter = LoaderUtil.newInstanceOf(className);
             PropertySetter propSetter = new PropertySetter(filter);
+
             forEachElement(filterElement.getChildNodes(), (currentElement) -> {
                 String tagName = currentElement.getTagName();
                 if (tagName.equals(PARAM_TAG)) {
@@ -517,8 +537,20 @@ public class XmlConfiguration extends Log4j1Configuration {
                 }
             });
             propSetter.activate();
+            return filter;
+        } catch (ConsumerException ex) {
+            Throwable t = ex.getCause();
+            if (t instanceof InterruptedException || t instanceof InterruptedIOException) {
+                Thread.currentThread().interrupt();
+            }
+            LOGGER.error("Could not create an Filter. Reported error follows.", t);
+        } catch (Exception oops) {
+            if (oops instanceof InterruptedException || oops instanceof InterruptedIOException) {
+                Thread.currentThread().interrupt();
+            }
+            LOGGER.error("Could not create an Filter. Reported error follows.", oops);
         }
-        return filter;
+        return null;
     }
 
     /**
diff --git a/log4j-1.2-api/src/test/java/org/apache/log4j/builders/Log4j2ListAppenderBuilder.java b/log4j-1.2-api/src/test/java/org/apache/log4j/builders/Log4j2ListAppenderBuilder.java
new file mode 100644
index 0000000..ccef99b
--- /dev/null
+++ b/log4j-1.2-api/src/test/java/org/apache/log4j/builders/Log4j2ListAppenderBuilder.java
@@ -0,0 +1,98 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache license, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the license for the specific language governing permissions and
+ * limitations under the license.
+ */
+package org.apache.log4j.builders;
+
+import static org.apache.log4j.builders.BuilderManager.CATEGORY;
+import static org.apache.log4j.xml.XmlConfiguration.FILTER_TAG;
+import static org.apache.log4j.xml.XmlConfiguration.LAYOUT_TAG;
+import static org.apache.log4j.xml.XmlConfiguration.NAME_ATTR;
+import static org.apache.log4j.xml.XmlConfiguration.forEachElement;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Properties;
+import java.util.concurrent.atomic.AtomicBoolean;
+import java.util.concurrent.atomic.AtomicReference;
+
+import org.apache.log4j.Appender;
+import org.apache.log4j.Layout;
+import org.apache.log4j.bridge.AppenderWrapper;
+import org.apache.log4j.bridge.LayoutAdapter;
+import org.apache.log4j.bridge.LayoutWrapper;
+import org.apache.log4j.builders.appender.AppenderBuilder;
+import org.apache.log4j.config.PropertiesConfiguration;
+import org.apache.log4j.spi.Filter;
+import org.apache.log4j.xml.XmlConfiguration;
+import org.apache.logging.log4j.core.test.appender.ListAppender;
+import org.apache.logging.log4j.plugins.Plugin;
+import org.w3c.dom.Element;
+
+/**
+ * Builder for the native Log4j 2.x list appender to be used in the tests.
+ */
+@Plugin(name = "org.apache.logging.log4j.test.appender.ListAppender", category = CATEGORY)
+public class Log4j2ListAppenderBuilder extends AbstractBuilder implements AppenderBuilder {
+
+    public Log4j2ListAppenderBuilder() {
+    }
+
+    public Log4j2ListAppenderBuilder(final String prefix, final Properties props) {
+        super(prefix, props);
+    }
+
+    @Override
+    public Appender parseAppender(Element element, XmlConfiguration configuration) {
+        final String name = getNameAttribute(element);
+        final Holder<Layout> layout = new Holder<>();
+        final Holder<Filter> filter = new Holder<>();
+        forEachElement(element.getChildNodes(), currentElement -> {
+            switch (currentElement.getTagName()) {
+                case LAYOUT_TAG :
+                    layout.set(configuration.parseLayout(currentElement));
+                    break;
+                case FILTER_TAG :
+                    configuration.addFilter(filter, currentElement);
+                    break;
+                default :
+            }
+        });
+        return createAppender(name, layout.get(), filter.get());
+    }
+
+    @Override
+    public Appender parseAppender(String name, String appenderPrefix, String layoutPrefix, String filterPrefix,
+            Properties props, PropertiesConfiguration configuration) {
+        final Layout layout = configuration.parseLayout(layoutPrefix, name, props);
+        final Filter filter = configuration.parseAppenderFilters(props, filterPrefix, name);
+        return createAppender(name, layout, filter);
+    }
+
+    private Appender createAppender(String name, Layout layout, Filter filter) {
+        final org.apache.logging.log4j.core.Layout<?> log4j2Layout;
+        if (layout instanceof LayoutWrapper) {
+            log4j2Layout = ((LayoutWrapper) layout).getLayout();
+        } else {
+            log4j2Layout = layout != null ? new LayoutAdapter(layout) : null;
+        }
+        return new AppenderWrapper(
+                ListAppender.newBuilder()
+                        .setName(name)
+                        .setLayout(log4j2Layout)
+                        .setFilter(AbstractBuilder.buildFilters(null, filter))
+                        .build());
+    }
+}
diff --git a/log4j-1.2-api/src/test/java/org/apache/log4j/config/AbstractLog4j1ConfigurationTest.java b/log4j-1.2-api/src/test/java/org/apache/log4j/config/AbstractLog4j1ConfigurationTest.java
index 21c9b7e..e4ac132 100644
--- a/log4j-1.2-api/src/test/java/org/apache/log4j/config/AbstractLog4j1ConfigurationTest.java
+++ b/log4j-1.2-api/src/test/java/org/apache/log4j/config/AbstractLog4j1ConfigurationTest.java
@@ -17,7 +17,9 @@
 package org.apache.log4j.config;
 
 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.io.File;
@@ -33,9 +35,16 @@ import java.nio.file.Files;
 import java.nio.file.Path;
 import java.util.concurrent.TimeUnit;
 
+import org.apache.log4j.ListAppender;
+import org.apache.log4j.LogManager;
+import org.apache.log4j.Logger;
+import org.apache.log4j.bridge.FilterAdapter;
+import org.apache.log4j.bridge.FilterWrapper;
+import org.apache.log4j.bridge.AppenderAdapter.Adapter;
 import org.apache.logging.log4j.Level;
 import org.apache.logging.log4j.core.Appender;
 import org.apache.logging.log4j.core.Layout;
+import org.apache.logging.log4j.core.LoggerContext;
 import org.apache.logging.log4j.core.appender.ConsoleAppender;
 import org.apache.logging.log4j.core.appender.ConsoleAppender.Target;
 import org.apache.logging.log4j.core.appender.FileAppender;
@@ -49,15 +58,28 @@ import org.apache.logging.log4j.core.appender.rolling.SizeBasedTriggeringPolicy;
 import org.apache.logging.log4j.core.appender.rolling.TimeBasedTriggeringPolicy;
 import org.apache.logging.log4j.core.appender.rolling.TriggeringPolicy;
 import org.apache.logging.log4j.core.config.Configuration;
+import org.apache.logging.log4j.core.config.Configurator;
 import org.apache.logging.log4j.core.config.LoggerConfig;
+import org.apache.logging.log4j.core.filter.CompositeFilter;
+import org.apache.logging.log4j.core.filter.Filterable;
 import org.apache.logging.log4j.core.layout.HtmlLayout;
 import org.apache.logging.log4j.core.layout.PatternLayout;
 import org.apache.logging.log4j.core.util.CloseShieldOutputStream;
+import org.junit.Rule;
+import org.junit.rules.TemporaryFolder;
 
 public abstract class AbstractLog4j1ConfigurationTest {
 
+    @Rule
+    public TemporaryFolder tempFolder = TemporaryFolder.builder().assureDeletion().build();
+
     abstract Configuration getConfiguration(String configResourcePrefix) throws URISyntaxException, IOException;
 
+    private LoggerContext configure(String configResourcePrefix) throws URISyntaxException, IOException {
+        Configurator.reconfigure(getConfiguration(configResourcePrefix));
+        return (LoggerContext) org.apache.logging.log4j.LogManager.getContext(false);
+    }
+
     public void testConsoleCapitalization() throws Exception {
         final Configuration config = getConfiguration("config-1.2/log4j-capitalization");
         final Appender capitalized = config.getAppender("ConsoleCapitalized");
@@ -378,4 +400,104 @@ public abstract class AbstractLog4j1ConfigurationTest {
         config.start();
         config.stop();
     }
+
+    /**
+     * Checks a hierarchy of filters.
+     * 
+     * @param filter
+     * @return the number of filters
+     */
+    private int checkFilters(final org.apache.logging.log4j.core.Filter filter) {
+        int count = 0;
+        if (filter instanceof CompositeFilter) {
+            for (final org.apache.logging.log4j.core.Filter part : ((CompositeFilter) filter).getFiltersArray()) {
+                count += checkFilters(part);
+            }
+        } else if (filter instanceof FilterAdapter) {
+            // Don't create adapters from wrappers
+            assertFalse("found FilterAdapter of a FilterWrapper", ((FilterAdapter) filter).getFilter() instanceof FilterWrapper);
+            count += checkFilters(((FilterAdapter) filter).getFilter());
+        } else {
+            count++;
+        }
+        return count;
+    }
+
+    /**
+     * Checks a hierarchy of filters.
+     * 
+     * @param filter
+     * @return the number of filters
+     */
+    private int checkFilters(final org.apache.log4j.spi.Filter filter) {
+        int count = 0;
+        if (filter instanceof FilterWrapper) {
+            // Don't create wrappers from adapters
+            assertFalse("found FilterWrapper of a FilterAdapter", ((FilterWrapper) filter).getFilter() instanceof FilterAdapter);
+            count += checkFilters(((FilterWrapper) filter).getFilter());
+        } else {
+            count++;
+        }
+        // We prefer a:
+        // CompositeFilter of native Log4j 2.x filters
+        // over a:
+        // FilterAdapter of a chain of FilterWrappers.
+        assertNull("found chain of Log4j 1.x filters", filter.getNext());
+        return count;
+    }
+
+    public void testMultipleFilters() throws Exception {
+        final File folder = tempFolder.newFolder();
+        System.setProperty("test.tmpDir", folder.getCanonicalPath());
+        try (LoggerContext loggerContext = configure("log4j-multipleFilters")) {
+            final Configuration configuration = loggerContext.getConfiguration();
+            assertNotNull(configuration);
+            // Check only number of filters.
+            final Filterable console = configuration.getAppender("CONSOLE");
+            assertNotNull(console);
+            assertEquals(4, checkFilters(console.getFilter()));
+            final Filterable file = configuration.getAppender("FILE");
+            assertNotNull(file);
+            assertEquals(4, checkFilters(file.getFilter()));
+            final Filterable rfa = configuration.getAppender("RFA");
+            assertNotNull(rfa);
+            assertEquals(4, checkFilters(rfa.getFilter()));
+            final Filterable drfa = configuration.getAppender("DRFA");
+            assertNotNull(drfa);
+            assertEquals(4, checkFilters(drfa.getFilter()));
+            // List appenders
+            final Appender appender = configuration.getAppender("LIST");
+            assertNotNull(appender);
+            assertEquals(3, checkFilters(((Filterable)appender).getFilter()));
+            final ListAppender legacyAppender = (ListAppender) ((Adapter) appender).getAppender();
+            final org.apache.logging.log4j.core.test.appender.ListAppender nativeAppender = configuration.getAppender("LIST2");
+            assertEquals(3, checkFilters(((Filterable)nativeAppender).getFilter()));
+            final Logger logger = LogManager.getLogger(PropertiesConfigurationTest.class);
+            int expected = 0;
+            // message blocked by Threshold
+            logger.trace("NEUTRAL message");
+            assertEquals(expected, legacyAppender.getEvents().size());
+            assertEquals(expected, nativeAppender.getEvents().size());
+            // message blocked by DenyAll filter
+            logger.warn("NEUTRAL message");
+            assertEquals(expected, legacyAppender.getEvents().size());
+            assertEquals(expected, nativeAppender.getEvents().size());
+            // message accepted by level filter
+            logger.info("NEUTRAL message");
+            expected++;
+            assertEquals(expected, legacyAppender.getEvents().size());
+            assertEquals(expected, nativeAppender.getEvents().size());
+            // message accepted by "StartsWith" filter
+            logger.warn("ACCEPT message");
+            expected++;
+            assertEquals(expected, legacyAppender.getEvents().size());
+            assertEquals(expected, nativeAppender.getEvents().size());
+            // message blocked by "StartsWith" filter
+            logger.info("DENY message");
+            assertEquals(expected, legacyAppender.getEvents().size());
+            assertEquals(expected, nativeAppender.getEvents().size());
+        } finally {
+            System.clearProperty("test.tmpDir");
+        }
+    }
 }
diff --git a/log4j-1.2-api/src/test/java/org/apache/log4j/config/PropertiesConfigurationTest.java b/log4j-1.2-api/src/test/java/org/apache/log4j/config/PropertiesConfigurationTest.java
index 85d2859..21534f5 100644
--- a/log4j-1.2-api/src/test/java/org/apache/log4j/config/PropertiesConfigurationTest.java
+++ b/log4j-1.2-api/src/test/java/org/apache/log4j/config/PropertiesConfigurationTest.java
@@ -21,7 +21,6 @@ import org.apache.log4j.LogManager;
 import org.apache.log4j.Logger;
 import org.apache.log4j.bridge.AppenderAdapter;
 import org.apache.log4j.bridge.FilterAdapter;
-import org.apache.log4j.bridge.FilterWrapper;
 import org.apache.log4j.spi.LoggingEvent;
 import org.apache.logging.log4j.Level;
 import org.apache.logging.log4j.core.Appender;
@@ -114,9 +113,9 @@ public class PropertiesConfigurationTest extends AbstractLog4j1ConfigurationTest
             final Filterable filterable = (Filterable) appender;
             final CompositeFilter filter = (CompositeFilter) filterable.getFilter();
             final org.apache.logging.log4j.core.Filter[] filters = filter.getFiltersArray();
-            final LevelRangeFilter customFilterReal = (LevelRangeFilter) ((FilterWrapper) ((FilterAdapter) filters[0]).getFilter()).getFilter();
+            final LevelRangeFilter customFilterReal = (LevelRangeFilter) filters[0];
             assertEquals(Level.ALL, customFilterReal.getMinLevel());
-            final LevelRangeFilter defaultFilter = (LevelRangeFilter) ((FilterWrapper) ((FilterAdapter) filters[1]).getFilter()).getFilter();
+            final LevelRangeFilter defaultFilter = (LevelRangeFilter) filters[1];
             assertEquals(Level.TRACE, defaultFilter.getMinLevel());
       }
     }
@@ -180,6 +179,7 @@ public class PropertiesConfigurationTest extends AbstractLog4j1ConfigurationTest
         }
     }
 
+
     @Override
     @Test
     public void testConsoleEnhancedPatternLayout() throws Exception {
@@ -263,4 +263,10 @@ public class PropertiesConfigurationTest extends AbstractLog4j1ConfigurationTest
     public void testDefaultValues() throws Exception {
         super.testDefaultValues();
     }
+
+    @Override
+    @Test
+    public void testMultipleFilters() throws Exception {
+        super.testMultipleFilters();
+    }
 }
diff --git a/log4j-1.2-api/src/test/java/org/apache/log4j/config/StartsWithFilter.java b/log4j-1.2-api/src/test/java/org/apache/log4j/config/StartsWithFilter.java
new file mode 100644
index 0000000..db16d84
--- /dev/null
+++ b/log4j-1.2-api/src/test/java/org/apache/log4j/config/StartsWithFilter.java
@@ -0,0 +1,38 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache license, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the license for the specific language governing permissions and
+ * limitations under the license.
+ */
+package org.apache.log4j.config;
+
+import org.apache.log4j.spi.Filter;
+import org.apache.log4j.spi.LoggingEvent;
+
+/**
+ * A Filter used in tests.
+ */
+public class StartsWithFilter extends Filter {
+
+    @Override
+    public int decide(LoggingEvent event) {
+        String message = String.valueOf(event.getMessage());
+        if (message.startsWith("DENY")) {
+            return DENY;
+        } else if (message.startsWith("ACCEPT")) {
+            return ACCEPT;
+        }
+        return NEUTRAL;
+    }
+
+}
diff --git a/log4j-1.2-api/src/test/java/org/apache/log4j/config/XmlConfigurationTest.java b/log4j-1.2-api/src/test/java/org/apache/log4j/config/XmlConfigurationTest.java
index 6cacbf8..98ac3ba 100644
--- a/log4j-1.2-api/src/test/java/org/apache/log4j/config/XmlConfigurationTest.java
+++ b/log4j-1.2-api/src/test/java/org/apache/log4j/config/XmlConfigurationTest.java
@@ -186,4 +186,10 @@ public class XmlConfigurationTest extends AbstractLog4j1ConfigurationTest {
         super.testDefaultValues();
     }
 
+    @Override
+    @Test
+    public void testMultipleFilters() throws Exception {
+        super.testMultipleFilters();
+    }
+
 }
diff --git a/log4j-1.2-api/src/test/resources/log4j-multipleFilters.properties b/log4j-1.2-api/src/test/resources/log4j-multipleFilters.properties
new file mode 100644
index 0000000..177d60c
--- /dev/null
+++ b/log4j-1.2-api/src/test/resources/log4j-multipleFilters.properties
@@ -0,0 +1,69 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one or more
+# contributor license agreements.  See the NOTICE file distributed with
+# this work for additional information regarding copyright ownership.
+# The ASF licenses this file to You under the Apache License, Version 2.0
+# (the "License"); you may not use this file except in compliance with
+# the License.  You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+log4j.appender.LIST=org.apache.log4j.ListAppender
+log4j.appender.LIST.Threshold=DEBUG
+log4j.appender.LIST.filter.1=org.apache.log4j.config.StartsWithFilter
+log4j.appender.LIST.filter.2=org.apache.log4j.varia.LevelMatchFilter
+log4j.appender.LIST.filter.2.LevelToMatch=INFO
+log4j.appender.LIST.filter.2.AcceptOnMatch=true
+log4j.appender.LIST.filter.3=org.apache.log4j.varia.DenyAllFilter
+
+log4j.appender.LIST2=org.apache.logging.log4j.test.appender.ListAppender
+log4j.appender.LIST2.Threshold=DEBUG
+log4j.appender.LIST2.filter.1=org.apache.log4j.config.StartsWithFilter
+log4j.appender.LIST2.filter.2=org.apache.log4j.varia.LevelMatchFilter
+log4j.appender.LIST2.filter.2.LevelToMatch=INFO
+log4j.appender.LIST2.filter.2.AcceptOnMatch=true
+log4j.appender.LIST2.filter.3=org.apache.log4j.varia.DenyAllFilter
+
+log4j.appender.CONSOLE=org.apache.log4j.ConsoleAppender
+log4j.appender.CONSOLE.Threshold=DEBUG
+log4j.appender.CONSOLE.filter.1=org.apache.log4j.config.StartsWithFilter
+log4j.appender.CONSOLE.filter.2=org.apache.log4j.varia.LevelMatchFilter
+log4j.appender.CONSOLE.filter.2.LevelToMatch=INFO
+log4j.appender.CONSOLE.filter.2.AcceptOnMatch=true
+log4j.appender.CONSOLE.filter.3=org.apache.log4j.varia.DenyAllFilter
+
+log4j.appender.FILE=org.apache.log4j.FileAppender
+log4j.appender.FILE.Threshold=DEBUG
+log4j.appender.FILE.File=${test.tmpDir}/file-appender.log
+log4j.appender.FILE.filter.1=org.apache.log4j.config.StartsWithFilter
+log4j.appender.FILE.filter.2=org.apache.log4j.varia.LevelMatchFilter
+log4j.appender.FILE.filter.2.LevelToMatch=INFO
+log4j.appender.FILE.filter.2.AcceptOnMatch=true
+log4j.appender.FILE.filter.3=org.apache.log4j.varia.DenyAllFilter
+
+log4j.appender.RFA=org.apache.log4j.RollingFileAppender
+log4j.appender.RFA.Threshold=DEBUG
+log4j.appender.RFA.File=${test.tmpDir}/rolling-file-appender.log
+log4j.appender.RFA.filter.1=org.apache.log4j.config.StartsWithFilter
+log4j.appender.RFA.filter.2=org.apache.log4j.varia.LevelMatchFilter
+log4j.appender.RFA.filter.2.LevelToMatch=INFO
+log4j.appender.RFA.filter.2.AcceptOnMatch=true
+log4j.appender.RFA.filter.3=org.apache.log4j.varia.DenyAllFilter
+
+log4j.appender.DRFA=org.apache.log4j.DailyRollingFileAppender
+log4j.appender.DRFA.Threshold=DEBUG
+log4j.appender.DRFA.File=${test.tmpDir}/daily-rolling-file-appender.log
+log4j.appender.DRFA.filter.1=org.apache.log4j.config.StartsWithFilter
+log4j.appender.DRFA.filter.2=org.apache.log4j.varia.LevelMatchFilter
+log4j.appender.DRFA.filter.2.LevelToMatch=INFO
+log4j.appender.DRFA.filter.2.AcceptOnMatch=true
+log4j.appender.DRFA.filter.3=org.apache.log4j.varia.DenyAllFilter
+
+log4j.rootLogger=TRACE, LIST, LIST2, CONSOLE, FILE, RFA, DRFA
\ No newline at end of file
diff --git a/log4j-1.2-api/src/test/resources/log4j-multipleFilters.xml b/log4j-1.2-api/src/test/resources/log4j-multipleFilters.xml
new file mode 100644
index 0000000..ea256e7
--- /dev/null
+++ b/log4j-1.2-api/src/test/resources/log4j-multipleFilters.xml
@@ -0,0 +1,92 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+  Licensed to the Apache Software Foundation (ASF) under one or more
+  contributor license agreements.  See the NOTICE file distributed with
+  this work for additional information regarding copyright ownership.
+  The ASF licenses this file to You under the Apache License, Version 2.0
+  (the "License"); you may not use this file except in compliance with
+  the License.  You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  See the License for the specific language governing permissions and
+  limitations under the License.
+-->
+<!DOCTYPE log4j:configuration SYSTEM "log4j.dtd">
+<log4j:configuration xmlns:log4j="http://jakarta.apache.org/log4j/">
+  <appender name="LIST" class="org.apache.log4j.ListAppender">
+    <param name="Threshold" value="DEBUG" />
+    <filter class="org.apache.log4j.config.StartsWithFilter" />
+    <filter class="org.apache.log4j.varia.LevelMatchFilter">
+      <param name="LevelToMatch" value="INFO" />
+      <param name="AcceptOnMatch" value="true" />
+    </filter>
+    <filter class="org.apache.log4j.varia.DenyAllFilter" />
+  </appender>
+
+  <appender name="LIST2" class="org.apache.logging.log4j.test.appender.ListAppender">
+    <param name="Threshold" value="DEBUG" />
+    <filter class="org.apache.log4j.config.StartsWithFilter" />
+    <filter class="org.apache.log4j.varia.LevelMatchFilter">
+      <param name="LevelToMatch" value="INFO" />
+      <param name="AcceptOnMatch" value="true" />
+    </filter>
+    <filter class="org.apache.log4j.varia.DenyAllFilter" />
+  </appender>
+
+  <appender name="CONSOLE" class="org.apache.log4j.ConsoleAppender">
+    <param name="Threshold" value="DEBUG" />
+    <filter class="org.apache.log4j.config.StartsWithFilter" />
+    <filter class="org.apache.log4j.varia.LevelMatchFilter">
+      <param name="LevelToMatch" value="INFO" />
+      <param name="AcceptOnMatch" value="true" />
+    </filter>
+    <filter class="org.apache.log4j.varia.DenyAllFilter" />
+  </appender>
+
+  <appender name="FILE" class="org.apache.log4j.FileAppender">
+    <param name="Threshold" value="DEBUG" />
+    <param name="File" value="${test.tmpDir}/file-appender.log" />
+    <filter class="org.apache.log4j.config.StartsWithFilter" />
+    <filter class="org.apache.log4j.varia.LevelMatchFilter">
+      <param name="LevelToMatch" value="INFO" />
+      <param name="AcceptOnMatch" value="true" />
+    </filter>
+    <filter class="org.apache.log4j.varia.DenyAllFilter" />
+  </appender>
+
+  <appender name="RFA" class="org.apache.log4j.RollingFileAppender">
+    <param name="Threshold" value="DEBUG" />
+    <param name="File" value="${test.tmpDir}/rolling-file-appender.log" />
+    <filter class="org.apache.log4j.config.StartsWithFilter" />
+    <filter class="org.apache.log4j.varia.LevelMatchFilter">
+      <param name="LevelToMatch" value="INFO" />
+      <param name="AcceptOnMatch" value="true" />
+    </filter>
+    <filter class="org.apache.log4j.varia.DenyAllFilter" />
+  </appender>
+
+  <appender name="DRFA" class="org.apache.log4j.DailyRollingFileAppender">
+    <param name="Threshold" value="DEBUG" />
+    <param name="File" value="${test.tmpDir}/daily-rolling-file-appender.log" />
+    <filter class="org.apache.log4j.config.StartsWithFilter" />
+    <filter class="org.apache.log4j.varia.LevelMatchFilter">
+      <param name="LevelToMatch" value="INFO" />
+      <param name="AcceptOnMatch" value="true" />
+    </filter>
+    <filter class="org.apache.log4j.varia.DenyAllFilter" />
+  </appender>
+
+  <root>
+    <priority value="TRACE" />
+    <appender-ref ref="LIST" />
+    <appender-ref ref="LIST2" />
+    <appender-ref ref="CONSOLE" />
+    <appender-ref ref="FILE" />
+    <appender-ref ref="RFA" />
+    <appender-ref ref="DRFA" />
+  </root>
+</log4j:configuration>
diff --git a/log4j-core-test/src/test/java/org/apache/logging/log4j/core/filter/CompositeFilterTest.java b/log4j-core-test/src/test/java/org/apache/logging/log4j/core/filter/CompositeFilterTest.java
new file mode 100644
index 0000000..0a5698b
--- /dev/null
+++ b/log4j-core-test/src/test/java/org/apache/logging/log4j/core/filter/CompositeFilterTest.java
@@ -0,0 +1,49 @@
+/* Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.logging.log4j.core.filter;
+
+import static org.junit.jupiter.api.Assertions.assertArrayEquals;
+import static org.junit.jupiter.api.Assertions.assertNotEquals;
+
+import org.apache.logging.log4j.core.Filter;
+import org.apache.logging.log4j.core.Filter.Result;
+import org.junit.jupiter.api.Test;
+
+public class CompositeFilterTest {
+
+    @Test
+    public void testConcatenation() {
+        final Filter a = DenyAllFilter.newBuilder().setOnMatch(Result.ACCEPT).build();
+        final Filter b = DenyAllFilter.newBuilder().setOnMatch(Result.NEUTRAL).build();
+        final Filter c = DenyAllFilter.newBuilder().setOnMatch(Result.DENY).build();
+        // The three values need to be distinguishable
+        assertNotEquals(a, b);
+        assertNotEquals(a,  c);
+        assertNotEquals(b, c);
+        final Filter[] expected = new Filter[] {a, b, c};
+        final CompositeFilter singleA = CompositeFilter.createFilters(new Filter[] {a});
+        final CompositeFilter singleB = CompositeFilter.createFilters(new Filter[] {b});
+        final CompositeFilter singleC = CompositeFilter.createFilters(new Filter[] {c});
+        // Concatenating one at a time
+        final CompositeFilter concat1 = singleA.addFilter(b).addFilter(c);
+        assertArrayEquals(expected, concat1.getFiltersArray());
+        // In reverse order
+        final CompositeFilter concat2 = singleA.addFilter(singleB.addFilter(singleC));
+        assertArrayEquals(expected, concat2.getFiltersArray());
+    }
+}
diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/filter/CompositeFilter.java b/log4j-core/src/main/java/org/apache/logging/log4j/core/filter/CompositeFilter.java
index 4ac1a3f..5b870b4 100644
--- a/log4j-core/src/main/java/org/apache/logging/log4j/core/filter/CompositeFilter.java
+++ b/log4j-core/src/main/java/org/apache/logging/log4j/core/filter/CompositeFilter.java
@@ -63,9 +63,9 @@ public final class CompositeFilter extends AbstractLifeCycle implements Iterable
         if (filter instanceof CompositeFilter) {
             final int size = this.filters.length + ((CompositeFilter) filter).size();
             final Filter[] copy = Arrays.copyOf(this.filters, size);
-            final int index = this.filters.length;
+            int index = this.filters.length;
             for (final Filter currentFilter : ((CompositeFilter) filter).filters) {
-                copy[index] = currentFilter;
+                copy[index++] = currentFilter;
             }
             return new CompositeFilter(copy);
         }

[logging-log4j2] 20/25: Log4j 1.2 bridge missing UtilLoggingLevel.

Posted by pk...@apache.org.
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 adda4769c6bd38f016a8d93af86d6cb92d1271b0
Author: Gary Gregory <ga...@gmail.com>
AuthorDate: Sun Feb 6 08:38:26 2022 -0500

    Log4j 1.2 bridge missing UtilLoggingLevel.
---
 .../org/apache/log4j/helpers/UtilLoggingLevel.java | 236 +++++++++++++++++++++
 .../apache/log4j/helpers/UtilLoggingLevelTest.java |  45 ++++
 src/changes/changes.xml                            |   3 +
 3 files changed, 284 insertions(+)

diff --git a/log4j-1.2-api/src/main/java/org/apache/log4j/helpers/UtilLoggingLevel.java b/log4j-1.2-api/src/main/java/org/apache/log4j/helpers/UtilLoggingLevel.java
new file mode 100644
index 0000000..9917c63
--- /dev/null
+++ b/log4j-1.2-api/src/main/java/org/apache/log4j/helpers/UtilLoggingLevel.java
@@ -0,0 +1,236 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.log4j.helpers;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.apache.log4j.Level;
+
+/**
+ * An extension of the Level class that provides support for java.util.logging Levels.
+ */
+public class UtilLoggingLevel extends Level {
+
+    /**
+     * Serialization version id.
+     */
+    private static final long serialVersionUID = 909301162611820211L;
+
+    /**
+     * Numerical value for SEVERE.
+     */
+    public static final int SEVERE_INT = 22000;
+    /**
+     * Numerical value for WARNING.
+     */
+    public static final int WARNING_INT = 21000;
+
+    // INFO level defined in parent as 20000..no need to redefine here
+
+    /**
+     * Numerical value for CONFIG.
+     */
+    public static final int CONFIG_INT = 14000;
+
+    /**
+     * Numerical value for FINE.
+     */
+    public static final int FINE_INT = 13000;
+    
+    /**
+     * Numerical value for FINER.
+     */
+    public static final int FINER_INT = 12000;
+    
+    /**
+     * Numerical value for FINEST.
+     */
+    public static final int FINEST_INT = 11000;
+    
+    /**
+     * Numerical value for UNKNOWN.
+     */
+    public static final int UNKNOWN_INT = 10000;
+
+    /**
+     * SEVERE.
+     */
+    public static final UtilLoggingLevel SEVERE = new UtilLoggingLevel(SEVERE_INT, "SEVERE", 0);
+    
+    /**
+     * WARNING.
+     */
+    public static final UtilLoggingLevel WARNING = new UtilLoggingLevel(WARNING_INT, "WARNING", 4);
+    
+    /**
+     * INFO.
+     */
+    // note: we've aligned the int values of the java.util.logging INFO level with log4j's level
+    public static final UtilLoggingLevel INFO = new UtilLoggingLevel(INFO_INT, "INFO", 5);
+    
+    /**
+     * CONFIG.
+     */
+    public static final UtilLoggingLevel CONFIG = new UtilLoggingLevel(CONFIG_INT, "CONFIG", 6);
+    
+    /**
+     * FINE.
+     */
+    public static final UtilLoggingLevel FINE = new UtilLoggingLevel(FINE_INT, "FINE", 7);
+    
+    /**
+     * FINER.
+     */
+    public static final UtilLoggingLevel FINER = new UtilLoggingLevel(FINER_INT, "FINER", 8);
+    
+    /**
+     * FINEST.
+     */
+    public static final UtilLoggingLevel FINEST = new UtilLoggingLevel(FINEST_INT, "FINEST", 9);
+
+    /**
+     * Create new instance.
+     *
+     * @param level numeric value for level.
+     * @param levelStr symbolic name for level.
+     * @param syslogEquivalent Equivalent syslog severity.
+     */
+    protected UtilLoggingLevel(final int level, final String levelStr, final int syslogEquivalent) {
+        super(level, levelStr, syslogEquivalent);
+    }
+
+    /**
+     * Convert an integer passed as argument to a level. If the conversion fails, then this method returns the specified
+     * default.
+     *
+     * @param val numeric value.
+     * @param defaultLevel level to be returned if no level matches numeric value.
+     * @return matching level or default level.
+     */
+    public static UtilLoggingLevel toLevel(final int val, final UtilLoggingLevel defaultLevel) {
+        switch (val) {
+        case SEVERE_INT:
+            return SEVERE;
+
+        case WARNING_INT:
+            return WARNING;
+
+        case INFO_INT:
+            return INFO;
+
+        case CONFIG_INT:
+            return CONFIG;
+
+        case FINE_INT:
+            return FINE;
+
+        case FINER_INT:
+            return FINER;
+
+        case FINEST_INT:
+            return FINEST;
+
+        default:
+            return defaultLevel;
+        }
+    }
+
+    /**
+     * Gets level matching numeric value.
+     *
+     * @param val numeric value.
+     * @return matching level or UtilLoggerLevel.FINEST if no match.
+     */
+    public static Level toLevel(final int val) {
+        return toLevel(val, FINEST);
+    }
+
+    /**
+     * Gets list of supported levels.
+     *
+     * @return list of supported levels.
+     */
+    public static List getAllPossibleLevels() {
+        final ArrayList<UtilLoggingLevel> list = new ArrayList<>();
+        list.add(FINE);
+        list.add(FINER);
+        list.add(FINEST);
+        list.add(INFO);
+        list.add(CONFIG);
+        list.add(WARNING);
+        list.add(SEVERE);
+        return list;
+    }
+
+    /**
+     * Get level with specified symbolic name.
+     *
+     * @param s symbolic name.
+     * @return matching level or Level.DEBUG if no match.
+     */
+    public static Level toLevel(final String s) {
+        return toLevel(s, Level.DEBUG);
+    }
+
+    /**
+     * Get level with specified symbolic name.
+     *
+     * @param sArg symbolic name.
+     * @param defaultLevel level to return if no match.
+     * @return matching level or defaultLevel if no match.
+     */
+    public static Level toLevel(final String sArg, final Level defaultLevel) {
+        if (sArg == null) {
+            return defaultLevel;
+        }
+
+        final String s = sArg.toUpperCase();
+
+        if (s.equals("SEVERE")) {
+            return SEVERE;
+        }
+
+        // if(s.equals("FINE")) return Level.FINE;
+        if (s.equals("WARNING")) {
+            return WARNING;
+        }
+
+        if (s.equals("INFO")) {
+            return INFO;
+        }
+
+        if (s.equals("CONFI")) {
+            return CONFIG;
+        }
+
+        if (s.equals("FINE")) {
+            return FINE;
+        }
+
+        if (s.equals("FINER")) {
+            return FINER;
+        }
+
+        if (s.equals("FINEST")) {
+            return FINEST;
+        }
+        return defaultLevel;
+    }
+
+}
diff --git a/log4j-1.2-api/src/test/java/org/apache/log4j/helpers/UtilLoggingLevelTest.java b/log4j-1.2-api/src/test/java/org/apache/log4j/helpers/UtilLoggingLevelTest.java
new file mode 100644
index 0000000..ad2868b
--- /dev/null
+++ b/log4j-1.2-api/src/test/java/org/apache/log4j/helpers/UtilLoggingLevelTest.java
@@ -0,0 +1,45 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.log4j.helpers;
+
+import junit.framework.*;
+
+
+/**
+ * Unit tests for UtilLoggingLevel.
+ */
+public class UtilLoggingLevelTest extends TestCase {
+
+    /**
+     * Create new instance of test.
+     *
+     * @param testName test name
+     */
+    public UtilLoggingLevelTest(final String testName) {
+        super(testName);
+    }
+
+    /**
+     * Test toLevel("fiNeSt").
+     */
+    public void testToLevelFINEST() {
+        assertSame(UtilLoggingLevel.FINEST, UtilLoggingLevel.toLevel("fiNeSt"));
+    }
+
+}
+
diff --git a/src/changes/changes.xml b/src/changes/changes.xml
index 61b691b..a8722bd 100644
--- a/src/changes/changes.xml
+++ b/src/changes/changes.xml
@@ -343,6 +343,9 @@
       <action dev="ggregory" type="fix" due-to="Gary Gregory">
         Log4j 1.2 bridge missed org.apache.log4j.pattern.NameAbbreviator.
       </action>
+      <action dev="ggregory" type="fix" due-to="Gary Gregory">
+        Log4j 1.2 bridge missing UtilLoggingLevel.
+      </action>
       <action dev="ggregory" type="fix">
         JndiManager reverts to 2.17.0 behavior: Read the system property for each call.
       </action>

[logging-log4j2] 03/25: Remove unused constants.

Posted by pk...@apache.org.
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 807e8452344c283e231f24048cfde9a46730e8ec
Author: Gary Gregory <ga...@gmail.com>
AuthorDate: Sat Feb 5 08:36:28 2022 -0500

    Remove unused constants.
    
    Conflicts:
    	log4j-1.2-api/src/main/java/org/apache/log4j/builders/filter/DenyAllFilterBuilder.java
    	log4j-1.2-api/src/main/java/org/apache/log4j/builders/filter/LevelMatchFilterBuilder.java
    	log4j-1.2-api/src/main/java/org/apache/log4j/builders/layout/SimpleLayoutBuilder.java
---
 .../org/apache/log4j/builders/filter/DenyAllFilterBuilder.java    | 8 ++------
 .../org/apache/log4j/builders/filter/LevelMatchFilterBuilder.java | 3 ---
 .../org/apache/log4j/builders/layout/SimpleLayoutBuilder.java     | 8 ++------
 3 files changed, 4 insertions(+), 15 deletions(-)

diff --git a/log4j-1.2-api/src/main/java/org/apache/log4j/builders/filter/DenyAllFilterBuilder.java b/log4j-1.2-api/src/main/java/org/apache/log4j/builders/filter/DenyAllFilterBuilder.java
index f15d6e5..e0b28b5 100644
--- a/log4j-1.2-api/src/main/java/org/apache/log4j/builders/filter/DenyAllFilterBuilder.java
+++ b/log4j-1.2-api/src/main/java/org/apache/log4j/builders/filter/DenyAllFilterBuilder.java
@@ -16,26 +16,22 @@
  */
 package org.apache.log4j.builders.filter;
 
+import static org.apache.log4j.builders.BuilderManager.CATEGORY;
+
 import org.apache.log4j.bridge.FilterWrapper;
 import org.apache.log4j.config.PropertiesConfiguration;
 import org.apache.log4j.spi.Filter;
 import org.apache.log4j.xml.XmlConfiguration;
-import org.apache.logging.log4j.Logger;
 import org.apache.logging.log4j.plugins.Plugin;
 import org.apache.logging.log4j.core.filter.DenyAllFilter;
-import org.apache.logging.log4j.status.StatusLogger;
 import org.w3c.dom.Element;
 
-import static org.apache.log4j.builders.BuilderManager.CATEGORY;
-
 /**
  * Build a Pattern Layout
  */
 @Plugin(name = "org.apache.log4j.varia.DenyAllFilter", category = CATEGORY)
 public class DenyAllFilterBuilder implements FilterBuilder {
 
-    private static final Logger LOGGER = StatusLogger.getLogger();
-
     @Override
     public Filter parseFilter(Element filterElement, XmlConfiguration config) {
         return new FilterWrapper(DenyAllFilter.newBuilder().build());
diff --git a/log4j-1.2-api/src/main/java/org/apache/log4j/builders/filter/LevelMatchFilterBuilder.java b/log4j-1.2-api/src/main/java/org/apache/log4j/builders/filter/LevelMatchFilterBuilder.java
index 218ae46..f7c1ce4 100644
--- a/log4j-1.2-api/src/main/java/org/apache/log4j/builders/filter/LevelMatchFilterBuilder.java
+++ b/log4j-1.2-api/src/main/java/org/apache/log4j/builders/filter/LevelMatchFilterBuilder.java
@@ -24,10 +24,8 @@ import org.apache.log4j.config.PropertiesConfiguration;
 import org.apache.log4j.spi.Filter;
 import org.apache.log4j.xml.XmlConfiguration;
 import org.apache.logging.log4j.Level;
-import org.apache.logging.log4j.Logger;
 import org.apache.logging.log4j.plugins.Plugin;
 import org.apache.logging.log4j.core.filter.LevelMatchFilter;
-import org.apache.logging.log4j.status.StatusLogger;
 import org.w3c.dom.Element;
 
 import java.util.Properties;
@@ -42,7 +40,6 @@ import static org.apache.log4j.xml.XmlConfiguration.VALUE_ATTR;
 @Plugin(name = "org.apache.log4j.varia.LevelMatchFilter", category = CATEGORY)
 public class LevelMatchFilterBuilder extends AbstractBuilder implements FilterBuilder {
 
-    private static final Logger LOGGER = StatusLogger.getLogger();
     private static final String LEVEL = "LevelToMatch";
     private static final String ACCEPT_ON_MATCH = "AcceptOnMatch";
 
diff --git a/log4j-1.2-api/src/main/java/org/apache/log4j/builders/layout/SimpleLayoutBuilder.java b/log4j-1.2-api/src/main/java/org/apache/log4j/builders/layout/SimpleLayoutBuilder.java
index d35beba..906f02a 100644
--- a/log4j-1.2-api/src/main/java/org/apache/log4j/builders/layout/SimpleLayoutBuilder.java
+++ b/log4j-1.2-api/src/main/java/org/apache/log4j/builders/layout/SimpleLayoutBuilder.java
@@ -16,28 +16,24 @@
  */
 package org.apache.log4j.builders.layout;
 
+import static org.apache.log4j.builders.BuilderManager.CATEGORY;
+
 import org.apache.log4j.Layout;
 import org.apache.log4j.bridge.LayoutWrapper;
 import org.apache.log4j.config.PropertiesConfiguration;
 import org.apache.log4j.xml.XmlConfiguration;
-import org.apache.logging.log4j.Logger;
 import org.apache.logging.log4j.plugins.Plugin;
 import org.apache.logging.log4j.core.layout.PatternLayout;
-import org.apache.logging.log4j.status.StatusLogger;
 import org.w3c.dom.Element;
 
 import java.util.Properties;
 
-import static org.apache.log4j.builders.BuilderManager.CATEGORY;
-
 /**
  * Build a Pattern Layout
  */
 @Plugin(name = "org.apache.log4j.SimpleLayout", category = CATEGORY)
 public class SimpleLayoutBuilder implements LayoutBuilder {
 
-    private static final Logger LOGGER = StatusLogger.getLogger();
-
     @Override
     public Layout parseLayout(Element layoutElement, XmlConfiguration config) {
         return new LayoutWrapper(PatternLayout.newBuilder()

[logging-log4j2] 25/25: FileAppenderTest: increase thread pool termination timeout

Posted by pk...@apache.org.
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 b0a74d7e589400932d6f43042b485796bc4328bb
Author: Piotr P. Karwasz <pi...@karwasz.org>
AuthorDate: Sun Mar 13 21:57:02 2022 +0100

    FileAppenderTest: increase thread pool termination timeout
    
    The thread pool termination timeout on MacOS might be too short.
    # Conflicts:
    #	log4j-core/src/test/java/org/apache/logging/log4j/core/appender/FileAppenderTest.java
---
 .../apache/logging/log4j/core/appender/FileAppenderTest.java   | 10 ++++++++--
 1 file changed, 8 insertions(+), 2 deletions(-)

diff --git a/log4j-core-test/src/test/java/org/apache/logging/log4j/core/appender/FileAppenderTest.java b/log4j-core-test/src/test/java/org/apache/logging/log4j/core/appender/FileAppenderTest.java
index b2667be..bb1d821 100644
--- a/log4j-core-test/src/test/java/org/apache/logging/log4j/core/appender/FileAppenderTest.java
+++ b/log4j-core-test/src/test/java/org/apache/logging/log4j/core/appender/FileAppenderTest.java
@@ -173,11 +173,17 @@ public class FileAppenderTest {
             threadPool.execute(runnable);
         }
         threadPool.shutdown();
-        assertTrue(
-                threadPool.awaitTermination(10, TimeUnit.SECONDS), "The thread pool has not shutdown: " + threadPool);
+        boolean stopped = false;
+        for (int i = 0; i < 20; i++) {
+            // intentional assignment
+            if (stopped = threadPool.awaitTermination(1, TimeUnit.SECONDS)) {
+                break;
+            }
+        }
         if (throwableRef.get() != null) {
             Throwables.rethrow(throwableRef.get());
         }
+        assertTrue(stopped, "The thread pool has not shutdown: " + threadPool);
         verifyFile(threadCount * logEventCount);
     }
 

[logging-log4j2] 17/25: Better error reporting.

Posted by pk...@apache.org.
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 e619cb02e8fe552456590de5e5ce180cbfcc36d4
Author: Gary Gregory <ga...@gmail.com>
AuthorDate: Sat Feb 5 18:15:44 2022 -0500

    Better error reporting.
---
 .../src/main/java/org/apache/log4j/config/PropertiesConfiguration.java  | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/log4j-1.2-api/src/main/java/org/apache/log4j/config/PropertiesConfiguration.java b/log4j-1.2-api/src/main/java/org/apache/log4j/config/PropertiesConfiguration.java
index d0ba733..405bd4b 100644
--- a/log4j-1.2-api/src/main/java/org/apache/log4j/config/PropertiesConfiguration.java
+++ b/log4j-1.2-api/src/main/java/org/apache/log4j/config/PropertiesConfiguration.java
@@ -609,7 +609,7 @@ public class PropertiesConfiguration extends Log4j1Configuration {
         try {
             return LoaderUtil.newInstanceOf(className);
         } catch (ReflectiveOperationException ex) {
-            LOGGER.error("Unable to create {} {} due to {}:{}", type, className, ex.getClass().getSimpleName(), ex.getMessage());
+            LOGGER.error("Unable to create {} {} due to {}:{}", type, className, ex.getClass().getSimpleName(), ex.getMessage(), ex);
             return null;
         }
     }