You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@logging.apache.org by gg...@apache.org on 2022/02/20 16:31:07 UTC

[logging-log4j2] branch release-2.x updated (a5b2574 -> 087a6fc)

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

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


    from a5b2574  Javadoc.
     new ae4cba2  Internal refactoring.
     new 087a6fc  Fill basic support in DOMConfigurator except for DOM-based APIs.

The 2 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:
 log4j-1.2-api/pom.xml                              |   7 +
 .../org/apache/log4j/builders/BuilderManager.java  | 125 +++---
 .../java/org/apache/log4j/xml/DOMConfigurator.java | 113 ++++-
 .../{SimpleLayout.java => xml/XMLWatchdog.java}    |  30 +-
 .../org/apache/log4j/xml/XmlConfiguration.java     |   6 +-
 .../log4j/util/AbsoluteDateAndTimeFilter.java}     |  19 +-
 .../org/apache/log4j/util/AbsoluteTimeFilter.java} |  18 +-
 .../java/org/apache/log4j/util/ControlFilter.java  |  47 ++
 .../log4j/util/EnhancedJunitTestRunnerFilter.java  |  58 +++
 .../log4j/util/EnhancedLineNumberFilter.java}      |  36 +-
 .../UtilLoggingLevelTest.java => util/Filter.java} |  32 +-
 .../java/org/apache/log4j/util/ISO8601Filter.java} |  19 +-
 .../apache/log4j/util/JunitTestRunnerFilter.java   |  54 +++
 .../org/apache/log4j/util/LineNumberFilter.java}   |  19 +-
 .../org/apache/log4j/util/RelativeTimeFilter.java} |  19 +-
 .../apache/log4j/util/SerializationTestHelper.java | 112 +++--
 .../org/apache/log4j/util/SunReflectFilter.java}   |  41 +-
 .../java/org/apache/log4j/util/Transformer.java    |  59 +++
 .../log4j/util/UnexpectedFormatException.java}     |  12 +-
 .../apache/log4j/util/XMLLineAttributeFilter.java} |  19 +-
 .../org/apache/log4j/util/XMLTimestampFilter.java} |  17 +-
 .../java/org/apache/log4j/xml/DOMTestCase.java     | 473 +++++++++++++++++++++
 .../log4j1-1.2.17/input/xml/DOMTestCase1.xml       |   4 +-
 23 files changed, 1043 insertions(+), 296 deletions(-)
 copy log4j-1.2-api/src/main/java/org/apache/log4j/{SimpleLayout.java => xml/XMLWatchdog.java} (62%)
 copy log4j-1.2-api/src/{main/java/org/apache/log4j/DefaultCategoryFactory.java => test/java/org/apache/log4j/util/AbsoluteDateAndTimeFilter.java} (64%)
 copy log4j-1.2-api/src/{main/java/org/apache/log4j/DefaultCategoryFactory.java => test/java/org/apache/log4j/util/AbsoluteTimeFilter.java} (66%)
 create mode 100644 log4j-1.2-api/src/test/java/org/apache/log4j/util/ControlFilter.java
 create mode 100644 log4j-1.2-api/src/test/java/org/apache/log4j/util/EnhancedJunitTestRunnerFilter.java
 copy log4j-1.2-api/src/{main/java/org/apache/log4j/CategoryKey.java => test/java/org/apache/log4j/util/EnhancedLineNumberFilter.java} (56%)
 copy log4j-1.2-api/src/test/java/org/apache/log4j/{helpers/UtilLoggingLevelTest.java => util/Filter.java} (56%)
 copy log4j-1.2-api/src/{main/java/org/apache/log4j/spi/DefaultRepositorySelector.java => test/java/org/apache/log4j/util/ISO8601Filter.java} (68%)
 create mode 100644 log4j-1.2-api/src/test/java/org/apache/log4j/util/JunitTestRunnerFilter.java
 copy log4j-1.2-api/src/{main/java/org/apache/log4j/DefaultCategoryFactory.java => test/java/org/apache/log4j/util/LineNumberFilter.java} (63%)
 copy log4j-1.2-api/src/{main/java/org/apache/log4j/DefaultCategoryFactory.java => test/java/org/apache/log4j/util/RelativeTimeFilter.java} (62%)
 copy log4j-1.2-api/src/{main/java/org/apache/log4j/CategoryKey.java => test/java/org/apache/log4j/util/SunReflectFilter.java} (51%)
 create mode 100644 log4j-1.2-api/src/test/java/org/apache/log4j/util/Transformer.java
 copy log4j-1.2-api/src/{main/java/org/apache/log4j/ProvisionNode.java => test/java/org/apache/log4j/util/UnexpectedFormatException.java} (76%)
 copy log4j-1.2-api/src/{main/java/org/apache/log4j/DefaultCategoryFactory.java => test/java/org/apache/log4j/util/XMLLineAttributeFilter.java} (61%)
 copy log4j-1.2-api/src/{main/java/org/apache/log4j/spi/DefaultRepositorySelector.java => test/java/org/apache/log4j/util/XMLTimestampFilter.java} (68%)
 create mode 100644 log4j-1.2-api/src/test/java/org/apache/log4j/xml/DOMTestCase.java

[logging-log4j2] 02/02: Fill basic support in DOMConfigurator except for DOM-based APIs.

Posted by gg...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

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

commit 087a6fcf434e047844cc6bede1795ff8d7fb6abc
Author: Gary Gregory <ga...@gmail.com>
AuthorDate: Sun Feb 20 11:31:04 2022 -0500

    Fill basic support in DOMConfigurator except for DOM-based APIs.
    
    Some internals mustn't be quite right, see @Disabled failing tests  in
    DOMTestCase.
---
 log4j-1.2-api/pom.xml                              |   7 +
 .../java/org/apache/log4j/xml/DOMConfigurator.java | 113 ++++-
 .../java/org/apache/log4j/xml/XMLWatchdog.java     |  37 ++
 .../org/apache/log4j/xml/XmlConfiguration.java     |   6 +-
 .../log4j/util/AbsoluteDateAndTimeFilter.java      |  36 ++
 .../org/apache/log4j/util/AbsoluteTimeFilter.java  |  35 ++
 .../java/org/apache/log4j/util/ControlFilter.java  |  47 ++
 .../log4j/util/EnhancedJunitTestRunnerFilter.java  |  58 +++
 .../log4j/util/EnhancedLineNumberFilter.java       |  42 ++
 .../test/java/org/apache/log4j/util/Filter.java    |  35 ++
 .../java/org/apache/log4j/util/ISO8601Filter.java  |  35 ++
 .../apache/log4j/util/JunitTestRunnerFilter.java   |  54 +++
 .../org/apache/log4j/util/LineNumberFilter.java    |  36 ++
 .../org/apache/log4j/util/RelativeTimeFilter.java  |  36 ++
 .../apache/log4j/util/SerializationTestHelper.java | 112 +++--
 .../org/apache/log4j/util/SunReflectFilter.java    |  43 ++
 .../java/org/apache/log4j/util/Transformer.java    |  59 +++
 .../log4j/util/UnexpectedFormatException.java      |  27 ++
 .../apache/log4j/util/XMLLineAttributeFilter.java  |  36 ++
 .../org/apache/log4j/util/XMLTimestampFilter.java  |  33 ++
 .../java/org/apache/log4j/xml/DOMTestCase.java     | 473 +++++++++++++++++++++
 .../log4j1-1.2.17/input/xml/DOMTestCase1.xml       |   4 +-
 22 files changed, 1280 insertions(+), 84 deletions(-)

diff --git a/log4j-1.2-api/pom.xml b/log4j-1.2-api/pom.xml
index 804fb92..f970643 100644
--- a/log4j-1.2-api/pom.xml
+++ b/log4j-1.2-api/pom.xml
@@ -104,6 +104,13 @@
       <artifactId>jackson-dataformat-xml</artifactId>
       <scope>test</scope>
     </dependency>
+    <!-- 1.2 tests -->
+    <dependency>
+      <groupId>oro</groupId>
+      <artifactId>oro</artifactId>
+      <version>2.0.8</version>
+      <scope>test</scope>
+    </dependency>    
   </dependencies>
   <build>
     <plugins>
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 494208e..e751653 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
@@ -16,64 +16,141 @@
  */
 package org.apache.log4j.xml;
 
+import java.io.ByteArrayInputStream;
+import java.io.IOException;
 import java.io.InputStream;
 import java.io.Reader;
+import java.io.StringWriter;
 import java.net.URL;
+import java.net.URLConnection;
+import java.nio.charset.StandardCharsets;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.Paths;
 import java.util.Properties;
 
 import javax.xml.parsers.FactoryConfigurationError;
 
+import org.apache.log4j.LogManager;
 import org.apache.log4j.config.PropertySetter;
+import org.apache.log4j.helpers.OptionConverter;
 import org.apache.log4j.spi.LoggerRepository;
+import org.apache.logging.log4j.core.LoggerContext;
+import org.apache.logging.log4j.core.config.Configuration;
+import org.apache.logging.log4j.core.config.ConfigurationSource;
+import org.apache.logging.log4j.core.config.Configurator;
+import org.apache.logging.log4j.core.util.IOUtils;
 import org.w3c.dom.Element;
 
 /**
+ * Use this class to initialize the log4j environment using a DOM tree.
  *
+ * <p>
+ * The DTD is specified in <a href="doc-files/log4j.dtd"><b>log4j.dtd</b></a>.
+ *
+ * <p>
+ * Sometimes it is useful to see how log4j is reading configuration files. You can enable log4j internal logging by
+ * defining the <b>log4j.debug</b> variable on the java command line. Alternatively, set the <code>debug</code>
+ * attribute in the <code>log4j:configuration</code> element. As in
+ *
+ * <pre>
+&lt;log4j:configuration <b>debug="true"</b> xmlns:log4j="http://jakarta.apache.org/log4j/">
+...
+&lt;/log4j:configuration>
+ * </pre>
+ *
+ * <p>
+ * There are sample XML files included in the package.
+ *
+ * @since 0.8.3
  */
 public class DOMConfigurator {
 
-    public void doConfigure(final String filename, final LoggerRepository repository) {
+    public static void configure(final Element element) {
     }
 
-    public void doConfigure(final URL url, final LoggerRepository repository) {
+    public static void configure(final String fileName) throws FactoryConfigurationError {
+        final Path path = Paths.get(fileName);
+        try (final InputStream inputStream = Files.newInputStream(path)) {
+            final ConfigurationSource source = new ConfigurationSource(inputStream, path);
+            final LoggerContext context = (LoggerContext) org.apache.logging.log4j.LogManager.getContext(false);
+            Configuration configuration;
+            configuration = new XmlConfigurationFactory().getConfiguration(context, source);
+            LogManager.getRootLogger().removeAllAppenders();
+            Configurator.reconfigure(configuration);
+        } catch (final IOException e) {
+            throw new FactoryConfigurationError(e);
+        }
     }
 
-    public void doConfigure(final InputStream inputStream, final LoggerRepository repository)
-        throws FactoryConfigurationError {
+    public static void configure(final URL url) throws FactoryConfigurationError {
+        new DOMConfigurator().doConfigure(url, LogManager.getLoggerRepository());
     }
 
-    public void doConfigure(final Reader reader, final LoggerRepository repository)
-        throws FactoryConfigurationError {
+    public static void configureAndWatch(final String fileName) {
+        // TODO Watch
+        configure(fileName);
     }
 
-    public void doConfigure(final Element element, final LoggerRepository repository) {
+    public static void configureAndWatch(final String fileName, final long delay) {
+        XMLWatchdog xdog = new XMLWatchdog(fileName);
+        xdog.setDelay(delay);
+        xdog.start();
     }
 
-    public static void configure(final Element element) {
+    public static Object parseElement(final Element element, final Properties props, @SuppressWarnings("rawtypes") final Class expectedClass) {
+        return null;
     }
 
-    public static void configureAndWatch(final String configFilename) {
+    public static void setParameter(final Element elem, final PropertySetter propSetter, final Properties props) {
+
     }
 
-    public static void configureAndWatch(final String configFilename, final long delay) {
+    public static String subst(final String value, final Properties props) {
+        return OptionConverter.substVars(value, props);
     }
 
-    public static void configure(final String filename) throws FactoryConfigurationError {
+    private void doConfigure(final ConfigurationSource source) {
+        final LoggerContext context = (LoggerContext) org.apache.logging.log4j.LogManager.getContext(false);
+        Configuration configuration;
+        configuration = new XmlConfigurationFactory().getConfiguration(context, source);
+        Configurator.reconfigure(configuration);
     }
 
-    public static void configure(final URL url) throws FactoryConfigurationError {
+    public void doConfigure(final Element element, final LoggerRepository repository) {
     }
 
-    public static String subst(final String value, final Properties props) {
-        return value;
+    public void doConfigure(final InputStream inputStream, final LoggerRepository repository) throws FactoryConfigurationError {
+        try {
+            doConfigure(new ConfigurationSource(inputStream));
+        } catch (final IOException e) {
+            throw new FactoryConfigurationError(e);
+        }
     }
 
-    public static void setParameter(final Element elem, final PropertySetter propSetter, final Properties props) {
+    public void doConfigure(final Reader reader, final LoggerRepository repository) throws FactoryConfigurationError {
+        try {
+            final StringWriter sw = new StringWriter();
+            IOUtils.copy(reader, sw);
+            doConfigure(new ConfigurationSource(new ByteArrayInputStream(sw.toString().getBytes(StandardCharsets.UTF_8))));
+        } catch (final IOException e) {
+            throw new FactoryConfigurationError(e);
+        }
+    }
 
+    public void doConfigure(final String fileName, final LoggerRepository repository) {
+        configure(fileName);
     }
 
-    public static Object parseElement(final Element element, final Properties props,
-                                      @SuppressWarnings("rawtypes") final Class expectedClass) {
-        return null;
+    public void doConfigure(final URL url, final LoggerRepository repository) {
+        try {
+            final URLConnection connection = url.openConnection();
+            connection.setUseCaches(false); // Otherwise, a "jar:" URL file remains open after the stream is closed.
+            try (InputStream inputStream = connection.getInputStream()) {
+                doConfigure(new ConfigurationSource(inputStream, url));
+            }
+        } catch (final IOException e) {
+            throw new FactoryConfigurationError(e);
+        }
     }
 }
diff --git a/log4j-1.2-api/src/main/java/org/apache/log4j/xml/XMLWatchdog.java b/log4j-1.2-api/src/main/java/org/apache/log4j/xml/XMLWatchdog.java
new file mode 100644
index 0000000..753bd8b
--- /dev/null
+++ b/log4j-1.2-api/src/main/java/org/apache/log4j/xml/XMLWatchdog.java
@@ -0,0 +1,37 @@
+/*
+ * 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.LogManager;
+import org.apache.log4j.helpers.FileWatchdog;
+import org.apache.log4j.spi.LoggerRepository;
+
+class XMLWatchdog extends FileWatchdog {
+
+    XMLWatchdog(final String filename) {
+        super(filename);
+    }
+
+    /**
+     * Calls {@link DOMConfigurator#doConfigure(String, LoggerRepository)} with the <code>filename</code> to reconfigure Log4j.
+     */
+    @Override
+    public void doOnChange() {
+        new DOMConfigurator().doConfigure(filename, LogManager.getLoggerRepository());
+    }
+}
\ No newline at end of file
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 3fe95d4..d604310 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
@@ -751,14 +751,14 @@ public class XmlConfiguration extends Log4j1Configuration {
                     parseRoot(currentElement);
                     break;
                 case RENDERER_TAG:
-                    LOGGER.warn("Renderers are not supported by Log4j 2 and will be ignored.");
+                    LOGGER.warn("Log4j 1 renderers are not supported by Log4j 2 and will be ignored.");
                     break;
                 case THROWABLE_RENDERER_TAG:
-                    LOGGER.warn("Throwable Renderers are not supported by Log4j 2 and will be ignored.");
+                    LOGGER.warn("Log4j 1 throwable renderers are not supported by Log4j 2 and will be ignored.");
                     break;
                 case CATEGORY_FACTORY_TAG: 
                 case LOGGER_FACTORY_TAG:
-                    LOGGER.warn("Log4j 1 Logger factories are not supported by Log4j 2 and will be ignored.");
+                    LOGGER.warn("Log4j 1 logger factories are not supported by Log4j 2 and will be ignored.");
                     break;
                 case APPENDER_TAG:
                     Appender appender = parseAppender(currentElement);
diff --git a/log4j-1.2-api/src/test/java/org/apache/log4j/util/AbsoluteDateAndTimeFilter.java b/log4j-1.2-api/src/test/java/org/apache/log4j/util/AbsoluteDateAndTimeFilter.java
new file mode 100644
index 0000000..73f3c2b
--- /dev/null
+++ b/log4j-1.2-api/src/test/java/org/apache/log4j/util/AbsoluteDateAndTimeFilter.java
@@ -0,0 +1,36 @@
+/*
+ * 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 org.apache.oro.text.perl.Perl5Util;
+
+public class AbsoluteDateAndTimeFilter implements Filter {
+
+    Perl5Util util = new Perl5Util();
+
+    @Override
+    public String filter(final String in) {
+        final String pat = "/" + Filter.ABSOLUTE_DATE_AND_TIME_PAT + "/";
+
+        if (util.match(pat, in)) {
+            return util.substitute("s/" + Filter.ABSOLUTE_DATE_AND_TIME_PAT + "//", in);
+        } else {
+            return in;
+        }
+    }
+}
diff --git a/log4j-1.2-api/src/test/java/org/apache/log4j/util/AbsoluteTimeFilter.java b/log4j-1.2-api/src/test/java/org/apache/log4j/util/AbsoluteTimeFilter.java
new file mode 100644
index 0000000..4528e8a
--- /dev/null
+++ b/log4j-1.2-api/src/test/java/org/apache/log4j/util/AbsoluteTimeFilter.java
@@ -0,0 +1,35 @@
+/*
+ * 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 org.apache.oro.text.perl.Perl5Util;
+
+public class AbsoluteTimeFilter implements Filter {
+
+    Perl5Util util = new Perl5Util();
+
+    @Override
+    public String filter(final String in) {
+        final String pat = "/" + Filter.ABSOLUTE_TIME_PAT + "/";
+
+        if (util.match(pat, in)) {
+            return util.substitute("s/" + Filter.ABSOLUTE_TIME_PAT + "//", in);
+        }
+        return in;
+    }
+}
diff --git a/log4j-1.2-api/src/test/java/org/apache/log4j/util/ControlFilter.java b/log4j-1.2-api/src/test/java/org/apache/log4j/util/ControlFilter.java
new file mode 100644
index 0000000..325d460
--- /dev/null
+++ b/log4j-1.2-api/src/test/java/org/apache/log4j/util/ControlFilter.java
@@ -0,0 +1,47 @@
+/*
+ * 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.util.Arrays;
+
+import org.apache.oro.text.perl.Perl5Util;
+
+public class ControlFilter implements Filter {
+
+    Perl5Util util = new Perl5Util();
+
+    String[] allowedPatterns;
+
+    public ControlFilter(final String[] allowedPatterns) {
+        this.allowedPatterns = allowedPatterns;
+    }
+
+    @Override
+    public String filter(final String in) throws UnexpectedFormatException {
+        final int len = allowedPatterns.length;
+        for (int i = 0; i < len; i++) {
+            // System.out.println("["+allowedPatterns[i]+"]");
+            if (util.match("/" + allowedPatterns[i] + "/", in)) {
+                // System.out.println("["+in+"] matched ["+allowedPatterns[i]);
+                return in;
+            }
+        }
+
+        throw new UnexpectedFormatException("[" + in + "] allowedPatterns = " + Arrays.toString(allowedPatterns));
+    }
+}
diff --git a/log4j-1.2-api/src/test/java/org/apache/log4j/util/EnhancedJunitTestRunnerFilter.java b/log4j-1.2-api/src/test/java/org/apache/log4j/util/EnhancedJunitTestRunnerFilter.java
new file mode 100644
index 0000000..172d317
--- /dev/null
+++ b/log4j-1.2-api/src/test/java/org/apache/log4j/util/EnhancedJunitTestRunnerFilter.java
@@ -0,0 +1,58 @@
+/*
+ * 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 org.apache.oro.text.perl.Perl5Util;
+
+public class EnhancedJunitTestRunnerFilter implements Filter {
+    private static final String[] PATTERNS = {"at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner", "at org.apache.tools.ant",
+        "at junit.textui.TestRunner", "at com.intellij.rt.execution.junit", "at java.lang.reflect.Method.invoke", "at org.apache.maven.", "at org.codehaus.",
+        "at org.junit.internal.runners.", "at junit.framework.JUnit4TestAdapter"};
+
+    private final Perl5Util util = new Perl5Util();
+
+    public EnhancedJunitTestRunnerFilter() {
+    }
+
+    /**
+     * Filter out stack trace lines coming from the various JUnit TestRunners.
+     */
+    @Override
+    public String filter(final String in) {
+        if (in == null) {
+            return null;
+        }
+
+        //
+        // restore the one instance of Method.invoke that we actually want
+        //
+        if (in.indexOf("at junit.framework.TestCase.runTest") != -1) {
+            return "\tat java.lang.reflect.Method.invoke(X)\n\t" + in.trim();
+        }
+
+        for (final String element : PATTERNS) {
+            if (in.indexOf(element) != -1) {
+                return null;
+            }
+        }
+        if (util.match("/\\sat /", in)) {
+            return "\t" + in.trim();
+        }
+        return in;
+    }
+}
diff --git a/log4j-1.2-api/src/test/java/org/apache/log4j/util/EnhancedLineNumberFilter.java b/log4j-1.2-api/src/test/java/org/apache/log4j/util/EnhancedLineNumberFilter.java
new file mode 100644
index 0000000..b112a03
--- /dev/null
+++ b/log4j-1.2-api/src/test/java/org/apache/log4j/util/EnhancedLineNumberFilter.java
@@ -0,0 +1,42 @@
+/*
+ * 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.util.regex.Pattern;
+
+public class EnhancedLineNumberFilter implements Filter {
+    private final Pattern linePattern;
+    private final Pattern nativePattern;
+
+    public EnhancedLineNumberFilter() {
+        linePattern = Pattern.compile("\\(.*:\\d{1,4}\\)");
+        nativePattern = Pattern.compile("\\(Native Method\\)");
+    }
+
+    @Override
+    public String filter(final String in) {
+
+        if (linePattern.matcher(in).find()) {
+            return linePattern.matcher(in).replaceAll("(X)");
+        } else if (nativePattern.matcher(in).find()) {
+            return nativePattern.matcher(in).replaceAll("(X)");
+        } else {
+            return in;
+        }
+    }
+}
diff --git a/log4j-1.2-api/src/test/java/org/apache/log4j/util/Filter.java b/log4j-1.2-api/src/test/java/org/apache/log4j/util/Filter.java
new file mode 100644
index 0000000..16bb43d
--- /dev/null
+++ b/log4j-1.2-api/src/test/java/org/apache/log4j/util/Filter.java
@@ -0,0 +1,35 @@
+/*
+ * 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;
+
+public interface Filter {
+
+    final String BASIC_PAT = "\\[main\\] (FATAL|ERROR|WARN|INFO|DEBUG)";
+    final String ISO8601_PAT = "^\\d{4}-\\d{2}-\\d{2} \\d{2}:\\d{2}:\\d{2},\\d{3}";
+
+    // 06 avr. 2002 18:36:32,036
+    // 18 fevr. 2002 20:05:36,222
+    static public final String ABSOLUTE_DATE_AND_TIME_PAT = "^\\d{1,2} .{2,6}\\.? 2\\d{3} \\d{2}:\\d{2}:\\d{2},\\d{3}";
+
+    // 18:54:19,201
+    static public final String ABSOLUTE_TIME_PAT = "^\\d{2}:\\d{2}:\\d{2},\\d{3}";
+
+    static public final String RELATIVE_TIME_PAT = "^\\d{1,10}";
+
+    String filter(String in) throws UnexpectedFormatException;
+}
diff --git a/log4j-1.2-api/src/test/java/org/apache/log4j/util/ISO8601Filter.java b/log4j-1.2-api/src/test/java/org/apache/log4j/util/ISO8601Filter.java
new file mode 100644
index 0000000..47232ad
--- /dev/null
+++ b/log4j-1.2-api/src/test/java/org/apache/log4j/util/ISO8601Filter.java
@@ -0,0 +1,35 @@
+/*
+ * 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 org.apache.oro.text.perl.Perl5Util;
+
+public class ISO8601Filter implements Filter {
+
+    Perl5Util util = new Perl5Util();
+
+    @Override
+    public String filter(final String in) {
+        final String pat = "/" + ISO8601_PAT + "/";
+
+        if (util.match(pat, in)) {
+            return util.substitute("s/" + ISO8601_PAT + "//", in);
+        }
+        return in;
+    }
+}
diff --git a/log4j-1.2-api/src/test/java/org/apache/log4j/util/JunitTestRunnerFilter.java b/log4j-1.2-api/src/test/java/org/apache/log4j/util/JunitTestRunnerFilter.java
new file mode 100644
index 0000000..175224a
--- /dev/null
+++ b/log4j-1.2-api/src/test/java/org/apache/log4j/util/JunitTestRunnerFilter.java
@@ -0,0 +1,54 @@
+/*
+ * 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 org.apache.oro.text.perl.Perl5Util;
+
+public class JunitTestRunnerFilter implements Filter {
+    Perl5Util util = new Perl5Util();
+
+    /**
+     * Filter out stack trace lines coming from the various JUnit TestRunners.
+     */
+    @Override
+    public String filter(final String in) {
+        if (in == null) {
+            return null;
+        }
+
+        if (util.match("/at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner/", in)) {
+            return null;
+        } else if (util.match("/at org.apache.tools.ant.taskdefs.optional.junit.JUnitTestRunner/", in)) {
+            return null;
+        } else if (util.match("/at com.intellij/", in)) {
+            return null;
+        } else if (in.indexOf("at junit.") >= 0 && in.indexOf("ui.TestRunner") >= 0) {
+            return null;
+        } else if (in.indexOf("org.apache.maven") >= 0) {
+            return null;
+        } else if (in.indexOf("junit.internal") >= 0) {
+            return null;
+        } else if (in.indexOf("JUnit4TestAdapter") >= 0) {
+            return null;
+        } else if (util.match("/\\sat /", in)) {
+            return "\t" + in.trim();
+        } else {
+            return in;
+        }
+    }
+}
diff --git a/log4j-1.2-api/src/test/java/org/apache/log4j/util/LineNumberFilter.java b/log4j-1.2-api/src/test/java/org/apache/log4j/util/LineNumberFilter.java
new file mode 100644
index 0000000..45c6578
--- /dev/null
+++ b/log4j-1.2-api/src/test/java/org/apache/log4j/util/LineNumberFilter.java
@@ -0,0 +1,36 @@
+/*
+ * 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 org.apache.oro.text.perl.Perl5Util;
+
+public class LineNumberFilter implements Filter {
+
+    Perl5Util util = new Perl5Util();
+
+    @Override
+    public String filter(final String in) {
+        if (util.match("/\\(.*:\\d{1,4}\\)/", in)) {
+            return util.substitute("s/:\\d{1,4}\\)/:XXX)/", in);
+        }
+        if (in.indexOf(", Compiled Code") >= 0) {
+            return util.substitute("s/, Compiled Code/:XXX/", in);
+        }
+        return in;
+    }
+}
diff --git a/log4j-1.2-api/src/test/java/org/apache/log4j/util/RelativeTimeFilter.java b/log4j-1.2-api/src/test/java/org/apache/log4j/util/RelativeTimeFilter.java
new file mode 100644
index 0000000..a15ffb1
--- /dev/null
+++ b/log4j-1.2-api/src/test/java/org/apache/log4j/util/RelativeTimeFilter.java
@@ -0,0 +1,36 @@
+/*
+ * 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 org.apache.oro.text.perl.Perl5Util;
+
+public class RelativeTimeFilter implements Filter {
+
+    Perl5Util util = new Perl5Util();
+
+    @Override
+    public String filter(final String in) {
+        final String pat = "/" + Filter.RELATIVE_TIME_PAT + "/";
+
+        if (util.match(pat, in)) {
+            // System.out.println("Removing relative time from line ["+in+"]");
+            return util.substitute("s/" + Filter.RELATIVE_TIME_PAT + "//", in);
+        }
+        return in;
+    }
+}
diff --git a/log4j-1.2-api/src/test/java/org/apache/log4j/util/SerializationTestHelper.java b/log4j-1.2-api/src/test/java/org/apache/log4j/util/SerializationTestHelper.java
index 882a37a..a3584c2 100644
--- a/log4j-1.2-api/src/test/java/org/apache/log4j/util/SerializationTestHelper.java
+++ b/log4j-1.2-api/src/test/java/org/apache/log4j/util/SerializationTestHelper.java
@@ -30,65 +30,20 @@ import java.io.ObjectOutputStream;
 
 import org.apache.commons.io.FileUtils;
 
-
 /**
  * Utiities for serialization tests.
  */
 public class SerializationTestHelper {
     /**
-     * Private constructor.
-     */
-    private SerializationTestHelper() {
-    }
-
-    /**
-     * Creates a clone by serializing object and
-     * deserializing byte stream.
-     *
-     * @param obj object to serialize and deserialize.
-     * @return clone
-     * @throws IOException            on IO error.
-     * @throws ClassNotFoundException if class not found.
-     */
-    public static Object serializeClone(final Object obj)
-        throws IOException, ClassNotFoundException {
-        final ByteArrayOutputStream memOut = new ByteArrayOutputStream();
-        try (final ObjectOutputStream objOut = new ObjectOutputStream(memOut)) {
-            objOut.writeObject(obj);
-        }
-
-        final ByteArrayInputStream src = new ByteArrayInputStream(memOut.toByteArray());
-        final ObjectInputStream objIs = new ObjectInputStream(src);
-
-        return objIs.readObject();
-    }
-
-    /**
-     * Deserializes a specified file.
-     *
-     * @param witness serialization file, may not be null.
-     * @return deserialized object.
-     * @throws Exception thrown on IO or deserialization exception.
-     */
-    public static Object deserializeStream(final String witness) throws Exception {
-        try (final ObjectInputStream objIs = new ObjectInputStream(new FileInputStream(witness))) {
-            return objIs.readObject();
-        }
-    }
-
-    /**
-     * Checks the serialization of an object against an file
-     * containing the expected serialization.
+     * Checks the serialization of an object against an file containing the expected serialization.
      *
-     * @param witness    name of file containing expected serialization.
-     * @param obj        object to be serialized.
-     * @param skip       positions in serialized stream that should not be compared.
+     * @param witness name of file containing expected serialization.
+     * @param obj object to be serialized.
+     * @param skip positions in serialized stream that should not be compared.
      * @param endCompare position to stop comparison.
      * @throws Exception thrown on IO or serialization exception.
      */
-    public static void assertSerializationEquals(
-        final String witness, final Object obj, final int[] skip,
-        final int endCompare) throws Exception {
+    public static void assertSerializationEquals(final String witness, final Object obj, final int[] skip, final int endCompare) throws Exception {
         final ByteArrayOutputStream memOut = new ByteArrayOutputStream();
         try (final ObjectOutputStream objOut = new ObjectOutputStream(memOut)) {
             objOut.writeObject(obj);
@@ -100,15 +55,13 @@ public class SerializationTestHelper {
     /**
      * Asserts the serialized form of an object.
      *
-     * @param witness    file name of expected serialization.
-     * @param actual     byte array of actual serialization.
-     * @param skip       positions to skip comparison.
+     * @param witness file name of expected serialization.
+     * @param actual byte array of actual serialization.
+     * @param skip positions to skip comparison.
      * @param endCompare position to stop comparison.
      * @throws IOException thrown on IO or serialization exception.
      */
-    public static void assertStreamEquals(
-        final String witness, final byte[] actual, final int[] skip,
-        final int endCompare) throws IOException {
+    public static void assertStreamEquals(final String witness, final byte[] actual, final int[] skip, final int endCompare) throws IOException {
         final File witnessFile = new File(witness);
 
         if (witnessFile.exists()) {
@@ -130,17 +83,54 @@ public class SerializationTestHelper {
                 if ((skipIndex < skip.length) && (skip[skipIndex] == i)) {
                     skipIndex++;
                 } else if (expected[i] != actual[i]) {
-                    assertEquals(
-                        "Difference at offset " + i, expected[i], actual[i]);
+                    assertEquals("Difference at offset " + i, expected[i], actual[i]);
                 }
             }
         } else {
             //
-            //  if the file doesn't exist then
-            //      assume that we are setting up and need to write it
+            // if the file doesn't exist then
+            // assume that we are setting up and need to write it
             FileUtils.writeByteArrayToFile(witnessFile, actual);
             fail("Writing witness file " + witness);
         }
     }
-}
 
+    /**
+     * Deserializes a specified file.
+     *
+     * @param witness serialization file, may not be null.
+     * @return deserialized object.
+     * @throws Exception thrown on IO or deserialization exception.
+     */
+    public static Object deserializeStream(final String witness) throws Exception {
+        try (final ObjectInputStream objIs = new ObjectInputStream(new FileInputStream(witness))) {
+            return objIs.readObject();
+        }
+    }
+
+    /**
+     * Creates a clone by serializing object and deserializing byte stream.
+     *
+     * @param obj object to serialize and deserialize.
+     * @return clone
+     * @throws IOException on IO error.
+     * @throws ClassNotFoundException if class not found.
+     */
+    public static Object serializeClone(final Object obj) throws IOException, ClassNotFoundException {
+        final ByteArrayOutputStream memOut = new ByteArrayOutputStream();
+        try (final ObjectOutputStream objOut = new ObjectOutputStream(memOut)) {
+            objOut.writeObject(obj);
+        }
+
+        final ByteArrayInputStream src = new ByteArrayInputStream(memOut.toByteArray());
+        final ObjectInputStream objIs = new ObjectInputStream(src);
+
+        return objIs.readObject();
+    }
+
+    /**
+     * Private constructor.
+     */
+    private SerializationTestHelper() {
+    }
+}
diff --git a/log4j-1.2-api/src/test/java/org/apache/log4j/util/SunReflectFilter.java b/log4j-1.2-api/src/test/java/org/apache/log4j/util/SunReflectFilter.java
new file mode 100644
index 0000000..14c8e00
--- /dev/null
+++ b/log4j-1.2-api/src/test/java/org/apache/log4j/util/SunReflectFilter.java
@@ -0,0 +1,43 @@
+/*
+ * 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 org.apache.oro.text.perl.Perl5Util;
+
+/**
+ * The sun.reflect.* and java.lang.reflect.* lines are not present in all JDKs.
+ */
+public class SunReflectFilter implements Filter {
+    Perl5Util util = new Perl5Util();
+
+    @Override
+    public String filter(final String in) {
+        if ((in == null) || util.match("/at sun.reflect/", in) || (in.indexOf("at java.lang.reflect.") >= 0)) {
+            return null;
+        }
+        if (in.indexOf("Compiled Code") >= 0) {
+            if (in.indexOf("junit.framework.TestSuite") >= 0) {
+                return util.substitute("s/Compiled Code/TestSuite.java:XXX/", in);
+            }
+        }
+        if (util.match("/\\(Method.java:.*\\)/", in)) {
+            return util.substitute("s/\\(Method.java:.*\\)/(Native Method)/", in);
+        }
+        return in;
+    }
+}
diff --git a/log4j-1.2-api/src/test/java/org/apache/log4j/util/Transformer.java b/log4j-1.2-api/src/test/java/org/apache/log4j/util/Transformer.java
new file mode 100644
index 0000000..79d659e
--- /dev/null
+++ b/log4j-1.2-api/src/test/java/org/apache/log4j/util/Transformer.java
@@ -0,0 +1,59 @@
+/*
+ * 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.FileOutputStream;
+import java.io.FileReader;
+import java.io.IOException;
+import java.io.PrintStream;
+
+public class Transformer {
+
+    public static void transform(final String in, final String out, final Filter filter) throws IOException, UnexpectedFormatException {
+
+        String line;
+        final BufferedReader input = new BufferedReader(new FileReader(in));
+        final PrintStream output = new PrintStream(new FileOutputStream(out));
+
+        // Initialization of input and output omitted
+        while ((line = input.readLine()) != null) {
+            line = filter.filter(line);
+            output.println(line);
+        }
+    }
+
+    public static void transform(final String in, final String out, final Filter[] filters) throws IOException, UnexpectedFormatException {
+
+        String line;
+        final BufferedReader input = new BufferedReader(new FileReader(in));
+        final PrintStream output = new PrintStream(new FileOutputStream(out, false));
+
+        // Initialization of input and output omitted
+        while ((line = input.readLine()) != null) {
+            // apply all filters
+            for (final Filter filter : filters) {
+                line = filter.filter(line);
+            }
+            if (line != null) {
+                output.println(line);
+            }
+        }
+    }
+
+}
diff --git a/log4j-1.2-api/src/test/java/org/apache/log4j/util/UnexpectedFormatException.java b/log4j-1.2-api/src/test/java/org/apache/log4j/util/UnexpectedFormatException.java
new file mode 100644
index 0000000..ed25a35
--- /dev/null
+++ b/log4j-1.2-api/src/test/java/org/apache/log4j/util/UnexpectedFormatException.java
@@ -0,0 +1,27 @@
+/*
+ * 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;
+
+public class UnexpectedFormatException extends Exception {
+
+    private static final long serialVersionUID = 1787725660780924147L;
+
+    public UnexpectedFormatException(final String msg) {
+        super(msg);
+    }
+}
diff --git a/log4j-1.2-api/src/test/java/org/apache/log4j/util/XMLLineAttributeFilter.java b/log4j-1.2-api/src/test/java/org/apache/log4j/util/XMLLineAttributeFilter.java
new file mode 100644
index 0000000..9fcd070
--- /dev/null
+++ b/log4j-1.2-api/src/test/java/org/apache/log4j/util/XMLLineAttributeFilter.java
@@ -0,0 +1,36 @@
+/*
+ * 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 org.apache.oro.text.perl.Perl5Util;
+
+public class XMLLineAttributeFilter implements Filter {
+
+    Perl5Util util = new Perl5Util();
+
+    @Override
+    public String filter(final String in) {
+        if (util.match("/line=\"\\d{1,3}\"/", in)) {
+            return util.substitute("s/line=\"\\d{1,3}\"/line=\"X\"/", in);
+        } else if (util.match("/line=\"?\"/", in)) {
+            return util.substitute("s/line=\"?\"/line=\"X\"/", in);
+        } else {
+            return in;
+        }
+    }
+}
diff --git a/log4j-1.2-api/src/test/java/org/apache/log4j/util/XMLTimestampFilter.java b/log4j-1.2-api/src/test/java/org/apache/log4j/util/XMLTimestampFilter.java
new file mode 100644
index 0000000..5012831
--- /dev/null
+++ b/log4j-1.2-api/src/test/java/org/apache/log4j/util/XMLTimestampFilter.java
@@ -0,0 +1,33 @@
+/*
+ * 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 org.apache.oro.text.perl.Perl5Util;
+
+public class XMLTimestampFilter implements Filter {
+
+    Perl5Util util = new Perl5Util();
+
+    @Override
+    public String filter(final String in) {
+        if (util.match("/timestamp=\"\\d{10,13}\"/", in)) {
+            return util.substitute("s/timestamp=\"\\d{10,13}\"/timestamp=\"XXX\"/", in);
+        }
+        return in;
+    }
+}
diff --git a/log4j-1.2-api/src/test/java/org/apache/log4j/xml/DOMTestCase.java b/log4j-1.2-api/src/test/java/org/apache/log4j/xml/DOMTestCase.java
new file mode 100644
index 0000000..a01aa88
--- /dev/null
+++ b/log4j-1.2-api/src/test/java/org/apache/log4j/xml/DOMTestCase.java
@@ -0,0 +1,473 @@
+/*
+ * 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 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;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.URL;
+import java.util.zip.ZipEntry;
+import java.util.zip.ZipOutputStream;
+
+import org.apache.log4j.Appender;
+import org.apache.log4j.FileAppender;
+import org.apache.log4j.Level;
+import org.apache.log4j.LogManager;
+import org.apache.log4j.Logger;
+import org.apache.log4j.VectorAppender;
+import org.apache.log4j.bridge.AppenderWrapper;
+import org.apache.log4j.spi.ErrorHandler;
+import org.apache.log4j.spi.LoggerFactory;
+import org.apache.log4j.spi.LoggingEvent;
+import org.apache.log4j.spi.OptionHandler;
+import org.apache.log4j.spi.ThrowableRenderer;
+import org.apache.log4j.spi.ThrowableRendererSupport;
+import org.apache.log4j.util.Compare;
+import org.apache.log4j.util.ControlFilter;
+import org.apache.log4j.util.Filter;
+import org.apache.log4j.util.ISO8601Filter;
+import org.apache.log4j.util.JunitTestRunnerFilter;
+import org.apache.log4j.util.LineNumberFilter;
+import org.apache.log4j.util.SunReflectFilter;
+import org.apache.log4j.util.Transformer;
+import org.junit.jupiter.api.AfterEach;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Disabled;
+import org.junit.jupiter.api.Test;
+
+public class DOMTestCase {
+
+    /**
+     * CustomErrorHandler for testCategoryFactory2.
+     */
+    public static class CustomErrorHandler implements ErrorHandler {
+        public CustomErrorHandler() {
+        }
+
+        public void activateOptions() {
+        }
+
+        @Override
+        public void error(final String message) {
+        }
+
+        @Override
+        public void error(final String message, final Exception e, final int errorCode) {
+        }
+
+        @Override
+        public void error(final String message, final Exception e, final int errorCode, final LoggingEvent event) {
+        }
+
+        @Override
+        public void setAppender(final Appender appender) {
+        }
+
+        @Override
+        public void setBackupAppender(final Appender appender) {
+        }
+
+        @Override
+        public void setLogger(final Logger logger) {
+        }
+    }
+
+    /**
+     * CustomLogger implementation for testCategoryFactory1 and 2.
+     */
+    private static class CustomLogger extends Logger {
+        /**
+         * Creates new instance.
+         *
+         * @param name logger name.
+         */
+        public CustomLogger(final String name) {
+            super(name);
+        }
+    }
+
+    /**
+     * Creates new instances of CustomLogger.
+     */
+    public static class CustomLoggerFactory implements LoggerFactory {
+
+        /**
+         * Additivity, expected to be set false in configuration file.
+         */
+        private boolean additivity;
+
+        /**
+         * Create new instance of factory.
+         */
+        public CustomLoggerFactory() {
+            additivity = true;
+        }
+
+        /**
+         * Create new logger.
+         *
+         * @param name logger name.
+         * @return new logger.
+         */
+        @Override
+        public Logger makeNewLoggerInstance(final String name) {
+            final Logger logger = new CustomLogger(name);
+            assertFalse(additivity);
+            return logger;
+        }
+
+        /**
+         * Set additivity.
+         *
+         * @param newVal new value of additivity.
+         */
+        public void setAdditivity(final boolean newVal) {
+            additivity = newVal;
+        }
+    }
+
+    /**
+     * Mock ThrowableRenderer for testThrowableRenderer. See bug 45721.
+     */
+    public static class MockThrowableRenderer implements ThrowableRenderer, OptionHandler {
+        private boolean activated = false;
+        private boolean showVersion = true;
+
+        public MockThrowableRenderer() {
+        }
+
+        @Override
+        public void activateOptions() {
+            activated = true;
+        }
+
+        @Override
+        public String[] doRender(final Throwable t) {
+            return new String[0];
+        }
+
+        public boolean getShowVersion() {
+            return showVersion;
+        }
+
+        public boolean isActivated() {
+            return activated;
+        }
+
+        public void setShowVersion(final boolean v) {
+            showVersion = v;
+        }
+    }
+
+    static String TEMP_A1 = "target/output/temp.A1";
+    static String TEMP_A2 = "target/output/temp.A2";
+    static String FILTERED_A1 = "target/output/filtered.A1";
+    static String FILTERED_A2 = "target/output/filtered.A2";
+    static String EXCEPTION1 = "java.lang.Exception: Just testing";
+    static String EXCEPTION2 = "\\s*at .*\\(.*\\)";
+    static String EXCEPTION3 = "\\s*at .*\\(Native Method\\)";
+    static String EXCEPTION4 = "\\s*at .*\\(.*Compiled Code\\)";
+    static String EXCEPTION5 = "\\s*at .*\\(.*libgcj.*\\)";
+    static String TEST1_1A_PAT = "(TRACE|DEBUG|INFO |WARN |ERROR|FATAL) \\w*\\.\\w* - Message \\d";
+    static String TEST1_1B_PAT = "(TRACE|DEBUG|INFO |WARN |ERROR|FATAL) root - Message \\d";
+    static String TEST1_2_PAT = "^\\d{4}-\\d{2}-\\d{2} \\d{2}:\\d{2}:\\d{2},\\d{3} " + "\\[main]\\ (TRACE|DEBUG|INFO|WARN|ERROR|FATAL) .* - Message \\d";
+
+    private static final boolean Log4j1ActualAppender = false;
+
+    private static final boolean AssumeThrowableRendererSupport = false;
+
+    Logger root;
+
+    Logger logger;
+
+    void common() {
+        final String oldThreadName = Thread.currentThread().getName();
+        Thread.currentThread().setName("main");
+
+        int i = -1;
+
+        logger.trace("Message " + ++i);
+        root.trace("Message " + i);
+
+        logger.debug("Message " + ++i);
+        root.debug("Message " + i);
+
+        logger.info("Message " + ++i);
+        root.info("Message " + i);
+
+        logger.warn("Message " + ++i);
+        root.warn("Message " + i);
+
+        logger.error("Message " + ++i);
+        root.error("Message " + i);
+
+        logger.log(Level.FATAL, "Message " + ++i);
+        root.log(Level.FATAL, "Message " + i);
+
+        final Exception e = new Exception("Just testing");
+        logger.debug("Message " + ++i, e);
+        root.debug("Message " + i, e);
+
+        logger.error("Message " + ++i, e);
+        root.error("Message " + i, e);
+
+        Thread.currentThread().setName(oldThreadName);
+    }
+
+    @BeforeEach
+    public void setUp() {
+        root = Logger.getRootLogger();
+        logger = Logger.getLogger(DOMTestCase.class);
+    }
+
+    @AfterEach
+    public void tearDown() {
+        root.getLoggerRepository().resetConfiguration();
+    }
+
+    @Test
+    @Disabled
+    public void test1() throws Exception {
+        DOMConfigurator.configure("src/test/resources/log4j1-1.2.17/input/xml/DOMTestCase1.xml");
+        common();
+
+        final ControlFilter cf1 = new ControlFilter(new String[] {TEST1_1A_PAT, TEST1_1B_PAT, EXCEPTION1, EXCEPTION2, EXCEPTION3, EXCEPTION4, EXCEPTION5});
+
+        final ControlFilter cf2 = new ControlFilter(new String[] {TEST1_2_PAT, EXCEPTION1, EXCEPTION2, EXCEPTION3, EXCEPTION4, EXCEPTION5});
+
+        Transformer.transform(TEMP_A1, FILTERED_A1, new Filter[] {cf1, new LineNumberFilter(), new SunReflectFilter(), new JunitTestRunnerFilter()});
+
+        Transformer.transform(TEMP_A2, FILTERED_A2,
+            new Filter[] {cf2, new LineNumberFilter(), new ISO8601Filter(), new SunReflectFilter(), new JunitTestRunnerFilter()});
+
+        assertTrue(Compare.compare(FILTERED_A1, "witness/dom.A1.1"));
+        assertTrue(Compare.compare(FILTERED_A2, "witness/dom.A2.1"));
+    }
+
+    /**
+     * Tests processing of external entities in XML file.
+     */
+    @Test
+    @Disabled
+    public void test4() throws Exception {
+        DOMConfigurator.configure("src/test/resources/log4j1-1.2.17/input/xml/DOMTest4.xml");
+        common();
+
+        final ControlFilter cf1 = new ControlFilter(new String[] {TEST1_1A_PAT, TEST1_1B_PAT, EXCEPTION1, EXCEPTION2, EXCEPTION3, EXCEPTION4, EXCEPTION5});
+
+        final ControlFilter cf2 = new ControlFilter(new String[] {TEST1_2_PAT, EXCEPTION1, EXCEPTION2, EXCEPTION3, EXCEPTION4, EXCEPTION5});
+
+        Transformer.transform(TEMP_A1 + ".4", FILTERED_A1 + ".4",
+            new Filter[] {cf1, new LineNumberFilter(), new SunReflectFilter(), new JunitTestRunnerFilter()});
+
+        Transformer.transform(TEMP_A2 + ".4", FILTERED_A2 + ".4",
+            new Filter[] {cf2, new LineNumberFilter(), new ISO8601Filter(), new SunReflectFilter(), new JunitTestRunnerFilter()});
+
+        assertTrue(Compare.compare(FILTERED_A1 + ".4", "witness/dom.A1.4"));
+        assertTrue(Compare.compare(FILTERED_A2 + ".4", "witness/dom.A2.4"));
+    }
+
+    /**
+     * Tests that loggers mentioned in logger elements use the specified categoryFactory. See bug 33708.
+     */
+    @Test
+    @Disabled
+    public void testCategoryFactory1() {
+        DOMConfigurator.configure("src/test/resources/log4j1-1.2.17/input/xml/categoryfactory1.xml");
+        //
+        // logger not explicitly mentioned in configuration,
+        // should use default factory
+        final Logger logger2 = Logger.getLogger("org.apache.log4j.xml.DOMTestCase.testCategoryFactory1.2");
+        assertFalse(logger2.toString(), logger2 instanceof CustomLogger);
+        //
+        // logger explicitly mentioned in configuration,
+        // should be a CustomLogger
+        final Logger logger1 = Logger.getLogger("org.apache.log4j.xml.DOMTestCase.testCategoryFactory1.1");
+        assertTrue(logger1.toString(), logger1 instanceof CustomLogger);
+        //
+        // logger not explicitly mentioned in configuration,
+        // should use default factory
+        final Logger logger2Bis = Logger.getLogger("org.apache.log4j.xml.DOMTestCase.testCategoryFactory1.2");
+        assertFalse(logger2Bis.toString(), logger2Bis instanceof CustomLogger);
+    }
+
+    /**
+     * Tests that loggers mentioned in logger-ref elements use the specified categoryFactory. See bug 33708.
+     */
+    @Test
+    @Disabled
+    public void testCategoryFactory2() {
+        DOMConfigurator.configure("src/test/resources/log4j1-1.2.17/input/xml/categoryfactory2.xml");
+        //
+        // logger not explicitly mentioned in configuration,
+        // should use default factory
+        final Logger logger2 = Logger.getLogger("org.apache.log4j.xml.DOMTestCase.testCategoryFactory2.2");
+        assertFalse(logger2.toString(), logger2 instanceof CustomLogger);
+        //
+        // logger explicitly mentioned in configuration,
+        // should be a CustomLogger
+        final Logger logger1 = Logger.getLogger("org.apache.log4j.xml.DOMTestCase.testCategoryFactory2.1");
+        assertTrue(logger1.toString(), logger1 instanceof CustomLogger);
+        //
+        // logger not explicitly mentioned in configuration,
+        // should use default factory
+        final Logger logger2Bis = Logger.getLogger("org.apache.log4j.xml.DOMTestCase.testCategoryFactory2.2");
+        assertFalse(logger2Bis.toString(), logger2Bis instanceof CustomLogger);
+    }
+
+    /**
+     * Test checks that configureAndWatch does initial configuration, see bug 33502.
+     *
+     * @throws Exception if IO error.
+     */
+    @Test
+    public void testConfigureAndWatch() throws Exception {
+        DOMConfigurator.configureAndWatch("src/test/resources/log4j1-1.2.17/input/xml/DOMTestCase1.xml");
+        assertNotNull(Logger.getRootLogger().getAppender("A1"));
+    }
+
+    /**
+     * Test for bug 47465. configure(URL) did not close opened JarURLConnection.
+     *
+     * @throws IOException if IOException creating properties jar.
+     */
+    @Test
+    public void testJarURL() throws IOException {
+        final File input = new File("src/test/resources/log4j1-1.2.17/input/xml/defaultInit.xml");
+        System.out.println(input.getAbsolutePath());
+        final File configJar = new File("target/output/xml.jar");
+        final File dir = new File("target/output");
+        dir.mkdirs();
+        try (final InputStream inputStream = new FileInputStream(input);
+            final FileOutputStream out = new FileOutputStream(configJar);
+            final ZipOutputStream zos = new ZipOutputStream(out)) {
+            zos.putNextEntry(new ZipEntry("log4j.xml"));
+            int len;
+            final byte[] buf = new byte[1024];
+            while ((len = inputStream.read(buf)) > 0) {
+                zos.write(buf, 0, len);
+            }
+            zos.closeEntry();
+        }
+        final URL urlInJar = new URL("jar:" + configJar.toURL() + "!/log4j.xml");
+        DOMConfigurator.configure(urlInJar);
+        assertTrue(configJar.delete());
+        assertFalse(configJar.exists());
+    }
+
+    /**
+     * Tests that loggers mentioned in logger elements use the specified loggerFactory. See bug 33708.
+     */
+    @Test
+    @Disabled("TODO")
+    public void testLoggerFactory1() {
+        DOMConfigurator.configure("src/test/resources/log4j1-1.2.17/input/xml/loggerfactory1.xml");
+        //
+        // logger not explicitly mentioned in configuration,
+        // should use default factory
+        final Logger logger2 = Logger.getLogger("org.apache.log4j.xml.DOMTestCase.testLoggerFactory1.2");
+        assertNotNull(logger2);
+        assertFalse(logger2.toString(), logger2 instanceof CustomLogger);
+        //
+        // logger explicitly mentioned in configuration,
+        // should be a CustomLogger
+        final Logger logger1 = Logger.getLogger("org.apache.log4j.xml.DOMTestCase.testLoggerFactory1.1");
+        assertNotNull(logger1);
+        assertTrue(logger1.toString(), logger1 instanceof CustomLogger);
+        //
+        // logger not explicitly mentioned in configuration,
+        // should use default factory
+        final Logger logger2Bis = Logger.getLogger("org.apache.log4j.xml.DOMTestCase.testLoggerFactory1.2");
+        assertNotNull(logger2Bis);
+        assertFalse(logger2Bis.toString(), logger2Bis instanceof CustomLogger);
+    }
+
+    /**
+     * This test checks that the subst method of an extending class is checked when evaluating parameters. See bug 43325.
+     *
+     */
+    @Test
+    public void testOverrideSubst() {
+        final DOMConfigurator configurator = new DOMConfigurator() {
+            protected String subst(final String value) {
+                if ("target/output/temp.A1".equals(value)) {
+                    return "target/output/subst-test.A1";
+                }
+                return value;
+            }
+        };
+        configurator.doConfigure("src/test/resources/log4j1-1.2.17/input/xml/DOMTestCase1.xml", LogManager.getLoggerRepository());
+        final String name = "A1";
+        final Appender appender = Logger.getRootLogger().getAppender(name);
+        assertNotNull(name, appender);
+        if (Log4j1ActualAppender) {
+            final FileAppender a1 = (FileAppender) appender;
+            assertNotNull(name, a1);
+            final String file = a1.getFile();
+            assertEquals("target/output/subst-test.A1", file);
+        } else {
+            final AppenderWrapper wrapper = (AppenderWrapper) appender;
+            assertNotNull(name, wrapper);
+            final org.apache.logging.log4j.core.appender.FileAppender a1 = (org.apache.logging.log4j.core.appender.FileAppender) wrapper.getAppender();
+            assertNotNull(wrapper.toString(), a1);
+            final String file = a1.getFileName();
+            assertNotNull(a1.toString(), file);
+            // TODO Support this or not?
+            // assertEquals("target/output/subst-test.A1", file);
+        }
+    }
+
+    /**
+     * Tests that reset="true" on log4j:configuration element resets repository before configuration.
+     *
+     * @throws Exception thrown on error.
+     */
+    @Test
+    public void testReset() throws Exception {
+        final VectorAppender appender = new VectorAppender();
+        appender.setName("V1");
+        Logger.getRootLogger().addAppender(appender);
+        DOMConfigurator.configure("src/test/resources/log4j1-1.2.17/input/xml/testReset.xml");
+        assertNull(Logger.getRootLogger().getAppender("V1"));
+    }
+
+    /**
+     * Test of log4j.throwableRenderer support. See bug 45721.
+     */
+    @Test
+    public void testThrowableRenderer1() {
+        DOMConfigurator.configure("src/test/resources/log4j1-1.2.17/input/xml/throwableRenderer1.xml");
+        final ThrowableRendererSupport repo = (ThrowableRendererSupport) LogManager.getLoggerRepository();
+        final MockThrowableRenderer renderer = (MockThrowableRenderer) repo.getThrowableRenderer();
+        LogManager.resetConfiguration();
+        if (AssumeThrowableRendererSupport) {
+            assertNotNull(renderer);
+            assertEquals(true, renderer.isActivated());
+            assertEquals(false, renderer.getShowVersion());
+        }
+    }
+
+}
diff --git a/log4j-1.2-api/src/test/resources/log4j1-1.2.17/input/xml/DOMTestCase1.xml b/log4j-1.2-api/src/test/resources/log4j1-1.2.17/input/xml/DOMTestCase1.xml
index fcd1a7c..90917d1 100644
--- a/log4j-1.2-api/src/test/resources/log4j1-1.2.17/input/xml/DOMTestCase1.xml
+++ b/log4j-1.2-api/src/test/resources/log4j1-1.2.17/input/xml/DOMTestCase1.xml
@@ -20,7 +20,7 @@
 <log4j:configuration xmlns:log4j="http://jakarta.apache.org/log4j/">
   <appender name="A1" class="org.apache.log4j.FileAppender">
     
-    <param name="File"   value="output/temp.A1" />
+    <param name="File"   value="target/output/temp.A1" />
     <param name="Append" value="false" />	    
     
     <layout class="org.apache.log4j.PatternLayout">
@@ -29,7 +29,7 @@
   </appender>
   
   <appender name="A2" class="org.apache.log4j.FileAppender">
-    <param name="File" value="output/temp.A2" />
+    <param name="File" value="target/output/temp.A2" />
     <param name="Append" value="false" />
     <layout class="org.apache.log4j.TTCCLayout">
       <param name="DateFormat" value="ISO8601" />

[logging-log4j2] 01/02: Internal refactoring.

Posted by gg...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

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

commit ae4cba2d494330cddeb924d4f997cef82f42c001
Author: Gary Gregory <ga...@gmail.com>
AuthorDate: Sun Feb 20 11:18:53 2022 -0500

    Internal refactoring.
---
 .../org/apache/log4j/builders/BuilderManager.java  | 125 +++++++++------------
 1 file changed, 56 insertions(+), 69 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 7add1ff..60c9e68 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
@@ -20,6 +20,7 @@ import java.util.Locale;
 import java.util.Map;
 import java.util.Objects;
 import java.util.Properties;
+import java.util.function.Function;
 
 import org.apache.log4j.Appender;
 import org.apache.log4j.Layout;
@@ -55,121 +56,107 @@ public class BuilderManager {
     }
 
     @SuppressWarnings("unchecked")
-    private <T extends Builder> T createBuilder(PluginType<T> plugin, String prefix, Properties props) {
+    private <T extends Builder> T createBuilder(final PluginType<T> plugin, final String prefix, final Properties props) {
+        if (plugin == null) {
+            return null;
+        }
         try {
-            Class<T> clazz = plugin.getPluginClass();
+            final Class<T> clazz = plugin.getPluginClass();
             if (AbstractBuilder.class.isAssignableFrom(clazz)) {
                 return clazz.getConstructor(CONSTRUCTOR_PARAMS).newInstance(prefix, props);
             }
-            Object builder = LoaderUtil.newInstanceOf(clazz);
+            final Object builder = LoaderUtil.newInstanceOf(clazz);
             // Reasonable message instead of `ClassCastException`
             if (!Builder.class.isAssignableFrom(clazz)) {
                 LOGGER.warn("Unable to load plugin: builder {} does not implement {}", clazz, Builder.class);
                 return null;
             }
             return (T) builder;
-        } catch (ReflectiveOperationException ex) {
+        } catch (final ReflectiveOperationException ex) {
             LOGGER.warn("Unable to load plugin: {} due to: {}", plugin.getKey(), ex.getMessage());
             return null;
         }
     }
 
     @SuppressWarnings("unchecked")
-    private <T> PluginType<T> getPlugin(String className) {
+    private <T> PluginType<T> getPlugin(final String className) {
         Objects.requireNonNull(plugins, "plugins");
         Objects.requireNonNull(className, "className");
-        return (PluginType<T>) plugins.get(className.toLowerCase(Locale.ROOT).trim());
+        final String key = className.toLowerCase(Locale.ROOT).trim();
+        final PluginType<?> pluginType = plugins.get(key);
+        if (pluginType == null) {
+            LOGGER.warn("Unable to load plugin class name {} with key {}", className, key);
+        }
+        return (PluginType<T>) pluginType;
     }
 
-    public Appender parseAppender(String className, Element appenderElement, XmlConfiguration config) {
-        PluginType<AppenderBuilder> plugin = getPlugin(className);
+    private <T extends Builder, U> U newInstance(final PluginType<T> plugin, final Function<T, U> consumer) {
         if (plugin != null) {
             try {
-                return LoaderUtil.newInstanceOf(plugin.getPluginClass()).parseAppender(appenderElement, config);
-            } catch (ReflectiveOperationException ex) {
+                final T builder = LoaderUtil.newInstanceOf(plugin.getPluginClass());
+                if (builder != null) {
+                    return consumer.apply(builder);
+                }
+            } catch (final ReflectiveOperationException ex) {
                 LOGGER.warn("Unable to load plugin: {} due to: {}", plugin.getKey(), ex.getMessage());
             }
         }
         return null;
     }
 
-    public Appender parseAppender(String name, String className, String prefix, String layoutPrefix, String filterPrefix, Properties props,
-        PropertiesConfiguration config) {
-        PluginType<AppenderBuilder> plugin = getPlugin(className);
-        if (plugin != null) {
-            AppenderBuilder builder = createBuilder(plugin, prefix, props);
-            if (builder != null) {
-                return builder.parseAppender(name, prefix, layoutPrefix, filterPrefix, props, config);
-            }
-        }
-        return null;
+    public Appender parseAppender(final String className, final Element appenderElement, final XmlConfiguration config) {
+        final PluginType<AppenderBuilder> plugin = getPlugin(className);
+        return newInstance(plugin, b -> b.parseAppender(appenderElement, config));
     }
 
-    public Filter parseFilter(String className, Element filterElement, XmlConfiguration config) {
-        PluginType<FilterBuilder> plugin = getPlugin(className);
-        if (plugin != null) {
-            try {
-                return LoaderUtil.newInstanceOf(plugin.getPluginClass()).parseFilter(filterElement, config);
-            } catch (ReflectiveOperationException ex) {
-                LOGGER.warn("Unable to load plugin: {} due to: {}", plugin.getKey(), ex.getMessage());
-            }
+    public Appender parseAppender(final String name, final String className, final String prefix, final String layoutPrefix, final String filterPrefix, final Properties props,
+        final PropertiesConfiguration config) {
+        final PluginType<AppenderBuilder> plugin = getPlugin(className);
+        final AppenderBuilder builder = createBuilder(plugin, prefix, props);
+        if (builder != null) {
+            return builder.parseAppender(name, prefix, layoutPrefix, filterPrefix, props, config);
         }
         return null;
     }
 
-    public Filter parseFilter(String className, String filterPrefix, Properties props, PropertiesConfiguration config) {
-        PluginType<FilterBuilder> plugin = getPlugin(className);
-        if (plugin != null) {
-            FilterBuilder builder = createBuilder(plugin, filterPrefix, props);
-            if (builder != null) {
-                return builder.parseFilter(config);
-            }
-        }
-        return null;
+    public Filter parseFilter(final String className, final Element filterElement, final XmlConfiguration config) {
+        final PluginType<FilterBuilder> plugin = getPlugin(className);
+        return newInstance(plugin, b -> b.parseFilter(filterElement, config));
     }
 
-    public Layout parseLayout(String className, Element layoutElement, XmlConfiguration config) {
-        PluginType<LayoutBuilder> plugin = getPlugin(className);
-        if (plugin != null) {
-            try {
-                return LoaderUtil.newInstanceOf(plugin.getPluginClass()).parseLayout(layoutElement, config);
-            } catch (ReflectiveOperationException ex) {
-                LOGGER.warn("Unable to load plugin: {} due to: {}", plugin.getKey(), ex.getMessage());
-            }
+    public Filter parseFilter(final String className, final String filterPrefix, final Properties props, final PropertiesConfiguration config) {
+        final PluginType<FilterBuilder> plugin = getPlugin(className);
+        final FilterBuilder builder = createBuilder(plugin, filterPrefix, props);
+        if (builder != null) {
+            return builder.parseFilter(config);
         }
         return null;
     }
 
-    public Layout parseLayout(String className, String layoutPrefix, Properties props, PropertiesConfiguration config) {
-        PluginType<LayoutBuilder> plugin = getPlugin(className);
-        if (plugin != null) {
-            LayoutBuilder builder = createBuilder(plugin, layoutPrefix, props);
-            if (builder != null) {
-                return builder.parseLayout(config);
-            }
-        }
-        return null;
+    public Layout parseLayout(final String className, final Element layoutElement, final XmlConfiguration config) {
+        final PluginType<LayoutBuilder> plugin = getPlugin(className);
+        return newInstance(plugin, b -> b.parseLayout(layoutElement, config));
     }
 
-    public RewritePolicy parseRewritePolicy(String className, Element rewriteElement, XmlConfiguration config) {
-        PluginType<RewritePolicyBuilder> plugin = getPlugin(className);
-        if (plugin != null) {
-            try {
-                return LoaderUtil.newInstanceOf(plugin.getPluginClass()).parseRewritePolicy(rewriteElement, config);
-            } catch (ReflectiveOperationException ex) {
-                LOGGER.warn("Unable to load plugin: {} due to: {}", plugin.getKey(), ex.getMessage());
-            }
+    public Layout parseLayout(final String className, final String layoutPrefix, final Properties props, final PropertiesConfiguration config) {
+        final PluginType<LayoutBuilder> plugin = getPlugin(className);
+        final LayoutBuilder builder = createBuilder(plugin, layoutPrefix, props);
+        if (builder != null) {
+            return builder.parseLayout(config);
         }
         return null;
     }
 
-    public RewritePolicy parseRewritePolicy(String className, String policyPrefix, Properties props, PropertiesConfiguration config) {
-        PluginType<RewritePolicyBuilder> plugin = getPlugin(className);
-        if (plugin != null) {
-            RewritePolicyBuilder builder = createBuilder(plugin, policyPrefix, props);
-            if (builder != null) {
-                return builder.parseRewritePolicy(config);
-            }
+    public RewritePolicy parseRewritePolicy(final String className, final Element rewriteElement, final XmlConfiguration config) {
+        final PluginType<RewritePolicyBuilder> plugin = getPlugin(className);
+        return newInstance(plugin, b -> b.parseRewritePolicy(rewriteElement, config));
+    }
+
+    public RewritePolicy parseRewritePolicy(final String className, final String policyPrefix, final Properties props, final PropertiesConfiguration config) {
+        final PluginType<RewritePolicyBuilder> plugin = getPlugin(className);
+        final RewritePolicyBuilder builder = createBuilder(plugin, policyPrefix, props);
+        if (builder != null) {
+            return builder.parseRewritePolicy(config);
         }
         return null;
     }