LOG4J2-63 - Almost full support for Log4j 1 XML config files
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 a4c5231..1353dae 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
@@ -56,7 +56,7 @@
     }
 
     @Override
-    public void addFilter(Filter newFilter) {
+    public void addFilter(final Filter newFilter) {
         if(headFilter == null) {
             headFilter = tailFilter = newFilter;
         } else {
@@ -104,7 +104,7 @@
         return threshold;
     }
 
-    public boolean isAsSevereAsThreshold(Priority priority) {
+    public boolean isAsSevereAsThreshold(final Priority priority) {
         return ((threshold == null) || priority.isGreaterOrEqual(threshold));
     }
 
@@ -113,7 +113,8 @@
      * @param event The LoggingEvent.
      */
     @Override
-    public void doAppend(LoggingEvent event) {
+    public void doAppend(final LoggingEvent event) {
+        append(event);
     }
 
     /**
@@ -122,54 +123,54 @@
      * @since 0.9.0
      */
     @Override
-    public synchronized void setErrorHandler(ErrorHandler eh) {
+    public synchronized void setErrorHandler(final ErrorHandler eh) {
         if (eh != null) {
             this.errorHandler = eh;
         }
     }
 
     @Override
-    public void setLayout(Layout layout) {
+    public void setLayout(final Layout layout) {
         this.layout = layout;
     }
 
     @Override
-    public void setName(String name) {
+    public void setName(final String name) {
         this.name = name;
     }
 
-    public void setThreshold(Priority threshold) {
+    public void setThreshold(final Priority threshold) {
         this.threshold = threshold;
     }
 
     public static class NoOpErrorHandler implements ErrorHandler {
         @Override
-        public void setLogger(Logger logger) {
+        public void setLogger(final Logger logger) {
 
         }
 
         @Override
-        public void error(String message, Exception e, int errorCode) {
+        public void error(final String message, final Exception e, final int errorCode) {
 
         }
 
         @Override
-        public void error(String message) {
+        public void error(final String message) {
 
         }
 
         @Override
-        public void error(String message, Exception e, int errorCode, LoggingEvent event) {
+        public void error(final String message, final Exception e, final int errorCode, final LoggingEvent event) {
 
         }
 
         @Override
-        public void setAppender(Appender appender) {
+        public void setAppender(final Appender appender) {
 
         }
 
         @Override
-        public void setBackupAppender(Appender appender) {
+        public void setBackupAppender(final Appender appender) {
 
         }
     }
diff --git a/log4j-1.2-api/src/main/java/org/apache/log4j/Category.java b/log4j-1.2-api/src/main/java/org/apache/log4j/Category.java
index 1da4560..e0e5aef 100644
--- a/log4j-1.2-api/src/main/java/org/apache/log4j/Category.java
+++ b/log4j-1.2-api/src/main/java/org/apache/log4j/Category.java
@@ -25,10 +25,12 @@
 
 import org.apache.log4j.helpers.NullEnumeration;
 import org.apache.log4j.legacy.core.CategoryUtil;
+import org.apache.log4j.or.ObjectRenderer;
+import org.apache.log4j.or.RendererSupport;
 import org.apache.log4j.spi.LoggerFactory;
 import org.apache.log4j.spi.LoggingEvent;
+import org.apache.logging.log4j.spi.ExtendedLogger;
 import org.apache.logging.log4j.spi.LoggerContext;
-import org.apache.logging.log4j.util.NameUtil;
 import org.apache.logging.log4j.message.LocalizedMessage;
 import org.apache.logging.log4j.message.Message;
 import org.apache.logging.log4j.message.ObjectMessage;
@@ -50,6 +52,7 @@
 
     private static final boolean isCoreAvailable;
 
+    private final Map<Class<?>, ObjectRenderer> rendererMap;
 
     static {
         boolean available;
@@ -76,6 +79,7 @@
      */
     protected Category(final LoggerContext context, final String name) {
         this.logger = context.getLogger(name);
+        rendererMap = ((RendererSupport) LogManager.getLoggerRepository()).getRendererMap();
     }
 
     /**
@@ -88,6 +92,7 @@
 
     private Category(final org.apache.logging.log4j.Logger logger) {
         this.logger = logger;
+        rendererMap = ((RendererSupport) LogManager.getLoggerRepository()).getRendererMap();
     }
 
     public static Category getInstance(final String name) {
@@ -373,11 +378,16 @@
     public static void shutdown() {
     }
 
-
     public void forcedLog(final String fqcn, final Priority level, final Object message, final Throwable t) {
         final org.apache.logging.log4j.Level lvl = org.apache.logging.log4j.Level.toLevel(level.toString());
-        final Message msg = message instanceof Message ? (Message) message : new ObjectMessage(message);
-        logger.logMessage(lvl, null, fqcn, null, msg, t);
+        ObjectRenderer renderer = get(message.getClass());
+        final Message msg = message instanceof Message ? (Message) message : renderer != null ?
+            new RenderedMessage(renderer, message) : new ObjectMessage(message);
+        if (logger instanceof ExtendedLogger) {
+            ((ExtendedLogger) logger).logMessage(fqcn, lvl, null, new ObjectMessage(message), t);
+        } else {
+            logger.log(lvl, msg, t);
+        }
     }
 
     public boolean exists(final String name) {
@@ -407,7 +417,7 @@
             LoggerContext ctx = CategoryUtil.getLoggerContext(logger);
             if (ctx != null) {
                 final ConcurrentMap<String, Logger> loggers = getLoggersMap(ctx);
-                while ((name = NameUtil.getSubName(name)) != null) {
+                while ((name = getSubName(name)) != null) {
                     final Logger subLogger = loggers.get(name);
                     if (subLogger != null) {
                         final ResourceBundle rb = subLogger.bundle;
@@ -421,6 +431,14 @@
         return null;
     }
 
+    private static  String getSubName(final String name) {
+        if (Strings.isEmpty(name)) {
+            return null;
+        }
+        final int i = name.lastIndexOf('.');
+        return i > 0 ? name.substring(0, i) : Strings.EMPTY;
+    }
+
     /**
      If <code>assertion</code> parameter is {@code false}, then
      logs <code>msg</code> as an {@link #error(Object) error} statement.
@@ -479,7 +497,11 @@
     private void maybeLog(final String fqcn, final org.apache.logging.log4j.Level level,
             final Object message, final Throwable throwable) {
         if (logger.isEnabled(level)) {
-            logger.logMessage(level, null, fqcn, null, new ObjectMessage(message), throwable);
+            if (logger instanceof ExtendedLogger) {
+                ((ExtendedLogger) logger).logMessage(fqcn, level, null, new ObjectMessage(message), throwable);
+            } else {
+                logger.log(level, message, throwable);
+            }
         }
     }
 
@@ -515,4 +537,35 @@
         return logger.isEnabled(level);
     }
 
+    private ObjectRenderer get(Class clazz) {
+        ObjectRenderer renderer = null;
+        for(Class c = clazz; c != null; c = c.getSuperclass()) {
+            renderer = rendererMap.get(c);
+            if (renderer != null) {
+                return renderer;
+            }
+            renderer = searchInterfaces(c);
+            if (renderer != null) {
+                return renderer;
+            }
+        }
+        return null;
+    }
+
+    ObjectRenderer searchInterfaces(Class c) {
+        ObjectRenderer renderer = rendererMap.get(c);
+        if(renderer != null) {
+            return renderer;
+        } else {
+            Class[] ia = c.getInterfaces();
+            for (Class clazz : ia) {
+                renderer = searchInterfaces(clazz);
+                if (renderer != null) {
+                    return renderer;
+                }
+            }
+        }
+        return null;
+    }
+
 }
diff --git a/log4j-1.2-api/src/main/java/org/apache/log4j/ConsoleAppender.java b/log4j-1.2-api/src/main/java/org/apache/log4j/ConsoleAppender.java
index 0e841c5..605fac7 100644
--- a/log4j-1.2-api/src/main/java/org/apache/log4j/ConsoleAppender.java
+++ b/log4j-1.2-api/src/main/java/org/apache/log4j/ConsoleAppender.java
@@ -45,7 +45,7 @@
    * {@inheritDoc}
    */
   @Override
-  protected void append(LoggingEvent theEvent)
+  protected void append(final LoggingEvent theEvent)
   {
   }
 
diff --git a/log4j-1.2-api/src/main/java/org/apache/log4j/Layout.java b/log4j-1.2-api/src/main/java/org/apache/log4j/Layout.java
index d0b9943..dbd2291 100644
--- a/log4j-1.2-api/src/main/java/org/apache/log4j/Layout.java
+++ b/log4j-1.2-api/src/main/java/org/apache/log4j/Layout.java
@@ -25,7 +25,7 @@
 public abstract class Layout {
 
     public final static String LINE_SEP = Strings.LINE_SEPARATOR;
-    
+
     /** Note that the line.separator property can be looked up even by applets. */
     public static final int LINE_SEP_LEN = Strings.LINE_SEPARATOR.length();
 
diff --git a/log4j-1.2-api/src/main/java/org/apache/log4j/LogManager.java b/log4j-1.2-api/src/main/java/org/apache/log4j/LogManager.java
index a4b6802..d595988 100644
--- a/log4j-1.2-api/src/main/java/org/apache/log4j/LogManager.java
+++ b/log4j-1.2-api/src/main/java/org/apache/log4j/LogManager.java
@@ -17,9 +17,13 @@
 package org.apache.log4j;

 

 import java.util.Enumeration;

+import java.util.HashMap;

+import java.util.Map;

 

 import org.apache.log4j.helpers.NullEnumeration;

 import org.apache.log4j.legacy.core.ContextUtil;

+import org.apache.log4j.or.ObjectRenderer;

+import org.apache.log4j.or.RendererSupport;

 import org.apache.log4j.spi.HierarchyEventListener;

 import org.apache.log4j.spi.LoggerFactory;

 import org.apache.log4j.spi.LoggerRepository;

@@ -146,7 +150,15 @@
     /**

      * The Repository.

      */

-    private static class Repository implements LoggerRepository {

+    private static class Repository implements LoggerRepository, RendererSupport {

+

+        private final Map<Class<?>, ObjectRenderer> rendererMap = new HashMap<>();

+

+        @Override

+        public Map<Class<?>, ObjectRenderer> getRendererMap() {

+            return rendererMap;

+        }

+

         @Override

         public void addHierarchyEventListener(final HierarchyEventListener listener) {

 

diff --git a/log4j-1.2-api/src/main/java/org/apache/log4j/Logger.java b/log4j-1.2-api/src/main/java/org/apache/log4j/Logger.java
index c228338..fe1f26a 100644
--- a/log4j-1.2-api/src/main/java/org/apache/log4j/Logger.java
+++ b/log4j-1.2-api/src/main/java/org/apache/log4j/Logger.java
@@ -56,7 +56,7 @@
         private static final String FQCN = Logger.class.getName();
 
         public static LoggerContext getContext() {
-            return (LoggerContext) getContext(FQCN, false);
+            return getContext(FQCN, false);
         }
 
         public static org.apache.logging.log4j.Logger getLogger(final String name) {
diff --git a/log4j-1.2-api/src/main/java/org/apache/log4j/RenderedMessage.java b/log4j-1.2-api/src/main/java/org/apache/log4j/RenderedMessage.java
new file mode 100644
index 0000000..422a565
--- /dev/null
+++ b/log4j-1.2-api/src/main/java/org/apache/log4j/RenderedMessage.java
@@ -0,0 +1,60 @@
+/*
+ * 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.or.ObjectRenderer;
+import org.apache.logging.log4j.message.Message;
+
+/**
+ * Implements object rendering for Log4j 1.x compatibility.
+ */
+public class RenderedMessage implements Message {
+
+    private final ObjectRenderer renderer;
+    private final Object object;
+    private String rendered = null;
+
+    public RenderedMessage(ObjectRenderer renderer, Object object) {
+        this.renderer = renderer;
+        this.object = object;
+    }
+
+
+    @Override
+    public String getFormattedMessage() {
+        if (rendered == null) {
+            rendered = renderer.doRender(object);
+        }
+
+        return rendered;
+    }
+
+    @Override
+    public String getFormat() {
+        return getFormattedMessage();
+    }
+
+    @Override
+    public Object[] getParameters() {
+        return null;
+    }
+
+    @Override
+    public Throwable getThrowable() {
+        return null;
+    }
+}
diff --git a/log4j-1.2-api/src/main/java/org/apache/log4j/SimpleLayout.java b/log4j-1.2-api/src/main/java/org/apache/log4j/SimpleLayout.java
index 3b2374c..c77b9be 100644
--- a/log4j-1.2-api/src/main/java/org/apache/log4j/SimpleLayout.java
+++ b/log4j-1.2-api/src/main/java/org/apache/log4j/SimpleLayout.java
@@ -29,7 +29,7 @@
    * {@inheritDoc}
    */
   @Override
-  public String format(LoggingEvent theEvent)
+  public String format(final LoggingEvent theEvent)
   {
     return Strings.EMPTY;
   }
diff --git a/log4j-1.2-api/src/main/java/org/apache/log4j/WriterAppender.java b/log4j-1.2-api/src/main/java/org/apache/log4j/WriterAppender.java
new file mode 100644
index 0000000..f026bfa
--- /dev/null
+++ b/log4j-1.2-api/src/main/java/org/apache/log4j/WriterAppender.java
@@ -0,0 +1,378 @@
+/*
+ * 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.helpers.QuietWriter;
+import org.apache.log4j.spi.ErrorHandler;
+import org.apache.log4j.spi.LoggingEvent;
+import org.apache.logging.log4j.status.StatusLogger;
+
+import java.io.IOException;
+import java.io.InterruptedIOException;
+import java.io.OutputStream;
+import java.io.OutputStreamWriter;
+import java.io.Writer;
+
+
+/**
+ * WriterAppender appends log events to a {@link Writer} or an
+ * {@link OutputStream} depending on the user's choice.
+ */
+public class WriterAppender extends AppenderSkeleton {
+    private static final org.apache.logging.log4j.Logger LOGGER = StatusLogger.getLogger();
+
+    /**
+     * Immediate flush means that the underlying writer or output stream
+     * will be flushed at the end of each append operation unless shouldFlush()
+     * is overridden. Immediate
+     * flush is slower but ensures that each append request is actually
+     * written. If <code>immediateFlush</code> is set to
+     * <code>false</code>, then there is a good chance that the last few
+     * logs events are not actually written to persistent media if and
+     * when the application crashes.
+     *
+     * <p>The <code>immediateFlush</code> variable is set to
+     * <code>true</code> by default.
+     */
+    protected boolean immediateFlush = true;
+
+    /**
+     * The encoding to use when writing.  <p>The
+     * <code>encoding</code> variable is set to <code>null</null> by
+     * default which results in the utilization of the system's default
+     * encoding.
+     */
+    protected String encoding;
+
+    /**
+     * This is the {@link QuietWriter quietWriter} where we will write
+     * to.
+     */
+    protected QuietWriter qw;
+
+
+    /**
+     * This default constructor does nothing.
+     */
+    public WriterAppender() {
+    }
+
+    /**
+     * Instantiate a WriterAppender and set the output destination to a
+     * new {@link OutputStreamWriter} initialized with <code>os</code>
+     * as its {@link OutputStream}.
+     */
+    public WriterAppender(Layout layout, OutputStream os) {
+        this(layout, new OutputStreamWriter(os));
+    }
+
+    /**
+     * Instantiate a WriterAppender and set the output destination to
+     * <code>writer</code>.
+     *
+     * <p>The <code>writer</code> must have been previously opened by
+     * the user.
+     */
+    public WriterAppender(Layout layout, Writer writer) {
+        this.layout = layout;
+        this.setWriter(writer);
+    }
+
+    /**
+     * Returns value of the <b>ImmediateFlush</b> option.
+     */
+    public boolean getImmediateFlush() {
+        return immediateFlush;
+    }
+
+    /**
+     * If the <b>ImmediateFlush</b> option is set to
+     * <code>true</code>, the appender will flush at the end of each
+     * write. This is the default behavior. If the option is set to
+     * <code>false</code>, then the underlying stream can defer writing
+     * to physical medium to a later time.
+     *
+     * <p>Avoiding the flush operation at the end of each append results in
+     * a performance gain of 10 to 20 percent. However, there is safety
+     * tradeoff involved in skipping flushing. Indeed, when flushing is
+     * skipped, then it is likely that the last few log events will not
+     * be recorded on disk when the application exits. This is a high
+     * price to pay even for a 20% performance gain.
+     */
+    public void setImmediateFlush(boolean value) {
+        immediateFlush = value;
+    }
+
+    /**
+     * Does nothing.
+     */
+    public void activateOptions() {
+    }
+
+
+    /**
+     * This method is called by the {@link AppenderSkeleton#doAppend}
+     * method.
+     *
+     * <p>If the output stream exists and is writable then write a log
+     * statement to the output stream. Otherwise, write a single warning
+     * message to <code>System.err</code>.
+     *
+     * <p>The format of the output will depend on this appender's
+     * layout.
+     */
+    public void append(LoggingEvent event) {
+
+        // Reminder: the nesting of calls is:
+        //
+        //    doAppend()
+        //      - check threshold
+        //      - filter
+        //      - append();
+        //        - checkEntryConditions();
+        //        - subAppend();
+
+        if (!checkEntryConditions()) {
+            return;
+        }
+        subAppend(event);
+    }
+
+    /**
+     * This method determines if there is a sense in attempting to append.
+     *
+     * <p>It checks whether there is a set output target and also if
+     * there is a set layout. If these checks fail, then the boolean
+     * value <code>false</code> is returned.
+     */
+    protected boolean checkEntryConditions() {
+        if (this.closed) {
+            LOGGER.warn("Not allowed to write to a closed appender.");
+            return false;
+        }
+
+        if (this.qw == null) {
+            errorHandler.error("No output stream or file set for the appender named [" + name + "].");
+            return false;
+        }
+
+        if (this.layout == null) {
+            errorHandler.error("No layout set for the appender named [" + name + "].");
+            return false;
+        }
+        return true;
+    }
+
+
+    /**
+     * Close this appender instance. The underlying stream or writer is
+     * also closed.
+     *
+     * <p>Closed appenders cannot be reused.
+     *
+     * @see #setWriter
+     * @since 0.8.4
+     */
+    public
+    synchronized void close() {
+        if (this.closed) {
+            return;
+        }
+        this.closed = true;
+        writeFooter();
+        reset();
+    }
+
+    /**
+     * Close the underlying {@link Writer}.
+     */
+    protected void closeWriter() {
+        if (qw != null) {
+            try {
+                qw.close();
+            } catch (IOException e) {
+                if (e instanceof InterruptedIOException) {
+                    Thread.currentThread().interrupt();
+                }
+                // There is do need to invoke an error handler at this late
+                // stage.
+                LOGGER.error("Could not close " + qw, e);
+            }
+        }
+    }
+
+    /**
+     * Returns an OutputStreamWriter when passed an OutputStream.  The
+     * encoding used will depend on the value of the
+     * <code>encoding</code> property.  If the encoding value is
+     * specified incorrectly the writer will be opened using the default
+     * system encoding (an error message will be printed to the LOGGER.
+     */
+    protected OutputStreamWriter createWriter(OutputStream os) {
+        OutputStreamWriter retval = null;
+
+        String enc = getEncoding();
+        if (enc != null) {
+            try {
+                retval = new OutputStreamWriter(os, enc);
+            } catch (IOException e) {
+                if (e instanceof InterruptedIOException) {
+                    Thread.currentThread().interrupt();
+                }
+                LOGGER.warn("Error initializing output writer.");
+                LOGGER.warn("Unsupported encoding?");
+            }
+        }
+        if (retval == null) {
+            retval = new OutputStreamWriter(os);
+        }
+        return retval;
+    }
+
+    public String getEncoding() {
+        return encoding;
+    }
+
+    public void setEncoding(String value) {
+        encoding = value;
+    }
+
+
+    /**
+     * Set the {@link ErrorHandler} for this WriterAppender and also the
+     * underlying {@link QuietWriter} if any.
+     */
+    public synchronized void setErrorHandler(ErrorHandler eh) {
+        if (eh == null) {
+            LOGGER.warn("You have tried to set a null error-handler.");
+        } else {
+            this.errorHandler = eh;
+            if (this.qw != null) {
+                this.qw.setErrorHandler(eh);
+            }
+        }
+    }
+
+    /**
+     * <p>Sets the Writer where the log output will go. The
+     * specified Writer must be opened by the user and be
+     * writable.
+     *
+     * <p>The <code>java.io.Writer</code> will be closed when the
+     * appender instance is closed.
+     *
+     *
+     * <p><b>WARNING:</b> Logging to an unopened Writer will fail.
+     * <p>
+     *
+     * @param writer An already opened Writer.
+     */
+    public synchronized void setWriter(Writer writer) {
+        reset();
+        this.qw = new QuietWriter(writer, errorHandler);
+        //this.tp = new TracerPrintWriter(qw);
+        writeHeader();
+    }
+
+
+    /**
+     * Actual writing occurs here.
+     *
+     * <p>Most subclasses of <code>WriterAppender</code> will need to
+     * override this method.
+     *
+     * @since 0.9.0
+     */
+    protected void subAppend(LoggingEvent event) {
+        this.qw.write(this.layout.format(event));
+
+        if (layout.ignoresThrowable()) {
+            String[] s = event.getThrowableStrRep();
+            if (s != null) {
+                int len = s.length;
+                for (int i = 0; i < len; i++) {
+                    this.qw.write(s[i]);
+                    this.qw.write(Layout.LINE_SEP);
+                }
+            }
+        }
+
+        if (shouldFlush(event)) {
+            this.qw.flush();
+        }
+    }
+
+
+    /**
+     * The WriterAppender requires a layout. Hence, this method returns
+     * <code>true</code>.
+     */
+    public boolean requiresLayout() {
+        return true;
+    }
+
+    /**
+     * Clear internal references to the writer and other variables.
+     * <p>
+     * Subclasses can override this method for an alternate closing
+     * behavior.
+     */
+    protected void reset() {
+        closeWriter();
+        this.qw = null;
+        //this.tp = null;
+    }
+
+
+    /**
+     * Write a footer as produced by the embedded layout's {@link
+     * Layout#getFooter} method.
+     */
+    protected void writeFooter() {
+        if (layout != null) {
+            String f = layout.getFooter();
+            if (f != null && this.qw != null) {
+                this.qw.write(f);
+                this.qw.flush();
+            }
+        }
+    }
+
+    /**
+     * Write a header as produced by the embedded layout's {@link
+     * Layout#getHeader} method.
+     */
+    protected void writeHeader() {
+        if (layout != null) {
+            String h = layout.getHeader();
+            if (h != null && this.qw != null) {
+                this.qw.write(h);
+            }
+        }
+    }
+
+    /**
+     * Determines whether the writer should be flushed after
+     * this event is written.
+     *
+     * @since 1.2.16
+     */
+    protected boolean shouldFlush(final LoggingEvent event) {
+        return immediateFlush;
+    }
+}
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
new file mode 100644
index 0000000..f2dd2c1
--- /dev/null
+++ b/log4j-1.2-api/src/main/java/org/apache/log4j/bridge/AppenderAdapter.java
@@ -0,0 +1,86 @@
+/*
+ * 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.bridge;
+
+import java.io.Serializable;
+import java.util.ArrayList;
+import java.util.List;
+
+import org.apache.log4j.Appender;
+import org.apache.logging.log4j.core.Filter;
+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.
+ */
+public class AppenderAdapter {
+
+    private final Appender appender;
+    private final Adapter adapter;
+
+    /**
+     * Constructor.
+     */
+    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());
+            }
+        }
+        this.adapter = new Adapter(appender.getName(), appenderFilter, null, true, null);
+    }
+
+    public Adapter getAdapter() {
+        return adapter;
+    }
+
+    public class Adapter extends AbstractAppender {
+
+        protected Adapter(final String name, final Filter filter, final Layout<? extends Serializable> layout,
+            final boolean ignoreExceptions, final Property[] properties) {
+            super(name, filter, layout, ignoreExceptions, properties);
+        }
+
+        @Override
+        public void append(LogEvent event) {
+            appender.doAppend(new LogEventAdapter(event));
+        }
+
+        @Override
+        public void stop() {
+            appender.close();
+        }
+
+        public Appender getAppender() {
+            return appender;
+        }
+    }
+}
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
new file mode 100644
index 0000000..f06f909
--- /dev/null
+++ b/log4j-1.2-api/src/main/java/org/apache/log4j/bridge/AppenderWrapper.java
@@ -0,0 +1,101 @@
+/*
+ * 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.bridge;
+
+import org.apache.log4j.Appender;
+import org.apache.log4j.Layout;
+import org.apache.log4j.spi.ErrorHandler;
+import org.apache.log4j.spi.Filter;
+import org.apache.log4j.spi.LoggingEvent;
+
+/**
+ * Holds a Log4j 2 Appender in an empty Log4j 1 Appender so it can be extracted when constructing the configuration.
+ * Allows a Log4j 1 Appender to reference a Log4j 2 Appender.
+ */
+public class AppenderWrapper implements Appender {
+
+    private final org.apache.logging.log4j.core.Appender appender;
+
+    public AppenderWrapper(org.apache.logging.log4j.core.Appender appender) {
+        this.appender = appender;
+    }
+
+    public org.apache.logging.log4j.core.Appender getAppender() {
+        return appender;
+    }
+
+    @Override
+    public void addFilter(Filter newFilter) {
+    }
+
+    @Override
+    public Filter getFilter() {
+        return null;
+    }
+
+    @Override
+    public void clearFilters() {
+
+    }
+
+    @Override
+    public void close() {
+        // Not supported with Log4j 2.
+    }
+
+    @Override
+    public void doAppend(LoggingEvent event) {
+        if (event instanceof LogEventAdapter) {
+            appender.append(((LogEventAdapter) event).getEvent());
+        }
+    }
+
+    @Override
+    public String getName() {
+        return appender.getName();
+    }
+
+    @Override
+    public void setErrorHandler(ErrorHandler errorHandler) {
+        appender.setHandler(new ErrorHandlerAdapter(errorHandler));
+    }
+
+    @Override
+    public ErrorHandler getErrorHandler() {
+        return ((ErrorHandlerAdapter)appender.getHandler()).getHandler();
+    }
+
+    @Override
+    public void setLayout(Layout layout) {
+        // Log4j 2 doesn't support this.
+    }
+
+    @Override
+    public Layout getLayout() {
+        return new LayoutWrapper(appender.getLayout());
+    }
+
+    @Override
+    public void setName(String name) {
+        // Log4j 2 doesn't support this.
+    }
+
+    @Override
+    public boolean requiresLayout() {
+        return false;
+    }
+}
diff --git a/log4j-1.2-api/src/main/java/org/apache/log4j/bridge/ErrorHandlerAdapter.java b/log4j-1.2-api/src/main/java/org/apache/log4j/bridge/ErrorHandlerAdapter.java
new file mode 100644
index 0000000..1f166a2
--- /dev/null
+++ b/log4j-1.2-api/src/main/java/org/apache/log4j/bridge/ErrorHandlerAdapter.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.bridge;
+
+import org.apache.log4j.spi.ErrorHandler;
+import org.apache.logging.log4j.core.LogEvent;
+
+/**
+ * Makes a Log4j 1 ErrorHandler usable by a Log4j 2 Appender.
+ */
+public class ErrorHandlerAdapter implements org.apache.logging.log4j.core.ErrorHandler {
+
+    private final ErrorHandler errorHandler;
+
+    public ErrorHandlerAdapter(ErrorHandler errorHandler) {
+        this.errorHandler = errorHandler;
+    }
+
+    public ErrorHandler getHandler() {
+        return errorHandler;
+    }
+
+    @Override
+    public void error(String msg) {
+        errorHandler.error(msg);
+    }
+
+    @Override
+    public void error(String msg, Throwable t) {
+        if (t instanceof Exception) {
+            errorHandler.error(msg, (Exception) t, 0);
+        } else {
+            errorHandler.error(msg);
+        }
+    }
+
+    @Override
+    public void error(String msg, LogEvent event, Throwable t) {
+        if (t == null || t instanceof Exception) {
+            errorHandler.error(msg, (Exception) t, 0, new LogEventAdapter(event));
+        } else {
+            errorHandler.error(msg);
+        }
+    }
+}
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
new file mode 100644
index 0000000..2dff272
--- /dev/null
+++ b/log4j-1.2-api/src/main/java/org/apache/log4j/bridge/FilterAdapter.java
@@ -0,0 +1,56 @@
+/*
+ * 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.bridge;
+
+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;
+
+/**
+ * Binds a Log4j 1.x Filter with Log4j 2.
+ */
+public class FilterAdapter extends AbstractFilter {
+
+    private final Filter filter;
+
+    public FilterAdapter(Filter filter) {
+        this.filter = filter;
+    }
+
+    @Override
+    public void start() {
+        filter.activateOptions();
+    }
+
+    @Override
+    public Result filter(LogEvent event) {
+        LoggingEvent loggingEvent = new LogEventAdapter(event);
+        Filter next = filter;
+        while (next != null) {
+            switch (filter.decide(loggingEvent)) {
+                case Filter.ACCEPT:
+                    return Result.ACCEPT;
+                case Filter.DENY:
+                    return Result.DENY;
+                default:
+            }
+            next = filter.getNext();
+        }
+        return Result.NEUTRAL;
+    }
+}
diff --git a/log4j-1.2-api/src/main/java/org/apache/log4j/bridge/FilterWrapper.java b/log4j-1.2-api/src/main/java/org/apache/log4j/bridge/FilterWrapper.java
new file mode 100644
index 0000000..b2855cd
--- /dev/null
+++ b/log4j-1.2-api/src/main/java/org/apache/log4j/bridge/FilterWrapper.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.bridge;
+
+import org.apache.log4j.spi.Filter;
+import org.apache.log4j.spi.LoggingEvent;
+
+/**
+ * This acts as a container for Log4j 2 Filters to be attached to Log4j 1 components. However, the Log4j 2
+ * Filters will always be called directly so this class just acts as a container.
+ */
+public class FilterWrapper extends Filter {
+
+    private final org.apache.logging.log4j.core.Filter filter;
+
+    public FilterWrapper(org.apache.logging.log4j.core.Filter filter) {
+        this.filter = filter;
+    }
+
+    public org.apache.logging.log4j.core.Filter getFilter() {
+        return filter;
+    }
+
+    /**
+     * This method is never called.
+     * @param event The LoggingEvent to decide upon.
+     * @return 0
+     */
+    @Override
+    public int decide(LoggingEvent event) {
+        return 0;
+    }
+}
diff --git a/log4j-1.2-api/src/main/java/org/apache/log4j/bridge/LayoutAdapter.java b/log4j-1.2-api/src/main/java/org/apache/log4j/bridge/LayoutAdapter.java
new file mode 100644
index 0000000..5494c92
--- /dev/null
+++ b/log4j-1.2-api/src/main/java/org/apache/log4j/bridge/LayoutAdapter.java
@@ -0,0 +1,73 @@
+/*
+ * 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.bridge;
+
+import org.apache.log4j.Layout;
+import org.apache.logging.log4j.core.LogEvent;
+import org.apache.logging.log4j.core.layout.ByteBufferDestination;
+
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * Class Description goes here.
+ */
+public class LayoutAdapter implements org.apache.logging.log4j.core.Layout<String> {
+    private Layout layout;
+
+    public LayoutAdapter(Layout layout) {
+        this.layout = layout;
+    }
+
+
+    @Override
+    public byte[] getFooter() {
+        return layout.getFooter() == null ? null : layout.getFooter().getBytes();
+    }
+
+    @Override
+    public byte[] getHeader() {
+        return layout.getHeader() == null ? null : layout.getHeader().getBytes();
+    }
+
+    @Override
+    public byte[] toByteArray(LogEvent event) {
+        String result = layout.format(new LogEventAdapter(event));
+        return result == null ? null : result.getBytes();
+    }
+
+    @Override
+    public String toSerializable(LogEvent event) {
+        return layout.format(new LogEventAdapter(event));
+    }
+
+    @Override
+    public String getContentType() {
+        return layout.getContentType();
+    }
+
+    @Override
+    public Map<String, String> getContentFormat() {
+        return new HashMap<>();
+    }
+
+    @Override
+    public void encode(LogEvent event, ByteBufferDestination destination) {
+        final byte[] data = toByteArray(event);
+        destination.writeBytes(data, 0, data.length);
+    }
+}
diff --git a/log4j-1.2-api/src/main/java/org/apache/log4j/bridge/LayoutWrapper.java b/log4j-1.2-api/src/main/java/org/apache/log4j/bridge/LayoutWrapper.java
new file mode 100644
index 0000000..fc1e72f
--- /dev/null
+++ b/log4j-1.2-api/src/main/java/org/apache/log4j/bridge/LayoutWrapper.java
@@ -0,0 +1,48 @@
+/*
+ * 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.bridge;
+
+
+import org.apache.log4j.Layout;
+import org.apache.log4j.spi.LoggingEvent;
+
+
+/**
+ * Bridge between the Log4j 1 Layout and a Log4j 2 Layout.
+ */
+public class LayoutWrapper extends Layout {
+
+    private final org.apache.logging.log4j.core.Layout<?> layout;
+
+    public LayoutWrapper(org.apache.logging.log4j.core.Layout<?> layout) {
+        this.layout = layout;
+    }
+
+    @Override
+    public String format(LoggingEvent event) {
+        return layout.toSerializable(((LogEventAdapter)event).getEvent()).toString();
+    }
+
+    @Override
+    public boolean ignoresThrowable() {
+        return false;
+    }
+
+    public org.apache.logging.log4j.core.Layout<?> getLayout() {
+        return this.layout;
+    }
+}
diff --git a/log4j-1.2-api/src/main/java/org/apache/log4j/bridge/LogEventAdapter.java b/log4j-1.2-api/src/main/java/org/apache/log4j/bridge/LogEventAdapter.java
new file mode 100644
index 0000000..9ad4d27
--- /dev/null
+++ b/log4j-1.2-api/src/main/java/org/apache/log4j/bridge/LogEventAdapter.java
@@ -0,0 +1,217 @@
+/*
+ * 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.bridge;
+
+import java.lang.reflect.Method;
+
+import org.apache.log4j.Category;
+import org.apache.log4j.Level;
+import org.apache.log4j.spi.LocationInfo;
+import org.apache.log4j.spi.LoggingEvent;
+import org.apache.log4j.spi.ThrowableInformation;
+import org.apache.logging.log4j.core.LogEvent;
+import org.apache.logging.log4j.core.util.Loader;
+import org.apache.logging.log4j.core.util.Throwables;
+import org.apache.logging.log4j.spi.StandardLevel;
+import org.apache.logging.log4j.status.StatusLogger;
+
+/**
+ * Converts a Log4j 2 LogEvent into the components needed by a Log4j 1.x LoggingEvent.
+ * This class requires Log4j 2.
+ */
+public class LogEventAdapter extends LoggingEvent {
+
+    private static final long JVM_START_TIME = initStartTime();
+
+    private final LogEvent event;
+
+    public LogEventAdapter(LogEvent event) {
+        this.event = event;
+    }
+
+    public LogEvent getEvent() {
+        return this.event;
+    }
+
+    /**
+     Set the location information for this logging event. The collected
+     information is cached for future use.
+     */
+    @Override
+    public LocationInfo getLocationInformation() {
+        return new LocationInfo(event.getSource());
+    }
+
+    /**
+     * Return the level of this event. Use this form instead of directly
+     * accessing the <code>level</code> field.  */
+    @Override
+    public Level getLevel() {
+        switch (StandardLevel.getStandardLevel(event.getLevel().intLevel())) {
+            case TRACE:
+                return Level.TRACE;
+            case DEBUG:
+                return Level.DEBUG;
+            case INFO:
+                return Level.INFO;
+            case WARN:
+                return Level.WARN;
+            case ERROR:
+                return Level.ERROR;
+            case FATAL:
+                return Level.FATAL;
+            case OFF:
+                return Level.OFF;
+            case ALL:
+                return Level.ALL;
+            default:
+                return Level.ERROR;
+        }
+    }
+
+    /**
+     * Return the name of the logger. Use this form instead of directly
+     * accessing the <code>categoryName</code> field.
+     */
+    @Override
+    public String getLoggerName() {
+        return event.getLoggerName();
+    }
+
+    /**
+     * Gets the logger of the event.
+     */
+    @Override
+    public Category getLogger() {
+        return Category.getInstance(event.getLoggerName());
+    }
+
+    /*
+     Return the message for this logging event.
+    */
+    @Override
+    public
+    Object getMessage() {
+        return event.getMessage().getFormattedMessage();
+    }
+
+    /*
+     * This method returns the NDC for this event.
+     */
+    @Override
+    public
+    String getNDC() {
+        return event.getContextStack().toString();
+    }
+
+
+    /*
+     Returns the the context corresponding to the <code>key</code> parameter.
+     */
+    @Override
+    public
+    Object getMDC(String key) {
+        if (event.getContextData() != null) {
+            return event.getContextData().getValue(key);
+        } else {
+            return null;
+        }
+    }
+
+    /**
+     Obtain a copy of this thread's MDC prior to serialization or
+     asynchronous logging.
+     */
+    @Override
+    public
+    void getMDCCopy() {
+    }
+
+    @Override
+    public
+    String getRenderedMessage() {
+        return event.getMessage().getFormattedMessage();
+    }
+
+    /**
+     Returns the time when the application started, in milliseconds
+     elapsed since 01.01.1970.  */
+    public static long getStartTime() {
+        return JVM_START_TIME;
+    }
+
+    @Override
+    public
+    String getThreadName() {
+        return event.getThreadName();
+    }
+
+    /**
+     Returns the throwable information contained within this
+     event. May be <code>null</code> if there is no such information.
+
+     <p>Note that the {@link Throwable} object contained within a
+     {@link ThrowableInformation} does not survive serialization.
+
+     @since 1.1 */
+    @Override
+    public
+    ThrowableInformation getThrowableInformation() {
+        if (event.getThrown() != null) {
+            return new ThrowableInformation(event.getThrown());
+        }
+        return null;
+    }
+
+    /**
+     Return this event's throwable's string[] representaion.
+     */
+    @Override
+    public
+    String[] getThrowableStrRep() {
+        if (event.getThrown() != null) {
+            return Throwables.toStringList(event.getThrown()).toArray(new String[0]);
+        }
+        return null;
+    }
+
+    /**
+     * Returns the result of {@code ManagementFactory.getRuntimeMXBean().getStartTime()},
+     * or the current system time if JMX is not available.
+     */
+    private static long initStartTime() {
+        // We'd like to call ManagementFactory.getRuntimeMXBean().getStartTime(),
+        // but Google App Engine throws a java.lang.NoClassDefFoundError
+        // "java.lang.management.ManagementFactory is a restricted class".
+        // The reflection is necessary because without it, Google App Engine
+        // will refuse to initialize this class.
+        try {
+            final Class<?> factoryClass = Loader.loadSystemClass("java.lang.management.ManagementFactory");
+            final Method getRuntimeMXBean = factoryClass.getMethod("getRuntimeMXBean");
+            final Object runtimeMXBean = getRuntimeMXBean.invoke(null);
+
+            final Class<?> runtimeMXBeanClass = Loader.loadSystemClass("java.lang.management.RuntimeMXBean");
+            final Method getStartTime = runtimeMXBeanClass.getMethod("getStartTime");
+            return (Long) getStartTime.invoke(runtimeMXBean);
+        } catch (final Throwable t) {
+            StatusLogger.getLogger().error("Unable to call ManagementFactory.getRuntimeMXBean().getStartTime(), "
+                + "using system time for OnStartupTriggeringPolicy", t);
+            // We have little option but to declare "now" as the beginning of time.
+            return System.currentTimeMillis();
+        }
+    }
+}
diff --git a/log4j-1.2-api/src/main/java/org/apache/log4j/builders/BooleanHolder.java b/log4j-1.2-api/src/main/java/org/apache/log4j/builders/BooleanHolder.java
new file mode 100644
index 0000000..16e46d3
--- /dev/null
+++ b/log4j-1.2-api/src/main/java/org/apache/log4j/builders/BooleanHolder.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.builders;
+
+/**
+ * Holds Boolean values created inside of a Lambda expression.
+ */
+public class BooleanHolder extends Holder<Boolean> {
+    public BooleanHolder() {
+        super(Boolean.FALSE);
+    }
+
+    @Override
+    public void set(Boolean value) {
+        if (value != null) {
+            super.set(value);
+        }
+    }
+}
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
new file mode 100644
index 0000000..9a6b7b8
--- /dev/null
+++ b/log4j-1.2-api/src/main/java/org/apache/log4j/builders/BuilderManager.java
@@ -0,0 +1,94 @@
+/*
+ * 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 org.apache.log4j.Appender;
+import org.apache.log4j.Layout;
+import org.apache.log4j.builders.appender.AppenderBuilder;
+import org.apache.log4j.builders.filter.FilterBuilder;
+import org.apache.log4j.builders.layout.LayoutBuilder;
+import org.apache.log4j.spi.Filter;
+import org.apache.log4j.xml.XmlConfigurationFactory;
+import org.apache.logging.log4j.Logger;
+import org.apache.logging.log4j.plugins.util.PluginManager;
+import org.apache.logging.log4j.plugins.util.PluginType;
+import org.apache.logging.log4j.status.StatusLogger;
+import org.apache.logging.log4j.util.LoaderUtil;
+import org.w3c.dom.Element;
+
+import java.lang.reflect.InvocationTargetException;
+import java.util.Map;
+
+/**
+ *
+ */
+public class BuilderManager {
+
+    public static final String CATEGORY = "Log4j Builder";
+    private static final Logger LOGGER = StatusLogger.getLogger();
+    private final Map<String, PluginType<?>> plugins;
+
+    public BuilderManager() {
+        final PluginManager manager = new PluginManager(CATEGORY);
+        manager.collectPlugins();
+        plugins = manager.getPlugins();
+    }
+
+    public Appender parseAppender(String className, Element appenderElement, XmlConfigurationFactory factory) {
+        PluginType<?> plugin = plugins.get(className.toLowerCase());
+        if (plugin != null) {
+            try {
+                @SuppressWarnings("unchecked")
+                AppenderBuilder builder = (AppenderBuilder) LoaderUtil.newInstanceOf(plugin.getPluginClass());
+                return builder.parseAppender(appenderElement, factory);
+            } catch (InstantiationException | IllegalAccessException | InvocationTargetException ex) {
+                LOGGER.warn("Unable to load plugin: {} due to: {}", plugin.getKey(), ex.getMessage());
+            }
+        }
+        return null;
+    }
+
+    public Filter parseFilter(String className, Element filterElement, XmlConfigurationFactory factory) {
+        PluginType<?> plugin = plugins.get(className.toLowerCase());
+        if (plugin != null) {
+            try {
+                @SuppressWarnings("unchecked")
+                FilterBuilder builder = (FilterBuilder) LoaderUtil.newInstanceOf(plugin.getPluginClass());
+                return builder.parseFilter(filterElement, factory);
+            } catch (InstantiationException | IllegalAccessException | InvocationTargetException ex) {
+                LOGGER.warn("Unable to load plugin: {} due to: {}", plugin.getKey(), ex.getMessage());
+            }
+        }
+        return null;
+    }
+
+
+    public Layout parseLayout(String className, Element layoutElement, XmlConfigurationFactory factory) {
+        PluginType<?> plugin = plugins.get(className.toLowerCase());
+        if (plugin != null) {
+            try {
+                @SuppressWarnings("unchecked")
+                LayoutBuilder builder = (LayoutBuilder) LoaderUtil.newInstanceOf(plugin.getPluginClass());
+                return builder.parseLayout(layoutElement, factory);
+            } catch (InstantiationException | IllegalAccessException | InvocationTargetException ex) {
+                LOGGER.warn("Unable to load plugin: {} due to: {}", plugin.getKey(), ex.getMessage());
+            }
+        }
+        return null;
+    }
+
+}
diff --git a/log4j-1.2-api/src/main/java/org/apache/log4j/builders/Holder.java b/log4j-1.2-api/src/main/java/org/apache/log4j/builders/Holder.java
new file mode 100644
index 0000000..b9ce2bf
--- /dev/null
+++ b/log4j-1.2-api/src/main/java/org/apache/log4j/builders/Holder.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.builders;
+
+/**
+ * Provides a place to hold values generated inside of a Lambda expression.
+ */
+public class Holder<T> {
+    private T value;
+
+    public Holder() {
+    }
+
+    public Holder(T defaultValue) {
+        this.value = defaultValue;
+    }
+
+    public void set(T value) {
+        this.value = value;
+    }
+
+    public T get() {
+        return value;
+    }
+}
diff --git a/log4j-1.2-api/src/main/java/org/apache/log4j/builders/appender/AppenderBuilder.java b/log4j-1.2-api/src/main/java/org/apache/log4j/builders/appender/AppenderBuilder.java
new file mode 100644
index 0000000..2b72efd
--- /dev/null
+++ b/log4j-1.2-api/src/main/java/org/apache/log4j/builders/appender/AppenderBuilder.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.builders.appender;
+
+import org.apache.log4j.Appender;
+import org.apache.log4j.xml.XmlConfigurationFactory;
+import org.apache.logging.log4j.core.config.ConfigurationFactory;
+import org.w3c.dom.Element;
+
+import java.util.function.BiFunction;
+
+/**
+ * Define an Appender Builder.
+ */
+public interface AppenderBuilder {
+
+    Appender parseAppender(Element element, XmlConfigurationFactory factory);
+
+}
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
new file mode 100644
index 0000000..d9b7905
--- /dev/null
+++ b/log4j-1.2-api/src/main/java/org/apache/log4j/builders/appender/ConsoleAppenderBuilder.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.builders.appender;
+
+import org.apache.log4j.Appender;
+import org.apache.log4j.Layout;
+import org.apache.log4j.bridge.AppenderWrapper;
+import org.apache.log4j.bridge.FilterAdapter;
+import org.apache.log4j.bridge.FilterWrapper;
+import org.apache.log4j.bridge.LayoutAdapter;
+import org.apache.log4j.bridge.LayoutWrapper;
+import org.apache.log4j.builders.Holder;
+import org.apache.log4j.spi.Filter;
+import org.apache.log4j.xml.XmlConfigurationFactory;
+import org.apache.logging.log4j.Logger;
+import org.apache.logging.log4j.core.appender.ConsoleAppender;
+import org.apache.logging.log4j.plugins.Plugin;
+import org.apache.logging.log4j.status.StatusLogger;
+import org.w3c.dom.Element;
+
+import static org.apache.log4j.builders.BuilderManager.CATEGORY;
+import static org.apache.log4j.xml.XmlConfigurationFactory.*;
+
+/**
+ * Build a Console Appender
+ */
+@Plugin(name = "org.apache.log4j.ConsoleAppender", category = CATEGORY)
+public class ConsoleAppenderBuilder implements AppenderBuilder {
+    private static final String SYSTEM_OUT = "System.out";
+    private static final String SYSTEM_ERR = "System.err";
+    private static final String TARGET = "target";
+
+    private static final Logger LOGGER = StatusLogger.getLogger();
+
+    @Override
+    public Appender parseAppender(Element appenderElement, XmlConfigurationFactory factory) {
+        String name = appenderElement.getAttribute(XmlConfigurationFactory.NAME_ATTR);
+        Holder<String> target = new Holder<>(SYSTEM_OUT);
+        Holder<Layout> layout = new Holder<>();
+        Holder<Filter> filter = new Holder<>();
+        forEachElement(appenderElement.getChildNodes(), (currentElement) -> {
+            switch (currentElement.getTagName()) {
+                case LAYOUT_TAG:
+                    layout.set(factory.parseLayout(currentElement));
+                    break;
+                case FILTER_TAG:
+                    filter.set(factory.parseFilters(currentElement));
+                    break;
+                case PARAM_TAG: {
+                    if (currentElement.getAttribute(NAME_ATTR).equalsIgnoreCase(TARGET)) {
+                        String value = currentElement.getAttribute(VALUE_ATTR);
+                        if (value == null) {
+                            LOGGER.warn("No value supplied for target parameter. Defaulting to System.out.");
+                        } else {
+                            switch (value) {
+                                case SYSTEM_OUT:
+                                    target.set(SYSTEM_OUT);
+                                    break;
+                                case SYSTEM_ERR:
+                                    target.set(SYSTEM_ERR);
+                                    break;
+                                default:
+                                    LOGGER.warn("Invalid value \"{}\" for target parameter. Using default of System.out",
+                                            value);
+                            }
+                        }
+                    }
+                    break;
+                }
+            }
+        });
+        org.apache.logging.log4j.core.Layout<?> consoleLayout = null;
+        org.apache.logging.log4j.core.Filter consoleFilter = null;
+
+        if (layout.get() instanceof LayoutWrapper) {
+            consoleLayout = ((LayoutWrapper) layout.get()).getLayout();
+        } else if (layout.get() != null) {
+            consoleLayout = new LayoutAdapter(layout.get());
+        }
+        if (filter.get() != null) {
+            if (filter.get() instanceof FilterWrapper) {
+                consoleFilter = ((FilterWrapper) filter.get()).getFilter();
+            } else {
+                consoleFilter = new FilterAdapter(filter.get());
+            }
+        }
+        ConsoleAppender.Target consoleTarget = SYSTEM_ERR.equals(target.get())
+                ? ConsoleAppender.Target.SYSTEM_ERR : ConsoleAppender.Target.SYSTEM_OUT;
+        return new AppenderWrapper(ConsoleAppender.newBuilder()
+                .setName(name)
+                .setTarget(consoleTarget)
+                .setLayout(consoleLayout)
+                .setFilter(consoleFilter)
+                .setConfiguration(factory.getConfiguration())
+                .build());
+    }
+}
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
new file mode 100644
index 0000000..e37d385
--- /dev/null
+++ b/log4j-1.2-api/src/main/java/org/apache/log4j/builders/appender/DailyRollingFileAppenderBuilder.java
@@ -0,0 +1,145 @@
+/*
+ * 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 org.apache.log4j.Appender;
+import org.apache.log4j.Layout;
+import org.apache.log4j.bridge.AppenderWrapper;
+import org.apache.log4j.bridge.FilterAdapter;
+import org.apache.log4j.bridge.FilterWrapper;
+import org.apache.log4j.bridge.LayoutAdapter;
+import org.apache.log4j.bridge.LayoutWrapper;
+import org.apache.log4j.builders.BooleanHolder;
+import org.apache.log4j.builders.Holder;
+import org.apache.log4j.spi.Filter;
+import org.apache.log4j.xml.XmlConfigurationFactory;
+import org.apache.logging.log4j.Logger;
+import org.apache.logging.log4j.core.appender.RollingFileAppender;
+import org.apache.logging.log4j.core.appender.rolling.DefaultRolloverStrategy;
+import org.apache.logging.log4j.core.appender.rolling.RolloverStrategy;
+import org.apache.logging.log4j.core.appender.rolling.TimeBasedTriggeringPolicy;
+import org.apache.logging.log4j.core.appender.rolling.TriggeringPolicy;
+import org.apache.logging.log4j.plugins.Plugin;
+import org.apache.logging.log4j.status.StatusLogger;
+import org.w3c.dom.Element;
+
+import static org.apache.log4j.builders.BuilderManager.CATEGORY;
+import static org.apache.log4j.xml.XmlConfigurationFactory.*;
+
+/**
+ * Build a Daily Rolling File Appender
+ */
+@Plugin(name = "org.apache.log4j.DailyRollingFileAppender", category = CATEGORY)
+public class DailyRollingFileAppenderBuilder implements AppenderBuilder {
+
+    private static final Logger LOGGER = StatusLogger.getLogger();
+
+    @Override
+    public Appender parseAppender(Element appenderElement, XmlConfigurationFactory factory) {
+        String name = appenderElement.getAttribute(NAME_ATTR);
+        Holder<Layout> layout = new Holder<>();
+        Holder<Filter> filter = new Holder<>();
+        Holder<String> fileName = new Holder<>();
+        Holder<Boolean> immediateFlush = new BooleanHolder();
+        Holder<Boolean> append = new BooleanHolder();
+        Holder<Boolean> bufferedIo = new BooleanHolder();
+        Holder<Integer> bufferSize = new Holder<>(8192);
+        forEachElement(appenderElement.getChildNodes(), (currentElement) -> {
+            switch (currentElement.getTagName()) {
+                case LAYOUT_TAG:
+                    layout.set(factory.parseLayout(currentElement));
+                    break;
+                case FILTER_TAG:
+                    filter.set(factory.parseFilters(currentElement));
+                    break;
+                case PARAM_TAG: {
+                    switch (currentElement.getAttribute(NAME_ATTR).toLowerCase()) {
+                        case FILE_PARAM:
+                            fileName.set(currentElement.getAttribute(VALUE_ATTR));
+                            break;
+                        case APPEND_PARAM: {
+                            String bool = currentElement.getAttribute(VALUE_ATTR);
+                            if (bool != null) {
+                                append.set(Boolean.parseBoolean(bool));
+                            } else {
+                                LOGGER.warn("No value provided for append parameter");
+                            }
+                            break;
+                        }
+                        case BUFFERED_IO_PARAM: {
+                            String bool = currentElement.getAttribute(VALUE_ATTR);
+                            if (bool != null) {
+                                bufferedIo.set(Boolean.parseBoolean(bool));
+                            } else {
+                                LOGGER.warn("No value provided for bufferedIo parameter");
+                            }
+                            break;
+                        }
+                        case BUFFER_SIZE_PARAM:
+                            String size = currentElement.getAttribute(VALUE_ATTR);
+                            if (size != null) {
+                                bufferSize.set(Integer.parseInt(size));
+                            } else {
+                                LOGGER.warn("No value provide for bufferSize parameter");
+                            }
+                            break;
+                    }
+                    break;
+                }
+            }
+        });
+
+        org.apache.logging.log4j.core.Layout<?> fileLayout = null;
+        org.apache.logging.log4j.core.Filter fileFilter = null;
+        if (bufferedIo.get()) {
+            immediateFlush.set(Boolean.TRUE);
+        }
+        if (layout.get() instanceof LayoutWrapper) {
+            fileLayout = ((LayoutWrapper) layout.get()).getLayout();
+        } else if (layout.get() != null) {
+            fileLayout = new LayoutAdapter(layout.get());
+        }
+        if (filter.get() != null) {
+            if (filter.get() instanceof FilterWrapper) {
+                fileFilter = ((FilterWrapper) filter.get()).getFilter();
+            } else {
+                fileFilter = new FilterAdapter(filter.get());
+            }
+        }
+        if (fileName.get() == null) {
+            LOGGER.warn("Unable to create File Appender, no file name provided");
+            return null;
+        }
+        String filePattern = fileName.get() +"%d{yyy-MM-dd}";
+        TriggeringPolicy policy = TimeBasedTriggeringPolicy.newBuilder().setModulate(true).build();
+        RolloverStrategy strategy = DefaultRolloverStrategy.newBuilder()
+                .setConfig(factory.getConfiguration())
+                .setMax(Integer.toString(Integer.MAX_VALUE))
+                .build();
+        return new AppenderWrapper(RollingFileAppender.newBuilder()
+                .setName(name)
+                .setConfiguration(factory.getConfiguration())
+                .setLayout(fileLayout)
+                .setFilter(fileFilter)
+                .setFileName(fileName.get())
+                .setImmediateFlush(immediateFlush.get())
+                .setFilePattern(filePattern)
+                .setPolicy(policy)
+                .setStrategy(strategy)
+                .build());
+    }
+}
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
new file mode 100644
index 0000000..732581e
--- /dev/null
+++ b/log4j-1.2-api/src/main/java/org/apache/log4j/builders/appender/FileAppenderBuilder.java
@@ -0,0 +1,135 @@
+/*
+ * 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 org.apache.log4j.Appender;
+import org.apache.log4j.Layout;
+import org.apache.log4j.bridge.AppenderWrapper;
+import org.apache.log4j.bridge.FilterAdapter;
+import org.apache.log4j.bridge.FilterWrapper;
+import org.apache.log4j.bridge.LayoutAdapter;
+import org.apache.log4j.bridge.LayoutWrapper;
+import org.apache.log4j.builders.BooleanHolder;
+import org.apache.log4j.builders.Holder;
+import org.apache.log4j.spi.Filter;
+import org.apache.log4j.xml.XmlConfigurationFactory;
+import org.apache.logging.log4j.Logger;
+import org.apache.logging.log4j.core.appender.FileAppender;
+import org.apache.logging.log4j.plugins.Plugin;
+import org.apache.logging.log4j.status.StatusLogger;
+import org.w3c.dom.Element;
+
+import static org.apache.log4j.builders.BuilderManager.CATEGORY;
+import static org.apache.log4j.xml.XmlConfigurationFactory.*;
+
+/**
+ * Build a File Appender
+ */
+@Plugin(name = "org.apache.log4j.FileAppender", category = CATEGORY)
+public class FileAppenderBuilder implements AppenderBuilder {
+
+    private static final Logger LOGGER = StatusLogger.getLogger();
+
+    @Override
+    public Appender parseAppender(Element appenderElement, XmlConfigurationFactory factory) {
+        String name = appenderElement.getAttribute(NAME_ATTR);
+        Holder<Layout> layout = new Holder<>();
+        Holder<Filter> filter = new Holder<>();
+        Holder<String> fileName = new Holder<>();
+        Holder<Boolean> immediateFlush = new BooleanHolder();
+        Holder<Boolean> append = new BooleanHolder();
+        Holder<Boolean> bufferedIo = new BooleanHolder();
+        Holder<Integer> bufferSize = new Holder<>(8192);
+        forEachElement(appenderElement.getChildNodes(), (currentElement) -> {
+            switch (currentElement.getTagName()) {
+                case LAYOUT_TAG:
+                    layout.set(factory.parseLayout(currentElement));
+                    break;
+                case FILTER_TAG:
+                    filter.set(factory.parseFilters(currentElement));
+                    break;
+                case PARAM_TAG: {
+                    switch (currentElement.getAttribute(NAME_ATTR).toLowerCase()) {
+                        case FILE_PARAM:
+                            fileName.set(currentElement.getAttribute(VALUE_ATTR));
+                            break;
+                        case APPEND_PARAM: {
+                            String bool = currentElement.getAttribute(VALUE_ATTR);
+                            if (bool != null) {
+                                append.set(Boolean.parseBoolean(bool));
+                            } else {
+                                LOGGER.warn("No value provided for append parameter");
+                            }
+                            break;
+                        }
+                        case BUFFERED_IO_PARAM: {
+                            String bool = currentElement.getAttribute(VALUE_ATTR);
+                            if (bool != null) {
+                                bufferedIo.set(Boolean.parseBoolean(bool));
+                            } else {
+                                LOGGER.warn("No value provided for bufferedIo parameter");
+                            }
+                            break;
+                        }
+                        case BUFFER_SIZE_PARAM:
+                            String size = currentElement.getAttribute(VALUE_ATTR);
+                            if (size != null) {
+                                bufferSize.set(Integer.parseInt(size));
+                            } else {
+                                LOGGER.warn("No value provide for bufferSize parameter");
+                            }
+                            break;
+                    }
+                    break;
+                }
+            }
+        });
+
+        org.apache.logging.log4j.core.Layout<?> fileLayout = null;
+        org.apache.logging.log4j.core.Filter fileFilter = null;
+        if (bufferedIo.get()) {
+            immediateFlush.set(Boolean.TRUE);
+        }
+        if (layout.get() instanceof LayoutWrapper) {
+            fileLayout = ((LayoutWrapper) layout.get()).getLayout();
+        } else if (layout.get() != null) {
+            fileLayout = new LayoutAdapter(layout.get());
+        }
+        if (filter.get() != null) {
+            if (filter.get() instanceof FilterWrapper) {
+                fileFilter = ((FilterWrapper) filter.get()).getFilter();
+            } else {
+                fileFilter = new FilterAdapter(filter.get());
+            }
+        }
+        if (fileName.get() == null) {
+            LOGGER.warn("Unable to create File Appender, no file name provided");
+            return null;
+        }
+        return new AppenderWrapper(FileAppender.newBuilder()
+                .setName(name)
+                .setConfiguration(factory.getConfiguration())
+                .setLayout(fileLayout)
+                .setFilter(fileFilter)
+                .setFileName(fileName.get())
+                .setImmediateFlush(immediateFlush.get())
+                .setAppend(append.get())
+                .setBufferedIo(bufferedIo.get())
+                .setBufferSize(bufferSize.get())
+                .build());
+    }
+}
diff --git a/log4j-1.2-api/src/main/java/org/apache/log4j/builders/appender/NullAppenderBuilder.java b/log4j-1.2-api/src/main/java/org/apache/log4j/builders/appender/NullAppenderBuilder.java
new file mode 100644
index 0000000..b972d6b
--- /dev/null
+++ b/log4j-1.2-api/src/main/java/org/apache/log4j/builders/appender/NullAppenderBuilder.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.builders.appender;
+
+import org.apache.log4j.Appender;
+import org.apache.log4j.bridge.AppenderWrapper;
+import org.apache.log4j.xml.XmlConfigurationFactory;
+import org.apache.logging.log4j.Logger;
+import org.apache.logging.log4j.core.appender.NullAppender;
+import org.apache.logging.log4j.plugins.Plugin;
+import org.apache.logging.log4j.status.StatusLogger;
+import org.w3c.dom.Element;
+
+import static org.apache.log4j.builders.BuilderManager.CATEGORY;
+
+/**
+ * Build a Null Appender
+ */
+@Plugin(name = "org.apache.log4j.varia.NullAppender", category = CATEGORY)
+public class NullAppenderBuilder implements AppenderBuilder {
+
+    private static final Logger LOGGER = StatusLogger.getLogger();
+
+    @Override
+    public Appender parseAppender(Element appenderElement, XmlConfigurationFactory factory) {
+        String name = appenderElement.getAttribute("name");
+        return new AppenderWrapper(NullAppender.createAppender(name));
+    }
+}
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
new file mode 100644
index 0000000..81c3901
--- /dev/null
+++ b/log4j-1.2-api/src/main/java/org/apache/log4j/builders/appender/RollingFileAppenderBuilder.java
@@ -0,0 +1,170 @@
+/*
+ * 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 org.apache.log4j.Appender;
+import org.apache.log4j.Layout;
+import org.apache.log4j.bridge.AppenderWrapper;
+import org.apache.log4j.bridge.FilterAdapter;
+import org.apache.log4j.bridge.FilterWrapper;
+import org.apache.log4j.bridge.LayoutAdapter;
+import org.apache.log4j.bridge.LayoutWrapper;
+import org.apache.log4j.builders.BooleanHolder;
+import org.apache.log4j.builders.Holder;
+import org.apache.log4j.spi.Filter;
+import org.apache.log4j.xml.XmlConfigurationFactory;
+import org.apache.logging.log4j.Logger;
+import org.apache.logging.log4j.core.appender.RollingFileAppender;
+import org.apache.logging.log4j.core.appender.rolling.CompositeTriggeringPolicy;
+import org.apache.logging.log4j.core.appender.rolling.DefaultRolloverStrategy;
+import org.apache.logging.log4j.core.appender.rolling.RolloverStrategy;
+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.plugins.Plugin;
+import org.apache.logging.log4j.status.StatusLogger;
+import org.w3c.dom.Element;
+
+import static org.apache.log4j.builders.BuilderManager.CATEGORY;
+import static org.apache.log4j.xml.XmlConfigurationFactory.*;
+
+/**
+ * Build a File Appender
+ */
+@Plugin(name = "org.apache.log4j.RollingFileAppender", category = CATEGORY)
+public class RollingFileAppenderBuilder implements AppenderBuilder {
+
+    private static final Logger LOGGER = StatusLogger.getLogger();
+
+    @Override
+    public Appender parseAppender(Element appenderElement, XmlConfigurationFactory factory) {
+        String name = appenderElement.getAttribute(NAME_ATTR);
+        Holder<Layout> layout = new Holder<>();
+        Holder<Filter> filter = new Holder<>();
+        Holder<String> fileName = new Holder<>();
+        Holder<Boolean> immediateFlush = new BooleanHolder();
+        Holder<Boolean> append = new BooleanHolder();
+        Holder<Boolean> bufferedIo = new BooleanHolder();
+        Holder<Integer> bufferSize = new Holder<>(8192);
+        Holder<String> maxSize = new Holder<>();
+        Holder<String> maxBackups = new Holder<>();
+        forEachElement(appenderElement.getChildNodes(), (currentElement) -> {
+            switch (currentElement.getTagName()) {
+                case LAYOUT_TAG:
+                    layout.set(factory.parseLayout(currentElement));
+                    break;
+                case FILTER_TAG:
+                    filter.set(factory.parseFilters(currentElement));
+                    break;
+                case PARAM_TAG: {
+                    switch (currentElement.getAttribute(NAME_ATTR).toLowerCase()) {
+                        case FILE_PARAM:
+                            fileName.set(currentElement.getAttribute(VALUE_ATTR));
+                            break;
+                        case APPEND_PARAM: {
+                            String bool = currentElement.getAttribute(VALUE_ATTR);
+                            if (bool != null) {
+                                append.set(Boolean.parseBoolean(bool));
+                            } else {
+                                LOGGER.warn("No value provided for append parameter");
+                            }
+                            break;
+                        }
+                        case BUFFERED_IO_PARAM: {
+                            String bool = currentElement.getAttribute(VALUE_ATTR);
+                            if (bool != null) {
+                                bufferedIo.set(Boolean.parseBoolean(bool));
+                            } else {
+                                LOGGER.warn("No value provided for bufferedIo parameter");
+                            }
+                            break;
+                        }
+                        case BUFFER_SIZE_PARAM: {
+                            String size = currentElement.getAttribute(VALUE_ATTR);
+                            if (size != null) {
+                                bufferSize.set(Integer.parseInt(size));
+                            } else {
+                                LOGGER.warn("No value provide for bufferSize parameter");
+                            }
+                            break;
+                        }
+                        case MAX_BACKUP_INDEX: {
+                            String size = currentElement.getAttribute(VALUE_ATTR);
+                            if (size != null) {
+                                maxBackups.set(size);
+                            } else {
+                                LOGGER.warn("No value provide for maxBackupIndex parameter");
+                            }
+                            break;
+                        }
+                        case MAX_SIZE_PARAM: {
+                            String size = currentElement.getAttribute(VALUE_ATTR);
+                            if (size != null) {
+                                maxSize.set(size);
+                            } else {
+                                LOGGER.warn("No value provide for bufferSize parameter");
+                            }
+                            break;
+                        }
+                    }
+                    break;
+                }
+            }
+        });
+
+        org.apache.logging.log4j.core.Layout<?> fileLayout = null;
+        org.apache.logging.log4j.core.Filter fileFilter = null;
+        if (bufferedIo.get()) {
+            immediateFlush.set(Boolean.TRUE);
+        }
+        if (layout.get() instanceof LayoutWrapper) {
+            fileLayout = ((LayoutWrapper) layout.get()).getLayout();
+        } else if (layout.get() != null) {
+            fileLayout = new LayoutAdapter(layout.get());
+        }
+        if (filter.get() != null) {
+            if (filter.get() instanceof FilterWrapper) {
+                fileFilter = ((FilterWrapper) filter.get()).getFilter();
+            } else {
+                fileFilter = new FilterAdapter(filter.get());
+            }
+        }
+        if (fileName.get() == null) {
+            LOGGER.warn("Unable to create File Appender, no file name provided");
+            return null;
+        }
+        String filePattern = fileName.get() +"%d{yyy-MM-dd}";
+        TriggeringPolicy timePolicy = TimeBasedTriggeringPolicy.newBuilder().setModulate(true).build();
+        SizeBasedTriggeringPolicy sizePolicy = SizeBasedTriggeringPolicy.createPolicy(maxSize.get());
+        CompositeTriggeringPolicy policy = CompositeTriggeringPolicy.createPolicy(sizePolicy, timePolicy);
+        RolloverStrategy strategy = DefaultRolloverStrategy.newBuilder()
+                .setConfig(factory.getConfiguration())
+                .setMax(maxBackups.get())
+                .build();
+        return new AppenderWrapper(RollingFileAppender.newBuilder()
+                .setName(name)
+                .setConfiguration(factory.getConfiguration())
+                .setLayout(fileLayout)
+                .setFilter(fileFilter)
+                .setImmediateFlush(immediateFlush.get())
+                .setFileName(fileName.get())
+                .setFilePattern(filePattern)
+                .setPolicy(policy)
+                .setStrategy(strategy)
+                .build());
+    }
+}
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
new file mode 100644
index 0000000..b449ca0
--- /dev/null
+++ b/log4j-1.2-api/src/main/java/org/apache/log4j/builders/filter/DenyAllFilterBuilder.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.builders.filter;
+
+import org.apache.log4j.bridge.FilterWrapper;
+import org.apache.log4j.spi.Filter;
+import org.apache.log4j.xml.XmlConfigurationFactory;
+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, XmlConfigurationFactory factory) {
+        return new FilterWrapper(DenyAllFilter.newBuilder().build());
+    }
+}
diff --git a/log4j-1.2-api/src/main/java/org/apache/log4j/builders/filter/FilterBuilder.java b/log4j-1.2-api/src/main/java/org/apache/log4j/builders/filter/FilterBuilder.java
new file mode 100644
index 0000000..fbeaefb
--- /dev/null
+++ b/log4j-1.2-api/src/main/java/org/apache/log4j/builders/filter/FilterBuilder.java
@@ -0,0 +1,30 @@
+/*
+ * 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.filter;
+
+import org.apache.log4j.spi.Filter;
+import org.apache.log4j.xml.XmlConfigurationFactory;
+import org.w3c.dom.Element;
+
+/**
+ * Define a Filter Builder.
+ */
+public interface FilterBuilder {
+
+    Filter parseFilter(Element element, XmlConfigurationFactory factory);
+
+}
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
new file mode 100644
index 0000000..0d13e3f
--- /dev/null
+++ b/log4j-1.2-api/src/main/java/org/apache/log4j/builders/filter/LevelMatchFilterBuilder.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.builders.filter;
+
+import org.apache.log4j.bridge.FilterWrapper;
+import org.apache.log4j.builders.BooleanHolder;
+import org.apache.log4j.builders.Holder;
+import org.apache.log4j.spi.Filter;
+import org.apache.log4j.xml.XmlConfigurationFactory;
+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 static org.apache.log4j.builders.BuilderManager.CATEGORY;
+import static org.apache.log4j.xml.XmlConfigurationFactory.*;
+import static org.apache.log4j.xml.XmlConfigurationFactory.VALUE_ATTR;
+
+/**
+ * Build a Level match failter.
+ */
+@Plugin(name = "org.apache.log4j.varia.LevelMatchFilter", category = CATEGORY)
+public class LevelMatchFilterBuilder implements FilterBuilder {
+
+    private static final Logger LOGGER = StatusLogger.getLogger();
+    private static final String LEVEL = "level";
+    private static final String ACCEPT_ON_MATCH = "acceptonmatch";
+
+    @Override
+    public Filter parseFilter(Element filterElement, XmlConfigurationFactory factory) {
+        final Holder<String> level = new Holder<>();
+        final Holder<Boolean> acceptOnMatch = new BooleanHolder();
+        forEachElement(filterElement.getElementsByTagName("param"), (currentElement) -> {
+            if (currentElement.getTagName().equals("param")) {
+                switch (currentElement.getAttribute(NAME_ATTR).toLowerCase()) {
+                    case LEVEL:
+                        level.set(currentElement.getAttribute(VALUE_ATTR));
+                        break;
+                    case ACCEPT_ON_MATCH:
+                        acceptOnMatch.set(Boolean.parseBoolean(currentElement.getAttribute(VALUE_ATTR)));
+                        break;
+                }
+            }
+        });
+        Level lvl = Level.ERROR;
+        if (level.get() != null) {
+            lvl = Level.toLevel(level.get(), Level.ERROR);
+        }
+        org.apache.logging.log4j.core.Filter.Result onMatch = acceptOnMatch.get() != null && acceptOnMatch.get()
+                ? org.apache.logging.log4j.core.Filter.Result.ACCEPT
+                : org.apache.logging.log4j.core.Filter.Result.DENY;
+        return new FilterWrapper(LevelMatchFilter.newBuilder()
+                .setLevel(lvl)
+                .setOnMatch(onMatch)
+                .setOnMismatch(org.apache.logging.log4j.core.Filter.Result.NEUTRAL)
+                .build());
+    }
+}
diff --git a/log4j-1.2-api/src/main/java/org/apache/log4j/builders/filter/LevelRangeFilterBuilder.java b/log4j-1.2-api/src/main/java/org/apache/log4j/builders/filter/LevelRangeFilterBuilder.java
new file mode 100644
index 0000000..838aafdc
--- /dev/null
+++ b/log4j-1.2-api/src/main/java/org/apache/log4j/builders/filter/LevelRangeFilterBuilder.java
@@ -0,0 +1,81 @@
+/*
+ * 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.filter;
+
+import org.apache.log4j.bridge.FilterWrapper;
+import org.apache.log4j.builders.BooleanHolder;
+import org.apache.log4j.builders.Holder;
+import org.apache.log4j.spi.Filter;
+import org.apache.log4j.xml.XmlConfigurationFactory;
+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.LevelRangeFilter;
+import org.apache.logging.log4j.status.StatusLogger;
+import org.w3c.dom.Element;
+
+
+import static org.apache.log4j.builders.BuilderManager.CATEGORY;
+import static org.apache.log4j.xml.XmlConfigurationFactory.*;
+
+/**
+ * Build a Level match failter.
+ */
+@Plugin(name = "org.apache.log4j.varia.LevelRangeFilter", category = CATEGORY)
+public class LevelRangeFilterBuilder implements FilterBuilder {
+
+    private static final Logger LOGGER = StatusLogger.getLogger();
+    private static final String LEVEL_MAX = "levelmax";
+    private static final String LEVEL_MIN = "levelmin";
+    private static final String ACCEPT_ON_MATCH = "acceptonmatch";
+
+    @Override
+    public Filter parseFilter(Element filterElement, XmlConfigurationFactory factory) {
+        final Holder<String> levelMax = new Holder<>();
+        final Holder<String> levelMin = new Holder<>();
+        final Holder<Boolean> acceptOnMatch = new BooleanHolder();
+        forEachElement(filterElement.getElementsByTagName("param"), (currentElement) -> {
+            if (currentElement.getTagName().equals("param")) {
+                switch (currentElement.getAttribute(NAME_ATTR).toLowerCase()) {
+                    case LEVEL_MAX:
+                        levelMax.set(currentElement.getAttribute(VALUE_ATTR));
+                        break;
+                    case LEVEL_MIN:
+                        levelMax.set(currentElement.getAttribute(VALUE_ATTR));
+                        break;
+                    case ACCEPT_ON_MATCH:
+                        acceptOnMatch.set(Boolean.parseBoolean(currentElement.getAttribute(VALUE_ATTR)));
+                        break;
+                }
+            }
+        });
+        Level max = Level.FATAL;
+        Level min = Level.TRACE;
+        if (levelMax.get() != null) {
+            max = Level.toLevel(levelMax.get(), Level.FATAL);
+        }
+        if (levelMin.get() != null) {
+            min = Level.toLevel(levelMin.get(), Level.DEBUG);
+        }
+        org.apache.logging.log4j.core.Filter.Result onMatch = acceptOnMatch.get() != null && acceptOnMatch.get()
+                ? org.apache.logging.log4j.core.Filter.Result.ACCEPT
+                : org.apache.logging.log4j.core.Filter.Result.NEUTRAL;
+
+        return new FilterWrapper(LevelRangeFilter.createFilter(min, max, onMatch,
+                org.apache.logging.log4j.core.Filter.Result.DENY));
+    }
+}
diff --git a/log4j-1.2-api/src/main/java/org/apache/log4j/builders/filter/StringMatchFilterBuilder.java b/log4j-1.2-api/src/main/java/org/apache/log4j/builders/filter/StringMatchFilterBuilder.java
new file mode 100644
index 0000000..a8ec3bf
--- /dev/null
+++ b/log4j-1.2-api/src/main/java/org/apache/log4j/builders/filter/StringMatchFilterBuilder.java
@@ -0,0 +1,73 @@
+/*
+ * 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.filter;
+
+import org.apache.log4j.bridge.FilterWrapper;
+import org.apache.log4j.builders.BooleanHolder;
+import org.apache.log4j.builders.Holder;
+import org.apache.log4j.spi.Filter;
+import org.apache.log4j.xml.XmlConfigurationFactory;
+import org.apache.logging.log4j.Logger;
+import org.apache.logging.log4j.plugins.Plugin;
+import org.apache.logging.log4j.core.filter.StringMatchFilter;
+import org.apache.logging.log4j.status.StatusLogger;
+import org.w3c.dom.Element;
+
+import static org.apache.log4j.builders.BuilderManager.CATEGORY;
+import static org.apache.log4j.xml.XmlConfigurationFactory.*;
+
+/**
+ * Build a String match filter.
+ */
+@Plugin(name = "org.apache.log4j.varia.StringMatchFilter", category = CATEGORY)
+public class StringMatchFilterBuilder implements FilterBuilder {
+
+    private static final Logger LOGGER = StatusLogger.getLogger();
+    private static final String STRING_TO_MATCH = "stringtomatch";
+    private static final String ACCEPT_ON_MATCH = "acceptonmatch";
+
+    @Override
+    public Filter parseFilter(Element filterElement, XmlConfigurationFactory factory) {
+        final Holder<Boolean> acceptOnMatch = new BooleanHolder();
+        final Holder<String> text = new Holder<>();
+        forEachElement(filterElement.getElementsByTagName("param"), (currentElement) -> {
+            if (currentElement.getTagName().equals("param")) {
+                switch (currentElement.getAttribute(NAME_ATTR).toLowerCase()) {
+                    case STRING_TO_MATCH:
+                        text.set(currentElement.getAttribute(VALUE_ATTR));
+                        break;
+                    case ACCEPT_ON_MATCH:
+                        acceptOnMatch.set(Boolean.parseBoolean(currentElement.getAttribute(VALUE_ATTR)));
+                        break;
+
+                }
+            }
+        });
+        if (text.get() == null) {
+            LOGGER.warn("No text provided for StringMatchFilter");
+            return null;
+        }
+        org.apache.logging.log4j.core.Filter.Result onMatch = acceptOnMatch.get() != null && acceptOnMatch.get()
+                ? org.apache.logging.log4j.core.Filter.Result.ACCEPT
+                : org.apache.logging.log4j.core.Filter.Result.DENY;
+        return new FilterWrapper(StringMatchFilter.newBuilder()
+                .setMatchString(text.get())
+                .setOnMatch(onMatch)
+                .setOnMismatch(org.apache.logging.log4j.core.Filter.Result.NEUTRAL)
+                .build());
+    }
+}
diff --git a/log4j-1.2-api/src/main/java/org/apache/log4j/builders/layout/HtmlLayoutBuilder.java b/log4j-1.2-api/src/main/java/org/apache/log4j/builders/layout/HtmlLayoutBuilder.java
new file mode 100644
index 0000000..0cbbf70
--- /dev/null
+++ b/log4j-1.2-api/src/main/java/org/apache/log4j/builders/layout/HtmlLayoutBuilder.java
@@ -0,0 +1,60 @@
+/*
+ * 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 org.apache.log4j.Layout;
+import org.apache.log4j.bridge.LayoutWrapper;
+import org.apache.log4j.builders.BooleanHolder;
+import org.apache.log4j.builders.Holder;
+import org.apache.log4j.xml.XmlConfigurationFactory;
+import org.apache.logging.log4j.Logger;
+import org.apache.logging.log4j.plugins.Plugin;
+import org.apache.logging.log4j.core.layout.HtmlLayout;
+import org.apache.logging.log4j.status.StatusLogger;
+import org.w3c.dom.Element;
+
+import static org.apache.log4j.builders.BuilderManager.CATEGORY;
+import static org.apache.log4j.xml.XmlConfigurationFactory.*;
+
+/**
+ * Build a Pattern Layout
+ */
+@Plugin(name = "org.apache.log4j.HTMLLayout", category = CATEGORY)
+public class HtmlLayoutBuilder implements LayoutBuilder {
+
+    private static final Logger LOGGER = StatusLogger.getLogger();
+
+
+    @Override
+    public Layout parseLayout(Element layoutElement, XmlConfigurationFactory factory) {
+        final Holder<String> title = new Holder<>();
+        final Holder<Boolean> locationInfo = new BooleanHolder();
+        forEachElement(layoutElement.getElementsByTagName("param"), (currentElement) -> {
+            if (currentElement.getTagName().equals("param")) {
+                if ("title".equalsIgnoreCase(currentElement.getAttribute("name"))) {
+                    title.set(currentElement.getAttribute("value"));
+                } else if ("locationInfo".equalsIgnoreCase(currentElement.getAttribute("name"))) {
+                    locationInfo.set(Boolean.parseBoolean(currentElement.getAttribute("value")));
+                }
+            }
+        });
+        return new LayoutWrapper(HtmlLayout.newBuilder()
+                .setTitle(title.get())
+                .setLocationInfo(locationInfo.get())
+                .build());
+    }
+}
diff --git a/log4j-1.2-api/src/main/java/org/apache/log4j/builders/layout/LayoutBuilder.java b/log4j-1.2-api/src/main/java/org/apache/log4j/builders/layout/LayoutBuilder.java
new file mode 100644
index 0000000..9dc88f3
--- /dev/null
+++ b/log4j-1.2-api/src/main/java/org/apache/log4j/builders/layout/LayoutBuilder.java
@@ -0,0 +1,30 @@
+/*
+ * 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 org.apache.log4j.Layout;
+import org.apache.log4j.xml.XmlConfigurationFactory;
+import org.w3c.dom.Element;
+
+/**
+ * Define a Layout Builder.
+ */
+public interface LayoutBuilder {
+
+    Layout parseLayout(Element element, XmlConfigurationFactory factory);
+
+}
diff --git a/log4j-1.2-api/src/main/java/org/apache/log4j/builders/layout/PatternLayoutBuilder.java b/log4j-1.2-api/src/main/java/org/apache/log4j/builders/layout/PatternLayoutBuilder.java
new file mode 100644
index 0000000..e066778
--- /dev/null
+++ b/log4j-1.2-api/src/main/java/org/apache/log4j/builders/layout/PatternLayoutBuilder.java
@@ -0,0 +1,77 @@
+/*
+ * 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 org.apache.log4j.Layout;
+import org.apache.log4j.bridge.LayoutWrapper;
+import org.apache.log4j.xml.XmlConfigurationFactory;
+import org.apache.logging.log4j.Logger;
+import org.apache.logging.log4j.core.layout.PatternLayout;
+import org.apache.logging.log4j.plugins.Plugin;
+import org.apache.logging.log4j.plugins.PluginAliases;
+import org.apache.logging.log4j.status.StatusLogger;
+import org.w3c.dom.Element;
+import org.w3c.dom.Node;
+import org.w3c.dom.NodeList;
+
+import static org.apache.log4j.builders.BuilderManager.CATEGORY;
+
+/**
+ * Build a Pattern Layout
+ */
+@Plugin(name = "org.apache.log4j.PatternLayout", category = CATEGORY)
+@PluginAliases("org.apache.log4j.EnhancedPatternLayout")
+public class PatternLayoutBuilder implements LayoutBuilder {
+
+    private static final Logger LOGGER = StatusLogger.getLogger();
+
+    @Override
+    public Layout parseLayout(Element layoutElement, XmlConfigurationFactory factory) {
+        NodeList params = layoutElement.getElementsByTagName("param");
+        final int length = params.getLength();
+        String pattern = null;
+        for (int index = 0; index < length; ++ index) {
+            Node currentNode = params.item(index);
+            if (currentNode.getNodeType() == Node.ELEMENT_NODE) {
+                Element currentElement = (Element) currentNode;
+                if (currentElement.getTagName().equals("param")) {
+                    if ("conversionPattern".equalsIgnoreCase(currentElement.getAttribute("name"))) {
+                        pattern = currentElement.getAttribute("value")
+                                // Log4j 2's %x (NDC) is not compatible with Log4j 1's
+                                // %x
+                                // Log4j 1: "foo bar baz"
+                                // Log4j 2: "[foo, bar, baz]"
+                                // Use %ndc to get the Log4j 1 format
+                                .replace("%x", "%ndc")
+
+                                // Log4j 2's %X (MDC) is not compatible with Log4j 1's
+                                // %X
+                                // Log4j 1: "{{foo,bar}{hoo,boo}}"
+                                // Log4j 2: "{foo=bar,hoo=boo}"
+                                // Use %properties to get the Log4j 1 format
+                                .replace("%X", "%properties");
+                        break;
+                    }
+                }
+            }
+        }
+        return new LayoutWrapper(PatternLayout.newBuilder()
+                .setPattern(pattern)
+                .setConfiguration(factory.getConfiguration())
+                .build());
+    }
+}
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
new file mode 100644
index 0000000..411b2ef
--- /dev/null
+++ b/log4j-1.2-api/src/main/java/org/apache/log4j/builders/layout/SimpleLayoutBuilder.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.builders.layout;
+
+import org.apache.log4j.Layout;
+import org.apache.log4j.bridge.LayoutWrapper;
+import org.apache.log4j.xml.XmlConfigurationFactory;
+import org.apache.logging.log4j.Logger;
+import org.apache.logging.log4j.core.layout.PatternLayout;
+import org.apache.logging.log4j.plugins.Plugin;
+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.SimpleLayout", category = CATEGORY)
+public class SimpleLayoutBuilder implements LayoutBuilder {
+
+    private static final Logger LOGGER = StatusLogger.getLogger();
+
+    @Override
+    public Layout parseLayout(Element layoutElement, XmlConfigurationFactory factory) {
+        return new LayoutWrapper(PatternLayout.newBuilder()
+                .setPattern("%level - %m%n")
+                .setConfiguration(factory.getConfiguration())
+                .build());
+    }
+}
diff --git a/log4j-1.2-api/src/main/java/org/apache/log4j/builders/layout/TTCCLayoutBuilder.java b/log4j-1.2-api/src/main/java/org/apache/log4j/builders/layout/TTCCLayoutBuilder.java
new file mode 100644
index 0000000..a2e85de
--- /dev/null
+++ b/log4j-1.2-api/src/main/java/org/apache/log4j/builders/layout/TTCCLayoutBuilder.java
@@ -0,0 +1,103 @@
+/*
+ * 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 org.apache.log4j.Layout;
+import org.apache.log4j.bridge.LayoutWrapper;
+import org.apache.log4j.builders.BooleanHolder;
+import org.apache.log4j.builders.Holder;
+import org.apache.log4j.xml.XmlConfigurationFactory;
+import org.apache.logging.log4j.Logger;
+import org.apache.logging.log4j.core.layout.PatternLayout;
+import org.apache.logging.log4j.plugins.Plugin;
+import org.apache.logging.log4j.status.StatusLogger;
+import org.w3c.dom.Element;
+
+import static org.apache.log4j.builders.BuilderManager.CATEGORY;
+import static org.apache.log4j.xml.XmlConfigurationFactory.*;
+
+/**
+ * Build a Pattern Layout
+ */
+@Plugin(name = "org.apache.log4j.TTCCLayout", category = CATEGORY)
+public class TTCCLayoutBuilder implements LayoutBuilder {
+
+    private static final Logger LOGGER = StatusLogger.getLogger();
+
+    private static final String THREAD_PRINTING_PARAM = "threadprinting";
+    private static final String CATEGORY_PREFIXING_PARAM = "categoryprefixing";
+    private static final String CONTEXT_PRINTING_PARAM = "contextprinting";
+    private static final String DATE_FORMAT_PARAM = "dateformat";
+    private static final String TIMEZONE_FORMAT = "timezone";
+
+    @Override
+    public Layout parseLayout(Element layoutElement, XmlConfigurationFactory factory) {
+        final Holder<Boolean> threadPrinting = new BooleanHolder();
+        final Holder<Boolean> categoryPrefixing = new BooleanHolder();
+        final Holder<Boolean> contextPrinting = new BooleanHolder();
+        final Holder<String> dateFormat = new Holder<>();
+        final Holder<String> timezone = new Holder<>();
+        forEachElement(layoutElement.getElementsByTagName("param"), (currentElement) -> {
+            if (currentElement.getTagName().equals("param")) {
+                switch (currentElement.getAttribute(NAME_ATTR).toLowerCase()) {
+                    case THREAD_PRINTING_PARAM:
+                        threadPrinting.set(Boolean.parseBoolean(currentElement.getAttribute(VALUE_ATTR)));
+                        break;
+                    case CATEGORY_PREFIXING_PARAM:
+                        categoryPrefixing.set(Boolean.parseBoolean(currentElement.getAttribute(VALUE_ATTR)));
+                        break;
+                    case CONTEXT_PRINTING_PARAM:
+                        contextPrinting.set(Boolean.parseBoolean(currentElement.getAttribute(VALUE_ATTR)));
+                        break;
+                    case DATE_FORMAT_PARAM:
+                        dateFormat.set(currentElement.getAttribute(VALUE_ATTR));
+                        break;
+                    case TIMEZONE_FORMAT:
+                        timezone.set(currentElement.getAttribute(VALUE_ATTR));
+                        break;
+                }
+            }
+        });
+        StringBuilder sb = new StringBuilder();
+        if (dateFormat.get() != null) {
+            if (RELATIVE.equalsIgnoreCase(dateFormat.get())) {
+                sb.append("%r ");
+            } else {
+                sb.append("%d{").append(dateFormat.get()).append("}");
+                if (timezone.get() != null) {
+                    sb.append("{").append(timezone.get()).append("}");
+                }
+                sb.append(" ");
+            }
+        }
+        if (threadPrinting.get()) {
+            sb.append("[%t] ");
+        }
+        sb.append("%p ");
+        if (categoryPrefixing.get()) {
+            sb.append("%c ");
+        }
+        if (contextPrinting.get()) {
+            sb.append("%notEmpty{%ndc }");
+        }
+        sb.append("- %m%n");
+        return new LayoutWrapper(PatternLayout.newBuilder()
+                .setPattern(sb.toString())
+                .setConfiguration(factory.getConfiguration())
+                .build());
+    }
+}
diff --git a/log4j-1.2-api/src/main/java/org/apache/log4j/config/Log4j1Configuration.java b/log4j-1.2-api/src/main/java/org/apache/log4j/config/Log4j1Configuration.java
new file mode 100644
index 0000000..c0717cb
--- /dev/null
+++ b/log4j-1.2-api/src/main/java/org/apache/log4j/config/Log4j1Configuration.java
@@ -0,0 +1,56 @@
+/*
+ * 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.logging.log4j.core.LoggerContext;
+import org.apache.logging.log4j.core.config.AbstractConfiguration;
+import org.apache.logging.log4j.core.config.Configuration;
+import org.apache.logging.log4j.core.config.ConfigurationSource;
+import org.apache.logging.log4j.core.config.Reconfigurable;
+
+/**
+ * Class Description goes here.
+ */
+public class Log4j1Configuration extends AbstractConfiguration implements Reconfigurable {
+
+    public Log4j1Configuration(final LoggerContext loggerContext, final ConfigurationSource source,
+            int monitorIntervalSeconds) {
+        super(loggerContext, source);
+        initializeWatchers(this, source, monitorIntervalSeconds);
+    }
+
+    @Override
+    protected void doConfigure() {
+        super.getScheduler().start();
+
+    }
+
+    /**
+     * Initialize the configuration.
+     */
+    @Override
+    public void initialize() {
+        doConfigure();
+        setState(State.INITIALIZED);
+        LOGGER.debug("Configuration {} initialized", this);
+    }
+
+    @Override
+    public Configuration reconfigure() {
+        return null;
+    }
+}
diff --git a/log4j-1.2-api/src/main/java/org/apache/log4j/config/PropertySetter.java b/log4j-1.2-api/src/main/java/org/apache/log4j/config/PropertySetter.java
index 342024a..5982278 100644
--- a/log4j-1.2-api/src/main/java/org/apache/log4j/config/PropertySetter.java
+++ b/log4j-1.2-api/src/main/java/org/apache/log4j/config/PropertySetter.java
@@ -1,47 +1,156 @@
 /*
  * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements. See the NOTICE file distributed with
+ * 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 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
+ * 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.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
  */
+
+// Contributors:  Georg Lundesgaard
+
 package org.apache.log4j.config;
 
+import org.apache.log4j.Appender;
+import org.apache.log4j.Level;
+import org.apache.log4j.Priority;
+import org.apache.log4j.spi.ErrorHandler;
+import org.apache.log4j.spi.OptionHandler;
+import org.apache.logging.log4j.Logger;
+import org.apache.logging.log4j.core.util.OptionConverter;
+import org.apache.logging.log4j.status.StatusLogger;
+
+import java.beans.BeanInfo;
+import java.beans.IntrospectionException;
+import java.beans.Introspector;
 import java.beans.PropertyDescriptor;
+import java.io.InterruptedIOException;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
 import java.util.Properties;
 
 /**
+ * General purpose Object property setter. Clients repeatedly invokes
+ * {@link #setProperty setProperty(name,value)} in order to invoke setters
+ * on the Object specified in the constructor. This class relies on the
+ * JavaBeans {@link Introspector} to analyze the given Object Class using
+ * reflection.
  *
- * @since 1.1
+ * <p>Usage:
+ * <pre>
+ * PropertySetter ps = new PropertySetter(anObject);
+ * ps.set("name", "Joe");
+ * ps.set("age", "32");
+ * ps.set("isMale", "true");
+ * </pre>
+ * will cause the invocations anObject.setName("Joe"), anObject.setAge(32),
+ * and setMale(true) if such methods exist with those signatures.
+ * Otherwise an {@link IntrospectionException} are thrown.
  */
 public class PropertySetter {
+    private static Logger LOGGER = StatusLogger.getLogger();
+    protected Object obj;
+    protected PropertyDescriptor[] props;
 
     /**
      * Create a new PropertySetter for the specified Object. This is done
-     * in preparation for invoking {@link #setProperty} one or more times.
+     * in prepartion for invoking {@link #setProperty} one or more times.
      *
-     * @param obj  the object for which to set properties
+     * @param obj the object for which to set properties
      */
-    public PropertySetter(final Object obj) {
+    public PropertySetter(Object obj) {
+        this.obj = obj;
     }
 
+    /**
+     * Set the properties of an object passed as a parameter in one
+     * go. The <code>properties</code> are parsed relative to a
+     * <code>prefix</code>.
+     *
+     * @param obj        The object to configure.
+     * @param properties A java.util.Properties containing keys and values.
+     * @param prefix     Only keys having the specified prefix will be set.
+     */
+    public static void setProperties(Object obj, Properties properties, String prefix) {
+        new PropertySetter(obj).setProperties(properties, prefix);
+    }
 
     /**
-     * Set the properties for the object that match the <code>prefix</code> passed as parameter.
-     *
-     * @param properties The properties
-     * @param prefix The prefix
+     * Uses JavaBeans {@link Introspector} to computer setters of object to be
+     * configured.
      */
-    public void setProperties(final Properties properties, final String prefix) {
+    protected void introspect() {
+        try {
+            BeanInfo bi = Introspector.getBeanInfo(obj.getClass());
+            props = bi.getPropertyDescriptors();
+        } catch (IntrospectionException ex) {
+            LOGGER.error("Failed to introspect {}: {}", obj, ex.getMessage());
+            props = new PropertyDescriptor[0];
+        }
+    }
+
+    /**
+     * Set the properites for the object that match the
+     * <code>prefix</code> passed as parameter.
+     */
+    public void setProperties(Properties properties, String prefix) {
+        int len = prefix.length();
+
+        for (String key : properties.stringPropertyNames()) {
+
+            // handle only properties that start with the desired frefix.
+            if (key.startsWith(prefix)) {
+
+
+                // ignore key if it contains dots after the prefix
+                if (key.indexOf('.', len + 1) > 0) {
+                    continue;
+                }
+
+                String value = OptionConverter.findAndSubst(key, properties);
+                key = key.substring(len);
+                if (("layout".equals(key) || "errorhandler".equals(key)) && obj instanceof Appender) {
+                    continue;
+                }
+                //
+                //   if the property type is an OptionHandler
+                //     (for example, triggeringPolicy of org.apache.log4j.rolling.RollingFileAppender)
+                PropertyDescriptor prop = getPropertyDescriptor(Introspector.decapitalize(key));
+                if (prop != null
+                        && OptionHandler.class.isAssignableFrom(prop.getPropertyType())
+                        && prop.getWriteMethod() != null) {
+                    OptionHandler opt = (OptionHandler)
+                            OptionConverter.instantiateByKey(properties, prefix + key,
+                                    prop.getPropertyType(),
+                                    null);
+                    PropertySetter setter = new PropertySetter(opt);
+                    setter.setProperties(properties, prefix + key + ".");
+                    try {
+                        prop.getWriteMethod().invoke(this.obj, opt);
+                    } catch (InvocationTargetException ex) {
+                        if (ex.getTargetException() instanceof InterruptedException
+                                || ex.getTargetException() instanceof InterruptedIOException) {
+                            Thread.currentThread().interrupt();
+                        }
+                        LOGGER.warn("Failed to set property [{}] to value \"{}\".", key, value, ex);
+                    } catch (IllegalAccessException | RuntimeException ex) {
+                        LOGGER.warn("Failed to set property [{}] to value \"{}\".", key, value, ex);
+                    }
+                    continue;
+                }
+
+                setProperty(key, value);
+            }
+        }
+        activate();
     }
 
     /**
@@ -56,33 +165,123 @@
      * to an int using new Integer(value). If the setter expects a boolean,
      * the conversion is by new Boolean(value).
      *
-     * @param name    name of the property
-     * @param value   String value of the property
+     * @param name  name of the property
+     * @param value String value of the property
      */
-    public void setProperty(final String name, final String value) {
+    public void setProperty(String name, String value) {
+        if (value == null) {
+            return;
+        }
+
+        name = Introspector.decapitalize(name);
+        PropertyDescriptor prop = getPropertyDescriptor(name);
+
+        //LOGGER.debug("---------Key: "+name+", type="+prop.getPropertyType());
+
+        if (prop == null) {
+            LOGGER.warn("No such property [" + name + "] in " +
+                    obj.getClass().getName() + ".");
+        } else {
+            try {
+                setProperty(prop, name, value);
+            } catch (PropertySetterException ex) {
+                LOGGER.warn("Failed to set property [{}] to value \"{}\".", name, value, ex.rootCause);
+            }
+        }
     }
 
     /**
      * Set the named property given a {@link PropertyDescriptor}.
      *
-     * @param prop A PropertyDescriptor describing the characteristics of the property to set.
-     * @param name The named of the property to set.
+     * @param prop  A PropertyDescriptor describing the characteristics
+     *              of the property to set.
+     * @param name  The named of the property to set.
      * @param value The value of the property.
-     * @throws PropertySetterException (Never actually throws this exception. Kept for historical purposes.)
      */
-    public void setProperty(final PropertyDescriptor prop, final String name, final String value)
-        throws PropertySetterException {
+    public void setProperty(PropertyDescriptor prop, String name, String value)
+            throws PropertySetterException {
+        Method setter = prop.getWriteMethod();
+        if (setter == null) {
+            throw new PropertySetterException("No setter for property [" + name + "].");
+        }
+        Class[] paramTypes = setter.getParameterTypes();
+        if (paramTypes.length != 1) {
+            throw new PropertySetterException("#params for setter != 1");
+        }
+
+        Object arg;
+        try {
+            arg = convertArg(value, paramTypes[0]);
+        } catch (Throwable t) {
+            throw new PropertySetterException("Conversion to type [" + paramTypes[0] +
+                    "] failed. Reason: " + t);
+        }
+        if (arg == null) {
+            throw new PropertySetterException(
+                    "Conversion to type [" + paramTypes[0] + "] failed.");
+        }
+        LOGGER.debug("Setting property [" + name + "] to [" + arg + "].");
+        try {
+            setter.invoke(obj, arg);
+        } catch (InvocationTargetException ex) {
+            if (ex.getTargetException() instanceof InterruptedException
+                    || ex.getTargetException() instanceof InterruptedIOException) {
+                Thread.currentThread().interrupt();
+            }
+            throw new PropertySetterException(ex);
+        } catch (IllegalAccessException | RuntimeException ex) {
+            throw new PropertySetterException(ex);
+        }
     }
 
+
     /**
-     * Set the properties of an object passed as a parameter in one
-     * go. The <code>properties</code> are parsed relative to a
-     * <code>prefix</code>.
-     *
-     * @param obj The object to configure.
-     * @param properties A java.util.Properties containing keys and values.
-     * @param prefix Only keys having the specified prefix will be set.
+     * Convert <code>val</code> a String parameter to an object of a
+     * given type.
      */
-    public static void setProperties(final Object obj, final Properties properties, final String prefix) {
+    protected Object convertArg(String val, Class type) {
+        if (val == null) {
+            return null;
+        }
+
+        String v = val.trim();
+        if (String.class.isAssignableFrom(type)) {
+            return val;
+        } else if (Integer.TYPE.isAssignableFrom(type)) {
+            return Integer.parseInt(v);
+        } else if (Long.TYPE.isAssignableFrom(type)) {
+            return Long.parseLong(v);
+        } else if (Boolean.TYPE.isAssignableFrom(type)) {
+            if ("true".equalsIgnoreCase(v)) {
+                return Boolean.TRUE;
+            } else if ("false".equalsIgnoreCase(v)) {
+                return Boolean.FALSE;
+            }
+        } else if (Priority.class.isAssignableFrom(type)) {
+            return org.apache.log4j.helpers.OptionConverter.toLevel(v, Level.DEBUG);
+        } else if (ErrorHandler.class.isAssignableFrom(type)) {
+            return OptionConverter.instantiateByClassName(v,
+                    ErrorHandler.class, null);
+        }
+        return null;
+    }
+
+
+    protected PropertyDescriptor getPropertyDescriptor(String name) {
+        if (props == null) {
+            introspect();
+        }
+        for (PropertyDescriptor prop : props) {
+            if (name.equals(prop.getName())) {
+                return prop;
+            }
+        }
+        return null;
+    }
+
+    public void activate() {
+        if (obj instanceof OptionHandler) {
+            ((OptionHandler) obj).activateOptions();
+        }
     }
 }
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
new file mode 100644
index 0000000..8ee0ea5
--- /dev/null
+++ b/log4j-1.2-api/src/main/java/org/apache/log4j/helpers/OptionConverter.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.helpers;
+
+import org.apache.log4j.Level;
+
+import org.apache.logging.log4j.LogManager;
+import org.apache.logging.log4j.Logger;
+import org.apache.logging.log4j.util.LoaderUtil;
+
+import java.io.InterruptedIOException;
+import java.lang.reflect.InvocationTargetException;
+import java.util.Properties;
+
+/**
+ * A convenience class to convert property values to specific types.
+ */
+public class OptionConverter {
+    
+    static String DELIM_START = "${";
+    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 CharMap[] charMap = new CharMap[] {
+        new CharMap('n', '\n'),
+        new CharMap('r', '\r'),
+        new CharMap('t', '\t'),
+        new CharMap('f', '\f'),
+        new CharMap('\b', '\b'),
+        new CharMap('\"', '\"'),
+        new CharMap('\'', '\''),
+        new CharMap('\\', '\\')    
+    };
+
+    /**
+     * OptionConverter is a static class.
+     */
+    private OptionConverter() {
+    }
+
+    public static String[] concatanateArrays(String[] l, String[] r) {
+        int len = l.length + r.length;
+        String[] a = new String[len];
+
+        System.arraycopy(l, 0, a, 0, l.length);
+        System.arraycopy(r, 0, a, l.length, r.length);
+
+        return a;
+    }
+
+    public static String convertSpecialChars(String s) {
+        char c;
+        int len = s.length();
+        StringBuilder sbuf = new StringBuilder(len);
+
+        int i = 0;
+        while (i < len) {
+            c = s.charAt(i++);
+            if (c == '\\') {
+                c = s.charAt(i++);
+                for (CharMap entry : charMap) {
+                    if (entry.key == c) {
+                        c = entry.replacement;
+                    }
+                }
+            }
+            sbuf.append(c);
+        }
+        return sbuf.toString();
+    }
+
+
+    /**
+     * Very similar to <code>System.getProperty</code> except
+     * that the {@link SecurityException} is hidden.
+     *
+     * @param key The key to search for.
+     * @param def The default value to return.
+     * @return the string value of the system property, or the default
+     * value if there is no property with that key.
+     * @since 1.1
+     */
+    public static String getSystemProperty(String key, String def) {
+        try {
+            return System.getProperty(key, def);
+        } catch (Throwable e) { // MS-Java throws com.ms.security.SecurityExceptionEx
+            LOGGER.debug("Was not allowed to read system property \"{}\".", key);
+            return def;
+        }
+    }
+
+    /**
+     * If <code>value</code> is "true", then <code>true</code> is
+     * returned. If <code>value</code> is "false", then
+     * <code>true</code> is returned. Otherwise, <code>default</code> is
+     * returned.
+     *
+     * <p>Case of value is unimportant.
+     */
+    public static boolean toBoolean(String value, boolean dEfault) {
+        if (value == null) {
+            return dEfault;
+        }
+        String trimmedVal = value.trim();
+        if ("true".equalsIgnoreCase(trimmedVal)) {
+            return true;
+        }
+        if ("false".equalsIgnoreCase(trimmedVal)) {
+            return false;
+        }
+        return dEfault;
+    }
+
+    /**
+     * Converts a standard or custom priority level to a Level
+     * object.  <p> If <code>value</code> is of form
+     * "level#classname", then the specified class' toLevel method
+     * is called to process the specified level string; if no '#'
+     * character is present, then the default {@link org.apache.log4j.Level}
+     * class is used to process the level value.
+     *
+     * <p>As a special case, if the <code>value</code> parameter is
+     * equal to the string "NULL", then the value <code>null</code> will
+     * be returned.
+     *
+     * <p> If any error occurs while converting the value to a level,
+     * the <code>defaultValue</code> parameter, which may be
+     * <code>null</code>, is returned.
+     *
+     * <p> Case of <code>value</code> is insignificant for the level level, but is
+     * significant for the class name part, if present.
+     *
+     * @since 1.1
+     */
+    public static Level toLevel(String value, Level defaultValue) {
+        if (value == null) {
+            return defaultValue;
+        }
+
+        value = value.trim();
+
+        int hashIndex = value.indexOf('#');
+        if (hashIndex == -1) {
+            if ("NULL".equalsIgnoreCase(value)) {
+                return null;
+            } else {
+                // no class name specified : use standard Level class
+                return Level.toLevel(value, defaultValue);
+            }
+        }
+
+        Level result = defaultValue;
+
+        String clazz = value.substring(hashIndex + 1);
+        String levelName = value.substring(0, hashIndex);
+
+        // This is degenerate case but you never know.
+        if ("NULL".equalsIgnoreCase(levelName)) {
+            return null;
+        }
+
+        LOGGER.debug("toLevel" + ":class=[" + clazz + "]"
+                + ":pri=[" + levelName + "]");
+
+        try {
+            Class customLevel = LoaderUtil.loadClass(clazz);
+
+            // get a ref to the specified class' static method
+            // toLevel(String, org.apache.log4j.Level)
+            Class[] paramTypes = new Class[]{String.class,
+                    org.apache.log4j.Level.class
+            };
+            java.lang.reflect.Method toLevelMethod =
+                    customLevel.getMethod("toLevel", paramTypes);
+
+            // now call the toLevel method, passing level string + default
+            Object[] params = new Object[]{levelName, defaultValue};
+            Object o = toLevelMethod.invoke(null, params);
+
+            result = (Level) o;
+        } catch (ClassNotFoundException e) {
+            LOGGER.warn("custom level class [" + clazz + "] not found.");
+        } catch (NoSuchMethodException e) {
+            LOGGER.warn("custom level class [" + clazz + "]"
+                    + " does not have a class function toLevel(String, Level)", e);
+        } catch (java.lang.reflect.InvocationTargetException e) {
+            if (e.getTargetException() instanceof InterruptedException
+                    || e.getTargetException() instanceof InterruptedIOException) {
+                Thread.currentThread().interrupt();
+            }
+            LOGGER.warn("custom level class [" + clazz + "]"
+                    + " could not be instantiated", e);
+        } catch (ClassCastException e) {
+            LOGGER.warn("class [" + clazz
+                    + "] is not a subclass of org.apache.log4j.Level", e);
+        } catch (IllegalAccessException e) {
+            LOGGER.warn("class [" + clazz +
+                    "] cannot be instantiated due to access restrictions", e);
+        } catch (RuntimeException e) {
+            LOGGER.warn("class [" + clazz + "], level [" + levelName +
+                    "] conversion failed.", e);
+        }
+        return result;
+    }
+
+    /**
+     * Instantiate an object given a class name. Check that the
+     * <code>className</code> is a subclass of
+     * <code>superClass</code>. If that test fails or the object could
+     * not be instantiated, then <code>defaultValue</code> is returned.
+     *
+     * @param className    The fully qualified class name of the object to instantiate.
+     * @param superClass   The class to which the new object should belong.
+     * @param defaultValue The object to return in case of non-fulfillment
+     */
+    public static Object instantiateByClassName(String className, Class<?> superClass,
+            Object defaultValue) {
+        if (className != null) {
+            try {
+                Object obj = LoaderUtil.newInstanceOf(className);
+                if (!superClass.isAssignableFrom(obj.getClass())) {
+                    LOGGER.error("A \"{}\" object is not assignable to a \"{}\" variable", className,
+                            superClass.getName());
+                    return defaultValue;
+                }
+                return obj;
+            } catch (ClassNotFoundException | NoSuchMethodException | IllegalAccessException
+                    | InstantiationException | InvocationTargetException e) {
+                LOGGER.error("Could not instantiate class [" + className + "].", e);
+            }
+        }
+        return defaultValue;
+    }
+
+
+    /**
+     * Perform variable substitution in string <code>val</code> from the
+     * values of keys found in the system propeties.
+     *
+     * <p>The variable substitution delimeters are <b>${</b> and <b>}</b>.
+     *
+     * <p>For example, if the System properties contains "key=value", then
+     * the call
+     * <pre>
+     * String s = OptionConverter.substituteVars("Value of key is ${key}.");
+     * </pre>
+     * <p>
+     * will set the variable <code>s</code> to "Value of key is value.".
+     *
+     * <p>If no value could be found for the specified key, then the
+     * <code>props</code> parameter is searched, if the value could not
+     * be found there, then substitution defaults to the empty string.
+     *
+     * <p>For example, if system propeties contains no value for the key
+     * "inexistentKey", then the call
+     *
+     * <pre>
+     * String s = OptionConverter.subsVars("Value of inexistentKey is [${inexistentKey}]");
+     * </pre>
+     * will set <code>s</code> to "Value of inexistentKey is []"
+     *
+     * <p>An {@link IllegalArgumentException} is thrown if
+     * <code>val</code> contains a start delimeter "${" which is not
+     * balanced by a stop delimeter "}". </p>
+     *
+     * <p><b>Author</b> Avy Sharell</a></p>
+     *
+     * @param val The string on which variable substitution is performed.
+     * @throws IllegalArgumentException if <code>val</code> is malformed.
+     */
+    public static String substVars(String val, Properties props) throws IllegalArgumentException {
+
+        StringBuilder sbuf = new StringBuilder();
+
+        int i = 0;
+        int j, k;
+
+        while (true) {
+            j = val.indexOf(DELIM_START, i);
+            if (j == -1) {
+                // no more variables
+                if (i == 0) { // this is a simple string
+                    return val;
+                } else { // add the tail string which contails no variables and return the result.
+                    sbuf.append(val.substring(i, val.length()));
+                    return sbuf.toString();
+                }
+            } else {
+                sbuf.append(val.substring(i, j));
+                k = val.indexOf(DELIM_STOP, j);
+                if (k == -1) {
+                    throw new IllegalArgumentException('"' + val +
+                            "\" has no closing brace. Opening brace at position " + j
+                            + '.');
+                } else {
+                    j += DELIM_START_LEN;
+                    String key = val.substring(j, k);
+                    // first try in System properties
+                    String replacement = getSystemProperty(key, null);
+                    // then try props parameter
+                    if (replacement == null && props != null) {
+                        replacement = props.getProperty(key);
+                    }
+
+                    if (replacement != null) {
+                        // Do variable substitution on the replacement string
+                        // such that we can solve "Hello ${x2}" as "Hello p1"
+                        // the where the properties are
+                        // x1=p1
+                        // x2=${x1}
+                        String recursiveReplacement = substVars(replacement, props);
+                        sbuf.append(recursiveReplacement);
+                    }
+                    i = k + DELIM_STOP_LEN;
+                }
+            }
+        }
+    }
+    
+    private static class CharMap {
+        final char key;
+        final char replacement;
+        
+        public CharMap(char key, char replacement) {
+            this.key = key;
+            this.replacement = replacement;
+        }
+    }
+}
diff --git a/log4j-1.2-api/src/main/java/org/apache/log4j/helpers/QuietWriter.java b/log4j-1.2-api/src/main/java/org/apache/log4j/helpers/QuietWriter.java
new file mode 100644
index 0000000..1779019
--- /dev/null
+++ b/log4j-1.2-api/src/main/java/org/apache/log4j/helpers/QuietWriter.java
@@ -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.
+ */
+
+package org.apache.log4j.helpers;
+
+import org.apache.log4j.spi.ErrorCode;
+import org.apache.log4j.spi.ErrorHandler;
+
+import java.io.FilterWriter;
+import java.io.Writer;
+
+
+/**
+ * QuietWriter does not throw exceptions when things go
+ * wrong. Instead, it delegates error handling to its {@link ErrorHandler}.
+ */
+public class QuietWriter extends FilterWriter {
+
+    protected ErrorHandler errorHandler;
+
+    public QuietWriter(Writer writer, ErrorHandler errorHandler) {
+        super(writer);
+        setErrorHandler(errorHandler);
+    }
+
+    public void write(String string) {
+        if (string != null) {
+            try {
+                out.write(string);
+            } catch (Exception e) {
+                errorHandler.error("Failed to write [" + string + "].", e,
+                        ErrorCode.WRITE_FAILURE);
+            }
+        }
+    }
+
+    public void flush() {
+        try {
+            out.flush();
+        } catch (Exception e) {
+            errorHandler.error("Failed to flush writer,", e,
+                    ErrorCode.FLUSH_FAILURE);
+        }
+    }
+
+
+    public void setErrorHandler(ErrorHandler eh) {
+        if (eh == null) {
+            // This is a programming error on the part of the enclosing appender.
+            throw new IllegalArgumentException("Attempted to set null ErrorHandler.");
+        } else {
+            this.errorHandler = eh;
+        }
+    }
+}
diff --git a/log4j-1.2-api/src/main/java/org/apache/log4j/or/ObjectRenderer.java b/log4j-1.2-api/src/main/java/org/apache/log4j/or/ObjectRenderer.java
new file mode 100644
index 0000000..f3fed18
--- /dev/null
+++ b/log4j-1.2-api/src/main/java/org/apache/log4j/or/ObjectRenderer.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.or;
+
+/**
+ * Converts objects to Strings.
+ */
+public interface ObjectRenderer {
+    /**
+     * Render the object passed as parameter as a String.
+     */
+	 String doRender(Object o);
+}
diff --git a/log4j-1.2-api/src/main/java/org/apache/log4j/or/RendererSupport.java b/log4j-1.2-api/src/main/java/org/apache/log4j/or/RendererSupport.java
new file mode 100644
index 0000000..9b8728d
--- /dev/null
+++ b/log4j-1.2-api/src/main/java/org/apache/log4j/or/RendererSupport.java
@@ -0,0 +1,26 @@
+/*
+ * 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.or;
+
+import java.util.Map;
+
+/**
+ * Interface that indicates the Renderer Map is available. This interface differs
+ */
+public interface RendererSupport {
+    Map<Class<?>, ObjectRenderer> getRendererMap();
+}
diff --git a/log4j-1.2-api/src/main/java/org/apache/log4j/or/ThreadGroupRenderer.java b/log4j-1.2-api/src/main/java/org/apache/log4j/or/ThreadGroupRenderer.java
new file mode 100644
index 0000000..08233bf
--- /dev/null
+++ b/log4j-1.2-api/src/main/java/org/apache/log4j/or/ThreadGroupRenderer.java
@@ -0,0 +1,57 @@
+/*
+ * 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.or;
+
+import org.apache.log4j.Layout;
+
+/**
+ */
+public class ThreadGroupRenderer implements ObjectRenderer {
+
+    public
+    String  doRender(Object obj) {
+        if(obj instanceof ThreadGroup) {
+            StringBuilder sb = new StringBuilder();
+            ThreadGroup threadGroup = (ThreadGroup) obj;
+            sb.append("java.lang.ThreadGroup[name=");
+            sb.append(threadGroup.getName());
+            sb.append(", maxpri=");
+            sb.append(threadGroup.getMaxPriority());
+            sb.append("]");
+            Thread[] threads = new Thread[threadGroup.activeCount()];
+            threadGroup.enumerate(threads);
+            for (Thread thread : threads) {
+                sb.append(Layout.LINE_SEP);
+                sb.append("   Thread=[");
+                sb.append(thread.getName());
+                sb.append(",");
+                sb.append(thread.getPriority());
+                sb.append(",");
+                sb.append(thread.isDaemon());
+                sb.append("]");
+            }
+            return sb.toString();
+        } else {
+            try {
+                // this is the best we can do
+                return obj.toString();
+            } catch(Exception ex) {
+                return ex.toString();
+            }
+        }
+    }
+}
diff --git a/log4j-1.2-api/src/main/java/org/apache/log4j/spi/AppenderAttachable.java b/log4j-1.2-api/src/main/java/org/apache/log4j/spi/AppenderAttachable.java
new file mode 100644
index 0000000..fd464a2
--- /dev/null
+++ b/log4j-1.2-api/src/main/java/org/apache/log4j/spi/AppenderAttachable.java
@@ -0,0 +1,70 @@
+/*
+ * 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 org.apache.log4j.Appender;
+
+import java.util.Enumeration;
+
+/**
+ * Interface for attaching appenders to objects.
+ */
+public interface AppenderAttachable {
+
+    /**
+     * Add an appender.
+     */
+    void addAppender(Appender newAppender);
+
+    /**
+     * Get all previously added appenders as an Enumeration.
+     */
+    Enumeration getAllAppenders();
+
+    /**
+     * Get an appender by name.
+     */
+    Appender getAppender(String name);
+
+
+    /**
+     * Returns <code>true</code> if the specified appender is in list of
+     * attached attached, <code>false</code> otherwise.
+     *
+     * @since 1.2
+     */
+    boolean isAttached(Appender appender);
+
+    /**
+     * Remove all previously added appenders.
+     */
+    void removeAllAppenders();
+
+
+    /**
+     * Remove the appender passed as parameter from the list of appenders.
+     */
+    void removeAppender(Appender appender);
+
+
+    /**
+     * Remove the appender with the name passed as parameter from the
+     * list of appenders.
+     */
+    void removeAppender(String name);
+}
diff --git a/log4j-1.2-api/src/main/java/org/apache/log4j/spi/Configurator.java b/log4j-1.2-api/src/main/java/org/apache/log4j/spi/Configurator.java
new file mode 100644
index 0000000..b418db8
--- /dev/null
+++ b/log4j-1.2-api/src/main/java/org/apache/log4j/spi/Configurator.java
@@ -0,0 +1,55 @@
+/*
+ * 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.InputStream;
+import java.net.URL;
+
+import org.apache.logging.log4j.core.LoggerContext;
+
+/**
+ * Log4j 1.x Configurator interface.
+ */
+public interface Configurator {
+
+    public static final String INHERITED = "inherited";
+
+    public static final String NULL = "null";
+
+
+    /**
+     Interpret a resource pointed by a InputStream and set up log4j accordingly.
+
+     The configuration is done relative to the <code>hierarchy</code>
+     parameter.
+
+     @param inputStream The InputStream to parse
+
+     @since 1.2.17
+     */
+    void doConfigure(InputStream inputStream, final LoggerContext loggerContext);
+
+    /**
+     Interpret a resource pointed by a URL and set up log4j accordingly.
+
+     The configuration is done relative to the <code>hierarchy</code>
+     parameter.
+
+     @param url The URL to parse
+     */
+    void doConfigure(URL url, final LoggerContext loggerContext);
+}
diff --git a/log4j-1.2-api/src/main/java/org/apache/log4j/spi/ErrorCode.java b/log4j-1.2-api/src/main/java/org/apache/log4j/spi/ErrorCode.java
new file mode 100644
index 0000000..7fbbf95
--- /dev/null
+++ b/log4j-1.2-api/src/main/java/org/apache/log4j/spi/ErrorCode.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.spi;
+
+
+/**
+   This interface defines commonly encoutered error codes.
+ */
+public interface ErrorCode {
+
+  public final int GENERIC_FAILURE = 0;
+  public final int WRITE_FAILURE = 1;
+  public final int FLUSH_FAILURE = 2;
+  public final int CLOSE_FAILURE = 3;
+  public final int FILE_OPEN_FAILURE = 4;
+  public final int MISSING_LAYOUT = 5;
+  public final int ADDRESS_PARSE_FAILURE = 6;
+}
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 9bba50c..997398b 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,10 +16,24 @@
  */
 package org.apache.log4j.spi;
 
+import org.apache.log4j.bridge.FilterAdapter;
+
 /**
  * @since 0.9.0
  */
 public abstract class Filter {
+    private final FilterAdapter adapter;
+
+    public Filter() {
+        FilterAdapter filterAdapter = null;
+        try {
+            Class.forName("org.apache.logging.log4j.core.Filter");
+            filterAdapter = new FilterAdapter(this);
+        } catch(ClassNotFoundException ex) {
+            // Ignore the exception. Log4j Core is not present.
+        }
+        this.adapter = filterAdapter;
+    }
 
     /**
      * The log event must be dropped immediately without consulting
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
new file mode 100644
index 0000000..2102802
--- /dev/null
+++ b/log4j-1.2-api/src/main/java/org/apache/log4j/spi/LocationInfo.java
@@ -0,0 +1,75 @@
+/*
+ * 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;
+
+/**
+ The internal representation of caller location information.
+
+ @since 0.8.3
+ */
+public class LocationInfo implements java.io.Serializable {
+
+    private final StackTraceElement element;
+
+    public String fullInfo;
+
+    public LocationInfo(StackTraceElement element) {
+        this.element = element;
+    }
+
+    /**
+     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.
+     */
+    public
+    String getClassName() {
+        return element.getClassName();
+    }
+
+    /**
+     Return the file name of the caller.
+     */
+    public
+    String getFileName() {
+        return element.getFileName();
+    }
+
+    /**
+     Returns the line number of the caller.
+     */
+    public
+    String getLineNumber() {
+        return Integer.toString(element.getLineNumber());
+    }
+
+    /**
+     Returns the method name of the caller.
+     */
+    public
+    String getMethodName() {
+        return element.getMethodName();
+    }
+}
diff --git a/log4j-1.2-api/src/main/java/org/apache/log4j/spi/LoggingEvent.java b/log4j-1.2-api/src/main/java/org/apache/log4j/spi/LoggingEvent.java
index 5f4b172..e9f57de 100644
--- a/log4j-1.2-api/src/main/java/org/apache/log4j/spi/LoggingEvent.java
+++ b/log4j-1.2-api/src/main/java/org/apache/log4j/spi/LoggingEvent.java
@@ -16,8 +16,116 @@
  */
 package org.apache.log4j.spi;
 
+import org.apache.log4j.Category;
+import org.apache.log4j.Level;
+import org.apache.log4j.bridge.LogEventAdapter;
+
 /**
- *  No-op version of Log4j 1.2 LoggingEvent.
+ *  No-op version of Log4j 1.2 LoggingEvent. This class is not directly used by Log4j 1.x clients but is used by
+ *  the Log4j 2 LogEvent adapter to be compatible with Log4j 1.x components.
  */
 public class LoggingEvent {
+
+    /**
+     Set the location information for this logging event. The collected
+     information is cached for future use.
+     */
+    public LocationInfo getLocationInformation() {
+        return null;
+    }
+
+    /**
+     * Return the level of this event. Use this form instead of directly
+     * accessing the <code>level</code> field.  */
+    public Level getLevel() {
+        return null;
+    }
+
+    /**
+     * Return the name of the logger. Use this form instead of directly
+     * accessing the <code>categoryName</code> field.
+     */
+    public String getLoggerName() {
+        return null;
+    }
+
+    /**
+     * Gets the logger of the event.
+     * Use should be restricted to cloning events.
+     * @since 1.2.15
+     */
+    public Category getLogger() {
+        return null;
+    }
+
+    /**
+     Return the message for this logging event.
+
+     <p>Before serialization, the returned object is the message
+     passed by the user to generate the logging event. After
+     serialization, the returned value equals the String form of the
+     message possibly after object rendering.
+
+     @since 1.1 */
+    public
+    Object getMessage() {
+        return null;
+    }
+
+    public
+    String getNDC() {
+        return null;
+    }
+
+    public
+    Object getMDC(String key) {
+        return null;
+    }
+
+    /**
+     Obtain a copy of this thread's MDC prior to serialization or
+     asynchronous logging.
+     */
+    public
+    void getMDCCopy() {
+    }
+
+    public
+    String getRenderedMessage() {
+        return null;
+    }
+
+    /**
+     Returns the time when the application started, in milliseconds
+     elapsed since 01.01.1970.  */
+    public static long getStartTime() {
+        return LogEventAdapter.getStartTime();
+    }
+
+    public
+    String getThreadName() {
+        return null;
+    }
+
+    /**
+     Returns the throwable information contained within this
+     event. May be <code>null</code> if there is no such information.
+
+     <p>Note that the {@link Throwable} object contained within a
+     {@link ThrowableInformation} does not survive serialization.
+
+     @since 1.1 */
+    public
+    ThrowableInformation getThrowableInformation() {
+        return null;
+    }
+
+    /**
+     Return this event's throwable's string[] representaion.
+     */
+    public
+    String[] getThrowableStrRep() {
+        return null;
+    }
+
 }
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
new file mode 100644
index 0000000..5a9ace5
--- /dev/null
+++ b/log4j-1.2-api/src/main/java/org/apache/log4j/spi/ThrowableInformation.java
@@ -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.
+ */
+package org.apache.log4j.spi;
+
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.util.List;
+
+import org.apache.logging.log4j.core.util.Throwables;
+
+/**
+ * Class Description goes here.
+ */
+public class ThrowableInformation implements java.io.Serializable {
+
+    static final long serialVersionUID = -4748765566864322735L;
+
+    private transient Throwable throwable;
+    private Method toStringList;
+
+    @SuppressWarnings("unchecked")
+    public
+    ThrowableInformation(Throwable throwable) {
+        this.throwable = throwable;
+        Method method = null;
+        try {
+            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;
+    }
+
+    public
+    Throwable getThrowable() {
+        return throwable;
+    }
+
+    public synchronized String[] getThrowableStrRep() {
+        if (toStringList != null && throwable != null) {
+            try {
+                @SuppressWarnings("unchecked")
+                List<String> elements = (List<String>) toStringList.invoke(null, throwable);
+                if (elements != null) {
+                    return elements.toArray(new String[0]);
+                }
+            } catch (IllegalAccessException | InvocationTargetException ex) {
+                // Ignore the exception.
+            }
+        }
+        return null;
+    }
+}
+
diff --git a/log4j-1.2-api/src/main/java/org/apache/log4j/xml/Log4jEntityResolver.java b/log4j-1.2-api/src/main/java/org/apache/log4j/xml/Log4jEntityResolver.java
new file mode 100644
index 0000000..edda022
--- /dev/null
+++ b/log4j-1.2-api/src/main/java/org/apache/log4j/xml/Log4jEntityResolver.java
@@ -0,0 +1,51 @@
+/*
+ * 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.logging.log4j.Logger;
+import org.apache.logging.log4j.status.StatusLogger;
+import org.xml.sax.EntityResolver;
+import org.xml.sax.InputSource;
+
+import java.io.ByteArrayInputStream;
+import java.io.InputStream;
+
+/**
+ * An {@link EntityResolver} specifically designed to return
+ * <code>log4j.dtd</code> which is embedded within the log4j jar
+ * file.
+ */
+public class Log4jEntityResolver implements EntityResolver {
+    private static final Logger LOGGER = StatusLogger.getLogger();
+    private static final String PUBLIC_ID = "-//APACHE//DTD LOG4J 1.2//EN";
+
+    public InputSource resolveEntity(String publicId, String systemId) {
+        if (systemId.endsWith("log4j.dtd") || PUBLIC_ID.equals(publicId)) {
+            Class clazz = getClass();
+            InputStream in = clazz.getResourceAsStream("/org/apache/log4j/xml/log4j.dtd");
+            if (in == null) {
+                LOGGER.warn("Could not find [log4j.dtd] using [{}] class loader, parsed without DTD.",
+                        clazz.getClassLoader());
+                in = new ByteArrayInputStream(new byte[0]);
+            }
+            return new InputSource(in);
+        } else {
+            return null;
+        }
+    }
+}
diff --git a/log4j-1.2-api/src/main/java/org/apache/log4j/xml/UnrecognizedElementHandler.java b/log4j-1.2-api/src/main/java/org/apache/log4j/xml/UnrecognizedElementHandler.java
new file mode 100644
index 0000000..463d5d9
--- /dev/null
+++ b/log4j-1.2-api/src/main/java/org/apache/log4j/xml/UnrecognizedElementHandler.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.xml;
+
+import org.w3c.dom.Element;
+import java.util.Properties;
+
+/**
+ * When implemented by an object configured by DOMConfigurator,
+ * the handle method will be called when an unrecognized child
+ * element is encountered.  Unrecognized child elements of
+ * the log4j:configuration element will be dispatched to
+ * the logger repository if it supports this interface.
+ *
+ * @since 1.2.15
+ */
+public interface UnrecognizedElementHandler {
+    /**
+     * Called to inform a configured object when
+     * an unrecognized child element is encountered.
+     * @param element element, may not be null.
+     * @param props properties in force, may be null.
+     * @return true if configured object recognized the element
+     * @throws Exception throw an exception to prevent activation
+     * of the configured object.
+     */
+    boolean parseUnrecognizedElement(Element element, Properties props) throws Exception;
+}
\ No newline at end of file
diff --git a/log4j-1.2-api/src/main/java/org/apache/log4j/xml/XmlConfigurationFactory.java b/log4j-1.2-api/src/main/java/org/apache/log4j/xml/XmlConfigurationFactory.java
new file mode 100644
index 0000000..17235a4
--- /dev/null
+++ b/log4j-1.2-api/src/main/java/org/apache/log4j/xml/XmlConfigurationFactory.java
@@ -0,0 +1,909 @@
+/*
+ * 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.Appender;
+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.builders.BuilderManager;
+import org.apache.log4j.config.Log4j1Configuration;
+import org.apache.log4j.config.PropertySetter;
+import org.apache.log4j.helpers.OptionConverter;
+import org.apache.log4j.spi.AppenderAttachable;
+import org.apache.log4j.spi.Configurator;
+import org.apache.log4j.spi.ErrorHandler;
+import org.apache.log4j.spi.Filter;
+import org.apache.logging.log4j.core.LoggerContext;
+import org.apache.logging.log4j.core.config.Configuration;
+import org.apache.logging.log4j.core.config.ConfigurationFactory;
+import org.apache.logging.log4j.core.config.ConfigurationSource;
+import org.apache.logging.log4j.core.config.LoggerConfig;
+import org.apache.logging.log4j.core.config.Order;
+import org.apache.logging.log4j.core.config.status.StatusConfiguration;
+import org.apache.logging.log4j.plugins.Plugin;
+import org.apache.logging.log4j.status.StatusLogger;
+import org.apache.logging.log4j.util.LoaderUtil;
+import org.w3c.dom.Document;
+import org.w3c.dom.Element;
+import org.w3c.dom.NamedNodeMap;
+import org.w3c.dom.Node;
+import org.w3c.dom.NodeList;
+import org.xml.sax.InputSource;
+import org.xml.sax.SAXException;
+import org.xml.sax.SAXParseException;
+
+import javax.xml.parsers.DocumentBuilder;
+import javax.xml.parsers.DocumentBuilderFactory;
+import javax.xml.parsers.FactoryConfigurationError;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InterruptedIOException;
+import java.io.Reader;
+import java.lang.reflect.Method;
+import java.net.URL;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Properties;
+import java.util.concurrent.TimeUnit;
+import java.util.function.Consumer;
+
+/**
+ * Constructs a Configuration usable in Log4j 2 from a Log4j 1 configuration file.
+ */
+@Plugin(name = "Log4j1XmlConfigurationFactory", category = ConfigurationFactory.CATEGORY)
+@Order(2)
+public class XmlConfigurationFactory extends ConfigurationFactory implements Configurator {
+    private static final org.apache.logging.log4j.Logger LOGGER = StatusLogger.getLogger();
+
+    private static final String CONFIGURATION_TAG = "log4j:configuration";
+    private static final String OLD_CONFIGURATION_TAG = "configuration";
+    private static final String RENDERER_TAG = "renderer";
+    private static final String APPENDER_TAG = "appender";
+    private static final String APPENDER_REF_TAG = "appender-ref";
+    public  static final String PARAM_TAG = "param";
+    public static final String LAYOUT_TAG = "layout";
+    private static final String CATEGORY = "category";
+    private static final String LOGGER_ELEMENT = "logger";
+    private static final String CATEGORY_FACTORY_TAG = "categoryFactory";
+    private static final String LOGGER_FACTORY_TAG = "loggerFactory";
+    public static final String NAME_ATTR = "name";
+    private static final String CLASS_ATTR = "class";
+    public static final String VALUE_ATTR = "value";
+    private static final String ROOT_TAG = "root";
+    private static final String LEVEL_TAG = "level";
+    private static final String PRIORITY_TAG = "priority";
+    public static final String FILTER_TAG = "filter";
+    private static final String ERROR_HANDLER_TAG = "errorHandler";
+    private static final String REF_ATTR = "ref";
+    private static final String ADDITIVITY_ATTR = "additivity";
+    private static final String CONFIG_DEBUG_ATTR = "configDebug";
+    private static final String INTERNAL_DEBUG_ATTR = "debug";
+    private static final String EMPTY_STR = "";
+    private static final Class[] ONE_STRING_PARAM = new Class[]{String.class};
+    private static final String dbfKey = "javax.xml.parsers.DocumentBuilderFactory";
+    private static final String THROWABLE_RENDERER_TAG = "throwableRenderer";
+    private static final String SYSTEM_OUT = "System.out";
+    private static final String SYSTEM_ERR = "System.err";
+    private static final String THREAD_PRINTING_PARAM = "threadprinting";
+    private static final String CATEGORY_PREFIXING_PARAM = "categoryprefixing";
+    private static final String CONTEXT_PRINTING_PARAM = "contextprinting";
+    private static final String DATE_FORMAT_PARAM = "dateformat";
+    private static final String TIMEZONE_FORMAT = "timezone";
+    public static final String FILE_PARAM = "file";
+    public static final String APPEND_PARAM = "append";
+    public static final String BUFFERED_IO_PARAM = "bufferedio";
+    public static final String BUFFER_SIZE_PARAM = "buffersize";
+    public static final String MAX_SIZE_PARAM = "maxfileSize";
+    public static final String MAX_BACKUP_INDEX = "maxbackupindex";
+    public static final String RELATIVE = "RELATIVE";
+    public static final long DEFAULT_DELAY = 60000;
+    /**
+     * File name prefix for test configurations.
+     */
+    protected static final String TEST_PREFIX = "log4j-test";
+
+    /**
+     * File name prefix for standard configurations.
+     */
+    protected static final String DEFAULT_PREFIX = "log4j";
+
+    private final BuilderManager manager;
+
+    // key: appenderName, value: appender
+    private Map<String, Appender> appenderBag;
+
+    private Properties props = null;
+
+    private final LoggerContext loggerContext;
+    private Log4j1Configuration configuration;
+
+    /**
+     * No argument constructor.
+     */
+    public XmlConfigurationFactory() {
+        appenderBag = new HashMap<>();
+        loggerContext = (LoggerContext) org.apache.logging.log4j.LogManager.getContext(false);
+        manager = new BuilderManager();
+    }
+
+
+    private XmlConfigurationFactory(ConfigurationSource source, int monitorIntervalSeconds) {
+        appenderBag = new HashMap<>();
+        loggerContext = (LoggerContext) org.apache.logging.log4j.LogManager.getContext(false);
+        configuration = new Log4j1Configuration(loggerContext, source, monitorIntervalSeconds);
+        manager = new BuilderManager();
+    }
+
+    @Override
+    protected String[] getSupportedTypes() {
+        return new String[] {".xml"};
+    }
+
+    @Override
+    public Configuration getConfiguration(LoggerContext loggerContext, ConfigurationSource source) {
+        configuration = new Log4j1Configuration(loggerContext, source, 0);
+        doConfigure();
+        return configuration;
+    }
+
+    public Configuration getConfiguration() {
+        return configuration;
+    }
+
+    @Override
+    protected String getTestPrefix() {
+        return TEST_PREFIX;
+    }
+
+    @Override
+    protected String getDefaultPrefix() {
+        return DEFAULT_PREFIX;
+    }
+
+    /**
+     * Delegates unrecognized content to created instance if
+     * it supports UnrecognizedElementParser.
+     *
+     * @param instance instance, may be null.
+     * @param element  element, may not be null.
+     * @param props    properties
+     * @throws IOException thrown if configuration of owner object
+     *                     should be abandoned.
+     * @since 1.2.15
+     */
+    private static void parseUnrecognizedElement(final Object instance, final Element element,
+            final Properties props) throws Exception {
+        boolean recognized = false;
+        if (instance instanceof UnrecognizedElementHandler) {
+            recognized = ((UnrecognizedElementHandler) instance).parseUnrecognizedElement(
+                    element, props);
+        }
+        if (!recognized) {
+            LOGGER.warn("Unrecognized element {}", element.getNodeName());
+        }
+    }
+
+    /**
+     * Delegates unrecognized content to created instance if
+     * it supports UnrecognizedElementParser and catches and
+     * logs any exception.
+     *
+     * @param instance instance, may be null.
+     * @param element  element, may not be null.
+     * @param props    properties
+     * @since 1.2.15
+     */
+    private static void quietParseUnrecognizedElement(final Object instance,
+            final Element element,
+            final Properties props) {
+        try {
+            parseUnrecognizedElement(instance, element, props);
+        } catch (Exception ex) {
+            if (ex instanceof InterruptedException || ex instanceof InterruptedIOException) {
+                Thread.currentThread().interrupt();
+            }
+            LOGGER.error("Error in extension content: ", ex);
+        }
+    }
+
+    /**
+     * Like {@link #configureAndWatch(String, long)} except that the
+     * default delay is used.
+     *
+     * @param configFilename A log4j configuration file in XML format.
+     */
+    public static void configureAndWatch(final String configFilename) {
+        configureAndWatch(configFilename, DEFAULT_DELAY);
+    }
+
+    /**
+     * Read the configuration file <code>configFilename</code> if it
+     * exists. Moreover, a thread will be created that will periodically
+     * check if <code>configFilename</code> has been created or
+     * modified. The period is determined by the <code>delay</code>
+     * argument. If a change or file creation is detected, then
+     * <code>configFilename</code> is read to configure log4j.
+     *
+     * @param configFilename A log4j configuration file in XML format.
+     * @param delay          The delay in milliseconds to wait between each check.
+     */
+    public static void configureAndWatch(final String configFilename, final long delay) {
+        try {
+            File file = new File(configFilename);
+            InputStream is = new FileInputStream(file);
+            ConfigurationSource source = new ConfigurationSource(is, file);
+            int seconds = (int) TimeUnit.MILLISECONDS.toSeconds(delay);
+            XmlConfigurationFactory factory = new XmlConfigurationFactory(source, seconds);
+            factory.doConfigure();
+            org.apache.logging.log4j.core.config.Configurator.reconfigure(factory.getConfiguration());
+
+        } catch (IOException ioe) {
+            LOGGER.error("Unable to process configuration file {} due to {}", configFilename, ioe.getMessage());
+        }
+    }
+
+    /**
+     * A static version of doConfigure(String).
+     */
+    public static void configure(final String filename) throws FactoryConfigurationError {
+        configureAndWatch(filename, 0);
+    }
+
+    /**
+     * A static version of doConfigure(URL).
+     */
+    public static void configure(final URL url) throws FactoryConfigurationError {
+        try {
+            InputStream is = url.openStream();
+            ConfigurationSource source = new ConfigurationSource(is, url);
+            XmlConfigurationFactory factory = new XmlConfigurationFactory(source, 0);
+            factory.doConfigure();
+            org.apache.logging.log4j.core.config.Configurator.reconfigure(factory.getConfiguration());
+        } catch (IOException ioe) {
+            LOGGER.error("Unable to process configuration {} due to {}", url.toString(), ioe.getMessage());
+        }
+    }
+
+    /**
+     * Substitutes property value for any references in expression.
+     *
+     * @param value value from configuration file, may contain
+     *              literal text, property references or both
+     * @param props properties.
+     * @return evaluated expression, may still contain expressions
+     * if unable to expand.
+     */
+    public static String subst(final String value, final Properties props) {
+        try {
+            return OptionConverter.substVars(value, props);
+        } catch (IllegalArgumentException e) {
+            LOGGER.warn("Could not perform variable substitution.", e);
+            return value;
+        }
+    }
+
+    /**
+     * Sets a parameter based from configuration file content.
+     *
+     * @param elem       param element, may not be null.
+     * @param propSetter property setter, may not be null.
+     * @param props      properties
+     * @since 1.2.15
+     */
+    public static void setParameter(final Element elem, final PropertySetter propSetter, final Properties props) {
+        String name = subst(elem.getAttribute("name"), props);
+        String value = (elem.getAttribute("value"));
+        value = subst(OptionConverter.convertSpecialChars(value), props);
+        propSetter.setProperty(name, value);
+    }
+
+    /**
+     * Creates an object and processes any nested param elements
+     * but does not call activateOptions.  If the class also supports
+     * UnrecognizedElementParser, the parseUnrecognizedElement method
+     * will be call for any child elements other than param.
+     *
+     * @param element       element, may not be null.
+     * @param props         properties
+     * @param expectedClass interface or class expected to be implemented
+     *                      by created class
+     * @return created class or null.
+     * @throws Exception thrown if the contain object should be abandoned.
+     * @since 1.2.15
+     */
+    public static Object parseElement(final Element element, final Properties props,
+            @SuppressWarnings("rawtypes") final Class expectedClass) throws Exception {
+        String clazz = subst(element.getAttribute("class"), props);
+        Object instance = OptionConverter.instantiateByClassName(clazz,
+                expectedClass, null);
+
+        if (instance != null) {
+            PropertySetter propSetter = new PropertySetter(instance);
+            NodeList children = element.getChildNodes();
+            final int length = children.getLength();
+
+            for (int loop = 0; loop < length; loop++) {
+                Node currentNode = children.item(loop);
+                if (currentNode.getNodeType() == Node.ELEMENT_NODE) {
+                    Element currentElement = (Element) currentNode;
+                    String tagName = currentElement.getTagName();
+                    if (tagName.equals("param")) {
+                        setParameter(currentElement, propSetter, props);
+                    } else {
+                        parseUnrecognizedElement(instance, currentElement, props);
+                    }
+                }
+            }
+            return instance;
+        }
+        return null;
+    }
+
+    /**
+     * Used internally to parse appenders by IDREF name.
+     */
+    private Appender findAppenderByName(Document doc, String appenderName) {
+        Appender appender = appenderBag.get(appenderName);
+
+        if (appender != null) {
+            return appender;
+        } else {
+            // Doesn't work on DOM Level 1 :
+            // Element element = doc.getElementById(appenderName);
+
+            // Endre's hack:
+            Element element = null;
+            NodeList list = doc.getElementsByTagName("appender");
+            for (int t = 0; t < list.getLength(); t++) {
+                Node node = list.item(t);
+                NamedNodeMap map = node.getAttributes();
+                Node attrNode = map.getNamedItem("name");
+                if (appenderName.equals(attrNode.getNodeValue())) {
+                    element = (Element) node;
+                    break;
+                }
+            }
+            // Hack finished.
+
+            if (element == null) {
+
+                LOGGER.error("No appender named [{}] could be found.", appenderName);
+                return null;
+            } else {
+                appender = parseAppender(element);
+                if (appender != null) {
+                    appenderBag.put(appenderName, appender);
+                }
+                return appender;
+            }
+        }
+    }
+
+    /**
+     * Used internally to parse appenders by IDREF element.
+     */
+    private Appender findAppenderByReference(Element appenderRef) {
+        String appenderName = subst(appenderRef.getAttribute(REF_ATTR));
+        Document doc = appenderRef.getOwnerDocument();
+        return findAppenderByName(doc, appenderName);
+    }
+
+    /**
+     * Used internally to parse an appender element.
+     */
+    private Appender parseAppender(Element appenderElement) {
+        String className = subst(appenderElement.getAttribute(CLASS_ATTR));
+        LOGGER.debug("Class name: [" + className + ']');
+        Appender appender = manager.parseAppender(className, appenderElement, this);
+        if (appender == null) {
+            appender = buildAppender(className, appenderElement);
+        }
+        return appender;
+    }
+
+    private Appender buildAppender(String className, Element appenderElement) {
+            try {
+                Appender appender = LoaderUtil.newInstanceOf(className);
+                PropertySetter propSetter = new PropertySetter(appender);
+
+                appender.setName(subst(appenderElement.getAttribute(NAME_ATTR)));
+                forEachElement(appenderElement.getChildNodes(), (currentElement) -> {
+                    // Parse appender parameters
+                    switch (currentElement.getTagName()) {
+                        case PARAM_TAG:
+                            setParameter(currentElement, propSetter);
+                            break;
+                        case LAYOUT_TAG:
+                            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);
+                            }
+                            break;
+                        case ERROR_HANDLER_TAG:
+                            parseErrorHandler(currentElement, appender);
+                            break;
+                        case APPENDER_REF_TAG:
+                            String refName = subst(currentElement.getAttribute(REF_ATTR));
+                            if (appender instanceof AppenderAttachable) {
+                                AppenderAttachable aa = (AppenderAttachable) appender;
+                                Appender child = findAppenderByReference(currentElement);
+                                LOGGER.debug("Attaching appender named [{}] to appender named [{}].", refName,
+                                        appender.getName());
+                                aa.addAppender(child);
+                            } else {
+                                LOGGER.error("Requesting attachment of appender named [{}] to appender named [{}}]"
+                                                + "which does not implement org.apache.log4j.spi.AppenderAttachable.",
+                                        refName, appender.getName());
+                            }
+                            break;
+                        default:
+                            try {
+                                parseUnrecognizedElement(appender, currentElement, props);
+                            } catch (Exception ex) {
+                                throw new ConsumerException(ex);
+                            }
+                    }
+                });
+                propSetter.activate();
+                return appender;
+            } catch (ConsumerException ex) {
+                Throwable t = ex.getCause();
+                if (t instanceof InterruptedException || t instanceof InterruptedIOException) {
+                    Thread.currentThread().interrupt();
+                }
+                LOGGER.error("Could not create an Appender. Reported error follows.", t);
+            } catch (Exception oops) {
+                if (oops instanceof InterruptedException || oops instanceof InterruptedIOException) {
+                    Thread.currentThread().interrupt();
+                }
+                LOGGER.error("Could not create an Appender. Reported error follows.", oops);
+            }
+            return null;
+        }
+
+    /**
+     * Used internally to parse an {@link ErrorHandler} element.
+     */
+    private void parseErrorHandler(Element element, Appender appender) {
+        ErrorHandler eh = (ErrorHandler) OptionConverter.instantiateByClassName(
+                subst(element.getAttribute(CLASS_ATTR)),
+                ErrorHandler.class,
+                null);
+
+        if (eh != null) {
+            eh.setAppender(appender);
+
+            PropertySetter propSetter = new PropertySetter(eh);
+            forEachElement(element.getChildNodes(), (currentElement) -> {
+                String tagName = currentElement.getTagName();
+                if (tagName.equals(PARAM_TAG)) {
+                    setParameter(currentElement, propSetter);
+                }
+            });
+            propSetter.activate();
+            appender.setErrorHandler(eh);
+        }
+    }
+
+    /**
+     * 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) {
+            PropertySetter propSetter = new PropertySetter(filter);
+            forEachElement(filterElement.getChildNodes(), (currentElement) -> {
+                String tagName = currentElement.getTagName();
+                if (tagName.equals(PARAM_TAG)) {
+                    setParameter(currentElement, propSetter);
+                } else {
+                    quietParseUnrecognizedElement(filter, currentElement, props);
+                }
+            });
+            propSetter.activate();
+        }
+        return filter;
+    }
+
+    /**
+     * Used internally to parse an category element.
+     */
+    private void parseCategory(Element loggerElement) {
+        // Create a new org.apache.log4j.Category object from the <category> element.
+        String catName = subst(loggerElement.getAttribute(NAME_ATTR));
+        boolean additivity = OptionConverter.toBoolean(subst(loggerElement.getAttribute(ADDITIVITY_ATTR)), true);
+        LoggerConfig loggerConfig = configuration.getLogger(catName);
+        if (loggerConfig == null) {
+            loggerConfig = new LoggerConfig(catName, org.apache.logging.log4j.Level.ERROR, additivity);
+            configuration.addLogger(catName, loggerConfig);
+        } else {
+            loggerConfig.setAdditive(additivity);
+        }
+        parseChildrenOfLoggerElement(loggerElement, loggerConfig, false);
+    }
+
+    /**
+     * Used internally to parse the roor category element.
+     */
+    private void parseRoot(Element rootElement) {
+        LoggerConfig root = configuration.getRootLogger();
+        parseChildrenOfLoggerElement(rootElement, root, true);
+    }
+
+    /**
+     * Used internally to parse the children of a LoggerConfig element.
+     */
+    private void parseChildrenOfLoggerElement(Element catElement, LoggerConfig loggerConfig, boolean isRoot) {
+
+        final PropertySetter propSetter = new PropertySetter(loggerConfig);
+        loggerConfig.getAppenderRefs().clear();
+        forEachElement(catElement.getChildNodes(), (currentElement) -> {
+            switch (currentElement.getTagName()) {
+                case APPENDER_REF_TAG: {
+                    Appender appender = findAppenderByReference(currentElement);
+                    String refName = subst(currentElement.getAttribute(REF_ATTR));
+                    if (appender != null) {
+                        LOGGER.debug("Adding appender named [{}] to loggerConfig [{}].", refName,
+                                loggerConfig.getName());
+                        loggerConfig.addAppender(configuration.getAppender(refName), null, null);
+                    } else {
+                        LOGGER.debug("Appender named [{}}] not found.", refName);
+                    }
+                    break;
+                }
+                case LEVEL_TAG: case PRIORITY_TAG: {
+                    parseLevel(currentElement, loggerConfig, isRoot);
+                    break;
+                }
+                case PARAM_TAG: {
+                    setParameter(currentElement, propSetter);
+                    break;
+                }
+                default: {
+                    quietParseUnrecognizedElement(loggerConfig, currentElement, props);
+                }
+            }
+        });
+        propSetter.activate();
+    }
+
+    /**
+     * Used internally to parse a layout element.
+     */
+    public Layout parseLayout(Element layoutElement) {
+        String className = subst(layoutElement.getAttribute(CLASS_ATTR));
+        LOGGER.debug("Parsing layout of class: \"{}\"", className);
+        Layout layout = manager.parseLayout(className, layoutElement, this);
+        if (layout == null) {
+            layout = buildLayout(className, layoutElement);
+        }
+        return layout;
+    }
+
+    private Layout buildLayout(String className, Element layout_element) {
+        try {
+            Layout layout = LoaderUtil.newInstanceOf(className);
+            PropertySetter propSetter = new PropertySetter(layout);
+            forEachElement(layout_element.getChildNodes(), (currentElement) -> {
+                String tagName = currentElement.getTagName();
+                if (tagName.equals(PARAM_TAG)) {
+                    setParameter(currentElement, propSetter);
+                } else {
+                    try {
+                        parseUnrecognizedElement(layout, currentElement, props);
+                    } catch (Exception ex) {
+                        throw new ConsumerException(ex);
+                    }
+                }
+            });
+
+            propSetter.activate();
+            return layout;
+        } catch (ConsumerException ce) {
+            Throwable cause = ce.getCause();
+            if (cause instanceof InterruptedException || cause instanceof InterruptedIOException) {
+                Thread.currentThread().interrupt();
+            }
+            LOGGER.error("Could not create the Layout. Reported error follows.", cause);
+        } catch (Exception oops) {
+            if (oops instanceof InterruptedException || oops instanceof InterruptedIOException) {
+                Thread.currentThread().interrupt();
+            }
+            LOGGER.error("Could not create the Layout. Reported error follows.", oops);
+        }
+        return null;
+    }
+
+    /**
+     * Used internally to parse a level  element.
+     */
+    private void parseLevel(Element element, LoggerConfig logger, boolean isRoot) {
+        String catName = logger.getName();
+        if (isRoot) {
+            catName = "root";
+        }
+
+        String priStr = subst(element.getAttribute(VALUE_ATTR));
+        LOGGER.debug("Level value for {} is [{}}].", catName, priStr);
+
+        if (INHERITED.equalsIgnoreCase(priStr) || NULL.equalsIgnoreCase(priStr)) {
+            if (isRoot) {
+                LOGGER.error("Root level cannot be inherited. Ignoring directive.");
+            } else {
+                logger.setLevel(null);
+            }
+        } else {
+            String className = subst(element.getAttribute(CLASS_ATTR));
+            if (EMPTY_STR.equals(className)) {
+                logger.setLevel(convertLevel(OptionConverter.toLevel(priStr, Level.DEBUG)));
+            } else {
+                LOGGER.debug("Desired Level sub-class: [{}]", className);
+                try {
+                    Class<?> clazz = LoaderUtil.loadClass(className);
+                    Method toLevelMethod = clazz.getMethod("toLevel", ONE_STRING_PARAM);
+                    Level pri = (Level) toLevelMethod.invoke(null, new Object[]{priStr});
+                    logger.setLevel(convertLevel(pri));
+                } catch (Exception oops) {
+                    if (oops instanceof InterruptedException || oops instanceof InterruptedIOException) {
+                        Thread.currentThread().interrupt();
+                    }
+                    LOGGER.error("Could not create level [" + priStr +
+                            "]. Reported error follows.", oops);
+                    return;
+                }
+            }
+        }
+        LOGGER.debug("{} level set to {}", catName,  logger.getLevel());
+    }
+
+    private void setParameter(Element elem, PropertySetter propSetter) {
+        String name = subst(elem.getAttribute(NAME_ATTR));
+        String value = (elem.getAttribute(VALUE_ATTR));
+        value = subst(OptionConverter.convertSpecialChars(value));
+        propSetter.setProperty(name, value);
+    }
+
+    /**
+     * Configure log4j by reading in a log4j.dtd compliant XML
+     * configuration file.
+     */
+    private void doConfigure() throws FactoryConfigurationError {
+        ConfigurationSource source = configuration.getConfigurationSource();
+        ParseAction action = new ParseAction() {
+            public Document parse(final DocumentBuilder parser) throws SAXException, IOException {
+                InputSource inputSource = new InputSource(source.getInputStream());
+                inputSource.setSystemId("dummy://log4j.dtd");
+                return parser.parse(inputSource);
+            }
+
+            public String toString() {
+                return configuration.getConfigurationSource().getLocation();
+            }
+        };
+        doConfigure(action);
+    }
+
+    private void doConfigure(final ParseAction action) throws FactoryConfigurationError {
+        DocumentBuilderFactory dbf;
+        try {
+            LOGGER.debug("System property is : {}", OptionConverter.getSystemProperty(dbfKey, null));
+            dbf = DocumentBuilderFactory.newInstance();
+            LOGGER.debug("Standard DocumentBuilderFactory search succeded.");
+            LOGGER.debug("DocumentBuilderFactory is: " + dbf.getClass().getName());
+        } catch (FactoryConfigurationError fce) {
+            Exception e = fce.getException();
+            LOGGER.debug("Could not instantiate a DocumentBuilderFactory.", e);
+            throw fce;
+        }
+
+        try {
+            dbf.setValidating(true);
+
+            DocumentBuilder docBuilder = dbf.newDocumentBuilder();
+
+            docBuilder.setErrorHandler(new SAXErrorHandler());
+            docBuilder.setEntityResolver(new Log4jEntityResolver());
+
+            Document doc = action.parse(docBuilder);
+            parse(doc.getDocumentElement());
+        } catch (Exception e) {
+            if (e instanceof InterruptedException || e instanceof InterruptedIOException) {
+                Thread.currentThread().interrupt();
+            }
+            // I know this is miserable...
+            LOGGER.error("Could not parse " + action.toString() + ".", e);
+        }
+    }
+
+    @Override
+    public void doConfigure(InputStream inputStream, LoggerContext loggerContext) {
+        try {
+            ConfigurationSource source = new ConfigurationSource(inputStream);
+            configuration = new Log4j1Configuration(loggerContext, source, 0);
+            doConfigure();
+        } catch (IOException ioe) {
+            LOGGER.error("Unable to process configuration due to {}",  ioe.getMessage());
+        }
+    }
+
+    @Override
+    public void doConfigure(URL url, LoggerContext loggerContext) {
+        try {
+            ConfigurationSource source = new ConfigurationSource(url.openStream(), url);
+            configuration = new Log4j1Configuration(loggerContext, source, 0);
+            doConfigure();
+        } catch (IOException ioe) {
+            LOGGER.error("Unable to process configuration due to {}",  ioe.getMessage());
+        }
+    }
+
+    /**
+     * Used internally to configure the log4j framework by parsing a DOM
+     * tree of XML elements based on <a
+     * href="doc-files/log4j.dtd">log4j.dtd</a>.
+     */
+    private void parse(Element element) {
+        String rootElementName = element.getTagName();
+
+        if (!rootElementName.equals(CONFIGURATION_TAG)) {
+            if (rootElementName.equals(OLD_CONFIGURATION_TAG)) {
+                LOGGER.warn("The <" + OLD_CONFIGURATION_TAG +
+                        "> element has been deprecated.");
+                LOGGER.warn("Use the <" + CONFIGURATION_TAG + "> element instead.");
+            } else {
+                LOGGER.error("DOM element is - not a <" + CONFIGURATION_TAG + "> element.");
+                return;
+            }
+        }
+
+
+        String debugAttrib = subst(element.getAttribute(INTERNAL_DEBUG_ATTR));
+
+        LOGGER.debug("debug attribute= \"" + debugAttrib + "\".");
+        // if the log4j.dtd is not specified in the XML file, then the
+        // "debug" attribute is returned as the empty string.
+        String status = "error";
+        if (!debugAttrib.equals("") && !debugAttrib.equals("null")) {
+            status = OptionConverter.toBoolean(debugAttrib, true) ? "debug" : "error";
+
+        } else {
+            LOGGER.debug("Ignoring " + INTERNAL_DEBUG_ATTR + " attribute.");
+        }
+
+        String confDebug = subst(element.getAttribute(CONFIG_DEBUG_ATTR));
+        if (!confDebug.equals("") && !confDebug.equals("null")) {
+            LOGGER.warn("The \"" + CONFIG_DEBUG_ATTR + "\" attribute is deprecated.");
+            LOGGER.warn("Use the \"" + INTERNAL_DEBUG_ATTR + "\" attribute instead.");
+            status = OptionConverter.toBoolean(confDebug, true) ? "debug" : "error";
+        }
+
+        final StatusConfiguration statusConfig = new StatusConfiguration().setStatus(status);
+        statusConfig.initialize();
+
+        forEachElement(element.getChildNodes(), (currentElement) -> {
+            switch (currentElement.getTagName()) {
+                case CATEGORY: case LOGGER_ELEMENT:
+                    parseCategory(currentElement);
+                    break;
+                case ROOT_TAG:
+                    parseRoot(currentElement);
+                    break;
+                case RENDERER_TAG:
+                    LOGGER.warn("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.");
+                    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.");
+                    break;
+                case APPENDER_TAG:
+                    Appender appender = parseAppender(currentElement);
+                    appenderBag.put(appender.getName(), appender);
+                    if (appender instanceof AppenderWrapper) {
+                        configuration.addAppender(((AppenderWrapper) appender).getAppender());
+                    } else {
+                        configuration.addAppender(new AppenderAdapter(appender).getAdapter());
+                    }
+                    break;
+                default:
+                    quietParseUnrecognizedElement(null, currentElement, props);
+            }
+        });
+    }
+
+    private org.apache.logging.log4j.Level convertLevel(Level level) {
+        if (level == null) {
+            return org.apache.logging.log4j.Level.ERROR;
+        }
+        if (level.isGreaterOrEqual(Level.FATAL)) {
+            return org.apache.logging.log4j.Level.FATAL;
+        } else if (level.isGreaterOrEqual(Level.ERROR)) {
+            return org.apache.logging.log4j.Level.ERROR;
+        } else if (level.isGreaterOrEqual(Level.WARN)) {
+            return org.apache.logging.log4j.Level.WARN;
+        } else if (level.isGreaterOrEqual(Level.INFO)) {
+            return org.apache.logging.log4j.Level.INFO;
+        } else if (level.isGreaterOrEqual(Level.DEBUG)) {
+            return org.apache.logging.log4j.Level.DEBUG;
+        } else if (level.isGreaterOrEqual(Level.TRACE)) {
+            return org.apache.logging.log4j.Level.TRACE;
+        }
+        return org.apache.logging.log4j.Level.ALL;
+    }
+
+    private String subst(final String value) {
+        return configuration.getStrSubstitutor().replace(value);
+    }
+
+    public static void forEachElement(NodeList list, Consumer<Element> consumer) {
+        final int length = list.getLength();
+        for (int loop = 0; loop < length; loop++) {
+            Node currentNode = list.item(loop);
+
+            if (currentNode.getNodeType() == Node.ELEMENT_NODE) {
+                Element currentElement = (Element) currentNode;
+                consumer.accept(currentElement);
+            }
+        }
+    }
+
+    private interface ParseAction {
+        Document parse(final DocumentBuilder parser) throws SAXException, IOException;
+    }
+
+    private static class SAXErrorHandler implements org.xml.sax.ErrorHandler {
+        private static final org.apache.logging.log4j.Logger LOGGER = StatusLogger.getLogger();
+
+        public void error(final SAXParseException ex) {
+            emitMessage("Continuable parsing error ", ex);
+        }
+
+        public void fatalError(final SAXParseException ex) {
+            emitMessage("Fatal parsing error ", ex);
+        }
+
+        public void warning(final SAXParseException ex) {
+            emitMessage("Parsing warning ", ex);
+        }
+
+        private static void emitMessage(final String msg, final SAXParseException ex) {
+            LOGGER.warn("{} {} and column {}", msg, ex.getLineNumber(), ex.getColumnNumber());
+            LOGGER.warn(ex.getMessage(), ex.getException());
+        }
+    }
+
+    private static class ConsumerException extends RuntimeException {
+
+        ConsumerException(Exception ex) {
+            super(ex);
+        }
+    }
+}
+
diff --git a/log4j-1.2-api/src/main/resources/org/apache/log4j/xml/log4j.dtd b/log4j-1.2-api/src/main/resources/org/apache/log4j/xml/log4j.dtd
new file mode 100644
index 0000000..f8e433a
--- /dev/null
+++ b/log4j-1.2-api/src/main/resources/org/apache/log4j/xml/log4j.dtd
@@ -0,0 +1,237 @@
+<?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.
+-->
+
+<!-- Authors: Chris Taylor, Ceki Gulcu. -->
+
+<!-- Version: 1.2 -->
+
+<!-- A configuration element consists of optional renderer
+elements,appender elements, categories and an optional root
+element. -->
+
+<!ELEMENT log4j:configuration (renderer*, throwableRenderer?,
+                               appender*,plugin*, (category|logger)*,root?,
+                               (categoryFactory|loggerFactory)?)>
+
+<!-- The "threshold" attribute takes a level value below which -->
+<!-- all logging statements are disabled. -->
+
+<!-- Setting the "debug" enable the printing of internal log4j logging   -->
+<!-- statements.                                                         -->
+
+<!-- By default, debug attribute is "null", meaning that we not do touch -->
+<!-- internal log4j logging settings. The "null" value for the threshold -->
+<!-- attribute can be misleading. The threshold field of a repository	 -->
+<!-- cannot be set to null. The "null" value for the threshold attribute -->
+<!-- simply means don't touch the threshold field, the threshold field   --> 
+<!-- keeps its old value.                                                -->
+     
+<!ATTLIST log4j:configuration
+  xmlns:log4j              CDATA #FIXED "http://jakarta.apache.org/log4j/" 
+  threshold                (all|trace|debug|info|warn|error|fatal|off|null) "null"
+  debug                    (true|false|null)  "null"
+  reset                    (true|false) "false"
+>
+
+<!-- renderer elements allow the user to customize the conversion of  -->
+<!-- message objects to String.                                       -->
+
+<!ELEMENT renderer EMPTY>
+<!ATTLIST renderer
+  renderedClass  CDATA #REQUIRED
+  renderingClass CDATA #REQUIRED
+>
+
+<!--  throwableRenderer allows the user to customize the conversion
+         of exceptions to a string representation.  -->
+<!ELEMENT throwableRenderer (param*)>
+<!ATTLIST throwableRenderer
+  class  CDATA #REQUIRED
+>
+
+
+<!-- Appenders must have a name and a class. -->
+<!-- Appenders may contain an error handler, a layout, optional parameters -->
+<!-- and filters. They may also reference (or include) other appenders. -->
+<!ELEMENT appender (errorHandler?, param*,
+      rollingPolicy?, triggeringPolicy?, connectionSource?,
+      layout?, filter*, appender-ref*)>
+<!ATTLIST appender
+  name 		CDATA 	#REQUIRED
+  class 	CDATA	#REQUIRED
+>
+
+<!ELEMENT layout (param*)>
+<!ATTLIST layout
+  class		CDATA	#REQUIRED
+>
+
+<!ELEMENT filter (param*)>
+<!ATTLIST filter
+  class		CDATA	#REQUIRED
+>
+
+<!-- ErrorHandlers can be of any class. They can admit any number of -->
+<!-- parameters. -->
+
+<!ELEMENT errorHandler (param*, root-ref?, logger-ref*,  appender-ref?)> 
+<!ATTLIST errorHandler
+   class        CDATA   #REQUIRED 
+>
+
+<!ELEMENT root-ref EMPTY>
+
+<!ELEMENT logger-ref EMPTY>
+<!ATTLIST logger-ref
+  ref CDATA #REQUIRED
+>
+
+<!ELEMENT param EMPTY>
+<!ATTLIST param
+  name		CDATA   #REQUIRED
+  value		CDATA	#REQUIRED
+>
+
+
+<!-- The priority class is org.apache.log4j.Level by default -->
+<!ELEMENT priority (param*)>
+<!ATTLIST priority
+  class   CDATA	#IMPLIED
+  value	  CDATA #REQUIRED
+>
+
+<!-- The level class is org.apache.log4j.Level by default -->
+<!ELEMENT level (param*)>
+<!ATTLIST level
+  class   CDATA	#IMPLIED
+  value	  CDATA #REQUIRED
+>
+
+
+<!-- If no level element is specified, then the configurator MUST not -->
+<!-- touch the level of the named category. -->
+<!ELEMENT category (param*,(priority|level)?,appender-ref*)>
+<!ATTLIST category
+  class         CDATA   #IMPLIED
+  name		CDATA	#REQUIRED
+  additivity	(true|false) "true"  
+>
+
+<!-- If no level element is specified, then the configurator MUST not -->
+<!-- touch the level of the named logger. -->
+<!ELEMENT logger (param*,level?,appender-ref*)>
+<!ATTLIST logger
+  class         CDATA   #IMPLIED
+  name		CDATA	#REQUIRED
+  additivity	(true|false) "true"  
+>
+
+
+<!ELEMENT categoryFactory (param*)>
+<!ATTLIST categoryFactory 
+   class        CDATA #REQUIRED>
+
+<!ELEMENT loggerFactory (param*)>
+<!ATTLIST loggerFactory
+   class        CDATA #REQUIRED>
+
+<!ELEMENT appender-ref EMPTY>
+<!ATTLIST appender-ref
+  ref CDATA #REQUIRED
+>
+
+<!-- plugins must have a name and class and can have optional parameters -->
+<!ELEMENT plugin (param*, connectionSource?)>
+<!ATTLIST plugin
+  name 		CDATA 	   #REQUIRED
+  class 	CDATA  #REQUIRED
+>
+
+<!ELEMENT connectionSource (dataSource?, param*)>
+<!ATTLIST connectionSource
+  class        CDATA  #REQUIRED
+>
+
+<!ELEMENT dataSource (param*)>
+<!ATTLIST dataSource
+  class        CDATA  #REQUIRED
+>
+
+<!ELEMENT triggeringPolicy ((param|filter)*)>
+<!ATTLIST triggeringPolicy
+  name 		CDATA  #IMPLIED
+  class 	CDATA  #REQUIRED
+>
+
+<!ELEMENT rollingPolicy (param*)>
+<!ATTLIST rollingPolicy
+  name 		CDATA  #IMPLIED
+  class 	CDATA  #REQUIRED
+>
+
+
+<!-- If no priority element is specified, then the configurator MUST not -->
+<!-- touch the priority of root. -->
+<!-- The root category always exists and cannot be subclassed. -->
+<!ELEMENT root (param*, (priority|level)?, appender-ref*)>
+
+
+<!-- ==================================================================== -->
+<!--                       A logging event                                -->
+<!-- ==================================================================== -->
+<!ELEMENT log4j:eventSet (log4j:event*)>
+<!ATTLIST log4j:eventSet
+  xmlns:log4j             CDATA #FIXED "http://jakarta.apache.org/log4j/" 
+  version                (1.1|1.2) "1.2" 
+  includesLocationInfo   (true|false) "true"
+>
+
+
+
+<!ELEMENT log4j:event (log4j:message, log4j:NDC?, log4j:throwable?, 
+                       log4j:locationInfo?, log4j:properties?) >
+
+<!-- The timestamp format is application dependent. -->
+<!ATTLIST log4j:event
+    logger     CDATA #REQUIRED
+    level      CDATA #REQUIRED
+    thread     CDATA #REQUIRED
+    timestamp  CDATA #REQUIRED
+    time       CDATA #IMPLIED
+>
+
+<!ELEMENT log4j:message (#PCDATA)>
+<!ELEMENT log4j:NDC (#PCDATA)>
+
+<!ELEMENT log4j:throwable (#PCDATA)>
+
+<!ELEMENT log4j:locationInfo EMPTY>
+<!ATTLIST log4j:locationInfo
+  class  CDATA	#REQUIRED
+  method CDATA	#REQUIRED
+  file   CDATA	#REQUIRED
+  line   CDATA	#REQUIRED
+>
+
+<!ELEMENT log4j:properties (log4j:data*)>
+
+<!ELEMENT log4j:data EMPTY>
+<!ATTLIST log4j:data
+  name   CDATA	#REQUIRED
+  value  CDATA	#REQUIRED
+>
diff --git a/log4j-1.2-api/src/site/markdown/index.md b/log4j-1.2-api/src/site/markdown/index.md
index 01d5a76..696e0bb 100644
--- a/log4j-1.2-api/src/site/markdown/index.md
+++ b/log4j-1.2-api/src/site/markdown/index.md
@@ -18,8 +18,7 @@
 
 # Log4j 1.2 Bridge
 
-The Log4j 1.2 Bridge allows applications coded to use Log4j 1.2 API to use
-Log4j 2 instead.
+The Log4j 1.2 Bridge allows applications coded to use Log4j 1.2 API to use Log4j 2 instead.
 
 ## Requirements
 
@@ -37,7 +36,7 @@
 | BasicConfigurator.configure() | NoOp               | Reconfigures Log4j 2                 |
 
 If log4j-core is not present location information will not be accurate in calls using the Log4j 1.2 API. The config
-package which attempts tp convert Log4j 1.x configurations to Log4j 2 is not supported without Log4j 2.  
+package which attempts tp convert Log4j 1.x configurations to Log4j 2 is not supported without Log4j 2.    
 
 For more information, see [Runtime Dependencies](../runtime-dependencies.html).
 
diff --git a/log4j-1.2-api/src/src/main/java/org/apache/log4j/Appender.java b/log4j-1.2-api/src/src/main/java/org/apache/log4j/Appender.java
new file mode 100644
index 0000000..de89cce
--- /dev/null
+++ b/log4j-1.2-api/src/src/main/java/org/apache/log4j/Appender.java
@@ -0,0 +1,143 @@
+/*
+ * 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.ErrorHandler;
+import org.apache.log4j.spi.Filter;
+import org.apache.log4j.spi.LoggingEvent;
+
+/**
+ * Implement this interface for your own strategies for outputting log
+ * statements.
+ */
+public interface Appender {
+
+    /**
+     * Add a filter to the end of the filter list.
+     * @param newFilter The filter to add.
+     *
+     * @since 0.9.0
+     */
+    void addFilter(Filter newFilter);
+
+    /**
+     * Returns the head Filter. The Filters are organized in a linked list
+     * and so all Filters on this Appender are available through the result.
+     *
+     * @return the head Filter or null, if no Filters are present
+     * @since 1.1
+     */
+    Filter getFilter();
+
+    /**
+     * Clear the list of filters by removing all the filters in it.
+     *
+     * @since 0.9.0
+     */
+    void clearFilters();
+
+    /**
+     * Release any resources allocated within the appender such as file
+     * handles, network connections, etc.
+     * <p>
+     * It is a programming error to append to a closed appender.
+     * </p>
+     *
+     * @since 0.8.4
+     */
+    void close();
+
+    /**
+     * Log in <code>Appender</code> specific way. When appropriate,
+     * Loggers will call the <code>doAppend</code> method of appender
+     * implementations in order to log.
+     * @param event The LoggingEvent.
+     */
+    void doAppend(LoggingEvent event);
+
+
+    /**
+     * Get the name of this appender.
+     *
+     * @return name, may be null.
+     */
+    String getName();
+
+
+    /**
+     * Set the {@link ErrorHandler} for this appender.
+     * @param errorHandler The error handler.
+     *
+     * @since 0.9.0
+     */
+    void setErrorHandler(ErrorHandler errorHandler);
+
+    /**
+     * Returns the {@link ErrorHandler} for this appender.
+     * @return The error handler.
+     *
+     * @since 1.1
+     */
+    ErrorHandler getErrorHandler();
+
+    /**
+     * Set the {@link Layout} for this appender.
+     * @param layout The Layout.
+     *
+     * @since 0.8.1
+     */
+    void setLayout(Layout layout);
+
+    /**
+     * Returns this appenders layout.
+     * @return the Layout.
+     *
+     * @since 1.1
+     */
+    Layout getLayout();
+
+
+    /**
+     * Set the name of this appender. The name is used by other
+     * components to identify this appender.
+     * @param name The appender name.
+     *
+     * @since 0.8.1
+     */
+    void setName(String name);
+
+    /**
+     * Configurators call this method to determine if the appender
+     * requires a layout. If this method returns {@code true},
+     * meaning that layout is required, then the configurator will
+     * configure an layout using the configuration information at its
+     * disposal.  If this method returns {@code false}, meaning that
+     * a layout is not required, then layout configuration will be
+     * skipped even if there is available layout configuration
+     * information at the disposal of the configurator..
+     * <p>
+     * In the rather exceptional case, where the appender
+     * implementation admits a layout but can also work without it, then
+     * the appender should return {@code true}.
+     * </p>
+     * @return true if a Layout is required.
+     *
+     * @since 0.8.4
+     */
+    boolean requiresLayout();
+}
+
diff --git a/log4j-1.2-api/src/src/main/java/org/apache/log4j/AppenderSkeleton.java b/log4j-1.2-api/src/src/main/java/org/apache/log4j/AppenderSkeleton.java
new file mode 100644
index 0000000..1353dae
--- /dev/null
+++ b/log4j-1.2-api/src/src/main/java/org/apache/log4j/AppenderSkeleton.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.
+ */
+package org.apache.log4j;
+
+import org.apache.log4j.spi.ErrorHandler;
+import org.apache.log4j.spi.Filter;
+import org.apache.log4j.spi.LoggingEvent;
+import org.apache.log4j.spi.OptionHandler;
+
+/**
+ * The base class for Appenders in Log4j 1. Appenders constructed using this are ignored in Log4j 2.
+ */
+public abstract class AppenderSkeleton implements Appender, OptionHandler {
+
+    protected Layout layout;
+
+    protected String name;
+
+    protected Priority threshold;
+
+    protected ErrorHandler errorHandler = new NoOpErrorHandler();
+
+    protected Filter headFilter;
+
+    protected Filter tailFilter;
+
+    protected boolean closed = false;
+
+    /**
+     * Create new instance.
+     */
+    public AppenderSkeleton() {
+        super();
+    }
+
+    protected AppenderSkeleton(final boolean isActive) {
+        super();
+    }
+
+    @Override
+    public void activateOptions() {
+    }
+
+    @Override
+    public void addFilter(final Filter newFilter) {
+        if(headFilter == null) {
+            headFilter = tailFilter = newFilter;
+        } else {
+            tailFilter.setNext(newFilter);
+            tailFilter = newFilter;
+        }
+    }
+
+    protected abstract void append(LoggingEvent event);
+
+    @Override
+    public void clearFilters() {
+        headFilter = tailFilter = null;
+    }
+
+    @Override
+    public void finalize() {
+    }
+
+    @Override
+    public ErrorHandler getErrorHandler() {
+        return this.errorHandler;
+    }
+
+    @Override
+    public Filter getFilter() {
+        return headFilter;
+    }
+
+    public final Filter getFirstFilter() {
+        return headFilter;
+    }
+
+    @Override
+    public Layout getLayout() {
+        return layout;
+    }
+
+    @Override
+    public final String getName() {
+        return this.name;
+    }
+
+    public Priority getThreshold() {
+        return threshold;
+    }
+
+    public boolean isAsSevereAsThreshold(final Priority priority) {
+        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) {
+        append(event);
+    }
+
+    /**
+     * Set the {@link ErrorHandler} for this Appender.
+     *
+     * @since 0.9.0
+     */
+    @Override
+    public synchronized void setErrorHandler(final ErrorHandler eh) {
+        if (eh != null) {
+            this.errorHandler = eh;
+        }
+    }
+
+    @Override
+    public void setLayout(final Layout layout) {
+        this.layout = layout;
+    }
+
+    @Override
+    public void setName(final String name) {
+        this.name = name;
+    }
+
+    public void setThreshold(final Priority threshold) {
+        this.threshold = threshold;
+    }
+
+    public static class NoOpErrorHandler implements ErrorHandler {
+        @Override
+        public void setLogger(final Logger logger) {
+
+        }
+
+        @Override
+        public void error(final String message, final Exception e, final int errorCode) {
+
+        }
+
+        @Override
+        public void error(final String message) {
+
+        }
+
+        @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) {
+
+        }
+    }
+}
diff --git a/log4j-1.2-api/src/src/main/java/org/apache/log4j/BasicConfigurator.java b/log4j-1.2-api/src/src/main/java/org/apache/log4j/BasicConfigurator.java
new file mode 100644
index 0000000..2b7ec7f
--- /dev/null
+++ b/log4j-1.2-api/src/src/main/java/org/apache/log4j/BasicConfigurator.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;
+
+/**
+ * Provided for compatibility with Log4j 1.x.
+ */
+public class BasicConfigurator {
+
+    protected BasicConfigurator() {
+    }
+
+    public static void configure() {
+        LogManager.reconfigure();
+    }
+
+    /**
+     * No-op implementation.
+     * @param appender The appender.
+     */
+    public static void configure(final Appender appender) {
+        // no-op
+    }
+
+    /**
+     * No-op implementation.
+     */
+    public static void resetConfiguration() {
+        // no-op
+    }
+}
diff --git a/log4j-1.2-api/src/src/main/java/org/apache/log4j/Category.java b/log4j-1.2-api/src/src/main/java/org/apache/log4j/Category.java
new file mode 100644
index 0000000..e0e5aef
--- /dev/null
+++ b/log4j-1.2-api/src/src/main/java/org/apache/log4j/Category.java
@@ -0,0 +1,571 @@
+/*
+ * 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.util.Enumeration;
+import java.util.Map;
+import java.util.ResourceBundle;
+import java.util.WeakHashMap;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.ConcurrentMap;
+
+import org.apache.log4j.helpers.NullEnumeration;
+import org.apache.log4j.legacy.core.CategoryUtil;
+import org.apache.log4j.or.ObjectRenderer;
+import org.apache.log4j.or.RendererSupport;
+import org.apache.log4j.spi.LoggerFactory;
+import org.apache.log4j.spi.LoggingEvent;
+import org.apache.logging.log4j.spi.ExtendedLogger;
+import org.apache.logging.log4j.spi.LoggerContext;
+import org.apache.logging.log4j.message.LocalizedMessage;
+import org.apache.logging.log4j.message.Message;
+import org.apache.logging.log4j.message.ObjectMessage;
+import org.apache.logging.log4j.spi.AbstractLoggerAdapter;
+import org.apache.logging.log4j.util.Strings;
+
+
+/**
+ * Implementation of the Category class for compatibility, despite it having been deprecated a long, long time ago.
+ */
+public class Category {
+
+    private static PrivateAdapter adapter = new PrivateAdapter();
+
+    private static final Map<LoggerContext, ConcurrentMap<String, Logger>> CONTEXT_MAP =
+        new WeakHashMap<>();
+
+    private static final String FQCN = Category.class.getName();
+
+    private static final boolean isCoreAvailable;
+
+    private final Map<Class<?>, ObjectRenderer> rendererMap;
+
+    static {
+        boolean available;
+
+        try {
+            available = Class.forName("org.apache.logging.log4j.core.Logger") != null;
+        } catch (Exception ex) {
+            available = false;
+        }
+        isCoreAvailable = available;
+    }
+
+    /**
+     * Resource bundle for localized messages.
+     */
+    protected ResourceBundle bundle = null;
+
+    private final org.apache.logging.log4j.Logger logger;
+
+    /**
+     * Constructor used by Logger to specify a LoggerContext.
+     * @param context The LoggerContext.
+     * @param name The name of the Logger.
+     */
+    protected Category(final LoggerContext context, final String name) {
+        this.logger = context.getLogger(name);
+        rendererMap = ((RendererSupport) LogManager.getLoggerRepository()).getRendererMap();
+    }
+
+    /**
+     * Constructor exposed by Log4j 1.2.
+     * @param name The name of the Logger.
+     */
+    protected Category(final String name) {
+        this(PrivateManager.getContext(), name);
+    }
+
+    private Category(final org.apache.logging.log4j.Logger logger) {
+        this.logger = logger;
+        rendererMap = ((RendererSupport) LogManager.getLoggerRepository()).getRendererMap();
+    }
+
+    public static Category getInstance(final String name) {
+        return getInstance(PrivateManager.getContext(), name, adapter);
+    }
+
+    static Logger getInstance(final LoggerContext context, final String name) {
+        return getInstance(context, name, adapter);
+    }
+
+    static Logger getInstance(final LoggerContext context, final String name, final LoggerFactory factory) {
+        final ConcurrentMap<String, Logger> loggers = getLoggersMap(context);
+        Logger logger = loggers.get(name);
+        if (logger != null) {
+            return logger;
+        }
+        logger = factory.makeNewLoggerInstance(name);
+        final Logger prev = loggers.putIfAbsent(name, logger);
+        return prev == null ? logger : prev;
+    }
+
+    static Logger getInstance(final LoggerContext context, final String name, final PrivateAdapter factory) {
+        final ConcurrentMap<String, Logger> loggers = getLoggersMap(context);
+        Logger logger = loggers.get(name);
+        if (logger != null) {
+            return logger;
+        }
+        logger = factory.newLogger(name, context);
+        final Logger prev = loggers.putIfAbsent(name, logger);
+        return prev == null ? logger : prev;
+    }
+
+    public static Category getInstance(@SuppressWarnings("rawtypes") final Class clazz) {
+        return getInstance(clazz.getName());
+    }
+
+    static Logger getInstance(final LoggerContext context, @SuppressWarnings("rawtypes") final Class clazz) {
+        return getInstance(context, clazz.getName());
+    }
+
+    public final String getName() {
+        return logger.getName();
+    }
+
+    org.apache.logging.log4j.Logger getLogger() {
+        return logger;
+    }
+
+    public final Category getParent() {
+        if (!isCoreAvailable) {
+            return null;
+        }
+        org.apache.logging.log4j.Logger parent = CategoryUtil.getParent(logger);
+        LoggerContext loggerContext = CategoryUtil.getLoggerContext(logger);
+        if (parent == null || loggerContext == null) {
+            return null;
+        }
+        final ConcurrentMap<String, Logger> loggers = getLoggersMap(loggerContext);
+        final Logger l = loggers.get(parent.getName());
+        return l == null ? new Category(parent) : l;
+    }
+
+    public static Category getRoot() {
+        return getInstance(Strings.EMPTY);
+    }
+
+    static Logger getRoot(final LoggerContext context) {
+        return getInstance(context, Strings.EMPTY);
+    }
+
+    private static ConcurrentMap<String, Logger> getLoggersMap(final LoggerContext context) {
+        synchronized (CONTEXT_MAP) {
+            ConcurrentMap<String, Logger> map = CONTEXT_MAP.get(context);
+            if (map == null) {
+                map = new ConcurrentHashMap<>();
+                CONTEXT_MAP.put(context, map);
+            }
+            return map;
+        }
+    }
+
+    /**
+     Returns all the currently defined categories in the default
+     hierarchy as an {@link java.util.Enumeration Enumeration}.
+
+     <p>The root category is <em>not</em> included in the returned
+     {@link Enumeration}.
+     @return and Enumeration of the Categories.
+
+     @deprecated Please use {@link LogManager#getCurrentLoggers()} instead.
+     */
+    @SuppressWarnings("rawtypes")
+    @Deprecated
+    public static Enumeration getCurrentCategories() {
+        return LogManager.getCurrentLoggers();
+    }
+
+    public final Level getEffectiveLevel() {
+        switch (logger.getLevel().getStandardLevel()) {
+        case ALL:
+            return Level.ALL;
+        case TRACE:
+            return Level.TRACE;
+        case DEBUG:
+            return Level.DEBUG;
+        case INFO:
+            return Level.INFO;
+        case WARN:
+            return Level.WARN;
+        case ERROR:
+            return Level.ERROR;
+        case FATAL:
+            return Level.FATAL;
+        default:
+            // TODO Should this be an IllegalStateException?
+            return Level.OFF;
+        }
+    }
+
+    public final Priority getChainedPriority() {
+        return getEffectiveLevel();
+    }
+
+    public final Level getLevel() {
+        return getEffectiveLevel();
+    }
+
+    public void setLevel(final Level level) {
+        setLevel(level.levelStr);
+    }
+
+    public final Level getPriority() {
+        return getEffectiveLevel();
+    }
+
+    public void setPriority(final Priority priority) {
+        setLevel(priority.levelStr);
+    }
+
+    private void setLevel(final String levelStr) {
+        if (isCoreAvailable) {
+            CategoryUtil.setLevel(logger, org.apache.logging.log4j.Level.toLevel(levelStr));
+        }
+    }
+
+    public void debug(final Object message) {
+        maybeLog(FQCN, org.apache.logging.log4j.Level.DEBUG, message, null);
+    }
+
+    public void debug(final Object message, final Throwable t) {
+        maybeLog(FQCN, org.apache.logging.log4j.Level.DEBUG, message, t);
+    }
+
+    public boolean isDebugEnabled() {
+        return logger.isDebugEnabled();
+    }
+
+    public void error(final Object message) {
+        maybeLog(FQCN, org.apache.logging.log4j.Level.ERROR, message, null);
+    }
+
+    public void error(final Object message, final Throwable t) {
+        maybeLog(FQCN, org.apache.logging.log4j.Level.ERROR, message, t);
+    }
+
+    public boolean isErrorEnabled() {
+        return logger.isErrorEnabled();
+    }
+
+    public void warn(final Object message) {
+        maybeLog(FQCN, org.apache.logging.log4j.Level.WARN, message, null);
+    }
+
+    public void warn(final Object message, final Throwable t) {
+        maybeLog(FQCN, org.apache.logging.log4j.Level.WARN, message, t);
+    }
+
+    public boolean isWarnEnabled() {
+        return logger.isWarnEnabled();
+    }
+
+    public void fatal(final Object message) {
+        maybeLog(FQCN, org.apache.logging.log4j.Level.FATAL, message, null);
+    }
+
+    public void fatal(final Object message, final Throwable t) {
+        maybeLog(FQCN, org.apache.logging.log4j.Level.FATAL, message, t);
+    }
+
+    public boolean isFatalEnabled() {
+        return logger.isFatalEnabled();
+    }
+
+    public void info(final Object message) {
+        maybeLog(FQCN, org.apache.logging.log4j.Level.INFO, message, null);
+    }
+
+    public void info(final Object message, final Throwable t) {
+        maybeLog(FQCN, org.apache.logging.log4j.Level.INFO, message, t);
+    }
+
+    public boolean isInfoEnabled() {
+        return logger.isInfoEnabled();
+    }
+
+    public void trace(final Object message) {
+        maybeLog(FQCN, org.apache.logging.log4j.Level.TRACE, message, null);
+    }
+
+    public void trace(final Object message, final Throwable t) {
+        maybeLog(FQCN, org.apache.logging.log4j.Level.TRACE, message, t);
+    }
+
+    public boolean isTraceEnabled() {
+        return logger.isTraceEnabled();
+    }
+
+    public boolean isEnabledFor(final Priority level) {
+        final org.apache.logging.log4j.Level lvl = org.apache.logging.log4j.Level.toLevel(level.toString());
+        return isEnabledFor(lvl);
+    }
+
+    /**
+     * No-op implementation.
+     * @param appender The Appender to add.
+     */
+    public void addAppender(final Appender appender) {
+    }
+
+    /**
+     * No-op implementation.
+     * @param event The logging event.
+     */
+    public void callAppenders(final LoggingEvent event) {
+    }
+
+    @SuppressWarnings("rawtypes")
+    public Enumeration getAllAppenders() {
+        return NullEnumeration.getInstance();
+    }
+
+    /**
+     * No-op implementation.
+     * @param name The name of the Appender.
+     * @return null.
+     */
+    public Appender getAppender(final String name) {
+        return null;
+    }
+
+    /**
+     Is the appender passed as parameter attached to this category?
+     * @param appender The Appender to add.
+     * @return true if the appender is attached.
+     */
+    public boolean isAttached(final Appender appender) {
+        return false;
+    }
+
+    /**
+     * No-op implementation.
+     */
+    public void removeAllAppenders() {
+    }
+
+    /**
+     * No-op implementation.
+     * @param appender The Appender to remove.
+     */
+    public void removeAppender(final Appender appender) {
+    }
+
+    /**
+     * No-op implementation.
+     * @param name The Appender to remove.
+     */
+    public void removeAppender(final String name) {
+    }
+
+    /**
+     * No-op implementation.
+     */
+    public static void shutdown() {
+    }
+
+    public void forcedLog(final String fqcn, final Priority level, final Object message, final Throwable t) {
+        final org.apache.logging.log4j.Level lvl = org.apache.logging.log4j.Level.toLevel(level.toString());
+        ObjectRenderer renderer = get(message.getClass());
+        final Message msg = message instanceof Message ? (Message) message : renderer != null ?
+            new RenderedMessage(renderer, message) : new ObjectMessage(message);
+        if (logger instanceof ExtendedLogger) {
+            ((ExtendedLogger) logger).logMessage(fqcn, lvl, null, new ObjectMessage(message), t);
+        } else {
+            logger.log(lvl, msg, t);
+        }
+    }
+
+    public boolean exists(final String name) {
+        return PrivateManager.getContext().hasLogger(name);
+    }
+
+    public boolean getAdditivity() {
+        return isCoreAvailable ? CategoryUtil.isAdditive(logger) : false;
+    }
+
+    public void setAdditivity(final boolean additivity) {
+        if (isCoreAvailable) {
+            CategoryUtil.setAdditivity(logger, additivity);
+        }
+    }
+
+    public void setResourceBundle(final ResourceBundle bundle) {
+        this.bundle = bundle;
+    }
+
+    public ResourceBundle getResourceBundle() {
+        if (bundle != null) {
+            return bundle;
+        }
+        String name = logger.getName();
+        if (isCoreAvailable) {
+            LoggerContext ctx = CategoryUtil.getLoggerContext(logger);
+            if (ctx != null) {
+                final ConcurrentMap<String, Logger> loggers = getLoggersMap(ctx);
+                while ((name = getSubName(name)) != null) {
+                    final Logger subLogger = loggers.get(name);
+                    if (subLogger != null) {
+                        final ResourceBundle rb = subLogger.bundle;
+                        if (rb != null) {
+                            return rb;
+                        }
+                    }
+                }
+            }
+        }
+        return null;
+    }
+
+    private static  String getSubName(final String name) {
+        if (Strings.isEmpty(name)) {
+            return null;
+        }
+        final int i = name.lastIndexOf('.');
+        return i > 0 ? name.substring(0, i) : Strings.EMPTY;
+    }
+
+    /**
+     If <code>assertion</code> parameter is {@code false}, then
+     logs <code>msg</code> as an {@link #error(Object) error} statement.
+
+     <p>The <code>assert</code> method has been renamed to
+     <code>assertLog</code> because <code>assert</code> is a language
+     reserved word in JDK 1.4.
+
+     @param assertion The assertion.
+     @param msg The message to print if <code>assertion</code> is
+     false.
+
+     @since 1.2
+     */
+    public void assertLog(final boolean assertion, final String msg) {
+        if (!assertion) {
+            this.error(msg);
+        }
+    }
+
+    public void l7dlog(final Priority priority, final String key, final Throwable t) {
+        if (isEnabledFor(priority)) {
+            final Message msg = new LocalizedMessage(bundle, key, null);
+            forcedLog(FQCN, priority, msg, t);
+        }
+    }
+
+    public void l7dlog(final Priority priority, final String key, final Object[] params, final Throwable t) {
+        if (isEnabledFor(priority)) {
+            final Message msg = new LocalizedMessage(bundle, key, params);
+            forcedLog(FQCN, priority, msg, t);
+        }
+    }
+
+    public void log(final Priority priority, final Object message, final Throwable t) {
+        if (isEnabledFor(priority)) {
+            final Message msg = new ObjectMessage(message);
+            forcedLog(FQCN, priority, msg, t);
+        }
+    }
+
+    public void log(final Priority priority, final Object message) {
+        if (isEnabledFor(priority)) {
+            final Message msg = new ObjectMessage(message);
+            forcedLog(FQCN, priority, msg, null);
+        }
+    }
+
+    public void log(final String fqcn, final Priority priority, final Object message, final Throwable t) {
+        if (isEnabledFor(priority)) {
+            final Message msg = new ObjectMessage(message);
+            forcedLog(fqcn, priority, msg, t);
+        }
+    }
+
+    private void maybeLog(final String fqcn, final org.apache.logging.log4j.Level level,
+            final Object message, final Throwable throwable) {
+        if (logger.isEnabled(level)) {
+            if (logger instanceof ExtendedLogger) {
+                ((ExtendedLogger) logger).logMessage(fqcn, level, null, new ObjectMessage(message), throwable);
+            } else {
+                logger.log(level, message, throwable);
+            }
+        }
+    }
+
+    private static class PrivateAdapter extends AbstractLoggerAdapter<Logger> {
+
+        @Override
+        protected Logger newLogger(final String name, final org.apache.logging.log4j.spi.LoggerContext context) {
+            return new Logger(context, name);
+        }
+
+        @Override
+        protected org.apache.logging.log4j.spi.LoggerContext getContext() {
+            return PrivateManager.getContext();
+        }
+    }
+
+    /**
+     * Private LogManager.
+     */
+    private static class PrivateManager extends org.apache.logging.log4j.LogManager {
+        private static final String FQCN = Category.class.getName();
+
+        public static LoggerContext getContext() {
+            return getContext(FQCN, false);
+        }
+
+        public static org.apache.logging.log4j.Logger getLogger(final String name) {
+            return getLogger(FQCN, name);
+        }
+    }
+
+    private boolean isEnabledFor(final org.apache.logging.log4j.Level level) {
+        return logger.isEnabled(level);
+    }
+
+    private ObjectRenderer get(Class clazz) {
+        ObjectRenderer renderer = null;
+        for(Class c = clazz; c != null; c = c.getSuperclass()) {
+            renderer = rendererMap.get(c);
+            if (renderer != null) {
+                return renderer;
+            }
+            renderer = searchInterfaces(c);
+            if (renderer != null) {
+                return renderer;
+            }
+        }
+        return null;
+    }
+
+    ObjectRenderer searchInterfaces(Class c) {
+        ObjectRenderer renderer = rendererMap.get(c);
+        if(renderer != null) {
+            return renderer;
+        } else {
+            Class[] ia = c.getInterfaces();
+            for (Class clazz : ia) {
+                renderer = searchInterfaces(clazz);
+                if (renderer != null) {
+                    return renderer;
+                }
+            }
+        }
+        return null;
+    }
+
+}
diff --git a/log4j-1.2-api/src/src/main/java/org/apache/log4j/ConsoleAppender.java b/log4j-1.2-api/src/src/main/java/org/apache/log4j/ConsoleAppender.java
new file mode 100644
index 0000000..605fac7
--- /dev/null
+++ b/log4j-1.2-api/src/src/main/java/org/apache/log4j/ConsoleAppender.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;
+
+import org.apache.log4j.spi.LoggingEvent;
+
+/**
+ * Console-appender.
+ */
+public class ConsoleAppender extends AppenderSkeleton
+{
+
+  /**
+   * {@inheritDoc}
+   */
+  @Override
+  public void close()
+  {
+  }
+
+  /**
+   * {@inheritDoc}
+   */
+  @Override
+  public boolean requiresLayout()
+  {
+    return false;
+  }
+
+  /**
+   * {@inheritDoc}
+   */
+  @Override
+  protected void append(final LoggingEvent theEvent)
+  {
+  }
+
+}
diff --git a/log4j-1.2-api/src/src/main/java/org/apache/log4j/Layout.java b/log4j-1.2-api/src/src/main/java/org/apache/log4j/Layout.java
new file mode 100644
index 0000000..dbd2291
--- /dev/null
+++ b/log4j-1.2-api/src/src/main/java/org/apache/log4j/Layout.java
@@ -0,0 +1,89 @@
+/*
+ * 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 org.apache.logging.log4j.util.Strings;
+
+/**
+ *
+ */
+public abstract class Layout {
+
+    public final static String LINE_SEP = Strings.LINE_SEPARATOR;
+
+    /** Note that the line.separator property can be looked up even by applets. */
+    public static final int LINE_SEP_LEN = Strings.LINE_SEPARATOR.length();
+
+    /**
+     * Implement this method to create your own layout format.
+     * @param event The LoggingEvent.
+     * @return The formatted LoggingEvent.
+     */
+    public abstract String format(LoggingEvent event);
+
+    /**
+     * Returns the content type output by this layout. The base class
+     * returns "text/plain".
+     * @return the type of content rendered by the Layout.
+     */
+    public String getContentType() {
+        return "text/plain";
+    }
+
+    /**
+     * Returns the header for the layout format. The base class returns
+     * <code>null</code>.
+     * @return The header.
+     */
+    public String getHeader() {
+        return null;
+    }
+
+    /**
+     * Returns the footer for the layout format. The base class returns
+     * <code>null</code>.
+     * @return The footer.
+     */
+    public String getFooter() {
+        return null;
+    }
+
+
+    /**
+     * If the layout handles the throwable object contained within
+     * {@link LoggingEvent}, then the layout should return
+     * {@code false}. Otherwise, if the layout ignores throwable
+     * object, then the layout should return {@code true}.
+     * If ignoresThrowable is true, the appender is responsible for
+     * rendering the throwable.
+     * <p>
+     * The <a href="/log4j/1.2/apidocs/org/apache/log4j/SimpleLayout.html">SimpleLayout</a>,
+     * <a href="/log4j/1.2/apidocs/org/apache/log4j/TTCCLayout.html">TTCCLayout</a>,
+     * <a href="/log4j/1.2/apidocs/org/apache/log4j/PatternLayout.html">PatternLayout</a>
+     * all return {@code true}. The
+     * <a href="/log4j/1.2/apidocs/org/apache/log4j/xml/XMLLayout.html">XMLLayout</a>
+     * returns {@code false}.
+     * </p>
+     *
+     * @return true if the Layout ignores Throwables.
+     *
+     * @since 0.8.4
+     */
+    public abstract boolean ignoresThrowable();
+}
+
diff --git a/log4j-1.2-api/src/src/main/java/org/apache/log4j/Level.java b/log4j-1.2-api/src/src/main/java/org/apache/log4j/Level.java
new file mode 100644
index 0000000..af53154
--- /dev/null
+++ b/log4j-1.2-api/src/src/main/java/org/apache/log4j/Level.java
@@ -0,0 +1,252 @@
+/*
+ * 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.ObjectInputStream;
+import java.io.ObjectOutputStream;
+import java.io.ObjectStreamException;
+import java.io.Serializable;
+import java.util.Locale;
+
+import org.apache.logging.log4j.util.Strings;
+
+/**
+ * Defines the minimum set of levels recognized by the system, that is
+ * <code>OFF</code>, <code>FATAL</code>, <code>ERROR</code>,
+ * <code>WARN</code>, <code>INFO</code>, <code>DEBUG</code>
+ * and <code>ALL</code>.
+ * <p>
+ * The <code>Level</code> class may be subclassed to define a larger
+ * level set.
+ * </p>
+ */
+public class Level extends Priority implements Serializable {
+
+    /**
+     * TRACE level integer value.
+     *
+     * @since 1.2.12
+     */
+    public static final int TRACE_INT = 5000;
+
+    /**
+     * The <code>OFF</code> has the highest possible rank and is
+     * intended to turn off logging.
+     */
+    public static final Level OFF = new Level(OFF_INT, "OFF", 0);
+
+    /**
+     * The <code>FATAL</code> level designates very severe error
+     * events that will presumably lead the application to abort.
+     */
+    public static final Level FATAL = new Level(FATAL_INT, "FATAL", 0);
+
+    /**
+     * The <code>ERROR</code> level designates error events that
+     * might still allow the application to continue running.
+     */
+    public static final Level ERROR = new Level(ERROR_INT, "ERROR", 3);
+
+    /**
+     * The <code>WARN</code> level designates potentially harmful situations.
+     */
+    public static final Level WARN = new Level(WARN_INT, "WARN", 4);
+
+    /**
+     * The <code>INFO</code> level designates informational messages
+     * that highlight the progress of the application at coarse-grained
+     * level.
+     */
+    public static final Level INFO = new Level(INFO_INT, "INFO", 6);
+
+    /**
+     * The <code>DEBUG</code> Level designates fine-grained
+     * informational events that are most useful to debug an
+     * application.
+     */
+    public static final Level DEBUG = new Level(DEBUG_INT, "DEBUG", 7);
+
+    /**
+     * The <code>TRACE</code> Level designates finer-grained
+     * informational events than the <code>DEBUG</code> level.
+     */
+    public static final Level TRACE = new Level(TRACE_INT, "TRACE", 7);
+
+    /**
+     * The <code>ALL</code> has the lowest possible rank and is intended to
+     * turn on all logging.
+     */
+    public static final Level ALL = new Level(ALL_INT, "ALL", 7);
+
+    /**
+     * Serialization version id.
+     */
+    private static final long serialVersionUID = 3491141966387921974L;
+
+    /**
+     * Instantiate a Level object.
+     *
+     * @param level            The logging level.
+     * @param levelStr         The level name.
+     * @param syslogEquivalent The matching syslog level.
+     */
+    protected Level(final int level, final String levelStr, final int syslogEquivalent) {
+        super(level, levelStr, syslogEquivalent);
+    }
+
+
+    /**
+     * Convert the string passed as argument to a level. If the
+     * conversion fails, then this method returns {@link #DEBUG}.
+     *
+     * @param sArg The level name.
+     * @return The Level.
+     */
+    public static Level toLevel(final String sArg) {
+        return toLevel(sArg, Level.DEBUG);
+    }
+
+    /**
+     * Convert an integer passed as argument to a level. If the
+     * conversion fails, then this method returns {@link #DEBUG}.
+     *
+     * @param val The integer value of the Level.
+     * @return The Level.
+     */
+    public static Level toLevel(final int val) {
+        return toLevel(val, Level.DEBUG);
+    }
+
+    /**
+     * Convert an integer passed as argument to a level. If the
+     * conversion fails, then this method returns the specified default.
+     *
+     * @param val          The integer value of the Level.
+     * @param defaultLevel the default level if the integer doesn't match.
+     * @return The matching Level.
+     */
+    public static Level toLevel(final int val, final Level defaultLevel) {
+        switch (val) {
+            case ALL_INT:
+                return ALL;
+            case DEBUG_INT:
+                return Level.DEBUG;
+            case INFO_INT:
+                return Level.INFO;
+            case WARN_INT:
+                return Level.WARN;
+            case ERROR_INT:
+                return Level.ERROR;
+            case FATAL_INT:
+                return Level.FATAL;
+            case OFF_INT:
+                return OFF;
+            case TRACE_INT:
+                return Level.TRACE;
+            default:
+                return defaultLevel;
+        }
+    }
+
+    /**
+     * Convert the string passed as argument to a level. If the
+     * conversion fails, then this method returns the value of
+     * <code>defaultLevel</code>.
+     * @param sArg The name of the Level.
+     * @param defaultLevel The default Level to use.
+     * @return the matching Level.
+     */
+    public static Level toLevel(final String sArg, final Level defaultLevel) {
+        if (sArg == null) {
+            return defaultLevel;
+        }
+        final String s = sArg.toUpperCase(Locale.ROOT);
+        switch (s) {
+        case "ALL":
+            return Level.ALL;
+        case "DEBUG":
+            return Level.DEBUG;
+        case "INFO":
+            return Level.INFO;
+        case "WARN":
+            return Level.WARN;
+        case "ERROR":
+            return Level.ERROR;
+        case "FATAL":
+            return Level.FATAL;
+        case "OFF":
+            return Level.OFF;
+        case "TRACE":
+            return Level.TRACE;
+        default:
+            return defaultLevel;
+        }
+    }
+
+    /**
+     * Custom deserialization of Level.
+     *
+     * @param s serialization stream.
+     * @throws IOException            if IO exception.
+     * @throws ClassNotFoundException if class not found.
+     */
+    private void readObject(final ObjectInputStream s) throws IOException, ClassNotFoundException {
+        s.defaultReadObject();
+        level = s.readInt();
+        syslogEquivalent = s.readInt();
+        levelStr = s.readUTF();
+        if (levelStr == null) {
+            levelStr = Strings.EMPTY;
+        }
+    }
+
+    /**
+     * Serialize level.
+     *
+     * @param s serialization stream.
+     * @throws IOException if exception during serialization.
+     */
+    private void writeObject(final ObjectOutputStream s) throws IOException {
+        s.defaultWriteObject();
+        s.writeInt(level);
+        s.writeInt(syslogEquivalent);
+        s.writeUTF(levelStr);
+    }
+
+    /**
+     * Resolved deserialized level to one of the stock instances.
+     * May be overridden in classes derived from Level.
+     *
+     * @return resolved object.
+     * @throws ObjectStreamException if exception during resolution.
+     */
+    protected Object readResolve() throws ObjectStreamException {
+        //
+        //  if the deserialized object is exactly an instance of Level
+        //
+        if (getClass() == Level.class) {
+            return toLevel(level);
+        }
+        //
+        //   extension of Level can't substitute stock item
+        //
+        return this;
+    }
+
+}
+
diff --git a/log4j-1.2-api/src/src/main/java/org/apache/log4j/LogManager.java b/log4j-1.2-api/src/src/main/java/org/apache/log4j/LogManager.java
new file mode 100644
index 0000000..c4966aa
--- /dev/null
+++ b/log4j-1.2-api/src/src/main/java/org/apache/log4j/LogManager.java
@@ -0,0 +1,251 @@
+/*
+ * 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.util.Enumeration;
+import java.util.HashMap;
+import java.util.Map;
+
+import org.apache.log4j.helpers.NullEnumeration;
+import org.apache.log4j.legacy.core.ContextUtil;
+import org.apache.log4j.or.ObjectRenderer;
+import org.apache.log4j.or.RendererSupport;
+import org.apache.log4j.spi.HierarchyEventListener;
+import org.apache.log4j.spi.LoggerFactory;
+import org.apache.log4j.spi.LoggerRepository;
+import org.apache.log4j.spi.RepositorySelector;
+import org.apache.logging.log4j.spi.LoggerContext;
+import org.apache.logging.log4j.util.Strings;
+
+/**
+ *
+ */
+public final class LogManager {
+
+    /**
+     * @deprecated This variable is for internal use only. It will
+     * become package protected in future versions.
+     * */
+    @Deprecated
+    public static final String DEFAULT_CONFIGURATION_FILE = "log4j.properties";
+
+    /**
+     * @deprecated This variable is for internal use only. It will
+     * become private in future versions.
+     * */
+    @Deprecated
+    public static final String DEFAULT_CONFIGURATION_KEY = "log4j.configuration";
+
+    /**
+     * @deprecated This variable is for internal use only. It will
+     * become private in future versions.
+     * */
+    @Deprecated
+    public static final String CONFIGURATOR_CLASS_KEY = "log4j.configuratorClass";
+
+    /**
+     * @deprecated This variable is for internal use only. It will
+     * become private in future versions.
+     */
+    @Deprecated
+    public static final String DEFAULT_INIT_OVERRIDE_KEY = "log4j.defaultInitOverride";
+
+    static final String DEFAULT_XML_CONFIGURATION_FILE = "log4j.xml";
+
+    private static final LoggerRepository REPOSITORY = new Repository();
+
+    private static final boolean isLog4jCore;
+
+    static {
+        boolean core = false;
+        try {
+            if (Class.forName("org.apache.logging.log4j.core.LoggerContext") != null) {
+                core = true;
+            }
+        } catch (Exception ex) {
+            // Ignore the exception;
+        }
+        isLog4jCore = core;
+    }
+
+    private LogManager() {
+    }
+
+    public static Logger getRootLogger() {
+        return Category.getInstance(PrivateManager.getContext(), Strings.EMPTY);
+    }
+
+    public static Logger getLogger(final String name) {
+        return Category.getInstance(PrivateManager.getContext(), name);
+    }
+
+    public static Logger getLogger(final Class<?> clazz) {
+        return Category.getInstance(PrivateManager.getContext(), clazz.getName());
+    }
+
+    public static Logger getLogger(final String name, final LoggerFactory factory) {
+        return Category.getInstance(PrivateManager.getContext(), name);
+    }
+
+    public static Logger exists(final String name) {
+        final LoggerContext ctx = PrivateManager.getContext();
+        if (!ctx.hasLogger(name)) {
+            return null;
+        }
+        return Logger.getLogger(name);
+    }
+
+    @SuppressWarnings("rawtypes")
+    public static Enumeration getCurrentLoggers() {
+        return NullEnumeration.getInstance();
+    }
+
+    static void reconfigure() {
+        if (isLog4jCore) {
+            final LoggerContext ctx = PrivateManager.getContext();
+            ContextUtil.reconfigure(ctx);
+        }
+    }
+
+    /**
+     * No-op implementation.
+     */
+    public static void shutdown() {
+    }
+
+    /**
+     * No-op implementation.
+     */
+    public static void resetConfiguration() {
+    }
+
+    /**
+     * No-op implementation.
+     * @param selector The RepositorySelector.
+     * @param guard prevents calls at the incorrect time.
+     * @throws IllegalArgumentException if a parameter is invalid.
+     */
+    public static void setRepositorySelector(final RepositorySelector selector, final Object guard)
+        throws IllegalArgumentException {
+    }
+
+    public static LoggerRepository getLoggerRepository() {
+        return REPOSITORY;
+    }
+
+    /**
+     * The Repository.
+     */
+    private static class Repository implements LoggerRepository, RendererSupport {
+
+        private final Map<Class<?>, ObjectRenderer> rendererMap = new HashMap<>();
+
+        @Override
+        public Map<Class<?>, ObjectRenderer> getRendererMap() {
+            return rendererMap;
+        }
+
+        @Override
+        public void addHierarchyEventListener(final HierarchyEventListener listener) {
+
+        }
+
+        @Override
+        public boolean isDisabled(final int level) {
+            return false;
+        }
+
+        @Override
+        public void setThreshold(final Level level) {
+
+        }
+
+        @Override
+        public void setThreshold(final String val) {
+
+        }
+
+        @Override
+        public void emitNoAppenderWarning(final Category cat) {
+
+        }
+
+        @Override
+        public Level getThreshold() {
+            return Level.OFF;
+        }
+
+        @Override
+        public Logger getLogger(final String name) {
+            return Category.getInstance(PrivateManager.getContext(), name);
+        }
+
+        @Override
+        public Logger getLogger(final String name, final LoggerFactory factory) {
+            return Category.getInstance(PrivateManager.getContext(), name);
+        }
+
+        @Override
+        public Logger getRootLogger() {
+            return Category.getRoot(PrivateManager.getContext());
+        }
+
+        @Override
+        public Logger exists(final String name) {
+            return LogManager.exists(name);
+        }
+
+        @Override
+        public void shutdown() {
+        }
+
+        @Override
+        @SuppressWarnings("rawtypes")
+        public Enumeration getCurrentLoggers() {
+            return NullEnumeration.getInstance();
+        }
+
+        @Override
+        @SuppressWarnings("rawtypes")
+        public Enumeration getCurrentCategories() {
+            return NullEnumeration.getInstance();
+        }
+
+        @Override
+        public void fireAddAppenderEvent(final Category logger, final Appender appender) {
+        }
+
+        @Override
+        public void resetConfiguration() {
+        }
+    }
+
+    /**
+     * Internal LogManager.
+     */
+    private static class PrivateManager extends org.apache.logging.log4j.LogManager {
+        private static final String FQCN = LogManager.class.getName();
+
+        public static LoggerContext getContext() {
+            return getContext(FQCN, false);
+        }
+
+        public static org.apache.logging.log4j.Logger getLogger(final String name) {
+            return getLogger(FQCN, name);
+        }
+    }
+}
diff --git a/log4j-1.2-api/src/src/main/java/org/apache/log4j/Logger.java b/log4j-1.2-api/src/src/main/java/org/apache/log4j/Logger.java
new file mode 100644
index 0000000..fe1f26a
--- /dev/null
+++ b/log4j-1.2-api/src/src/main/java/org/apache/log4j/Logger.java
@@ -0,0 +1,66 @@
+/*
+ * 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.LoggerFactory;
+import org.apache.logging.log4j.spi.LoggerContext;
+
+/**
+ *
+ */
+public class Logger extends Category {
+
+    protected Logger(final String name) {
+        super(PrivateManager.getContext(), name);
+    }
+
+    Logger(final LoggerContext context, final String name) {
+        super(context, name);
+    }
+
+    public static Logger getLogger(final String name) {
+        return Category.getInstance(PrivateManager.getContext(), name);
+    }
+
+    public static Logger getLogger(final Class<?> clazz) {
+        return Category.getInstance(PrivateManager.getContext(), clazz);
+    }
+
+    public static Logger getRootLogger() {
+        return Category.getRoot(PrivateManager.getContext());
+    }
+
+    public static Logger getLogger(final String name, final LoggerFactory factory) {
+        return Category.getInstance(PrivateManager.getContext(), name, factory);
+    }
+
+    /**
+     * Internal Log Manager.
+     */
+    private static class PrivateManager extends org.apache.logging.log4j.LogManager {
+        private static final String FQCN = Logger.class.getName();
+
+        public static LoggerContext getContext() {
+            return getContext(FQCN, false);
+        }
+
+        public static org.apache.logging.log4j.Logger getLogger(final String name) {
+            return getLogger(FQCN, name);
+        }
+    }
+}
diff --git a/log4j-1.2-api/src/src/main/java/org/apache/log4j/MDC.java b/log4j-1.2-api/src/src/main/java/org/apache/log4j/MDC.java
new file mode 100644
index 0000000..ee7631a
--- /dev/null
+++ b/log4j-1.2-api/src/src/main/java/org/apache/log4j/MDC.java
@@ -0,0 +1,77 @@
+/*
+ * 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.util.HashMap;
+import java.util.Hashtable;
+import java.util.Map;
+
+import org.apache.logging.log4j.ThreadContext;
+
+/**
+ * This class behaves just like Log4j's MDC would - and so can cause issues with the redeployment of web
+ * applications if the Objects stored in the threads Map cannot be garbage collected.
+ */
+public final class MDC {
+
+
+    private static ThreadLocal<Map<String, Object>> localMap =
+        new InheritableThreadLocal<Map<String, Object>>() {
+            @Override
+            protected Map<String, Object> initialValue() {
+                return new HashMap<>();
+            }
+
+            @Override
+            protected Map<String, Object> childValue(final Map<String, Object> parentValue) {
+                return parentValue == null ? new HashMap<String, Object>() : new HashMap<>(parentValue);
+            }
+        };
+
+    private MDC() {
+    }
+
+
+    public static void put(final String key, final String value) {
+        localMap.get().put(key, value);
+        ThreadContext.put(key, value);
+    }
+
+
+    public static void put(final String key, final Object value) {
+        localMap.get().put(key, value);
+        ThreadContext.put(key, value.toString());
+    }
+
+    public static Object get(final String key) {
+        return localMap.get().get(key);
+    }
+
+    public static void remove(final String key) {
+        localMap.get().remove(key);
+        ThreadContext.remove(key);
+    }
+
+    public static void clear() {
+        localMap.get().clear();
+        ThreadContext.clearMap();
+    }
+
+    public static Hashtable<String, Object> getContext() {
+        return new Hashtable<>(localMap.get());
+    }
+}
diff --git a/log4j-1.2-api/src/src/main/java/org/apache/log4j/NDC.java b/log4j-1.2-api/src/src/main/java/org/apache/log4j/NDC.java
new file mode 100644
index 0000000..a4e23dc
--- /dev/null
+++ b/log4j-1.2-api/src/src/main/java/org/apache/log4j/NDC.java
@@ -0,0 +1,207 @@
+/*
+ * 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.util.Stack;
+
+/**
+ *
+ */
+public final class NDC {
+
+    private NDC() {
+    }
+
+    /**
+     * Clear any nested diagnostic information if any. This method is
+     * useful in cases where the same thread can be potentially used
+     * over and over in different unrelated contexts.
+     * <p>
+     * This method is equivalent to calling the {@link #setMaxDepth}
+     * method with a zero <code>maxDepth</code> argument.
+     * </p>
+     */
+    public static void clear() {
+        org.apache.logging.log4j.ThreadContext.clearStack();
+    }
+
+
+    /**
+     * Clone the diagnostic context for the current thread.
+     * <p>
+     * Internally a diagnostic context is represented as a stack.  A
+     * given thread can supply the stack (i.e. diagnostic context) to a
+     * child thread so that the child can inherit the parent thread's
+     * diagnostic context.
+     * </p>
+     * <p>
+     * The child thread uses the {@link #inherit inherit} method to
+     * inherit the parent's diagnostic context.
+     * </p>
+     * @return Stack A clone of the current thread's  diagnostic context.
+     */
+    @SuppressWarnings("rawtypes")
+    public static Stack cloneStack() {
+        final Stack<String> stack = new Stack<>();
+        for (final String element : org.apache.logging.log4j.ThreadContext.cloneStack().asList()) {
+            stack.push(element);
+        }
+        return stack;
+    }
+
+
+    /**
+     * Inherit the diagnostic context of another thread.
+     * <p>
+     * The parent thread can obtain a reference to its diagnostic
+     * context using the {@link #cloneStack} method.  It should
+     * communicate this information to its child so that it may inherit
+     * the parent's diagnostic context.
+     * </p>
+     * <p>
+     * The parent's diagnostic context is cloned before being
+     * inherited. In other words, once inherited, the two diagnostic
+     * contexts can be managed independently.
+     * </p>
+     * <p>
+     * In java, a child thread cannot obtain a reference to its
+     * parent, unless it is directly handed the reference. Consequently,
+     * there is no client-transparent way of inheriting diagnostic
+     * contexts. Do you know any solution to this problem?
+     * </p>
+     * @param stack The diagnostic context of the parent thread.
+     */
+    public static void inherit(final Stack<String> stack) {
+        org.apache.logging.log4j.ThreadContext.setStack(stack);
+    }
+
+
+    /**
+     * <strong style="color:#FF4040">Never use this method directly.</strong>
+     *
+     * @return The string value of the specified key.
+     */
+    public static String get() {
+        return org.apache.logging.log4j.ThreadContext.peek();
+    }
+
+    /**
+     * Get the current nesting depth of this diagnostic context.
+     * @return int The number of elements in the call stack.
+     * @see #setMaxDepth
+     */
+    public static int getDepth() {
+        return org.apache.logging.log4j.ThreadContext.getDepth();
+    }
+
+    /**
+     * Clients should call this method before leaving a diagnostic
+     * context.
+     * <p>
+     * The returned value is the value that was pushed last. If no
+     * context is available, then the empty string "" is returned.
+     * </p>
+     * @return String The innermost diagnostic context.
+     */
+    public static String pop() {
+        return org.apache.logging.log4j.ThreadContext.pop();
+    }
+
+    /**
+     * Looks at the last diagnostic context at the top of this NDC
+     * without removing it.
+     * <p>
+     * The returned value is the value that was pushed last. If no
+     * context is available, then the empty string "" is returned.
+     * </p>
+     * @return String The innermost diagnostic context.
+     */
+    public static String peek() {
+        return org.apache.logging.log4j.ThreadContext.peek();
+    }
+
+    /**
+     * Push new diagnostic context information for the current thread.
+     * <p>
+     * The contents of the <code>message</code> parameter is
+     * determined solely by the client.
+     * </p>
+     * @param message The new diagnostic context information.
+     */
+    public static void push(final String message) {
+        org.apache.logging.log4j.ThreadContext.push(message);
+    }
+
+    /**
+     * Remove the diagnostic context for this thread.
+     * <p>
+     * Each thread that created a diagnostic context by calling
+     * {@link #push} should call this method before exiting. Otherwise,
+     * the memory used by the <b>thread</b> cannot be reclaimed by the
+     * VM.
+     * </p>
+     * <p>
+     * As this is such an important problem in heavy duty systems and
+     * because it is difficult to always guarantee that the remove
+     * method is called before exiting a thread, this method has been
+     * augmented to lazily remove references to dead threads. In
+     * practice, this means that you can be a little sloppy and
+     * occasionally forget to call {@code remove} before exiting a
+     * thread. However, you must call <code>remove</code> sometime. If
+     * you never call it, then your application is sure to run out of
+     * memory.
+     * </p>
+     */
+    public static void remove() {
+        org.apache.logging.log4j.ThreadContext.removeStack();
+    }
+
+    /**
+     * Set maximum depth of this diagnostic context. If the current
+     * depth is smaller or equal to <code>maxDepth</code>, then no
+     * action is taken.
+     * <p>
+     * This method is a convenient alternative to multiple {@link
+     * #pop} calls. Moreover, it is often the case that at the end of
+     * complex call sequences, the depth of the NDC is
+     * unpredictable. The <code>setMaxDepth</code> method circumvents
+     * this problem.
+     * </p>
+     * <p>
+     * For example, the combination
+     * </p>
+     * <pre>
+     * void foo() {
+     * &nbsp;  int depth = NDC.getDepth();
+     *
+     * &nbsp;  ... complex sequence of calls
+     *
+     * &nbsp;  NDC.setMaxDepth(depth);
+     * }
+     * </pre>
+     * <p>
+     * ensures that between the entry and exit of foo the depth of the
+     * diagnostic stack is conserved.
+     * </p>
+     *
+     * @see #getDepth
+     * @param maxDepth The maximum depth of the stack.
+     */
+    public static void setMaxDepth(final int maxDepth) {
+        org.apache.logging.log4j.ThreadContext.trim(maxDepth);
+    }
+}
diff --git a/log4j-1.2-api/src/src/main/java/org/apache/log4j/PatternLayout.java b/log4j-1.2-api/src/src/main/java/org/apache/log4j/PatternLayout.java
new file mode 100644
index 0000000..c2e1251
--- /dev/null
+++ b/log4j-1.2-api/src/src/main/java/org/apache/log4j/PatternLayout.java
@@ -0,0 +1,40 @@
+/*
+ * 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 org.apache.logging.log4j.util.Strings;
+
+/**
+ *
+ */
+public class PatternLayout extends Layout {
+
+    public PatternLayout(final String pattern) {
+
+    }
+
+    @Override
+    public String format(final LoggingEvent event) {
+        return Strings.EMPTY;
+    }
+
+    @Override
+    public boolean ignoresThrowable() {
+        return true;
+    }
+}
diff --git a/log4j-1.2-api/src/src/main/java/org/apache/log4j/Priority.java b/log4j-1.2-api/src/src/main/java/org/apache/log4j/Priority.java
new file mode 100644
index 0000000..8f6eee9
--- /dev/null
+++ b/log4j-1.2-api/src/src/main/java/org/apache/log4j/Priority.java
@@ -0,0 +1,239 @@
+/*
+ * 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;
+
+/**
+ * <em style="color:#A44">Refrain from using this class directly, use
+ * the {@link Level} class instead.</em>
+ */
+public class Priority {
+
+    /**
+     * The <code>OFF</code> has the highest possible rank and is
+     * intended to turn off logging.
+     */
+    public static final int OFF_INT = Integer.MAX_VALUE;
+    /**
+     * The <code>FATAL</code> level designates very severe error
+     * events that will presumably lead the application to abort.
+     */
+    public static final int FATAL_INT = 50000;
+    /**
+     * The <code>ERROR</code> level designates error events that
+     * might still allow the application to continue running.
+     */
+    public static final int ERROR_INT = 40000;
+    /**
+     * The <code>WARN</code> level designates potentially harmful situations.
+     */
+    public static final int WARN_INT = 30000;
+    /**
+     * The <code>INFO</code> level designates informational messages
+     * that highlight the progress of the application at coarse-grained
+     * level.
+     */
+    public static final int INFO_INT = 20000;
+    /**
+     * The <code>DEBUG</code> Level designates fine-grained
+     * informational events that are most useful to debug an
+     * application.
+     */
+    public static final int DEBUG_INT = 10000;
+    //public final static int FINE_INT = DEBUG_INT;
+    /**
+     * The <code>ALL</code> has the lowest possible rank and is intended to
+     * turn on all logging.
+     */
+    public static final int ALL_INT = Integer.MIN_VALUE;
+
+    /**
+     * @deprecated Use {@link Level#FATAL} instead.
+     */
+    @Deprecated
+    public static final Priority FATAL = new Level(FATAL_INT, "FATAL", 0);
+
+    /**
+     * @deprecated Use {@link Level#ERROR} instead.
+     */
+    @Deprecated
+    public static final Priority ERROR = new Level(ERROR_INT, "ERROR", 3);
+
+    /**
+     * @deprecated Use {@link Level#WARN} instead.
+     */
+    @Deprecated
+    public static final Priority WARN = new Level(WARN_INT, "WARN", 4);
+
+    /**
+     * @deprecated Use {@link Level#INFO} instead.
+     */
+    @Deprecated
+    public static final Priority INFO = new Level(INFO_INT, "INFO", 6);
+
+    /**
+     * @deprecated Use {@link Level#DEBUG} instead.
+     */
+    @Deprecated
+    public static final Priority DEBUG = new Level(DEBUG_INT, "DEBUG", 7);
+
+    /*
+     * These variables should be private but were not in Log4j 1.2 so are left the same way here.
+     */
+    transient int level;
+    transient String levelStr;
+    transient int syslogEquivalent;
+
+    /**
+     * Default constructor for deserialization.
+     */
+    protected Priority() {
+        level = DEBUG_INT;
+        levelStr = "DEBUG";
+        syslogEquivalent = 7;
+    }
+
+    /**
+     * Instantiate a level object.
+     * @param level The level value.
+     * @param levelStr The level name.
+     * @param syslogEquivalent The equivalent syslog value.
+     */
+    protected Priority(final int level, final String levelStr, final int syslogEquivalent) {
+        this.level = level;
+        this.levelStr = levelStr;
+        this.syslogEquivalent = syslogEquivalent;
+    }
+
+    /**
+     * Two priorities are equal if their level fields are equal.
+     * @param o The Object to check.
+     * @return true if the objects are equal, false otherwise.
+     *
+     * @since 1.2
+     */
+    @Override
+    public boolean equals(final Object o) {
+        if (o instanceof Priority) {
+            final Priority r = (Priority) o;
+            return this.level == r.level;
+        }
+        return false;
+    }
+
+    @Override
+    public int hashCode() {
+        return this.level;
+    }
+
+    /**
+     * Returns the syslog equivalent of this priority as an integer.
+     * @return The equivalent syslog value.
+     */
+    public
+    final int getSyslogEquivalent() {
+        return syslogEquivalent;
+    }
+
+
+    /**
+     * Returns {@code true} if this level has a higher or equal
+     * level than the level passed as argument, {@code false}
+     * otherwise.
+     * <p>
+     * You should think twice before overriding the default
+     * implementation of <code>isGreaterOrEqual</code> method.
+     * </p>
+     * @param r The Priority to check.
+     * @return true if the current level is greater or equal to the specified Priority.
+     */
+    public boolean isGreaterOrEqual(final Priority r) {
+        return level >= r.level;
+    }
+
+    /**
+     * Returns all possible priorities as an array of Level objects in
+     * descending order.
+     * @return An array of all possible Priorities.
+     *
+     * @deprecated This method will be removed with no replacement.
+     */
+    @Deprecated
+    public static Priority[] getAllPossiblePriorities() {
+        return new Priority[]{Priority.FATAL, Priority.ERROR, Level.WARN,
+            Priority.INFO, Priority.DEBUG};
+    }
+
+
+    /**
+     * Returns the string representation of this priority.
+     * @return The name of the Priority.
+     */
+    @Override
+    public final String toString() {
+        return levelStr;
+    }
+
+    /**
+     * Returns the integer representation of this level.
+     * @return The integer value of this level.
+     */
+    public final int toInt() {
+        return level;
+    }
+
+    /**
+     * @param sArg The name of the Priority.
+     * @return The Priority matching the name.
+     * @deprecated Please use the {@link Level#toLevel(String)} method instead.
+     */
+    @Deprecated
+    public static Priority toPriority(final String sArg) {
+        return Level.toLevel(sArg);
+    }
+
+    /**
+     * @param val The value of the Priority.
+     * @return The Priority matching the value.
+     * @deprecated Please use the {@link Level#toLevel(int)} method instead.
+     */
+    @Deprecated
+    public static Priority toPriority(final int val) {
+        return toPriority(val, Priority.DEBUG);
+    }
+
+    /**
+     * @param val The value of the Priority.
+     * @param defaultPriority The default Priority to use if the value is invalid.
+     * @return The Priority matching the value or the default Priority if no match is found.
+     * @deprecated Please use the {@link Level#toLevel(int, Level)} method instead.
+     */
+    @Deprecated
+    public static Priority toPriority(final int val, final Priority defaultPriority) {
+        return Level.toLevel(val, (Level) defaultPriority);
+    }
+
+    /**
+     * @param sArg The name of the Priority.
+     * @param defaultPriority The default Priority to use if the name is not found.
+     * @return The Priority matching the name or the default Priority if no match is found.
+     * @deprecated Please use the {@link Level#toLevel(String, Level)} method instead.
+     */
+    @Deprecated
+    public static Priority toPriority(final String sArg, final Priority defaultPriority) {
+        return Level.toLevel(sArg, (Level) defaultPriority);
+    }
+}
diff --git a/log4j-1.2-api/src/src/main/java/org/apache/log4j/PropertyConfigurator.java b/log4j-1.2-api/src/src/main/java/org/apache/log4j/PropertyConfigurator.java
new file mode 100644
index 0000000..0fe1fe0
--- /dev/null
+++ b/log4j-1.2-api/src/src/main/java/org/apache/log4j/PropertyConfigurator.java
@@ -0,0 +1,126 @@
+/*
+ * 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.InputStream;
+import java.net.URL;
+import java.util.Properties;
+
+import org.apache.log4j.spi.LoggerRepository;
+
+/**
+ * A configurator for properties.
+ */
+public class PropertyConfigurator {
+
+    /**
+     * Read configuration options from configuration file.
+     *
+     * @param configFileName The configuration file
+     * @param hierarchy The hierarchy
+     */
+    public void doConfigure(final String configFileName, final LoggerRepository hierarchy) {
+
+    }
+
+    /**
+     * Read configuration options from <code>properties</code>.
+     *
+     * See {@link #doConfigure(String, LoggerRepository)} for the expected format.
+     *
+     * @param properties The properties
+     * @param hierarchy The hierarchy
+     */
+    public void doConfigure(final Properties properties, final LoggerRepository hierarchy) {
+    }
+
+    /**
+     * Read configuration options from an InputStream.
+     *
+     * @param inputStream The input stream
+     * @param hierarchy The hierarchy
+     */
+    public void doConfigure(final InputStream inputStream, final LoggerRepository hierarchy) {
+    }
+
+    /**
+     * Read configuration options from url <code>configURL</code>.
+     *
+     * @param configURL The configuration URL
+     * @param hierarchy The hierarchy
+     */
+    public void doConfigure(final URL configURL, final LoggerRepository hierarchy) {
+    }
+
+    /**
+     * Read configuration options from configuration file.
+     *
+     * @param configFileName The configuration file.
+     */
+    public static void configure(final String configFileName) {
+    }
+
+    /**
+     * Read configuration options from url <code>configURL</code>.
+     *
+     * @param configURL The configuration URL
+     */
+    public static void configure(final URL configURL) {
+    }
+
+    /**
+     * Reads configuration options from an InputStream.
+     *
+     * @param inputStream The input stream
+     */
+    public static void configure(final InputStream inputStream) {
+    }
+
+    /**
+     * Read configuration options from <code>properties</code>.
+     *
+     * See {@link #doConfigure(String, LoggerRepository)} for the expected format.
+     *
+     * @param properties The properties
+     */
+    public static void configure(final Properties properties) {
+    }
+
+    /**
+     * Like {@link #configureAndWatch(String, long)} except that the
+     * default delay as defined by FileWatchdog.DEFAULT_DELAY is
+     * used.
+     *
+     * @param configFilename A file in key=value format.
+     */
+    public static void configureAndWatch(final String configFilename) {
+    }
+
+    /**
+     * Read the configuration file <code>configFilename</code> if it
+     * exists. Moreover, a thread will be created that will periodically
+     * check if <code>configFilename</code> has been created or
+     * modified. The period is determined by the <code>delay</code>
+     * argument. If a change or file creation is detected, then
+     * <code>configFilename</code> is read to configure log4j.
+     *
+     * @param configFilename A file in key=value format.
+     * @param delay The delay in milliseconds to wait between each check.
+     */
+    public static void configureAndWatch(final String configFilename, final long delay) {
+    }
+}
diff --git a/log4j-1.2-api/src/src/main/java/org/apache/log4j/RenderedMessage.java b/log4j-1.2-api/src/src/main/java/org/apache/log4j/RenderedMessage.java
new file mode 100644
index 0000000..422a565
--- /dev/null
+++ b/log4j-1.2-api/src/src/main/java/org/apache/log4j/RenderedMessage.java
@@ -0,0 +1,60 @@
+/*
+ * 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.or.ObjectRenderer;
+import org.apache.logging.log4j.message.Message;
+
+/**
+ * Implements object rendering for Log4j 1.x compatibility.
+ */
+public class RenderedMessage implements Message {
+
+    private final ObjectRenderer renderer;
+    private final Object object;
+    private String rendered = null;
+
+    public RenderedMessage(ObjectRenderer renderer, Object object) {
+        this.renderer = renderer;
+        this.object = object;
+    }
+
+
+    @Override
+    public String getFormattedMessage() {
+        if (rendered == null) {
+            rendered = renderer.doRender(object);
+        }
+
+        return rendered;
+    }
+
+    @Override
+    public String getFormat() {
+        return getFormattedMessage();
+    }
+
+    @Override
+    public Object[] getParameters() {
+        return null;
+    }
+
+    @Override
+    public Throwable getThrowable() {
+        return null;
+    }
+}
diff --git a/log4j-1.2-api/src/src/main/java/org/apache/log4j/SimpleLayout.java b/log4j-1.2-api/src/src/main/java/org/apache/log4j/SimpleLayout.java
new file mode 100644
index 0000000..c77b9be
--- /dev/null
+++ b/log4j-1.2-api/src/src/main/java/org/apache/log4j/SimpleLayout.java
@@ -0,0 +1,46 @@
+/*
+ * 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 org.apache.logging.log4j.util.Strings;
+
+/**
+ * Simple-layout.
+ */
+public class SimpleLayout extends Layout
+{
+
+  /**
+   * {@inheritDoc}
+   */
+  @Override
+  public String format(final LoggingEvent theEvent)
+  {
+    return Strings.EMPTY;
+  }
+
+  /**
+   * {@inheritDoc}
+   */
+  @Override
+  public boolean ignoresThrowable()
+  {
+    return true;
+  }
+
+}
diff --git a/log4j-1.2-api/src/src/main/java/org/apache/log4j/WriterAppender.java b/log4j-1.2-api/src/src/main/java/org/apache/log4j/WriterAppender.java
new file mode 100644
index 0000000..f026bfa
--- /dev/null
+++ b/log4j-1.2-api/src/src/main/java/org/apache/log4j/WriterAppender.java
@@ -0,0 +1,378 @@
+/*
+ * 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.helpers.QuietWriter;
+import org.apache.log4j.spi.ErrorHandler;
+import org.apache.log4j.spi.LoggingEvent;
+import org.apache.logging.log4j.status.StatusLogger;
+
+import java.io.IOException;
+import java.io.InterruptedIOException;
+import java.io.OutputStream;
+import java.io.OutputStreamWriter;
+import java.io.Writer;
+
+
+/**
+ * WriterAppender appends log events to a {@link Writer} or an
+ * {@link OutputStream} depending on the user's choice.
+ */
+public class WriterAppender extends AppenderSkeleton {
+    private static final org.apache.logging.log4j.Logger LOGGER = StatusLogger.getLogger();
+
+    /**
+     * Immediate flush means that the underlying writer or output stream
+     * will be flushed at the end of each append operation unless shouldFlush()
+     * is overridden. Immediate
+     * flush is slower but ensures that each append request is actually
+     * written. If <code>immediateFlush</code> is set to
+     * <code>false</code>, then there is a good chance that the last few
+     * logs events are not actually written to persistent media if and
+     * when the application crashes.
+     *
+     * <p>The <code>immediateFlush</code> variable is set to
+     * <code>true</code> by default.
+     */
+    protected boolean immediateFlush = true;
+
+    /**
+     * The encoding to use when writing.  <p>The
+     * <code>encoding</code> variable is set to <code>null</null> by
+     * default which results in the utilization of the system's default
+     * encoding.
+     */
+    protected String encoding;
+
+    /**
+     * This is the {@link QuietWriter quietWriter} where we will write
+     * to.
+     */
+    protected QuietWriter qw;
+
+
+    /**
+     * This default constructor does nothing.
+     */
+    public WriterAppender() {
+    }
+
+    /**
+     * Instantiate a WriterAppender and set the output destination to a
+     * new {@link OutputStreamWriter} initialized with <code>os</code>
+     * as its {@link OutputStream}.
+     */
+    public WriterAppender(Layout layout, OutputStream os) {
+        this(layout, new OutputStreamWriter(os));
+    }
+
+    /**
+     * Instantiate a WriterAppender and set the output destination to
+     * <code>writer</code>.
+     *
+     * <p>The <code>writer</code> must have been previously opened by
+     * the user.
+     */
+    public WriterAppender(Layout layout, Writer writer) {
+        this.layout = layout;
+        this.setWriter(writer);
+    }
+
+    /**
+     * Returns value of the <b>ImmediateFlush</b> option.
+     */
+    public boolean getImmediateFlush() {
+        return immediateFlush;
+    }
+
+    /**
+     * If the <b>ImmediateFlush</b> option is set to
+     * <code>true</code>, the appender will flush at the end of each
+     * write. This is the default behavior. If the option is set to
+     * <code>false</code>, then the underlying stream can defer writing
+     * to physical medium to a later time.
+     *
+     * <p>Avoiding the flush operation at the end of each append results in
+     * a performance gain of 10 to 20 percent. However, there is safety
+     * tradeoff involved in skipping flushing. Indeed, when flushing is
+     * skipped, then it is likely that the last few log events will not
+     * be recorded on disk when the application exits. This is a high
+     * price to pay even for a 20% performance gain.
+     */
+    public void setImmediateFlush(boolean value) {
+        immediateFlush = value;
+    }
+
+    /**
+     * Does nothing.
+     */
+    public void activateOptions() {
+    }
+
+
+    /**
+     * This method is called by the {@link AppenderSkeleton#doAppend}
+     * method.
+     *
+     * <p>If the output stream exists and is writable then write a log
+     * statement to the output stream. Otherwise, write a single warning
+     * message to <code>System.err</code>.
+     *
+     * <p>The format of the output will depend on this appender's
+     * layout.
+     */
+    public void append(LoggingEvent event) {
+
+        // Reminder: the nesting of calls is:
+        //
+        //    doAppend()
+        //      - check threshold
+        //      - filter
+        //      - append();
+        //        - checkEntryConditions();
+        //        - subAppend();
+
+        if (!checkEntryConditions()) {
+            return;
+        }
+        subAppend(event);
+    }
+
+    /**
+     * This method determines if there is a sense in attempting to append.
+     *
+     * <p>It checks whether there is a set output target and also if
+     * there is a set layout. If these checks fail, then the boolean
+     * value <code>false</code> is returned.
+     */
+    protected boolean checkEntryConditions() {
+        if (this.closed) {
+            LOGGER.warn("Not allowed to write to a closed appender.");
+            return false;
+        }
+
+        if (this.qw == null) {
+            errorHandler.error("No output stream or file set for the appender named [" + name + "].");
+            return false;
+        }
+
+        if (this.layout == null) {
+            errorHandler.error("No layout set for the appender named [" + name + "].");
+            return false;
+        }
+        return true;
+    }
+
+
+    /**
+     * Close this appender instance. The underlying stream or writer is
+     * also closed.
+     *
+     * <p>Closed appenders cannot be reused.
+     *
+     * @see #setWriter
+     * @since 0.8.4
+     */
+    public
+    synchronized void close() {
+        if (this.closed) {
+            return;
+        }
+        this.closed = true;
+        writeFooter();
+        reset();
+    }
+
+    /**
+     * Close the underlying {@link Writer}.
+     */
+    protected void closeWriter() {
+        if (qw != null) {
+            try {
+                qw.close();
+            } catch (IOException e) {
+                if (e instanceof InterruptedIOException) {
+                    Thread.currentThread().interrupt();
+                }
+                // There is do need to invoke an error handler at this late
+                // stage.
+                LOGGER.error("Could not close " + qw, e);
+            }
+        }
+    }
+
+    /**
+     * Returns an OutputStreamWriter when passed an OutputStream.  The
+     * encoding used will depend on the value of the
+     * <code>encoding</code> property.  If the encoding value is
+     * specified incorrectly the writer will be opened using the default
+     * system encoding (an error message will be printed to the LOGGER.
+     */
+    protected OutputStreamWriter createWriter(OutputStream os) {
+        OutputStreamWriter retval = null;
+
+        String enc = getEncoding();
+        if (enc != null) {
+            try {
+                retval = new OutputStreamWriter(os, enc);
+            } catch (IOException e) {
+                if (e instanceof InterruptedIOException) {
+                    Thread.currentThread().interrupt();
+                }
+                LOGGER.warn("Error initializing output writer.");
+                LOGGER.warn("Unsupported encoding?");
+            }
+        }
+        if (retval == null) {
+            retval = new OutputStreamWriter(os);
+        }
+        return retval;
+    }
+
+    public String getEncoding() {
+        return encoding;
+    }
+
+    public void setEncoding(String value) {
+        encoding = value;
+    }
+
+
+    /**
+     * Set the {@link ErrorHandler} for this WriterAppender and also the
+     * underlying {@link QuietWriter} if any.
+     */
+    public synchronized void setErrorHandler(ErrorHandler eh) {
+        if (eh == null) {
+            LOGGER.warn("You have tried to set a null error-handler.");
+        } else {
+            this.errorHandler = eh;
+            if (this.qw != null) {
+                this.qw.setErrorHandler(eh);
+            }
+        }
+    }
+
+    /**
+     * <p>Sets the Writer where the log output will go. The
+     * specified Writer must be opened by the user and be
+     * writable.
+     *
+     * <p>The <code>java.io.Writer</code> will be closed when the
+     * appender instance is closed.
+     *
+     *
+     * <p><b>WARNING:</b> Logging to an unopened Writer will fail.
+     * <p>
+     *
+     * @param writer An already opened Writer.
+     */
+    public synchronized void setWriter(Writer writer) {
+        reset();
+        this.qw = new QuietWriter(writer, errorHandler);
+        //this.tp = new TracerPrintWriter(qw);
+        writeHeader();
+    }
+
+
+    /**
+     * Actual writing occurs here.
+     *
+     * <p>Most subclasses of <code>WriterAppender</code> will need to
+     * override this method.
+     *
+     * @since 0.9.0
+     */
+    protected void subAppend(LoggingEvent event) {
+        this.qw.write(this.layout.format(event));
+
+        if (layout.ignoresThrowable()) {
+            String[] s = event.getThrowableStrRep();
+            if (s != null) {
+                int len = s.length;
+                for (int i = 0; i < len; i++) {
+                    this.qw.write(s[i]);
+                    this.qw.write(Layout.LINE_SEP);
+                }
+            }
+        }
+
+        if (shouldFlush(event)) {
+            this.qw.flush();
+        }
+    }
+
+
+    /**
+     * The WriterAppender requires a layout. Hence, this method returns
+     * <code>true</code>.
+     */
+    public boolean requiresLayout() {
+        return true;
+    }
+
+    /**
+     * Clear internal references to the writer and other variables.
+     * <p>
+     * Subclasses can override this method for an alternate closing
+     * behavior.
+     */
+    protected void reset() {
+        closeWriter();
+        this.qw = null;
+        //this.tp = null;
+    }
+
+
+    /**
+     * Write a footer as produced by the embedded layout's {@link
+     * Layout#getFooter} method.
+     */
+    protected void writeFooter() {
+        if (layout != null) {
+            String f = layout.getFooter();
+            if (f != null && this.qw != null) {
+                this.qw.write(f);
+                this.qw.flush();
+            }
+        }
+    }
+
+    /**
+     * Write a header as produced by the embedded layout's {@link
+     * Layout#getHeader} method.
+     */
+    protected void writeHeader() {
+        if (layout != null) {
+            String h = layout.getHeader();
+            if (h != null && this.qw != null) {
+                this.qw.write(h);
+            }
+        }
+    }
+
+    /**
+     * Determines whether the writer should be flushed after
+     * this event is written.
+     *
+     * @since 1.2.16
+     */
+    protected boolean shouldFlush(final LoggingEvent event) {
+        return immediateFlush;
+    }
+}
diff --git a/log4j-1.2-api/src/src/main/java/org/apache/log4j/bridge/AppenderAdapter.java b/log4j-1.2-api/src/src/main/java/org/apache/log4j/bridge/AppenderAdapter.java
new file mode 100644
index 0000000..f2dd2c1
--- /dev/null
+++ b/log4j-1.2-api/src/src/main/java/org/apache/log4j/bridge/AppenderAdapter.java
@@ -0,0 +1,86 @@
+/*
+ * 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.bridge;
+
+import java.io.Serializable;
+import java.util.ArrayList;
+import java.util.List;
+
+import org.apache.log4j.Appender;
+import org.apache.logging.log4j.core.Filter;
+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.
+ */
+public class AppenderAdapter {
+
+    private final Appender appender;
+    private final Adapter adapter;
+
+    /**
+     * Constructor.
+     */
+    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());
+            }
+        }
+        this.adapter = new Adapter(appender.getName(), appenderFilter, null, true, null);
+    }
+
+    public Adapter getAdapter() {
+        return adapter;
+    }
+
+    public class Adapter extends AbstractAppender {
+
+        protected Adapter(final String name, final Filter filter, final Layout<? extends Serializable> layout,
+            final boolean ignoreExceptions, final Property[] properties) {
+            super(name, filter, layout, ignoreExceptions, properties);
+        }
+
+        @Override
+        public void append(LogEvent event) {
+            appender.doAppend(new LogEventAdapter(event));
+        }
+
+        @Override
+        public void stop() {
+            appender.close();
+        }
+
+        public Appender getAppender() {
+            return appender;
+        }
+    }
+}
diff --git a/log4j-1.2-api/src/src/main/java/org/apache/log4j/bridge/AppenderWrapper.java b/log4j-1.2-api/src/src/main/java/org/apache/log4j/bridge/AppenderWrapper.java
new file mode 100644
index 0000000..f06f909
--- /dev/null
+++ b/log4j-1.2-api/src/src/main/java/org/apache/log4j/bridge/AppenderWrapper.java
@@ -0,0 +1,101 @@
+/*
+ * 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.bridge;
+
+import org.apache.log4j.Appender;
+import org.apache.log4j.Layout;
+import org.apache.log4j.spi.ErrorHandler;
+import org.apache.log4j.spi.Filter;
+import org.apache.log4j.spi.LoggingEvent;
+
+/**
+ * Holds a Log4j 2 Appender in an empty Log4j 1 Appender so it can be extracted when constructing the configuration.
+ * Allows a Log4j 1 Appender to reference a Log4j 2 Appender.
+ */
+public class AppenderWrapper implements Appender {
+
+    private final org.apache.logging.log4j.core.Appender appender;
+
+    public AppenderWrapper(org.apache.logging.log4j.core.Appender appender) {
+        this.appender = appender;
+    }
+
+    public org.apache.logging.log4j.core.Appender getAppender() {
+        return appender;
+    }
+
+    @Override
+    public void addFilter(Filter newFilter) {
+    }
+
+    @Override
+    public Filter getFilter() {
+        return null;
+    }
+
+    @Override
+    public void clearFilters() {
+
+    }
+
+    @Override
+    public void close() {
+        // Not supported with Log4j 2.
+    }
+
+    @Override
+    public void doAppend(LoggingEvent event) {
+        if (event instanceof LogEventAdapter) {
+            appender.append(((LogEventAdapter) event).getEvent());
+        }
+    }
+
+    @Override
+    public String getName() {
+        return appender.getName();
+    }
+
+    @Override
+    public void setErrorHandler(ErrorHandler errorHandler) {
+        appender.setHandler(new ErrorHandlerAdapter(errorHandler));
+    }
+
+    @Override
+    public ErrorHandler getErrorHandler() {
+        return ((ErrorHandlerAdapter)appender.getHandler()).getHandler();
+    }
+
+    @Override
+    public void setLayout(Layout layout) {
+        // Log4j 2 doesn't support this.
+    }
+
+    @Override
+    public Layout getLayout() {
+        return new LayoutWrapper(appender.getLayout());
+    }
+
+    @Override
+    public void setName(String name) {
+        // Log4j 2 doesn't support this.
+    }
+
+    @Override
+    public boolean requiresLayout() {
+        return false;
+    }
+}
diff --git a/log4j-1.2-api/src/src/main/java/org/apache/log4j/bridge/ErrorHandlerAdapter.java b/log4j-1.2-api/src/src/main/java/org/apache/log4j/bridge/ErrorHandlerAdapter.java
new file mode 100644
index 0000000..1f166a2
--- /dev/null
+++ b/log4j-1.2-api/src/src/main/java/org/apache/log4j/bridge/ErrorHandlerAdapter.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.bridge;
+
+import org.apache.log4j.spi.ErrorHandler;
+import org.apache.logging.log4j.core.LogEvent;
+
+/**
+ * Makes a Log4j 1 ErrorHandler usable by a Log4j 2 Appender.
+ */
+public class ErrorHandlerAdapter implements org.apache.logging.log4j.core.ErrorHandler {
+
+    private final ErrorHandler errorHandler;
+
+    public ErrorHandlerAdapter(ErrorHandler errorHandler) {
+        this.errorHandler = errorHandler;
+    }
+
+    public ErrorHandler getHandler() {
+        return errorHandler;
+    }
+
+    @Override
+    public void error(String msg) {
+        errorHandler.error(msg);
+    }
+
+    @Override
+    public void error(String msg, Throwable t) {
+        if (t instanceof Exception) {
+            errorHandler.error(msg, (Exception) t, 0);
+        } else {
+            errorHandler.error(msg);
+        }
+    }
+
+    @Override
+    public void error(String msg, LogEvent event, Throwable t) {
+        if (t == null || t instanceof Exception) {
+            errorHandler.error(msg, (Exception) t, 0, new LogEventAdapter(event));
+        } else {
+            errorHandler.error(msg);
+        }
+    }
+}
diff --git a/log4j-1.2-api/src/src/main/java/org/apache/log4j/bridge/FilterAdapter.java b/log4j-1.2-api/src/src/main/java/org/apache/log4j/bridge/FilterAdapter.java
new file mode 100644
index 0000000..2dff272
--- /dev/null
+++ b/log4j-1.2-api/src/src/main/java/org/apache/log4j/bridge/FilterAdapter.java
@@ -0,0 +1,56 @@
+/*
+ * 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.bridge;
+
+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;
+
+/**
+ * Binds a Log4j 1.x Filter with Log4j 2.
+ */
+public class FilterAdapter extends AbstractFilter {
+
+    private final Filter filter;
+
+    public FilterAdapter(Filter filter) {
+        this.filter = filter;
+    }
+
+    @Override
+    public void start() {
+        filter.activateOptions();
+    }
+
+    @Override
+    public Result filter(LogEvent event) {
+        LoggingEvent loggingEvent = new LogEventAdapter(event);
+        Filter next = filter;
+        while (next != null) {
+            switch (filter.decide(loggingEvent)) {
+                case Filter.ACCEPT:
+                    return Result.ACCEPT;
+                case Filter.DENY:
+                    return Result.DENY;
+                default:
+            }
+            next = filter.getNext();
+        }
+        return Result.NEUTRAL;
+    }
+}
diff --git a/log4j-1.2-api/src/src/main/java/org/apache/log4j/bridge/FilterWrapper.java b/log4j-1.2-api/src/src/main/java/org/apache/log4j/bridge/FilterWrapper.java
new file mode 100644
index 0000000..b2855cd
--- /dev/null
+++ b/log4j-1.2-api/src/src/main/java/org/apache/log4j/bridge/FilterWrapper.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.bridge;
+
+import org.apache.log4j.spi.Filter;
+import org.apache.log4j.spi.LoggingEvent;
+
+/**
+ * This acts as a container for Log4j 2 Filters to be attached to Log4j 1 components. However, the Log4j 2
+ * Filters will always be called directly so this class just acts as a container.
+ */
+public class FilterWrapper extends Filter {
+
+    private final org.apache.logging.log4j.core.Filter filter;
+
+    public FilterWrapper(org.apache.logging.log4j.core.Filter filter) {
+        this.filter = filter;
+    }
+
+    public org.apache.logging.log4j.core.Filter getFilter() {
+        return filter;
+    }
+
+    /**
+     * This method is never called.
+     * @param event The LoggingEvent to decide upon.
+     * @return 0
+     */
+    @Override
+    public int decide(LoggingEvent event) {
+        return 0;
+    }
+}
diff --git a/log4j-1.2-api/src/src/main/java/org/apache/log4j/bridge/LayoutAdapter.java b/log4j-1.2-api/src/src/main/java/org/apache/log4j/bridge/LayoutAdapter.java
new file mode 100644
index 0000000..5494c92
--- /dev/null
+++ b/log4j-1.2-api/src/src/main/java/org/apache/log4j/bridge/LayoutAdapter.java
@@ -0,0 +1,73 @@
+/*
+ * 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.bridge;
+
+import org.apache.log4j.Layout;
+import org.apache.logging.log4j.core.LogEvent;
+import org.apache.logging.log4j.core.layout.ByteBufferDestination;
+
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * Class Description goes here.
+ */
+public class LayoutAdapter implements org.apache.logging.log4j.core.Layout<String> {
+    private Layout layout;
+
+    public LayoutAdapter(Layout layout) {
+        this.layout = layout;
+    }
+
+
+    @Override
+    public byte[] getFooter() {
+        return layout.getFooter() == null ? null : layout.getFooter().getBytes();
+    }
+
+    @Override
+    public byte[] getHeader() {
+        return layout.getHeader() == null ? null : layout.getHeader().getBytes();
+    }
+
+    @Override
+    public byte[] toByteArray(LogEvent event) {
+        String result = layout.format(new LogEventAdapter(event));
+        return result == null ? null : result.getBytes();
+    }
+
+    @Override
+    public String toSerializable(LogEvent event) {
+        return layout.format(new LogEventAdapter(event));
+    }
+
+    @Override
+    public String getContentType() {
+        return layout.getContentType();
+    }
+
+    @Override
+    public Map<String, String> getContentFormat() {
+        return new HashMap<>();
+    }
+
+    @Override
+    public void encode(LogEvent event, ByteBufferDestination destination) {
+        final byte[] data = toByteArray(event);
+        destination.writeBytes(data, 0, data.length);
+    }
+}
diff --git a/log4j-1.2-api/src/src/main/java/org/apache/log4j/bridge/LayoutWrapper.java b/log4j-1.2-api/src/src/main/java/org/apache/log4j/bridge/LayoutWrapper.java
new file mode 100644
index 0000000..fc1e72f
--- /dev/null
+++ b/log4j-1.2-api/src/src/main/java/org/apache/log4j/bridge/LayoutWrapper.java
@@ -0,0 +1,48 @@
+/*
+ * 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.bridge;
+
+
+import org.apache.log4j.Layout;
+import org.apache.log4j.spi.LoggingEvent;
+
+
+/**
+ * Bridge between the Log4j 1 Layout and a Log4j 2 Layout.
+ */
+public class LayoutWrapper extends Layout {
+
+    private final org.apache.logging.log4j.core.Layout<?> layout;
+
+    public LayoutWrapper(org.apache.logging.log4j.core.Layout<?> layout) {
+        this.layout = layout;
+    }
+
+    @Override
+    public String format(LoggingEvent event) {
+        return layout.toSerializable(((LogEventAdapter)event).getEvent()).toString();
+    }
+
+    @Override
+    public boolean ignoresThrowable() {
+        return false;
+    }
+
+    public org.apache.logging.log4j.core.Layout<?> getLayout() {
+        return this.layout;
+    }
+}
diff --git a/log4j-1.2-api/src/src/main/java/org/apache/log4j/bridge/LogEventAdapter.java b/log4j-1.2-api/src/src/main/java/org/apache/log4j/bridge/LogEventAdapter.java
new file mode 100644
index 0000000..9ad4d27
--- /dev/null
+++ b/log4j-1.2-api/src/src/main/java/org/apache/log4j/bridge/LogEventAdapter.java
@@ -0,0 +1,217 @@
+/*
+ * 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.bridge;
+
+import java.lang.reflect.Method;
+
+import org.apache.log4j.Category;
+import org.apache.log4j.Level;
+import org.apache.log4j.spi.LocationInfo;
+import org.apache.log4j.spi.LoggingEvent;
+import org.apache.log4j.spi.ThrowableInformation;
+import org.apache.logging.log4j.core.LogEvent;
+import org.apache.logging.log4j.core.util.Loader;
+import org.apache.logging.log4j.core.util.Throwables;
+import org.apache.logging.log4j.spi.StandardLevel;
+import org.apache.logging.log4j.status.StatusLogger;
+
+/**
+ * Converts a Log4j 2 LogEvent into the components needed by a Log4j 1.x LoggingEvent.
+ * This class requires Log4j 2.
+ */
+public class LogEventAdapter extends LoggingEvent {
+
+    private static final long JVM_START_TIME = initStartTime();
+
+    private final LogEvent event;
+
+    public LogEventAdapter(LogEvent event) {
+        this.event = event;
+    }
+
+    public LogEvent getEvent() {
+        return this.event;
+    }
+
+    /**
+     Set the location information for this logging event. The collected
+     information is cached for future use.
+     */
+    @Override
+    public LocationInfo getLocationInformation() {
+        return new LocationInfo(event.getSource());
+    }
+
+    /**
+     * Return the level of this event. Use this form instead of directly
+     * accessing the <code>level</code> field.  */
+    @Override
+    public Level getLevel() {
+        switch (StandardLevel.getStandardLevel(event.getLevel().intLevel())) {
+            case TRACE:
+                return Level.TRACE;
+            case DEBUG:
+                return Level.DEBUG;
+            case INFO:
+                return Level.INFO;
+            case WARN:
+                return Level.WARN;
+            case ERROR:
+                return Level.ERROR;
+            case FATAL:
+                return Level.FATAL;
+            case OFF:
+                return Level.OFF;
+            case ALL:
+                return Level.ALL;
+            default:
+                return Level.ERROR;
+        }
+    }
+
+    /**
+     * Return the name of the logger. Use this form instead of directly
+     * accessing the <code>categoryName</code> field.
+     */
+    @Override
+    public String getLoggerName() {
+        return event.getLoggerName();
+    }
+
+    /**
+     * Gets the logger of the event.
+     */
+    @Override
+    public Category getLogger() {
+        return Category.getInstance(event.getLoggerName());
+    }
+
+    /*
+     Return the message for this logging event.
+    */
+    @Override
+    public
+    Object getMessage() {
+        return event.getMessage().getFormattedMessage();
+    }
+
+    /*
+     * This method returns the NDC for this event.
+     */
+    @Override
+    public
+    String getNDC() {
+        return event.getContextStack().toString();
+    }
+
+
+    /*
+     Returns the the context corresponding to the <code>key</code> parameter.
+     */
+    @Override
+    public
+    Object getMDC(String key) {
+        if (event.getContextData() != null) {
+            return event.getContextData().getValue(key);
+        } else {
+            return null;
+        }
+    }
+
+    /**
+     Obtain a copy of this thread's MDC prior to serialization or
+     asynchronous logging.
+     */
+    @Override
+    public
+    void getMDCCopy() {
+    }
+
+    @Override
+    public
+    String getRenderedMessage() {
+        return event.getMessage().getFormattedMessage();
+    }
+
+    /**
+     Returns the time when the application started, in milliseconds
+     elapsed since 01.01.1970.  */
+    public static long getStartTime() {
+        return JVM_START_TIME;
+    }
+
+    @Override
+    public
+    String getThreadName() {
+        return event.getThreadName();
+    }
+
+    /**
+     Returns the throwable information contained within this
+     event. May be <code>null</code> if there is no such information.
+
+     <p>Note that the {@link Throwable} object contained within a
+     {@link ThrowableInformation} does not survive serialization.
+
+     @since 1.1 */
+    @Override
+    public
+    ThrowableInformation getThrowableInformation() {
+        if (event.getThrown() != null) {
+            return new ThrowableInformation(event.getThrown());
+        }
+        return null;
+    }
+
+    /**
+     Return this event's throwable's string[] representaion.
+     */
+    @Override
+    public
+    String[] getThrowableStrRep() {
+        if (event.getThrown() != null) {
+            return Throwables.toStringList(event.getThrown()).toArray(new String[0]);
+        }
+        return null;
+    }
+
+    /**
+     * Returns the result of {@code ManagementFactory.getRuntimeMXBean().getStartTime()},
+     * or the current system time if JMX is not available.
+     */
+    private static long initStartTime() {
+        // We'd like to call ManagementFactory.getRuntimeMXBean().getStartTime(),
+        // but Google App Engine throws a java.lang.NoClassDefFoundError
+        // "java.lang.management.ManagementFactory is a restricted class".
+        // The reflection is necessary because without it, Google App Engine
+        // will refuse to initialize this class.
+        try {
+            final Class<?> factoryClass = Loader.loadSystemClass("java.lang.management.ManagementFactory");
+            final Method getRuntimeMXBean = factoryClass.getMethod("getRuntimeMXBean");
+            final Object runtimeMXBean = getRuntimeMXBean.invoke(null);
+
+            final Class<?> runtimeMXBeanClass = Loader.loadSystemClass("java.lang.management.RuntimeMXBean");
+            final Method getStartTime = runtimeMXBeanClass.getMethod("getStartTime");
+            return (Long) getStartTime.invoke(runtimeMXBean);
+        } catch (final Throwable t) {
+            StatusLogger.getLogger().error("Unable to call ManagementFactory.getRuntimeMXBean().getStartTime(), "
+                + "using system time for OnStartupTriggeringPolicy", t);
+            // We have little option but to declare "now" as the beginning of time.
+            return System.currentTimeMillis();
+        }
+    }
+}
diff --git a/log4j-1.2-api/src/src/main/java/org/apache/log4j/builders/BooleanHolder.java b/log4j-1.2-api/src/src/main/java/org/apache/log4j/builders/BooleanHolder.java
new file mode 100644
index 0000000..16e46d3
--- /dev/null
+++ b/log4j-1.2-api/src/src/main/java/org/apache/log4j/builders/BooleanHolder.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.builders;
+
+/**
+ * Holds Boolean values created inside of a Lambda expression.
+ */
+public class BooleanHolder extends Holder<Boolean> {
+    public BooleanHolder() {
+        super(Boolean.FALSE);
+    }
+
+    @Override
+    public void set(Boolean value) {
+        if (value != null) {
+            super.set(value);
+        }
+    }
+}
diff --git a/log4j-1.2-api/src/src/main/java/org/apache/log4j/builders/BuilderManager.java b/log4j-1.2-api/src/src/main/java/org/apache/log4j/builders/BuilderManager.java
new file mode 100644
index 0000000..c677513
--- /dev/null
+++ b/log4j-1.2-api/src/src/main/java/org/apache/log4j/builders/BuilderManager.java
@@ -0,0 +1,94 @@
+/*
+ * 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 org.apache.log4j.Appender;
+import org.apache.log4j.Layout;
+import org.apache.log4j.builders.appender.AppenderBuilder;
+import org.apache.log4j.builders.filter.FilterBuilder;
+import org.apache.log4j.builders.layout.LayoutBuilder;
+import org.apache.log4j.spi.Filter;
+import org.apache.log4j.xml.XmlConfigurationFactory;
+import org.apache.logging.log4j.Logger;
+import org.apache.logging.log4j.core.config.plugins.util.PluginManager;
+import org.apache.logging.log4j.core.config.plugins.util.PluginType;
+import org.apache.logging.log4j.status.StatusLogger;
+import org.apache.logging.log4j.util.LoaderUtil;
+import org.w3c.dom.Element;
+
+import java.lang.reflect.InvocationTargetException;
+import java.util.Map;
+
+/**
+ *
+ */
+public class BuilderManager {
+
+    public static final String CATEGORY = "Log4j Builder";
+    private static final Logger LOGGER = StatusLogger.getLogger();
+    private final Map<String, PluginType<?>> plugins;
+
+    public BuilderManager() {
+        final PluginManager manager = new PluginManager(CATEGORY);
+        manager.collectPlugins();
+        plugins = manager.getPlugins();
+    }
+
+    public Appender parseAppender(String className, Element appenderElement, XmlConfigurationFactory factory) {
+        PluginType<?> plugin = plugins.get(className.toLowerCase());
+        if (plugin != null) {
+            try {
+                @SuppressWarnings("unchecked")
+                AppenderBuilder builder = (AppenderBuilder) LoaderUtil.newInstanceOf(plugin.getPluginClass());
+                return builder.parseAppender(appenderElement, factory);
+            } catch (InstantiationException | IllegalAccessException | InvocationTargetException ex) {
+                LOGGER.warn("Unable to load plugin: {} due to: {}", plugin.getKey(), ex.getMessage());
+            }
+        }
+        return null;
+    }
+
+    public Filter parseFilter(String className, Element filterElement, XmlConfigurationFactory factory) {
+        PluginType<?> plugin = plugins.get(className.toLowerCase());
+        if (plugin != null) {
+            try {
+                @SuppressWarnings("unchecked")
+                FilterBuilder builder = (FilterBuilder) LoaderUtil.newInstanceOf(plugin.getPluginClass());
+                return builder.parseFilter(filterElement, factory);
+            } catch (InstantiationException | IllegalAccessException | InvocationTargetException ex) {
+                LOGGER.warn("Unable to load plugin: {} due to: {}", plugin.getKey(), ex.getMessage());
+            }
+        }
+        return null;
+    }
+
+
+    public Layout parseLayout(String className, Element layoutElement, XmlConfigurationFactory factory) {
+        PluginType<?> plugin = plugins.get(className.toLowerCase());
+        if (plugin != null) {
+            try {
+                @SuppressWarnings("unchecked")
+                LayoutBuilder builder = (LayoutBuilder) LoaderUtil.newInstanceOf(plugin.getPluginClass());
+                return builder.parseLayout(layoutElement, factory);
+            } catch (InstantiationException | IllegalAccessException | InvocationTargetException ex) {
+                LOGGER.warn("Unable to load plugin: {} due to: {}", plugin.getKey(), ex.getMessage());
+            }
+        }
+        return null;
+    }
+
+}
diff --git a/log4j-1.2-api/src/src/main/java/org/apache/log4j/builders/Holder.java b/log4j-1.2-api/src/src/main/java/org/apache/log4j/builders/Holder.java
new file mode 100644
index 0000000..b9ce2bf
--- /dev/null
+++ b/log4j-1.2-api/src/src/main/java/org/apache/log4j/builders/Holder.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.builders;
+
+/**
+ * Provides a place to hold values generated inside of a Lambda expression.
+ */
+public class Holder<T> {
+    private T value;
+
+    public Holder() {
+    }
+
+    public Holder(T defaultValue) {
+        this.value = defaultValue;
+    }
+
+    public void set(T value) {
+        this.value = value;
+    }
+
+    public T get() {
+        return value;
+    }
+}
diff --git a/log4j-1.2-api/src/src/main/java/org/apache/log4j/builders/appender/AppenderBuilder.java b/log4j-1.2-api/src/src/main/java/org/apache/log4j/builders/appender/AppenderBuilder.java
new file mode 100644
index 0000000..2b72efd
--- /dev/null
+++ b/log4j-1.2-api/src/src/main/java/org/apache/log4j/builders/appender/AppenderBuilder.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.builders.appender;
+
+import org.apache.log4j.Appender;
+import org.apache.log4j.xml.XmlConfigurationFactory;
+import org.apache.logging.log4j.core.config.ConfigurationFactory;
+import org.w3c.dom.Element;
+
+import java.util.function.BiFunction;
+
+/**
+ * Define an Appender Builder.
+ */
+public interface AppenderBuilder {
+
+    Appender parseAppender(Element element, XmlConfigurationFactory factory);
+
+}
diff --git a/log4j-1.2-api/src/src/main/java/org/apache/log4j/builders/appender/ConsoleAppenderBuilder.java b/log4j-1.2-api/src/src/main/java/org/apache/log4j/builders/appender/ConsoleAppenderBuilder.java
new file mode 100644
index 0000000..bfb7ff8
--- /dev/null
+++ b/log4j-1.2-api/src/src/main/java/org/apache/log4j/builders/appender/ConsoleAppenderBuilder.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.builders.appender;
+
+import org.apache.log4j.Appender;
+import org.apache.log4j.Layout;
+import org.apache.log4j.bridge.AppenderWrapper;
+import org.apache.log4j.bridge.FilterAdapter;
+import org.apache.log4j.bridge.FilterWrapper;
+import org.apache.log4j.bridge.LayoutAdapter;
+import org.apache.log4j.bridge.LayoutWrapper;
+import org.apache.log4j.builders.Holder;
+import org.apache.log4j.spi.Filter;
+import org.apache.log4j.xml.XmlConfigurationFactory;
+import org.apache.logging.log4j.Logger;
+import org.apache.logging.log4j.core.appender.ConsoleAppender;
+import org.apache.logging.log4j.core.config.plugins.Plugin;
+import org.apache.logging.log4j.status.StatusLogger;
+import org.w3c.dom.Element;
+
+import static org.apache.log4j.builders.BuilderManager.CATEGORY;
+import static org.apache.log4j.xml.XmlConfigurationFactory.*;
+
+/**
+ * Build a Console Appender
+ */
+@Plugin(name = "org.apache.log4j.ConsoleAppender", category = CATEGORY)
+public class ConsoleAppenderBuilder implements AppenderBuilder {
+    private static final String SYSTEM_OUT = "System.out";
+    private static final String SYSTEM_ERR = "System.err";
+    private static final String TARGET = "target";
+
+    private static final Logger LOGGER = StatusLogger.getLogger();
+
+    @Override
+    public Appender parseAppender(Element appenderElement, XmlConfigurationFactory factory) {
+        String name = appenderElement.getAttribute(XmlConfigurationFactory.NAME_ATTR);
+        Holder<String> target = new Holder<>(SYSTEM_OUT);
+        Holder<Layout> layout = new Holder<>();
+        Holder<Filter> filter = new Holder<>();
+        forEachElement(appenderElement.getChildNodes(), (currentElement) -> {
+            switch (currentElement.getTagName()) {
+                case LAYOUT_TAG:
+                    layout.set(factory.parseLayout(currentElement));
+                    break;
+                case FILTER_TAG:
+                    filter.set(factory.parseFilters(currentElement));
+                    break;
+                case PARAM_TAG: {
+                    if (currentElement.getAttribute(NAME_ATTR).equalsIgnoreCase(TARGET)) {
+                        String value = currentElement.getAttribute(VALUE_ATTR);
+                        if (value == null) {
+                            LOGGER.warn("No value supplied for target parameter. Defaulting to System.out.");
+                        } else {
+                            switch (value) {
+                                case SYSTEM_OUT:
+                                    target.set(SYSTEM_OUT);
+                                    break;
+                                case SYSTEM_ERR:
+                                    target.set(SYSTEM_ERR);
+                                    break;
+                                default:
+                                    LOGGER.warn("Invalid value \"{}\" for target parameter. Using default of System.out",
+                                            value);
+                            }
+                        }
+                    }
+                    break;
+                }
+            }
+        });
+        org.apache.logging.log4j.core.Layout<?> consoleLayout = null;
+        org.apache.logging.log4j.core.Filter consoleFilter = null;
+
+        if (layout.get() instanceof LayoutWrapper) {
+            consoleLayout = ((LayoutWrapper) layout.get()).getLayout();
+        } else if (layout.get() != null) {
+            consoleLayout = new LayoutAdapter(layout.get());
+        }
+        if (filter.get() != null) {
+            if (filter.get() instanceof FilterWrapper) {
+                consoleFilter = ((FilterWrapper) filter.get()).getFilter();
+            } else {
+                consoleFilter = new FilterAdapter(filter.get());
+            }
+        }
+        ConsoleAppender.Target consoleTarget = SYSTEM_ERR.equals(target.get())
+                ? ConsoleAppender.Target.SYSTEM_ERR : ConsoleAppender.Target.SYSTEM_OUT;
+        return new AppenderWrapper(ConsoleAppender.newBuilder()
+                .setName(name)
+                .setTarget(consoleTarget)
+                .setLayout(consoleLayout)
+                .setFilter(consoleFilter)
+                .setConfiguration(factory.getConfiguration())
+                .build());
+    }
+}
diff --git a/log4j-1.2-api/src/src/main/java/org/apache/log4j/builders/appender/DailyRollingFileAppenderBuilder.java b/log4j-1.2-api/src/src/main/java/org/apache/log4j/builders/appender/DailyRollingFileAppenderBuilder.java
new file mode 100644
index 0000000..878020f
--- /dev/null
+++ b/log4j-1.2-api/src/src/main/java/org/apache/log4j/builders/appender/DailyRollingFileAppenderBuilder.java
@@ -0,0 +1,146 @@
+/*
+ * 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 org.apache.log4j.Appender;
+import org.apache.log4j.Layout;
+import org.apache.log4j.bridge.AppenderWrapper;
+import org.apache.log4j.bridge.FilterAdapter;
+import org.apache.log4j.bridge.FilterWrapper;
+import org.apache.log4j.bridge.LayoutAdapter;
+import org.apache.log4j.bridge.LayoutWrapper;
+import org.apache.log4j.builders.BooleanHolder;
+import org.apache.log4j.builders.Holder;
+import org.apache.log4j.spi.Filter;
+import org.apache.log4j.xml.XmlConfigurationFactory;
+import org.apache.logging.log4j.Logger;
+import org.apache.logging.log4j.core.appender.FileAppender;
+import org.apache.logging.log4j.core.appender.RollingFileAppender;
+import org.apache.logging.log4j.core.appender.rolling.DefaultRolloverStrategy;
+import org.apache.logging.log4j.core.appender.rolling.RolloverStrategy;
+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.plugins.Plugin;
+import org.apache.logging.log4j.status.StatusLogger;
+import org.w3c.dom.Element;
+
+import static org.apache.log4j.builders.BuilderManager.CATEGORY;
+import static org.apache.log4j.xml.XmlConfigurationFactory.*;
+
+/**
+ * Build a Daily Rolling File Appender
+ */
+@Plugin(name = "org.apache.log4j.DailyRollingFileAppender", category = CATEGORY)
+public class DailyRollingFileAppenderBuilder implements AppenderBuilder {
+
+    private static final Logger LOGGER = StatusLogger.getLogger();
+
+    @Override
+    public Appender parseAppender(Element appenderElement, XmlConfigurationFactory factory) {
+        String name = appenderElement.getAttribute(NAME_ATTR);
+        Holder<Layout> layout = new Holder<>();
+        Holder<Filter> filter = new Holder<>();
+        Holder<String> fileName = new Holder<>();
+        Holder<Boolean> immediateFlush = new BooleanHolder();
+        Holder<Boolean> append = new BooleanHolder();
+        Holder<Boolean> bufferedIo = new BooleanHolder();
+        Holder<Integer> bufferSize = new Holder<>(8192);
+        forEachElement(appenderElement.getChildNodes(), (currentElement) -> {
+            switch (currentElement.getTagName()) {
+                case LAYOUT_TAG:
+                    layout.set(factory.parseLayout(currentElement));
+                    break;
+                case FILTER_TAG:
+                    filter.set(factory.parseFilters(currentElement));
+                    break;
+                case PARAM_TAG: {
+                    switch (currentElement.getAttribute(NAME_ATTR).toLowerCase()) {
+                        case FILE_PARAM:
+                            fileName.set(currentElement.getAttribute(VALUE_ATTR));
+                            break;
+                        case APPEND_PARAM: {
+                            String bool = currentElement.getAttribute(VALUE_ATTR);
+                            if (bool != null) {
+                                append.set(Boolean.parseBoolean(bool));
+                            } else {
+                                LOGGER.warn("No value provided for append parameter");
+                            }
+                            break;
+                        }
+                        case BUFFERED_IO_PARAM: {
+                            String bool = currentElement.getAttribute(VALUE_ATTR);
+                            if (bool != null) {
+                                bufferedIo.set(Boolean.parseBoolean(bool));
+                            } else {
+                                LOGGER.warn("No value provided for bufferedIo parameter");
+                            }
+                            break;
+                        }
+                        case BUFFER_SIZE_PARAM:
+                            String size = currentElement.getAttribute(VALUE_ATTR);
+                            if (size != null) {
+                                bufferSize.set(Integer.parseInt(size));
+                            } else {
+                                LOGGER.warn("No value provide for bufferSize parameter");
+                            }
+                            break;
+                    }
+                    break;
+                }
+            }
+        });
+
+        org.apache.logging.log4j.core.Layout<?> fileLayout = null;
+        org.apache.logging.log4j.core.Filter fileFilter = null;
+        if (bufferedIo.get()) {
+            immediateFlush.set(Boolean.TRUE);
+        }
+        if (layout.get() instanceof LayoutWrapper) {
+            fileLayout = ((LayoutWrapper) layout.get()).getLayout();
+        } else if (layout.get() != null) {
+            fileLayout = new LayoutAdapter(layout.get());
+        }
+        if (filter.get() != null) {
+            if (filter.get() instanceof FilterWrapper) {
+                fileFilter = ((FilterWrapper) filter.get()).getFilter();
+            } else {
+                fileFilter = new FilterAdapter(filter.get());
+            }
+        }
+        if (fileName.get() == null) {
+            LOGGER.warn("Unable to create File Appender, no file name provided");
+            return null;
+        }
+        String filePattern = fileName.get() +"%d{yyy-MM-dd}";
+        TriggeringPolicy policy = TimeBasedTriggeringPolicy.newBuilder().withModulate(true).build();
+        RolloverStrategy strategy = DefaultRolloverStrategy.newBuilder()
+                .withConfig(factory.getConfiguration())
+                .withMax(Integer.toString(Integer.MAX_VALUE))
+                .build();
+        return new AppenderWrapper(RollingFileAppender.newBuilder()
+                .setName(name)
+                .setConfiguration(factory.getConfiguration())
+                .setLayout(fileLayout)
+                .setFilter(fileFilter)
+                .withFileName(fileName.get())
+                .withImmediateFlush(immediateFlush.get())
+                .withFilePattern(filePattern)
+                .withPolicy(policy)
+                .withStrategy(strategy)
+                .build());
+    }
+}
diff --git a/log4j-1.2-api/src/src/main/java/org/apache/log4j/builders/appender/FileAppenderBuilder.java b/log4j-1.2-api/src/src/main/java/org/apache/log4j/builders/appender/FileAppenderBuilder.java
new file mode 100644
index 0000000..4224c5d
--- /dev/null
+++ b/log4j-1.2-api/src/src/main/java/org/apache/log4j/builders/appender/FileAppenderBuilder.java
@@ -0,0 +1,135 @@
+/*
+ * 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 org.apache.log4j.Appender;
+import org.apache.log4j.Layout;
+import org.apache.log4j.bridge.AppenderWrapper;
+import org.apache.log4j.bridge.FilterAdapter;
+import org.apache.log4j.bridge.FilterWrapper;
+import org.apache.log4j.bridge.LayoutAdapter;
+import org.apache.log4j.bridge.LayoutWrapper;
+import org.apache.log4j.builders.BooleanHolder;
+import org.apache.log4j.builders.Holder;
+import org.apache.log4j.spi.Filter;
+import org.apache.log4j.xml.XmlConfigurationFactory;
+import org.apache.logging.log4j.Logger;
+import org.apache.logging.log4j.core.appender.FileAppender;
+import org.apache.logging.log4j.core.config.plugins.Plugin;
+import org.apache.logging.log4j.status.StatusLogger;
+import org.w3c.dom.Element;
+
+import static org.apache.log4j.builders.BuilderManager.CATEGORY;
+import static org.apache.log4j.xml.XmlConfigurationFactory.*;
+
+/**
+ * Build a File Appender
+ */
+@Plugin(name = "org.apache.log4j.FileAppender", category = CATEGORY)
+public class FileAppenderBuilder implements AppenderBuilder {
+
+    private static final Logger LOGGER = StatusLogger.getLogger();
+
+    @Override
+    public Appender parseAppender(Element appenderElement, XmlConfigurationFactory factory) {
+        String name = appenderElement.getAttribute(NAME_ATTR);
+        Holder<Layout> layout = new Holder<>();
+        Holder<Filter> filter = new Holder<>();
+        Holder<String> fileName = new Holder<>();
+        Holder<Boolean> immediateFlush = new BooleanHolder();
+        Holder<Boolean> append = new BooleanHolder();
+        Holder<Boolean> bufferedIo = new BooleanHolder();
+        Holder<Integer> bufferSize = new Holder<>(8192);
+        forEachElement(appenderElement.getChildNodes(), (currentElement) -> {
+            switch (currentElement.getTagName()) {
+                case LAYOUT_TAG:
+                    layout.set(factory.parseLayout(currentElement));
+                    break;
+                case FILTER_TAG:
+                    filter.set(factory.parseFilters(currentElement));
+                    break;
+                case PARAM_TAG: {
+                    switch (currentElement.getAttribute(NAME_ATTR).toLowerCase()) {
+                        case FILE_PARAM:
+                            fileName.set(currentElement.getAttribute(VALUE_ATTR));
+                            break;
+                        case APPEND_PARAM: {
+                            String bool = currentElement.getAttribute(VALUE_ATTR);
+                            if (bool != null) {
+                                append.set(Boolean.parseBoolean(bool));
+                            } else {
+                                LOGGER.warn("No value provided for append parameter");
+                            }
+                            break;
+                        }
+                        case BUFFERED_IO_PARAM: {
+                            String bool = currentElement.getAttribute(VALUE_ATTR);
+                            if (bool != null) {
+                                bufferedIo.set(Boolean.parseBoolean(bool));
+                            } else {
+                                LOGGER.warn("No value provided for bufferedIo parameter");
+                            }
+                            break;
+                        }
+                        case BUFFER_SIZE_PARAM:
+                            String size = currentElement.getAttribute(VALUE_ATTR);
+                            if (size != null) {
+                                bufferSize.set(Integer.parseInt(size));
+                            } else {
+                                LOGGER.warn("No value provide for bufferSize parameter");
+                            }
+                            break;
+                    }
+                    break;
+                }
+            }
+        });
+
+        org.apache.logging.log4j.core.Layout<?> fileLayout = null;
+        org.apache.logging.log4j.core.Filter fileFilter = null;
+        if (bufferedIo.get()) {
+            immediateFlush.set(Boolean.TRUE);
+        }
+        if (layout.get() instanceof LayoutWrapper) {
+            fileLayout = ((LayoutWrapper) layout.get()).getLayout();
+        } else if (layout.get() != null) {
+            fileLayout = new LayoutAdapter(layout.get());
+        }
+        if (filter.get() != null) {
+            if (filter.get() instanceof FilterWrapper) {
+                fileFilter = ((FilterWrapper) filter.get()).getFilter();
+            } else {
+                fileFilter = new FilterAdapter(filter.get());
+            }
+        }
+        if (fileName.get() == null) {
+            LOGGER.warn("Unable to create File Appender, no file name provided");
+            return null;
+        }
+        return new AppenderWrapper(FileAppender.newBuilder()
+                .setName(name)
+                .setConfiguration(factory.getConfiguration())
+                .setLayout(fileLayout)
+                .setFilter(fileFilter)
+                .withFileName(fileName.get())
+                .withImmediateFlush(immediateFlush.get())
+                .withAppend(append.get())
+                .withBufferedIo(bufferedIo.get())
+                .withBufferSize(bufferSize.get())
+                .build());
+    }
+}
diff --git a/log4j-1.2-api/src/src/main/java/org/apache/log4j/builders/appender/NullAppenderBuilder.java b/log4j-1.2-api/src/src/main/java/org/apache/log4j/builders/appender/NullAppenderBuilder.java
new file mode 100644
index 0000000..857203e
--- /dev/null
+++ b/log4j-1.2-api/src/src/main/java/org/apache/log4j/builders/appender/NullAppenderBuilder.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.builders.appender;
+
+import org.apache.log4j.Appender;
+import org.apache.log4j.bridge.AppenderWrapper;
+import org.apache.log4j.xml.XmlConfigurationFactory;
+import org.apache.logging.log4j.Logger;
+import org.apache.logging.log4j.core.appender.NullAppender;
+import org.apache.logging.log4j.core.config.plugins.Plugin;
+import org.apache.logging.log4j.status.StatusLogger;
+import org.w3c.dom.Element;
+
+import static org.apache.log4j.builders.BuilderManager.CATEGORY;
+
+/**
+ * Build a Null Appender
+ */
+@Plugin(name = "org.apache.log4j.varia.NullAppender", category = CATEGORY)
+public class NullAppenderBuilder implements AppenderBuilder {
+
+    private static final Logger LOGGER = StatusLogger.getLogger();
+
+    @Override
+    public Appender parseAppender(Element appenderElement, XmlConfigurationFactory factory) {
+        String name = appenderElement.getAttribute("name");
+        return new AppenderWrapper(NullAppender.createAppender(name));
+    }
+}
diff --git a/log4j-1.2-api/src/src/main/java/org/apache/log4j/builders/appender/RollingFileAppenderBuilder.java b/log4j-1.2-api/src/src/main/java/org/apache/log4j/builders/appender/RollingFileAppenderBuilder.java
new file mode 100644
index 0000000..08a5129
--- /dev/null
+++ b/log4j-1.2-api/src/src/main/java/org/apache/log4j/builders/appender/RollingFileAppenderBuilder.java
@@ -0,0 +1,170 @@
+/*
+ * 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 org.apache.log4j.Appender;
+import org.apache.log4j.Layout;
+import org.apache.log4j.bridge.AppenderWrapper;
+import org.apache.log4j.bridge.FilterAdapter;
+import org.apache.log4j.bridge.FilterWrapper;
+import org.apache.log4j.bridge.LayoutAdapter;
+import org.apache.log4j.bridge.LayoutWrapper;
+import org.apache.log4j.builders.BooleanHolder;
+import org.apache.log4j.builders.Holder;
+import org.apache.log4j.spi.Filter;
+import org.apache.log4j.xml.XmlConfigurationFactory;
+import org.apache.logging.log4j.Logger;
+import org.apache.logging.log4j.core.appender.RollingFileAppender;
+import org.apache.logging.log4j.core.appender.rolling.CompositeTriggeringPolicy;
+import org.apache.logging.log4j.core.appender.rolling.DefaultRolloverStrategy;
+import org.apache.logging.log4j.core.appender.rolling.RolloverStrategy;
+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.plugins.Plugin;
+import org.apache.logging.log4j.status.StatusLogger;
+import org.w3c.dom.Element;
+
+import static org.apache.log4j.builders.BuilderManager.CATEGORY;
+import static org.apache.log4j.xml.XmlConfigurationFactory.*;
+
+/**
+ * Build a File Appender
+ */
+@Plugin(name = "org.apache.log4j.RollingFileAppender", category = CATEGORY)
+public class RollingFileAppenderBuilder implements AppenderBuilder {
+
+    private static final Logger LOGGER = StatusLogger.getLogger();
+
+    @Override
+    public Appender parseAppender(Element appenderElement, XmlConfigurationFactory factory) {
+        String name = appenderElement.getAttribute(NAME_ATTR);
+        Holder<Layout> layout = new Holder<>();
+        Holder<Filter> filter = new Holder<>();
+        Holder<String> fileName = new Holder<>();
+        Holder<Boolean> immediateFlush = new BooleanHolder();
+        Holder<Boolean> append = new BooleanHolder();
+        Holder<Boolean> bufferedIo = new BooleanHolder();
+        Holder<Integer> bufferSize = new Holder<>(8192);
+        Holder<String> maxSize = new Holder<>();
+        Holder<String> maxBackups = new Holder<>();
+        forEachElement(appenderElement.getChildNodes(), (currentElement) -> {
+            switch (currentElement.getTagName()) {
+                case LAYOUT_TAG:
+                    layout.set(factory.parseLayout(currentElement));
+                    break;
+                case FILTER_TAG:
+                    filter.set(factory.parseFilters(currentElement));
+                    break;
+                case PARAM_TAG: {
+                    switch (currentElement.getAttribute(NAME_ATTR).toLowerCase()) {
+                        case FILE_PARAM:
+                            fileName.set(currentElement.getAttribute(VALUE_ATTR));
+                            break;
+                        case APPEND_PARAM: {
+                            String bool = currentElement.getAttribute(VALUE_ATTR);
+                            if (bool != null) {
+                                append.set(Boolean.parseBoolean(bool));
+                            } else {
+                                LOGGER.warn("No value provided for append parameter");
+                            }
+                            break;
+                        }
+                        case BUFFERED_IO_PARAM: {
+                            String bool = currentElement.getAttribute(VALUE_ATTR);
+                            if (bool != null) {
+                                bufferedIo.set(Boolean.parseBoolean(bool));
+                            } else {
+                                LOGGER.warn("No value provided for bufferedIo parameter");
+                            }
+                            break;
+                        }
+                        case BUFFER_SIZE_PARAM: {
+                            String size = currentElement.getAttribute(VALUE_ATTR);
+                            if (size != null) {
+                                bufferSize.set(Integer.parseInt(size));
+                            } else {
+                                LOGGER.warn("No value provide for bufferSize parameter");
+                            }
+                            break;
+                        }
+                        case MAX_BACKUP_INDEX: {
+                            String size = currentElement.getAttribute(VALUE_ATTR);
+                            if (size != null) {
+                                maxBackups.set(size);
+                            } else {
+                                LOGGER.warn("No value provide for maxBackupIndex parameter");
+                            }
+                            break;
+                        }
+                        case MAX_SIZE_PARAM: {
+                            String size = currentElement.getAttribute(VALUE_ATTR);
+                            if (size != null) {
+                                maxSize.set(size);
+                            } else {
+                                LOGGER.warn("No value provide for bufferSize parameter");
+                            }
+                            break;
+                        }
+                    }
+                    break;
+                }
+            }
+        });
+
+        org.apache.logging.log4j.core.Layout<?> fileLayout = null;
+        org.apache.logging.log4j.core.Filter fileFilter = null;
+        if (bufferedIo.get()) {
+            immediateFlush.set(Boolean.TRUE);
+        }
+        if (layout.get() instanceof LayoutWrapper) {
+            fileLayout = ((LayoutWrapper) layout.get()).getLayout();
+        } else if (layout.get() != null) {
+            fileLayout = new LayoutAdapter(layout.get());
+        }
+        if (filter.get() != null) {
+            if (filter.get() instanceof FilterWrapper) {
+                fileFilter = ((FilterWrapper) filter.get()).getFilter();
+            } else {
+                fileFilter = new FilterAdapter(filter.get());
+            }
+        }
+        if (fileName.get() == null) {
+            LOGGER.warn("Unable to create File Appender, no file name provided");
+            return null;
+        }
+        String filePattern = fileName.get() +"%d{yyy-MM-dd}";
+        TriggeringPolicy timePolicy = TimeBasedTriggeringPolicy.newBuilder().withModulate(true).build();
+        SizeBasedTriggeringPolicy sizePolicy = SizeBasedTriggeringPolicy.createPolicy(maxSize.get());
+        CompositeTriggeringPolicy policy = CompositeTriggeringPolicy.createPolicy(sizePolicy, timePolicy);
+        RolloverStrategy strategy = DefaultRolloverStrategy.newBuilder()
+                .withConfig(factory.getConfiguration())
+                .withMax(maxBackups.get())
+                .build();
+        return new AppenderWrapper(RollingFileAppender.newBuilder()
+                .setName(name)
+                .setConfiguration(factory.getConfiguration())
+                .setLayout(fileLayout)
+                .setFilter(fileFilter)
+                .withImmediateFlush(immediateFlush.get())
+                .withFileName(fileName.get())
+                .withFilePattern(filePattern)
+                .withPolicy(policy)
+                .withStrategy(strategy)
+                .build());
+    }
+}
diff --git a/log4j-1.2-api/src/src/main/java/org/apache/log4j/builders/filter/DenyAllFilterBuilder.java b/log4j-1.2-api/src/src/main/java/org/apache/log4j/builders/filter/DenyAllFilterBuilder.java
new file mode 100644
index 0000000..e942b34
--- /dev/null
+++ b/log4j-1.2-api/src/src/main/java/org/apache/log4j/builders/filter/DenyAllFilterBuilder.java
@@ -0,0 +1,46 @@
+/*
+ * 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.filter;
+
+import org.apache.log4j.Layout;
+import org.apache.log4j.bridge.FilterWrapper;
+import org.apache.log4j.bridge.LayoutWrapper;
+import org.apache.log4j.builders.layout.LayoutBuilder;
+import org.apache.log4j.spi.Filter;
+import org.apache.log4j.xml.XmlConfigurationFactory;
+import org.apache.logging.log4j.Logger;
+import org.apache.logging.log4j.core.config.plugins.Plugin;
+import org.apache.logging.log4j.core.filter.DenyAllFilter;
+import org.apache.logging.log4j.core.layout.PatternLayout;
+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, XmlConfigurationFactory factory) {
+        return new FilterWrapper(DenyAllFilter.newBuilder().build());
+    }
+}
diff --git a/log4j-1.2-api/src/src/main/java/org/apache/log4j/builders/filter/FilterBuilder.java b/log4j-1.2-api/src/src/main/java/org/apache/log4j/builders/filter/FilterBuilder.java
new file mode 100644
index 0000000..fbeaefb
--- /dev/null
+++ b/log4j-1.2-api/src/src/main/java/org/apache/log4j/builders/filter/FilterBuilder.java
@@ -0,0 +1,30 @@
+/*
+ * 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.filter;
+
+import org.apache.log4j.spi.Filter;
+import org.apache.log4j.xml.XmlConfigurationFactory;
+import org.w3c.dom.Element;
+
+/**
+ * Define a Filter Builder.
+ */
+public interface FilterBuilder {
+
+    Filter parseFilter(Element element, XmlConfigurationFactory factory);
+
+}
diff --git a/log4j-1.2-api/src/src/main/java/org/apache/log4j/builders/filter/LevelMatchFilterBuilder.java b/log4j-1.2-api/src/src/main/java/org/apache/log4j/builders/filter/LevelMatchFilterBuilder.java
new file mode 100644
index 0000000..b5df7ff
--- /dev/null
+++ b/log4j-1.2-api/src/src/main/java/org/apache/log4j/builders/filter/LevelMatchFilterBuilder.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.builders.filter;
+
+import org.apache.log4j.bridge.FilterWrapper;
+import org.apache.log4j.builders.BooleanHolder;
+import org.apache.log4j.builders.Holder;
+import org.apache.log4j.spi.Filter;
+import org.apache.log4j.xml.XmlConfigurationFactory;
+import org.apache.logging.log4j.Level;
+import org.apache.logging.log4j.Logger;
+import org.apache.logging.log4j.core.config.plugins.Plugin;
+import org.apache.logging.log4j.core.filter.LevelMatchFilter;
+import org.apache.logging.log4j.status.StatusLogger;
+import org.w3c.dom.Element;
+
+import static org.apache.log4j.builders.BuilderManager.CATEGORY;
+import static org.apache.log4j.xml.XmlConfigurationFactory.*;
+import static org.apache.log4j.xml.XmlConfigurationFactory.VALUE_ATTR;
+
+/**
+ * Build a Level match failter.
+ */
+@Plugin(name = "org.apache.log4j.varia.LevelMatchFilter", category = CATEGORY)
+public class LevelMatchFilterBuilder implements FilterBuilder {
+
+    private static final Logger LOGGER = StatusLogger.getLogger();
+    private static final String LEVEL = "level";
+    private static final String ACCEPT_ON_MATCH = "acceptonmatch";
+
+    @Override
+    public Filter parseFilter(Element filterElement, XmlConfigurationFactory factory) {
+        final Holder<String> level = new Holder<>();
+        final Holder<Boolean> acceptOnMatch = new BooleanHolder();
+        forEachElement(filterElement.getElementsByTagName("param"), (currentElement) -> {
+            if (currentElement.getTagName().equals("param")) {
+                switch (currentElement.getAttribute(NAME_ATTR).toLowerCase()) {
+                    case LEVEL:
+                        level.set(currentElement.getAttribute(VALUE_ATTR));
+                        break;
+                    case ACCEPT_ON_MATCH:
+                        acceptOnMatch.set(Boolean.parseBoolean(currentElement.getAttribute(VALUE_ATTR)));
+                        break;
+                }
+            }
+        });
+        Level lvl = Level.ERROR;
+        if (level.get() != null) {
+            lvl = Level.toLevel(level.get(), Level.ERROR);
+        }
+        org.apache.logging.log4j.core.Filter.Result onMatch = acceptOnMatch.get() != null && acceptOnMatch.get()
+                ? org.apache.logging.log4j.core.Filter.Result.ACCEPT
+                : org.apache.logging.log4j.core.Filter.Result.DENY;
+        return new FilterWrapper(LevelMatchFilter.newBuilder()
+                .setLevel(lvl)
+                .setOnMatch(onMatch)
+                .setOnMismatch(org.apache.logging.log4j.core.Filter.Result.NEUTRAL)
+                .build());
+    }
+}
diff --git a/log4j-1.2-api/src/src/main/java/org/apache/log4j/builders/filter/LevelRangeFilterBuilder.java b/log4j-1.2-api/src/src/main/java/org/apache/log4j/builders/filter/LevelRangeFilterBuilder.java
new file mode 100644
index 0000000..1082245
--- /dev/null
+++ b/log4j-1.2-api/src/src/main/java/org/apache/log4j/builders/filter/LevelRangeFilterBuilder.java
@@ -0,0 +1,81 @@
+/*
+ * 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.filter;
+
+import org.apache.log4j.bridge.FilterWrapper;
+import org.apache.log4j.builders.BooleanHolder;
+import org.apache.log4j.builders.Holder;
+import org.apache.log4j.spi.Filter;
+import org.apache.log4j.xml.XmlConfigurationFactory;
+import org.apache.logging.log4j.Level;
+import org.apache.logging.log4j.Logger;
+import org.apache.logging.log4j.core.config.plugins.Plugin;
+import org.apache.logging.log4j.core.filter.LevelRangeFilter;
+import org.apache.logging.log4j.status.StatusLogger;
+import org.w3c.dom.Element;
+
+
+import static org.apache.log4j.builders.BuilderManager.CATEGORY;
+import static org.apache.log4j.xml.XmlConfigurationFactory.*;
+
+/**
+ * Build a Level match failter.
+ */
+@Plugin(name = "org.apache.log4j.varia.LevelRangeFilter", category = CATEGORY)
+public class LevelRangeFilterBuilder implements FilterBuilder {
+
+    private static final Logger LOGGER = StatusLogger.getLogger();
+    private static final String LEVEL_MAX = "levelmax";
+    private static final String LEVEL_MIN = "levelmin";
+    private static final String ACCEPT_ON_MATCH = "acceptonmatch";
+
+    @Override
+    public Filter parseFilter(Element filterElement, XmlConfigurationFactory factory) {
+        final Holder<String> levelMax = new Holder<>();
+        final Holder<String> levelMin = new Holder<>();
+        final Holder<Boolean> acceptOnMatch = new BooleanHolder();
+        forEachElement(filterElement.getElementsByTagName("param"), (currentElement) -> {
+            if (currentElement.getTagName().equals("param")) {
+                switch (currentElement.getAttribute(NAME_ATTR).toLowerCase()) {
+                    case LEVEL_MAX:
+                        levelMax.set(currentElement.getAttribute(VALUE_ATTR));
+                        break;
+                    case LEVEL_MIN:
+                        levelMax.set(currentElement.getAttribute(VALUE_ATTR));
+                        break;
+                    case ACCEPT_ON_MATCH:
+                        acceptOnMatch.set(Boolean.parseBoolean(currentElement.getAttribute(VALUE_ATTR)));
+                        break;
+                }
+            }
+        });
+        Level max = Level.FATAL;
+        Level min = Level.TRACE;
+        if (levelMax.get() != null) {
+            max = Level.toLevel(levelMax.get(), Level.FATAL);
+        }
+        if (levelMin.get() != null) {
+            min = Level.toLevel(levelMin.get(), Level.DEBUG);
+        }
+        org.apache.logging.log4j.core.Filter.Result onMatch = acceptOnMatch.get() != null && acceptOnMatch.get()
+                ? org.apache.logging.log4j.core.Filter.Result.ACCEPT
+                : org.apache.logging.log4j.core.Filter.Result.NEUTRAL;
+
+        return new FilterWrapper(LevelRangeFilter.createFilter(min, max, onMatch,
+                org.apache.logging.log4j.core.Filter.Result.DENY));
+    }
+}
diff --git a/log4j-1.2-api/src/src/main/java/org/apache/log4j/builders/filter/StringMatchFilterBuilder.java b/log4j-1.2-api/src/src/main/java/org/apache/log4j/builders/filter/StringMatchFilterBuilder.java
new file mode 100644
index 0000000..8e4f71a
--- /dev/null
+++ b/log4j-1.2-api/src/src/main/java/org/apache/log4j/builders/filter/StringMatchFilterBuilder.java
@@ -0,0 +1,75 @@
+/*
+ * 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.filter;
+
+import org.apache.log4j.bridge.FilterWrapper;
+import org.apache.log4j.builders.BooleanHolder;
+import org.apache.log4j.builders.Holder;
+import org.apache.log4j.spi.Filter;
+import org.apache.log4j.xml.XmlConfigurationFactory;
+import org.apache.logging.log4j.Level;
+import org.apache.logging.log4j.Logger;
+import org.apache.logging.log4j.core.config.plugins.Plugin;
+import org.apache.logging.log4j.core.filter.LevelMatchFilter;
+import org.apache.logging.log4j.core.filter.StringMatchFilter;
+import org.apache.logging.log4j.status.StatusLogger;
+import org.w3c.dom.Element;
+
+import static org.apache.log4j.builders.BuilderManager.CATEGORY;
+import static org.apache.log4j.xml.XmlConfigurationFactory.*;
+
+/**
+ * Build a String match filter.
+ */
+@Plugin(name = "org.apache.log4j.varia.StringMatchFilter", category = CATEGORY)
+public class StringMatchFilterBuilder implements FilterBuilder {
+
+    private static final Logger LOGGER = StatusLogger.getLogger();
+    private static final String STRING_TO_MATCH = "stringtomatch";
+    private static final String ACCEPT_ON_MATCH = "acceptonmatch";
+
+    @Override
+    public Filter parseFilter(Element filterElement, XmlConfigurationFactory factory) {
+        final Holder<Boolean> acceptOnMatch = new BooleanHolder();
+        final Holder<String> text = new Holder<>();
+        forEachElement(filterElement.getElementsByTagName("param"), (currentElement) -> {
+            if (currentElement.getTagName().equals("param")) {
+                switch (currentElement.getAttribute(NAME_ATTR).toLowerCase()) {
+                    case STRING_TO_MATCH:
+                        text.set(currentElement.getAttribute(VALUE_ATTR));
+                        break;
+                    case ACCEPT_ON_MATCH:
+                        acceptOnMatch.set(Boolean.parseBoolean(currentElement.getAttribute(VALUE_ATTR)));
+                        break;
+
+                }
+            }
+        });
+        if (text.get() == null) {
+            LOGGER.warn("No text provided for StringMatchFilter");
+            return null;
+        }
+        org.apache.logging.log4j.core.Filter.Result onMatch = acceptOnMatch.get() != null && acceptOnMatch.get()
+                ? org.apache.logging.log4j.core.Filter.Result.ACCEPT
+                : org.apache.logging.log4j.core.Filter.Result.DENY;
+        return new FilterWrapper(StringMatchFilter.newBuilder()
+                .setMatchString(text.get())
+                .setOnMatch(onMatch)
+                .setOnMismatch(org.apache.logging.log4j.core.Filter.Result.NEUTRAL)
+                .build());
+    }
+}
diff --git a/log4j-1.2-api/src/src/main/java/org/apache/log4j/builders/layout/HtmlLayoutBuilder.java b/log4j-1.2-api/src/src/main/java/org/apache/log4j/builders/layout/HtmlLayoutBuilder.java
new file mode 100644
index 0000000..78b5f40
--- /dev/null
+++ b/log4j-1.2-api/src/src/main/java/org/apache/log4j/builders/layout/HtmlLayoutBuilder.java
@@ -0,0 +1,60 @@
+/*
+ * 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 org.apache.log4j.Layout;
+import org.apache.log4j.bridge.LayoutWrapper;
+import org.apache.log4j.builders.BooleanHolder;
+import org.apache.log4j.builders.Holder;
+import org.apache.log4j.xml.XmlConfigurationFactory;
+import org.apache.logging.log4j.Logger;
+import org.apache.logging.log4j.core.config.plugins.Plugin;
+import org.apache.logging.log4j.core.layout.HtmlLayout;
+import org.apache.logging.log4j.status.StatusLogger;
+import org.w3c.dom.Element;
+
+import static org.apache.log4j.builders.BuilderManager.CATEGORY;
+import static org.apache.log4j.xml.XmlConfigurationFactory.*;
+
+/**
+ * Build a Pattern Layout
+ */
+@Plugin(name = "org.apache.log4j.HTMLLayout", category = CATEGORY)
+public class HtmlLayoutBuilder implements LayoutBuilder {
+
+    private static final Logger LOGGER = StatusLogger.getLogger();
+
+
+    @Override
+    public Layout parseLayout(Element layoutElement, XmlConfigurationFactory factory) {
+        final Holder<String> title = new Holder<>();
+        final Holder<Boolean> locationInfo = new BooleanHolder();
+        forEachElement(layoutElement.getElementsByTagName("param"), (currentElement) -> {
+            if (currentElement.getTagName().equals("param")) {
+                if ("title".equalsIgnoreCase(currentElement.getAttribute("name"))) {
+                    title.set(currentElement.getAttribute("value"));
+                } else if ("locationInfo".equalsIgnoreCase(currentElement.getAttribute("name"))) {
+                    locationInfo.set(Boolean.parseBoolean(currentElement.getAttribute("value")));
+                }
+            }
+        });
+        return new LayoutWrapper(HtmlLayout.newBuilder()
+                .withTitle(title.get())
+                .withLocationInfo(locationInfo.get())
+                .build());
+    }
+}
diff --git a/log4j-1.2-api/src/src/main/java/org/apache/log4j/builders/layout/LayoutBuilder.java b/log4j-1.2-api/src/src/main/java/org/apache/log4j/builders/layout/LayoutBuilder.java
new file mode 100644
index 0000000..9dc88f3
--- /dev/null
+++ b/log4j-1.2-api/src/src/main/java/org/apache/log4j/builders/layout/LayoutBuilder.java
@@ -0,0 +1,30 @@
+/*
+ * 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 org.apache.log4j.Layout;
+import org.apache.log4j.xml.XmlConfigurationFactory;
+import org.w3c.dom.Element;
+
+/**
+ * Define a Layout Builder.
+ */
+public interface LayoutBuilder {
+
+    Layout parseLayout(Element element, XmlConfigurationFactory factory);
+
+}
diff --git a/log4j-1.2-api/src/src/main/java/org/apache/log4j/builders/layout/PatternLayoutBuilder.java b/log4j-1.2-api/src/src/main/java/org/apache/log4j/builders/layout/PatternLayoutBuilder.java
new file mode 100644
index 0000000..b6a1964
--- /dev/null
+++ b/log4j-1.2-api/src/src/main/java/org/apache/log4j/builders/layout/PatternLayoutBuilder.java
@@ -0,0 +1,77 @@
+/*
+ * 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 org.apache.log4j.Layout;
+import org.apache.log4j.bridge.LayoutWrapper;
+import org.apache.log4j.xml.XmlConfigurationFactory;
+import org.apache.logging.log4j.Logger;
+import org.apache.logging.log4j.core.config.plugins.Plugin;
+import org.apache.logging.log4j.core.config.plugins.PluginAliases;
+import org.apache.logging.log4j.core.layout.PatternLayout;
+import org.apache.logging.log4j.status.StatusLogger;
+import org.w3c.dom.Element;
+import org.w3c.dom.Node;
+import org.w3c.dom.NodeList;
+
+import static org.apache.log4j.builders.BuilderManager.CATEGORY;
+
+/**
+ * Build a Pattern Layout
+ */
+@Plugin(name = "org.apache.log4j.PatternLayout", category = CATEGORY)
+@PluginAliases("org.apache.log4j.EnhancedPatternLayout")
+public class PatternLayoutBuilder implements LayoutBuilder {
+
+    private static final Logger LOGGER = StatusLogger.getLogger();
+
+    @Override
+    public Layout parseLayout(Element layoutElement, XmlConfigurationFactory factory) {
+        NodeList params = layoutElement.getElementsByTagName("param");
+        final int length = params.getLength();
+        String pattern = null;
+        for (int index = 0; index < length; ++ index) {
+            Node currentNode = params.item(index);
+            if (currentNode.getNodeType() == Node.ELEMENT_NODE) {
+                Element currentElement = (Element) currentNode;
+                if (currentElement.getTagName().equals("param")) {
+                    if ("conversionPattern".equalsIgnoreCase(currentElement.getAttribute("name"))) {
+                        pattern = currentElement.getAttribute("value")
+                                // Log4j 2's %x (NDC) is not compatible with Log4j 1's
+                                // %x
+                                // Log4j 1: "foo bar baz"
+                                // Log4j 2: "[foo, bar, baz]"
+                                // Use %ndc to get the Log4j 1 format
+                                .replace("%x", "%ndc")
+
+                                // Log4j 2's %X (MDC) is not compatible with Log4j 1's
+                                // %X
+                                // Log4j 1: "{{foo,bar}{hoo,boo}}"
+                                // Log4j 2: "{foo=bar,hoo=boo}"
+                                // Use %properties to get the Log4j 1 format
+                                .replace("%X", "%properties");
+                        break;
+                    }
+                }
+            }
+        }
+        return new LayoutWrapper(PatternLayout.newBuilder()
+                .withPattern(pattern)
+                .withConfiguration(factory.getConfiguration())
+                .build());
+    }
+}
diff --git a/log4j-1.2-api/src/src/main/java/org/apache/log4j/builders/layout/SimpleLayoutBuilder.java b/log4j-1.2-api/src/src/main/java/org/apache/log4j/builders/layout/SimpleLayoutBuilder.java
new file mode 100644
index 0000000..4f09bc2
--- /dev/null
+++ b/log4j-1.2-api/src/src/main/java/org/apache/log4j/builders/layout/SimpleLayoutBuilder.java
@@ -0,0 +1,46 @@
+/*
+ * 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 org.apache.log4j.Layout;
+import org.apache.log4j.bridge.LayoutWrapper;
+import org.apache.log4j.xml.XmlConfigurationFactory;
+import org.apache.logging.log4j.Logger;
+import org.apache.logging.log4j.core.config.plugins.Plugin;
+import org.apache.logging.log4j.core.layout.PatternLayout;
+import org.apache.logging.log4j.status.StatusLogger;
+import org.w3c.dom.Element;
+
+import static org.apache.log4j.builders.BuilderManager.CATEGORY;
+import static org.apache.log4j.xml.XmlConfigurationFactory.*;
+
+/**
+ * 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, XmlConfigurationFactory factory) {
+        return new LayoutWrapper(PatternLayout.newBuilder()
+                .withPattern("%level - %m%n")
+                .withConfiguration(factory.getConfiguration())
+                .build());
+    }
+}
diff --git a/log4j-1.2-api/src/src/main/java/org/apache/log4j/builders/layout/TTCCLayoutBuilder.java b/log4j-1.2-api/src/src/main/java/org/apache/log4j/builders/layout/TTCCLayoutBuilder.java
new file mode 100644
index 0000000..b15d351
--- /dev/null
+++ b/log4j-1.2-api/src/src/main/java/org/apache/log4j/builders/layout/TTCCLayoutBuilder.java
@@ -0,0 +1,103 @@
+/*
+ * 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 org.apache.log4j.Layout;
+import org.apache.log4j.bridge.LayoutWrapper;
+import org.apache.log4j.builders.BooleanHolder;
+import org.apache.log4j.builders.Holder;
+import org.apache.log4j.xml.XmlConfigurationFactory;
+import org.apache.logging.log4j.Logger;
+import org.apache.logging.log4j.core.config.plugins.Plugin;
+import org.apache.logging.log4j.core.layout.PatternLayout;
+import org.apache.logging.log4j.status.StatusLogger;
+import org.w3c.dom.Element;
+
+import static org.apache.log4j.builders.BuilderManager.CATEGORY;
+import static org.apache.log4j.xml.XmlConfigurationFactory.*;
+
+/**
+ * Build a Pattern Layout
+ */
+@Plugin(name = "org.apache.log4j.TTCCLayout", category = CATEGORY)
+public class TTCCLayoutBuilder implements LayoutBuilder {
+
+    private static final Logger LOGGER = StatusLogger.getLogger();
+
+    private static final String THREAD_PRINTING_PARAM = "threadprinting";
+    private static final String CATEGORY_PREFIXING_PARAM = "categoryprefixing";
+    private static final String CONTEXT_PRINTING_PARAM = "contextprinting";
+    private static final String DATE_FORMAT_PARAM = "dateformat";
+    private static final String TIMEZONE_FORMAT = "timezone";
+
+    @Override
+    public Layout parseLayout(Element layoutElement, XmlConfigurationFactory factory) {
+        final Holder<Boolean> threadPrinting = new BooleanHolder();
+        final Holder<Boolean> categoryPrefixing = new BooleanHolder();
+        final Holder<Boolean> contextPrinting = new BooleanHolder();
+        final Holder<String> dateFormat = new Holder<>();
+        final Holder<String> timezone = new Holder<>();
+        forEachElement(layoutElement.getElementsByTagName("param"), (currentElement) -> {
+            if (currentElement.getTagName().equals("param")) {
+                switch (currentElement.getAttribute(NAME_ATTR).toLowerCase()) {
+                    case THREAD_PRINTING_PARAM:
+                        threadPrinting.set(Boolean.parseBoolean(currentElement.getAttribute(VALUE_ATTR)));
+                        break;
+                    case CATEGORY_PREFIXING_PARAM:
+                        categoryPrefixing.set(Boolean.parseBoolean(currentElement.getAttribute(VALUE_ATTR)));
+                        break;
+                    case CONTEXT_PRINTING_PARAM:
+                        contextPrinting.set(Boolean.parseBoolean(currentElement.getAttribute(VALUE_ATTR)));
+                        break;
+                    case DATE_FORMAT_PARAM:
+                        dateFormat.set(currentElement.getAttribute(VALUE_ATTR));
+                        break;
+                    case TIMEZONE_FORMAT:
+                        timezone.set(currentElement.getAttribute(VALUE_ATTR));
+                        break;
+                }
+            }
+        });
+        StringBuilder sb = new StringBuilder();
+        if (dateFormat.get() != null) {
+            if (RELATIVE.equalsIgnoreCase(dateFormat.get())) {
+                sb.append("%r ");
+            } else {
+                sb.append("%d{").append(dateFormat.get()).append("}");
+                if (timezone.get() != null) {
+                    sb.append("{").append(timezone.get()).append("}");
+                }
+                sb.append(" ");
+            }
+        }
+        if (threadPrinting.get()) {
+            sb.append("[%t] ");
+        }
+        sb.append("%p ");
+        if (categoryPrefixing.get()) {
+            sb.append("%c ");
+        }
+        if (contextPrinting.get()) {
+            sb.append("%notEmpty{%ndc }");
+        }
+        sb.append("- %m%n");
+        return new LayoutWrapper(PatternLayout.newBuilder()
+                .withPattern(sb.toString())
+                .withConfiguration(factory.getConfiguration())
+                .build());
+    }
+}
diff --git a/log4j-1.2-api/src/src/main/java/org/apache/log4j/builders/layout/XmlLayoutBuilder.java b/log4j-1.2-api/src/src/main/java/org/apache/log4j/builders/layout/XmlLayoutBuilder.java
new file mode 100644
index 0000000..5436e5c
--- /dev/null
+++ b/log4j-1.2-api/src/src/main/java/org/apache/log4j/builders/layout/XmlLayoutBuilder.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.builders.layout;
+
+import org.apache.log4j.Layout;
+import org.apache.log4j.bridge.LayoutWrapper;
+import org.apache.log4j.builders.BooleanHolder;
+import org.apache.log4j.builders.Holder;
+import org.apache.log4j.xml.XmlConfigurationFactory;
+import org.apache.logging.log4j.Logger;
+import org.apache.logging.log4j.core.config.plugins.Plugin;
+import org.apache.logging.log4j.core.layout.HtmlLayout;
+import org.apache.logging.log4j.core.layout.XmlLayout;
+import org.apache.logging.log4j.status.StatusLogger;
+import org.w3c.dom.Element;
+
+import static org.apache.log4j.builders.BuilderManager.CATEGORY;
+import static org.apache.log4j.xml.XmlConfigurationFactory.forEachElement;
+
+/**
+ * Build a Pattern Layout
+ */
+@Plugin(name = "org.apache.log4j.xml.XMLLayout", category = CATEGORY)
+public class XmlLayoutBuilder implements LayoutBuilder {
+
+    private static final Logger LOGGER = StatusLogger.getLogger();
+
+
+    @Override
+    public Layout parseLayout(Element layoutElement, XmlConfigurationFactory factory) {
+        final Holder<Boolean> properties = new BooleanHolder();
+        final Holder<Boolean> locationInfo = new BooleanHolder();
+        forEachElement(layoutElement.getElementsByTagName("param"), (currentElement) -> {
+            if ("properties".equalsIgnoreCase(currentElement.getAttribute("name"))) {
+                properties.set(Boolean.parseBoolean(currentElement.getAttribute("value")));
+            } else if ("locationInfo".equalsIgnoreCase(currentElement.getAttribute("name"))) {
+                locationInfo.set(Boolean.parseBoolean(currentElement.getAttribute("value")));
+            }
+        });
+        return new LayoutWrapper(XmlLayout.newBuilder()
+                .setLocationInfo(locationInfo.get())
+                .setProperties(properties.get())
+                .build());
+    }
+}
diff --git a/log4j-1.2-api/src/src/main/java/org/apache/log4j/config/InputStreamWrapper.java b/log4j-1.2-api/src/src/main/java/org/apache/log4j/config/InputStreamWrapper.java
new file mode 100644
index 0000000..19bf9a9
--- /dev/null
+++ b/log4j-1.2-api/src/src/main/java/org/apache/log4j/config/InputStreamWrapper.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.config;
+
+import java.io.IOException;
+import java.io.InputStream;
+
+class InputStreamWrapper extends InputStream {
+
+    private final String description;
+    private final InputStream input;
+
+    public InputStreamWrapper(final InputStream input, final String description) {
+        this.input = input;
+        this.description = description;
+    }
+
+    @Override
+    public int available() throws IOException {
+        return input.available();
+    }
+
+    @Override
+    public void close() throws IOException {
+        input.close();
+    }
+
+    @Override
+    public boolean equals(final Object obj) {
+        return input.equals(obj);
+    }
+
+    @Override
+    public int hashCode() {
+        return input.hashCode();
+    }
+
+    @Override
+    public synchronized void mark(final int readlimit) {
+        input.mark(readlimit);
+    }
+
+    @Override
+    public boolean markSupported() {
+        return input.markSupported();
+    }
+
+    @Override
+    public int read() throws IOException {
+        return input.read();
+    }
+
+    @Override
+    public int read(final byte[] b) throws IOException {
+        return input.read(b);
+    }
+
+    @Override
+    public int read(final byte[] b, final int off, final int len) throws IOException {
+        return input.read(b, off, len);
+    }
+
+    @Override
+    public synchronized void reset() throws IOException {
+        input.reset();
+    }
+
+    @Override
+    public long skip(final long n) throws IOException {
+        return input.skip(n);
+    }
+
+    @Override
+    public String toString() {
+        return getClass().getSimpleName() + " [description=" + description + ", input=" + input + "]";
+    }
+
+}
diff --git a/log4j-1.2-api/src/src/main/java/org/apache/log4j/config/Log4j1Configuration.java b/log4j-1.2-api/src/src/main/java/org/apache/log4j/config/Log4j1Configuration.java
new file mode 100644
index 0000000..5a6b50d
--- /dev/null
+++ b/log4j-1.2-api/src/src/main/java/org/apache/log4j/config/Log4j1Configuration.java
@@ -0,0 +1,57 @@
+/*
+ * 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.logging.log4j.core.LoggerContext;
+import org.apache.logging.log4j.core.config.AbstractConfiguration;
+import org.apache.logging.log4j.core.config.Configuration;
+import org.apache.logging.log4j.core.config.ConfigurationSource;
+import org.apache.logging.log4j.core.config.Node;
+import org.apache.logging.log4j.core.config.Reconfigurable;
+
+/**
+ * Class Description goes here.
+ */
+public class Log4j1Configuration extends AbstractConfiguration implements Reconfigurable {
+
+    public Log4j1Configuration(final LoggerContext loggerContext, final ConfigurationSource source,
+            int monitorIntervalSeconds) {
+        super(loggerContext, source);
+        initializeWatchers(this, source, monitorIntervalSeconds);
+    }
+
+    @Override
+    protected void doConfigure() {
+        super.getScheduler().start();
+
+    }
+
+    /**
+     * Initialize the configuration.
+     */
+    @Override
+    public void initialize() {
+        doConfigure();
+        setState(State.INITIALIZED);
+        LOGGER.debug("Configuration {} initialized", this);
+    }
+
+    @Override
+    public Configuration reconfigure() {
+        return null;
+    }
+}
diff --git a/log4j-1.2-api/src/src/main/java/org/apache/log4j/config/Log4j1ConfigurationConverter.java b/log4j-1.2-api/src/src/main/java/org/apache/log4j/config/Log4j1ConfigurationConverter.java
new file mode 100644
index 0000000..4475f6a
--- /dev/null
+++ b/log4j-1.2-api/src/src/main/java/org/apache/log4j/config/Log4j1ConfigurationConverter.java
@@ -0,0 +1,220 @@
+/*
+ * 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 java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.nio.file.FileVisitResult;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.SimpleFileVisitor;
+import java.nio.file.attribute.BasicFileAttributes;
+import java.util.concurrent.atomic.AtomicInteger;
+
+import org.apache.logging.log4j.core.config.ConfigurationException;
+import org.apache.logging.log4j.core.config.builder.api.ConfigurationBuilder;
+import org.apache.logging.log4j.core.config.builder.impl.BuiltConfiguration;
+import org.apache.logging.log4j.core.tools.BasicCommandLineArguments;
+import org.apache.logging.log4j.core.tools.picocli.CommandLine;
+import org.apache.logging.log4j.core.tools.picocli.CommandLine.Command;
+import org.apache.logging.log4j.core.tools.picocli.CommandLine.Option;
+
+/**
+ * Tool for converting a Log4j 1.x properties configuration file to Log4j 2.x XML configuration file.
+ *
+ * <p>
+ * Run with "--help" on the command line.
+ * </p>
+ *
+ * <p>
+ * Example:
+ * </p>
+ *
+ * <pre>
+ * java org.apache.log4j.config.Log4j1ConfigurationConverter --recurse
+ * E:\vcs\git\apache\logging\logging-log4j2\log4j-1.2-api\src\test\resources\config-1.2\hadoop --in log4j.properties --verbose
+ * </pre>
+ */
+public final class Log4j1ConfigurationConverter {
+
+    @Command(name = "Log4j1ConfigurationConverter")
+    public static class CommandLineArguments extends BasicCommandLineArguments implements Runnable {
+
+        @Option(names = { "--failfast", "-f" }, description = "Fails on the first failure in recurse mode.")
+        private boolean failFast;
+
+        @Option(names = { "--in", "-i" }, description = "Specifies the input file.")
+        private Path pathIn;
+
+        @Option(names = { "--out", "-o" }, description = "Specifies the output file.")
+        private Path pathOut;
+
+        @Option(names = { "--recurse", "-r" }, description = "Recurses into this folder looking for the input file")
+        private Path recurseIntoPath;
+
+        @Option(names = { "--verbose", "-v" }, description = "Be verbose.")
+        private boolean verbose;
+
+        public Path getPathIn() {
+            return pathIn;
+        }
+
+        public Path getPathOut() {
+            return pathOut;
+        }
+
+        public Path getRecurseIntoPath() {
+            return recurseIntoPath;
+        }
+
+        public boolean isFailFast() {
+            return failFast;
+        }
+
+        public boolean isVerbose() {
+            return verbose;
+        }
+
+        public void setFailFast(final boolean failFast) {
+            this.failFast = failFast;
+        }
+
+        public void setPathIn(final Path pathIn) {
+            this.pathIn = pathIn;
+        }
+
+        public void setPathOut(final Path pathOut) {
+            this.pathOut = pathOut;
+        }
+
+        public void setRecurseIntoPath(final Path recurseIntoPath) {
+            this.recurseIntoPath = recurseIntoPath;
+        }
+
+        public void setVerbose(final boolean verbose) {
+            this.verbose = verbose;
+        }
+
+        @Override
+        public void run() {
+            if (isHelp()) {
+                CommandLine.usage(this, System.err);
+                return;
+            }
+            new Log4j1ConfigurationConverter(this).run();
+        }
+
+        @Override
+        public String toString() {
+            return "CommandLineArguments [recurseIntoPath=" + recurseIntoPath + ", verbose=" + verbose + ", pathIn="
+                    + pathIn + ", pathOut=" + pathOut + "]";
+        }
+    }
+
+    private static final String FILE_EXT_XML = ".xml";
+
+    public static void main(final String[] args) {
+        CommandLine.run(new CommandLineArguments(), System.err, args);
+    }
+
+    public static Log4j1ConfigurationConverter run(final CommandLineArguments cla) {
+        final Log4j1ConfigurationConverter log4j1ConfigurationConverter = new Log4j1ConfigurationConverter(cla);
+        log4j1ConfigurationConverter.run();
+        return log4j1ConfigurationConverter;
+    }
+
+    private final CommandLineArguments cla;
+
+    private Log4j1ConfigurationConverter(final CommandLineArguments cla) {
+        this.cla = cla;
+    }
+
+    protected void convert(final InputStream input, final OutputStream output) throws IOException {
+        final ConfigurationBuilder<BuiltConfiguration> builder = new Log4j1ConfigurationParser()
+                .buildConfigurationBuilder(input);
+        builder.writeXmlConfiguration(output);
+    }
+
+    InputStream getInputStream() throws IOException {
+        final Path pathIn = cla.getPathIn();
+        return pathIn == null ? System.in : new InputStreamWrapper(Files.newInputStream(pathIn), pathIn.toString());
+    }
+
+    OutputStream getOutputStream() throws IOException {
+        final Path pathOut = cla.getPathOut();
+        return pathOut == null ? System.out : Files.newOutputStream(pathOut);
+    }
+
+    private void run() {
+        if (cla.getRecurseIntoPath() != null) {
+            final AtomicInteger countOKs = new AtomicInteger();
+            final AtomicInteger countFails = new AtomicInteger();
+            try {
+                Files.walkFileTree(cla.getRecurseIntoPath(), new SimpleFileVisitor<Path>() {
+                    @Override
+                    public FileVisitResult visitFile(final Path file, final BasicFileAttributes attrs)
+                            throws IOException {
+                        if (cla.getPathIn() == null || file.getFileName().equals(cla.getPathIn())) {
+                            verbose("Reading %s", file);
+                            String newFile = file.getFileName().toString();
+                            final int lastIndex = newFile.lastIndexOf(".");
+                            newFile = lastIndex < 0 ? newFile + FILE_EXT_XML
+                                    : newFile.substring(0, lastIndex) + FILE_EXT_XML;
+                            final Path resolved = file.resolveSibling(newFile);
+                            try (final InputStream input = new InputStreamWrapper(Files.newInputStream(file), file.toString());
+                                    final OutputStream output = Files.newOutputStream(resolved)) {
+                                try {
+                                    convert(input, output);
+                                    countOKs.incrementAndGet();
+                                } catch (ConfigurationException | IOException e) {
+                                    countFails.incrementAndGet();
+                                    if (cla.isFailFast()) {
+                                        throw e;
+                                    }
+                                    e.printStackTrace();
+                                }
+                                verbose("Wrote %s", resolved);
+                            }
+                        }
+                        return FileVisitResult.CONTINUE;
+                    }
+                });
+            } catch (final IOException e) {
+                throw new ConfigurationException(e);
+            } finally {
+                verbose("OK = %,d, Failures = %,d, Total = %,d", countOKs.get(), countFails.get(),
+                        countOKs.get() + countFails.get());
+            }
+        } else {
+            verbose("Reading %s", cla.getPathIn());
+            try (final InputStream input = getInputStream(); final OutputStream output = getOutputStream()) {
+                convert(input, output);
+            } catch (final IOException e) {
+                throw new ConfigurationException(e);
+            }
+            verbose("Wrote %s", cla.getPathOut());
+        }
+    }
+
+    private void verbose(final String template, final Object... args) {
+        if (cla.isVerbose()) {
+            System.err.println(String.format(template, args));
+        }
+    }
+
+}
diff --git a/log4j-1.2-api/src/src/main/java/org/apache/log4j/config/Log4j1ConfigurationFactory.java b/log4j-1.2-api/src/src/main/java/org/apache/log4j/config/Log4j1ConfigurationFactory.java
new file mode 100644
index 0000000..83f6675
--- /dev/null
+++ b/log4j-1.2-api/src/src/main/java/org/apache/log4j/config/Log4j1ConfigurationFactory.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.config;
+
+import java.io.IOException;
+import java.io.InputStream;
+
+import org.apache.logging.log4j.core.LoggerContext;
+import org.apache.logging.log4j.core.config.Configuration;
+import org.apache.logging.log4j.core.config.ConfigurationException;
+import org.apache.logging.log4j.core.config.ConfigurationFactory;
+import org.apache.logging.log4j.core.config.ConfigurationSource;
+import org.apache.logging.log4j.core.config.builder.api.ConfigurationBuilder;
+import org.apache.logging.log4j.core.config.builder.impl.BuiltConfiguration;
+
+/**
+ * Experimental ConfigurationFactory for Log4j 1.2 properties configuration files.
+ */
+// TODO
+// @Plugin(name = "Log4j1ConfigurationFactory", category = ConfigurationFactory.CATEGORY)
+//
+// Best Value?
+// @Order(50)
+public class Log4j1ConfigurationFactory extends ConfigurationFactory {
+
+    private static final String[] SUFFIXES = {".properties"};
+
+    @Override
+    public Configuration getConfiguration(final LoggerContext loggerContext, final ConfigurationSource source) {
+        final ConfigurationBuilder<BuiltConfiguration> builder;
+        try (final InputStream configStream = source.getInputStream()) {
+            builder = new Log4j1ConfigurationParser().buildConfigurationBuilder(configStream);
+        } catch (final IOException e) {
+            throw new ConfigurationException("Unable to load " + source, e);
+        }
+        return builder.build();
+    }
+
+    @Override
+    protected String[] getSupportedTypes() {
+        return SUFFIXES;
+    }
+
+}
diff --git a/log4j-1.2-api/src/src/main/java/org/apache/log4j/config/Log4j1ConfigurationParser.java b/log4j-1.2-api/src/src/main/java/org/apache/log4j/config/Log4j1ConfigurationParser.java
new file mode 100644
index 0000000..112ab42
--- /dev/null
+++ b/log4j-1.2-api/src/src/main/java/org/apache/log4j/config/Log4j1ConfigurationParser.java
@@ -0,0 +1,446 @@
+/*
+ * 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 java.io.IOException;
+import java.io.InputStream;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Objects;
+import java.util.Properties;
+import java.util.TreeMap;
+
+import org.apache.logging.log4j.Level;
+import org.apache.logging.log4j.core.appender.ConsoleAppender;
+import org.apache.logging.log4j.core.appender.FileAppender;
+import org.apache.logging.log4j.core.appender.NullAppender;
+import org.apache.logging.log4j.core.appender.RollingFileAppender;
+import org.apache.logging.log4j.core.config.ConfigurationException;
+import org.apache.logging.log4j.core.config.builder.api.AppenderComponentBuilder;
+import org.apache.logging.log4j.core.config.builder.api.ComponentBuilder;
+import org.apache.logging.log4j.core.config.builder.api.ConfigurationBuilder;
+import org.apache.logging.log4j.core.config.builder.api.ConfigurationBuilderFactory;
+import org.apache.logging.log4j.core.config.builder.api.LayoutComponentBuilder;
+import org.apache.logging.log4j.core.config.builder.api.LoggerComponentBuilder;
+import org.apache.logging.log4j.core.config.builder.api.RootLoggerComponentBuilder;
+import org.apache.logging.log4j.core.config.builder.impl.BuiltConfiguration;
+import org.apache.logging.log4j.core.lookup.StrSubstitutor;
+import org.apache.logging.log4j.status.StatusLogger;
+import org.apache.logging.log4j.util.Strings;
+
+/**
+ * Experimental parser for Log4j 1.2 properties configuration files.
+ *
+ * This class is not thread-safe.
+ * 
+ * <p>
+ * From the Log4j 1.2 Javadocs:
+ * </p>
+ * <p>
+ * All option values admit variable substitution. The syntax of variable substitution is similar to that of Unix shells. The string between
+ * an opening "${" and closing "}" is interpreted as a key. The value of the substituted variable can be defined as a system property or in
+ * the configuration file itself. The value of the key is first searched in the system properties, and if not found there, it is then
+ * searched in the configuration file being parsed. The corresponding value replaces the ${variableName} sequence. For example, if java.home
+ * system property is set to /home/xyz, then every occurrence of the sequence ${java.home} will be interpreted as /home/xyz.
+ * </p>
+ */
+public class Log4j1ConfigurationParser {
+
+    private static final String COMMA_DELIMITED_RE = "\\s*,\\s*";
+    private static final String ROOTLOGGER = "rootLogger";
+    private static final String ROOTCATEGORY = "rootCategory";
+    private static final String TRUE = "true";
+    private static final String FALSE = "false";
+
+    private final Properties properties = new Properties();
+    private StrSubstitutor strSubstitutorProperties;
+    private StrSubstitutor strSubstitutorSystem;
+
+    private final ConfigurationBuilder<BuiltConfiguration> builder = ConfigurationBuilderFactory
+            .newConfigurationBuilder();
+
+    /**
+     * Parses a Log4j 1.2 properties configuration file in ISO 8859-1 encoding into a ConfigurationBuilder.
+     *
+     * @param input
+     *            InputStream to read from is assumed to be ISO 8859-1, and will not be closed.
+     * @return the populated ConfigurationBuilder, never {@literal null}
+     * @throws IOException
+     *             if unable to read the input
+     * @throws ConfigurationException
+     *             if the input does not contain a valid configuration
+     */
+    public ConfigurationBuilder<BuiltConfiguration> buildConfigurationBuilder(final InputStream input)
+            throws IOException {
+        try {
+            properties.load(input);
+            strSubstitutorProperties = new StrSubstitutor(properties);
+            strSubstitutorSystem = new StrSubstitutor(System.getProperties());
+            final String rootCategoryValue = getLog4jValue(ROOTCATEGORY);
+            final String rootLoggerValue = getLog4jValue(ROOTLOGGER);
+            if (rootCategoryValue == null && rootLoggerValue == null) {
+                // This is not a Log4j 1 properties configuration file.
+                warn("Missing " + ROOTCATEGORY + " or " + ROOTLOGGER + " in " + input);
+                // throw new ConfigurationException(
+                // "Missing " + ROOTCATEGORY + " or " + ROOTLOGGER + " in " + input);
+            }
+            builder.setConfigurationName("Log4j1");
+            // DEBUG
+            final String debugValue = getLog4jValue("debug");
+            if (Boolean.valueOf(debugValue)) {
+                builder.setStatusLevel(Level.DEBUG);
+            }
+            // Root
+            buildRootLogger(getLog4jValue(ROOTCATEGORY));
+            buildRootLogger(getLog4jValue(ROOTLOGGER));
+            // Appenders
+            final Map<String, String> appenderNameToClassName = buildClassToPropertyPrefixMap();
+            for (final Map.Entry<String, String> entry : appenderNameToClassName.entrySet()) {
+                final String appenderName = entry.getKey();
+                final String appenderClass = entry.getValue();
+                buildAppender(appenderName, appenderClass);
+            }
+            // Loggers
+            buildLoggers("log4j.category.");
+            buildLoggers("log4j.logger.");
+            buildProperties();
+            return builder;
+        } catch (final IllegalArgumentException e) {
+            throw new ConfigurationException(e);
+        }
+    }
+
+    private void buildProperties() {
+        for (final Map.Entry<Object, Object> entry : new TreeMap<>(properties).entrySet()) {
+            final String key = entry.getKey().toString();
+            if (!key.startsWith("log4j.") && !key.equals(ROOTCATEGORY) && !key.equals(ROOTLOGGER)) {
+                builder.addProperty(key, Objects.toString(entry.getValue(), Strings.EMPTY));
+            }
+        }
+    }
+
+    private void warn(final String string) {
+        System.err.println(string);
+    }
+
+    private Map<String, String> buildClassToPropertyPrefixMap() {
+        final String prefix = "log4j.appender.";
+        final int preLength = prefix.length();
+        final Map<String, String> map = new HashMap<>();
+        for (final Map.Entry<Object, Object> entry : properties.entrySet()) {
+            final Object keyObj = entry.getKey();
+            if (keyObj != null) {
+                final String key = keyObj.toString();
+                if (key.startsWith(prefix)) {
+                    if (key.indexOf('.', preLength) < 0) {
+                        final String name = key.substring(preLength);
+                        final Object value = entry.getValue();
+                        if (value != null) {
+                            map.put(name, value.toString());
+                        }
+                    }
+                }
+            }
+        }
+        return map;
+    }
+
+    private void buildAppender(final String appenderName, final String appenderClass) {
+        switch (appenderClass) {
+        case "org.apache.log4j.ConsoleAppender":
+            buildConsoleAppender(appenderName);
+            break;
+        case "org.apache.log4j.FileAppender":
+            buildFileAppender(appenderName);
+            break;
+        case "org.apache.log4j.DailyRollingFileAppender":
+            buildDailyRollingFileAppender(appenderName);
+            break;
+        case "org.apache.log4j.RollingFileAppender":
+            buildRollingFileAppender(appenderName);
+            break;
+        case "org.apache.log4j.varia.NullAppender":
+            buildNullAppender(appenderName);
+            break;
+        default:
+            reportWarning("Unknown appender class: " + appenderClass + "; ignoring appender: " + appenderName);
+        }
+    }
+
+    private void buildConsoleAppender(final String appenderName) {
+        final AppenderComponentBuilder appenderBuilder = builder.newAppender(appenderName, ConsoleAppender.PLUGIN_NAME);
+        final String targetValue = getLog4jAppenderValue(appenderName, "Target", "System.out");
+        if (targetValue != null) {
+            final ConsoleAppender.Target target;
+            switch (targetValue) {
+            case "System.out":
+                target = ConsoleAppender.Target.SYSTEM_OUT;
+                break;
+            case "System.err":
+                target = ConsoleAppender.Target.SYSTEM_ERR;
+                break;
+            default:
+                reportWarning("Unknown value for console Target: " + targetValue);
+                target = null;
+            }
+            if (target != null) {
+                appenderBuilder.addAttribute("target", target);
+            }
+        }
+        buildAttribute(appenderName, appenderBuilder, "Follow", "follow");
+        if (FALSE.equalsIgnoreCase(getLog4jAppenderValue(appenderName, "ImmediateFlush"))) {
+            reportWarning("ImmediateFlush=false is not supported on Console appender");
+        }
+        buildAppenderLayout(appenderName, appenderBuilder);
+        builder.add(appenderBuilder);
+    }
+
+    private void buildFileAppender(final String appenderName) {
+        final AppenderComponentBuilder appenderBuilder = builder.newAppender(appenderName, FileAppender.PLUGIN_NAME);
+        buildFileAppender(appenderName, appenderBuilder);
+        builder.add(appenderBuilder);
+    }
+
+    private void buildFileAppender(final String appenderName, final AppenderComponentBuilder appenderBuilder) {
+        buildMandatoryAttribute(appenderName, appenderBuilder, "File", "fileName");
+        buildAttribute(appenderName, appenderBuilder, "Append", "append");
+        buildAttribute(appenderName, appenderBuilder, "BufferedIO", "bufferedIo");
+        buildAttribute(appenderName, appenderBuilder, "BufferSize", "bufferSize");
+        buildAttribute(appenderName, appenderBuilder, "ImmediateFlush", "immediateFlush");
+        buildAppenderLayout(appenderName, appenderBuilder);
+    }
+
+    private void buildDailyRollingFileAppender(final String appenderName) {
+        final AppenderComponentBuilder appenderBuilder = builder.newAppender(appenderName,
+                RollingFileAppender.PLUGIN_NAME);
+        buildFileAppender(appenderName, appenderBuilder);
+        final String fileName = getLog4jAppenderValue(appenderName, "File");
+        final String datePattern = getLog4jAppenderValue(appenderName, "DatePattern", fileName + "'.'yyyy-MM-dd");
+        appenderBuilder.addAttribute("filePattern", fileName + "%d{" + datePattern + "}");
+        final ComponentBuilder<?> triggeringPolicy = builder.newComponent("Policies")
+                .addComponent(builder.newComponent("TimeBasedTriggeringPolicy").addAttribute("modulate", true));
+        appenderBuilder.addComponent(triggeringPolicy);
+        appenderBuilder
+                .addComponent(builder.newComponent("DefaultRolloverStrategy").addAttribute("max", Integer.MAX_VALUE));
+        builder.add(appenderBuilder);
+    }
+
+    private void buildRollingFileAppender(final String appenderName) {
+        final AppenderComponentBuilder appenderBuilder = builder.newAppender(appenderName,
+                RollingFileAppender.PLUGIN_NAME);
+        buildFileAppender(appenderName, appenderBuilder);
+        final String fileName = getLog4jAppenderValue(appenderName, "File");
+        appenderBuilder.addAttribute("filePattern", fileName + ".%i");
+        final String maxFileSizeString = getLog4jAppenderValue(appenderName, "MaxFileSize", "10485760");
+        final String maxBackupIndexString = getLog4jAppenderValue(appenderName, "MaxBackupIndex", "1");
+        final ComponentBuilder<?> triggeringPolicy = builder.newComponent("Policies").addComponent(
+                builder.newComponent("SizeBasedTriggeringPolicy").addAttribute("size", maxFileSizeString));
+        appenderBuilder.addComponent(triggeringPolicy);
+        appenderBuilder.addComponent(
+                builder.newComponent("DefaultRolloverStrategy").addAttribute("max", maxBackupIndexString));
+        builder.add(appenderBuilder);
+    }
+
+    private void buildAttribute(final String componentName, final ComponentBuilder componentBuilder,
+            final String sourceAttributeName, final String targetAttributeName) {
+        final String attributeValue = getLog4jAppenderValue(componentName, sourceAttributeName);
+        if (attributeValue != null) {
+            componentBuilder.addAttribute(targetAttributeName, attributeValue);
+        }
+    }
+
+    private void buildAttributeWithDefault(final String componentName, final ComponentBuilder componentBuilder,
+            final String sourceAttributeName, final String targetAttributeName, final String defaultValue) {
+        final String attributeValue = getLog4jAppenderValue(componentName, sourceAttributeName, defaultValue);
+        componentBuilder.addAttribute(targetAttributeName, attributeValue);
+    }
+
+    private void buildMandatoryAttribute(final String componentName, final ComponentBuilder componentBuilder,
+            final String sourceAttributeName, final String targetAttributeName) {
+        final String attributeValue = getLog4jAppenderValue(componentName, sourceAttributeName);
+        if (attributeValue != null) {
+            componentBuilder.addAttribute(targetAttributeName, attributeValue);
+        } else {
+            reportWarning("Missing " + sourceAttributeName + " for " + componentName);
+        }
+    }
+
+    private void buildNullAppender(final String appenderName) {
+        final AppenderComponentBuilder appenderBuilder = builder.newAppender(appenderName, NullAppender.PLUGIN_NAME);
+        builder.add(appenderBuilder);
+    }
+
+    private void buildAppenderLayout(final String name, final AppenderComponentBuilder appenderBuilder) {
+        final String layoutClass = getLog4jAppenderValue(name, "layout", null);
+        if (layoutClass != null) {
+            switch (layoutClass) {
+            case "org.apache.log4j.PatternLayout":
+            case "org.apache.log4j.EnhancedPatternLayout": {
+                final String pattern = getLog4jAppenderValue(name, "layout.ConversionPattern", null)
+
+                        // Log4j 2's %x (NDC) is not compatible with Log4j 1's
+                        // %x
+                        // Log4j 1: "foo bar baz"
+                        // Log4j 2: "[foo, bar, baz]"
+                        // Use %ndc to get the Log4j 1 format
+                        .replace("%x", "%ndc")
+
+                        // Log4j 2's %X (MDC) is not compatible with Log4j 1's
+                        // %X
+                        // Log4j 1: "{{foo,bar}{hoo,boo}}"
+                        // Log4j 2: "{foo=bar,hoo=boo}"
+                        // Use %properties to get the Log4j 1 format
+                        .replace("%X", "%properties");
+
+                appenderBuilder.add(newPatternLayout(pattern));
+                break;
+            }
+            case "org.apache.log4j.SimpleLayout": {
+                appenderBuilder.add(newPatternLayout("%level - %m%n"));
+                break;
+            }
+            case "org.apache.log4j.TTCCLayout": {
+                String pattern = "%r ";
+                if (Boolean.parseBoolean(getLog4jAppenderValue(name, "layout.ThreadPrinting", TRUE))) {
+                    pattern += "[%t] ";
+                }
+                pattern += "%p ";
+                if (Boolean.parseBoolean(getLog4jAppenderValue(name, "layout.CategoryPrefixing", TRUE))) {
+                    pattern += "%c ";
+                }
+                if (Boolean.parseBoolean(getLog4jAppenderValue(name, "layout.ContextPrinting", TRUE))) {
+                    pattern += "%notEmpty{%ndc }";
+                }
+                pattern += "- %m%n";
+                appenderBuilder.add(newPatternLayout(pattern));
+                break;
+            }
+            case "org.apache.log4j.HTMLLayout": {
+                final LayoutComponentBuilder htmlLayout = builder.newLayout("HtmlLayout");
+                htmlLayout.addAttribute("title", getLog4jAppenderValue(name, "layout.Title", "Log4J Log Messages"));
+                htmlLayout.addAttribute("locationInfo",
+                        Boolean.parseBoolean(getLog4jAppenderValue(name, "layout.LocationInfo", FALSE)));
+                appenderBuilder.add(htmlLayout);
+                break;
+            }
+            case "org.apache.log4j.xml.XMLLayout": {
+                final LayoutComponentBuilder xmlLayout = builder.newLayout("Log4j1XmlLayout");
+                xmlLayout.addAttribute("locationInfo",
+                        Boolean.parseBoolean(getLog4jAppenderValue(name, "layout.LocationInfo", FALSE)));
+                xmlLayout.addAttribute("properties",
+                        Boolean.parseBoolean(getLog4jAppenderValue(name, "layout.Properties", FALSE)));
+                appenderBuilder.add(xmlLayout);
+                break;
+            }
+            default:
+                reportWarning("Unknown layout class: " + layoutClass);
+            }
+        }
+    }
+
+    private LayoutComponentBuilder newPatternLayout(final String pattern) {
+        final LayoutComponentBuilder layoutBuilder = builder.newLayout("PatternLayout");
+        if (pattern != null) {
+            layoutBuilder.addAttribute("pattern", pattern);
+        }
+        return layoutBuilder;
+    }
+
+    private void buildRootLogger(final String rootLoggerValue) {
+        if (rootLoggerValue == null) {
+            return;
+        }
+        final String[] rootLoggerParts = rootLoggerValue.split(COMMA_DELIMITED_RE);
+        final String rootLoggerLevel = getLevelString(rootLoggerParts, Level.ERROR.name());
+        final RootLoggerComponentBuilder loggerBuilder = builder.newRootLogger(rootLoggerLevel);
+        //
+        final String[] sortedAppenderNames = Arrays.copyOfRange(rootLoggerParts, 1, rootLoggerParts.length);
+        Arrays.sort(sortedAppenderNames);
+        for (final String appender : sortedAppenderNames) {
+            loggerBuilder.add(builder.newAppenderRef(appender));
+        }
+        builder.add(loggerBuilder);
+    }
+
+    private String getLevelString(final String[] loggerParts, final String defaultLevel) {
+        return loggerParts.length > 0 ? loggerParts[0] : defaultLevel;
+    }
+
+    private void buildLoggers(final String prefix) {
+        final int preLength = prefix.length();
+        for (final Map.Entry<Object, Object> entry : properties.entrySet()) {
+            final Object keyObj = entry.getKey();
+            if (keyObj != null) {
+                final String key = keyObj.toString();
+                if (key.startsWith(prefix)) {
+                    final String name = key.substring(preLength);
+                    final Object value = entry.getValue();
+                    if (value != null) {
+                        // a Level may be followed by a list of Appender refs.
+                        final String valueStr = value.toString();
+                        final String[] split = valueStr.split(COMMA_DELIMITED_RE);
+                        final String level = getLevelString(split, null);
+                        if (level == null) {
+                            warn("Level is missing for entry " + entry);
+                        } else {
+                            final LoggerComponentBuilder newLogger = builder.newLogger(name, level);
+                            if (split.length > 1) {
+                                // Add Appenders to this logger
+                                final String[] sortedAppenderNames = Arrays.copyOfRange(split, 1, split.length);
+                                Arrays.sort(sortedAppenderNames);
+                                for (final String appenderName : sortedAppenderNames) {
+                                    newLogger.add(builder.newAppenderRef(appenderName));
+                                }
+                            }
+                            builder.add(newLogger);
+                        }
+                    }
+                }
+            }
+        }
+    }
+
+    private String getLog4jAppenderValue(final String appenderName, final String attributeName) {
+        return getProperty("log4j.appender." + appenderName + "." + attributeName);
+    }
+
+    private String getProperty(final String key) {
+        final String value = properties.getProperty(key);
+        final String sysValue = strSubstitutorSystem.replace(value);
+        return strSubstitutorProperties.replace(sysValue);
+    }
+
+    private String getProperty(final String key, final String defaultValue) {
+        final String value = getProperty(key);
+        return value == null ? defaultValue : value;
+    }
+
+    private String getLog4jAppenderValue(final String appenderName, final String attributeName,
+            final String defaultValue) {
+        return getProperty("log4j.appender." + appenderName + "." + attributeName, defaultValue);
+    }
+
+    private String getLog4jValue(final String key) {
+        return getProperty("log4j." + key);
+    }
+
+    private void reportWarning(final String msg) {
+        StatusLogger.getLogger().warn("Log4j 1 configuration parser: " + msg);
+    }
+
+}
diff --git a/log4j-1.2-api/src/src/main/java/org/apache/log4j/config/PropertySetter.java b/log4j-1.2-api/src/src/main/java/org/apache/log4j/config/PropertySetter.java
new file mode 100644
index 0000000..5982278
--- /dev/null
+++ b/log4j-1.2-api/src/src/main/java/org/apache/log4j/config/PropertySetter.java
@@ -0,0 +1,287 @@
+/*
+ * 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.
+ */
+
+// Contributors:  Georg Lundesgaard
+
+package org.apache.log4j.config;
+
+import org.apache.log4j.Appender;
+import org.apache.log4j.Level;
+import org.apache.log4j.Priority;
+import org.apache.log4j.spi.ErrorHandler;
+import org.apache.log4j.spi.OptionHandler;
+import org.apache.logging.log4j.Logger;
+import org.apache.logging.log4j.core.util.OptionConverter;
+import org.apache.logging.log4j.status.StatusLogger;
+
+import java.beans.BeanInfo;
+import java.beans.IntrospectionException;
+import java.beans.Introspector;
+import java.beans.PropertyDescriptor;
+import java.io.InterruptedIOException;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.util.Properties;
+
+/**
+ * General purpose Object property setter. Clients repeatedly invokes
+ * {@link #setProperty setProperty(name,value)} in order to invoke setters
+ * on the Object specified in the constructor. This class relies on the
+ * JavaBeans {@link Introspector} to analyze the given Object Class using
+ * reflection.
+ *
+ * <p>Usage:
+ * <pre>
+ * PropertySetter ps = new PropertySetter(anObject);
+ * ps.set("name", "Joe");
+ * ps.set("age", "32");
+ * ps.set("isMale", "true");
+ * </pre>
+ * will cause the invocations anObject.setName("Joe"), anObject.setAge(32),
+ * and setMale(true) if such methods exist with those signatures.
+ * Otherwise an {@link IntrospectionException} are thrown.
+ */
+public class PropertySetter {
+    private static Logger LOGGER = StatusLogger.getLogger();
+    protected Object obj;
+    protected PropertyDescriptor[] props;
+
+    /**
+     * Create a new PropertySetter for the specified Object. This is done
+     * in prepartion for invoking {@link #setProperty} one or more times.
+     *
+     * @param obj the object for which to set properties
+     */
+    public PropertySetter(Object obj) {
+        this.obj = obj;
+    }
+
+    /**
+     * Set the properties of an object passed as a parameter in one
+     * go. The <code>properties</code> are parsed relative to a
+     * <code>prefix</code>.
+     *
+     * @param obj        The object to configure.
+     * @param properties A java.util.Properties containing keys and values.
+     * @param prefix     Only keys having the specified prefix will be set.
+     */
+    public static void setProperties(Object obj, Properties properties, String prefix) {
+        new PropertySetter(obj).setProperties(properties, prefix);
+    }
+
+    /**
+     * Uses JavaBeans {@link Introspector} to computer setters of object to be
+     * configured.
+     */
+    protected void introspect() {
+        try {
+            BeanInfo bi = Introspector.getBeanInfo(obj.getClass());
+            props = bi.getPropertyDescriptors();
+        } catch (IntrospectionException ex) {
+            LOGGER.error("Failed to introspect {}: {}", obj, ex.getMessage());
+            props = new PropertyDescriptor[0];
+        }
+    }
+
+    /**
+     * Set the properites for the object that match the
+     * <code>prefix</code> passed as parameter.
+     */
+    public void setProperties(Properties properties, String prefix) {
+        int len = prefix.length();
+
+        for (String key : properties.stringPropertyNames()) {
+
+            // handle only properties that start with the desired frefix.
+            if (key.startsWith(prefix)) {
+
+
+                // ignore key if it contains dots after the prefix
+                if (key.indexOf('.', len + 1) > 0) {
+                    continue;
+                }
+
+                String value = OptionConverter.findAndSubst(key, properties);
+                key = key.substring(len);
+                if (("layout".equals(key) || "errorhandler".equals(key)) && obj instanceof Appender) {
+                    continue;
+                }
+                //
+                //   if the property type is an OptionHandler
+                //     (for example, triggeringPolicy of org.apache.log4j.rolling.RollingFileAppender)
+                PropertyDescriptor prop = getPropertyDescriptor(Introspector.decapitalize(key));
+                if (prop != null
+                        && OptionHandler.class.isAssignableFrom(prop.getPropertyType())
+                        && prop.getWriteMethod() != null) {
+                    OptionHandler opt = (OptionHandler)
+                            OptionConverter.instantiateByKey(properties, prefix + key,
+                                    prop.getPropertyType(),
+                                    null);
+                    PropertySetter setter = new PropertySetter(opt);
+                    setter.setProperties(properties, prefix + key + ".");
+                    try {
+                        prop.getWriteMethod().invoke(this.obj, opt);
+                    } catch (InvocationTargetException ex) {
+                        if (ex.getTargetException() instanceof InterruptedException
+                                || ex.getTargetException() instanceof InterruptedIOException) {
+                            Thread.currentThread().interrupt();
+                        }
+                        LOGGER.warn("Failed to set property [{}] to value \"{}\".", key, value, ex);
+                    } catch (IllegalAccessException | RuntimeException ex) {
+                        LOGGER.warn("Failed to set property [{}] to value \"{}\".", key, value, ex);
+                    }
+                    continue;
+                }
+
+                setProperty(key, value);
+            }
+        }
+        activate();
+    }
+
+    /**
+     * Set a property on this PropertySetter's Object. If successful, this
+     * method will invoke a setter method on the underlying Object. The
+     * setter is the one for the specified property name and the value is
+     * determined partly from the setter argument type and partly from the
+     * value specified in the call to this method.
+     *
+     * <p>If the setter expects a String no conversion is necessary.
+     * If it expects an int, then an attempt is made to convert 'value'
+     * to an int using new Integer(value). If the setter expects a boolean,
+     * the conversion is by new Boolean(value).
+     *
+     * @param name  name of the property
+     * @param value String value of the property
+     */
+    public void setProperty(String name, String value) {
+        if (value == null) {
+            return;
+        }
+
+        name = Introspector.decapitalize(name);
+        PropertyDescriptor prop = getPropertyDescriptor(name);
+
+        //LOGGER.debug("---------Key: "+name+", type="+prop.getPropertyType());
+
+        if (prop == null) {
+            LOGGER.warn("No such property [" + name + "] in " +
+                    obj.getClass().getName() + ".");
+        } else {
+            try {
+                setProperty(prop, name, value);
+            } catch (PropertySetterException ex) {
+                LOGGER.warn("Failed to set property [{}] to value \"{}\".", name, value, ex.rootCause);
+            }
+        }
+    }
+
+    /**
+     * Set the named property given a {@link PropertyDescriptor}.
+     *
+     * @param prop  A PropertyDescriptor describing the characteristics
+     *              of the property to set.
+     * @param name  The named of the property to set.
+     * @param value The value of the property.
+     */
+    public void setProperty(PropertyDescriptor prop, String name, String value)
+            throws PropertySetterException {
+        Method setter = prop.getWriteMethod();
+        if (setter == null) {
+            throw new PropertySetterException("No setter for property [" + name + "].");
+        }
+        Class[] paramTypes = setter.getParameterTypes();
+        if (paramTypes.length != 1) {
+            throw new PropertySetterException("#params for setter != 1");
+        }
+
+        Object arg;
+        try {
+            arg = convertArg(value, paramTypes[0]);
+        } catch (Throwable t) {
+            throw new PropertySetterException("Conversion to type [" + paramTypes[0] +
+                    "] failed. Reason: " + t);
+        }
+        if (arg == null) {
+            throw new PropertySetterException(
+                    "Conversion to type [" + paramTypes[0] + "] failed.");
+        }
+        LOGGER.debug("Setting property [" + name + "] to [" + arg + "].");
+        try {
+            setter.invoke(obj, arg);
+        } catch (InvocationTargetException ex) {
+            if (ex.getTargetException() instanceof InterruptedException
+                    || ex.getTargetException() instanceof InterruptedIOException) {
+                Thread.currentThread().interrupt();
+            }
+            throw new PropertySetterException(ex);
+        } catch (IllegalAccessException | RuntimeException ex) {
+            throw new PropertySetterException(ex);
+        }
+    }
+
+
+    /**
+     * Convert <code>val</code> a String parameter to an object of a
+     * given type.
+     */
+    protected Object convertArg(String val, Class type) {
+        if (val == null) {
+            return null;
+        }
+
+        String v = val.trim();
+        if (String.class.isAssignableFrom(type)) {
+            return val;
+        } else if (Integer.TYPE.isAssignableFrom(type)) {
+            return Integer.parseInt(v);
+        } else if (Long.TYPE.isAssignableFrom(type)) {
+            return Long.parseLong(v);
+        } else if (Boolean.TYPE.isAssignableFrom(type)) {
+            if ("true".equalsIgnoreCase(v)) {
+                return Boolean.TRUE;
+            } else if ("false".equalsIgnoreCase(v)) {
+                return Boolean.FALSE;
+            }
+        } else if (Priority.class.isAssignableFrom(type)) {
+            return org.apache.log4j.helpers.OptionConverter.toLevel(v, Level.DEBUG);
+        } else if (ErrorHandler.class.isAssignableFrom(type)) {
+            return OptionConverter.instantiateByClassName(v,
+                    ErrorHandler.class, null);
+        }
+        return null;
+    }
+
+
+    protected PropertyDescriptor getPropertyDescriptor(String name) {
+        if (props == null) {
+            introspect();
+        }
+        for (PropertyDescriptor prop : props) {
+            if (name.equals(prop.getName())) {
+                return prop;
+            }
+        }
+        return null;
+    }
+
+    public void activate() {
+        if (obj instanceof OptionHandler) {
+            ((OptionHandler) obj).activateOptions();
+        }
+    }
+}
diff --git a/log4j-1.2-api/src/src/main/java/org/apache/log4j/config/PropertySetterException.java b/log4j-1.2-api/src/src/main/java/org/apache/log4j/config/PropertySetterException.java
new file mode 100644
index 0000000..c9dc4cf
--- /dev/null
+++ b/log4j-1.2-api/src/src/main/java/org/apache/log4j/config/PropertySetterException.java
@@ -0,0 +1,65 @@
+/*
+ * 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;
+
+/**
+ * Thrown when an error is encountered whilst attempting to set a property
+ * using the {@link PropertySetter} utility class.
+ *
+ * @since 1.1
+ */
+public class PropertySetterException extends Exception {
+    private static final long serialVersionUID = -1352613734254235861L;
+
+    /**
+     * The root cause.
+     */
+    protected Throwable rootCause;
+
+    /**
+     * Construct the exception with the given message.
+     *
+     * @param msg The message
+     */
+    public PropertySetterException(final String msg) {
+        super(msg);
+    }
+
+    /**
+     * Construct the exception with the given root cause.
+     *
+     * @param rootCause The root cause
+     */
+    public PropertySetterException(final Throwable rootCause) {
+        super();
+        this.rootCause = rootCause;
+    }
+
+    /**
+     * Returns descriptive text on the cause of this exception.
+     *
+     * @return the descriptive text.
+     */
+    @Override
+    public String getMessage() {
+        String msg = super.getMessage();
+        if (msg == null && rootCause != null) {
+            msg = rootCause.getMessage();
+        }
+        return msg;
+    }
+}
diff --git a/log4j-1.2-api/src/src/main/java/org/apache/log4j/config/package-info.java b/log4j-1.2-api/src/src/main/java/org/apache/log4j/config/package-info.java
new file mode 100644
index 0000000..7f96630
--- /dev/null
+++ b/log4j-1.2-api/src/src/main/java/org/apache/log4j/config/package-info.java
@@ -0,0 +1,20 @@
+/*
+ * 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 1.x compatibility layer.
+ */
+package org.apache.log4j.config;
diff --git a/log4j-1.2-api/src/src/main/java/org/apache/log4j/helpers/NullEnumeration.java b/log4j-1.2-api/src/src/main/java/org/apache/log4j/helpers/NullEnumeration.java
new file mode 100644
index 0000000..d064004
--- /dev/null
+++ b/log4j-1.2-api/src/src/main/java/org/apache/log4j/helpers/NullEnumeration.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.helpers;
+
+import java.util.Enumeration;
+import java.util.NoSuchElementException;
+
+/**
+ * An always-empty Enumerator.
+ *
+ * @since version 1.0
+ */
+@SuppressWarnings("rawtypes")
+public final class NullEnumeration implements Enumeration {
+    private static final NullEnumeration INSTANCE = new NullEnumeration();
+
+    private NullEnumeration() {
+    }
+
+    public static NullEnumeration getInstance() {
+        return INSTANCE;
+    }
+
+    @Override
+    public boolean hasMoreElements() {
+        return false;
+    }
+
+    @Override
+    public Object nextElement() {
+        throw new NoSuchElementException();
+    }
+}
diff --git a/log4j-1.2-api/src/src/main/java/org/apache/log4j/helpers/OptionConverter.java b/log4j-1.2-api/src/src/main/java/org/apache/log4j/helpers/OptionConverter.java
new file mode 100644
index 0000000..8ee0ea5
--- /dev/null
+++ b/log4j-1.2-api/src/src/main/java/org/apache/log4j/helpers/OptionConverter.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.helpers;
+
+import org.apache.log4j.Level;
+
+import org.apache.logging.log4j.LogManager;
+import org.apache.logging.log4j.Logger;
+import org.apache.logging.log4j.util.LoaderUtil;
+
+import java.io.InterruptedIOException;
+import java.lang.reflect.InvocationTargetException;
+import java.util.Properties;
+
+/**
+ * A convenience class to convert property values to specific types.
+ */
+public class OptionConverter {
+    
+    static String DELIM_START = "${";
+    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 CharMap[] charMap = new CharMap[] {
+        new CharMap('n', '\n'),
+        new CharMap('r', '\r'),
+        new CharMap('t', '\t'),
+        new CharMap('f', '\f'),
+        new CharMap('\b', '\b'),
+        new CharMap('\"', '\"'),
+        new CharMap('\'', '\''),
+        new CharMap('\\', '\\')    
+    };
+
+    /**
+     * OptionConverter is a static class.
+     */
+    private OptionConverter() {
+    }
+
+    public static String[] concatanateArrays(String[] l, String[] r) {
+        int len = l.length + r.length;
+        String[] a = new String[len];
+
+        System.arraycopy(l, 0, a, 0, l.length);
+        System.arraycopy(r, 0, a, l.length, r.length);
+
+        return a;
+    }
+
+    public static String convertSpecialChars(String s) {
+        char c;
+        int len = s.length();
+        StringBuilder sbuf = new StringBuilder(len);
+
+        int i = 0;
+        while (i < len) {
+            c = s.charAt(i++);
+            if (c == '\\') {
+                c = s.charAt(i++);
+                for (CharMap entry : charMap) {
+                    if (entry.key == c) {
+                        c = entry.replacement;
+                    }
+                }
+            }
+            sbuf.append(c);
+        }
+        return sbuf.toString();
+    }
+
+
+    /**
+     * Very similar to <code>System.getProperty</code> except
+     * that the {@link SecurityException} is hidden.
+     *
+     * @param key The key to search for.
+     * @param def The default value to return.
+     * @return the string value of the system property, or the default
+     * value if there is no property with that key.
+     * @since 1.1
+     */
+    public static String getSystemProperty(String key, String def) {
+        try {
+            return System.getProperty(key, def);
+        } catch (Throwable e) { // MS-Java throws com.ms.security.SecurityExceptionEx
+            LOGGER.debug("Was not allowed to read system property \"{}\".", key);
+            return def;
+        }
+    }
+
+    /**
+     * If <code>value</code> is "true", then <code>true</code> is
+     * returned. If <code>value</code> is "false", then
+     * <code>true</code> is returned. Otherwise, <code>default</code> is
+     * returned.
+     *
+     * <p>Case of value is unimportant.
+     */
+    public static boolean toBoolean(String value, boolean dEfault) {
+        if (value == null) {
+            return dEfault;
+        }
+        String trimmedVal = value.trim();
+        if ("true".equalsIgnoreCase(trimmedVal)) {
+            return true;
+        }
+        if ("false".equalsIgnoreCase(trimmedVal)) {
+            return false;
+        }
+        return dEfault;
+    }
+
+    /**
+     * Converts a standard or custom priority level to a Level
+     * object.  <p> If <code>value</code> is of form
+     * "level#classname", then the specified class' toLevel method
+     * is called to process the specified level string; if no '#'
+     * character is present, then the default {@link org.apache.log4j.Level}
+     * class is used to process the level value.
+     *
+     * <p>As a special case, if the <code>value</code> parameter is
+     * equal to the string "NULL", then the value <code>null</code> will
+     * be returned.
+     *
+     * <p> If any error occurs while converting the value to a level,
+     * the <code>defaultValue</code> parameter, which may be
+     * <code>null</code>, is returned.
+     *
+     * <p> Case of <code>value</code> is insignificant for the level level, but is
+     * significant for the class name part, if present.
+     *
+     * @since 1.1
+     */
+    public static Level toLevel(String value, Level defaultValue) {
+        if (value == null) {
+            return defaultValue;
+        }
+
+        value = value.trim();
+
+        int hashIndex = value.indexOf('#');
+        if (hashIndex == -1) {
+            if ("NULL".equalsIgnoreCase(value)) {
+                return null;
+            } else {
+                // no class name specified : use standard Level class
+                return Level.toLevel(value, defaultValue);
+            }
+        }
+
+        Level result = defaultValue;
+
+        String clazz = value.substring(hashIndex + 1);
+        String levelName = value.substring(0, hashIndex);
+
+        // This is degenerate case but you never know.
+        if ("NULL".equalsIgnoreCase(levelName)) {
+            return null;
+        }
+
+        LOGGER.debug("toLevel" + ":class=[" + clazz + "]"
+                + ":pri=[" + levelName + "]");
+
+        try {
+            Class customLevel = LoaderUtil.loadClass(clazz);
+
+            // get a ref to the specified class' static method
+            // toLevel(String, org.apache.log4j.Level)
+            Class[] paramTypes = new Class[]{String.class,
+                    org.apache.log4j.Level.class
+            };
+            java.lang.reflect.Method toLevelMethod =
+                    customLevel.getMethod("toLevel", paramTypes);
+
+            // now call the toLevel method, passing level string + default
+            Object[] params = new Object[]{levelName, defaultValue};
+            Object o = toLevelMethod.invoke(null, params);
+
+            result = (Level) o;
+        } catch (ClassNotFoundException e) {
+            LOGGER.warn("custom level class [" + clazz + "] not found.");
+        } catch (NoSuchMethodException e) {
+            LOGGER.warn("custom level class [" + clazz + "]"
+                    + " does not have a class function toLevel(String, Level)", e);
+        } catch (java.lang.reflect.InvocationTargetException e) {
+            if (e.getTargetException() instanceof InterruptedException
+                    || e.getTargetException() instanceof InterruptedIOException) {
+                Thread.currentThread().interrupt();
+            }
+            LOGGER.warn("custom level class [" + clazz + "]"
+                    + " could not be instantiated", e);
+        } catch (ClassCastException e) {
+            LOGGER.warn("class [" + clazz
+                    + "] is not a subclass of org.apache.log4j.Level", e);
+        } catch (IllegalAccessException e) {
+            LOGGER.warn("class [" + clazz +
+                    "] cannot be instantiated due to access restrictions", e);
+        } catch (RuntimeException e) {
+            LOGGER.warn("class [" + clazz + "], level [" + levelName +
+                    "] conversion failed.", e);
+        }
+        return result;
+    }
+
+    /**
+     * Instantiate an object given a class name. Check that the
+     * <code>className</code> is a subclass of
+     * <code>superClass</code>. If that test fails or the object could
+     * not be instantiated, then <code>defaultValue</code> is returned.
+     *
+     * @param className    The fully qualified class name of the object to instantiate.
+     * @param superClass   The class to which the new object should belong.
+     * @param defaultValue The object to return in case of non-fulfillment
+     */
+    public static Object instantiateByClassName(String className, Class<?> superClass,
+            Object defaultValue) {
+        if (className != null) {
+            try {
+                Object obj = LoaderUtil.newInstanceOf(className);
+                if (!superClass.isAssignableFrom(obj.getClass())) {
+                    LOGGER.error("A \"{}\" object is not assignable to a \"{}\" variable", className,
+                            superClass.getName());
+                    return defaultValue;
+                }
+                return obj;
+            } catch (ClassNotFoundException | NoSuchMethodException | IllegalAccessException
+                    | InstantiationException | InvocationTargetException e) {
+                LOGGER.error("Could not instantiate class [" + className + "].", e);
+            }
+        }
+        return defaultValue;
+    }
+
+
+    /**
+     * Perform variable substitution in string <code>val</code> from the
+     * values of keys found in the system propeties.
+     *
+     * <p>The variable substitution delimeters are <b>${</b> and <b>}</b>.
+     *
+     * <p>For example, if the System properties contains "key=value", then
+     * the call
+     * <pre>
+     * String s = OptionConverter.substituteVars("Value of key is ${key}.");
+     * </pre>
+     * <p>
+     * will set the variable <code>s</code> to "Value of key is value.".
+     *
+     * <p>If no value could be found for the specified key, then the
+     * <code>props</code> parameter is searched, if the value could not
+     * be found there, then substitution defaults to the empty string.
+     *
+     * <p>For example, if system propeties contains no value for the key
+     * "inexistentKey", then the call
+     *
+     * <pre>
+     * String s = OptionConverter.subsVars("Value of inexistentKey is [${inexistentKey}]");
+     * </pre>
+     * will set <code>s</code> to "Value of inexistentKey is []"
+     *
+     * <p>An {@link IllegalArgumentException} is thrown if
+     * <code>val</code> contains a start delimeter "${" which is not
+     * balanced by a stop delimeter "}". </p>
+     *
+     * <p><b>Author</b> Avy Sharell</a></p>
+     *
+     * @param val The string on which variable substitution is performed.
+     * @throws IllegalArgumentException if <code>val</code> is malformed.
+     */
+    public static String substVars(String val, Properties props) throws IllegalArgumentException {
+
+        StringBuilder sbuf = new StringBuilder();
+
+        int i = 0;
+        int j, k;
+
+        while (true) {
+            j = val.indexOf(DELIM_START, i);
+            if (j == -1) {
+                // no more variables
+                if (i == 0) { // this is a simple string
+                    return val;
+                } else { // add the tail string which contails no variables and return the result.
+                    sbuf.append(val.substring(i, val.length()));
+                    return sbuf.toString();
+                }
+            } else {
+                sbuf.append(val.substring(i, j));
+                k = val.indexOf(DELIM_STOP, j);
+                if (k == -1) {
+                    throw new IllegalArgumentException('"' + val +
+                            "\" has no closing brace. Opening brace at position " + j
+                            + '.');
+                } else {
+                    j += DELIM_START_LEN;
+                    String key = val.substring(j, k);
+                    // first try in System properties
+                    String replacement = getSystemProperty(key, null);
+                    // then try props parameter
+                    if (replacement == null && props != null) {
+                        replacement = props.getProperty(key);
+                    }
+
+                    if (replacement != null) {
+                        // Do variable substitution on the replacement string
+                        // such that we can solve "Hello ${x2}" as "Hello p1"
+                        // the where the properties are
+                        // x1=p1
+                        // x2=${x1}
+                        String recursiveReplacement = substVars(replacement, props);
+                        sbuf.append(recursiveReplacement);
+                    }
+                    i = k + DELIM_STOP_LEN;
+                }
+            }
+        }
+    }
+    
+    private static class CharMap {
+        final char key;
+        final char replacement;
+        
+        public CharMap(char key, char replacement) {
+            this.key = key;
+            this.replacement = replacement;
+        }
+    }
+}
diff --git a/log4j-1.2-api/src/src/main/java/org/apache/log4j/helpers/QuietWriter.java b/log4j-1.2-api/src/src/main/java/org/apache/log4j/helpers/QuietWriter.java
new file mode 100644
index 0000000..1779019
--- /dev/null
+++ b/log4j-1.2-api/src/src/main/java/org/apache/log4j/helpers/QuietWriter.java
@@ -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.
+ */
+
+package org.apache.log4j.helpers;
+
+import org.apache.log4j.spi.ErrorCode;
+import org.apache.log4j.spi.ErrorHandler;
+
+import java.io.FilterWriter;
+import java.io.Writer;
+
+
+/**
+ * QuietWriter does not throw exceptions when things go
+ * wrong. Instead, it delegates error handling to its {@link ErrorHandler}.
+ */
+public class QuietWriter extends FilterWriter {
+
+    protected ErrorHandler errorHandler;
+
+    public QuietWriter(Writer writer, ErrorHandler errorHandler) {
+        super(writer);
+        setErrorHandler(errorHandler);
+    }
+
+    public void write(String string) {
+        if (string != null) {
+            try {
+                out.write(string);
+            } catch (Exception e) {
+                errorHandler.error("Failed to write [" + string + "].", e,
+                        ErrorCode.WRITE_FAILURE);
+            }
+        }
+    }
+
+    public void flush() {
+        try {
+            out.flush();
+        } catch (Exception e) {
+            errorHandler.error("Failed to flush writer,", e,
+                    ErrorCode.FLUSH_FAILURE);
+        }
+    }
+
+
+    public void setErrorHandler(ErrorHandler eh) {
+        if (eh == null) {
+            // This is a programming error on the part of the enclosing appender.
+            throw new IllegalArgumentException("Attempted to set null ErrorHandler.");
+        } else {
+            this.errorHandler = eh;
+        }
+    }
+}
diff --git a/log4j-1.2-api/src/src/main/java/org/apache/log4j/helpers/package-info.java b/log4j-1.2-api/src/src/main/java/org/apache/log4j/helpers/package-info.java
new file mode 100644
index 0000000..00d0e12
--- /dev/null
+++ b/log4j-1.2-api/src/src/main/java/org/apache/log4j/helpers/package-info.java
@@ -0,0 +1,20 @@
+/*
+ * 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 1.x compatibility layer.
+ */
+package org.apache.log4j.helpers;
diff --git a/log4j-1.2-api/src/src/main/java/org/apache/log4j/layout/Log4j1XmlLayout.java b/log4j-1.2-api/src/src/main/java/org/apache/log4j/layout/Log4j1XmlLayout.java
new file mode 100644
index 0000000..9522b9e
--- /dev/null
+++ b/log4j-1.2-api/src/src/main/java/org/apache/log4j/layout/Log4j1XmlLayout.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.
+ */
+package org.apache.log4j.layout;
+
+import java.io.PrintWriter;
+import java.io.StringWriter;
+import java.nio.charset.StandardCharsets;
+import java.util.List;
+
+import org.apache.logging.log4j.core.Layout;
+import org.apache.logging.log4j.core.LogEvent;
+import org.apache.logging.log4j.core.config.Node;
+import org.apache.logging.log4j.core.config.plugins.Plugin;
+import org.apache.logging.log4j.core.config.plugins.PluginAttribute;
+import org.apache.logging.log4j.core.config.plugins.PluginFactory;
+import org.apache.logging.log4j.core.layout.AbstractStringLayout;
+import org.apache.logging.log4j.core.layout.ByteBufferDestination;
+import org.apache.logging.log4j.core.util.Transform;
+import org.apache.logging.log4j.util.BiConsumer;
+import org.apache.logging.log4j.util.ReadOnlyStringMap;
+import org.apache.logging.log4j.util.Strings;
+
+/**
+ * Port of XMLLayout in Log4j 1.x. Provided for compatibility with existing Log4j 1 configurations.
+ *
+ * Originally developed by Ceki G&uuml;lc&uuml;, Mathias Bogaert.
+ */
+@Plugin(name = "Log4j1XmlLayout", category = Node.CATEGORY, elementType = Layout.ELEMENT_TYPE, printObject = true)
+public final class Log4j1XmlLayout extends AbstractStringLayout {
+
+    private final boolean locationInfo;
+    private final boolean properties;
+
+    @PluginFactory
+    public static Log4j1XmlLayout createLayout(
+            // @formatter:off
+            @PluginAttribute(value = "locationInfo") final boolean locationInfo,
+            @PluginAttribute(value = "properties") final boolean properties
+            // @formatter:on
+    ) {
+        return new Log4j1XmlLayout(locationInfo, properties);
+    }
+
+    private Log4j1XmlLayout(final boolean locationInfo, final boolean properties) {
+        super(StandardCharsets.UTF_8);
+        this.locationInfo = locationInfo;
+        this.properties = properties;
+    }
+
+    public boolean isLocationInfo() {
+        return locationInfo;
+    }
+
+    public boolean isProperties() {
+        return properties;
+    }
+
+    @Override
+    public void encode(final LogEvent event, final ByteBufferDestination destination) {
+        final StringBuilder text = getStringBuilder();
+        formatTo(event, text);
+        getStringBuilderEncoder().encode(text, destination);
+    }
+
+    @Override
+    public String toSerializable(final LogEvent event) {
+        final StringBuilder text = getStringBuilder();
+        formatTo(event, text);
+        return text.toString();
+    }
+
+    private void formatTo(final LogEvent event, final StringBuilder buf) {
+        // We yield to the \r\n heresy.
+
+        buf.append("<log4j:event logger=\"");
+        buf.append(Transform.escapeHtmlTags(event.getLoggerName()));
+        buf.append("\" timestamp=\"");
+        buf.append(event.getTimeMillis());
+        buf.append("\" level=\"");
+        buf.append(Transform.escapeHtmlTags(String.valueOf(event.getLevel())));
+        buf.append("\" thread=\"");
+        buf.append(Transform.escapeHtmlTags(event.getThreadName()));
+        buf.append("\">\r\n");
+
+        buf.append("<log4j:message><![CDATA[");
+        // Append the rendered message. Also make sure to escape any existing CDATA sections.
+        Transform.appendEscapingCData(buf, event.getMessage().getFormattedMessage());
+        buf.append("]]></log4j:message>\r\n");
+
+        final List<String> ndc = event.getContextStack().asList();
+        if (!ndc.isEmpty()) {
+            buf.append("<log4j:NDC><![CDATA[");
+            Transform.appendEscapingCData(buf, Strings.join(ndc, ' '));
+            buf.append("]]></log4j:NDC>\r\n");
+        }
+
+        @SuppressWarnings("ThrowableResultOfMethodCallIgnored")
+		final Throwable thrown = event.getThrown();
+        if (thrown != null) {
+            buf.append("<log4j:throwable><![CDATA[");
+            final StringWriter w = new StringWriter();
+            thrown.printStackTrace(new PrintWriter(w));
+            Transform.appendEscapingCData(buf, w.toString());
+            buf.append("]]></log4j:throwable>\r\n");
+        }
+
+        if (locationInfo) {
+            final StackTraceElement source = event.getSource();
+            if (source != null) {
+                buf.append("<log4j:locationInfo class=\"");
+                buf.append(Transform.escapeHtmlTags(source.getClassName()));
+                buf.append("\" method=\"");
+                buf.append(Transform.escapeHtmlTags(source.getMethodName()));
+                buf.append("\" file=\"");
+                buf.append(Transform.escapeHtmlTags(source.getFileName()));
+                buf.append("\" line=\"");
+                buf.append(source.getLineNumber());
+                buf.append("\"/>\r\n");
+            }
+        }
+
+        if (properties) {
+            final ReadOnlyStringMap contextMap = event.getContextData();
+            if (!contextMap.isEmpty()) {
+                buf.append("<log4j:properties>\r\n");
+                contextMap.forEach(new BiConsumer<String, String>() {
+                    @Override
+                    public void accept(final String key, final String val) {
+                        if (val != null) {
+                            buf.append("<log4j:data name=\"");
+                            buf.append(Transform.escapeHtmlTags(key));
+                            buf.append("\" value=\"");
+                            buf.append(Transform.escapeHtmlTags(val));
+                            buf.append("\"/>\r\n");
+                        }
+                    }
+                });
+                buf.append("</log4j:properties>\r\n");
+            }
+        }
+
+        buf.append("</log4j:event>\r\n\r\n");
+    }
+
+}
diff --git a/log4j-1.2-api/src/src/main/java/org/apache/log4j/legacy/core/CategoryUtil.java b/log4j-1.2-api/src/src/main/java/org/apache/log4j/legacy/core/CategoryUtil.java
new file mode 100644
index 0000000..f9e9f7c
--- /dev/null
+++ b/log4j-1.2-api/src/src/main/java/org/apache/log4j/legacy/core/CategoryUtil.java
@@ -0,0 +1,65 @@
+/*
+ * 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.legacy.core;
+
+import org.apache.logging.log4j.Level;
+import org.apache.logging.log4j.Logger;
+import org.apache.logging.log4j.spi.LoggerContext;
+
+/**
+ * Provide access to Log4j Core Logger methods.
+ */
+public final class CategoryUtil {
+
+    private CategoryUtil() {
+    }
+
+    public static boolean isAdditive(Logger logger) {
+        if (logger instanceof org.apache.logging.log4j.core.Logger) {
+            return ((org.apache.logging.log4j.core.Logger) logger).isAdditive();
+        }
+        return false;
+    }
+
+    public static void setAdditivity(Logger logger, boolean additivity) {
+        if (logger instanceof org.apache.logging.log4j.core.Logger) {
+            ((org.apache.logging.log4j.core.Logger) logger).setAdditive(additivity);
+        }
+    }
+
+    public static Logger getParent(Logger logger) {
+        if (logger instanceof org.apache.logging.log4j.core.Logger) {
+            return ((org.apache.logging.log4j.core.Logger) logger).getParent();
+
+        }
+        return null;
+    }
+
+    public static LoggerContext getLoggerContext(Logger logger) {
+        if (logger instanceof org.apache.logging.log4j.core.Logger) {
+            return ((org.apache.logging.log4j.core.Logger) logger).getContext();
+        }
+        return null;
+    }
+
+    public static void setLevel(Logger logger, Level level) {
+        if (logger instanceof org.apache.logging.log4j.core.Logger) {
+            ((org.apache.logging.log4j.core.Logger) logger).setLevel(level);
+
+        }
+    }
+}
diff --git a/log4j-1.2-api/src/src/main/java/org/apache/log4j/legacy/core/ContextUtil.java b/log4j-1.2-api/src/src/main/java/org/apache/log4j/legacy/core/ContextUtil.java
new file mode 100644
index 0000000..d3b99fa
--- /dev/null
+++ b/log4j-1.2-api/src/src/main/java/org/apache/log4j/legacy/core/ContextUtil.java
@@ -0,0 +1,34 @@
+/*
+ * 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.legacy.core;
+
+import org.apache.logging.log4j.spi.LoggerContext;
+
+/**
+ * Implements LoggerContext methods specific to log4j-core.
+ */
+public final class ContextUtil {
+
+    private ContextUtil() {
+    }
+
+    public static void reconfigure(LoggerContext ctx) {
+        if (ctx instanceof org.apache.logging.log4j.core.LoggerContext) {
+            ((org.apache.logging.log4j.core.LoggerContext) ctx).reconfigure();
+        }
+    }
+}
diff --git a/log4j-1.2-api/src/src/main/java/org/apache/log4j/or/ObjectRenderer.java b/log4j-1.2-api/src/src/main/java/org/apache/log4j/or/ObjectRenderer.java
new file mode 100644
index 0000000..f3fed18
--- /dev/null
+++ b/log4j-1.2-api/src/src/main/java/org/apache/log4j/or/ObjectRenderer.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.or;
+
+/**
+ * Converts objects to Strings.
+ */
+public interface ObjectRenderer {
+    /**
+     * Render the object passed as parameter as a String.
+     */
+	 String doRender(Object o);
+}
diff --git a/log4j-1.2-api/src/src/main/java/org/apache/log4j/or/RendererSupport.java b/log4j-1.2-api/src/src/main/java/org/apache/log4j/or/RendererSupport.java
new file mode 100644
index 0000000..9b8728d
--- /dev/null
+++ b/log4j-1.2-api/src/src/main/java/org/apache/log4j/or/RendererSupport.java
@@ -0,0 +1,26 @@
+/*
+ * 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.or;
+
+import java.util.Map;
+
+/**
+ * Interface that indicates the Renderer Map is available. This interface differs
+ */
+public interface RendererSupport {
+    Map<Class<?>, ObjectRenderer> getRendererMap();
+}
diff --git a/log4j-1.2-api/src/src/main/java/org/apache/log4j/or/ThreadGroupRenderer.java b/log4j-1.2-api/src/src/main/java/org/apache/log4j/or/ThreadGroupRenderer.java
new file mode 100644
index 0000000..08233bf
--- /dev/null
+++ b/log4j-1.2-api/src/src/main/java/org/apache/log4j/or/ThreadGroupRenderer.java
@@ -0,0 +1,57 @@
+/*
+ * 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.or;
+
+import org.apache.log4j.Layout;
+
+/**
+ */
+public class ThreadGroupRenderer implements ObjectRenderer {
+
+    public
+    String  doRender(Object obj) {
+        if(obj instanceof ThreadGroup) {
+            StringBuilder sb = new StringBuilder();
+            ThreadGroup threadGroup = (ThreadGroup) obj;
+            sb.append("java.lang.ThreadGroup[name=");
+            sb.append(threadGroup.getName());
+            sb.append(", maxpri=");
+            sb.append(threadGroup.getMaxPriority());
+            sb.append("]");
+            Thread[] threads = new Thread[threadGroup.activeCount()];
+            threadGroup.enumerate(threads);
+            for (Thread thread : threads) {
+                sb.append(Layout.LINE_SEP);
+                sb.append("   Thread=[");
+                sb.append(thread.getName());
+                sb.append(",");
+                sb.append(thread.getPriority());
+                sb.append(",");
+                sb.append(thread.isDaemon());
+                sb.append("]");
+            }
+            return sb.toString();
+        } else {
+            try {
+                // this is the best we can do
+                return obj.toString();
+            } catch(Exception ex) {
+                return ex.toString();
+            }
+        }
+    }
+}
diff --git a/log4j-1.2-api/src/src/main/java/org/apache/log4j/or/jms/MessageRenderer.java b/log4j-1.2-api/src/src/main/java/org/apache/log4j/or/jms/MessageRenderer.java
new file mode 100644
index 0000000..e6f4b6b
--- /dev/null
+++ b/log4j-1.2-api/src/src/main/java/org/apache/log4j/or/jms/MessageRenderer.java
@@ -0,0 +1,87 @@
+/*
+ * 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.or.jms;
+
+import org.apache.log4j.or.ObjectRenderer;
+import org.apache.logging.log4j.Logger;
+import org.apache.logging.log4j.status.StatusLogger;
+
+import javax.jms.Message;
+import javax.jms.JMSException;
+import javax.jms.DeliveryMode;
+
+/**
+ * Log4j 1.x JMS Message Renderer
+ */
+public class MessageRenderer implements ObjectRenderer {
+    private static final Logger LOGGER = StatusLogger.getLogger();
+    
+    /**
+     Render a {@link javax.jms.Message}.
+     */
+    public
+    String  doRender(Object obj) {
+        if (obj instanceof Message) {
+            StringBuilder sb = new StringBuilder();
+            Message message = (Message) obj;
+            try {
+                sb.append("DeliveryMode=");
+                switch(message.getJMSDeliveryMode()) {
+                    case DeliveryMode.NON_PERSISTENT :
+                        sb.append("NON_PERSISTENT");
+                        break;
+                    case DeliveryMode.PERSISTENT :
+                        sb.append("PERSISTENT");
+                        break;
+                    default: sb.append("UNKNOWN");
+                }
+                sb.append(", CorrelationID=");
+                sb.append(message.getJMSCorrelationID());
+
+                sb.append(", Destination=");
+                sb.append(message.getJMSDestination());
+
+                sb.append(", Expiration=");
+                sb.append(message.getJMSExpiration());
+
+                sb.append(", MessageID=");
+                sb.append(message.getJMSMessageID());
+
+                sb.append(", Priority=");
+                sb.append(message.getJMSPriority());
+
+                sb.append(", Redelivered=");
+                sb.append(message.getJMSRedelivered());
+
+                sb.append(", ReplyTo=");
+                sb.append(message.getJMSReplyTo());
+
+                sb.append(", Timestamp=");
+                sb.append(message.getJMSTimestamp());
+
+                sb.append(", Type=");
+                sb.append(message.getJMSType());
+
+            } catch(JMSException e) {
+                LOGGER.error("Could not parse Message.", e);
+            }
+            return sb.toString();
+        } else {
+            return obj.toString();
+        }
+    }
+}
diff --git a/log4j-1.2-api/src/src/main/java/org/apache/log4j/package-info.java b/log4j-1.2-api/src/src/main/java/org/apache/log4j/package-info.java
new file mode 100644
index 0000000..714c200
--- /dev/null
+++ b/log4j-1.2-api/src/src/main/java/org/apache/log4j/package-info.java
@@ -0,0 +1,20 @@
+/*
+ * 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 1.x compatibility layer.
+ */
+package org.apache.log4j;
diff --git a/log4j-1.2-api/src/src/main/java/org/apache/log4j/pattern/Log4j1MdcPatternConverter.java b/log4j-1.2-api/src/src/main/java/org/apache/log4j/pattern/Log4j1MdcPatternConverter.java
new file mode 100644
index 0000000..b4ae0c5
--- /dev/null
+++ b/log4j-1.2-api/src/src/main/java/org/apache/log4j/pattern/Log4j1MdcPatternConverter.java
@@ -0,0 +1,88 @@
+/*
+ * 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 org.apache.logging.log4j.core.LogEvent;
+import org.apache.logging.log4j.core.config.plugins.Plugin;
+import org.apache.logging.log4j.core.pattern.ConverterKeys;
+import org.apache.logging.log4j.core.pattern.LogEventPatternConverter;
+import org.apache.logging.log4j.core.pattern.PatternConverter;
+import org.apache.logging.log4j.util.TriConsumer;
+
+/**
+ * Able to handle the contents of the LogEvent's MDC and either
+ * output the entire contents of the properties, or to output the value of a specific key
+ * within the property bundle when this pattern converter has the option set.
+ */
+@Plugin(name = "Log4j1MdcPatternConverter", category = PatternConverter.CATEGORY)
+@ConverterKeys({ "properties" })
+public final class Log4j1MdcPatternConverter extends LogEventPatternConverter {
+    /**
+     * Name of property to output.
+     */
+    private final String key;
+
+    /**
+     * Private constructor.
+     *
+     * @param options options, may be null.
+     */
+    private Log4j1MdcPatternConverter(final String[] options) {
+        super(options != null && options.length > 0 ? "Log4j1MDC{" + options[0] + '}' : "Log4j1MDC", "property");
+        if (options != null && options.length > 0) {
+            key = options[0];
+        } else {
+            key = null;
+        }
+    }
+
+    /**
+     * Obtains an instance of PropertiesPatternConverter.
+     *
+     * @param options options, may be null or first element contains name of property to format.
+     * @return instance of PropertiesPatternConverter.
+     */
+    public static Log4j1MdcPatternConverter newInstance(final String[] options) {
+        return new Log4j1MdcPatternConverter(options);
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public void format(final LogEvent event, final StringBuilder toAppendTo) {
+        if (key == null) {
+            // if there is no additional options, we output every single Key/Value pair for the MDC
+            toAppendTo.append('{');
+            event.getContextData().forEach(APPEND_EACH, toAppendTo);
+            toAppendTo.append('}');
+        } else {
+            // otherwise they just want a single key output
+            final Object val = event.getContextData().getValue(key);
+            if (val != null) {
+                toAppendTo.append(val);
+            }
+        }
+    }
+
+    private static TriConsumer<String, Object, StringBuilder> APPEND_EACH = new TriConsumer<String, Object, StringBuilder>() {
+        @Override
+        public void accept(final String key, final Object value, final StringBuilder toAppendTo) {
+            toAppendTo.append('{').append(key).append(',').append(value).append('}');
+        }
+    };
+}
diff --git a/log4j-1.2-api/src/src/main/java/org/apache/log4j/pattern/Log4j1NdcPatternConverter.java b/log4j-1.2-api/src/src/main/java/org/apache/log4j/pattern/Log4j1NdcPatternConverter.java
new file mode 100644
index 0000000..405db00
--- /dev/null
+++ b/log4j-1.2-api/src/src/main/java/org/apache/log4j/pattern/Log4j1NdcPatternConverter.java
@@ -0,0 +1,63 @@
+/*
+ * 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 org.apache.logging.log4j.core.LogEvent;
+import org.apache.logging.log4j.core.config.plugins.Plugin;
+import org.apache.logging.log4j.core.pattern.ConverterKeys;
+import org.apache.logging.log4j.core.pattern.LogEventPatternConverter;
+import org.apache.logging.log4j.core.pattern.PatternConverter;
+import org.apache.logging.log4j.util.Strings;
+
+import java.util.List;
+
+
+/**
+ * Returns the event's NDC in a StringBuilder.
+ */
+@Plugin(name = "Log4j1NdcPatternConverter", category = PatternConverter.CATEGORY)
+@ConverterKeys({ "ndc" })
+public final class Log4j1NdcPatternConverter extends LogEventPatternConverter {
+    /**
+     * Singleton.
+     */
+    private static final Log4j1NdcPatternConverter INSTANCE =
+        new Log4j1NdcPatternConverter();
+
+    /**
+     * Private constructor.
+     */
+    private Log4j1NdcPatternConverter() {
+        super("Log4j1NDC", "ndc");
+    }
+
+    /**
+     * Obtains an instance of NdcPatternConverter.
+     *
+     * @param options options, may be null.
+     * @return instance of NdcPatternConverter.
+     */
+    public static Log4j1NdcPatternConverter newInstance(final String[] options) {
+        return INSTANCE;
+    }
+
+    @Override
+    public void format(final LogEvent event, final StringBuilder toAppendTo) {
+        final List<String> ndc = event.getContextStack().asList();
+        toAppendTo.append(Strings.join(ndc, ' '));
+    }
+}
diff --git a/log4j-1.2-api/src/src/main/java/org/apache/log4j/spi/AppenderAttachable.java b/log4j-1.2-api/src/src/main/java/org/apache/log4j/spi/AppenderAttachable.java
new file mode 100644
index 0000000..fd464a2
--- /dev/null
+++ b/log4j-1.2-api/src/src/main/java/org/apache/log4j/spi/AppenderAttachable.java
@@ -0,0 +1,70 @@
+/*
+ * 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 org.apache.log4j.Appender;
+
+import java.util.Enumeration;
+
+/**
+ * Interface for attaching appenders to objects.
+ */
+public interface AppenderAttachable {
+
+    /**
+     * Add an appender.
+     */
+    void addAppender(Appender newAppender);
+
+    /**
+     * Get all previously added appenders as an Enumeration.
+     */
+    Enumeration getAllAppenders();
+
+    /**
+     * Get an appender by name.
+     */
+    Appender getAppender(String name);
+
+
+    /**
+     * Returns <code>true</code> if the specified appender is in list of
+     * attached attached, <code>false</code> otherwise.
+     *
+     * @since 1.2
+     */
+    boolean isAttached(Appender appender);
+
+    /**
+     * Remove all previously added appenders.
+     */
+    void removeAllAppenders();
+
+
+    /**
+     * Remove the appender passed as parameter from the list of appenders.
+     */
+    void removeAppender(Appender appender);
+
+
+    /**
+     * Remove the appender with the name passed as parameter from the
+     * list of appenders.
+     */
+    void removeAppender(String name);
+}
diff --git a/log4j-1.2-api/src/src/main/java/org/apache/log4j/spi/Configurator.java b/log4j-1.2-api/src/src/main/java/org/apache/log4j/spi/Configurator.java
new file mode 100644
index 0000000..b418db8
--- /dev/null
+++ b/log4j-1.2-api/src/src/main/java/org/apache/log4j/spi/Configurator.java
@@ -0,0 +1,55 @@
+/*
+ * 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.InputStream;
+import java.net.URL;
+
+import org.apache.logging.log4j.core.LoggerContext;
+
+/**
+ * Log4j 1.x Configurator interface.
+ */
+public interface Configurator {
+
+    public static final String INHERITED = "inherited";
+
+    public static final String NULL = "null";
+
+
+    /**
+     Interpret a resource pointed by a InputStream and set up log4j accordingly.
+
+     The configuration is done relative to the <code>hierarchy</code>
+     parameter.
+
+     @param inputStream The InputStream to parse
+
+     @since 1.2.17
+     */
+    void doConfigure(InputStream inputStream, final LoggerContext loggerContext);
+
+    /**
+     Interpret a resource pointed by a URL and set up log4j accordingly.
+
+     The configuration is done relative to the <code>hierarchy</code>
+     parameter.
+
+     @param url The URL to parse
+     */
+    void doConfigure(URL url, final LoggerContext loggerContext);
+}
diff --git a/log4j-1.2-api/src/src/main/java/org/apache/log4j/spi/ErrorCode.java b/log4j-1.2-api/src/src/main/java/org/apache/log4j/spi/ErrorCode.java
new file mode 100644
index 0000000..7fbbf95
--- /dev/null
+++ b/log4j-1.2-api/src/src/main/java/org/apache/log4j/spi/ErrorCode.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.spi;
+
+
+/**
+   This interface defines commonly encoutered error codes.
+ */
+public interface ErrorCode {
+
+  public final int GENERIC_FAILURE = 0;
+  public final int WRITE_FAILURE = 1;
+  public final int FLUSH_FAILURE = 2;
+  public final int CLOSE_FAILURE = 3;
+  public final int FILE_OPEN_FAILURE = 4;
+  public final int MISSING_LAYOUT = 5;
+  public final int ADDRESS_PARSE_FAILURE = 6;
+}
diff --git a/log4j-1.2-api/src/src/main/java/org/apache/log4j/spi/ErrorHandler.java b/log4j-1.2-api/src/src/main/java/org/apache/log4j/spi/ErrorHandler.java
new file mode 100644
index 0000000..2e64103
--- /dev/null
+++ b/log4j-1.2-api/src/src/main/java/org/apache/log4j/spi/ErrorHandler.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.spi;
+
+import org.apache.log4j.Appender;
+import org.apache.log4j.Logger;
+
+
+/**
+ * Appenders may delegate their error handling to
+ * <code>ErrorHandlers</code>.
+ * <p>
+ * Error handling is a particularly tedious to get right because by
+ * definition errors are hard to predict and to reproduce.
+ * </p>
+ * <p>
+ * Please take the time to contact the author in case you discover
+ * that errors are not properly handled. You are most welcome to
+ * suggest new error handling policies or criticize existing policies.
+ * </p>
+ */
+public interface ErrorHandler {
+
+    /**
+     * Add a reference to a logger to which the failing appender might
+     * be attached to. The failing appender will be searched and
+     * replaced only in the loggers you add through this method.
+     *
+     * @param logger One of the loggers that will be searched for the failing
+     *               appender in view of replacement.
+     * @since 1.2
+     */
+    void setLogger(Logger logger);
+
+
+    /**
+     * Equivalent to the {@link #error(String, Exception, int,
+     * LoggingEvent)} with the the event parameter set to
+     * <code>null</code>.
+     *
+     * @param message   The message associated with the error.
+     * @param e         The Exception that was thrown when the error occurred.
+     * @param errorCode The error code associated with the error.
+     */
+    void error(String message, Exception e, int errorCode);
+
+    /**
+     * This method is normally used to just print the error message
+     * passed as a parameter.
+     *
+     * @param message   The message associated with the error.
+     */
+    void error(String message);
+
+    /**
+     * This method is invoked to handle the error.
+     *
+     * @param message   The message associated with the error.
+     * @param e         The Exception that was thrown when the error occurred.
+     * @param errorCode The error code associated with the error.
+     * @param event     The logging event that the failing appender is asked
+     *                  to log.
+     * @since 1.2
+     */
+    void error(String message, Exception e, int errorCode, LoggingEvent event);
+
+    /**
+     * Set the appender for which errors are handled. This method is
+     * usually called when the error handler is configured.
+     *
+     * @param appender The appender
+     * @since 1.2
+     */
+    void setAppender(Appender appender);
+
+    /**
+     * Set the appender to fallback upon in case of failure.
+     *
+     * @param appender The backup appender
+     * @since 1.2
+     */
+    void setBackupAppender(Appender appender);
+}
+
diff --git a/log4j-1.2-api/src/src/main/java/org/apache/log4j/spi/Filter.java b/log4j-1.2-api/src/src/main/java/org/apache/log4j/spi/Filter.java
new file mode 100644
index 0000000..997398b
--- /dev/null
+++ b/log4j-1.2-api/src/src/main/java/org/apache/log4j/spi/Filter.java
@@ -0,0 +1,100 @@
+/*
+ * 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 org.apache.log4j.bridge.FilterAdapter;
+
+/**
+ * @since 0.9.0
+ */
+public abstract class Filter {
+    private final FilterAdapter adapter;
+
+    public Filter() {
+        FilterAdapter filterAdapter = null;
+        try {
+            Class.forName("org.apache.logging.log4j.core.Filter");
+            filterAdapter = new FilterAdapter(this);
+        } catch(ClassNotFoundException ex) {
+            // Ignore the exception. Log4j Core is not present.
+        }
+        this.adapter = filterAdapter;
+    }
+
+    /**
+     * The log event must be dropped immediately without consulting
+     * with the remaining filters, if any, in the chain.
+     */
+    public static final int DENY = -1;
+
+    /**
+     * This filter is neutral with respect to the log event. The
+     * remaining filters, if any, should be consulted for a final decision.
+     */
+    public static final int NEUTRAL = 0;
+
+    /**
+     * The log event must be logged immediately without consulting with
+     * the remaining filters, if any, in the chain.
+     */
+    public static final int ACCEPT = 1;
+
+    /**
+     * Points to the next filter in the filter chain.
+     *
+     * @deprecated As of 1.2.12, use {@link #getNext} and {@link #setNext} instead
+     */
+    @Deprecated
+    public Filter next;
+
+    /**
+     * Usually filters options become active when set. We provide a
+     * default do-nothing implementation for convenience.
+     */
+    public void activateOptions() {
+    }
+
+
+    /**
+     * <p>If the decision is <code>DENY</code>, then the event will be
+     * dropped. If the decision is <code>NEUTRAL</code>, then the next
+     * filter, if any, will be invoked. If the decision is ACCEPT then
+     * the event will be logged without consulting with other filters in
+     * the chain.
+     *
+     * @param event The LoggingEvent to decide upon.
+     * @return decision The decision of the filter.
+     */
+    public abstract int decide(LoggingEvent event);
+
+    /**
+     * Set the next filter pointer.
+     * @param next The next Filter.
+     */
+    public void setNext(final Filter next) {
+        this.next = next;
+    }
+
+    /**
+     * Return the pointer to the next filter.
+     * @return The next Filter.
+     */
+    public Filter getNext() {
+        return next;
+    }
+
+}
diff --git a/log4j-1.2-api/src/src/main/java/org/apache/log4j/spi/HierarchyEventListener.java b/log4j-1.2-api/src/src/main/java/org/apache/log4j/spi/HierarchyEventListener.java
new file mode 100644
index 0000000..286ba54
--- /dev/null
+++ b/log4j-1.2-api/src/src/main/java/org/apache/log4j/spi/HierarchyEventListener.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.spi;
+
+import org.apache.log4j.Appender;
+import org.apache.log4j.Category;
+
+/**
+ Listen to events occurring within a Hierarchy.
+
+ @since 1.2
+
+ */
+public interface HierarchyEventListener {
+
+    void addAppenderEvent(Category cat, Appender appender);
+
+    void removeAppenderEvent(Category cat, Appender appender);
+}
diff --git a/log4j-1.2-api/src/src/main/java/org/apache/log4j/spi/LocationInfo.java b/log4j-1.2-api/src/src/main/java/org/apache/log4j/spi/LocationInfo.java
new file mode 100644
index 0000000..2102802
--- /dev/null
+++ b/log4j-1.2-api/src/src/main/java/org/apache/log4j/spi/LocationInfo.java
@@ -0,0 +1,75 @@
+/*
+ * 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;
+
+/**
+ The internal representation of caller location information.
+
+ @since 0.8.3
+ */
+public class LocationInfo implements java.io.Serializable {
+
+    private final StackTraceElement element;
+
+    public String fullInfo;
+
+    public LocationInfo(StackTraceElement element) {
+        this.element = element;
+    }
+
+    /**
+     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.
+     */
+    public
+    String getClassName() {
+        return element.getClassName();
+    }
+
+    /**
+     Return the file name of the caller.
+     */
+    public
+    String getFileName() {
+        return element.getFileName();
+    }
+
+    /**
+     Returns the line number of the caller.
+     */
+    public
+    String getLineNumber() {
+        return Integer.toString(element.getLineNumber());
+    }
+
+    /**
+     Returns the method name of the caller.
+     */
+    public
+    String getMethodName() {
+        return element.getMethodName();
+    }
+}
diff --git a/log4j-1.2-api/src/src/main/java/org/apache/log4j/spi/LoggerFactory.java b/log4j-1.2-api/src/src/main/java/org/apache/log4j/spi/LoggerFactory.java
new file mode 100644
index 0000000..e2f3708
--- /dev/null
+++ b/log4j-1.2-api/src/src/main/java/org/apache/log4j/spi/LoggerFactory.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.spi;
+
+import org.apache.log4j.Logger;
+
+/**
+ *
+ * Implement this interface to create new instances of Logger or a sub-class of Logger.
+ *
+ * <p>
+ * See <code>examples/subclass/MyLogger.java</code> for an example.
+ * </p>
+ */
+public interface LoggerFactory {
+
+    Logger makeNewLoggerInstance(String name);
+
+}
diff --git a/log4j-1.2-api/src/src/main/java/org/apache/log4j/spi/LoggerRepository.java b/log4j-1.2-api/src/src/main/java/org/apache/log4j/spi/LoggerRepository.java
new file mode 100644
index 0000000..812280f
--- /dev/null
+++ b/log4j-1.2-api/src/src/main/java/org/apache/log4j/spi/LoggerRepository.java
@@ -0,0 +1,109 @@
+/*
+ * 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.util.Enumeration;
+
+import org.apache.log4j.Appender;
+import org.apache.log4j.Category;
+import org.apache.log4j.Level;
+import org.apache.log4j.Logger;
+
+/**
+ * A <code>LoggerRepository</code> is used to create and retrieve <code>Loggers</code>.
+ * <p>
+ * The relation between loggers in a repository depends on the repository but typically loggers are arranged in a named
+ * hierarchy.
+ * </p>
+ * <p>
+ * In addition to the creational methods, a <code>LoggerRepository</code> can be queried for existing loggers, can act
+ * as a point of registry for events related to loggers.
+ * </p>
+ *
+ * @since 1.2
+ */
+public interface LoggerRepository {
+
+    /**
+     * Add a {@link HierarchyEventListener} event to the repository.
+     *
+     * @param listener The listener
+     */
+    void addHierarchyEventListener(HierarchyEventListener listener);
+
+    /**
+     * Returns whether this repository is disabled for a given
+     * level. The answer depends on the repository threshold and the
+     * <code>level</code> parameter. See also {@link #setThreshold}
+     * method.
+     *
+     * @param level The level
+     * @return whether this repository is disabled.
+     */
+    boolean isDisabled(int level);
+
+    /**
+     * Set the repository-wide threshold. All logging requests below the
+     * threshold are immediately dropped. By default, the threshold is
+     * set to <code>Level.ALL</code> which has the lowest possible rank.
+     *
+     * @param level The level
+     */
+    void setThreshold(Level level);
+
+    /**
+     * Another form of {@link #setThreshold(Level)} accepting a string
+     * parameter instead of a <code>Level</code>.
+     *
+     * @param val The threshold value
+     */
+    void setThreshold(String val);
+
+    void emitNoAppenderWarning(Category cat);
+
+    /**
+     * Get the repository-wide threshold. See {@link #setThreshold(Level)} for an explanation.
+     *
+     * @return the level.
+     */
+    Level getThreshold();
+
+    Logger getLogger(String name);
+
+    Logger getLogger(String name, LoggerFactory factory);
+
+    Logger getRootLogger();
+
+    Logger exists(String name);
+
+    void shutdown();
+
+    @SuppressWarnings("rawtypes")
+    Enumeration getCurrentLoggers();
+
+    /**
+     * Deprecated. Please use {@link #getCurrentLoggers} instead.
+     *
+     * @return an enumeration of loggers.
+     */
+    @SuppressWarnings("rawtypes")
+    Enumeration getCurrentCategories();
+
+    void fireAddAppenderEvent(Category logger, Appender appender);
+
+    void resetConfiguration();
+}
diff --git a/log4j-1.2-api/src/src/main/java/org/apache/log4j/spi/LoggingEvent.java b/log4j-1.2-api/src/src/main/java/org/apache/log4j/spi/LoggingEvent.java
new file mode 100644
index 0000000..e9f57de
--- /dev/null
+++ b/log4j-1.2-api/src/src/main/java/org/apache/log4j/spi/LoggingEvent.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.spi;
+
+import org.apache.log4j.Category;
+import org.apache.log4j.Level;
+import org.apache.log4j.bridge.LogEventAdapter;
+
+/**
+ *  No-op version of Log4j 1.2 LoggingEvent. This class is not directly used by Log4j 1.x clients but is used by
+ *  the Log4j 2 LogEvent adapter to be compatible with Log4j 1.x components.
+ */
+public class LoggingEvent {
+
+    /**
+     Set the location information for this logging event. The collected
+     information is cached for future use.
+     */
+    public LocationInfo getLocationInformation() {
+        return null;
+    }
+
+    /**
+     * Return the level of this event. Use this form instead of directly
+     * accessing the <code>level</code> field.  */
+    public Level getLevel() {
+        return null;
+    }
+
+    /**
+     * Return the name of the logger. Use this form instead of directly
+     * accessing the <code>categoryName</code> field.
+     */
+    public String getLoggerName() {
+        return null;
+    }
+
+    /**
+     * Gets the logger of the event.
+     * Use should be restricted to cloning events.
+     * @since 1.2.15
+     */
+    public Category getLogger() {
+        return null;
+    }
+
+    /**
+     Return the message for this logging event.
+
+     <p>Before serialization, the returned object is the message
+     passed by the user to generate the logging event. After
+     serialization, the returned value equals the String form of the
+     message possibly after object rendering.
+
+     @since 1.1 */
+    public
+    Object getMessage() {
+        return null;
+    }
+
+    public
+    String getNDC() {
+        return null;
+    }
+
+    public
+    Object getMDC(String key) {
+        return null;
+    }
+
+    /**
+     Obtain a copy of this thread's MDC prior to serialization or
+     asynchronous logging.
+     */
+    public
+    void getMDCCopy() {
+    }
+
+    public
+    String getRenderedMessage() {
+        return null;
+    }
+
+    /**
+     Returns the time when the application started, in milliseconds
+     elapsed since 01.01.1970.  */
+    public static long getStartTime() {
+        return LogEventAdapter.getStartTime();
+    }
+
+    public
+    String getThreadName() {
+        return null;
+    }
+
+    /**
+     Returns the throwable information contained within this
+     event. May be <code>null</code> if there is no such information.
+
+     <p>Note that the {@link Throwable} object contained within a
+     {@link ThrowableInformation} does not survive serialization.
+
+     @since 1.1 */
+    public
+    ThrowableInformation getThrowableInformation() {
+        return null;
+    }
+
+    /**
+     Return this event's throwable's string[] representaion.
+     */
+    public
+    String[] getThrowableStrRep() {
+        return null;
+    }
+
+}
diff --git a/log4j-1.2-api/src/src/main/java/org/apache/log4j/spi/OptionHandler.java b/log4j-1.2-api/src/src/main/java/org/apache/log4j/spi/OptionHandler.java
new file mode 100644
index 0000000..1b855bc
--- /dev/null
+++ b/log4j-1.2-api/src/src/main/java/org/apache/log4j/spi/OptionHandler.java
@@ -0,0 +1,26 @@
+/*
+ * 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;
+
+
+/**
+ * Log4j 1 Interface for dealing with configuration. Ignored in Log4j 2.
+ */
+public interface OptionHandler {
+
+    void activateOptions();
+}
diff --git a/log4j-1.2-api/src/src/main/java/org/apache/log4j/spi/RepositorySelector.java b/log4j-1.2-api/src/src/main/java/org/apache/log4j/spi/RepositorySelector.java
new file mode 100644
index 0000000..9566659
--- /dev/null
+++ b/log4j-1.2-api/src/src/main/java/org/apache/log4j/spi/RepositorySelector.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.spi;
+
+/**
+
+ The <code>LogManager</code> uses one (and only one)
+ <code>RepositorySelector</code> implementation to select the
+ {@link org.apache.log4j.spi.LoggerRepository} for a particular application context.
+
+ <p>It is the responsibility of the <code>RepositorySelector</code>
+ implementation to track the application context. Log4j makes no
+ assumptions about the application context or on its management.
+
+ <p>See also {@link org.apache.log4j.LogManager LogManager}.
+
+ @since 1.2
+
+ */
+public interface RepositorySelector {
+
+    /**
+     * Returns a {@link org.apache.log4j.spi.LoggerRepository} depending on the
+     * context. Implementers must make sure that a valid (non-null)
+     * LoggerRepository is returned.
+     * @return a LoggerRepository.
+     */
+    LoggerRepository getLoggerRepository();
+}
diff --git a/log4j-1.2-api/src/src/main/java/org/apache/log4j/spi/ThrowableInformation.java b/log4j-1.2-api/src/src/main/java/org/apache/log4j/spi/ThrowableInformation.java
new file mode 100644
index 0000000..5a9ace5
--- /dev/null
+++ b/log4j-1.2-api/src/src/main/java/org/apache/log4j/spi/ThrowableInformation.java
@@ -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.
+ */
+package org.apache.log4j.spi;
+
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.util.List;
+
+import org.apache.logging.log4j.core.util.Throwables;
+
+/**
+ * Class Description goes here.
+ */
+public class ThrowableInformation implements java.io.Serializable {
+
+    static final long serialVersionUID = -4748765566864322735L;
+
+    private transient Throwable throwable;
+    private Method toStringList;
+
+    @SuppressWarnings("unchecked")
+    public
+    ThrowableInformation(Throwable throwable) {
+        this.throwable = throwable;
+        Method method = null;
+        try {
+            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;
+    }
+
+    public
+    Throwable getThrowable() {
+        return throwable;
+    }
+
+    public synchronized String[] getThrowableStrRep() {
+        if (toStringList != null && throwable != null) {
+            try {
+                @SuppressWarnings("unchecked")
+                List<String> elements = (List<String>) toStringList.invoke(null, throwable);
+                if (elements != null) {
+                    return elements.toArray(new String[0]);
+                }
+            } catch (IllegalAccessException | InvocationTargetException ex) {
+                // Ignore the exception.
+            }
+        }
+        return null;
+    }
+}
+
diff --git a/log4j-1.2-api/src/src/main/java/org/apache/log4j/spi/package-info.java b/log4j-1.2-api/src/src/main/java/org/apache/log4j/spi/package-info.java
new file mode 100644
index 0000000..a7648dc
--- /dev/null
+++ b/log4j-1.2-api/src/src/main/java/org/apache/log4j/spi/package-info.java
@@ -0,0 +1,20 @@
+/*
+ * 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 1.x compatibility layer.
+ */
+package org.apache.log4j.spi;
diff --git a/log4j-1.2-api/src/src/main/java/org/apache/log4j/xml/DOMConfigurator.java b/log4j-1.2-api/src/src/main/java/org/apache/log4j/xml/DOMConfigurator.java
new file mode 100644
index 0000000..04a4555
--- /dev/null
+++ b/log4j-1.2-api/src/src/main/java/org/apache/log4j/xml/DOMConfigurator.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.xml;
+
+import java.io.InputStream;
+import java.io.Reader;
+import java.net.URL;
+import java.util.Properties;
+
+import javax.xml.parsers.FactoryConfigurationError;
+
+import org.apache.log4j.config.PropertySetter;
+import org.apache.log4j.spi.LoggerRepository;
+import org.w3c.dom.Element;
+
+/**
+ *
+ */
+public class DOMConfigurator {
+
+    public void doConfigure(final String filename, final LoggerRepository repository) {
+    }
+
+    public void doConfigure(final URL url, final LoggerRepository repository) {
+    }
+
+    public void doConfigure(final InputStream inputStream, final LoggerRepository repository)
+        throws FactoryConfigurationError {
+    }
+
+    public void doConfigure(final Reader reader, final LoggerRepository repository)
+        throws FactoryConfigurationError {
+    }
+
+    public void doConfigure(final Element element, final LoggerRepository repository) {
+    }
+
+    public static void configure(final Element element) {
+    }
+
+    public static void configureAndWatch(final String configFilename) {
+    }
+
+    public static void configureAndWatch(final String configFilename, final long delay) {
+    }
+
+    public static void configure(final String filename) throws FactoryConfigurationError {
+    }
+
+    public static void configure(final URL url) throws FactoryConfigurationError {
+    }
+
+    public static String subst(final String value, final Properties props) {
+        return value;
+    }
+
+    public static void setParameter(final Element elem, final PropertySetter propSetter, final Properties props) {
+
+    }
+
+    public static Object parseElement(final Element element, final Properties props,
+                                      @SuppressWarnings("rawtypes") final Class expectedClass)
+        throws Exception {
+        return null;
+    }
+}
diff --git a/log4j-1.2-api/src/src/main/java/org/apache/log4j/xml/Log4jEntityResolver.java b/log4j-1.2-api/src/src/main/java/org/apache/log4j/xml/Log4jEntityResolver.java
new file mode 100644
index 0000000..edda022
--- /dev/null
+++ b/log4j-1.2-api/src/src/main/java/org/apache/log4j/xml/Log4jEntityResolver.java
@@ -0,0 +1,51 @@
+/*
+ * 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.logging.log4j.Logger;
+import org.apache.logging.log4j.status.StatusLogger;
+import org.xml.sax.EntityResolver;
+import org.xml.sax.InputSource;
+
+import java.io.ByteArrayInputStream;
+import java.io.InputStream;
+
+/**
+ * An {@link EntityResolver} specifically designed to return
+ * <code>log4j.dtd</code> which is embedded within the log4j jar
+ * file.
+ */
+public class Log4jEntityResolver implements EntityResolver {
+    private static final Logger LOGGER = StatusLogger.getLogger();
+    private static final String PUBLIC_ID = "-//APACHE//DTD LOG4J 1.2//EN";
+
+    public InputSource resolveEntity(String publicId, String systemId) {
+        if (systemId.endsWith("log4j.dtd") || PUBLIC_ID.equals(publicId)) {
+            Class clazz = getClass();
+            InputStream in = clazz.getResourceAsStream("/org/apache/log4j/xml/log4j.dtd");
+            if (in == null) {
+                LOGGER.warn("Could not find [log4j.dtd] using [{}] class loader, parsed without DTD.",
+                        clazz.getClassLoader());
+                in = new ByteArrayInputStream(new byte[0]);
+            }
+            return new InputSource(in);
+        } else {
+            return null;
+        }
+    }
+}
diff --git a/log4j-1.2-api/src/src/main/java/org/apache/log4j/xml/UnrecognizedElementHandler.java b/log4j-1.2-api/src/src/main/java/org/apache/log4j/xml/UnrecognizedElementHandler.java
new file mode 100644
index 0000000..463d5d9
--- /dev/null
+++ b/log4j-1.2-api/src/src/main/java/org/apache/log4j/xml/UnrecognizedElementHandler.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.xml;
+
+import org.w3c.dom.Element;
+import java.util.Properties;
+
+/**
+ * When implemented by an object configured by DOMConfigurator,
+ * the handle method will be called when an unrecognized child
+ * element is encountered.  Unrecognized child elements of
+ * the log4j:configuration element will be dispatched to
+ * the logger repository if it supports this interface.
+ *
+ * @since 1.2.15
+ */
+public interface UnrecognizedElementHandler {
+    /**
+     * Called to inform a configured object when
+     * an unrecognized child element is encountered.
+     * @param element element, may not be null.
+     * @param props properties in force, may be null.
+     * @return true if configured object recognized the element
+     * @throws Exception throw an exception to prevent activation
+     * of the configured object.
+     */
+    boolean parseUnrecognizedElement(Element element, Properties props) throws Exception;
+}
\ No newline at end of file
diff --git a/log4j-1.2-api/src/src/main/java/org/apache/log4j/xml/XmlConfigurationFactory.java b/log4j-1.2-api/src/src/main/java/org/apache/log4j/xml/XmlConfigurationFactory.java
new file mode 100644
index 0000000..a2f7dc7
--- /dev/null
+++ b/log4j-1.2-api/src/src/main/java/org/apache/log4j/xml/XmlConfigurationFactory.java
@@ -0,0 +1,926 @@
+/*
+ * 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.Appender;
+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.bridge.LayoutAdapter;
+import org.apache.log4j.bridge.LayoutWrapper;
+import org.apache.log4j.builders.BuilderManager;
+import org.apache.log4j.config.Log4j1Configuration;
+import org.apache.log4j.config.PropertySetter;
+import org.apache.log4j.helpers.OptionConverter;
+import org.apache.log4j.spi.AppenderAttachable;
+import org.apache.log4j.spi.Configurator;
+import org.apache.log4j.spi.ErrorHandler;
+import org.apache.log4j.spi.Filter;
+import org.apache.log4j.spi.LoggerRepository;
+import org.apache.logging.log4j.core.LoggerContext;
+import org.apache.logging.log4j.core.appender.ConsoleAppender;
+import org.apache.logging.log4j.core.appender.FileAppender;
+import org.apache.logging.log4j.core.appender.NullAppender;
+import org.apache.logging.log4j.core.appender.RollingFileAppender;
+import org.apache.logging.log4j.core.appender.rolling.CompositeTriggeringPolicy;
+import org.apache.logging.log4j.core.appender.rolling.DefaultRolloverStrategy;
+import org.apache.logging.log4j.core.appender.rolling.RolloverStrategy;
+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.ConfigurationFactory;
+import org.apache.logging.log4j.core.config.ConfigurationSource;
+import org.apache.logging.log4j.core.config.LoggerConfig;
+import org.apache.logging.log4j.core.config.Order;
+import org.apache.logging.log4j.core.config.plugins.Plugin;
+import org.apache.logging.log4j.core.config.status.StatusConfiguration;
+import org.apache.logging.log4j.core.layout.HtmlLayout;
+import org.apache.logging.log4j.core.layout.PatternLayout;
+import org.apache.logging.log4j.core.layout.XmlLayout;
+import org.apache.logging.log4j.status.StatusLogger;
+import org.apache.logging.log4j.util.LoaderUtil;
+import org.w3c.dom.Document;
+import org.w3c.dom.Element;
+import org.w3c.dom.NamedNodeMap;
+import org.w3c.dom.Node;
+import org.w3c.dom.NodeList;
+import org.xml.sax.InputSource;
+import org.xml.sax.SAXException;
+import org.xml.sax.SAXParseException;
+
+import javax.xml.parsers.DocumentBuilder;
+import javax.xml.parsers.DocumentBuilderFactory;
+import javax.xml.parsers.FactoryConfigurationError;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InterruptedIOException;
+import java.io.Reader;
+import java.lang.reflect.Method;
+import java.net.URL;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Properties;
+import java.util.concurrent.TimeUnit;
+import java.util.function.Consumer;
+
+/**
+ * Constructs a Configuration usable in Log4j 2 from a Log4j 1 configuration file.
+ */
+@Plugin(name = "Log4j1XmlConfigurationFactory", category = ConfigurationFactory.CATEGORY)
+@Order(2)
+public class XmlConfigurationFactory extends ConfigurationFactory implements Configurator {
+    private static final org.apache.logging.log4j.Logger LOGGER = StatusLogger.getLogger();
+
+    private static final String CONFIGURATION_TAG = "log4j:configuration";
+    private static final String OLD_CONFIGURATION_TAG = "configuration";
+    private static final String RENDERER_TAG = "renderer";
+    private static final String APPENDER_TAG = "appender";
+    private static final String APPENDER_REF_TAG = "appender-ref";
+    public  static final String PARAM_TAG = "param";
+    public static final String LAYOUT_TAG = "layout";
+    private static final String CATEGORY = "category";
+    private static final String LOGGER_ELEMENT = "logger";
+    private static final String CATEGORY_FACTORY_TAG = "categoryFactory";
+    private static final String LOGGER_FACTORY_TAG = "loggerFactory";
+    public static final String NAME_ATTR = "name";
+    private static final String CLASS_ATTR = "class";
+    public static final String VALUE_ATTR = "value";
+    private static final String ROOT_TAG = "root";
+    private static final String LEVEL_TAG = "level";
+    private static final String PRIORITY_TAG = "priority";
+    public static final String FILTER_TAG = "filter";
+    private static final String ERROR_HANDLER_TAG = "errorHandler";
+    private static final String REF_ATTR = "ref";
+    private static final String ADDITIVITY_ATTR = "additivity";
+    private static final String CONFIG_DEBUG_ATTR = "configDebug";
+    private static final String INTERNAL_DEBUG_ATTR = "debug";
+    private static final String EMPTY_STR = "";
+    private static final Class[] ONE_STRING_PARAM = new Class[]{String.class};
+    private static final String dbfKey = "javax.xml.parsers.DocumentBuilderFactory";
+    private static final String THROWABLE_RENDERER_TAG = "throwableRenderer";
+    private static final String SYSTEM_OUT = "System.out";
+    private static final String SYSTEM_ERR = "System.err";
+    private static final String THREAD_PRINTING_PARAM = "threadprinting";
+    private static final String CATEGORY_PREFIXING_PARAM = "categoryprefixing";
+    private static final String CONTEXT_PRINTING_PARAM = "contextprinting";
+    private static final String DATE_FORMAT_PARAM = "dateformat";
+    private static final String TIMEZONE_FORMAT = "timezone";
+    public static final String FILE_PARAM = "file";
+    public static final String APPEND_PARAM = "append";
+    public static final String BUFFERED_IO_PARAM = "bufferedio";
+    public static final String BUFFER_SIZE_PARAM = "buffersize";
+    public static final String MAX_SIZE_PARAM = "maxfileSize";
+    public static final String MAX_BACKUP_INDEX = "maxbackupindex";
+    public static final String RELATIVE = "RELATIVE";
+    public static final long DEFAULT_DELAY = 60000;
+    /**
+     * File name prefix for test configurations.
+     */
+    protected static final String TEST_PREFIX = "log4j-test";
+
+    /**
+     * File name prefix for standard configurations.
+     */
+    protected static final String DEFAULT_PREFIX = "log4j";
+
+    private final BuilderManager manager;
+
+    // key: appenderName, value: appender
+    private Map<String, Appender> appenderBag;
+
+    private Properties props = null;
+
+    private final LoggerContext loggerContext;
+    private Log4j1Configuration configuration;
+
+    /**
+     * No argument constructor.
+     */
+    public XmlConfigurationFactory() {
+        appenderBag = new HashMap<>();
+        loggerContext = (LoggerContext) org.apache.logging.log4j.LogManager.getContext(false);
+        manager = new BuilderManager();
+    }
+
+
+    private XmlConfigurationFactory(ConfigurationSource source, int monitorIntervalSeconds) {
+        appenderBag = new HashMap<>();
+        loggerContext = (LoggerContext) org.apache.logging.log4j.LogManager.getContext(false);
+        configuration = new Log4j1Configuration(loggerContext, source, monitorIntervalSeconds);
+        manager = new BuilderManager();
+    }
+
+    @Override
+    protected String[] getSupportedTypes() {
+        return new String[] {".xml"};
+    }
+
+    @Override
+    public Configuration getConfiguration(LoggerContext loggerContext, ConfigurationSource source) {
+        configuration = new Log4j1Configuration(loggerContext, source, 0);
+        doConfigure();
+        return configuration;
+    }
+
+    public Configuration getConfiguration() {
+        return configuration;
+    }
+
+    @Override
+    protected String getTestPrefix() {
+        return TEST_PREFIX;
+    }
+
+    @Override
+    protected String getDefaultPrefix() {
+        return DEFAULT_PREFIX;
+    }
+
+    /**
+     * Delegates unrecognized content to created instance if
+     * it supports UnrecognizedElementParser.
+     *
+     * @param instance instance, may be null.
+     * @param element  element, may not be null.
+     * @param props    properties
+     * @throws IOException thrown if configuration of owner object
+     *                     should be abandoned.
+     * @since 1.2.15
+     */
+    private static void parseUnrecognizedElement(final Object instance, final Element element,
+            final Properties props) throws Exception {
+        boolean recognized = false;
+        if (instance instanceof UnrecognizedElementHandler) {
+            recognized = ((UnrecognizedElementHandler) instance).parseUnrecognizedElement(
+                    element, props);
+        }
+        if (!recognized) {
+            LOGGER.warn("Unrecognized element {}", element.getNodeName());
+        }
+    }
+
+    /**
+     * Delegates unrecognized content to created instance if
+     * it supports UnrecognizedElementParser and catches and
+     * logs any exception.
+     *
+     * @param instance instance, may be null.
+     * @param element  element, may not be null.
+     * @param props    properties
+     * @since 1.2.15
+     */
+    private static void quietParseUnrecognizedElement(final Object instance,
+            final Element element,
+            final Properties props) {
+        try {
+            parseUnrecognizedElement(instance, element, props);
+        } catch (Exception ex) {
+            if (ex instanceof InterruptedException || ex instanceof InterruptedIOException) {
+                Thread.currentThread().interrupt();
+            }
+            LOGGER.error("Error in extension content: ", ex);
+        }
+    }
+
+    /**
+     * Like {@link #configureAndWatch(String, long)} except that the
+     * default delay is used.
+     *
+     * @param configFilename A log4j configuration file in XML format.
+     */
+    public static void configureAndWatch(final String configFilename) {
+        configureAndWatch(configFilename, DEFAULT_DELAY);
+    }
+
+    /**
+     * Read the configuration file <code>configFilename</code> if it
+     * exists. Moreover, a thread will be created that will periodically
+     * check if <code>configFilename</code> has been created or
+     * modified. The period is determined by the <code>delay</code>
+     * argument. If a change or file creation is detected, then
+     * <code>configFilename</code> is read to configure log4j.
+     *
+     * @param configFilename A log4j configuration file in XML format.
+     * @param delay          The delay in milliseconds to wait between each check.
+     */
+    public static void configureAndWatch(final String configFilename, final long delay) {
+        try {
+            File file = new File(configFilename);
+            InputStream is = new FileInputStream(file);
+            ConfigurationSource source = new ConfigurationSource(is, file);
+            int seconds = (int) TimeUnit.MILLISECONDS.toSeconds(delay);
+            XmlConfigurationFactory factory = new XmlConfigurationFactory(source, seconds);
+            factory.doConfigure();
+            org.apache.logging.log4j.core.config.Configurator.reconfigure(factory.getConfiguration());
+
+        } catch (IOException ioe) {
+            LOGGER.error("Unable to process configuration file {} due to {}", configFilename, ioe.getMessage());
+        }
+    }
+
+    /**
+     * A static version of doConfigure(String).
+     */
+    public static void configure(final String filename) throws FactoryConfigurationError {
+        configureAndWatch(filename, 0);
+    }
+
+    /**
+     * A static version of doConfigure(URL).
+     */
+    public static void configure(final URL url) throws FactoryConfigurationError {
+        try {
+            InputStream is = url.openStream();
+            ConfigurationSource source = new ConfigurationSource(is, url);
+            XmlConfigurationFactory factory = new XmlConfigurationFactory(source, 0);
+            factory.doConfigure();
+            org.apache.logging.log4j.core.config.Configurator.reconfigure(factory.getConfiguration());
+        } catch (IOException ioe) {
+            LOGGER.error("Unable to process configuration {} due to {}", url.toString(), ioe.getMessage());
+        }
+    }
+
+    /**
+     * Substitutes property value for any references in expression.
+     *
+     * @param value value from configuration file, may contain
+     *              literal text, property references or both
+     * @param props properties.
+     * @return evaluated expression, may still contain expressions
+     * if unable to expand.
+     */
+    public static String subst(final String value, final Properties props) {
+        try {
+            return OptionConverter.substVars(value, props);
+        } catch (IllegalArgumentException e) {
+            LOGGER.warn("Could not perform variable substitution.", e);
+            return value;
+        }
+    }
+
+    /**
+     * Sets a parameter based from configuration file content.
+     *
+     * @param elem       param element, may not be null.
+     * @param propSetter property setter, may not be null.
+     * @param props      properties
+     * @since 1.2.15
+     */
+    public static void setParameter(final Element elem, final PropertySetter propSetter, final Properties props) {
+        String name = subst(elem.getAttribute("name"), props);
+        String value = (elem.getAttribute("value"));
+        value = subst(OptionConverter.convertSpecialChars(value), props);
+        propSetter.setProperty(name, value);
+    }
+
+    /**
+     * Creates an object and processes any nested param elements
+     * but does not call activateOptions.  If the class also supports
+     * UnrecognizedElementParser, the parseUnrecognizedElement method
+     * will be call for any child elements other than param.
+     *
+     * @param element       element, may not be null.
+     * @param props         properties
+     * @param expectedClass interface or class expected to be implemented
+     *                      by created class
+     * @return created class or null.
+     * @throws Exception thrown if the contain object should be abandoned.
+     * @since 1.2.15
+     */
+    public static Object parseElement(final Element element, final Properties props,
+            @SuppressWarnings("rawtypes") final Class expectedClass) throws Exception {
+        String clazz = subst(element.getAttribute("class"), props);
+        Object instance = OptionConverter.instantiateByClassName(clazz,
+                expectedClass, null);
+
+        if (instance != null) {
+            PropertySetter propSetter = new PropertySetter(instance);
+            NodeList children = element.getChildNodes();
+            final int length = children.getLength();
+
+            for (int loop = 0; loop < length; loop++) {
+                Node currentNode = children.item(loop);
+                if (currentNode.getNodeType() == Node.ELEMENT_NODE) {
+                    Element currentElement = (Element) currentNode;
+                    String tagName = currentElement.getTagName();
+                    if (tagName.equals("param")) {
+                        setParameter(currentElement, propSetter, props);
+                    } else {
+                        parseUnrecognizedElement(instance, currentElement, props);
+                    }
+                }
+            }
+            return instance;
+        }
+        return null;
+    }
+
+    /**
+     * Used internally to parse appenders by IDREF name.
+     */
+    private Appender findAppenderByName(Document doc, String appenderName) {
+        Appender appender = appenderBag.get(appenderName);
+
+        if (appender != null) {
+            return appender;
+        } else {
+            // Doesn't work on DOM Level 1 :
+            // Element element = doc.getElementById(appenderName);
+
+            // Endre's hack:
+            Element element = null;
+            NodeList list = doc.getElementsByTagName("appender");
+            for (int t = 0; t < list.getLength(); t++) {
+                Node node = list.item(t);
+                NamedNodeMap map = node.getAttributes();
+                Node attrNode = map.getNamedItem("name");
+                if (appenderName.equals(attrNode.getNodeValue())) {
+                    element = (Element) node;
+                    break;
+                }
+            }
+            // Hack finished.
+
+            if (element == null) {
+
+                LOGGER.error("No appender named [{}] could be found.", appenderName);
+                return null;
+            } else {
+                appender = parseAppender(element);
+                if (appender != null) {
+                    appenderBag.put(appenderName, appender);
+                }
+                return appender;
+            }
+        }
+    }
+
+    /**
+     * Used internally to parse appenders by IDREF element.
+     */
+    private Appender findAppenderByReference(Element appenderRef) {
+        String appenderName = subst(appenderRef.getAttribute(REF_ATTR));
+        Document doc = appenderRef.getOwnerDocument();
+        return findAppenderByName(doc, appenderName);
+    }
+
+    /**
+     * Used internally to parse an appender element.
+     */
+    private Appender parseAppender(Element appenderElement) {
+        String className = subst(appenderElement.getAttribute(CLASS_ATTR));
+        LOGGER.debug("Class name: [" + className + ']');
+        Appender appender = manager.parseAppender(className, appenderElement, this);
+        if (appender == null) {
+            appender = buildAppender(className, appenderElement);
+        }
+        return appender;
+    }
+
+    private Appender buildAppender(String className, Element appenderElement) {
+            try {
+                Appender appender = LoaderUtil.newInstanceOf(className);
+                PropertySetter propSetter = new PropertySetter(appender);
+
+                appender.setName(subst(appenderElement.getAttribute(NAME_ATTR)));
+                forEachElement(appenderElement.getChildNodes(), (currentElement) -> {
+                    // Parse appender parameters
+                    switch (currentElement.getTagName()) {
+                        case PARAM_TAG:
+                            setParameter(currentElement, propSetter);
+                            break;
+                        case LAYOUT_TAG:
+                            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);
+                            }
+                            break;
+                        case ERROR_HANDLER_TAG:
+                            parseErrorHandler(currentElement, appender);
+                            break;
+                        case APPENDER_REF_TAG:
+                            String refName = subst(currentElement.getAttribute(REF_ATTR));
+                            if (appender instanceof AppenderAttachable) {
+                                AppenderAttachable aa = (AppenderAttachable) appender;
+                                Appender child = findAppenderByReference(currentElement);
+                                LOGGER.debug("Attaching appender named [{}] to appender named [{}].", refName,
+                                        appender.getName());
+                                aa.addAppender(child);
+                            } else {
+                                LOGGER.error("Requesting attachment of appender named [{}] to appender named [{}}]"
+                                                + "which does not implement org.apache.log4j.spi.AppenderAttachable.",
+                                        refName, appender.getName());
+                            }
+                            break;
+                        default:
+                            try {
+                                parseUnrecognizedElement(appender, currentElement, props);
+                            } catch (Exception ex) {
+                                throw new ConsumerException(ex);
+                            }
+                    }
+                });
+                propSetter.activate();
+                return appender;
+            } catch (ConsumerException ex) {
+                Throwable t = ex.getCause();
+                if (t instanceof InterruptedException || t instanceof InterruptedIOException) {
+                    Thread.currentThread().interrupt();
+                }
+                LOGGER.error("Could not create an Appender. Reported error follows.", t);
+            } catch (Exception oops) {
+                if (oops instanceof InterruptedException || oops instanceof InterruptedIOException) {
+                    Thread.currentThread().interrupt();
+                }
+                LOGGER.error("Could not create an Appender. Reported error follows.", oops);
+            }
+            return null;
+        }
+
+    /**
+     * Used internally to parse an {@link ErrorHandler} element.
+     */
+    private void parseErrorHandler(Element element, Appender appender) {
+        ErrorHandler eh = (ErrorHandler) OptionConverter.instantiateByClassName(
+                subst(element.getAttribute(CLASS_ATTR)),
+                ErrorHandler.class,
+                null);
+
+        if (eh != null) {
+            eh.setAppender(appender);
+
+            PropertySetter propSetter = new PropertySetter(eh);
+            forEachElement(element.getChildNodes(), (currentElement) -> {
+                String tagName = currentElement.getTagName();
+                if (tagName.equals(PARAM_TAG)) {
+                    setParameter(currentElement, propSetter);
+                }
+            });
+            propSetter.activate();
+            appender.setErrorHandler(eh);
+        }
+    }
+
+    /**
+     * 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) {
+            PropertySetter propSetter = new PropertySetter(filter);
+            forEachElement(filterElement.getChildNodes(), (currentElement) -> {
+                String tagName = currentElement.getTagName();
+                if (tagName.equals(PARAM_TAG)) {
+                    setParameter(currentElement, propSetter);
+                } else {
+                    quietParseUnrecognizedElement(filter, currentElement, props);
+                }
+            });
+            propSetter.activate();
+        }
+        return filter;
+    }
+
+    /**
+     * Used internally to parse an category element.
+     */
+    private void parseCategory(Element loggerElement) {
+        // Create a new org.apache.log4j.Category object from the <category> element.
+        String catName = subst(loggerElement.getAttribute(NAME_ATTR));
+        boolean additivity = OptionConverter.toBoolean(subst(loggerElement.getAttribute(ADDITIVITY_ATTR)), true);
+        LoggerConfig loggerConfig = configuration.getLogger(catName);
+        if (loggerConfig == null) {
+            loggerConfig = new LoggerConfig(catName, org.apache.logging.log4j.Level.ERROR, additivity);
+            configuration.addLogger(catName, loggerConfig);
+        } else {
+            loggerConfig.setAdditive(additivity);
+        }
+        parseChildrenOfLoggerElement(loggerElement, loggerConfig, false);
+    }
+
+    /**
+     * Used internally to parse the roor category element.
+     */
+    private void parseRoot(Element rootElement) {
+        LoggerConfig root = configuration.getRootLogger();
+        parseChildrenOfLoggerElement(rootElement, root, true);
+    }
+
+    /**
+     * Used internally to parse the children of a LoggerConfig element.
+     */
+    private void parseChildrenOfLoggerElement(Element catElement, LoggerConfig loggerConfig, boolean isRoot) {
+
+        final PropertySetter propSetter = new PropertySetter(loggerConfig);
+        loggerConfig.getAppenderRefs().clear();
+        forEachElement(catElement.getChildNodes(), (currentElement) -> {
+            switch (currentElement.getTagName()) {
+                case APPENDER_REF_TAG: {
+                    Appender appender = findAppenderByReference(currentElement);
+                    String refName = subst(currentElement.getAttribute(REF_ATTR));
+                    if (appender != null) {
+                        LOGGER.debug("Adding appender named [{}] to loggerConfig [{}].", refName,
+                                loggerConfig.getName());
+                        loggerConfig.addAppender(configuration.getAppender(refName), null, null);
+                    } else {
+                        LOGGER.debug("Appender named [{}}] not found.", refName);
+                    }
+                    break;
+                }
+                case LEVEL_TAG: case PRIORITY_TAG: {
+                    parseLevel(currentElement, loggerConfig, isRoot);
+                    break;
+                }
+                case PARAM_TAG: {
+                    setParameter(currentElement, propSetter);
+                    break;
+                }
+                default: {
+                    quietParseUnrecognizedElement(loggerConfig, currentElement, props);
+                }
+            }
+        });
+        propSetter.activate();
+    }
+
+    /**
+     * Used internally to parse a layout element.
+     */
+    public Layout parseLayout(Element layoutElement) {
+        String className = subst(layoutElement.getAttribute(CLASS_ATTR));
+        LOGGER.debug("Parsing layout of class: \"{}\"", className);
+        Layout layout = manager.parseLayout(className, layoutElement, this);
+        if (layout == null) {
+            layout = buildLayout(className, layoutElement);
+        }
+        return layout;
+    }
+
+    private Layout buildLayout(String className, Element layout_element) {
+        try {
+            Layout layout = LoaderUtil.newInstanceOf(className);
+            PropertySetter propSetter = new PropertySetter(layout);
+            forEachElement(layout_element.getChildNodes(), (currentElement) -> {
+                String tagName = currentElement.getTagName();
+                if (tagName.equals(PARAM_TAG)) {
+                    setParameter(currentElement, propSetter);
+                } else {
+                    try {
+                        parseUnrecognizedElement(layout, currentElement, props);
+                    } catch (Exception ex) {
+                        throw new ConsumerException(ex);
+                    }
+                }
+            });
+
+            propSetter.activate();
+            return layout;
+        } catch (ConsumerException ce) {
+            Throwable cause = ce.getCause();
+            if (cause instanceof InterruptedException || cause instanceof InterruptedIOException) {
+                Thread.currentThread().interrupt();
+            }
+            LOGGER.error("Could not create the Layout. Reported error follows.", cause);
+        } catch (Exception oops) {
+            if (oops instanceof InterruptedException || oops instanceof InterruptedIOException) {
+                Thread.currentThread().interrupt();
+            }
+            LOGGER.error("Could not create the Layout. Reported error follows.", oops);
+        }
+        return null;
+    }
+
+    /**
+     * Used internally to parse a level  element.
+     */
+    private void parseLevel(Element element, LoggerConfig logger, boolean isRoot) {
+        String catName = logger.getName();
+        if (isRoot) {
+            catName = "root";
+        }
+
+        String priStr = subst(element.getAttribute(VALUE_ATTR));
+        LOGGER.debug("Level value for {} is [{}}].", catName, priStr);
+
+        if (INHERITED.equalsIgnoreCase(priStr) || NULL.equalsIgnoreCase(priStr)) {
+            if (isRoot) {
+                LOGGER.error("Root level cannot be inherited. Ignoring directive.");
+            } else {
+                logger.setLevel(null);
+            }
+        } else {
+            String className = subst(element.getAttribute(CLASS_ATTR));
+            if (EMPTY_STR.equals(className)) {
+                logger.setLevel(convertLevel(OptionConverter.toLevel(priStr, Level.DEBUG)));
+            } else {
+                LOGGER.debug("Desired Level sub-class: [{}]", className);
+                try {
+                    Class<?> clazz = LoaderUtil.loadClass(className);
+                    Method toLevelMethod = clazz.getMethod("toLevel", ONE_STRING_PARAM);
+                    Level pri = (Level) toLevelMethod.invoke(null, new Object[]{priStr});
+                    logger.setLevel(convertLevel(pri));
+                } catch (Exception oops) {
+                    if (oops instanceof InterruptedException || oops instanceof InterruptedIOException) {
+                        Thread.currentThread().interrupt();
+                    }
+                    LOGGER.error("Could not create level [" + priStr +
+                            "]. Reported error follows.", oops);
+                    return;
+                }
+            }
+        }
+        LOGGER.debug("{} level set to {}", catName,  logger.getLevel());
+    }
+
+    private void setParameter(Element elem, PropertySetter propSetter) {
+        String name = subst(elem.getAttribute(NAME_ATTR));
+        String value = (elem.getAttribute(VALUE_ATTR));
+        value = subst(OptionConverter.convertSpecialChars(value));
+        propSetter.setProperty(name, value);
+    }
+
+    /**
+     * Configure log4j by reading in a log4j.dtd compliant XML
+     * configuration file.
+     */
+    private void doConfigure() throws FactoryConfigurationError {
+        ConfigurationSource source = configuration.getConfigurationSource();
+        ParseAction action = new ParseAction() {
+            public Document parse(final DocumentBuilder parser) throws SAXException, IOException {
+                InputSource inputSource = new InputSource(source.getInputStream());
+                inputSource.setSystemId("dummy://log4j.dtd");
+                return parser.parse(inputSource);
+            }
+
+            public String toString() {
+                return configuration.getConfigurationSource().getLocation();
+            }
+        };
+        doConfigure(action);
+    }
+
+    private void doConfigure(final ParseAction action) throws FactoryConfigurationError {
+        DocumentBuilderFactory dbf;
+        try {
+            LOGGER.debug("System property is : {}", OptionConverter.getSystemProperty(dbfKey, null));
+            dbf = DocumentBuilderFactory.newInstance();
+            LOGGER.debug("Standard DocumentBuilderFactory search succeded.");
+            LOGGER.debug("DocumentBuilderFactory is: " + dbf.getClass().getName());
+        } catch (FactoryConfigurationError fce) {
+            Exception e = fce.getException();
+            LOGGER.debug("Could not instantiate a DocumentBuilderFactory.", e);
+            throw fce;
+        }
+
+        try {
+            dbf.setValidating(true);
+
+            DocumentBuilder docBuilder = dbf.newDocumentBuilder();
+
+            docBuilder.setErrorHandler(new SAXErrorHandler());
+            docBuilder.setEntityResolver(new Log4jEntityResolver());
+
+            Document doc = action.parse(docBuilder);
+            parse(doc.getDocumentElement());
+        } catch (Exception e) {
+            if (e instanceof InterruptedException || e instanceof InterruptedIOException) {
+                Thread.currentThread().interrupt();
+            }
+            // I know this is miserable...
+            LOGGER.error("Could not parse " + action.toString() + ".", e);
+        }
+    }
+
+    @Override
+    public void doConfigure(InputStream inputStream, LoggerContext loggerContext) {
+        try {
+            ConfigurationSource source = new ConfigurationSource(inputStream);
+            configuration = new Log4j1Configuration(loggerContext, source, 0);
+            doConfigure();
+        } catch (IOException ioe) {
+            LOGGER.error("Unable to process configuration due to {}",  ioe.getMessage());
+        }
+    }
+
+    @Override
+    public void doConfigure(URL url, LoggerContext loggerContext) {
+        try {
+            ConfigurationSource source = new ConfigurationSource(url.openStream(), url);
+            configuration = new Log4j1Configuration(loggerContext, source, 0);
+            doConfigure();
+        } catch (IOException ioe) {
+            LOGGER.error("Unable to process configuration due to {}",  ioe.getMessage());
+        }
+    }
+
+    /**
+     * Used internally to configure the log4j framework by parsing a DOM
+     * tree of XML elements based on <a
+     * href="doc-files/log4j.dtd">log4j.dtd</a>.
+     */
+    private void parse(Element element) {
+        String rootElementName = element.getTagName();
+
+        if (!rootElementName.equals(CONFIGURATION_TAG)) {
+            if (rootElementName.equals(OLD_CONFIGURATION_TAG)) {
+                LOGGER.warn("The <" + OLD_CONFIGURATION_TAG +
+                        "> element has been deprecated.");
+                LOGGER.warn("Use the <" + CONFIGURATION_TAG + "> element instead.");
+            } else {
+                LOGGER.error("DOM element is - not a <" + CONFIGURATION_TAG + "> element.");
+                return;
+            }
+        }
+
+
+        String debugAttrib = subst(element.getAttribute(INTERNAL_DEBUG_ATTR));
+
+        LOGGER.debug("debug attribute= \"" + debugAttrib + "\".");
+        // if the log4j.dtd is not specified in the XML file, then the
+        // "debug" attribute is returned as the empty string.
+        String status = "error";
+        if (!debugAttrib.equals("") && !debugAttrib.equals("null")) {
+            status = OptionConverter.toBoolean(debugAttrib, true) ? "debug" : "error";
+
+        } else {
+            LOGGER.debug("Ignoring " + INTERNAL_DEBUG_ATTR + " attribute.");
+        }
+
+        String confDebug = subst(element.getAttribute(CONFIG_DEBUG_ATTR));
+        if (!confDebug.equals("") && !confDebug.equals("null")) {
+            LOGGER.warn("The \"" + CONFIG_DEBUG_ATTR + "\" attribute is deprecated.");
+            LOGGER.warn("Use the \"" + INTERNAL_DEBUG_ATTR + "\" attribute instead.");
+            status = OptionConverter.toBoolean(confDebug, true) ? "debug" : "error";
+        }
+
+        final StatusConfiguration statusConfig = new StatusConfiguration().withStatus(status);
+        statusConfig.initialize();
+
+        forEachElement(element.getChildNodes(), (currentElement) -> {
+            switch (currentElement.getTagName()) {
+                case CATEGORY: case LOGGER_ELEMENT:
+                    parseCategory(currentElement);
+                    break;
+                case ROOT_TAG:
+                    parseRoot(currentElement);
+                    break;
+                case RENDERER_TAG:
+                    LOGGER.warn("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.");
+                    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.");
+                    break;
+                case APPENDER_TAG:
+                    Appender appender = parseAppender(currentElement);
+                    appenderBag.put(appender.getName(), appender);
+                    if (appender instanceof AppenderWrapper) {
+                        configuration.addAppender(((AppenderWrapper) appender).getAppender());
+                    } else {
+                        configuration.addAppender(new AppenderAdapter(appender).getAdapter());
+                    }
+                    break;
+                default:
+                    quietParseUnrecognizedElement(null, currentElement, props);
+            }
+        });
+    }
+
+    private org.apache.logging.log4j.Level convertLevel(Level level) {
+        if (level == null) {
+            return org.apache.logging.log4j.Level.ERROR;
+        }
+        if (level.isGreaterOrEqual(Level.FATAL)) {
+            return org.apache.logging.log4j.Level.FATAL;
+        } else if (level.isGreaterOrEqual(Level.ERROR)) {
+            return org.apache.logging.log4j.Level.ERROR;
+        } else if (level.isGreaterOrEqual(Level.WARN)) {
+            return org.apache.logging.log4j.Level.WARN;
+        } else if (level.isGreaterOrEqual(Level.INFO)) {
+            return org.apache.logging.log4j.Level.INFO;
+        } else if (level.isGreaterOrEqual(Level.DEBUG)) {
+            return org.apache.logging.log4j.Level.DEBUG;
+        } else if (level.isGreaterOrEqual(Level.TRACE)) {
+            return org.apache.logging.log4j.Level.TRACE;
+        }
+        return org.apache.logging.log4j.Level.ALL;
+    }
+
+    private String subst(final String value) {
+        return configuration.getStrSubstitutor().replace(value);
+    }
+
+    public static void forEachElement(NodeList list, Consumer<Element> consumer) {
+        final int length = list.getLength();
+        for (int loop = 0; loop < length; loop++) {
+            Node currentNode = list.item(loop);
+
+            if (currentNode.getNodeType() == Node.ELEMENT_NODE) {
+                Element currentElement = (Element) currentNode;
+                consumer.accept(currentElement);
+            }
+        }
+    }
+
+    private interface ParseAction {
+        Document parse(final DocumentBuilder parser) throws SAXException, IOException;
+    }
+
+    private static class SAXErrorHandler implements org.xml.sax.ErrorHandler {
+        private static final org.apache.logging.log4j.Logger LOGGER = StatusLogger.getLogger();
+
+        public void error(final SAXParseException ex) {
+            emitMessage("Continuable parsing error ", ex);
+        }
+
+        public void fatalError(final SAXParseException ex) {
+            emitMessage("Fatal parsing error ", ex);
+        }
+
+        public void warning(final SAXParseException ex) {
+            emitMessage("Parsing warning ", ex);
+        }
+
+        private static void emitMessage(final String msg, final SAXParseException ex) {
+            LOGGER.warn("{} {} and column {}", msg, ex.getLineNumber(), ex.getColumnNumber());
+            LOGGER.warn(ex.getMessage(), ex.getException());
+        }
+    }
+
+    private static class ConsumerException extends RuntimeException {
+
+        ConsumerException(Exception ex) {
+            super(ex);
+        }
+    }
+}
+
diff --git a/log4j-1.2-api/src/src/main/java/org/apache/log4j/xml/package-info.java b/log4j-1.2-api/src/src/main/java/org/apache/log4j/xml/package-info.java
new file mode 100644
index 0000000..e3ed0d1
--- /dev/null
+++ b/log4j-1.2-api/src/src/main/java/org/apache/log4j/xml/package-info.java
@@ -0,0 +1,20 @@
+/*
+ * 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 1.x compatibility layer.
+ */
+package org.apache.log4j.xml;
diff --git a/log4j-1.2-api/src/src/main/resources/org/apache/log4j/xml/log4j.dtd b/log4j-1.2-api/src/src/main/resources/org/apache/log4j/xml/log4j.dtd
new file mode 100644
index 0000000..f8e433a
--- /dev/null
+++ b/log4j-1.2-api/src/src/main/resources/org/apache/log4j/xml/log4j.dtd
@@ -0,0 +1,237 @@
+<?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.
+-->
+
+<!-- Authors: Chris Taylor, Ceki Gulcu. -->
+
+<!-- Version: 1.2 -->
+
+<!-- A configuration element consists of optional renderer
+elements,appender elements, categories and an optional root
+element. -->
+
+<!ELEMENT log4j:configuration (renderer*, throwableRenderer?,
+                               appender*,plugin*, (category|logger)*,root?,
+                               (categoryFactory|loggerFactory)?)>
+
+<!-- The "threshold" attribute takes a level value below which -->
+<!-- all logging statements are disabled. -->
+
+<!-- Setting the "debug" enable the printing of internal log4j logging   -->
+<!-- statements.                                                         -->
+
+<!-- By default, debug attribute is "null", meaning that we not do touch -->
+<!-- internal log4j logging settings. The "null" value for the threshold -->
+<!-- attribute can be misleading. The threshold field of a repository	 -->
+<!-- cannot be set to null. The "null" value for the threshold attribute -->
+<!-- simply means don't touch the threshold field, the threshold field   --> 
+<!-- keeps its old value.                                                -->
+     
+<!ATTLIST log4j:configuration
+  xmlns:log4j              CDATA #FIXED "http://jakarta.apache.org/log4j/" 
+  threshold                (all|trace|debug|info|warn|error|fatal|off|null) "null"
+  debug                    (true|false|null)  "null"
+  reset                    (true|false) "false"
+>
+
+<!-- renderer elements allow the user to customize the conversion of  -->
+<!-- message objects to String.                                       -->
+
+<!ELEMENT renderer EMPTY>
+<!ATTLIST renderer
+  renderedClass  CDATA #REQUIRED
+  renderingClass CDATA #REQUIRED
+>
+
+<!--  throwableRenderer allows the user to customize the conversion
+         of exceptions to a string representation.  -->
+<!ELEMENT throwableRenderer (param*)>
+<!ATTLIST throwableRenderer
+  class  CDATA #REQUIRED
+>
+
+
+<!-- Appenders must have a name and a class. -->
+<!-- Appenders may contain an error handler, a layout, optional parameters -->
+<!-- and filters. They may also reference (or include) other appenders. -->
+<!ELEMENT appender (errorHandler?, param*,
+      rollingPolicy?, triggeringPolicy?, connectionSource?,
+      layout?, filter*, appender-ref*)>
+<!ATTLIST appender
+  name 		CDATA 	#REQUIRED
+  class 	CDATA	#REQUIRED
+>
+
+<!ELEMENT layout (param*)>
+<!ATTLIST layout
+  class		CDATA	#REQUIRED
+>
+
+<!ELEMENT filter (param*)>
+<!ATTLIST filter
+  class		CDATA	#REQUIRED
+>
+
+<!-- ErrorHandlers can be of any class. They can admit any number of -->
+<!-- parameters. -->
+
+<!ELEMENT errorHandler (param*, root-ref?, logger-ref*,  appender-ref?)> 
+<!ATTLIST errorHandler
+   class        CDATA   #REQUIRED 
+>
+
+<!ELEMENT root-ref EMPTY>
+
+<!ELEMENT logger-ref EMPTY>
+<!ATTLIST logger-ref
+  ref CDATA #REQUIRED
+>
+
+<!ELEMENT param EMPTY>
+<!ATTLIST param
+  name		CDATA   #REQUIRED
+  value		CDATA	#REQUIRED
+>
+
+
+<!-- The priority class is org.apache.log4j.Level by default -->
+<!ELEMENT priority (param*)>
+<!ATTLIST priority
+  class   CDATA	#IMPLIED
+  value	  CDATA #REQUIRED
+>
+
+<!-- The level class is org.apache.log4j.Level by default -->
+<!ELEMENT level (param*)>
+<!ATTLIST level
+  class   CDATA	#IMPLIED
+  value	  CDATA #REQUIRED
+>
+
+
+<!-- If no level element is specified, then the configurator MUST not -->
+<!-- touch the level of the named category. -->
+<!ELEMENT category (param*,(priority|level)?,appender-ref*)>
+<!ATTLIST category
+  class         CDATA   #IMPLIED
+  name		CDATA	#REQUIRED
+  additivity	(true|false) "true"  
+>
+
+<!-- If no level element is specified, then the configurator MUST not -->
+<!-- touch the level of the named logger. -->
+<!ELEMENT logger (param*,level?,appender-ref*)>
+<!ATTLIST logger
+  class         CDATA   #IMPLIED
+  name		CDATA	#REQUIRED
+  additivity	(true|false) "true"  
+>
+
+
+<!ELEMENT categoryFactory (param*)>
+<!ATTLIST categoryFactory 
+   class        CDATA #REQUIRED>
+
+<!ELEMENT loggerFactory (param*)>
+<!ATTLIST loggerFactory
+   class        CDATA #REQUIRED>
+
+<!ELEMENT appender-ref EMPTY>
+<!ATTLIST appender-ref
+  ref CDATA #REQUIRED
+>
+
+<!-- plugins must have a name and class and can have optional parameters -->
+<!ELEMENT plugin (param*, connectionSource?)>
+<!ATTLIST plugin
+  name 		CDATA 	   #REQUIRED
+  class 	CDATA  #REQUIRED
+>
+
+<!ELEMENT connectionSource (dataSource?, param*)>
+<!ATTLIST connectionSource
+  class        CDATA  #REQUIRED
+>
+
+<!ELEMENT dataSource (param*)>
+<!ATTLIST dataSource
+  class        CDATA  #REQUIRED
+>
+
+<!ELEMENT triggeringPolicy ((param|filter)*)>
+<!ATTLIST triggeringPolicy
+  name 		CDATA  #IMPLIED
+  class 	CDATA  #REQUIRED
+>
+
+<!ELEMENT rollingPolicy (param*)>
+<!ATTLIST rollingPolicy
+  name 		CDATA  #IMPLIED
+  class 	CDATA  #REQUIRED
+>
+
+
+<!-- If no priority element is specified, then the configurator MUST not -->
+<!-- touch the priority of root. -->
+<!-- The root category always exists and cannot be subclassed. -->
+<!ELEMENT root (param*, (priority|level)?, appender-ref*)>
+
+
+<!-- ==================================================================== -->
+<!--                       A logging event                                -->
+<!-- ==================================================================== -->
+<!ELEMENT log4j:eventSet (log4j:event*)>
+<!ATTLIST log4j:eventSet
+  xmlns:log4j             CDATA #FIXED "http://jakarta.apache.org/log4j/" 
+  version                (1.1|1.2) "1.2" 
+  includesLocationInfo   (true|false) "true"
+>
+
+
+
+<!ELEMENT log4j:event (log4j:message, log4j:NDC?, log4j:throwable?, 
+                       log4j:locationInfo?, log4j:properties?) >
+
+<!-- The timestamp format is application dependent. -->
+<!ATTLIST log4j:event
+    logger     CDATA #REQUIRED
+    level      CDATA #REQUIRED
+    thread     CDATA #REQUIRED
+    timestamp  CDATA #REQUIRED
+    time       CDATA #IMPLIED
+>
+
+<!ELEMENT log4j:message (#PCDATA)>
+<!ELEMENT log4j:NDC (#PCDATA)>
+
+<!ELEMENT log4j:throwable (#PCDATA)>
+
+<!ELEMENT log4j:locationInfo EMPTY>
+<!ATTLIST log4j:locationInfo
+  class  CDATA	#REQUIRED
+  method CDATA	#REQUIRED
+  file   CDATA	#REQUIRED
+  line   CDATA	#REQUIRED
+>
+
+<!ELEMENT log4j:properties (log4j:data*)>
+
+<!ELEMENT log4j:data EMPTY>
+<!ATTLIST log4j:data
+  name   CDATA	#REQUIRED
+  value  CDATA	#REQUIRED
+>
diff --git a/log4j-1.2-api/src/src/site/markdown/index.md b/log4j-1.2-api/src/src/site/markdown/index.md
new file mode 100644
index 0000000..696e0bb
--- /dev/null
+++ b/log4j-1.2-api/src/src/site/markdown/index.md
@@ -0,0 +1,48 @@
+<!-- vim: set syn=markdown : -->
+<!--
+    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 1.2 Bridge
+
+The Log4j 1.2 Bridge allows applications coded to use Log4j 1.2 API to use Log4j 2 instead.
+
+## Requirements
+
+The Log4j 1.2 bridge is dependent on the Log4j 2 API. The following Log4j 1.x methods will behave differently when
+the Log4j 2 Core module is included then when it is not:
+
+| Method                        | Without log4j-core | With log4j-core                      |
+| ----------------------------- | ------------------ | ------------------------------------ |
+| Category.getParent()          | Returns null       | Returns parent logger                |
+| Category.setLevel()           | NoOp               | Sets Logger Level                    |
+| Category.setPriority()        | NoOp               | Sets Logger Level                    | 
+| Category.getAdditivity()      | Returns false      | Returns Logger's additivity setting  | 
+| Category.setAdditivity()      | NoOp               | Sets additivity of LoggerConfig      |
+| Category.getResourceBundle()  | NoOp               | Returns the resource bundle associated with the Logger |
+| BasicConfigurator.configure() | NoOp               | Reconfigures Log4j 2                 |
+
+If log4j-core is not present location information will not be accurate in calls using the Log4j 1.2 API. The config
+package which attempts tp convert Log4j 1.x configurations to Log4j 2 is not supported without Log4j 2.    
+
+For more information, see [Runtime Dependencies](../runtime-dependencies.html).
+
+## Usage
+
+To use the Log4j Legacy Bridge just remove all the Log4j 1.x jars from the application and replace them
+with the bridge jar. Once in place all logging that uses Log4j 1.x will be routed to Log4j 2. However,
+applications that attempt to modify legacy Log4j by adding Appenders, Filters, etc may experience problems
+if they try to verify the success of these actions as these methods are largely no-ops.
diff --git a/log4j-1.2-api/src/src/site/site.xml b/log4j-1.2-api/src/src/site/site.xml
new file mode 100644
index 0000000..b27991c
--- /dev/null
+++ b/log4j-1.2-api/src/src/site/site.xml
@@ -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.
+
+-->
+<project name="Log4j 1.x Adaptor"
+         xmlns="http://maven.apache.org/DECORATION/1.4.0"
+         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+         xsi:schemaLocation="http://maven.apache.org/DECORATION/1.4.0 http://maven.apache.org/xsd/decoration-1.4.0.xsd">
+  <body>
+    <links>
+      <item name="Apache" href="http://www.apache.org/" />
+      <item name="Logging Services" href="http://logging.apache.org/"/>
+      <item name="Log4j" href="../index.html"/>
+    </links>
+
+    <!-- Component-specific reports -->
+    <menu ref="reports"/>
+
+	<!-- Overall Project Info -->
+    <menu name="Log4j Project Information" img="icon-info-sign">
+      <item name="Dependencies" href="../dependencies.html" />
+      <item name="Dependency Convergence" href="../dependency-convergence.html" />
+      <item name="Dependency Management" href="../dependency-management.html" />
+      <item name="Project Team" href="../team-list.html" />
+      <item name="Mailing Lists" href="../mail-lists.html" />
+      <item name="Issue Tracking" href="../issue-tracking.html" />
+      <item name="Project License" href="../license.html" />
+      <item name="Source Repository" href="../source-repository.html" />
+      <item name="Project Summary" href="../project-summary.html" />
+    </menu>
+
+    <menu name="Log4j Project Reports" img="icon-cog">
+      <item name="Changes Report" href="../changes-report.html" />
+      <item name="JIRA Report" href="../jira-report.html" />
+      <item name="Surefire Report" href="../surefire-report.html" />
+      <item name="RAT Report" href="../rat-report.html" />
+    </menu>
+  </body>
+</project>
diff --git a/log4j-1.2-api/src/src/test/java/org/apache/log4j/BasicConfigurationFactory.java b/log4j-1.2-api/src/src/test/java/org/apache/log4j/BasicConfigurationFactory.java
new file mode 100644
index 0000000..d231d82
--- /dev/null
+++ b/log4j-1.2-api/src/src/test/java/org/apache/log4j/BasicConfigurationFactory.java
@@ -0,0 +1,70 @@
+/*
+ * 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.net.URI;
+
+import org.apache.logging.log4j.Level;
+import org.apache.logging.log4j.core.LoggerContext;
+import org.apache.logging.log4j.core.config.AbstractConfiguration;
+import org.apache.logging.log4j.core.config.Configuration;
+import org.apache.logging.log4j.core.config.ConfigurationFactory;
+import org.apache.logging.log4j.core.config.ConfigurationSource;
+import org.apache.logging.log4j.core.config.LoggerConfig;
+
+/**
+ *
+ */
+public class BasicConfigurationFactory extends ConfigurationFactory {
+
+    @Override
+    public String[] getSupportedTypes() {
+        return new String[] { "*" };
+    }
+
+    @Override
+    public Configuration getConfiguration(final LoggerContext loggerContext, final ConfigurationSource source) {
+        return new BasicConfiguration(loggerContext);
+    }
+
+    @Override
+    public Configuration getConfiguration(final LoggerContext loggerContext, final String name, final URI configLocation) {
+        return new BasicConfiguration(loggerContext);
+    }
+
+    public class BasicConfiguration extends AbstractConfiguration {
+
+        private static final long serialVersionUID = -2716784321395089563L;
+
+        private static final String DEFAULT_LEVEL = "org.apache.logging.log4j.level";
+
+        public BasicConfiguration(final LoggerContext loggerContext) {
+            super(loggerContext, ConfigurationSource.NULL_SOURCE);
+
+            final LoggerConfig root = getRootLogger();
+            setName("BasicConfiguration");
+            final String levelName = System.getProperty(DEFAULT_LEVEL);
+            final Level level = (levelName != null && Level.getLevel(levelName) != null) ? Level.getLevel(levelName)
+                    : Level.DEBUG;
+            root.setLevel(level);
+        }
+
+        @Override
+        protected void doConfigure() {
+        }
+    }
+}
diff --git a/log4j-1.2-api/src/src/test/java/org/apache/log4j/CallerInformationTest.java b/log4j-1.2-api/src/src/test/java/org/apache/log4j/CallerInformationTest.java
new file mode 100644
index 0000000..1aa6c31
--- /dev/null
+++ b/log4j-1.2-api/src/src/test/java/org/apache/log4j/CallerInformationTest.java
@@ -0,0 +1,65 @@
+/*
+ * 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 static org.junit.Assert.assertEquals;
+
+import java.util.List;
+
+import org.apache.logging.log4j.junit.LoggerContextRule;
+import org.apache.logging.log4j.test.appender.ListAppender;
+import org.junit.ClassRule;
+import org.junit.Test;
+
+public class CallerInformationTest {
+
+    // config from log4j-core test-jar
+    private static final String CONFIG = "log4j2-calling-class.xml";
+
+    @ClassRule
+    public static final LoggerContextRule ctx = new LoggerContextRule(CONFIG);
+
+    @Test
+    public void testClassLogger() throws Exception {
+        final ListAppender app = ctx.getListAppender("Class").clear();
+        final Logger logger = Logger.getLogger("ClassLogger");
+        logger.info("Ignored message contents.");
+        logger.warn("Verifying the caller class is still correct.");
+        logger.error("Hopefully nobody breaks me!");
+        final List<String> messages = app.getMessages();
+        assertEquals("Incorrect number of messages.", 3, messages.size());
+        for (final String message : messages) {
+            assertEquals("Incorrect caller class name.", this.getClass().getName(), message);
+        }
+    }
+
+    @Test
+    public void testMethodLogger() throws Exception {
+        final ListAppender app = ctx.getListAppender("Method").clear();
+        final Logger logger = Logger.getLogger("MethodLogger");
+        logger.info("More messages.");
+        logger.warn("CATASTROPHE INCOMING!");
+        logger.error("ZOMBIES!!!");
+        logger.warn("brains~~~");
+        logger.info("Itchy. Tasty.");
+        final List<String> messages = app.getMessages();
+        assertEquals("Incorrect number of messages.", 5, messages.size());
+        for (final String message : messages) {
+            assertEquals("Incorrect caller method name.", "testMethodLogger", message);
+        }
+    }
+}
diff --git a/log4j-1.2-api/src/src/test/java/org/apache/log4j/CategoryTest.java b/log4j-1.2-api/src/src/test/java/org/apache/log4j/CategoryTest.java
new file mode 100644
index 0000000..6a8af76
--- /dev/null
+++ b/log4j-1.2-api/src/src/test/java/org/apache/log4j/CategoryTest.java
@@ -0,0 +1,209 @@
+/*
+ * 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 static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+
+import java.lang.reflect.Method;
+import java.util.List;
+
+import org.apache.logging.log4j.core.Layout;
+import org.apache.logging.log4j.core.LogEvent;
+import org.apache.logging.log4j.core.LoggerContext;
+import org.apache.logging.log4j.core.config.ConfigurationFactory;
+import org.apache.logging.log4j.core.layout.PatternLayout;
+import org.apache.logging.log4j.message.Message;
+import org.apache.logging.log4j.message.ObjectMessage;
+import org.apache.logging.log4j.test.appender.ListAppender;
+import org.apache.logging.log4j.util.Strings;
+import org.junit.AfterClass;
+import org.junit.Before;
+import org.junit.BeforeClass;
+import org.junit.Test;
+
+
+/**
+ * Tests of Category.
+ */
+public class CategoryTest {
+
+    static ConfigurationFactory cf = new BasicConfigurationFactory();
+
+    private static ListAppender appender = new ListAppender("List");
+
+    @BeforeClass
+    public static void setupClass() {
+        appender.start();
+        ConfigurationFactory.setConfigurationFactory(cf);
+        LoggerContext.getContext().reconfigure();
+    }
+
+    @AfterClass
+    public static void cleanupClass() {
+        ConfigurationFactory.removeConfigurationFactory(cf);
+        appender.stop();
+    }
+
+    @Before
+    public void before() {
+        appender.clear();
+    }
+    
+    /**
+     * Tests Category.forcedLog.
+     */
+    @Test
+    @SuppressWarnings("deprecation")
+    public void testForcedLog() {
+        final MockCategory category = new MockCategory("org.example.foo");
+        category.setAdditivity(false);
+        ((org.apache.logging.log4j.core.Logger) category.getLogger()).addAppender(appender);
+        category.info("Hello, World");
+        final List<LogEvent> list = appender.getEvents();
+        int events = list.size();
+        assertTrue("Number of events should be 1, was " + events, events == 1);
+        LogEvent event = list.get(0);
+        Message msg = event.getMessage();
+        assertNotNull("No message", msg);
+        assertTrue("Incorrect Message type", msg instanceof ObjectMessage);
+        Object[] objects = msg.getParameters();
+        assertTrue("Incorrect Object type", objects[0] instanceof String);
+        appender.clear();
+        category.log(Priority.INFO, "Hello, World");
+        events = list.size();
+        assertTrue("Number of events should be 1, was " + events, events == 1);
+        event = list.get(0);
+        msg = event.getMessage();
+        assertNotNull("No message", msg);
+        assertTrue("Incorrect Message type", msg instanceof ObjectMessage);
+        objects = msg.getParameters();
+        assertTrue("Incorrect Object type", objects[0] instanceof String);
+        appender.clear();
+    }
+
+    /**
+     * Tests that the return type of getChainedPriority is Priority.
+     *
+     * @throws Exception thrown if Category.getChainedPriority can not be found.
+     */
+    @Test
+    public void testGetChainedPriorityReturnType() throws Exception {
+        final Method method = Category.class.getMethod("getChainedPriority", (Class[]) null);
+        assertTrue(method.getReturnType() == Priority.class);
+    }
+
+    /**
+     * Tests l7dlog(Priority, String, Throwable).
+     */
+    @Test
+    public void testL7dlog() {
+        final Logger logger = Logger.getLogger("org.example.foo");
+        logger.setLevel(Level.ERROR);
+        final Priority debug = Level.DEBUG;
+        logger.l7dlog(debug, "Hello, World", null);
+        assertTrue(appender.getEvents().size() == 0);
+    }
+
+    /**
+     * Tests l7dlog(Priority, String, Object[], Throwable).
+     */
+    @Test
+    public void testL7dlog4Param() {
+        final Logger logger = Logger.getLogger("org.example.foo");
+        logger.setLevel(Level.ERROR);
+        final Priority debug = Level.DEBUG;
+        logger.l7dlog(debug, "Hello, World", new Object[0], null);
+        assertTrue(appender.getEvents().size() == 0);
+    }
+
+    /**
+     * Test using a pre-existing Log4j 2 logger
+     */
+    @Test
+    public void testExistingLog4j2Logger() {
+        // create the logger using LogManager
+        org.apache.logging.log4j.LogManager.getLogger("existingLogger");
+        // Logger will be the one created above
+        final Logger logger = Logger.getLogger("existingLogger");
+        final Logger l2 = LogManager.getLogger("existingLogger");
+        assertEquals(logger, l2);
+        logger.setLevel(Level.ERROR);
+        final Priority debug = Level.DEBUG;
+        // the next line will throw an exception if the LogManager loggers
+        // aren't supported by 1.2 Logger/Category
+        logger.l7dlog(debug, "Hello, World", new Object[0], null);
+        assertTrue(appender.getEvents().size() == 0);
+    }
+
+    /**
+     * Tests setPriority(Priority).
+     *
+     * @deprecated
+     */
+    @Deprecated
+    @Test
+    public void testSetPriority() {
+        final Logger logger = Logger.getLogger("org.example.foo");
+        final Priority debug = Level.DEBUG;
+        logger.setPriority(debug);
+    }
+
+    @Test
+    public void testClassName() {
+        final Category category = Category.getInstance("TestCategory");
+        final Layout<String> layout = PatternLayout.newBuilder().withPattern("%d %p %C{1.} [%t] %m%n").build();
+        final ListAppender appender = new ListAppender("List2", null, layout, false, false);
+        appender.start();
+        category.setAdditivity(false);
+        ((org.apache.logging.log4j.core.Logger) category.getLogger()).addAppender(appender);
+        category.error("Test Message");
+        final List<String> msgs = appender.getMessages();
+        assertTrue("Incorrect number of messages. Expected 1 got " + msgs.size(), msgs.size() == 1);
+        final String msg = msgs.get(0);
+        appender.clear();
+        final String threadName = Thread.currentThread().getName();
+        final String expected = "ERROR o.a.l.CategoryTest [" + threadName + "] Test Message" + Strings.LINE_SEPARATOR;
+        assertTrue("Incorrect message " + Strings.dquote(msg) + " expected " + Strings.dquote(expected), msg.endsWith(expected));
+    }
+
+    /**
+     * Derived category to check method signature of forcedLog.
+     */
+    private static class MockCategory extends Logger {
+        /**
+         * Create new instance of MockCategory.
+         *
+         * @param name category name
+         */
+        public MockCategory(final String name) {
+            super(name);
+        }
+
+        /**
+         * Request an info level message.
+         *
+         * @param msg message
+         */
+        public void info(final String msg) {
+            final Priority info = Level.INFO;
+            forcedLog(MockCategory.class.toString(), info, msg, null);
+        }
+    }
+}
diff --git a/log4j-1.2-api/src/src/test/java/org/apache/log4j/LevelTest.java b/log4j-1.2-api/src/src/test/java/org/apache/log4j/LevelTest.java
new file mode 100644
index 0000000..bb991ad
--- /dev/null
+++ b/log4j-1.2-api/src/src/test/java/org/apache/log4j/LevelTest.java
@@ -0,0 +1,284 @@
+/*
+ * 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.util.Locale;
+
+import org.apache.log4j.util.SerializationTestHelper;
+import org.junit.Test;
+
+import static org.junit.Assert.*;
+
+
+/**
+ * Tests of Level.
+ *
+ * @since 1.2.12
+ */
+public class LevelTest {
+
+    /**
+     * Serialize Level.INFO and check against witness.
+     *
+     * @throws Exception if exception during test.
+     */
+    @Test
+    public void testSerializeINFO() throws Exception {
+        final int[] skip = new int[]{};
+        SerializationTestHelper.assertSerializationEquals(
+            "target/test-classes/witness/serialization/info.bin",
+            Level.INFO, skip, Integer.MAX_VALUE);
+    }
+
+    /**
+     * Deserialize witness and see if resolved to Level.INFO.
+     *
+     * @throws Exception if exception during test.
+     */
+    @Test
+    public void testDeserializeINFO() throws Exception {
+        final Object obj =
+            SerializationTestHelper.deserializeStream(
+                "target/test-classes/witness/serialization/info.bin");
+        assertTrue(obj instanceof Level);
+        final Level info = (Level) obj;
+        assertEquals("INFO", info.toString());
+        //
+        //  JDK 1.1 doesn't support readResolve necessary for the assertion
+        if (!System.getProperty("java.version").startsWith("1.1.")) {
+            assertTrue(obj == Level.INFO);
+        }
+    }
+
+    /**
+     * Tests that a custom level can be serialized and deserialized
+     * and is not resolved to a stock level.
+     *
+     * @throws Exception if exception during test.
+     */
+    @Test
+    public void testCustomLevelSerialization() throws Exception {
+        final CustomLevel custom = new CustomLevel();
+        final Object obj = SerializationTestHelper.serializeClone(custom);
+        assertTrue(obj instanceof CustomLevel);
+
+        final CustomLevel clone = (CustomLevel) obj;
+        assertEquals(Level.INFO.level, clone.level);
+        assertEquals(Level.INFO.levelStr, clone.levelStr);
+        assertEquals(Level.INFO.syslogEquivalent, clone.syslogEquivalent);
+    }
+
+    /**
+     * Custom level to check that custom levels are
+     * serializable, but not resolved to a plain Level.
+     */
+    private static class CustomLevel extends Level {
+        /**
+         * Generated serial version ID.
+         */
+        private static final long serialVersionUID = -6931920872225831135L;
+
+        /**
+         * Create an instance of CustomLevel.
+         */
+        public CustomLevel() {
+            super(
+                Level.INFO.level, Level.INFO.levelStr, Level.INFO.syslogEquivalent);
+        }
+    }
+
+    /**
+     * Tests Level.TRACE_INT.
+     */
+    @Test
+    public void testTraceInt() {
+        assertEquals(5000, Level.TRACE_INT);
+    }
+
+    /**
+     * Tests Level.TRACE.
+     */
+    @Test
+    public void testTrace() {
+        assertEquals("TRACE", Level.TRACE.toString());
+        assertEquals(5000, Level.TRACE.toInt());
+        assertEquals(7, Level.TRACE.getSyslogEquivalent());
+    }
+
+    /**
+     * Tests Level.toLevel(Level.TRACE_INT).
+     */
+    @Test
+    public void testIntToTrace() {
+        final Level trace = Level.toLevel(5000);
+        assertEquals("TRACE", trace.toString());
+    }
+
+    /**
+     * Tests Level.toLevel("TRACE");
+     */
+    @Test
+    public void testStringToTrace() {
+        final Level trace = Level.toLevel("TRACE");
+        assertEquals("TRACE", trace.toString());
+    }
+
+    /**
+     * Tests that Level extends Priority.
+     */
+    @Test
+    public void testLevelExtendsPriority() {
+        assertTrue(Priority.class.isAssignableFrom(Level.class));
+    }
+
+    /**
+     * Tests Level.OFF.
+     */
+    @Test
+    public void testOFF() {
+        assertTrue(Level.OFF instanceof Level);
+    }
+
+    /**
+     * Tests Level.FATAL.
+     */
+    @Test
+    public void testFATAL() {
+        assertTrue(Level.FATAL instanceof Level);
+    }
+
+    /**
+     * Tests Level.ERROR.
+     */
+    @Test
+    public void testERROR() {
+        assertTrue(Level.ERROR instanceof Level);
+    }
+
+    /**
+     * Tests Level.WARN.
+     */
+    @Test
+    public void testWARN() {
+        assertTrue(Level.WARN instanceof Level);
+    }
+
+    /**
+     * Tests Level.INFO.
+     */
+    @Test
+    public void testINFO() {
+        assertTrue(Level.INFO instanceof Level);
+    }
+
+    /**
+     * Tests Level.DEBUG.
+     */
+    @Test
+    public void testDEBUG() {
+        assertTrue(Level.DEBUG instanceof Level);
+    }
+
+    /**
+     * Tests Level.TRACE.
+     */
+    @Test
+    public void testTRACE() {
+        assertTrue(Level.TRACE instanceof Level);
+    }
+
+    /**
+     * Tests Level.ALL.
+     */
+    @Test
+    public void testALL() {
+        assertTrue(Level.ALL instanceof Level);
+    }
+
+    /**
+     * Tests Level.toLevel(Level.All_INT).
+     */
+    @Test
+    public void testIntToAll() {
+        final Level level = Level.toLevel(Priority.ALL_INT);
+        assertEquals("ALL", level.toString());
+    }
+
+    /**
+     * Tests Level.toLevel(Level.FATAL_INT).
+     */
+    @Test
+    public void testIntToFatal() {
+        final Level level = Level.toLevel(Priority.FATAL_INT);
+        assertEquals("FATAL", level.toString());
+    }
+
+
+    /**
+     * Tests Level.toLevel(Level.OFF_INT).
+     */
+    @Test
+    public void testIntToOff() {
+        final Level level = Level.toLevel(Priority.OFF_INT);
+        assertEquals("OFF", level.toString());
+    }
+
+    /**
+     * Tests Level.toLevel(17, Level.FATAL).
+     */
+    @Test
+    public void testToLevelUnrecognizedInt() {
+        final Level level = Level.toLevel(17, Level.FATAL);
+        assertEquals("FATAL", level.toString());
+    }
+
+    /**
+     * Tests Level.toLevel(null, Level.FATAL).
+     */
+    @Test
+    public void testToLevelNull() {
+        final Level level = Level.toLevel(null, Level.FATAL);
+        assertEquals("FATAL", level.toString());
+    }
+
+    /**
+     * Test that dotless lower I + "nfo" is recognized as INFO.
+     */
+    @Test
+    public void testDotlessLowerI() {
+        final Level level = Level.toLevel("\u0131nfo");
+        assertEquals("INFO", level.toString());
+    }
+
+    /**
+     * Test that dotted lower I + "nfo" is recognized as INFO
+     * even in Turkish locale.
+     */
+    @Test
+    public void testDottedLowerI() {
+        final Locale defaultLocale = Locale.getDefault();
+        final Locale turkey = new Locale("tr", "TR");
+        Locale.setDefault(turkey);
+        final Level level = Level.toLevel("info");
+        Locale.setDefault(defaultLocale);
+        assertEquals("INFO", level.toString());
+    }
+
+
+}
+
diff --git a/log4j-1.2-api/src/src/test/java/org/apache/log4j/ListAppender.java b/log4j-1.2-api/src/src/test/java/org/apache/log4j/ListAppender.java
new file mode 100644
index 0000000..83c2758
--- /dev/null
+++ b/log4j-1.2-api/src/src/test/java/org/apache/log4j/ListAppender.java
@@ -0,0 +1,83 @@
+/*
+ * 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 java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+import java.util.concurrent.TimeUnit;
+
+/**
+ * Used to test Log4j 1 support.
+ */
+public class ListAppender extends AppenderSkeleton {
+    // Use Collections.synchronizedList rather than CopyOnWriteArrayList because we expect
+    // more frequent writes than reads.
+    final List<LoggingEvent> events = Collections.synchronizedList(new ArrayList<>());
+
+    private final List<String> messages = Collections.synchronizedList(new ArrayList<>());
+
+
+    private static final String WINDOWS_LINE_SEP = "\r\n";
+
+    @Override
+    protected void append(LoggingEvent event) {
+        Layout layout = getLayout();
+        if (layout != null) {
+            String result = layout.format(event);
+            if (result != null) {
+                messages.add(result);
+            }
+        } else {
+            events.add(event);
+        }
+    }
+
+    @Override
+    public void close() {
+
+    }
+
+    @Override
+    public boolean requiresLayout() {
+        return false;
+    }
+
+    /** Returns an immutable snapshot of captured log events */
+    public List<LoggingEvent> getEvents() {
+        return Collections.unmodifiableList(new ArrayList<>(events));
+    }
+
+    /** Returns an immutable snapshot of captured messages */
+    public List<String> getMessages() {
+        return Collections.unmodifiableList(new ArrayList<>(messages));
+    }
+
+    /**
+     * Polls the messages list for it to grow to a given minimum size at most timeout timeUnits and return a copy of
+     * what we have so far.
+     */
+    public List<String> getMessages(final int minSize, final long timeout, final TimeUnit timeUnit) throws InterruptedException {
+        final long endMillis = System.currentTimeMillis() + timeUnit.toMillis(timeout);
+        while (messages.size() < minSize && System.currentTimeMillis() < endMillis) {
+            Thread.sleep(100);
+        }
+        return getMessages();
+    }
+}
diff --git a/log4j-1.2-api/src/src/test/java/org/apache/log4j/LogWithMDCTest.java b/log4j-1.2-api/src/src/test/java/org/apache/log4j/LogWithMDCTest.java
new file mode 100644
index 0000000..997d745
--- /dev/null
+++ b/log4j-1.2-api/src/src/test/java/org/apache/log4j/LogWithMDCTest.java
@@ -0,0 +1,57 @@
+/*
+ * 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.logging.log4j.junit.LoggerContextRule;
+import org.apache.logging.log4j.test.appender.ListAppender;
+import org.junit.ClassRule;
+import org.junit.Test;
+
+import java.util.List;
+
+import static org.junit.Assert.*;
+
+/**
+ * Test logging with MDC values.
+ */
+public class LogWithMDCTest {
+
+    private static final String CONFIG = "logWithMDC.xml";
+
+    @ClassRule
+    public static final LoggerContextRule CTX = new LoggerContextRule(CONFIG);
+
+    @Test
+    public void testMDC() throws Exception {
+        MDC.put("Key1", "John");
+        MDC.put("Key2", "Smith");
+        try {
+            final Logger logger = Logger.getLogger("org.apache.test.logging");
+            logger.debug("This is a test");
+            final ListAppender listApp = (ListAppender) CTX.getAppender("List");
+            assertNotNull(listApp);
+            final List<String> msgs = listApp.getMessages();
+            assertNotNull("No messages received", msgs);
+            assertTrue(msgs.size() == 1);
+            assertTrue("Key1 is missing", msgs.get(0).contains("Key1=John"));
+            assertTrue("Key2 is missing", msgs.get(0).contains("Key2=Smith"));
+        } finally {
+            MDC.remove("Key1");
+            MDC.remove("Key2");
+        }
+    }
+}
diff --git a/log4j-1.2-api/src/src/test/java/org/apache/log4j/LogWithRouteTest.java b/log4j-1.2-api/src/src/test/java/org/apache/log4j/LogWithRouteTest.java
new file mode 100644
index 0000000..606e87b
--- /dev/null
+++ b/log4j-1.2-api/src/src/test/java/org/apache/log4j/LogWithRouteTest.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;
+
+import org.apache.logging.log4j.junit.LoggerContextRule;
+import org.apache.logging.log4j.test.appender.ListAppender;
+import org.junit.ClassRule;
+import org.junit.Test;
+
+import java.util.List;
+
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+
+/**
+ * Test passing MDC values to the Routing appender.
+ */
+public class LogWithRouteTest {
+
+    private static final String CONFIG = "log-RouteWithMDC.xml";
+
+    @ClassRule
+    public static final LoggerContextRule CTX = new LoggerContextRule(CONFIG);
+
+    @Test
+    public void testMDC() throws Exception {
+        MDC.put("Type", "Service");
+        MDC.put("Name", "John Smith");
+        try {
+            final Logger logger = Logger.getLogger("org.apache.test.logging");
+            logger.debug("This is a test");
+            final ListAppender listApp = (ListAppender) CTX.getAppender("List");
+            assertNotNull(listApp);
+            final List<String> msgs = listApp.getMessages();
+            assertNotNull("No messages received", msgs);
+            assertTrue(msgs.size() == 1);
+            assertTrue("Type is missing", msgs.get(0).contains("Type=Service"));
+            assertTrue("Name is missing", msgs.get(0).contains("Name=John Smith"));
+        } finally {
+            MDC.remove("Type");
+            MDC.remove("Name");
+        }
+    }
+}
diff --git a/log4j-1.2-api/src/src/test/java/org/apache/log4j/LoggerTest.java b/log4j-1.2-api/src/src/test/java/org/apache/log4j/LoggerTest.java
new file mode 100644
index 0000000..0c4308f
--- /dev/null
+++ b/log4j-1.2-api/src/src/test/java/org/apache/log4j/LoggerTest.java
@@ -0,0 +1,525 @@
+/*
+ * 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.util.List;
+import java.util.Locale;
+import java.util.ResourceBundle;
+
+import org.apache.logging.log4j.core.Appender;
+import org.apache.logging.log4j.core.LogEvent;
+import org.apache.logging.log4j.core.LoggerContext;
+import org.apache.logging.log4j.core.appender.AbstractAppender;
+import org.apache.logging.log4j.core.config.ConfigurationFactory;
+import org.apache.logging.log4j.core.config.Property;
+import org.apache.logging.log4j.core.layout.PatternLayout;
+import org.apache.logging.log4j.test.appender.ListAppender;
+import org.junit.After;
+import org.junit.AfterClass;
+import org.junit.BeforeClass;
+import org.junit.Test;
+
+import static org.junit.Assert.*;
+
+/**
+ * Used for internal unit testing the Logger class.
+ */
+public class LoggerTest {
+
+    Appender a1;
+    Appender a2;
+
+    static ResourceBundle rbUS;
+    static ResourceBundle rbFR;
+    static ResourceBundle rbCH;
+
+    // A short message.
+    static String MSG = "M";
+
+    static ConfigurationFactory configurationFactory = new BasicConfigurationFactory();
+
+    @BeforeClass
+    public static void setUpClass() {
+        rbUS = ResourceBundle.getBundle("L7D", new Locale("en", "US"));
+        assertNotNull(rbUS);
+
+        rbFR = ResourceBundle.getBundle("L7D", new Locale("fr", "FR"));
+        assertNotNull("Got a null resource bundle.", rbFR);
+
+        rbCH = ResourceBundle.getBundle("L7D", new Locale("fr", "CH"));
+        assertNotNull("Got a null resource bundle.", rbCH);
+
+        ConfigurationFactory.setConfigurationFactory(configurationFactory);
+    }
+
+    @AfterClass
+    public static void tearDownClass() {
+        ConfigurationFactory.removeConfigurationFactory(configurationFactory);
+    }
+
+    @After
+    public void tearDown() {
+        LoggerContext.getContext().reconfigure();
+        a1 = null;
+        a2 = null;
+    }
+
+    /**
+     * Add an appender and see if it can be retrieved.
+     *  Skipping this test as the Appender interface isn't compatible with legacy Log4j.
+    public void testAppender1() {
+        logger = Logger.getLogger("test");
+        a1 = new ListAppender("testAppender1");
+        logger.addAppender(a1);
+
+        Enumeration enumeration = logger.getAllAppenders();
+        Appender aHat = (Appender) enumeration.nextElement();
+        assertEquals(a1, aHat);
+    } */
+
+    /**
+     * Add an appender X, Y, remove X and check if Y is the only
+     * remaining appender.
+     * Skipping this test as the Appender interface isn't compatible with legacy Log4j.
+    public void testAppender2() {
+        a1 = new FileAppender();
+        a1.setName("testAppender2.1");
+        a2 = new FileAppender();
+        a2.setName("testAppender2.2");
+
+        logger = Logger.getLogger("test");
+        logger.addAppender(a1);
+        logger.addAppender(a2);
+        logger.removeAppender("testAppender2.1");
+        Enumeration enumeration = logger.getAllAppenders();
+        Appender aHat = (Appender) enumeration.nextElement();
+        assertEquals(a2, aHat);
+        assertTrue(!enumeration.hasMoreElements());
+    }  */
+
+    /**
+     * Test if logger a.b inherits its appender from a.
+     */
+    @Test
+    public void testAdditivity1() {
+        final Logger loggerA = Logger.getLogger("a");
+        final Logger loggerAB = Logger.getLogger("a.b");
+        final CountingAppender coutingAppender = new CountingAppender();
+        coutingAppender.start();
+        try {
+            ((org.apache.logging.log4j.core.Logger) loggerA.getLogger()).addAppender(coutingAppender);
+
+            assertEquals(0, coutingAppender.counter);
+            loggerAB.debug(MSG);
+            assertEquals(1, coutingAppender.counter);
+            loggerAB.info(MSG);
+            assertEquals(2, coutingAppender.counter);
+            loggerAB.warn(MSG);
+            assertEquals(3, coutingAppender.counter);
+            loggerAB.error(MSG);
+            assertEquals(4, coutingAppender.counter);
+            coutingAppender.stop();
+        } finally {
+            ((org.apache.logging.log4j.core.Logger) loggerA.getLogger()).removeAppender(coutingAppender);
+        }
+    }
+
+    /**
+     * Test multiple additivity.
+     */
+    @Test
+    public void testAdditivity2() {
+        final Logger a = Logger.getLogger("a");
+        final Logger ab = Logger.getLogger("a.b");
+        final Logger abc = Logger.getLogger("a.b.c");
+        final Logger x = Logger.getLogger("x");
+
+        final CountingAppender ca1 = new CountingAppender();
+        ca1.start();
+        final CountingAppender ca2 = new CountingAppender();
+        ca2.start();
+
+        try {
+            ((org.apache.logging.log4j.core.Logger) a.getLogger()).addAppender(ca1);
+            ((org.apache.logging.log4j.core.Logger) abc.getLogger()).addAppender(ca2);
+
+            assertEquals(ca1.counter, 0);
+            assertEquals(ca2.counter, 0);
+
+            ab.debug(MSG);
+            assertEquals(ca1.counter, 1);
+            assertEquals(ca2.counter, 0);
+
+            abc.debug(MSG);
+            assertEquals(ca1.counter, 2);
+            assertEquals(ca2.counter, 1);
+
+            x.debug(MSG);
+            assertEquals(ca1.counter, 2);
+            assertEquals(ca2.counter, 1);
+            ca1.stop();
+            ca2.stop();
+        } finally {
+            ((org.apache.logging.log4j.core.Logger) a.getLogger()).removeAppender(ca1);
+            ((org.apache.logging.log4j.core.Logger) abc.getLogger()).removeAppender(ca2);
+        }}
+
+    /**
+     * Test additivity flag.
+     */
+    @Test
+    public void testAdditivity3() {
+        final Logger root = Logger.getRootLogger();
+        final Logger a = Logger.getLogger("a");
+        final Logger ab = Logger.getLogger("a.b");
+        final Logger abc = Logger.getLogger("a.b.c");
+        Logger.getLogger("x");
+
+        final CountingAppender caRoot = new CountingAppender();
+        caRoot.start();
+        final CountingAppender caA = new CountingAppender();
+        caA.start();
+        final CountingAppender caABC = new CountingAppender();
+        caABC.start();
+        try {
+            ((org.apache.logging.log4j.core.Logger) root.getLogger()).addAppender(caRoot);
+            ((org.apache.logging.log4j.core.Logger) a.getLogger()).addAppender(caA);
+            ((org.apache.logging.log4j.core.Logger) abc.getLogger()).addAppender(caABC);
+
+            assertEquals(caRoot.counter, 0);
+            assertEquals(caA.counter, 0);
+            assertEquals(caABC.counter, 0);
+
+            ab.setAdditivity(false);
+
+            a.debug(MSG);
+            assertEquals(caRoot.counter, 1);
+            assertEquals(caA.counter, 1);
+            assertEquals(caABC.counter, 0);
+
+            ab.debug(MSG);
+            assertEquals(caRoot.counter, 1);
+            assertEquals(caA.counter, 1);
+            assertEquals(caABC.counter, 0);
+
+            abc.debug(MSG);
+            assertEquals(caRoot.counter, 1);
+            assertEquals(caA.counter, 1);
+            assertEquals(caABC.counter, 1);
+            caRoot.stop();
+            caA.stop();
+            caABC.stop();
+        } finally {
+            ((org.apache.logging.log4j.core.Logger) root.getLogger()).removeAppender(caRoot);
+            ((org.apache.logging.log4j.core.Logger) a.getLogger()).removeAppender(caA);
+            ((org.apache.logging.log4j.core.Logger) abc.getLogger()).removeAppender(caABC);
+        }}
+
+    /* Don't support getLoggerRepository
+    public void testDisable1() {
+        CountingAppender caRoot = new CountingAppender();
+        Logger root = Logger.getRootLogger();
+        root.getLogger().addAppender(caRoot);
+
+        LoggerRepository h = LogManager.getLoggerRepository();
+        //h.disableDebug();
+        h.setThreshold((Level) Level.INFO);
+        assertEquals(caRoot.counter, 0);
+
+        root.debug(MSG);
+        assertEquals(caRoot.counter, 0);
+        root.info(MSG);
+        assertEquals(caRoot.counter, 1);
+        root.log(Level.WARN, MSG);
+        assertEquals(caRoot.counter, 2);
+        root.warn(MSG);
+        assertEquals(caRoot.counter, 3);
+
+        //h.disableInfo();
+        h.setThreshold((Level) Level.WARN);
+        root.debug(MSG);
+        assertEquals(caRoot.counter, 3);
+        root.info(MSG);
+        assertEquals(caRoot.counter, 3);
+        root.log(Level.WARN, MSG);
+        assertEquals(caRoot.counter, 4);
+        root.error(MSG);
+        assertEquals(caRoot.counter, 5);
+        root.log(Level.ERROR, MSG);
+        assertEquals(caRoot.counter, 6);
+
+        //h.disableAll();
+        h.setThreshold(Level.OFF);
+        root.debug(MSG);
+        assertEquals(caRoot.counter, 6);
+        root.info(MSG);
+        assertEquals(caRoot.counter, 6);
+        root.log(Level.WARN, MSG);
+        assertEquals(caRoot.counter, 6);
+        root.error(MSG);
+        assertEquals(caRoot.counter, 6);
+        root.log(Level.FATAL, MSG);
+        assertEquals(caRoot.counter, 6);
+        root.log(Level.FATAL, MSG);
+        assertEquals(caRoot.counter, 6);
+
+        //h.disable(Level.FATAL);
+        h.setThreshold(Level.OFF);
+        root.debug(MSG);
+        assertEquals(caRoot.counter, 6);
+        root.info(MSG);
+        assertEquals(caRoot.counter, 6);
+        root.log(Level.WARN, MSG);
+        assertEquals(caRoot.counter, 6);
+        root.error(MSG);
+        assertEquals(caRoot.counter, 6);
+        root.log(Level.ERROR, MSG);
+        assertEquals(caRoot.counter, 6);
+        root.log(Level.FATAL, MSG);
+        assertEquals(caRoot.counter, 6);
+    }  */
+
+    @Test
+    public void testRB1() {
+        final Logger root = Logger.getRootLogger();
+        root.setResourceBundle(rbUS);
+        ResourceBundle t = root.getResourceBundle();
+        assertSame(t, rbUS);
+
+        final Logger x = Logger.getLogger("x");
+        final Logger x_y = Logger.getLogger("x.y");
+        final Logger x_y_z = Logger.getLogger("x.y.z");
+
+        t = x.getResourceBundle();
+        assertSame(t, rbUS);
+        t = x_y.getResourceBundle();
+        assertSame(t, rbUS);
+        t = x_y_z.getResourceBundle();
+        assertSame(t, rbUS);
+    }
+
+    @Test
+    public void testRB2() {
+        final Logger root = Logger.getRootLogger();
+        root.setResourceBundle(rbUS);
+        ResourceBundle t = root.getResourceBundle();
+        assertTrue(t == rbUS);
+
+        final Logger x = Logger.getLogger("x");
+        final Logger x_y = Logger.getLogger("x.y");
+        final Logger x_y_z = Logger.getLogger("x.y.z");
+
+        x_y.setResourceBundle(rbFR);
+        t = x.getResourceBundle();
+        assertSame(t, rbUS);
+        t = x_y.getResourceBundle();
+        assertSame(t, rbFR);
+        t = x_y_z.getResourceBundle();
+        assertSame(t, rbFR);
+    }
+
+    @Test
+    public void testRB3() {
+        final Logger root = Logger.getRootLogger();
+        root.setResourceBundle(rbUS);
+        ResourceBundle t = root.getResourceBundle();
+        assertTrue(t == rbUS);
+
+        final Logger x = Logger.getLogger("x");
+        final Logger x_y = Logger.getLogger("x.y");
+        final Logger x_y_z = Logger.getLogger("x.y.z");
+
+        x_y.setResourceBundle(rbFR);
+        x_y_z.setResourceBundle(rbCH);
+        t = x.getResourceBundle();
+        assertSame(t, rbUS);
+        t = x_y.getResourceBundle();
+        assertSame(t, rbFR);
+        t = x_y_z.getResourceBundle();
+        assertSame(t, rbCH);
+    }
+
+    @Test
+    public void testExists() {
+        final Logger a = Logger.getLogger("a");
+        final Logger a_b = Logger.getLogger("a.b");
+        final Logger a_b_c = Logger.getLogger("a.b.c");
+
+        Logger t;
+        t = LogManager.exists("xx");
+        assertNull(t);
+        t = LogManager.exists("a");
+        assertSame(a, t);
+        t = LogManager.exists("a.b");
+        assertSame(a_b, t);
+        t = LogManager.exists("a.b.c");
+        assertSame(a_b_c, t);
+    }
+    /* Don't support hierarchy
+    public void testHierarchy1() {
+        Hierarchy h = new Hierarchy(new RootLogger((Level) Level.ERROR));
+        Logger a0 = h.getLogger("a");
+        assertEquals("a", a0.getName());
+        assertNull(a0.getLevel());
+        assertSame(Level.ERROR, a0.getEffectiveLevel());
+
+        Logger a1 = h.getLogger("a");
+        assertSame(a0, a1);
+    } */
+
+    /**
+     * Tests logger.trace(Object).
+     */
+    @Test
+    public void testTrace() {
+        final ListAppender appender = new ListAppender("List");
+        appender.start();
+        final Logger root = Logger.getRootLogger();
+        ((org.apache.logging.log4j.core.Logger) root.getLogger()).addAppender(appender);
+        root.setLevel(Level.INFO);
+
+        final Logger tracer = Logger.getLogger("com.example.Tracer");
+        tracer.setLevel(Level.TRACE);
+
+        tracer.trace("Message 1");
+        root.trace("Discarded Message");
+        root.trace("Discarded Message");
+
+        final List<LogEvent> msgs = appender.getEvents();
+        assertEquals(1, msgs.size());
+        final LogEvent event = msgs.get(0);
+        assertEquals(org.apache.logging.log4j.Level.TRACE, event.getLevel());
+        assertEquals("Message 1", event.getMessage().getFormat());
+        appender.stop();
+        ((org.apache.logging.log4j.core.Logger) root.getLogger()).removeAppender(appender);
+    }
+
+    /**
+     * Tests logger.trace(Object, Exception).
+     */
+    @Test
+    public void testTraceWithException() {
+        final ListAppender appender = new ListAppender("List");
+        appender.start();
+        final Logger root = Logger.getRootLogger();
+        try {
+            ((org.apache.logging.log4j.core.Logger) root.getLogger()).addAppender(appender);
+            root.setLevel(Level.INFO);
+
+            final Logger tracer = Logger.getLogger("com.example.Tracer");
+            tracer.setLevel(Level.TRACE);
+            final NullPointerException ex = new NullPointerException();
+
+            tracer.trace("Message 1", ex);
+            root.trace("Discarded Message", ex);
+            root.trace("Discarded Message", ex);
+
+            final List<LogEvent> msgs = appender.getEvents();
+            assertEquals(1, msgs.size());
+            final LogEvent event = msgs.get(0);
+            assertEquals(org.apache.logging.log4j.Level.TRACE, event.getLevel());
+            assertEquals("Message 1", event.getMessage().getFormattedMessage());
+            appender.stop();
+        } finally {
+            ((org.apache.logging.log4j.core.Logger) root.getLogger()).removeAppender(appender);
+        }
+    }
+
+    /**
+     * Tests isTraceEnabled.
+     */
+    @Test
+    public void testIsTraceEnabled() {
+        final ListAppender appender = new ListAppender("List");
+        appender.start();
+        final Logger root = Logger.getRootLogger();
+        try {
+            ((org.apache.logging.log4j.core.Logger) root.getLogger()).addAppender(appender);
+            root.setLevel(Level.INFO);
+
+            final Logger tracer = Logger.getLogger("com.example.Tracer");
+            tracer.setLevel(Level.TRACE);
+
+            assertTrue(tracer.isTraceEnabled());
+            assertFalse(root.isTraceEnabled());
+            appender.stop();
+        } finally {
+            ((org.apache.logging.log4j.core.Logger) root.getLogger()).removeAppender(appender);
+        }
+    }
+
+    @Test
+    @SuppressWarnings("deprecation")
+    public void testLog() {
+        final PatternLayout layout = PatternLayout.newBuilder().withPattern("%d %C %L %m").build();
+        final ListAppender appender = new ListAppender("List", null, layout, false, false);
+        appender.start();
+        final Logger root = Logger.getRootLogger();
+        try {
+            ((org.apache.logging.log4j.core.Logger) root.getLogger()).addAppender(appender);
+            root.setLevel(Level.INFO);
+            final MyLogger log = new MyLogger(root);
+            log.logInfo("This is a test", null);
+            root.log(Priority.INFO, "Test msg2", null);
+            root.log(Priority.INFO, "Test msg3");
+            final List<String> msgs = appender.getMessages();
+            assertTrue("Incorrect number of messages", msgs.size() == 3);
+            final String msg = msgs.get(0);
+            assertTrue("Message contains incorrect class name: " + msg, msg.contains(LoggerTest.class.getName()));
+            appender.stop();
+        } finally {
+            ((org.apache.logging.log4j.core.Logger) root.getLogger()).removeAppender(appender);
+        }
+    }
+
+    private static class MyLogger {
+
+        private final Logger logger;
+
+        public MyLogger(final Logger logger) {
+            this.logger = logger;
+        }
+
+        @SuppressWarnings("deprecation")
+        public void logInfo(final String msg, final Throwable t) {
+            logger.log(MyLogger.class.getName(), Priority.INFO, msg, t);
+        }
+    }
+
+    private static class CountingAppender extends AbstractAppender {
+
+        private static final long serialVersionUID = 1L;
+
+        int counter;
+
+        CountingAppender() {
+            super("Counter", null, null, true, Property.EMPTY_ARRAY);
+            counter = 0;
+        }
+
+        @Override
+        public void append(final LogEvent event) {
+            counter++;
+        }
+
+        public boolean requiresLayout() {
+            return true;
+        }
+    }
+}
+
diff --git a/log4j-1.2-api/src/src/test/java/org/apache/log4j/LoggingTest.java b/log4j-1.2-api/src/src/test/java/org/apache/log4j/LoggingTest.java
new file mode 100644
index 0000000..4ec4fb8
--- /dev/null
+++ b/log4j-1.2-api/src/src/test/java/org/apache/log4j/LoggingTest.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;
+
+import org.apache.logging.log4j.junit.LoggerContextRule;
+import org.junit.ClassRule;
+import org.junit.Test;
+
+import static org.junit.Assert.*;
+
+/**
+ *
+ */
+public class LoggingTest {
+
+    private static final String CONFIG = "log4j2-config.xml";
+
+    @ClassRule
+    public static final LoggerContextRule CTX = new LoggerContextRule(CONFIG);
+
+    @Test
+    public void testParent() {
+        final Logger logger = Logger.getLogger("org.apache.test.logging.Test");
+        final Category parent = logger.getParent();
+        assertNotNull("No parent Logger", parent);
+        assertEquals("Incorrect parent logger", "org.apache.test.logging", parent.getName());
+    }
+
+}
diff --git a/log4j-1.2-api/src/src/test/java/org/apache/log4j/MDCTestCase.java b/log4j-1.2-api/src/src/test/java/org/apache/log4j/MDCTestCase.java
new file mode 100644
index 0000000..c0e5ba5
--- /dev/null
+++ b/log4j-1.2-api/src/src/test/java/org/apache/log4j/MDCTestCase.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.log4j;
+
+import org.junit.After;
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.Test;
+
+public class MDCTestCase {
+
+    @Before
+    public void setUp() {
+        MDC.clear();
+    }
+
+    @After
+    public void tearDown() {
+        MDC.clear();
+    }
+
+    @Test
+    public void testPut() throws Exception {
+        MDC.put("key", "some value");
+        Assert.assertEquals("some value", MDC.get("key"));
+        Assert.assertEquals(1, MDC.getContext().size());
+    }
+
+    @Test
+    public void testRemoveLastKey() throws Exception {
+        MDC.put("key", "some value");
+        MDC.remove("key");
+    }
+
+}
diff --git a/log4j-1.2-api/src/src/test/java/org/apache/log4j/NDCTest.java b/log4j-1.2-api/src/src/test/java/org/apache/log4j/NDCTest.java
new file mode 100644
index 0000000..c8baf0f
--- /dev/null
+++ b/log4j-1.2-api/src/src/test/java/org/apache/log4j/NDCTest.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;
+
+import org.apache.logging.log4j.util.Strings;
+import org.junit.Assert;
+import org.junit.Test;
+
+public class NDCTest {
+
+    @Test
+    public void testPopEmpty() {
+        NDC.clear();
+        Assert.assertEquals(Strings.EMPTY, NDC.pop());
+    }
+
+    @Test
+    public void testPeekEmpty() {
+        NDC.clear();
+        Assert.assertEquals(Strings.EMPTY, NDC.peek());
+    }
+}
diff --git a/log4j-1.2-api/src/src/test/java/org/apache/log4j/PriorityTest.java b/log4j-1.2-api/src/src/test/java/org/apache/log4j/PriorityTest.java
new file mode 100644
index 0000000..63321a7
--- /dev/null
+++ b/log4j-1.2-api/src/src/test/java/org/apache/log4j/PriorityTest.java
@@ -0,0 +1,226 @@
+/*
+ * 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.util.Locale;
+
+import org.junit.Test;
+
+import static org.junit.Assert.*;
+
+/**
+ * Tests of Priority.
+ *
+ */
+public class PriorityTest {
+
+    /**
+     * Tests Priority.OFF_INT.
+     */
+    @Test
+    public void testOffInt() {
+        assertEquals(Integer.MAX_VALUE, Priority.OFF_INT);
+    }
+
+    /**
+     * Tests Priority.FATAL_INT.
+     */
+    @Test
+    public void testFatalInt() {
+        assertEquals(50000, Priority.FATAL_INT);
+    }
+
+    /**
+     * Tests Priority.ERROR_INT.
+     */
+    @Test
+    public void testErrorInt() {
+        assertEquals(40000, Priority.ERROR_INT);
+    }
+
+    /**
+     * Tests Priority.WARN_INT.
+     */
+    @Test
+    public void testWarnInt() {
+        assertEquals(30000, Priority.WARN_INT);
+    }
+
+    /**
+     * Tests Priority.INFO_INT.
+     */
+    @Test
+    public void testInfoInt() {
+        assertEquals(20000, Priority.INFO_INT);
+    }
+
+    /**
+     * Tests Priority.DEBUG_INT.
+     */
+    @Test
+    public void testDebugInt() {
+        assertEquals(10000, Priority.DEBUG_INT);
+    }
+
+    /**
+     * Tests Priority.ALL_INT.
+     */
+    @Test
+    public void testAllInt() {
+        assertEquals(Integer.MIN_VALUE, Priority.ALL_INT);
+    }
+
+    /**
+     * Tests Priority.FATAL.
+     */
+    @Test
+    @SuppressWarnings("deprecation")
+    public void testFatal() {
+        assertTrue(Priority.FATAL instanceof Level);
+    }
+
+    /**
+     * Tests Priority.ERROR.
+     */
+    @Test
+    @SuppressWarnings("deprecation")
+    public void testERROR() {
+        assertTrue(Priority.ERROR instanceof Level);
+    }
+
+    /**
+     * Tests Priority.WARN.
+     */
+    @Test
+    @SuppressWarnings("deprecation")
+    public void testWARN() {
+        assertTrue(Priority.WARN instanceof Level);
+    }
+
+    /**
+     * Tests Priority.INFO.
+     */
+    @Test
+    @SuppressWarnings("deprecation")
+    public void testINFO() {
+        assertTrue(Priority.INFO instanceof Level);
+    }
+
+    /**
+     * Tests Priority.DEBUG.
+     */
+    @Test
+    @SuppressWarnings("deprecation")
+    public void testDEBUG() {
+        assertTrue(Priority.DEBUG instanceof Level);
+    }
+
+    /**
+     * Tests Priority.equals(null).
+     */
+    @Test
+    @SuppressWarnings("deprecation")
+    public void testEqualsNull() {
+        assertFalse(Priority.DEBUG.equals(null));
+    }
+
+    /**
+     * Tests Priority.equals(Level.DEBUG).
+     */
+    @Test
+    @SuppressWarnings("deprecation")
+    public void testEqualsLevel() {
+        //
+        //   this behavior violates the equals contract.
+        //
+        assertTrue(Priority.DEBUG.equals(Level.DEBUG));
+    }
+
+    /**
+     * Tests getAllPossiblePriorities().
+     */
+    @Test
+    @SuppressWarnings("deprecation")
+    public void testGetAllPossiblePriorities() {
+        final Priority[] priorities = Priority.getAllPossiblePriorities();
+        assertEquals(5, priorities.length);
+    }
+
+    /**
+     * Tests toPriority(String).
+     */
+    @Test
+    @SuppressWarnings("deprecation")
+    public void testToPriorityString() {
+        assertTrue(Priority.toPriority("DEBUG") == Level.DEBUG);
+    }
+
+    /**
+     * Tests toPriority(int).
+     */
+    @Test
+    @SuppressWarnings("deprecation")
+    public void testToPriorityInt() {
+        assertTrue(Priority.toPriority(Priority.DEBUG_INT) == Level.DEBUG);
+    }
+
+    /**
+     * Tests toPriority(String, Priority).
+     */
+    @Test
+    @SuppressWarnings("deprecation")
+    public void testToPriorityStringPriority() {
+        assertTrue(Priority.toPriority("foo", Priority.DEBUG) == Priority.DEBUG);
+    }
+
+    /**
+     * Tests toPriority(int, Priority).
+     */
+    @Test
+    @SuppressWarnings("deprecation")
+    public void testToPriorityIntPriority() {
+        assertTrue(Priority.toPriority(17, Priority.DEBUG) == Priority.DEBUG);
+    }
+
+    /**
+     * Test that dotless lower I + "nfo" is recognized as INFO.
+     */
+    @Test
+    @SuppressWarnings("deprecation")
+    public void testDotlessLowerI() {
+        final Priority level = Priority.toPriority("\u0131nfo");
+        assertEquals("INFO", level.toString());
+    }
+
+    /**
+     * Test that dotted lower I + "nfo" is recognized as INFO
+     * even in Turkish locale.
+     */
+    @Test
+    @SuppressWarnings("deprecation")
+    public void testDottedLowerI() {
+        final Locale defaultLocale = Locale.getDefault();
+        final Locale turkey = new Locale("tr", "TR");
+        Locale.setDefault(turkey);
+        final Priority level = Priority.toPriority("info");
+        Locale.setDefault(defaultLocale);
+        assertEquals("INFO", level.toString());
+  }
+
+}
+
diff --git a/log4j-1.2-api/src/src/test/java/org/apache/log4j/VelocityTest.java b/log4j-1.2-api/src/src/test/java/org/apache/log4j/VelocityTest.java
new file mode 100644
index 0000000..99bd5a2
--- /dev/null
+++ b/log4j-1.2-api/src/src/test/java/org/apache/log4j/VelocityTest.java
@@ -0,0 +1,61 @@
+/*
+ * 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.StringWriter;
+
+import org.apache.logging.log4j.core.LoggerContext;
+import org.apache.logging.log4j.core.config.Configurator;
+import org.apache.logging.log4j.status.StatusLogger;
+import org.apache.velocity.Template;
+import org.apache.velocity.VelocityContext;
+import org.apache.velocity.app.Velocity;
+import org.junit.AfterClass;
+import org.junit.BeforeClass;
+import org.junit.Test;
+
+/**
+ * Note that this test must clean up after itself or it may cause other tests to fail.
+ */
+public class VelocityTest {
+
+private static LoggerContext context;
+    
+    @BeforeClass
+    public static void setupClass() {
+        context = LoggerContext.getContext(false);
+    }
+
+    @AfterClass
+    public static void tearDownClass() {
+        Configurator.shutdown(context);
+        StatusLogger.getLogger().reset();
+    }    
+    
+    @Test
+    public void testVelocity() {
+        Velocity.init();
+        final VelocityContext vContext = new VelocityContext();
+        vContext.put("name", new String("Velocity"));
+
+        final Template template = Velocity.getTemplate("target/test-classes/hello.vm");
+
+        final StringWriter sw = new StringWriter();
+
+        template.merge(vContext, sw);
+    }
+}
diff --git a/log4j-1.2-api/src/src/test/java/org/apache/log4j/config/AbstractLog4j1ConfigurationConverterTest.java b/log4j-1.2-api/src/src/test/java/org/apache/log4j/config/AbstractLog4j1ConfigurationConverterTest.java
new file mode 100644
index 0000000..9c973ee
--- /dev/null
+++ b/log4j-1.2-api/src/src/test/java/org/apache/log4j/config/AbstractLog4j1ConfigurationConverterTest.java
@@ -0,0 +1,68 @@
+package org.apache.log4j.config;
+
+import java.io.IOException;
+import java.nio.file.FileVisitResult;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.nio.file.SimpleFileVisitor;
+import java.nio.file.attribute.BasicFileAttributes;
+import java.util.ArrayList;
+import java.util.List;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+
+/*
+ * 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.
+ */
+
+@RunWith(Parameterized.class)
+public abstract class AbstractLog4j1ConfigurationConverterTest {
+
+    protected static List<Path> getPaths(final String root) throws IOException {
+        final List<Path> paths = new ArrayList<>();
+        Files.walkFileTree(Paths.get(root), new SimpleFileVisitor<Path>() {
+            @Override
+            public FileVisitResult visitFile(final Path file, final BasicFileAttributes attrs) throws IOException {
+                paths.add(file.toAbsolutePath());
+                return FileVisitResult.CONTINUE;
+            }
+        });
+        return paths;
+    }
+
+    private final Path pathIn;
+
+    public AbstractLog4j1ConfigurationConverterTest(final Path path) {
+        super();
+        this.pathIn = path;
+    }
+
+    @Test
+    public void test() throws IOException {
+        final Path tempFile = Files.createTempFile("log4j2", ".xml");
+        try {
+            final Log4j1ConfigurationConverter.CommandLineArguments cla = new Log4j1ConfigurationConverter.CommandLineArguments();
+            cla.setPathIn(pathIn);
+            cla.setPathOut(tempFile);
+            Log4j1ConfigurationConverter.run(cla);
+        } finally {
+            Files.deleteIfExists(tempFile);
+        }
+    }
+}
diff --git a/log4j-1.2-api/src/src/test/java/org/apache/log4j/config/AutoConfigTest.java b/log4j-1.2-api/src/src/test/java/org/apache/log4j/config/AutoConfigTest.java
new file mode 100644
index 0000000..764f612
--- /dev/null
+++ b/log4j-1.2-api/src/src/test/java/org/apache/log4j/config/AutoConfigTest.java
@@ -0,0 +1,66 @@
+/*
+ * 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.ListAppender;
+import org.apache.log4j.LogManager;
+import org.apache.log4j.Logger;
+import org.apache.log4j.bridge.AppenderAdapter;
+import org.apache.log4j.spi.LoggingEvent;
+import org.apache.log4j.xml.XmlConfigurationFactory;
+import org.apache.logging.log4j.core.Appender;
+import org.apache.logging.log4j.core.config.Configuration;
+import org.apache.logging.log4j.spi.LoggerContext;
+import org.junit.Test;
+
+import java.io.File;
+import java.util.List;
+import java.util.Map;
+
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+
+/**
+ * Test configuration from XML.
+ */
+public class AutoConfigTest {
+
+    @Test
+    public void testListAppender() {
+        Logger logger = LogManager.getLogger("test");
+        logger.debug("This is a test of the root logger");
+        LoggerContext loggerContext = org.apache.logging.log4j.LogManager.getContext(false);
+        Configuration configuration = ((org.apache.logging.log4j.core.LoggerContext) loggerContext).getConfiguration();
+        Map<String, Appender> appenders = configuration.getAppenders();
+        ListAppender eventAppender = null;
+        ListAppender messageAppender = null;
+        for (Map.Entry<String, Appender> entry : appenders.entrySet()) {
+            if (entry.getKey().equals("list")) {
+                messageAppender = (ListAppender) ((AppenderAdapter.Adapter) entry.getValue()).getAppender();
+            } else if (entry.getKey().equals("events")) {
+                eventAppender = (ListAppender) ((AppenderAdapter.Adapter) entry.getValue()).getAppender();
+            }
+        }
+        assertNotNull("No Event Appender", eventAppender);
+        assertNotNull("No Message Appender", messageAppender);
+        List<LoggingEvent> events = eventAppender.getEvents();
+        assertTrue("No events", events != null && events.size() > 0);
+        List<String> messages = messageAppender.getMessages();
+        assertTrue("No messages", messages != null && messages.size() > 0);
+    }
+
+}
diff --git a/log4j-1.2-api/src/src/test/java/org/apache/log4j/config/Log4j1ConfigurationConverterHadoopTest.java b/log4j-1.2-api/src/src/test/java/org/apache/log4j/config/Log4j1ConfigurationConverterHadoopTest.java
new file mode 100644
index 0000000..152f5dd
--- /dev/null
+++ b/log4j-1.2-api/src/src/test/java/org/apache/log4j/config/Log4j1ConfigurationConverterHadoopTest.java
@@ -0,0 +1,39 @@
+package org.apache.log4j.config;
+
+import java.io.IOException;
+import java.nio.file.Path;
+import java.util.List;
+
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+
+/*
+ * 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.
+ */
+
+@RunWith(Parameterized.class)
+public class Log4j1ConfigurationConverterHadoopTest extends AbstractLog4j1ConfigurationConverterTest {
+
+    @Parameterized.Parameters(name = "{0}")
+    public static List<Path> data() throws IOException {
+        return getPaths("src/test/resources/config-1.2/hadoop");
+    }
+
+    public Log4j1ConfigurationConverterHadoopTest(final Path path) {
+        super(path);
+    }
+
+}
diff --git a/log4j-1.2-api/src/src/test/java/org/apache/log4j/config/Log4j1ConfigurationConverterSparkTest.java b/log4j-1.2-api/src/src/test/java/org/apache/log4j/config/Log4j1ConfigurationConverterSparkTest.java
new file mode 100644
index 0000000..2b39d4f
--- /dev/null
+++ b/log4j-1.2-api/src/src/test/java/org/apache/log4j/config/Log4j1ConfigurationConverterSparkTest.java
@@ -0,0 +1,39 @@
+package org.apache.log4j.config;
+
+import java.io.IOException;
+import java.nio.file.Path;
+import java.util.List;
+
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+
+/*
+ * 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.
+ */
+
+@RunWith(Parameterized.class)
+public class Log4j1ConfigurationConverterSparkTest extends AbstractLog4j1ConfigurationConverterTest {
+
+    @Parameterized.Parameters(name = "{0}")
+    public static List<Path> data() throws IOException {
+        return getPaths("src/test/resources/config-1.2/spark");
+    }
+
+    public Log4j1ConfigurationConverterSparkTest(final Path path) {
+        super(path);
+    }
+
+}
diff --git a/log4j-1.2-api/src/src/test/java/org/apache/log4j/config/Log4j1ConfigurationFactoryTest.java b/log4j-1.2-api/src/src/test/java/org/apache/log4j/config/Log4j1ConfigurationFactoryTest.java
new file mode 100644
index 0000000..ebe3e54
--- /dev/null
+++ b/log4j-1.2-api/src/src/test/java/org/apache/log4j/config/Log4j1ConfigurationFactoryTest.java
@@ -0,0 +1,249 @@
+/*
+ * 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.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+
+import java.io.File;
+import java.net.URISyntaxException;
+import java.net.URL;
+import java.nio.file.FileSystemException;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.util.concurrent.TimeUnit;
+
+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.Layout;
+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;
+import org.apache.logging.log4j.core.appender.NullAppender;
+import org.apache.logging.log4j.core.appender.RollingFileAppender;
+import org.apache.logging.log4j.core.appender.rolling.CompositeTriggeringPolicy;
+import org.apache.logging.log4j.core.appender.rolling.DefaultRolloverStrategy;
+import org.apache.logging.log4j.core.appender.rolling.RolloverStrategy;
+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.LoggerConfig;
+import org.apache.logging.log4j.core.layout.HtmlLayout;
+import org.apache.logging.log4j.core.layout.PatternLayout;
+import org.junit.Test;
+
+public class Log4j1ConfigurationFactoryTest {
+
+    private Layout<?> testConsole(final String configResource) throws Exception {
+        final Configuration configuration = getConfiguration(configResource);
+        final String name = "Console";
+        final ConsoleAppender appender = configuration.getAppender(name);
+        assertNotNull("Missing appender '" + name + "' in configuration " + configResource + " → " + configuration,
+                appender);
+        assertEquals(Target.SYSTEM_ERR, appender.getTarget());
+        //
+        final LoggerConfig loggerConfig = configuration.getLoggerConfig("com.example.foo");
+        assertNotNull(loggerConfig);
+        assertEquals(Level.DEBUG, loggerConfig.getLevel());
+        configuration.start();
+        configuration.stop();
+        return appender.getLayout();
+    }
+
+	private Layout<?> testFile(final String configResource) throws Exception {
+		final Configuration configuration = getConfiguration(configResource);
+		final FileAppender appender = configuration.getAppender("File");
+		assertNotNull(appender);
+		assertEquals("target/mylog.txt", appender.getFileName());
+		//
+		final LoggerConfig loggerConfig = configuration.getLoggerConfig("com.example.foo");
+		assertNotNull(loggerConfig);
+		assertEquals(Level.DEBUG, loggerConfig.getLevel());
+		configuration.start();
+		configuration.stop();
+		return appender.getLayout();
+	}
+
+	private Configuration getConfiguration(final String configResource) throws URISyntaxException {
+		final URL configLocation = ClassLoader.getSystemResource(configResource);
+		assertNotNull(configResource, configLocation);
+		final Configuration configuration = new Log4j1ConfigurationFactory().getConfiguration(null, "test",
+				configLocation.toURI());
+		assertNotNull(configuration);
+		return configuration;
+	}
+
+	@Test
+	public void testConsoleEnhancedPatternLayout() throws Exception {
+		final PatternLayout layout = (PatternLayout) testConsole(
+				"config-1.2/log4j-console-EnhancedPatternLayout.properties");
+		assertEquals("%d{ISO8601} [%t][%c] %-5p %properties %ndc: %m%n", layout.getConversionPattern());
+	}
+
+	@Test
+	public void testConsoleHtmlLayout() throws Exception {
+		final HtmlLayout layout = (HtmlLayout) testConsole("config-1.2/log4j-console-HtmlLayout.properties");
+		assertEquals("Headline", layout.getTitle());
+		assertTrue(layout.isLocationInfo());
+	}
+
+	@Test
+	public void testConsolePatternLayout() throws Exception {
+		final PatternLayout layout = (PatternLayout) testConsole("config-1.2/log4j-console-PatternLayout.properties");
+		assertEquals("%d{ISO8601} [%t][%c] %-5p: %m%n", layout.getConversionPattern());
+	}
+
+	@Test
+	public void testConsoleSimpleLayout() throws Exception {
+		final PatternLayout layout = (PatternLayout) testConsole("config-1.2/log4j-console-SimpleLayout.properties");
+		assertEquals("%level - %m%n", layout.getConversionPattern());
+	}
+
+	@Test
+	public void testConsoleTtccLayout() throws Exception {
+		final PatternLayout layout = (PatternLayout) testConsole("config-1.2/log4j-console-TTCCLayout.properties");
+		assertEquals("%r [%t] %p %notEmpty{%ndc }- %m%n", layout.getConversionPattern());
+	}
+
+	@Test
+	public void testConsoleXmlLayout() throws Exception {
+		final Log4j1XmlLayout layout = (Log4j1XmlLayout) testConsole("config-1.2/log4j-console-XmlLayout.properties");
+		assertTrue(layout.isLocationInfo());
+		assertFalse(layout.isProperties());
+	}
+
+	@Test
+	public void testFileSimpleLayout() throws Exception {
+		final PatternLayout layout = (PatternLayout) testFile("config-1.2/log4j-file-SimpleLayout.properties");
+		assertEquals("%level - %m%n", layout.getConversionPattern());
+	}
+
+	@Test
+	public void testNullAppender() throws Exception {
+		final Configuration configuration = getConfiguration("config-1.2/log4j-NullAppender.properties");
+		final Appender appender = configuration.getAppender("NullAppender");
+		assertNotNull(appender);
+		assertEquals("NullAppender", appender.getName());
+		assertTrue(appender.getClass().getName(), appender instanceof NullAppender);
+	}
+
+	@Test
+	public void testRollingFileAppender() throws Exception {
+		testRollingFileAppender("config-1.2/log4j-RollingFileAppender.properties", "RFA", "target/hadoop.log.%i");
+	}
+
+	@Test
+	public void testDailyRollingFileAppender() throws Exception {
+		testDailyRollingFileAppender("config-1.2/log4j-DailyRollingFileAppender.properties", "DRFA", "target/hadoop.log%d{.yyyy-MM-dd}");
+	}
+
+	@Test
+	public void testRollingFileAppenderWithProperties() throws Exception {
+		testRollingFileAppender("config-1.2/log4j-RollingFileAppender-with-props.properties", "RFA", "target/hadoop.log.%i");
+	}
+
+	@Test
+	public void testSystemProperties1() throws Exception {
+        final String tempFileName = System.getProperty("java.io.tmpdir") + "/hadoop.log";
+        final Path tempFilePath = new File(tempFileName).toPath();
+        Files.deleteIfExists(tempFilePath);
+        try {
+            final Configuration configuration = getConfiguration("config-1.2/log4j-system-properties-1.properties");
+            final RollingFileAppender appender = configuration.getAppender("RFA");
+			appender.stop(10, TimeUnit.SECONDS);
+            System.out.println("expected: " + tempFileName + " Actual: " + appender.getFileName());
+            assertEquals(tempFileName, appender.getFileName());
+        } finally {
+			try {
+				Files.deleteIfExists(tempFilePath);
+			} catch (final FileSystemException e) {
+				e.printStackTrace();
+			}
+        }
+	}
+
+	@Test
+	public void testSystemProperties2() throws Exception {
+		final Configuration configuration = getConfiguration("config-1.2/log4j-system-properties-2.properties");
+		final RollingFileAppender appender = configuration.getAppender("RFA");
+		assertEquals("${java.io.tmpdir}/hadoop.log", appender.getFileName());
+		appender.stop(10, TimeUnit.SECONDS);
+		Path path = new File(appender.getFileName()).toPath();
+        Files.deleteIfExists(path);
+        path = new File("${java.io.tmpdir}").toPath();
+        Files.deleteIfExists(path);
+	}
+
+	private void testRollingFileAppender(final String configResource, final String name, final String filePattern) throws URISyntaxException {
+		final Configuration configuration = getConfiguration(configResource);
+		final Appender appender = configuration.getAppender(name);
+		assertNotNull(appender);
+		assertEquals(name, appender.getName());
+		assertTrue(appender.getClass().getName(), appender instanceof RollingFileAppender);
+		final RollingFileAppender rfa = (RollingFileAppender) appender;
+		assertEquals("target/hadoop.log", rfa.getFileName());
+		assertEquals(filePattern, rfa.getFilePattern());
+		final TriggeringPolicy triggeringPolicy = rfa.getTriggeringPolicy();
+		assertNotNull(triggeringPolicy);
+		assertTrue(triggeringPolicy.getClass().getName(), triggeringPolicy instanceof CompositeTriggeringPolicy);
+		final CompositeTriggeringPolicy ctp = (CompositeTriggeringPolicy) triggeringPolicy;
+		final TriggeringPolicy[] triggeringPolicies = ctp.getTriggeringPolicies();
+		assertEquals(1, triggeringPolicies.length);
+		final TriggeringPolicy tp = triggeringPolicies[0];
+		assertTrue(tp.getClass().getName(), tp instanceof SizeBasedTriggeringPolicy);
+		final SizeBasedTriggeringPolicy sbtp = (SizeBasedTriggeringPolicy) tp;
+		assertEquals(256 * 1024 * 1024, sbtp.getMaxFileSize());
+		final RolloverStrategy rolloverStrategy = rfa.getManager().getRolloverStrategy();
+		assertTrue(rolloverStrategy.getClass().getName(), rolloverStrategy instanceof DefaultRolloverStrategy);
+		final DefaultRolloverStrategy drs = (DefaultRolloverStrategy) rolloverStrategy;
+		assertEquals(20, drs.getMaxIndex());
+		configuration.start();
+		configuration.stop();
+	}
+
+	private void testDailyRollingFileAppender(final String configResource, final String name, final String filePattern) throws URISyntaxException {
+		final Configuration configuration = getConfiguration(configResource);
+		final Appender appender = configuration.getAppender(name);
+		assertNotNull(appender);
+		assertEquals(name, appender.getName());
+		assertTrue(appender.getClass().getName(), appender instanceof RollingFileAppender);
+		final RollingFileAppender rfa = (RollingFileAppender) appender;
+		assertEquals("target/hadoop.log", rfa.getFileName());
+		assertEquals(filePattern, rfa.getFilePattern());
+		final TriggeringPolicy triggeringPolicy = rfa.getTriggeringPolicy();
+		assertNotNull(triggeringPolicy);
+		assertTrue(triggeringPolicy.getClass().getName(), triggeringPolicy instanceof CompositeTriggeringPolicy);
+		final CompositeTriggeringPolicy ctp = (CompositeTriggeringPolicy) triggeringPolicy;
+		final TriggeringPolicy[] triggeringPolicies = ctp.getTriggeringPolicies();
+		assertEquals(1, triggeringPolicies.length);
+		final TriggeringPolicy tp = triggeringPolicies[0];
+		assertTrue(tp.getClass().getName(), tp instanceof TimeBasedTriggeringPolicy);
+		final TimeBasedTriggeringPolicy tbtp = (TimeBasedTriggeringPolicy) tp;
+		assertEquals(1, tbtp.getInterval());
+		final RolloverStrategy rolloverStrategy = rfa.getManager().getRolloverStrategy();
+		assertTrue(rolloverStrategy.getClass().getName(), rolloverStrategy instanceof DefaultRolloverStrategy);
+		final DefaultRolloverStrategy drs = (DefaultRolloverStrategy) rolloverStrategy;
+		assertEquals(Integer.MAX_VALUE, drs.getMaxIndex());
+		configuration.start();
+		configuration.stop();
+	}
+
+}
diff --git a/log4j-1.2-api/src/src/test/java/org/apache/log4j/config/XmlConfigurationFactoryTest.java b/log4j-1.2-api/src/src/test/java/org/apache/log4j/config/XmlConfigurationFactoryTest.java
new file mode 100644
index 0000000..d522a18
--- /dev/null
+++ b/log4j-1.2-api/src/src/test/java/org/apache/log4j/config/XmlConfigurationFactoryTest.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.config;
+
+import org.apache.log4j.ListAppender;
+import org.apache.log4j.LogManager;
+import org.apache.log4j.Logger;
+import org.apache.log4j.bridge.AppenderAdapter;
+import org.apache.log4j.spi.LoggingEvent;
+import org.apache.log4j.xml.DOMConfigurator;
+import org.apache.log4j.xml.XmlConfigurationFactory;
+import org.apache.logging.log4j.core.Appender;
+import org.apache.logging.log4j.core.config.Configuration;
+import org.apache.logging.log4j.spi.LoggerContext;
+import org.junit.Test;
+
+import java.io.File;
+import java.util.List;
+import java.util.Map;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+
+/**
+ * Test configuration from XML.
+ */
+public class XmlConfigurationFactoryTest {
+
+    @Test
+    public void testXML() throws Exception {
+        XmlConfigurationFactory.configure("target/test-classes/log4j1-file.xml");
+        Logger logger = LogManager.getLogger("test");
+        logger.debug("This is a test of the root logger");
+        File file = new File("target/temp.A1");
+        assertTrue("File A1 was not created", file.exists());
+        assertTrue("File A1 is empty", file.length() > 0);
+        file = new File("target/temp.A2");
+        assertTrue("File A2 was not created", file.exists());
+        assertTrue("File A2 is empty", file.length() > 0);
+    }
+
+    @Test
+    public void testListAppender() {
+        XmlConfigurationFactory.configure("target/test-classes/log4j1-list.xml");
+        Logger logger = LogManager.getLogger("test");
+        logger.debug("This is a test of the root logger");
+        LoggerContext loggerContext = org.apache.logging.log4j.LogManager.getContext(false);
+        Configuration configuration = ((org.apache.logging.log4j.core.LoggerContext) loggerContext).getConfiguration();
+        Map<String, Appender> appenders = configuration.getAppenders();
+        ListAppender eventAppender = null;
+        ListAppender messageAppender = null;
+        for (Map.Entry<String, Appender> entry : appenders.entrySet()) {
+            if (entry.getKey().equals("list")) {
+                messageAppender = (ListAppender) ((AppenderAdapter.Adapter) entry.getValue()).getAppender();
+            } else if (entry.getKey().equals("events")) {
+                eventAppender = (ListAppender) ((AppenderAdapter.Adapter) entry.getValue()).getAppender();
+            }
+        }
+        assertNotNull("No Event Appender", eventAppender);
+        assertNotNull("No Message Appender", messageAppender);
+        List<LoggingEvent> events = eventAppender.getEvents();
+        assertTrue("No events", events != null && events.size() > 0);
+        List<String> messages = messageAppender.getMessages();
+        assertTrue("No messages", messages != null && messages.size() > 0);
+    }
+
+}
diff --git a/log4j-1.2-api/src/src/test/java/org/apache/log4j/layout/Log4j1XmlLayoutTest.java b/log4j-1.2-api/src/src/test/java/org/apache/log4j/layout/Log4j1XmlLayoutTest.java
new file mode 100644
index 0000000..28579fe
--- /dev/null
+++ b/log4j-1.2-api/src/src/test/java/org/apache/log4j/layout/Log4j1XmlLayoutTest.java
@@ -0,0 +1,88 @@
+/*
+ * 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.layout;
+
+import static org.junit.Assert.assertEquals;
+
+import org.apache.logging.log4j.Level;
+import org.apache.logging.log4j.core.impl.ContextDataFactory;
+import org.apache.logging.log4j.core.impl.Log4jLogEvent;
+import org.apache.logging.log4j.junit.ThreadContextRule;
+import org.apache.logging.log4j.message.SimpleMessage;
+import org.apache.logging.log4j.util.StringMap;
+import org.junit.Rule;
+import org.junit.Test;
+
+public class Log4j1XmlLayoutTest {
+
+    @Rule
+    public ThreadContextRule threadContextRule = new ThreadContextRule();
+
+    @Test
+    public void testWithoutThrown() {
+        final Log4j1XmlLayout layout = Log4j1XmlLayout.createLayout(false, true);
+
+        final Log4jLogEvent event = Log4jLogEvent.newBuilder()
+                .setLoggerName("a.B")
+                .setLevel(Level.INFO)
+                .setMessage(new SimpleMessage("Hello, World"))
+                .setTimeMillis(System.currentTimeMillis() + 17)
+                .build();
+
+        final String result = layout.toSerializable(event);
+
+        final String expected =
+                "<log4j:event logger=\"a.B\" timestamp=\"" + event.getTimeMillis() + "\" level=\"INFO\" thread=\"main\">\r\n" +
+                "<log4j:message><![CDATA[Hello, World]]></log4j:message>\r\n" +
+                "</log4j:event>\r\n\r\n";
+
+        assertEquals(expected, result);
+    }
+
+    @Test
+    public void testWithPropertiesAndLocationInfo() {
+        final Log4j1XmlLayout layout = Log4j1XmlLayout.createLayout(true, true);
+
+        final StringMap contextMap = ContextDataFactory.createContextData(2);
+        contextMap.putValue("key1", "value1");
+        contextMap.putValue("key2", "value2");
+        final Log4jLogEvent event = Log4jLogEvent.newBuilder()
+                .setLoggerName("a.B")
+                .setLevel(Level.INFO)
+                .setMessage(new SimpleMessage("Hello, World"))
+                .setTimeMillis(System.currentTimeMillis() + 17)
+                .setIncludeLocation(true)
+                .setSource(new StackTraceElement("pack.MyClass", "myMethod", "MyClass.java", 17))
+                .setContextData(contextMap)
+                .build();
+
+        final String result = layout.toSerializable(event);
+
+        final String expected =
+                "<log4j:event logger=\"a.B\" timestamp=\"" + event.getTimeMillis() + "\" level=\"INFO\" thread=\"main\">\r\n" +
+                "<log4j:message><![CDATA[Hello, World]]></log4j:message>\r\n" +
+                "<log4j:locationInfo class=\"pack.MyClass\" method=\"myMethod\" file=\"MyClass.java\" line=\"17\"/>\r\n" +
+                "<log4j:properties>\r\n" +
+                "<log4j:data name=\"key1\" value=\"value1\"/>\r\n" +
+                "<log4j:data name=\"key2\" value=\"value2\"/>\r\n" +
+                "</log4j:properties>\r\n"+
+                "</log4j:event>\r\n\r\n";
+
+        assertEquals(expected, result);
+    }
+
+}
diff --git a/log4j-1.2-api/src/src/test/java/org/apache/log4j/pattern/Log4j1MdcPatternConverterTest.java b/log4j-1.2-api/src/src/test/java/org/apache/log4j/pattern/Log4j1MdcPatternConverterTest.java
new file mode 100644
index 0000000..c1d5b83
--- /dev/null
+++ b/log4j-1.2-api/src/src/test/java/org/apache/log4j/pattern/Log4j1MdcPatternConverterTest.java
@@ -0,0 +1,78 @@
+/*
+ * 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 static org.junit.Assert.assertEquals;
+
+import org.apache.logging.log4j.Level;
+import org.apache.logging.log4j.core.LogEvent;
+import org.apache.logging.log4j.core.impl.ContextDataFactory;
+import org.apache.logging.log4j.core.impl.Log4jLogEvent;
+import org.apache.logging.log4j.message.SimpleMessage;
+import org.apache.logging.log4j.util.StringMap;
+import org.junit.Test;
+
+public class Log4j1MdcPatternConverterTest {
+
+    @Test
+    public void testConverter0() {
+        final StringMap contextMap = ContextDataFactory.createContextData(0);
+        final String expected = "{}";
+        test(contextMap, expected, null);
+    }
+
+    @Test
+    public void testConverter1() {
+        final StringMap contextMap = ContextDataFactory.createContextData(1);
+        contextMap.putValue("key1", "value1");
+        final String expected = "{{key1,value1}}";
+        test(contextMap, expected, null);
+    }
+
+    @Test
+    public void testConverter2() {
+        final StringMap contextMap = ContextDataFactory.createContextData(2);
+        contextMap.putValue("key1", "value1");
+        contextMap.putValue("key2", "value2");
+        final String expected = "{{key1,value1}{key2,value2}}";
+        test(contextMap, expected, null);
+    }
+
+    @Test
+    public void testConverterWithKey() {
+        final StringMap contextMap = ContextDataFactory.createContextData(2);
+        contextMap.putValue("key1", "value1");
+        contextMap.putValue("key2", "value2");
+        final String expected = "value1";
+        test(contextMap, expected, new String[] {"key1"});
+    }
+
+    private void test(final StringMap contextMap, final String expected, final String[] options) {
+        final LogEvent event = Log4jLogEvent.newBuilder()
+                .setLoggerName("MyLogger")
+                .setLevel(Level.DEBUG)
+                .setMessage(new SimpleMessage("Hello"))
+                .setContextData(contextMap)
+                .build();
+        final StringBuilder sb = new StringBuilder();
+        final Log4j1MdcPatternConverter converter = Log4j1MdcPatternConverter.newInstance(options);
+        converter.format(event, sb);
+        assertEquals(expected, sb.toString());
+    }
+
+}
+
diff --git a/log4j-1.2-api/src/src/test/java/org/apache/log4j/pattern/Log4j1NdcPatternConverterTest.java b/log4j-1.2-api/src/src/test/java/org/apache/log4j/pattern/Log4j1NdcPatternConverterTest.java
new file mode 100644
index 0000000..2f0b80f
--- /dev/null
+++ b/log4j-1.2-api/src/src/test/java/org/apache/log4j/pattern/Log4j1NdcPatternConverterTest.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.pattern;
+
+import org.apache.logging.log4j.Level;
+import org.apache.logging.log4j.ThreadContext;
+import org.apache.logging.log4j.core.LogEvent;
+import org.apache.logging.log4j.core.impl.Log4jLogEvent;
+import org.apache.logging.log4j.junit.ThreadContextStackRule;
+import org.apache.logging.log4j.message.SimpleMessage;
+import org.junit.Rule;
+import org.junit.Test;
+
+import static org.junit.Assert.assertEquals;
+
+public class Log4j1NdcPatternConverterTest {
+
+    @Rule
+    public final ThreadContextStackRule threadContextRule = new ThreadContextStackRule();
+
+    @Test
+    public void testEmpty() {
+        testConverter("");
+    }
+
+    @Test
+    public void test1() {
+        ThreadContext.push("foo");
+        testConverter("foo");
+    }
+
+    @Test
+    public void test2() {
+        ThreadContext.push("foo");
+        ThreadContext.push("bar");
+        testConverter("foo bar");
+    }
+
+    @Test
+    public void test3() {
+        ThreadContext.push("foo");
+        ThreadContext.push("bar");
+        ThreadContext.push("baz");
+        testConverter("foo bar baz");
+    }
+
+    private void testConverter(final String expected) {
+        final Log4j1NdcPatternConverter converter = Log4j1NdcPatternConverter.newInstance(null);
+        final LogEvent event = Log4jLogEvent.newBuilder()
+                .setLoggerName("MyLogger")
+                .setLevel(Level.DEBUG)
+                .setMessage(new SimpleMessage("Hello"))
+                .build();
+        final StringBuilder sb = new StringBuilder();
+        converter.format(event, sb);
+        assertEquals(expected, sb.toString());
+    }
+
+}
+
diff --git a/log4j-1.2-api/src/src/test/java/org/apache/log4j/util/SerializationTestHelper.java b/log4j-1.2-api/src/src/test/java/org/apache/log4j/util/SerializationTestHelper.java
new file mode 100644
index 0000000..0576463
--- /dev/null
+++ b/log4j-1.2-api/src/src/test/java/org/apache/log4j/util/SerializationTestHelper.java
@@ -0,0 +1,148 @@
+/*
+ * 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 static org.junit.Assert.assertEquals;
+import static org.junit.Assert.fail;
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.io.ObjectInputStream;
+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.
+     *
+     * @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 {
+        final ByteArrayOutputStream memOut = new ByteArrayOutputStream();
+        try (final ObjectOutputStream objOut = new ObjectOutputStream(memOut)) {
+            objOut.writeObject(obj);
+        }
+
+        assertStreamEquals(witness, memOut.toByteArray(), skip, endCompare);
+    }
+
+    /**
+     * 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 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 {
+        final File witnessFile = new File(witness);
+
+        if (witnessFile.exists()) {
+            int skipIndex = 0;
+            final byte[] expected = FileUtils.readFileToByteArray(witnessFile);
+            final int bytesRead = expected.length;
+
+            if (bytesRead < endCompare) {
+                assertEquals(bytesRead, actual.length);
+            }
+
+            int endScan = actual.length;
+
+            if (endScan > endCompare) {
+                endScan = endCompare;
+            }
+
+            for (int i = 0; i < endScan; i++) {
+                if ((skipIndex < skip.length) && (skip[skipIndex] == i)) {
+                    skipIndex++;
+                } else {
+                    if (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
+            FileUtils.writeByteArrayToFile(witnessFile, actual);
+            fail("Writing witness file " + witness);
+        }
+    }
+}
+
diff --git a/log4j-1.2-api/src/src/test/resources/L7D_en_US.properties b/log4j-1.2-api/src/src/test/resources/L7D_en_US.properties
new file mode 100644
index 0000000..c3c2802
--- /dev/null
+++ b/log4j-1.2-api/src/src/test/resources/L7D_en_US.properties
@@ -0,0 +1,17 @@
+# 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.
+test=This is the English, US test.
+hello_world=Hello world.
+msg1=This is test number {0} with string argument {1}.
diff --git a/log4j-1.2-api/src/src/test/resources/L7D_fr.properties b/log4j-1.2-api/src/src/test/resources/L7D_fr.properties
new file mode 100644
index 0000000..25b878a
--- /dev/null
+++ b/log4j-1.2-api/src/src/test/resources/L7D_fr.properties
@@ -0,0 +1,17 @@
+# 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.
+test=Ceci est le test en francais pour la France.
+hello_world=Bonjour la France.
+msg1=Ceci est le test numero {0} contenant l''argument {1}.
diff --git a/log4j-1.2-api/src/src/test/resources/L7D_fr_CH.properties b/log4j-1.2-api/src/src/test/resources/L7D_fr_CH.properties
new file mode 100644
index 0000000..ba9b1ff
--- /dev/null
+++ b/log4j-1.2-api/src/src/test/resources/L7D_fr_CH.properties
@@ -0,0 +1,16 @@
+# 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.
+test=Ceci est le test en francais pour la p'tite Suisse.
+hello world=Salut le monde.
diff --git a/log4j-1.2-api/src/src/test/resources/config-1.2/hadoop/hadoop-common-project/hadoop-auth-examples/src/main/resources/log4j.properties b/log4j-1.2-api/src/src/test/resources/config-1.2/hadoop/hadoop-common-project/hadoop-auth-examples/src/main/resources/log4j.properties
new file mode 100644
index 0000000..5fa4020
--- /dev/null
+++ b/log4j-1.2-api/src/src/test/resources/config-1.2/hadoop/hadoop-common-project/hadoop-auth-examples/src/main/resources/log4j.properties
@@ -0,0 +1,19 @@
+#
+# Licensed 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. See accompanying LICENSE file.
+#
+log4j.appender.test=org.apache.log4j.ConsoleAppender
+log4j.appender.test.Target=System.out
+log4j.appender.test.layout=org.apache.log4j.PatternLayout
+log4j.appender.test.layout.ConversionPattern=%d{ABSOLUTE} %5p %c{1}:%L - %m%n
+
+log4j.logger.org.apache.hadoop.security.authentication=DEBUG, test
diff --git a/log4j-1.2-api/src/src/test/resources/config-1.2/hadoop/hadoop-common-project/hadoop-common/src/main/conf/log4j.properties b/log4j-1.2-api/src/src/test/resources/config-1.2/hadoop/hadoop-common-project/hadoop-common/src/main/conf/log4j.properties
new file mode 100644
index 0000000..b08514c
--- /dev/null
+++ b/log4j-1.2-api/src/src/test/resources/config-1.2/hadoop/hadoop-common-project/hadoop-common/src/main/conf/log4j.properties
@@ -0,0 +1,323 @@
+# 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.
+
+# Define some default values that can be overridden by system properties
+hadoop.root.logger=INFO,console
+hadoop.log.dir=target
+hadoop.log.file=hadoop.log
+
+# Define the root logger to the system property "hadoop.root.logger".
+log4j.rootLogger=${hadoop.root.logger}, EventCounter
+
+# Logging Threshold
+log4j.threshold=ALL
+
+# Null Appender
+log4j.appender.NullAppender=org.apache.log4j.varia.NullAppender
+
+#
+# Rolling File Appender - cap space usage at 5gb.
+#
+hadoop.log.maxfilesize=256MB
+hadoop.log.maxbackupindex=20
+log4j.appender.RFA=org.apache.log4j.RollingFileAppender
+log4j.appender.RFA.File=${hadoop.log.dir}/${hadoop.log.file}
+
+log4j.appender.RFA.MaxFileSize=${hadoop.log.maxfilesize}
+log4j.appender.RFA.MaxBackupIndex=${hadoop.log.maxbackupindex}
+
+log4j.appender.RFA.layout=org.apache.log4j.PatternLayout
+
+# Pattern format: Date LogLevel LoggerName LogMessage
+log4j.appender.RFA.layout.ConversionPattern=%d{ISO8601} %p %c: %m%n
+# Debugging Pattern format
+#log4j.appender.RFA.layout.ConversionPattern=%d{ISO8601} %-5p %c{2} (%F:%M(%L)) - %m%n
+
+
+#
+# Daily Rolling File Appender
+#
+
+log4j.appender.DRFA=org.apache.log4j.DailyRollingFileAppender
+log4j.appender.DRFA.File=${hadoop.log.dir}/${hadoop.log.file}
+
+# Rollover at midnight
+log4j.appender.DRFA.DatePattern=.yyyy-MM-dd
+
+log4j.appender.DRFA.layout=org.apache.log4j.PatternLayout
+
+# Pattern format: Date LogLevel LoggerName LogMessage
+log4j.appender.DRFA.layout.ConversionPattern=%d{ISO8601} %p %c: %m%n
+# Debugging Pattern format
+#log4j.appender.DRFA.layout.ConversionPattern=%d{ISO8601} %-5p %c{2} (%F:%M(%L)) - %m%n
+
+
+#
+# console
+# Add "console" to rootlogger above if you want to use this
+#
+
+log4j.appender.console=org.apache.log4j.ConsoleAppender
+log4j.appender.console.target=System.err
+log4j.appender.console.layout=org.apache.log4j.PatternLayout
+log4j.appender.console.layout.ConversionPattern=%d{ISO8601} %p %c{2}: %m%n
+
+#
+# TaskLog Appender
+#
+
+#Default values
+hadoop.tasklog.taskid=null
+hadoop.tasklog.iscleanup=false
+hadoop.tasklog.noKeepSplits=4
+hadoop.tasklog.totalLogFileSize=100
+hadoop.tasklog.purgeLogSplits=true
+hadoop.tasklog.logsRetainHours=12
+
+log4j.appender.TLA=org.apache.hadoop.mapred.TaskLogAppender
+log4j.appender.TLA.taskId=${hadoop.tasklog.taskid}
+log4j.appender.TLA.isCleanup=${hadoop.tasklog.iscleanup}
+log4j.appender.TLA.totalLogFileSize=${hadoop.tasklog.totalLogFileSize}
+
+log4j.appender.TLA.layout=org.apache.log4j.PatternLayout
+log4j.appender.TLA.layout.ConversionPattern=%d{ISO8601} %p %c: %m%n
+
+#
+# HDFS block state change log from block manager
+#
+# Uncomment the following to log normal block state change
+# messages from BlockManager in NameNode.
+#log4j.logger.BlockStateChange=DEBUG
+
+#
+#Security appender
+#
+hadoop.security.logger=INFO,NullAppender
+hadoop.security.log.maxfilesize=256MB
+hadoop.security.log.maxbackupindex=20
+log4j.category.SecurityLogger=${hadoop.security.logger}
+hadoop.security.log.file=SecurityAuth-${user.name}.audit
+log4j.appender.RFAS=org.apache.log4j.RollingFileAppender
+log4j.appender.RFAS.File=${hadoop.log.dir}/${hadoop.security.log.file}
+log4j.appender.RFAS.layout=org.apache.log4j.PatternLayout
+log4j.appender.RFAS.layout.ConversionPattern=%d{ISO8601} %p %c: %m%n
+log4j.appender.RFAS.MaxFileSize=${hadoop.security.log.maxfilesize}
+log4j.appender.RFAS.MaxBackupIndex=${hadoop.security.log.maxbackupindex}
+
+#
+# Daily Rolling Security appender
+#
+log4j.appender.DRFAS=org.apache.log4j.DailyRollingFileAppender
+log4j.appender.DRFAS.File=${hadoop.log.dir}/${hadoop.security.log.file}
+log4j.appender.DRFAS.layout=org.apache.log4j.PatternLayout
+log4j.appender.DRFAS.layout.ConversionPattern=%d{ISO8601} %p %c: %m%n
+log4j.appender.DRFAS.DatePattern=.yyyy-MM-dd
+
+#
+# hadoop configuration logging
+#
+
+# Uncomment the following line to turn off configuration deprecation warnings.
+# log4j.logger.org.apache.hadoop.conf.Configuration.deprecation=WARN
+
+#
+# hdfs audit logging
+#
+hdfs.audit.logger=INFO,NullAppender
+hdfs.audit.log.maxfilesize=256MB
+hdfs.audit.log.maxbackupindex=20
+log4j.logger.org.apache.hadoop.hdfs.server.namenode.FSNamesystem.audit=${hdfs.audit.logger}
+log4j.additivity.org.apache.hadoop.hdfs.server.namenode.FSNamesystem.audit=false
+log4j.appender.RFAAUDIT=org.apache.log4j.RollingFileAppender
+log4j.appender.RFAAUDIT.File=${hadoop.log.dir}/hdfs-audit.log
+log4j.appender.RFAAUDIT.layout=org.apache.log4j.PatternLayout
+log4j.appender.RFAAUDIT.layout.ConversionPattern=%d{ISO8601} %p %c{2}: %m%n
+log4j.appender.RFAAUDIT.MaxFileSize=${hdfs.audit.log.maxfilesize}
+log4j.appender.RFAAUDIT.MaxBackupIndex=${hdfs.audit.log.maxbackupindex}
+
+#
+# NameNode metrics logging.
+# The default is to retain two namenode-metrics.log files up to 64MB each.
+#
+namenode.metrics.logger=INFO,NullAppender
+log4j.logger.NameNodeMetricsLog=${namenode.metrics.logger}
+log4j.additivity.NameNodeMetricsLog=false
+log4j.appender.NNMETRICSRFA=org.apache.log4j.RollingFileAppender
+log4j.appender.NNMETRICSRFA.File=${hadoop.log.dir}/namenode-metrics.log
+log4j.appender.NNMETRICSRFA.layout=org.apache.log4j.PatternLayout
+log4j.appender.NNMETRICSRFA.layout.ConversionPattern=%d{ISO8601} %m%n
+log4j.appender.NNMETRICSRFA.MaxBackupIndex=1
+log4j.appender.NNMETRICSRFA.MaxFileSize=64MB
+
+#
+# DataNode metrics logging.
+# The default is to retain two datanode-metrics.log files up to 64MB each.
+#
+datanode.metrics.logger=INFO,NullAppender
+log4j.logger.DataNodeMetricsLog=${datanode.metrics.logger}
+log4j.additivity.DataNodeMetricsLog=false
+log4j.appender.DNMETRICSRFA=org.apache.log4j.RollingFileAppender
+log4j.appender.DNMETRICSRFA.File=${hadoop.log.dir}/datanode-metrics.log
+log4j.appender.DNMETRICSRFA.layout=org.apache.log4j.PatternLayout
+log4j.appender.DNMETRICSRFA.layout.ConversionPattern=%d{ISO8601} %m%n
+log4j.appender.DNMETRICSRFA.MaxBackupIndex=1
+log4j.appender.DNMETRICSRFA.MaxFileSize=64MB
+
+#
+# mapred audit logging
+#
+mapred.audit.logger=INFO,NullAppender
+mapred.audit.log.maxfilesize=256MB
+mapred.audit.log.maxbackupindex=20
+log4j.logger.org.apache.hadoop.mapred.AuditLogger=${mapred.audit.logger}
+log4j.additivity.org.apache.hadoop.mapred.AuditLogger=false
+log4j.appender.MRAUDIT=org.apache.log4j.RollingFileAppender
+log4j.appender.MRAUDIT.File=${hadoop.log.dir}/mapred-audit.log
+log4j.appender.MRAUDIT.layout=org.apache.log4j.PatternLayout
+log4j.appender.MRAUDIT.layout.ConversionPattern=%d{ISO8601} %p %c{2}: %m%n
+log4j.appender.MRAUDIT.MaxFileSize=${mapred.audit.log.maxfilesize}
+log4j.appender.MRAUDIT.MaxBackupIndex=${mapred.audit.log.maxbackupindex}
+
+# Custom Logging levels
+
+#log4j.logger.org.apache.hadoop.mapred.JobTracker=DEBUG
+#log4j.logger.org.apache.hadoop.mapred.TaskTracker=DEBUG
+#log4j.logger.org.apache.hadoop.hdfs.server.namenode.FSNamesystem.audit=DEBUG
+
+# Jets3t library
+log4j.logger.org.jets3t.service.impl.rest.httpclient.RestS3Service=ERROR
+
+# AWS SDK & S3A FileSystem
+log4j.logger.com.amazonaws=ERROR
+log4j.logger.com.amazonaws.http.AmazonHttpClient=ERROR
+log4j.logger.org.apache.hadoop.fs.s3a.S3AFileSystem=WARN
+
+#
+# Event Counter Appender
+# Sends counts of logging messages at different severity levels to Hadoop Metrics.
+#
+log4j.appender.EventCounter=org.apache.hadoop.log.metrics.EventCounter
+
+#
+# Job Summary Appender
+#
+# Use following logger to send summary to separate file defined by
+# hadoop.mapreduce.jobsummary.log.file :
+# hadoop.mapreduce.jobsummary.logger=INFO,JSA
+#
+hadoop.mapreduce.jobsummary.logger=${hadoop.root.logger}
+hadoop.mapreduce.jobsummary.log.file=hadoop-mapreduce.jobsummary.log
+hadoop.mapreduce.jobsummary.log.maxfilesize=256MB
+hadoop.mapreduce.jobsummary.log.maxbackupindex=20
+log4j.appender.JSA=org.apache.log4j.RollingFileAppender
+log4j.appender.JSA.File=${hadoop.log.dir}/${hadoop.mapreduce.jobsummary.log.file}
+log4j.appender.JSA.MaxFileSize=${hadoop.mapreduce.jobsummary.log.maxfilesize}
+log4j.appender.JSA.MaxBackupIndex=${hadoop.mapreduce.jobsummary.log.maxbackupindex}
+log4j.appender.JSA.layout=org.apache.log4j.PatternLayout
+log4j.appender.JSA.layout.ConversionPattern=%d{ISO8601} %p %c{2}: %m%n
+log4j.logger.org.apache.hadoop.mapred.JobInProgress$JobSummary=${hadoop.mapreduce.jobsummary.logger}
+log4j.additivity.org.apache.hadoop.mapred.JobInProgress$JobSummary=false
+
+#
+# shuffle connection log from shuffleHandler
+# Uncomment the following line to enable logging of shuffle connections
+# log4j.logger.org.apache.hadoop.mapred.ShuffleHandler.audit=DEBUG
+
+#
+# Yarn ResourceManager Application Summary Log
+#
+# Set the ResourceManager summary log filename
+yarn.server.resourcemanager.appsummary.log.file=rm-appsummary.log
+# Set the ResourceManager summary log level and appender
+yarn.server.resourcemanager.appsummary.logger=${hadoop.root.logger}
+#yarn.server.resourcemanager.appsummary.logger=INFO,RMSUMMARY
+
+# To enable AppSummaryLogging for the RM,
+# set yarn.server.resourcemanager.appsummary.logger to
+# <LEVEL>,RMSUMMARY in hadoop-env.sh
+
+# Appender for ResourceManager Application Summary Log
+# Requires the following properties to be set
+#    - hadoop.log.dir (Hadoop Log directory)
+#    - yarn.server.resourcemanager.appsummary.log.file (resource manager app summary log filename)
+#    - yarn.server.resourcemanager.appsummary.logger (resource manager app summary log level and appender)
+
+log4j.logger.org.apache.hadoop.yarn.server.resourcemanager.RMAppManager$ApplicationSummary=${yarn.server.resourcemanager.appsummary.logger}
+log4j.additivity.org.apache.hadoop.yarn.server.resourcemanager.RMAppManager$ApplicationSummary=false
+log4j.appender.RMSUMMARY=org.apache.log4j.RollingFileAppender
+log4j.appender.RMSUMMARY.File=${hadoop.log.dir}/${yarn.server.resourcemanager.appsummary.log.file}
+log4j.appender.RMSUMMARY.MaxFileSize=256MB
+log4j.appender.RMSUMMARY.MaxBackupIndex=20
+log4j.appender.RMSUMMARY.layout=org.apache.log4j.PatternLayout
+log4j.appender.RMSUMMARY.layout.ConversionPattern=%d{ISO8601} %p %c{2}: %m%n
+
+# HS audit log configs
+#mapreduce.hs.audit.logger=INFO,HSAUDIT
+#log4j.logger.org.apache.hadoop.mapreduce.v2.hs.HSAuditLogger=${mapreduce.hs.audit.logger}
+#log4j.additivity.org.apache.hadoop.mapreduce.v2.hs.HSAuditLogger=false
+#log4j.appender.HSAUDIT=org.apache.log4j.DailyRollingFileAppender
+#log4j.appender.HSAUDIT.File=${hadoop.log.dir}/hs-audit.log
+#log4j.appender.HSAUDIT.layout=org.apache.log4j.PatternLayout
+#log4j.appender.HSAUDIT.layout.ConversionPattern=%d{ISO8601} %p %c{2}: %m%n
+#log4j.appender.HSAUDIT.DatePattern=.yyyy-MM-dd
+
+# Http Server Request Logs
+#log4j.logger.http.requests.namenode=INFO,namenoderequestlog
+#log4j.appender.namenoderequestlog=org.apache.hadoop.http.HttpRequestLogAppender
+#log4j.appender.namenoderequestlog.Filename=${hadoop.log.dir}/jetty-namenode-yyyy_mm_dd.log
+#log4j.appender.namenoderequestlog.RetainDays=3
+
+#log4j.logger.http.requests.datanode=INFO,datanoderequestlog
+#log4j.appender.datanoderequestlog=org.apache.hadoop.http.HttpRequestLogAppender
+#log4j.appender.datanoderequestlog.Filename=${hadoop.log.dir}/jetty-datanode-yyyy_mm_dd.log
+#log4j.appender.datanoderequestlog.RetainDays=3
+
+#log4j.logger.http.requests.resourcemanager=INFO,resourcemanagerrequestlog
+#log4j.appender.resourcemanagerrequestlog=org.apache.hadoop.http.HttpRequestLogAppender
+#log4j.appender.resourcemanagerrequestlog.Filename=${hadoop.log.dir}/jetty-resourcemanager-yyyy_mm_dd.log
+#log4j.appender.resourcemanagerrequestlog.RetainDays=3
+
+#log4j.logger.http.requests.jobhistory=INFO,jobhistoryrequestlog
+#log4j.appender.jobhistoryrequestlog=org.apache.hadoop.http.HttpRequestLogAppender
+#log4j.appender.jobhistoryrequestlog.Filename=${hadoop.log.dir}/jetty-jobhistory-yyyy_mm_dd.log
+#log4j.appender.jobhistoryrequestlog.RetainDays=3
+
+#log4j.logger.http.requests.nodemanager=INFO,nodemanagerrequestlog
+#log4j.appender.nodemanagerrequestlog=org.apache.hadoop.http.HttpRequestLogAppender
+#log4j.appender.nodemanagerrequestlog.Filename=${hadoop.log.dir}/jetty-nodemanager-yyyy_mm_dd.log
+#log4j.appender.nodemanagerrequestlog.RetainDays=3
+
+
+# WebHdfs request log on datanodes
+# Specify -Ddatanode.webhdfs.logger=INFO,HTTPDRFA on datanode startup to
+# direct the log to a separate file.
+#datanode.webhdfs.logger=INFO,console
+#log4j.logger.datanode.webhdfs=${datanode.webhdfs.logger}
+#log4j.appender.HTTPDRFA=org.apache.log4j.DailyRollingFileAppender
+#log4j.appender.HTTPDRFA.File=${hadoop.log.dir}/hadoop-datanode-webhdfs.log
+#log4j.appender.HTTPDRFA.layout=org.apache.log4j.PatternLayout
+#log4j.appender.HTTPDRFA.layout.ConversionPattern=%d{ISO8601} %m%n
+#log4j.appender.HTTPDRFA.DatePattern=.yyyy-MM-dd
+
+
+# Appender for viewing information for errors and warnings
+yarn.ewma.cleanupInterval=300
+yarn.ewma.messageAgeLimitSeconds=86400
+yarn.ewma.maxUniqueMessages=250
+log4j.appender.EWMA=org.apache.hadoop.yarn.util.Log4jWarningErrorMetricsAppender
+log4j.appender.EWMA.cleanupInterval=${yarn.ewma.cleanupInterval}
+log4j.appender.EWMA.messageAgeLimitSeconds=${yarn.ewma.messageAgeLimitSeconds}
+log4j.appender.EWMA.maxUniqueMessages=${yarn.ewma.maxUniqueMessages}
diff --git a/log4j-1.2-api/src/src/test/resources/config-1.2/hadoop/hadoop-common-project/hadoop-common/src/test/resources/log4j.properties b/log4j-1.2-api/src/src/test/resources/config-1.2/hadoop/hadoop-common-project/hadoop-common/src/test/resources/log4j.properties
new file mode 100644
index 0000000..ced0687
--- /dev/null
+++ b/log4j-1.2-api/src/src/test/resources/config-1.2/hadoop/hadoop-common-project/hadoop-common/src/test/resources/log4j.properties
@@ -0,0 +1,18 @@
+#   Licensed 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 configuration used during build and unit tests
+
+log4j.rootLogger=info,stdout
+log4j.threshold=ALL
+log4j.appender.stdout=org.apache.log4j.ConsoleAppender
+log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
+log4j.appender.stdout.layout.ConversionPattern=%d{ISO8601} %-5p %c{2} (%F:%M(%L)) - %m%n
diff --git a/log4j-1.2-api/src/src/test/resources/config-1.2/hadoop/hadoop-common-project/hadoop-kms/src/test/resources/log4j.properties b/log4j-1.2-api/src/src/test/resources/config-1.2/hadoop/hadoop-common-project/hadoop-kms/src/test/resources/log4j.properties
new file mode 100644
index 0000000..b347d27
--- /dev/null
+++ b/log4j-1.2-api/src/src/test/resources/config-1.2/hadoop/hadoop-common-project/hadoop-kms/src/test/resources/log4j.properties
@@ -0,0 +1,31 @@
+#
+# 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.
+#
+
+# STDOUT Appender
+log4j.appender.stdout=org.apache.log4j.ConsoleAppender
+log4j.appender.stdout.Target=System.out
+log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
+log4j.appender.stdout.layout.ConversionPattern=%d{ISO8601} %-5p %c{1} - %m%n
+
+log4j.rootLogger=INFO, stdout
+log4j.logger.org.apache.hadoop.conf=ERROR
+log4j.logger.org.apache.hadoop.crytpo.key.kms.server=ALL
+log4j.logger.com.sun.jersey.server.wadl.generators.WadlGeneratorJAXBGrammarGenerator=OFF
+log4j.logger.org.apache.hadoop.security=OFF
+log4j.logger.org.apache.directory.server.core=OFF
+log4j.logger.org.apache.hadoop.util.NativeCodeLoader=OFF
\ No newline at end of file
diff --git a/log4j-1.2-api/src/src/test/resources/config-1.2/hadoop/hadoop-common-project/hadoop-minikdc/src/main/resources/log4j.properties b/log4j-1.2-api/src/src/test/resources/config-1.2/hadoop/hadoop-common-project/hadoop-minikdc/src/main/resources/log4j.properties
new file mode 100644
index 0000000..9efd671
--- /dev/null
+++ b/log4j-1.2-api/src/src/test/resources/config-1.2/hadoop/hadoop-common-project/hadoop-minikdc/src/main/resources/log4j.properties
@@ -0,0 +1,31 @@
+#
+# 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.
+#
+
+# STDOUT Appender
+log4j.appender.stdout=org.apache.log4j.ConsoleAppender
+log4j.appender.stdout.Target=System.err
+log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
+log4j.appender.stdout.layout.ConversionPattern=%d{ISO8601} %-5p %c{1} - %m%n
+
+log4j.rootLogger=INFO, stdout
+
+# Switching off most of Apache DS logqing which is QUITE verbose
+log4j.logger.org.apache.directory=OFF
+log4j.logger.org.apache.directory.server.kerberos=INFO, stdout
+log4j.additivity.org.apache.directory=false
+log4j.logger.net.sf.ehcache=INFO, stdout
\ No newline at end of file
diff --git a/log4j-1.2-api/src/src/test/resources/config-1.2/hadoop/hadoop-common-project/hadoop-nfs/src/test/resources/log4j.properties b/log4j-1.2-api/src/src/test/resources/config-1.2/hadoop/hadoop-common-project/hadoop-nfs/src/test/resources/log4j.properties
new file mode 100644
index 0000000..ced0687
--- /dev/null
+++ b/log4j-1.2-api/src/src/test/resources/config-1.2/hadoop/hadoop-common-project/hadoop-nfs/src/test/resources/log4j.properties
@@ -0,0 +1,18 @@
+#   Licensed 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 configuration used during build and unit tests
+
+log4j.rootLogger=info,stdout
+log4j.threshold=ALL
+log4j.appender.stdout=org.apache.log4j.ConsoleAppender
+log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
+log4j.appender.stdout.layout.ConversionPattern=%d{ISO8601} %-5p %c{2} (%F:%M(%L)) - %m%n
diff --git a/log4j-1.2-api/src/src/test/resources/config-1.2/hadoop/hadoop-hdfs-project/hadoop-hdfs-client/src/test/resources/log4j.properties b/log4j-1.2-api/src/src/test/resources/config-1.2/hadoop/hadoop-hdfs-project/hadoop-hdfs-client/src/test/resources/log4j.properties
new file mode 100644
index 0000000..7378846
--- /dev/null
+++ b/log4j-1.2-api/src/src/test/resources/config-1.2/hadoop/hadoop-hdfs-project/hadoop-hdfs-client/src/test/resources/log4j.properties
@@ -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.
+#
+# log4j configuration used during build and unit tests
+
+log4j.rootLogger=info,stdout
+log4j.threshold=ALL
+log4j.appender.stdout=org.apache.log4j.ConsoleAppender
+log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
+log4j.appender.stdout.layout.ConversionPattern=%d{ISO8601} [%t] %-5p %c{2} (%F:%M(%L)) - %m%n
+
+#
+# NameNode metrics logging.
+# The default is to retain two namenode-metrics.log files up to 64MB each.
+#
+log4j.logger.NameNodeMetricsLog=INFO,NNMETRICSRFA
+log4j.additivity.NameNodeMetricsLog=false
+log4j.appender.NNMETRICSRFA=org.apache.log4j.RollingFileAppender
+log4j.appender.NNMETRICSRFA.File=${hadoop.log.dir}/namenode-metrics.log
+log4j.appender.NNMETRICSRFA.layout=org.apache.log4j.PatternLayout
+log4j.appender.NNMETRICSRFA.layout.ConversionPattern=%d{ISO8601} %m%n
+log4j.appender.NNMETRICSRFA.MaxBackupIndex=1
+log4j.appender.NNMETRICSRFA.MaxFileSize=64MB
+
+#
+# DataNode metrics logging.
+# The default is to retain two datanode-metrics.log files up to 64MB each.
+#
+log4j.logger.DataNodeMetricsLog=INFO,DNMETRICSRFA
+log4j.additivity.DataNodeMetricsLog=false
+log4j.appender.DNMETRICSRFA=org.apache.log4j.RollingFileAppender
+log4j.appender.DNMETRICSRFA.File=${hadoop.log.dir}/datanode-metrics.log
+log4j.appender.DNMETRICSRFA.layout=org.apache.log4j.PatternLayout
+log4j.appender.DNMETRICSRFA.layout.ConversionPattern=%d{ISO8601} %m%n
+log4j.appender.DNMETRICSRFA.MaxBackupIndex=1
+log4j.appender.DNMETRICSRFA.MaxFileSize=64MB
diff --git a/log4j-1.2-api/src/src/test/resources/config-1.2/hadoop/hadoop-hdfs-project/hadoop-hdfs/src/contrib/bkjournal/src/test/resources/log4j.properties b/log4j-1.2-api/src/src/test/resources/config-1.2/hadoop/hadoop-hdfs-project/hadoop-hdfs/src/contrib/bkjournal/src/test/resources/log4j.properties
new file mode 100644
index 0000000..52aac43
--- /dev/null
+++ b/log4j-1.2-api/src/src/test/resources/config-1.2/hadoop/hadoop-hdfs-project/hadoop-hdfs/src/contrib/bkjournal/src/test/resources/log4j.properties
@@ -0,0 +1,55 @@
+#
+# 
+# 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.
+# 
+#
+
+#
+# Bookkeeper Journal Logging Configuration
+#
+
+# Format is "<default threshold> (, <appender>)+
+
+# DEFAULT: console appender only
+log4j.rootLogger=DEBUG, CONSOLE
+
+# Example with rolling log file
+#log4j.rootLogger=DEBUG, CONSOLE, ROLLINGFILE
+
+# Example with rolling log file and tracing
+#log4j.rootLogger=TRACE, CONSOLE, ROLLINGFILE, TRACEFILE
+
+#
+# Log INFO level and above messages to the console
+#
+log4j.appender.CONSOLE=org.apache.log4j.ConsoleAppender
+log4j.appender.CONSOLE.Threshold=INFO
+log4j.appender.CONSOLE.layout=org.apache.log4j.PatternLayout
+log4j.appender.CONSOLE.layout.ConversionPattern=%d{ISO8601} - %-5p - [%t:%C{1}@%L] - %m%n
+
+#
+# Add ROLLINGFILE to rootLogger to get log file output
+#    Log DEBUG level and above messages to a log file
+log4j.appender.ROLLINGFILE=org.apache.log4j.DailyRollingFileAppender
+log4j.appender.ROLLINGFILE.Threshold=DEBUG
+log4j.appender.ROLLINGFILE.File=hdfs-namenode.log
+log4j.appender.ROLLINGFILE.layout=org.apache.log4j.PatternLayout
+log4j.appender.ROLLINGFILE.layout.ConversionPattern=%d{ISO8601} - %-5p - [%t:%C{1}@%L] - %m%n
+
+# Max log file size of 10MB
+log4j.appender.ROLLINGFILE.MaxFileSize=10MB
diff --git a/log4j-1.2-api/src/src/test/resources/config-1.2/hadoop/hadoop-hdfs-project/hadoop-hdfs/src/test/resources/log4j.properties b/log4j-1.2-api/src/src/test/resources/config-1.2/hadoop/hadoop-hdfs-project/hadoop-hdfs/src/test/resources/log4j.properties
new file mode 100644
index 0000000..7378846
--- /dev/null
+++ b/log4j-1.2-api/src/src/test/resources/config-1.2/hadoop/hadoop-hdfs-project/hadoop-hdfs/src/test/resources/log4j.properties
@@ -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.
+#
+# log4j configuration used during build and unit tests
+
+log4j.rootLogger=info,stdout
+log4j.threshold=ALL
+log4j.appender.stdout=org.apache.log4j.ConsoleAppender
+log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
+log4j.appender.stdout.layout.ConversionPattern=%d{ISO8601} [%t] %-5p %c{2} (%F:%M(%L)) - %m%n
+
+#
+# NameNode metrics logging.
+# The default is to retain two namenode-metrics.log files up to 64MB each.
+#
+log4j.logger.NameNodeMetricsLog=INFO,NNMETRICSRFA
+log4j.additivity.NameNodeMetricsLog=false
+log4j.appender.NNMETRICSRFA=org.apache.log4j.RollingFileAppender
+log4j.appender.NNMETRICSRFA.File=${hadoop.log.dir}/namenode-metrics.log
+log4j.appender.NNMETRICSRFA.layout=org.apache.log4j.PatternLayout
+log4j.appender.NNMETRICSRFA.layout.ConversionPattern=%d{ISO8601} %m%n
+log4j.appender.NNMETRICSRFA.MaxBackupIndex=1
+log4j.appender.NNMETRICSRFA.MaxFileSize=64MB
+
+#
+# DataNode metrics logging.
+# The default is to retain two datanode-metrics.log files up to 64MB each.
+#
+log4j.logger.DataNodeMetricsLog=INFO,DNMETRICSRFA
+log4j.additivity.DataNodeMetricsLog=false
+log4j.appender.DNMETRICSRFA=org.apache.log4j.RollingFileAppender
+log4j.appender.DNMETRICSRFA.File=${hadoop.log.dir}/datanode-metrics.log
+log4j.appender.DNMETRICSRFA.layout=org.apache.log4j.PatternLayout
+log4j.appender.DNMETRICSRFA.layout.ConversionPattern=%d{ISO8601} %m%n
+log4j.appender.DNMETRICSRFA.MaxBackupIndex=1
+log4j.appender.DNMETRICSRFA.MaxFileSize=64MB
diff --git a/log4j-1.2-api/src/src/test/resources/config-1.2/hadoop/hadoop-mapreduce-project/hadoop-mapreduce-client/hadoop-mapreduce-client-app/src/test/resources/log4j.properties b/log4j-1.2-api/src/src/test/resources/config-1.2/hadoop/hadoop-mapreduce-project/hadoop-mapreduce-client/hadoop-mapreduce-client-app/src/test/resources/log4j.properties
new file mode 100644
index 0000000..81a3f6a
--- /dev/null
+++ b/log4j-1.2-api/src/src/test/resources/config-1.2/hadoop/hadoop-mapreduce-project/hadoop-mapreduce-client/hadoop-mapreduce-client-app/src/test/resources/log4j.properties
@@ -0,0 +1,19 @@
+#   Licensed 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 configuration used during build and unit tests
+
+log4j.rootLogger=info,stdout
+log4j.threshold=ALL
+log4j.appender.stdout=org.apache.log4j.ConsoleAppender
+log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
+log4j.appender.stdout.layout.ConversionPattern=%d{ISO8601} %-5p [%t] %c{2} (%F:%M(%L)) - %m%n
diff --git a/log4j-1.2-api/src/src/test/resources/config-1.2/hadoop/hadoop-mapreduce-project/hadoop-mapreduce-client/hadoop-mapreduce-client-common/src/test/resources/log4j.properties b/log4j-1.2-api/src/src/test/resources/config-1.2/hadoop/hadoop-mapreduce-project/hadoop-mapreduce-client/hadoop-mapreduce-client-common/src/test/resources/log4j.properties
new file mode 100644
index 0000000..81a3f6a
--- /dev/null
+++ b/log4j-1.2-api/src/src/test/resources/config-1.2/hadoop/hadoop-mapreduce-project/hadoop-mapreduce-client/hadoop-mapreduce-client-common/src/test/resources/log4j.properties
@@ -0,0 +1,19 @@
+#   Licensed 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 configuration used during build and unit tests
+
+log4j.rootLogger=info,stdout
+log4j.threshold=ALL
+log4j.appender.stdout=org.apache.log4j.ConsoleAppender
+log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
+log4j.appender.stdout.layout.ConversionPattern=%d{ISO8601} %-5p [%t] %c{2} (%F:%M(%L)) - %m%n
diff --git a/log4j-1.2-api/src/src/test/resources/config-1.2/hadoop/hadoop-mapreduce-project/hadoop-mapreduce-client/hadoop-mapreduce-client-core/src/test/resources/log4j.properties b/log4j-1.2-api/src/src/test/resources/config-1.2/hadoop/hadoop-mapreduce-project/hadoop-mapreduce-client/hadoop-mapreduce-client-core/src/test/resources/log4j.properties
new file mode 100644
index 0000000..81a3f6a
--- /dev/null
+++ b/log4j-1.2-api/src/src/test/resources/config-1.2/hadoop/hadoop-mapreduce-project/hadoop-mapreduce-client/hadoop-mapreduce-client-core/src/test/resources/log4j.properties
@@ -0,0 +1,19 @@
+#   Licensed 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 configuration used during build and unit tests
+
+log4j.rootLogger=info,stdout
+log4j.threshold=ALL
+log4j.appender.stdout=org.apache.log4j.ConsoleAppender
+log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
+log4j.appender.stdout.layout.ConversionPattern=%d{ISO8601} %-5p [%t] %c{2} (%F:%M(%L)) - %m%n
diff --git a/log4j-1.2-api/src/src/test/resources/config-1.2/hadoop/hadoop-mapreduce-project/hadoop-mapreduce-client/hadoop-mapreduce-client-hs/src/test/resources/log4j.properties b/log4j-1.2-api/src/src/test/resources/config-1.2/hadoop/hadoop-mapreduce-project/hadoop-mapreduce-client/hadoop-mapreduce-client-hs/src/test/resources/log4j.properties
new file mode 100644
index 0000000..81a3f6a
--- /dev/null
+++ b/log4j-1.2-api/src/src/test/resources/config-1.2/hadoop/hadoop-mapreduce-project/hadoop-mapreduce-client/hadoop-mapreduce-client-hs/src/test/resources/log4j.properties
@@ -0,0 +1,19 @@
+#   Licensed 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 configuration used during build and unit tests
+
+log4j.rootLogger=info,stdout
+log4j.threshold=ALL
+log4j.appender.stdout=org.apache.log4j.ConsoleAppender
+log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
+log4j.appender.stdout.layout.ConversionPattern=%d{ISO8601} %-5p [%t] %c{2} (%F:%M(%L)) - %m%n
diff --git a/log4j-1.2-api/src/src/test/resources/config-1.2/hadoop/hadoop-mapreduce-project/hadoop-mapreduce-client/hadoop-mapreduce-client-jobclient/src/test/resources/log4j.properties b/log4j-1.2-api/src/src/test/resources/config-1.2/hadoop/hadoop-mapreduce-project/hadoop-mapreduce-client/hadoop-mapreduce-client-jobclient/src/test/resources/log4j.properties
new file mode 100644
index 0000000..81a3f6a
--- /dev/null
+++ b/log4j-1.2-api/src/src/test/resources/config-1.2/hadoop/hadoop-mapreduce-project/hadoop-mapreduce-client/hadoop-mapreduce-client-jobclient/src/test/resources/log4j.properties
@@ -0,0 +1,19 @@
+#   Licensed 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 configuration used during build and unit tests
+
+log4j.rootLogger=info,stdout
+log4j.threshold=ALL
+log4j.appender.stdout=org.apache.log4j.ConsoleAppender
+log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
+log4j.appender.stdout.layout.ConversionPattern=%d{ISO8601} %-5p [%t] %c{2} (%F:%M(%L)) - %m%n
diff --git a/log4j-1.2-api/src/src/test/resources/config-1.2/hadoop/hadoop-mapreduce-project/hadoop-mapreduce-client/hadoop-mapreduce-client-nativetask/src/test/resources/log4j.properties b/log4j-1.2-api/src/src/test/resources/config-1.2/hadoop/hadoop-mapreduce-project/hadoop-mapreduce-client/hadoop-mapreduce-client-nativetask/src/test/resources/log4j.properties
new file mode 100644
index 0000000..81a3f6a
--- /dev/null
+++ b/log4j-1.2-api/src/src/test/resources/config-1.2/hadoop/hadoop-mapreduce-project/hadoop-mapreduce-client/hadoop-mapreduce-client-nativetask/src/test/resources/log4j.properties
@@ -0,0 +1,19 @@
+#   Licensed 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 configuration used during build and unit tests
+
+log4j.rootLogger=info,stdout
+log4j.threshold=ALL
+log4j.appender.stdout=org.apache.log4j.ConsoleAppender
+log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
+log4j.appender.stdout.layout.ConversionPattern=%d{ISO8601} %-5p [%t] %c{2} (%F:%M(%L)) - %m%n
diff --git a/log4j-1.2-api/src/src/test/resources/config-1.2/hadoop/hadoop-mapreduce-project/hadoop-mapreduce-client/hadoop-mapreduce-client-shuffle/src/test/resources/log4j.properties b/log4j-1.2-api/src/src/test/resources/config-1.2/hadoop/hadoop-mapreduce-project/hadoop-mapreduce-client/hadoop-mapreduce-client-shuffle/src/test/resources/log4j.properties
new file mode 100644
index 0000000..81a3f6a
--- /dev/null
+++ b/log4j-1.2-api/src/src/test/resources/config-1.2/hadoop/hadoop-mapreduce-project/hadoop-mapreduce-client/hadoop-mapreduce-client-shuffle/src/test/resources/log4j.properties
@@ -0,0 +1,19 @@
+#   Licensed 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 configuration used during build and unit tests
+
+log4j.rootLogger=info,stdout
+log4j.threshold=ALL
+log4j.appender.stdout=org.apache.log4j.ConsoleAppender
+log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
+log4j.appender.stdout.layout.ConversionPattern=%d{ISO8601} %-5p [%t] %c{2} (%F:%M(%L)) - %m%n
diff --git a/log4j-1.2-api/src/src/test/resources/config-1.2/hadoop/hadoop-tools/hadoop-aws/src/test/resources/log4j.properties b/log4j-1.2-api/src/src/test/resources/config-1.2/hadoop/hadoop-tools/hadoop-aws/src/test/resources/log4j.properties
new file mode 100644
index 0000000..1330ed1
--- /dev/null
+++ b/log4j-1.2-api/src/src/test/resources/config-1.2/hadoop/hadoop-tools/hadoop-aws/src/test/resources/log4j.properties
@@ -0,0 +1,23 @@
+#   Licensed 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 configuration used during build and unit tests
+
+log4j.rootLogger=info,stdout
+log4j.threshold=ALL
+log4j.appender.stdout=org.apache.log4j.ConsoleAppender
+log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
+log4j.appender.stdout.layout.ConversionPattern=%d{ISO8601} [%t] %-5p %c{2} (%F:%M(%L)) - %m%n
+
+log4j.logger.org.apache.hadoop.util.NativeCodeLoader=ERROR
+
+# for debugging low level S3a operations, uncomment this line
+# log4j.logger.org.apache.hadoop.fs.s3a=DEBUG
diff --git a/log4j-1.2-api/src/src/test/resources/config-1.2/hadoop/hadoop-tools/hadoop-azure/src/test/resources/log4j.properties b/log4j-1.2-api/src/src/test/resources/config-1.2/hadoop/hadoop-tools/hadoop-azure/src/test/resources/log4j.properties
new file mode 100644
index 0000000..73ee3f9
--- /dev/null
+++ b/log4j-1.2-api/src/src/test/resources/config-1.2/hadoop/hadoop-tools/hadoop-azure/src/test/resources/log4j.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 configuration used during build and unit tests
+
+log4j.rootLogger=INFO,stdout
+log4j.threshold=ALL
+log4j.appender.stdout=org.apache.log4j.ConsoleAppender
+log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
+log4j.appender.stdout.layout.ConversionPattern=%d{ISO8601} %-5p [%t]: %c{2} (%F:%M(%L)) - %m%n
+
+log4j.logger.org.apache.hadoop.fs.azure.AzureFileSystemThreadPoolExecutor=DEBUG
diff --git a/log4j-1.2-api/src/src/test/resources/config-1.2/hadoop/hadoop-tools/hadoop-openstack/src/test/resources/log4j.properties b/log4j-1.2-api/src/src/test/resources/config-1.2/hadoop/hadoop-tools/hadoop-openstack/src/test/resources/log4j.properties
new file mode 100644
index 0000000..6aeb41d
--- /dev/null
+++ b/log4j-1.2-api/src/src/test/resources/config-1.2/hadoop/hadoop-tools/hadoop-openstack/src/test/resources/log4j.properties
@@ -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.
+#
+
+#   Licensed 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 configuration used during build and unit tests
+
+log4j.rootLogger=INFO,stdout
+log4j.threshold=ALL
+log4j.appender.stdout=org.apache.log4j.ConsoleAppender
+log4j.appender.stdout.target=System.out
+log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
+log4j.appender.stdout.layout.ConversionPattern=%d{ISO8601} [%t] %-5p %c{2} (%F:%M(%L)) - %m%n
+#log4j.appender.stdout.layout.ConversionPattern=%d{ISO8601} [%t] %-5p %c %x - %m%n"
+#log4j.logger.org.apache.hadoop.fs.swift=DEBUG
+
+#crank back on warnings about -1 content length GETs
+log4j.logger.org.apache.commons.httpclient.HttpMethodBase=ERROR
diff --git a/log4j-1.2-api/src/src/test/resources/config-1.2/hadoop/hadoop-tools/hadoop-sls/src/main/sample-conf/log4j.properties b/log4j-1.2-api/src/src/test/resources/config-1.2/hadoop/hadoop-tools/hadoop-sls/src/main/sample-conf/log4j.properties
new file mode 100644
index 0000000..cfd405b
--- /dev/null
+++ b/log4j-1.2-api/src/src/test/resources/config-1.2/hadoop/hadoop-tools/hadoop-sls/src/main/sample-conf/log4j.properties
@@ -0,0 +1,19 @@
+#
+# Licensed 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. See accompanying LICENSE file.
+#
+log4j.appender.test=org.apache.log4j.ConsoleAppender
+log4j.appender.test.Target=System.out
+log4j.appender.test.layout=org.apache.log4j.PatternLayout
+log4j.appender.test.layout.ConversionPattern=%d{ABSOLUTE} %5p %c{1}:%L - %m%n
+
+log4j.logger=NONE, test
diff --git a/log4j-1.2-api/src/src/test/resources/config-1.2/hadoop/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-applications/hadoop-yarn-applications-distributedshell/src/test/resources/log4j.properties b/log4j-1.2-api/src/src/test/resources/config-1.2/hadoop/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-applications/hadoop-yarn-applications-distributedshell/src/test/resources/log4j.properties
new file mode 100644
index 0000000..81a3f6a
--- /dev/null
+++ b/log4j-1.2-api/src/src/test/resources/config-1.2/hadoop/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-applications/hadoop-yarn-applications-distributedshell/src/test/resources/log4j.properties
@@ -0,0 +1,19 @@
+#   Licensed 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 configuration used during build and unit tests
+
+log4j.rootLogger=info,stdout
+log4j.threshold=ALL
+log4j.appender.stdout=org.apache.log4j.ConsoleAppender
+log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
+log4j.appender.stdout.layout.ConversionPattern=%d{ISO8601} %-5p [%t] %c{2} (%F:%M(%L)) - %m%n
diff --git a/log4j-1.2-api/src/src/test/resources/config-1.2/hadoop/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-applications/hadoop-yarn-applications-unmanaged-am-launcher/src/test/resources/log4j.properties b/log4j-1.2-api/src/src/test/resources/config-1.2/hadoop/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-applications/hadoop-yarn-applications-unmanaged-am-launcher/src/test/resources/log4j.properties
new file mode 100644
index 0000000..e46856e
--- /dev/null
+++ b/log4j-1.2-api/src/src/test/resources/config-1.2/hadoop/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-applications/hadoop-yarn-applications-unmanaged-am-launcher/src/test/resources/log4j.properties
@@ -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.
+#
+
+#   Licensed 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 configuration used during build and unit tests
+
+log4j.rootLogger=INFO,stdout
+log4j.threshold=ALL
+log4j.appender.stdout=org.apache.log4j.ConsoleAppender
+log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
+log4j.appender.stdout.layout.ConversionPattern=%d{ISO8601} %-5p [%t] %c{2} (%F:%M(%L)) - %m%n
diff --git a/log4j-1.2-api/src/src/test/resources/config-1.2/hadoop/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-common/src/test/resources/log4j.properties b/log4j-1.2-api/src/src/test/resources/config-1.2/hadoop/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-common/src/test/resources/log4j.properties
new file mode 100644
index 0000000..81a3f6a
--- /dev/null
+++ b/log4j-1.2-api/src/src/test/resources/config-1.2/hadoop/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-common/src/test/resources/log4j.properties
@@ -0,0 +1,19 @@
+#   Licensed 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 configuration used during build and unit tests
+
+log4j.rootLogger=info,stdout
+log4j.threshold=ALL
+log4j.appender.stdout=org.apache.log4j.ConsoleAppender
+log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
+log4j.appender.stdout.layout.ConversionPattern=%d{ISO8601} %-5p [%t] %c{2} (%F:%M(%L)) - %m%n
diff --git a/log4j-1.2-api/src/src/test/resources/config-1.2/hadoop/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-registry/src/test/resources/log4j.properties b/log4j-1.2-api/src/src/test/resources/config-1.2/hadoop/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-registry/src/test/resources/log4j.properties
new file mode 100644
index 0000000..bed1abc
--- /dev/null
+++ b/log4j-1.2-api/src/src/test/resources/config-1.2/hadoop/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-registry/src/test/resources/log4j.properties
@@ -0,0 +1,63 @@
+# 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 configuration used during build and unit tests
+
+log4j.rootLogger=INFO,stdout
+log4j.threshold=ALL
+log4j.appender.stdout=org.apache.log4j.ConsoleAppender
+log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
+log4j.appender.stdout.layout.ConversionPattern=%d{ISO8601} [%t] %-5p %c{2} (%F:%M(%L)) - %m%n
+
+log4j.appender.subprocess=org.apache.log4j.ConsoleAppender
+log4j.appender.subprocess.layout=org.apache.log4j.PatternLayout
+log4j.appender.subprocess.layout.ConversionPattern=[%c{1}]: %m%n
+
+# packages under test
+log4j.logger.org.apache.hadoop.yarn.registry=DEBUG
+log4j.logger.org.apache.hadoop.service=DEBUG
+
+log4j.logger.org.apache.hadoop.security.UserGroupInformation=DEBUG
+
+
+#crank back on some noise
+log4j.logger.org.apache.hadoop.util.NativeCodeLoader=ERROR
+log4j.logger.org.apache.hadoop.hdfs.server.datanode.BlockPoolSliceScanner=WARN
+log4j.logger.org.apache.hadoop.hdfs.server.blockmanagement=WARN
+log4j.logger.org.apache.hadoop.hdfs.server.namenode.FSNamesystem.audit=WARN
+log4j.logger.org.apache.hadoop.hdfs=WARN
+
+
+log4j.logger.org.apache.hadoop.yarn.server.nodemanager.containermanager.monitor=WARN
+log4j.logger.org.apache.hadoop.yarn.server.nodemanager.NodeStatusUpdaterImpl=WARN
+log4j.logger.org.apache.zookeeper=INFO
+log4j.logger.org.apache.zookeeper.ClientCnxn=DEBUG
+
+log4j.logger.org.apache.hadoop.yarn.server.resourcemanager.security=WARN
+log4j.logger.org.apache.hadoop.metrics2=ERROR
+log4j.logger.org.apache.hadoop.util.HostsFileReader=WARN
+log4j.logger.org.apache.hadoop.yarn.event.AsyncDispatcher=WARN
+log4j.logger.org.apache.hadoop.security.token.delegation=WARN
+log4j.logger.org.apache.hadoop.yarn.util.AbstractLivelinessMonitor=WARN
+log4j.logger.org.apache.hadoop.yarn.server.nodemanager.security=WARN
+log4j.logger.org.apache.hadoop.yarn.server.resourcemanager.RMNMInfo=WARN
+
+# curator noise
+log4j.logger.org.apache.curator.framework.imps=WARN
+log4j.logger.org.apache.curator.framework.state.ConnectionStateManager=ERROR
+
+log4j.logger.org.apache.directory.api.ldap=ERROR
+log4j.logger.org.apache.directory.server=ERROR
\ No newline at end of file
diff --git a/log4j-1.2-api/src/src/test/resources/config-1.2/hadoop/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-common/src/test/resources/log4j.properties b/log4j-1.2-api/src/src/test/resources/config-1.2/hadoop/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-common/src/test/resources/log4j.properties
new file mode 100644
index 0000000..81a3f6a
--- /dev/null
+++ b/log4j-1.2-api/src/src/test/resources/config-1.2/hadoop/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-common/src/test/resources/log4j.properties
@@ -0,0 +1,19 @@
+#   Licensed 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 configuration used during build and unit tests
+
+log4j.rootLogger=info,stdout
+log4j.threshold=ALL
+log4j.appender.stdout=org.apache.log4j.ConsoleAppender
+log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
+log4j.appender.stdout.layout.ConversionPattern=%d{ISO8601} %-5p [%t] %c{2} (%F:%M(%L)) - %m%n
diff --git a/log4j-1.2-api/src/src/test/resources/config-1.2/hadoop/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-nodemanager/src/test/resources/log4j.properties b/log4j-1.2-api/src/src/test/resources/config-1.2/hadoop/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-nodemanager/src/test/resources/log4j.properties
new file mode 100644
index 0000000..81a3f6a
--- /dev/null
+++ b/log4j-1.2-api/src/src/test/resources/config-1.2/hadoop/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-nodemanager/src/test/resources/log4j.properties
@@ -0,0 +1,19 @@
+#   Licensed 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 configuration used during build and unit tests
+
+log4j.rootLogger=info,stdout
+log4j.threshold=ALL
+log4j.appender.stdout=org.apache.log4j.ConsoleAppender
+log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
+log4j.appender.stdout.layout.ConversionPattern=%d{ISO8601} %-5p [%t] %c{2} (%F:%M(%L)) - %m%n
diff --git a/log4j-1.2-api/src/src/test/resources/config-1.2/hadoop/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-resourcemanager/src/test/resources/log4j.properties b/log4j-1.2-api/src/src/test/resources/config-1.2/hadoop/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-resourcemanager/src/test/resources/log4j.properties
new file mode 100644
index 0000000..81a3f6a
--- /dev/null
+++ b/log4j-1.2-api/src/src/test/resources/config-1.2/hadoop/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-resourcemanager/src/test/resources/log4j.properties
@@ -0,0 +1,19 @@
+#   Licensed 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 configuration used during build and unit tests
+
+log4j.rootLogger=info,stdout
+log4j.threshold=ALL
+log4j.appender.stdout=org.apache.log4j.ConsoleAppender
+log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
+log4j.appender.stdout.layout.ConversionPattern=%d{ISO8601} %-5p [%t] %c{2} (%F:%M(%L)) - %m%n
diff --git a/log4j-1.2-api/src/src/test/resources/config-1.2/hadoop/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-tests/src/test/resources/log4j.properties b/log4j-1.2-api/src/src/test/resources/config-1.2/hadoop/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-tests/src/test/resources/log4j.properties
new file mode 100644
index 0000000..c088bb7
--- /dev/null
+++ b/log4j-1.2-api/src/src/test/resources/config-1.2/hadoop/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-tests/src/test/resources/log4j.properties
@@ -0,0 +1,19 @@
+#   Licensed 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 configuration used during build and unit tests
+
+log4j.rootLogger=INFO,stdout
+log4j.threshold=ALL
+log4j.appender.stdout=org.apache.log4j.ConsoleAppender
+log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
+log4j.appender.stdout.layout.ConversionPattern=%d{ISO8601} %-5p [%t] %c{2} (%F:%M(%L)) - %m%n
diff --git a/log4j-1.2-api/src/src/test/resources/config-1.2/hadoop/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-timelineservice-hbase-tests/src/test/resources/log4j.properties b/log4j-1.2-api/src/src/test/resources/config-1.2/hadoop/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-timelineservice-hbase-tests/src/test/resources/log4j.properties
new file mode 100644
index 0000000..81a3f6a
--- /dev/null
+++ b/log4j-1.2-api/src/src/test/resources/config-1.2/hadoop/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-timelineservice-hbase-tests/src/test/resources/log4j.properties
@@ -0,0 +1,19 @@
+#   Licensed 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 configuration used during build and unit tests
+
+log4j.rootLogger=info,stdout
+log4j.threshold=ALL
+log4j.appender.stdout=org.apache.log4j.ConsoleAppender
+log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
+log4j.appender.stdout.layout.ConversionPattern=%d{ISO8601} %-5p [%t] %c{2} (%F:%M(%L)) - %m%n
diff --git a/log4j-1.2-api/src/src/test/resources/config-1.2/hadoop/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-timelineservice/src/test/resources/log4j.properties b/log4j-1.2-api/src/src/test/resources/config-1.2/hadoop/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-timelineservice/src/test/resources/log4j.properties
new file mode 100644
index 0000000..81a3f6a
--- /dev/null
+++ b/log4j-1.2-api/src/src/test/resources/config-1.2/hadoop/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-timelineservice/src/test/resources/log4j.properties
@@ -0,0 +1,19 @@
+#   Licensed 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 configuration used during build and unit tests
+
+log4j.rootLogger=info,stdout
+log4j.threshold=ALL
+log4j.appender.stdout=org.apache.log4j.ConsoleAppender
+log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
+log4j.appender.stdout.layout.ConversionPattern=%d{ISO8601} %-5p [%t] %c{2} (%F:%M(%L)) - %m%n
diff --git a/log4j-1.2-api/src/src/test/resources/config-1.2/log4j-DailyRollingFileAppender.properties b/log4j-1.2-api/src/src/test/resources/config-1.2/log4j-DailyRollingFileAppender.properties
new file mode 100644
index 0000000..123a51d
--- /dev/null
+++ b/log4j-1.2-api/src/src/test/resources/config-1.2/log4j-DailyRollingFileAppender.properties
@@ -0,0 +1,26 @@
+###############################################################################
+#
+# Log4J 1.2 Configuration.
+#
+
+hadoop.log.dir=target
+hadoop.log.file=hadoop.log
+
+log4j.rootLogger=TRACE, DRFA
+
+#
+# Daily Rolling File Appender
+#
+
+log4j.appender.DRFA=org.apache.log4j.DailyRollingFileAppender
+log4j.appender.DRFA.File=${hadoop.log.dir}/${hadoop.log.file}
+
+# Rollover at midnight
+log4j.appender.DRFA.DatePattern=.yyyy-MM-dd
+
+log4j.appender.DRFA.layout=org.apache.log4j.PatternLayout
+
+# Pattern format: Date LogLevel LoggerName LogMessage
+log4j.appender.DRFA.layout.ConversionPattern=%d{ISO8601} %p %c: %m%n
+# Debugging Pattern format
+#log4j.appender.DRFA.layout.ConversionPattern=%d{ISO8601} %-5p %c{2} (%F:%M(%L)) - %m%n
diff --git a/log4j-1.2-api/src/src/test/resources/config-1.2/log4j-NullAppender.properties b/log4j-1.2-api/src/src/test/resources/config-1.2/log4j-NullAppender.properties
new file mode 100644
index 0000000..d89a4f4
--- /dev/null
+++ b/log4j-1.2-api/src/src/test/resources/config-1.2/log4j-NullAppender.properties
@@ -0,0 +1,9 @@
+###############################################################################
+#
+# Log4J 1.2 Configuration.
+#
+
+log4j.rootLogger=TRACE, NullAppender
+
+# Null Appender
+log4j.appender.NullAppender=org.apache.log4j.varia.NullAppender
diff --git a/log4j-1.2-api/src/src/test/resources/config-1.2/log4j-RollingFileAppender-with-props.properties b/log4j-1.2-api/src/src/test/resources/config-1.2/log4j-RollingFileAppender-with-props.properties
new file mode 100644
index 0000000..b664bb8
--- /dev/null
+++ b/log4j-1.2-api/src/src/test/resources/config-1.2/log4j-RollingFileAppender-with-props.properties
@@ -0,0 +1,27 @@
+###############################################################################
+#
+# Log4J 1.2 Configuration.
+#
+
+hadoop.log.dir=target
+hadoop.log.file=hadoop.log
+
+log4j.rootLogger=TRACE, RFA
+
+#
+# Rolling File Appender - cap space usage at 5gb.
+#
+hadoop.log.maxfilesize=256MB
+hadoop.log.maxbackupindex=20
+log4j.appender.RFA=org.apache.log4j.RollingFileAppender
+log4j.appender.RFA.File=${hadoop.log.dir}/${hadoop.log.file}
+
+log4j.appender.RFA.MaxFileSize=${hadoop.log.maxfilesize}
+log4j.appender.RFA.MaxBackupIndex=${hadoop.log.maxbackupindex}
+
+log4j.appender.RFA.layout=org.apache.log4j.PatternLayout
+
+# Pattern format: Date LogLevel LoggerName LogMessage
+log4j.appender.RFA.layout.ConversionPattern=%d{ISO8601} %p %c: %m%n
+# Debugging Pattern format
+#log4j.appender.RFA.layout.ConversionPattern=%d{ISO8601} %-5p %c{2} (%F:%M(%L)) - %m%n
diff --git a/log4j-1.2-api/src/src/test/resources/config-1.2/log4j-RollingFileAppender.properties b/log4j-1.2-api/src/src/test/resources/config-1.2/log4j-RollingFileAppender.properties
new file mode 100644
index 0000000..55234ba
--- /dev/null
+++ b/log4j-1.2-api/src/src/test/resources/config-1.2/log4j-RollingFileAppender.properties
@@ -0,0 +1,22 @@
+###############################################################################
+#
+# Log4J 1.2 Configuration.
+#
+
+log4j.rootLogger=TRACE, RFA
+
+#
+# Rolling File Appender - cap space usage at 5gb.
+#
+log4j.appender.RFA=org.apache.log4j.RollingFileAppender
+log4j.appender.RFA.File=target/hadoop.log
+
+log4j.appender.RFA.MaxFileSize=256MB
+log4j.appender.RFA.MaxBackupIndex=20
+
+log4j.appender.RFA.layout=org.apache.log4j.PatternLayout
+
+# Pattern format: Date LogLevel LoggerName LogMessage
+log4j.appender.RFA.layout.ConversionPattern=%d{ISO8601} %p %c: %m%n
+# Debugging Pattern format
+#log4j.appender.RFA.layout.ConversionPattern=%d{ISO8601} %-5p %c{2} (%F:%M(%L)) - %m%n
diff --git a/log4j-1.2-api/src/src/test/resources/config-1.2/log4j-console-EnhancedPatternLayout.properties b/log4j-1.2-api/src/src/test/resources/config-1.2/log4j-console-EnhancedPatternLayout.properties
new file mode 100644
index 0000000..6793eb2
--- /dev/null
+++ b/log4j-1.2-api/src/src/test/resources/config-1.2/log4j-console-EnhancedPatternLayout.properties
@@ -0,0 +1,18 @@
+###############################################################################
+#
+# Log4J 1.2 Configuration.
+#
+
+log4j.rootLogger=TRACE, Console
+
+##############################################################################
+#
+# The Console log
+#
+
+log4j.appender.Console=org.apache.log4j.ConsoleAppender
+log4j.appender.Console.Target=System.err
+log4j.appender.Console.layout=org.apache.log4j.EnhancedPatternLayout
+log4j.appender.Console.layout.ConversionPattern=%d{ISO8601} [%t][%c] %-5p %X %x: %m%n
+
+log4j.logger.com.example.foo = DEBUG
diff --git a/log4j-1.2-api/src/src/test/resources/config-1.2/log4j-console-HtmlLayout.properties b/log4j-1.2-api/src/src/test/resources/config-1.2/log4j-console-HtmlLayout.properties
new file mode 100644
index 0000000..216a12e
--- /dev/null
+++ b/log4j-1.2-api/src/src/test/resources/config-1.2/log4j-console-HtmlLayout.properties
@@ -0,0 +1,19 @@
+###############################################################################
+#
+# Log4J 1.2 Configuration.
+#
+
+log4j.rootLogger=TRACE, Console
+
+##############################################################################
+#
+# The Console log
+#
+
+log4j.appender.Console=org.apache.log4j.ConsoleAppender
+log4j.appender.Console.Target=System.err
+log4j.appender.Console.layout=org.apache.log4j.HTMLLayout
+log4j.appender.Console.layout.Title=Headline
+log4j.appender.Console.layout.LocationInfo=true
+
+log4j.logger.com.example.foo = DEBUG
diff --git a/log4j-1.2-api/src/src/test/resources/config-1.2/log4j-console-PatternLayout.properties b/log4j-1.2-api/src/src/test/resources/config-1.2/log4j-console-PatternLayout.properties
new file mode 100644
index 0000000..810a494
--- /dev/null
+++ b/log4j-1.2-api/src/src/test/resources/config-1.2/log4j-console-PatternLayout.properties
@@ -0,0 +1,18 @@
+###############################################################################
+#
+# Log4J 1.2 Configuration.
+#
+
+log4j.rootLogger=TRACE, Console
+
+##############################################################################
+#
+# The Console log
+#
+
+log4j.appender.Console=org.apache.log4j.ConsoleAppender
+log4j.appender.Console.Target=System.err
+log4j.appender.Console.layout=org.apache.log4j.PatternLayout
+log4j.appender.Console.layout.ConversionPattern=%d{ISO8601} [%t][%c] %-5p: %m%n
+
+log4j.logger.com.example.foo = DEBUG
diff --git a/log4j-1.2-api/src/src/test/resources/config-1.2/log4j-console-SimpleLayout.properties b/log4j-1.2-api/src/src/test/resources/config-1.2/log4j-console-SimpleLayout.properties
new file mode 100644
index 0000000..5a8ac4e
--- /dev/null
+++ b/log4j-1.2-api/src/src/test/resources/config-1.2/log4j-console-SimpleLayout.properties
@@ -0,0 +1,17 @@
+###############################################################################
+#
+# Log4J 1.2 Configuration.
+#
+
+log4j.rootLogger=TRACE, Console
+
+##############################################################################
+#
+# The Console log
+#
+
+log4j.appender.Console=org.apache.log4j.ConsoleAppender
+log4j.appender.Console.Target=System.err
+log4j.appender.Console.layout=org.apache.log4j.SimpleLayout
+
+log4j.logger.com.example.foo = DEBUG
diff --git a/log4j-1.2-api/src/src/test/resources/config-1.2/log4j-console-TTCCLayout.properties b/log4j-1.2-api/src/src/test/resources/config-1.2/log4j-console-TTCCLayout.properties
new file mode 100644
index 0000000..80d38c2
--- /dev/null
+++ b/log4j-1.2-api/src/src/test/resources/config-1.2/log4j-console-TTCCLayout.properties
@@ -0,0 +1,19 @@
+###############################################################################
+#
+# Log4J 1.2 Configuration.
+#
+
+log4j.rootLogger=TRACE, Console
+
+##############################################################################
+#
+# The Console log
+#
+
+log4j.appender.Console=org.apache.log4j.ConsoleAppender
+log4j.appender.Console.Target=System.err
+log4j.appender.Console.layout=org.apache.log4j.TTCCLayout
+log4j.appender.Console.layout.ThreadPrinting=true
+log4j.appender.Console.layout.CategoryPrefixing=false
+
+log4j.logger.com.example.foo = DEBUG
diff --git a/log4j-1.2-api/src/src/test/resources/config-1.2/log4j-console-XmlLayout.properties b/log4j-1.2-api/src/src/test/resources/config-1.2/log4j-console-XmlLayout.properties
new file mode 100644
index 0000000..c8190ec
--- /dev/null
+++ b/log4j-1.2-api/src/src/test/resources/config-1.2/log4j-console-XmlLayout.properties
@@ -0,0 +1,19 @@
+###############################################################################
+#
+# Log4J 1.2 Configuration.
+#
+
+log4j.rootLogger=TRACE, Console
+
+##############################################################################
+#
+# The Console log
+#
+
+log4j.appender.Console=org.apache.log4j.ConsoleAppender
+log4j.appender.Console.Target=System.err
+log4j.appender.Console.layout=org.apache.log4j.xml.XMLLayout
+log4j.appender.Console.layout.LocationInfo=true
+log4j.appender.Console.layout.Properties=false
+
+log4j.logger.com.example.foo = DEBUG
diff --git a/log4j-1.2-api/src/src/test/resources/config-1.2/log4j-file-SimpleLayout.properties b/log4j-1.2-api/src/src/test/resources/config-1.2/log4j-file-SimpleLayout.properties
new file mode 100644
index 0000000..4d3ec0d
--- /dev/null
+++ b/log4j-1.2-api/src/src/test/resources/config-1.2/log4j-file-SimpleLayout.properties
@@ -0,0 +1,17 @@
+###############################################################################
+#
+# Log4J 1.2 Configuration.
+#
+
+log4j.rootLogger=TRACE, File
+
+##############################################################################
+#
+# The Console log
+#
+
+log4j.appender.File=org.apache.log4j.FileAppender
+log4j.appender.File.File=target/mylog.txt
+log4j.appender.File.layout=org.apache.log4j.SimpleLayout
+
+log4j.logger.com.example.foo = DEBUG
diff --git a/log4j-1.2-api/src/src/test/resources/config-1.2/log4j-system-properties-1.properties b/log4j-1.2-api/src/src/test/resources/config-1.2/log4j-system-properties-1.properties
new file mode 100644
index 0000000..a82c4c3
--- /dev/null
+++ b/log4j-1.2-api/src/src/test/resources/config-1.2/log4j-system-properties-1.properties
@@ -0,0 +1,14 @@
+###############################################################################
+#
+# Log4J 1.2 Configuration.
+#
+
+hadoop.log.file=hadoop.log
+
+log4j.rootLogger=TRACE, RFA
+
+#
+# Rolling File Appender
+#
+log4j.appender.RFA=org.apache.log4j.RollingFileAppender
+log4j.appender.RFA.File=${java.io.tmpdir}/${hadoop.log.file}
diff --git a/log4j-1.2-api/src/src/test/resources/config-1.2/log4j-system-properties-2.properties b/log4j-1.2-api/src/src/test/resources/config-1.2/log4j-system-properties-2.properties
new file mode 100644
index 0000000..9228434
--- /dev/null
+++ b/log4j-1.2-api/src/src/test/resources/config-1.2/log4j-system-properties-2.properties
@@ -0,0 +1,15 @@
+###############################################################################
+#
+# Log4J 1.2 Configuration.
+#
+
+hadoop.log.dir=${java.io.tmpdir}
+hadoop.log.file=hadoop.log
+
+log4j.rootLogger=TRACE, RFA
+
+#
+# Rolling File Appender
+#
+log4j.appender.RFA=org.apache.log4j.RollingFileAppender
+log4j.appender.RFA.File=${hadoop.log.dir}/${hadoop.log.file}
diff --git a/log4j-1.2-api/src/src/test/resources/config-1.2/spark/R/log4j.properties b/log4j-1.2-api/src/src/test/resources/config-1.2/spark/R/log4j.properties
new file mode 100644
index 0000000..cce8d91
--- /dev/null
+++ b/log4j-1.2-api/src/src/test/resources/config-1.2/spark/R/log4j.properties
@@ -0,0 +1,28 @@
+#
+# 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.
+#
+
+# Set everything to be logged to the file target/unit-tests.log
+log4j.rootCategory=INFO, file
+log4j.appender.file=org.apache.log4j.FileAppender
+log4j.appender.file.append=true
+log4j.appender.file.file=R/target/unit-tests.log
+log4j.appender.file.layout=org.apache.log4j.PatternLayout
+log4j.appender.file.layout.ConversionPattern=%d{yy/MM/dd HH:mm:ss.SSS} %t %p %c{1}: %m%n
+
+# Ignore messages below warning level from Jetty, because it's a bit verbose
+log4j.logger.org.eclipse.jetty=WARN
+org.eclipse.jetty.LEVEL=WARN
diff --git a/log4j-1.2-api/src/src/test/resources/config-1.2/spark/common/network-common/src/test/resources/log4j.properties b/log4j-1.2-api/src/src/test/resources/config-1.2/spark/common/network-common/src/test/resources/log4j.properties
new file mode 100644
index 0000000..e8da774
--- /dev/null
+++ b/log4j-1.2-api/src/src/test/resources/config-1.2/spark/common/network-common/src/test/resources/log4j.properties
@@ -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.
+#
+
+# Set everything to be logged to the file target/unit-tests.log
+log4j.rootCategory=DEBUG, file
+log4j.appender.file=org.apache.log4j.FileAppender
+log4j.appender.file.append=true
+log4j.appender.file.file=target/unit-tests.log
+log4j.appender.file.layout=org.apache.log4j.PatternLayout
+log4j.appender.file.layout.ConversionPattern=%d{yy/MM/dd HH:mm:ss.SSS} %t %p %c{1}: %m%n
+
+# Silence verbose logs from 3rd-party libraries.
+log4j.logger.io.netty=INFO
diff --git a/log4j-1.2-api/src/src/test/resources/config-1.2/spark/common/network-shuffle/src/test/resources/log4j.properties b/log4j-1.2-api/src/src/test/resources/config-1.2/spark/common/network-shuffle/src/test/resources/log4j.properties
new file mode 100644
index 0000000..e739789
--- /dev/null
+++ b/log4j-1.2-api/src/src/test/resources/config-1.2/spark/common/network-shuffle/src/test/resources/log4j.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.
+#
+
+# Set everything to be logged to the file target/unit-tests.log
+log4j.rootCategory=DEBUG, file
+log4j.appender.file=org.apache.log4j.FileAppender
+log4j.appender.file.append=true
+log4j.appender.file.file=target/unit-tests.log
+log4j.appender.file.layout=org.apache.log4j.PatternLayout
+log4j.appender.file.layout.ConversionPattern=%d{yy/MM/dd HH:mm:ss.SSS} %t %p %c{1}: %m%n
diff --git a/log4j-1.2-api/src/src/test/resources/config-1.2/spark/core/src/test/resources/log4j.properties b/log4j-1.2-api/src/src/test/resources/config-1.2/spark/core/src/test/resources/log4j.properties
new file mode 100644
index 0000000..fb9d985
--- /dev/null
+++ b/log4j-1.2-api/src/src/test/resources/config-1.2/spark/core/src/test/resources/log4j.properties
@@ -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.
+#
+
+# Set everything to be logged to the file target/unit-tests.log
+test.appender=file
+log4j.rootCategory=INFO, ${test.appender}
+log4j.appender.file=org.apache.log4j.FileAppender
+log4j.appender.file.append=true
+log4j.appender.file.file=target/unit-tests.log
+log4j.appender.file.layout=org.apache.log4j.PatternLayout
+log4j.appender.file.layout.ConversionPattern=%d{yy/MM/dd HH:mm:ss.SSS} %t %p %c{1}: %m%n
+
+# Tests that launch java subprocesses can set the "test.appender" system property to
+# "console" to avoid having the child process's logs overwrite the unit test's
+# log file.
+log4j.appender.console=org.apache.log4j.ConsoleAppender
+log4j.appender.console.target=System.err
+log4j.appender.console.layout=org.apache.log4j.PatternLayout
+log4j.appender.console.layout.ConversionPattern=%t: %m%n
+
+# Ignore messages below warning level from Jetty, because it's a bit verbose
+log4j.logger.org.spark_project.jetty=WARN
diff --git a/log4j-1.2-api/src/src/test/resources/config-1.2/spark/external/flume-sink/src/test/resources/log4j.properties b/log4j-1.2-api/src/src/test/resources/config-1.2/spark/external/flume-sink/src/test/resources/log4j.properties
new file mode 100644
index 0000000..1e3f163
--- /dev/null
+++ b/log4j-1.2-api/src/src/test/resources/config-1.2/spark/external/flume-sink/src/test/resources/log4j.properties
@@ -0,0 +1,28 @@
+#
+# 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.
+#
+
+# Set everything to be logged to the file streaming/target/unit-tests.log
+log4j.rootCategory=INFO, file
+log4j.appender.file=org.apache.log4j.FileAppender
+log4j.appender.file.append=true
+log4j.appender.file.file=target/unit-tests.log
+log4j.appender.file.layout=org.apache.log4j.PatternLayout
+log4j.appender.file.layout.ConversionPattern=%d{yy/MM/dd HH:mm:ss.SSS} %t %p %c{1}: %m%n
+
+# Ignore messages below warning level from Jetty, because it's a bit verbose
+log4j.logger.org.spark_project.jetty=WARN
+
diff --git a/log4j-1.2-api/src/src/test/resources/config-1.2/spark/external/flume/src/test/resources/log4j.properties b/log4j-1.2-api/src/src/test/resources/config-1.2/spark/external/flume/src/test/resources/log4j.properties
new file mode 100644
index 0000000..fd51f8f
--- /dev/null
+++ b/log4j-1.2-api/src/src/test/resources/config-1.2/spark/external/flume/src/test/resources/log4j.properties
@@ -0,0 +1,28 @@
+#
+# 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.
+#
+
+# Set everything to be logged to the file target/unit-tests.log
+log4j.rootCategory=INFO, file
+log4j.appender.file=org.apache.log4j.FileAppender
+log4j.appender.file.append=true
+log4j.appender.file.file=target/unit-tests.log
+log4j.appender.file.layout=org.apache.log4j.PatternLayout
+log4j.appender.file.layout.ConversionPattern=%d{yy/MM/dd HH:mm:ss.SSS} %t %p %c{1}: %m%n
+
+# Ignore messages below warning level from Jetty, because it's a bit verbose
+log4j.logger.org.spark_project.jetty=WARN
+
diff --git a/log4j-1.2-api/src/src/test/resources/config-1.2/spark/external/java8-tests/src/test/resources/log4j.properties b/log4j-1.2-api/src/src/test/resources/config-1.2/spark/external/java8-tests/src/test/resources/log4j.properties
new file mode 100644
index 0000000..3706a6e
--- /dev/null
+++ b/log4j-1.2-api/src/src/test/resources/config-1.2/spark/external/java8-tests/src/test/resources/log4j.properties
@@ -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.
+#
+
+# Set everything to be logged to the file target/unit-tests.log
+log4j.rootCategory=INFO, file
+log4j.appender.file=org.apache.log4j.FileAppender
+log4j.appender.file.append=true
+log4j.appender.file.file=target/unit-tests.log
+log4j.appender.file.layout=org.apache.log4j.PatternLayout
+log4j.appender.file.layout.ConversionPattern=%d{yy/MM/dd HH:mm:ss.SSS} %t %p %c{1}: %m%n
+
+# Ignore messages below warning level from Jetty, because it's a bit verbose
+log4j.logger.org.spark_project.jetty=WARN
diff --git a/log4j-1.2-api/src/src/test/resources/config-1.2/spark/external/kafka-0-10/src/test/resources/log4j.properties b/log4j-1.2-api/src/src/test/resources/config-1.2/spark/external/kafka-0-10/src/test/resources/log4j.properties
new file mode 100644
index 0000000..75e3b53
--- /dev/null
+++ b/log4j-1.2-api/src/src/test/resources/config-1.2/spark/external/kafka-0-10/src/test/resources/log4j.properties
@@ -0,0 +1,28 @@
+#
+# 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.
+#
+
+# Set everything to be logged to the file target/unit-tests.log
+log4j.rootCategory=INFO, file
+log4j.appender.file=org.apache.log4j.FileAppender
+log4j.appender.file.append=true
+log4j.appender.file.file=target/unit-tests.log
+log4j.appender.file.layout=org.apache.log4j.PatternLayout
+log4j.appender.file.layout.ConversionPattern=%d{yy/MM/dd HH:mm:ss.SSS} %t %p %c{1}: %m%n
+
+# Ignore messages below warning level from Jetty, because it's a bit verbose
+log4j.logger.org.spark-project.jetty=WARN
+
diff --git a/log4j-1.2-api/src/src/test/resources/config-1.2/spark/external/kafka-0-8/src/test/resources/log4j.properties b/log4j-1.2-api/src/src/test/resources/config-1.2/spark/external/kafka-0-8/src/test/resources/log4j.properties
new file mode 100644
index 0000000..fd51f8f
--- /dev/null
+++ b/log4j-1.2-api/src/src/test/resources/config-1.2/spark/external/kafka-0-8/src/test/resources/log4j.properties
@@ -0,0 +1,28 @@
+#
+# 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.
+#
+
+# Set everything to be logged to the file target/unit-tests.log
+log4j.rootCategory=INFO, file
+log4j.appender.file=org.apache.log4j.FileAppender
+log4j.appender.file.append=true
+log4j.appender.file.file=target/unit-tests.log
+log4j.appender.file.layout=org.apache.log4j.PatternLayout
+log4j.appender.file.layout.ConversionPattern=%d{yy/MM/dd HH:mm:ss.SSS} %t %p %c{1}: %m%n
+
+# Ignore messages below warning level from Jetty, because it's a bit verbose
+log4j.logger.org.spark_project.jetty=WARN
+
diff --git a/log4j-1.2-api/src/src/test/resources/config-1.2/spark/external/kinesis-asl/src/main/resources/log4j.properties b/log4j-1.2-api/src/src/test/resources/config-1.2/spark/external/kinesis-asl/src/main/resources/log4j.properties
new file mode 100644
index 0000000..4f5ea7b
--- /dev/null
+++ b/log4j-1.2-api/src/src/test/resources/config-1.2/spark/external/kinesis-asl/src/main/resources/log4j.properties
@@ -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.
+#
+
+log4j.rootCategory=WARN, console
+
+# File appender
+log4j.appender.file=org.apache.log4j.FileAppender
+log4j.appender.file.append=false
+log4j.appender.file.file=target/unit-tests.log
+log4j.appender.file.layout=org.apache.log4j.PatternLayout
+log4j.appender.file.layout.ConversionPattern=%d{yy/MM/dd HH:mm:ss.SSS} %p %c{1}: %m%n
+
+# Console appender
+log4j.appender.console=org.apache.log4j.ConsoleAppender
+log4j.appender.console.target=System.out
+log4j.appender.console.layout=org.apache.log4j.PatternLayout
+log4j.appender.console.layout.ConversionPattern=%d{yy/MM/dd HH:mm:ss} %p %c{1}: %m%n
+
+# Settings to quiet third party logs that are too verbose
+log4j.logger.org.spark_project.jetty=WARN
+log4j.logger.org.spark_project.jetty.util.component.AbstractLifeCycle=ERROR
+log4j.logger.org.apache.spark.repl.SparkIMain$exprTyper=INFO
+log4j.logger.org.apache.spark.repl.SparkILoop$SparkILoopInterpreter=INFO
diff --git a/log4j-1.2-api/src/src/test/resources/config-1.2/spark/external/kinesis-asl/src/test/resources/log4j.properties b/log4j-1.2-api/src/src/test/resources/config-1.2/spark/external/kinesis-asl/src/test/resources/log4j.properties
new file mode 100644
index 0000000..3706a6e
--- /dev/null
+++ b/log4j-1.2-api/src/src/test/resources/config-1.2/spark/external/kinesis-asl/src/test/resources/log4j.properties
@@ -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.
+#
+
+# Set everything to be logged to the file target/unit-tests.log
+log4j.rootCategory=INFO, file
+log4j.appender.file=org.apache.log4j.FileAppender
+log4j.appender.file.append=true
+log4j.appender.file.file=target/unit-tests.log
+log4j.appender.file.layout=org.apache.log4j.PatternLayout
+log4j.appender.file.layout.ConversionPattern=%d{yy/MM/dd HH:mm:ss.SSS} %t %p %c{1}: %m%n
+
+# Ignore messages below warning level from Jetty, because it's a bit verbose
+log4j.logger.org.spark_project.jetty=WARN
diff --git a/log4j-1.2-api/src/src/test/resources/config-1.2/spark/graphx/src/test/resources/log4j.properties b/log4j-1.2-api/src/src/test/resources/config-1.2/spark/graphx/src/test/resources/log4j.properties
new file mode 100644
index 0000000..3706a6e
--- /dev/null
+++ b/log4j-1.2-api/src/src/test/resources/config-1.2/spark/graphx/src/test/resources/log4j.properties
@@ -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.
+#
+
+# Set everything to be logged to the file target/unit-tests.log
+log4j.rootCategory=INFO, file
+log4j.appender.file=org.apache.log4j.FileAppender
+log4j.appender.file.append=true
+log4j.appender.file.file=target/unit-tests.log
+log4j.appender.file.layout=org.apache.log4j.PatternLayout
+log4j.appender.file.layout.ConversionPattern=%d{yy/MM/dd HH:mm:ss.SSS} %t %p %c{1}: %m%n
+
+# Ignore messages below warning level from Jetty, because it's a bit verbose
+log4j.logger.org.spark_project.jetty=WARN
diff --git a/log4j-1.2-api/src/src/test/resources/config-1.2/spark/launcher/src/test/resources/log4j.properties b/log4j-1.2-api/src/src/test/resources/config-1.2/spark/launcher/src/test/resources/log4j.properties
new file mode 100644
index 0000000..744c456
--- /dev/null
+++ b/log4j-1.2-api/src/src/test/resources/config-1.2/spark/launcher/src/test/resources/log4j.properties
@@ -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.
+#
+
+# Set everything to be logged to the file core/target/unit-tests.log
+test.appender=file
+log4j.rootCategory=INFO, ${test.appender}
+log4j.appender.file=org.apache.log4j.FileAppender
+log4j.appender.file.append=false
+log4j.appender.file.file=target/unit-tests.log
+log4j.appender.file.layout=org.apache.log4j.PatternLayout
+log4j.appender.file.layout.ConversionPattern=%d{yy/MM/dd HH:mm:ss.SSS} %t %p %c{1}: %m%n
+
+log4j.appender.childproc=org.apache.log4j.ConsoleAppender
+log4j.appender.childproc.target=System.err
+log4j.appender.childproc.layout=org.apache.log4j.PatternLayout
+log4j.appender.childproc.layout.ConversionPattern=%t: %m%n
+
+# Ignore messages below warning level from Jetty, because it's a bit verbose
+log4j.logger.org.spark_project.jetty=WARN
diff --git a/log4j-1.2-api/src/src/test/resources/config-1.2/spark/mllib/src/test/resources/log4j.properties b/log4j-1.2-api/src/src/test/resources/config-1.2/spark/mllib/src/test/resources/log4j.properties
new file mode 100644
index 0000000..fd51f8f
--- /dev/null
+++ b/log4j-1.2-api/src/src/test/resources/config-1.2/spark/mllib/src/test/resources/log4j.properties
@@ -0,0 +1,28 @@
+#
+# 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.
+#
+
+# Set everything to be logged to the file target/unit-tests.log
+log4j.rootCategory=INFO, file
+log4j.appender.file=org.apache.log4j.FileAppender
+log4j.appender.file.append=true
+log4j.appender.file.file=target/unit-tests.log
+log4j.appender.file.layout=org.apache.log4j.PatternLayout
+log4j.appender.file.layout.ConversionPattern=%d{yy/MM/dd HH:mm:ss.SSS} %t %p %c{1}: %m%n
+
+# Ignore messages below warning level from Jetty, because it's a bit verbose
+log4j.logger.org.spark_project.jetty=WARN
+
diff --git a/log4j-1.2-api/src/src/test/resources/config-1.2/spark/repl/src/test/resources/log4j.properties b/log4j-1.2-api/src/src/test/resources/config-1.2/spark/repl/src/test/resources/log4j.properties
new file mode 100644
index 0000000..7665bd5
--- /dev/null
+++ b/log4j-1.2-api/src/src/test/resources/config-1.2/spark/repl/src/test/resources/log4j.properties
@@ -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.
+#
+
+# Set everything to be logged to the target/unit-tests.log
+log4j.rootCategory=INFO, file
+log4j.appender.file=org.apache.log4j.FileAppender
+log4j.appender.file.append=true
+log4j.appender.file.file=target/unit-tests.log
+log4j.appender.file.layout=org.apache.log4j.PatternLayout
+log4j.appender.file.layout.ConversionPattern=%d{yy/MM/dd HH:mm:ss.SSS} %t %p %c{1}: %m%n
+
+# Ignore messages below warning level from Jetty, because it's a bit verbose
+log4j.logger.org.spark_project.jetty=WARN
diff --git a/log4j-1.2-api/src/src/test/resources/config-1.2/spark/sql/catalyst/src/test/resources/log4j.properties b/log4j-1.2-api/src/src/test/resources/config-1.2/spark/sql/catalyst/src/test/resources/log4j.properties
new file mode 100644
index 0000000..3706a6e
--- /dev/null
+++ b/log4j-1.2-api/src/src/test/resources/config-1.2/spark/sql/catalyst/src/test/resources/log4j.properties
@@ -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.
+#
+
+# Set everything to be logged to the file target/unit-tests.log
+log4j.rootCategory=INFO, file
+log4j.appender.file=org.apache.log4j.FileAppender
+log4j.appender.file.append=true
+log4j.appender.file.file=target/unit-tests.log
+log4j.appender.file.layout=org.apache.log4j.PatternLayout
+log4j.appender.file.layout.ConversionPattern=%d{yy/MM/dd HH:mm:ss.SSS} %t %p %c{1}: %m%n
+
+# Ignore messages below warning level from Jetty, because it's a bit verbose
+log4j.logger.org.spark_project.jetty=WARN
diff --git a/log4j-1.2-api/src/src/test/resources/config-1.2/spark/sql/core/src/test/resources/log4j.properties b/log4j-1.2-api/src/src/test/resources/config-1.2/spark/sql/core/src/test/resources/log4j.properties
new file mode 100644
index 0000000..33b9ecf
--- /dev/null
+++ b/log4j-1.2-api/src/src/test/resources/config-1.2/spark/sql/core/src/test/resources/log4j.properties
@@ -0,0 +1,57 @@
+#
+# 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.
+#
+
+# Set everything to be logged to the file core/target/unit-tests.log
+log4j.rootLogger=INFO, CA, FA
+
+#Console Appender
+log4j.appender.CA=org.apache.log4j.ConsoleAppender
+log4j.appender.CA.layout=org.apache.log4j.PatternLayout
+log4j.appender.CA.layout.ConversionPattern=%d{HH:mm:ss.SSS} %p %c: %m%n
+log4j.appender.CA.Threshold = WARN
+log4j.appender.CA.follow = true
+
+
+#File Appender
+log4j.appender.FA=org.apache.log4j.FileAppender
+log4j.appender.FA.append=false
+log4j.appender.FA.file=target/unit-tests.log
+log4j.appender.FA.layout=org.apache.log4j.PatternLayout
+log4j.appender.FA.layout.ConversionPattern=%d{HH:mm:ss.SSS} %t %p %c{1}: %m%n
+
+# Set the logger level of File Appender to WARN
+log4j.appender.FA.Threshold = INFO
+
+# Some packages are noisy for no good reason.
+log4j.additivity.org.apache.parquet.hadoop.ParquetRecordReader=false
+log4j.logger.org.apache.parquet.hadoop.ParquetRecordReader=OFF
+
+log4j.additivity.org.apache.parquet.hadoop.ParquetOutputCommitter=false
+log4j.logger.org.apache.parquet.hadoop.ParquetOutputCommitter=OFF
+
+log4j.additivity.org.apache.hadoop.hive.serde2.lazy.LazyStruct=false
+log4j.logger.org.apache.hadoop.hive.serde2.lazy.LazyStruct=OFF
+
+log4j.additivity.org.apache.hadoop.hive.metastore.RetryingHMSHandler=false
+log4j.logger.org.apache.hadoop.hive.metastore.RetryingHMSHandler=OFF
+
+log4j.additivity.hive.ql.metadata.Hive=false
+log4j.logger.hive.ql.metadata.Hive=OFF
+
+# Parquet related logging
+log4j.logger.org.apache.parquet.hadoop=WARN
+log4j.logger.org.apache.spark.sql.parquet=INFO
diff --git a/log4j-1.2-api/src/src/test/resources/config-1.2/spark/sql/hive/src/test/resources/log4j.properties b/log4j-1.2-api/src/src/test/resources/config-1.2/spark/sql/hive/src/test/resources/log4j.properties
new file mode 100644
index 0000000..fea3404
--- /dev/null
+++ b/log4j-1.2-api/src/src/test/resources/config-1.2/spark/sql/hive/src/test/resources/log4j.properties
@@ -0,0 +1,61 @@
+#
+# 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.
+#
+
+# Set everything to be logged to the file core/target/unit-tests.log
+log4j.rootLogger=DEBUG, CA, FA
+
+#Console Appender
+log4j.appender.CA=org.apache.log4j.ConsoleAppender
+log4j.appender.CA.layout=org.apache.log4j.PatternLayout
+log4j.appender.CA.layout.ConversionPattern=%d{HH:mm:ss.SSS} %p %c: %m%n
+log4j.appender.CA.Threshold = WARN
+
+
+#File Appender
+log4j.appender.FA=org.apache.log4j.FileAppender
+log4j.appender.FA.append=false
+log4j.appender.FA.file=target/unit-tests.log
+log4j.appender.FA.layout=org.apache.log4j.PatternLayout
+log4j.appender.FA.layout.ConversionPattern=%d{HH:mm:ss.SSS} %t %p %c{1}: %m%n
+
+# Set the logger level of File Appender to WARN
+log4j.appender.FA.Threshold = DEBUG
+
+# Some packages are noisy for no good reason.
+log4j.additivity.org.apache.hadoop.hive.serde2.lazy.LazyStruct=false
+log4j.logger.org.apache.hadoop.hive.serde2.lazy.LazyStruct=OFF
+
+log4j.additivity.org.apache.hadoop.hive.metastore.RetryingHMSHandler=false
+log4j.logger.org.apache.hadoop.hive.metastore.RetryingHMSHandler=OFF
+
+log4j.additivity.hive.log=false
+log4j.logger.hive.log=OFF
+
+log4j.additivity.parquet.hadoop.ParquetRecordReader=false
+log4j.logger.parquet.hadoop.ParquetRecordReader=OFF
+
+log4j.additivity.org.apache.parquet.hadoop.ParquetRecordReader=false
+log4j.logger.org.apache.parquet.hadoop.ParquetRecordReader=OFF
+
+log4j.additivity.org.apache.parquet.hadoop.ParquetOutputCommitter=false
+log4j.logger.org.apache.parquet.hadoop.ParquetOutputCommitter=OFF
+
+log4j.additivity.hive.ql.metadata.Hive=false
+log4j.logger.hive.ql.metadata.Hive=OFF
+
+log4j.additivity.org.apache.hadoop.hive.ql.io.RCFile=false
+log4j.logger.org.apache.hadoop.hive.ql.io.RCFile=ERROR
diff --git a/log4j-1.2-api/src/src/test/resources/config-1.2/spark/streaming/src/test/resources/log4j.properties b/log4j-1.2-api/src/src/test/resources/config-1.2/spark/streaming/src/test/resources/log4j.properties
new file mode 100644
index 0000000..fd51f8f
--- /dev/null
+++ b/log4j-1.2-api/src/src/test/resources/config-1.2/spark/streaming/src/test/resources/log4j.properties
@@ -0,0 +1,28 @@
+#
+# 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.
+#
+
+# Set everything to be logged to the file target/unit-tests.log
+log4j.rootCategory=INFO, file
+log4j.appender.file=org.apache.log4j.FileAppender
+log4j.appender.file.append=true
+log4j.appender.file.file=target/unit-tests.log
+log4j.appender.file.layout=org.apache.log4j.PatternLayout
+log4j.appender.file.layout.ConversionPattern=%d{yy/MM/dd HH:mm:ss.SSS} %t %p %c{1}: %m%n
+
+# Ignore messages below warning level from Jetty, because it's a bit verbose
+log4j.logger.org.spark_project.jetty=WARN
+
diff --git a/log4j-1.2-api/src/src/test/resources/config-1.2/spark/yarn/src/test/resources/log4j.properties b/log4j-1.2-api/src/src/test/resources/config-1.2/spark/yarn/src/test/resources/log4j.properties
new file mode 100644
index 0000000..d13454d
--- /dev/null
+++ b/log4j-1.2-api/src/src/test/resources/config-1.2/spark/yarn/src/test/resources/log4j.properties
@@ -0,0 +1,31 @@
+#
+# 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.
+#
+
+# Set everything to be logged to the file target/unit-tests.log
+log4j.rootCategory=DEBUG, file
+log4j.appender.file=org.apache.log4j.FileAppender
+log4j.appender.file.append=true
+log4j.appender.file.file=target/unit-tests.log
+log4j.appender.file.layout=org.apache.log4j.PatternLayout
+log4j.appender.file.layout.ConversionPattern=%d{yy/MM/dd HH:mm:ss.SSS} %t %p %c{1}: %m%n
+
+# Ignore messages below warning level from a few verbose libraries.
+log4j.logger.com.sun.jersey=WARN
+log4j.logger.org.apache.hadoop=WARN
+log4j.logger.org.eclipse.jetty=WARN
+log4j.logger.org.mortbay=WARN
+log4j.logger.org.spark_project.jetty=WARN
diff --git a/log4j-1.2-api/src/src/test/resources/hello.vm b/log4j-1.2-api/src/src/test/resources/hello.vm
new file mode 100644
index 0000000..5ce9755
--- /dev/null
+++ b/log4j-1.2-api/src/src/test/resources/hello.vm
@@ -0,0 +1,6 @@
+<html>
+<body>
+    #set( $foo = "Velocity" )
+Hello $foo World!
+</body>
+<html>
\ No newline at end of file
diff --git a/log4j-1.2-api/src/src/test/resources/log-RouteWithMDC.xml b/log4j-1.2-api/src/src/test/resources/log-RouteWithMDC.xml
new file mode 100644
index 0000000..eb7e8a5
--- /dev/null
+++ b/log4j-1.2-api/src/src/test/resources/log-RouteWithMDC.xml
@@ -0,0 +1,47 @@
+<?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.
+
+-->
+<Configuration name="ConfigTest" status="error" packages="org.apache.logging.log4j.test"
+               monitorInterval="5">
+  <Appenders>
+    <Console name="STDOUT">
+      <PatternLayout pattern="%X{Type, Name} %m%n"/>
+    </Console>
+    <List name="List">
+      <PatternLayout pattern="%X{Type, Name} %m%n"/>
+    </List>
+    <Routing name="Routing">
+      <Routes pattern="$${ctx:Type}">
+        <Route ref="STDOUT"/>
+        <Route ref="STDOUT" key="Audit"/>
+        <Route ref="List" key="Service"/>
+      </Routes>
+    </Routing>
+  </Appenders>
+  <Loggers>
+    <Logger name="org.apache.test.logging" level="debug" additivity="false">
+      <AppenderRef ref="Routing"/>
+    </Logger>
+    <Logger name="org.apache.test" level="trace" additivity="false">
+      <AppenderRef ref="List"/>
+    </Logger>
+    <Root level="error">
+      <AppenderRef ref="STDOUT"/>
+    </Root>
+  </Loggers>
+</Configuration>
\ No newline at end of file
diff --git a/log4j-1.2-api/src/src/test/resources/log4j.xml b/log4j-1.2-api/src/src/test/resources/log4j.xml
new file mode 100644
index 0000000..065c748
--- /dev/null
+++ b/log4j-1.2-api/src/src/test/resources/log4j.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="list" class="org.apache.log4j.ListAppender">
+    <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>
+
+  <appender name="events" class="org.apache.log4j.ListAppender">
+  </appender>
+
+  <root>
+    <priority value ="trace" />
+    <appender-ref ref="list" />
+    <appender-ref ref="events" />
+  </root>
+
+</log4j:configuration>
\ No newline at end of file
diff --git a/log4j-1.2-api/src/src/test/resources/log4j1-file.xml b/log4j-1.2-api/src/src/test/resources/log4j1-file.xml
new file mode 100644
index 0000000..bc7df16
--- /dev/null
+++ b/log4j-1.2-api/src/src/test/resources/log4j1-file.xml
@@ -0,0 +1,57 @@
+<?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="console" class="org.apache.log4j.ConsoleAppender">
+    <param name="Target" value="System.out"/>
+    <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>
+
+  <appender name="A1" class="org.apache.log4j.FileAppender">
+
+    <param name="File"   value="target/temp.A1" />
+    <param name="Append" value="false" />
+
+    <layout class="org.apache.log4j.PatternLayout">
+      <param name="ConversionPattern" value="%-5p %c{2} - %m%n"/>
+    </layout>
+  </appender>
+
+  <appender name="A2" class="org.apache.log4j.FileAppender">
+    <param name="File" value="target/temp.A2" />
+    <param name="Append" value="false" />
+    <layout class="org.apache.log4j.TTCCLayout">
+      <param name="DateFormat" value="ISO8601" />
+    </layout>
+  </appender>
+
+  <logger name="org.apache.log4j.xml">
+    <level value="trace" />
+    <appender-ref ref="A1" />
+  </logger>
+
+  <root>
+    <priority value ="trace" />
+    <appender-ref ref="A1" />
+    <appender-ref ref="A2" />
+  </root>
+
+</log4j:configuration>
\ No newline at end of file
diff --git a/log4j-1.2-api/src/src/test/resources/log4j1-list.xml b/log4j-1.2-api/src/src/test/resources/log4j1-list.xml
new file mode 100644
index 0000000..065c748
--- /dev/null
+++ b/log4j-1.2-api/src/src/test/resources/log4j1-list.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="list" class="org.apache.log4j.ListAppender">
+    <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>
+
+  <appender name="events" class="org.apache.log4j.ListAppender">
+  </appender>
+
+  <root>
+    <priority value ="trace" />
+    <appender-ref ref="list" />
+    <appender-ref ref="events" />
+  </root>
+
+</log4j:configuration>
\ No newline at end of file
diff --git a/log4j-1.2-api/src/src/test/resources/log4j2-config.xml b/log4j-1.2-api/src/src/test/resources/log4j2-config.xml
new file mode 100644
index 0000000..2427af8
--- /dev/null
+++ b/log4j-1.2-api/src/src/test/resources/log4j2-config.xml
@@ -0,0 +1,39 @@
+<?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.
+
+-->
+<Configuration name="ConfigTest" status="error" packages="org.apache.logging.log4j.test"
+               monitorInterval="5">
+  <Appenders>
+    <Console name="STDOUT">
+      <PatternLayout pattern="%m%n"/>
+    </Console>
+    <List name="List">
+    </List>
+  </Appenders>
+  <Loggers>
+    <Logger name="org.apache.test.logging" level="debug" additivity="false">
+      <AppenderRef ref="List"/>
+    </Logger>
+    <Logger name="org.apache.test" level="trace" additivity="false">
+      <AppenderRef ref="List"/>
+    </Logger>
+    <Root level="error">
+      <AppenderRef ref="STDOUT"/>
+    </Root>
+  </Loggers>
+</Configuration>
\ No newline at end of file
diff --git a/log4j-1.2-api/src/src/test/resources/logWithMDC.xml b/log4j-1.2-api/src/src/test/resources/logWithMDC.xml
new file mode 100644
index 0000000..1fe8482
--- /dev/null
+++ b/log4j-1.2-api/src/src/test/resources/logWithMDC.xml
@@ -0,0 +1,40 @@
+<?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.
+
+-->
+<Configuration name="ConfigTest" status="error" packages="org.apache.logging.log4j.test"
+               monitorInterval="5">
+  <Appenders>
+    <Console name="STDOUT">
+      <PatternLayout pattern="%m%n"/>
+    </Console>
+    <List name="List">
+      <PatternLayout pattern="%X{Key1, Key2} %m%n"/>
+    </List>
+  </Appenders>
+  <Loggers>
+    <Logger name="org.apache.test.logging" level="debug" additivity="false">
+      <AppenderRef ref="List"/>
+    </Logger>
+    <Logger name="org.apache.test" level="trace" additivity="false">
+      <AppenderRef ref="List"/>
+    </Logger>
+    <Root level="error">
+      <AppenderRef ref="STDOUT"/>
+    </Root>
+  </Loggers>
+</Configuration>
\ No newline at end of file
diff --git a/log4j-1.2-api/src/src/test/resources/witness/serialization/info.bin b/log4j-1.2-api/src/src/test/resources/witness/serialization/info.bin
new file mode 100644
index 0000000..f887f39
--- /dev/null
+++ b/log4j-1.2-api/src/src/test/resources/witness/serialization/info.bin
Binary files differ
diff --git a/log4j-1.2-api/src/test/java/org/apache/log4j/ListAppender.java b/log4j-1.2-api/src/test/java/org/apache/log4j/ListAppender.java
new file mode 100644
index 0000000..83c2758
--- /dev/null
+++ b/log4j-1.2-api/src/test/java/org/apache/log4j/ListAppender.java
@@ -0,0 +1,83 @@
+/*
+ * 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 java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+import java.util.concurrent.TimeUnit;
+
+/**
+ * Used to test Log4j 1 support.
+ */
+public class ListAppender extends AppenderSkeleton {
+    // Use Collections.synchronizedList rather than CopyOnWriteArrayList because we expect
+    // more frequent writes than reads.
+    final List<LoggingEvent> events = Collections.synchronizedList(new ArrayList<>());
+
+    private final List<String> messages = Collections.synchronizedList(new ArrayList<>());
+
+
+    private static final String WINDOWS_LINE_SEP = "\r\n";
+
+    @Override
+    protected void append(LoggingEvent event) {
+        Layout layout = getLayout();
+        if (layout != null) {
+            String result = layout.format(event);
+            if (result != null) {
+                messages.add(result);
+            }
+        } else {
+            events.add(event);
+        }
+    }
+
+    @Override
+    public void close() {
+
+    }
+
+    @Override
+    public boolean requiresLayout() {
+        return false;
+    }
+
+    /** Returns an immutable snapshot of captured log events */
+    public List<LoggingEvent> getEvents() {
+        return Collections.unmodifiableList(new ArrayList<>(events));
+    }
+
+    /** Returns an immutable snapshot of captured messages */
+    public List<String> getMessages() {
+        return Collections.unmodifiableList(new ArrayList<>(messages));
+    }
+
+    /**
+     * Polls the messages list for it to grow to a given minimum size at most timeout timeUnits and return a copy of
+     * what we have so far.
+     */
+    public List<String> getMessages(final int minSize, final long timeout, final TimeUnit timeUnit) throws InterruptedException {
+        final long endMillis = System.currentTimeMillis() + timeUnit.toMillis(timeout);
+        while (messages.size() < minSize && System.currentTimeMillis() < endMillis) {
+            Thread.sleep(100);
+        }
+        return getMessages();
+    }
+}
diff --git a/log4j-1.2-api/src/test/java/org/apache/log4j/config/AutoConfigTest.java b/log4j-1.2-api/src/test/java/org/apache/log4j/config/AutoConfigTest.java
new file mode 100644
index 0000000..764f612
--- /dev/null
+++ b/log4j-1.2-api/src/test/java/org/apache/log4j/config/AutoConfigTest.java
@@ -0,0 +1,66 @@
+/*
+ * 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.ListAppender;
+import org.apache.log4j.LogManager;
+import org.apache.log4j.Logger;
+import org.apache.log4j.bridge.AppenderAdapter;
+import org.apache.log4j.spi.LoggingEvent;
+import org.apache.log4j.xml.XmlConfigurationFactory;
+import org.apache.logging.log4j.core.Appender;
+import org.apache.logging.log4j.core.config.Configuration;
+import org.apache.logging.log4j.spi.LoggerContext;
+import org.junit.Test;
+
+import java.io.File;
+import java.util.List;
+import java.util.Map;
+
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+
+/**
+ * Test configuration from XML.
+ */
+public class AutoConfigTest {
+
+    @Test
+    public void testListAppender() {
+        Logger logger = LogManager.getLogger("test");
+        logger.debug("This is a test of the root logger");
+        LoggerContext loggerContext = org.apache.logging.log4j.LogManager.getContext(false);
+        Configuration configuration = ((org.apache.logging.log4j.core.LoggerContext) loggerContext).getConfiguration();
+        Map<String, Appender> appenders = configuration.getAppenders();
+        ListAppender eventAppender = null;
+        ListAppender messageAppender = null;
+        for (Map.Entry<String, Appender> entry : appenders.entrySet()) {
+            if (entry.getKey().equals("list")) {
+                messageAppender = (ListAppender) ((AppenderAdapter.Adapter) entry.getValue()).getAppender();
+            } else if (entry.getKey().equals("events")) {
+                eventAppender = (ListAppender) ((AppenderAdapter.Adapter) entry.getValue()).getAppender();
+            }
+        }
+        assertNotNull("No Event Appender", eventAppender);
+        assertNotNull("No Message Appender", messageAppender);
+        List<LoggingEvent> events = eventAppender.getEvents();
+        assertTrue("No events", events != null && events.size() > 0);
+        List<String> messages = messageAppender.getMessages();
+        assertTrue("No messages", messages != null && messages.size() > 0);
+    }
+
+}
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 8d3e4e2..ebe3e54 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
@@ -174,7 +174,7 @@
         } finally {
 			try {
 				Files.deleteIfExists(tempFilePath);
-			} catch (FileSystemException e) {
+			} catch (final FileSystemException e) {
 				e.printStackTrace();
 			}
         }
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
new file mode 100644
index 0000000..d522a18
--- /dev/null
+++ b/log4j-1.2-api/src/test/java/org/apache/log4j/config/XmlConfigurationFactoryTest.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.config;
+
+import org.apache.log4j.ListAppender;
+import org.apache.log4j.LogManager;
+import org.apache.log4j.Logger;
+import org.apache.log4j.bridge.AppenderAdapter;
+import org.apache.log4j.spi.LoggingEvent;
+import org.apache.log4j.xml.DOMConfigurator;
+import org.apache.log4j.xml.XmlConfigurationFactory;
+import org.apache.logging.log4j.core.Appender;
+import org.apache.logging.log4j.core.config.Configuration;
+import org.apache.logging.log4j.spi.LoggerContext;
+import org.junit.Test;
+
+import java.io.File;
+import java.util.List;
+import java.util.Map;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+
+/**
+ * Test configuration from XML.
+ */
+public class XmlConfigurationFactoryTest {
+
+    @Test
+    public void testXML() throws Exception {
+        XmlConfigurationFactory.configure("target/test-classes/log4j1-file.xml");
+        Logger logger = LogManager.getLogger("test");
+        logger.debug("This is a test of the root logger");
+        File file = new File("target/temp.A1");
+        assertTrue("File A1 was not created", file.exists());
+        assertTrue("File A1 is empty", file.length() > 0);
+        file = new File("target/temp.A2");
+        assertTrue("File A2 was not created", file.exists());
+        assertTrue("File A2 is empty", file.length() > 0);
+    }
+
+    @Test
+    public void testListAppender() {
+        XmlConfigurationFactory.configure("target/test-classes/log4j1-list.xml");
+        Logger logger = LogManager.getLogger("test");
+        logger.debug("This is a test of the root logger");
+        LoggerContext loggerContext = org.apache.logging.log4j.LogManager.getContext(false);
+        Configuration configuration = ((org.apache.logging.log4j.core.LoggerContext) loggerContext).getConfiguration();
+        Map<String, Appender> appenders = configuration.getAppenders();
+        ListAppender eventAppender = null;
+        ListAppender messageAppender = null;
+        for (Map.Entry<String, Appender> entry : appenders.entrySet()) {
+            if (entry.getKey().equals("list")) {
+                messageAppender = (ListAppender) ((AppenderAdapter.Adapter) entry.getValue()).getAppender();
+            } else if (entry.getKey().equals("events")) {
+                eventAppender = (ListAppender) ((AppenderAdapter.Adapter) entry.getValue()).getAppender();
+            }
+        }
+        assertNotNull("No Event Appender", eventAppender);
+        assertNotNull("No Message Appender", messageAppender);
+        List<LoggingEvent> events = eventAppender.getEvents();
+        assertTrue("No events", events != null && events.size() > 0);
+        List<String> messages = messageAppender.getMessages();
+        assertTrue("No messages", messages != null && messages.size() > 0);
+    }
+
+}
diff --git a/log4j-1.2-api/src/test/resources/log4j.xml b/log4j-1.2-api/src/test/resources/log4j.xml
new file mode 100644
index 0000000..065c748
--- /dev/null
+++ b/log4j-1.2-api/src/test/resources/log4j.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="list" class="org.apache.log4j.ListAppender">
+    <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>
+
+  <appender name="events" class="org.apache.log4j.ListAppender">
+  </appender>
+
+  <root>
+    <priority value ="trace" />
+    <appender-ref ref="list" />
+    <appender-ref ref="events" />
+  </root>
+
+</log4j:configuration>
\ No newline at end of file
diff --git a/log4j-1.2-api/src/test/resources/log4j1-file.xml b/log4j-1.2-api/src/test/resources/log4j1-file.xml
new file mode 100644
index 0000000..bc7df16
--- /dev/null
+++ b/log4j-1.2-api/src/test/resources/log4j1-file.xml
@@ -0,0 +1,57 @@
+<?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="console" class="org.apache.log4j.ConsoleAppender">
+    <param name="Target" value="System.out"/>
+    <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>
+
+  <appender name="A1" class="org.apache.log4j.FileAppender">
+
+    <param name="File"   value="target/temp.A1" />
+    <param name="Append" value="false" />
+
+    <layout class="org.apache.log4j.PatternLayout">
+      <param name="ConversionPattern" value="%-5p %c{2} - %m%n"/>
+    </layout>
+  </appender>
+
+  <appender name="A2" class="org.apache.log4j.FileAppender">
+    <param name="File" value="target/temp.A2" />
+    <param name="Append" value="false" />
+    <layout class="org.apache.log4j.TTCCLayout">
+      <param name="DateFormat" value="ISO8601" />
+    </layout>
+  </appender>
+
+  <logger name="org.apache.log4j.xml">
+    <level value="trace" />
+    <appender-ref ref="A1" />
+  </logger>
+
+  <root>
+    <priority value ="trace" />
+    <appender-ref ref="A1" />
+    <appender-ref ref="A2" />
+  </root>
+
+</log4j:configuration>
\ No newline at end of file
diff --git a/log4j-1.2-api/src/test/resources/log4j1-list.xml b/log4j-1.2-api/src/test/resources/log4j1-list.xml
new file mode 100644
index 0000000..065c748
--- /dev/null
+++ b/log4j-1.2-api/src/test/resources/log4j1-list.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="list" class="org.apache.log4j.ListAppender">
+    <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>
+
+  <appender name="events" class="org.apache.log4j.ListAppender">
+  </appender>
+
+  <root>
+    <priority value ="trace" />
+    <appender-ref ref="list" />
+    <appender-ref ref="events" />
+  </root>
+
+</log4j:configuration>
\ No newline at end of file
diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/LoggerContext.java b/log4j-core/src/main/java/org/apache/logging/log4j/core/LoggerContext.java
index 351b2ae..21cb54e 100644
--- a/log4j-core/src/main/java/org/apache/logging/log4j/core/LoggerContext.java
+++ b/log4j-core/src/main/java/org/apache/logging/log4j/core/LoggerContext.java
@@ -704,6 +704,17 @@
         reconfigure(configLocation);
     }
 
+    public void reconfigure(Configuration configuration) {
+        setConfiguration(configuration);
+        ConfigurationSource source = configuration.getConfigurationSource();
+        if (source != null) {
+            URI uri = source.getURI();
+            if (uri != null) {
+                configLocation = uri;
+            }
+        }
+    }
+
     /**
      * Causes all Loggers to be updated against the current Configuration.
      */
diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/config/ConfigurationFactory.java b/log4j-core/src/main/java/org/apache/logging/log4j/core/config/ConfigurationFactory.java
index ca6b7df..17cbbed 100644
--- a/log4j-core/src/main/java/org/apache/logging/log4j/core/config/ConfigurationFactory.java
+++ b/log4j-core/src/main/java/org/apache/logging/log4j/core/config/ConfigurationFactory.java
@@ -257,6 +257,14 @@
 
     protected abstract String[] getSupportedTypes();
 
+    protected String getTestPrefix() {
+        return TEST_PREFIX;
+    }
+
+    protected String getDefaultPrefix() {
+        return DEFAULT_PREFIX;
+    }
+
     protected boolean isActive() {
         return true;
     }
@@ -480,7 +488,7 @@
             final ClassLoader loader = LoaderUtil.getThreadContextClassLoader();
             for (final ConfigurationFactory factory : getFactories()) {
                 String configName;
-                final String prefix = isTest ? TEST_PREFIX : DEFAULT_PREFIX;
+                final String prefix = isTest ? factory.getTestPrefix() : factory.getDefaultPrefix();
                 final String [] types = factory.getSupportedTypes();
                 if (types == null) {
                     continue;
diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/config/Configurator.java b/log4j-core/src/main/java/org/apache/logging/log4j/core/config/Configurator.java
index 30f8ca8..8effd0a 100644
--- a/log4j-core/src/main/java/org/apache/logging/log4j/core/config/Configurator.java
+++ b/log4j-core/src/main/java/org/apache/logging/log4j/core/config/Configurator.java
@@ -227,6 +227,24 @@
     }
 
     /**
+     * Reconfigure using an already constructed Configuration.
+     * @param configuration The configuration.
+     * @since 2.13.0
+     */
+    public static void reconfigure(final Configuration configuration) {
+        try {
+            final Log4jContextFactory factory = getFactory();
+            if (factory != null) {
+                factory.getContext(FQCN, null, null, false)
+                        .reconfigure(configuration);
+            }
+        } catch (final Exception ex) {
+            LOGGER.error("There was a problem initializing the LoggerContext using configuration {}",
+                    configuration.getName(), ex);
+        }
+    }
+
+    /**
      * Reload the existing reconfiguration.
      * @since 2.12.0
      */
diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/filter/DenyAllFilter.java b/log4j-core/src/main/java/org/apache/logging/log4j/core/filter/DenyAllFilter.java
new file mode 100644
index 0000000..dbdc550
--- /dev/null
+++ b/log4j-core/src/main/java/org/apache/logging/log4j/core/filter/DenyAllFilter.java
@@ -0,0 +1,156 @@
+/*
+ * 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 org.apache.logging.log4j.Level;
+import org.apache.logging.log4j.Marker;
+import org.apache.logging.log4j.core.Filter;
+import org.apache.logging.log4j.core.LogEvent;
+import org.apache.logging.log4j.core.Logger;
+import org.apache.logging.log4j.message.Message;
+import org.apache.logging.log4j.plugins.Node;
+import org.apache.logging.log4j.plugins.Plugin;
+import org.apache.logging.log4j.plugins.PluginFactory;
+
+import org.apache.logging.log4j.util.PerformanceSensitive;
+
+/**
+ * This filter causes all logging events to be dropped.
+ */
+@Plugin(name = "DenyAllFilter", category = Node.CATEGORY, elementType = Filter.ELEMENT_TYPE, printObject = true)
+@PerformanceSensitive("allocation")
+public final class DenyAllFilter extends AbstractFilter {
+
+    private DenyAllFilter(final Result onMatch, final Result onMismatch) {
+        super(onMatch, onMismatch);
+    }
+
+    @Override
+    public Result filter(final Logger logger, final Level level, final Marker marker, final String msg,
+                         final Object... params) {
+        return Result.DENY;
+    }
+
+    @Override
+    public Result filter(final Logger logger, final Level level, final Marker marker, final Object msg,
+                         final Throwable t) {
+        return Result.DENY;
+    }
+
+    @Override
+    public Result filter(final Logger logger, final Level level, final Marker marker, final Message msg,
+                         final Throwable t) {
+        return Result.DENY;
+    }
+
+    @Override
+    public Result filter(final LogEvent event) {
+        return Result.DENY;
+    }
+
+    private Result filter(final Marker marker) {
+        return Result.DENY;
+    }
+
+    @Override
+    public Result filter(final Logger logger, final Level level, final Marker marker, final String msg,
+            final Object p0) {
+        return Result.DENY;
+    }
+
+    @Override
+    public Result filter(final Logger logger, final Level level, final Marker marker, final String msg,
+            final Object p0, final Object p1) {
+        return Result.DENY;
+    }
+
+    @Override
+    public Result filter(final Logger logger, final Level level, final Marker marker, final String msg,
+            final Object p0, final Object p1, final Object p2) {
+        return filter(marker);
+    }
+
+    @Override
+    public Result filter(final Logger logger, final Level level, final Marker marker, final String msg,
+            final Object p0, final Object p1, final Object p2, final Object p3) {
+        return Result.DENY;
+    }
+
+    @Override
+    public Result filter(final Logger logger, final Level level, final Marker marker, final String msg,
+            final Object p0, final Object p1, final Object p2, final Object p3,
+            final Object p4) {
+        return Result.DENY;
+    }
+
+    @Override
+    public Result filter(final Logger logger, final Level level, final Marker marker, final String msg,
+            final Object p0, final Object p1, final Object p2, final Object p3,
+            final Object p4, final Object p5) {
+        return Result.DENY;
+    }
+
+    @Override
+    public Result filter(final Logger logger, final Level level, final Marker marker, final String msg,
+            final Object p0, final Object p1, final Object p2, final Object p3,
+            final Object p4, final Object p5, final Object p6) {
+        return Result.DENY;
+    }
+
+    @Override
+    public Result filter(final Logger logger, final Level level, final Marker marker, final String msg,
+            final Object p0, final Object p1, final Object p2, final Object p3,
+            final Object p4, final Object p5, final Object p6,
+            final Object p7) {
+        return Result.DENY;
+    }
+
+    @Override
+    public Result filter(final Logger logger, final Level level, final Marker marker, final String msg,
+            final Object p0, final Object p1, final Object p2, final Object p3,
+            final Object p4, final Object p5, final Object p6,
+            final Object p7, final Object p8) {
+        return Result.DENY;
+    }
+
+    @Override
+    public Result filter(final Logger logger, final Level level, final Marker marker, final String msg,
+            final Object p0, final Object p1, final Object p2, final Object p3,
+            final Object p4, final Object p5, final Object p6,
+            final Object p7, final Object p8, final Object p9) {
+        return Result.DENY;
+    }
+
+    @Override
+    public String toString() {
+        return "DenyAll";
+    }
+
+    @PluginFactory
+    public static DenyAllFilter.Builder newBuilder() {
+        return new DenyAllFilter.Builder();
+    }
+
+    public static class Builder extends AbstractFilterBuilder<DenyAllFilter.Builder> implements org.apache.logging.log4j.core.util.Builder<DenyAllFilter> {
+
+        @Override
+        public DenyAllFilter build() {
+            return new DenyAllFilter(this.getOnMatch(), this.getOnMismatch());
+        }
+    }
+
+}
diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/filter/LevelMatchFilter.java b/log4j-core/src/main/java/org/apache/logging/log4j/core/filter/LevelMatchFilter.java
new file mode 100644
index 0000000..cafbac1
--- /dev/null
+++ b/log4j-core/src/main/java/org/apache/logging/log4j/core/filter/LevelMatchFilter.java
@@ -0,0 +1,173 @@
+/*
+ * 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 org.apache.logging.log4j.Level;
+import org.apache.logging.log4j.Marker;
+import org.apache.logging.log4j.core.Filter;
+import org.apache.logging.log4j.core.LogEvent;
+import org.apache.logging.log4j.core.Logger;
+import org.apache.logging.log4j.message.Message;
+import org.apache.logging.log4j.plugins.Node;
+import org.apache.logging.log4j.plugins.Plugin;
+import org.apache.logging.log4j.plugins.PluginBuilderAttribute;
+import org.apache.logging.log4j.plugins.PluginFactory;
+import org.apache.logging.log4j.util.PerformanceSensitive;
+
+/**
+ * This filter returns the onMatch result if the logging level in the event matches the specified logging level
+ * exactly.
+ */
+@Plugin(name = "LevelMatchFilter", category = Node.CATEGORY, elementType = Filter.ELEMENT_TYPE, printObject = true)
+@PerformanceSensitive("allocation")
+public final class LevelMatchFilter extends AbstractFilter {
+
+    public static final String ATTR_MATCH = "match";
+    private final Level level;
+
+    private LevelMatchFilter(final Level level, final Result onMatch, final Result onMismatch) {
+        super(onMatch, onMismatch);
+        this.level = level;
+    }
+
+    @Override
+    public Result filter(final Logger logger, final Level level, final Marker marker, final String msg,
+                         final Object... params) {
+        return filter(level);
+    }
+
+    @Override
+    public Result filter(final Logger logger, final Level level, final Marker marker, final Object msg,
+                         final Throwable t) {
+        return filter(level);
+    }
+
+    @Override
+    public Result filter(final Logger logger, final Level level, final Marker marker, final Message msg,
+                         final Throwable t) {
+        return filter(level);
+    }
+
+    @Override
+    public Result filter(final LogEvent event) {
+        return filter(event.getLevel());
+    }
+
+    private Result filter(final Level level) {
+        return level == this.level ? onMatch : onMismatch;
+    }
+
+    @Override
+    public Result filter(final Logger logger, final Level level, final Marker marker, final String msg,
+            final Object p0) {
+        return filter(level);
+    }
+
+    @Override
+    public Result filter(final Logger logger, final Level level, final Marker marker, final String msg,
+            final Object p0, final Object p1) {
+        return filter(level);
+    }
+
+    @Override
+    public Result filter(final Logger logger, final Level level, final Marker marker, final String msg,
+            final Object p0, final Object p1, final Object p2) {
+        return filter(level);
+    }
+
+    @Override
+    public Result filter(final Logger logger, final Level level, final Marker marker, final String msg,
+            final Object p0, final Object p1, final Object p2, final Object p3) {
+        return filter(level);
+    }
+
+    @Override
+    public Result filter(final Logger logger, final Level level, final Marker marker, final String msg,
+            final Object p0, final Object p1, final Object p2, final Object p3,
+            final Object p4) {
+        return filter(level);
+    }
+
+    @Override
+    public Result filter(final Logger logger, final Level level, final Marker marker, final String msg,
+            final Object p0, final Object p1, final Object p2, final Object p3,
+            final Object p4, final Object p5) {
+        return filter(level);
+    }
+
+    @Override
+    public Result filter(final Logger logger, final Level level, final Marker marker, final String msg,
+            final Object p0, final Object p1, final Object p2, final Object p3,
+            final Object p4, final Object p5, final Object p6) {
+        return filter(level);
+    }
+
+    @Override
+    public Result filter(final Logger logger, final Level level, final Marker marker, final String msg,
+            final Object p0, final Object p1, final Object p2, final Object p3,
+            final Object p4, final Object p5, final Object p6,
+            final Object p7) {
+        return filter(level);
+    }
+
+    @Override
+    public Result filter(final Logger logger, final Level level, final Marker marker, final String msg,
+            final Object p0, final Object p1, final Object p2, final Object p3,
+            final Object p4, final Object p5, final Object p6,
+            final Object p7, final Object p8) {
+        return filter(level);
+    }
+
+    @Override
+    public Result filter(final Logger logger, final Level level, final Marker marker, final String msg,
+            final Object p0, final Object p1, final Object p2, final Object p3,
+            final Object p4, final Object p5, final Object p6,
+            final Object p7, final Object p8, final Object p9) {
+        return filter(level);
+    }
+
+    @Override
+    public String toString() {
+        return level.toString();
+    }
+
+    @PluginFactory
+    public static LevelMatchFilter.Builder newBuilder() {
+        return new LevelMatchFilter.Builder();
+    }
+
+    public static class Builder extends AbstractFilterBuilder<LevelMatchFilter.Builder> implements org.apache.logging.log4j.core.util.Builder<LevelMatchFilter> {
+        @PluginBuilderAttribute
+        private Level level = Level.ERROR;
+
+        /**
+         * Sets the logging level to use.
+         * @param level the logging level to use.
+         * @return this
+         */
+        public LevelMatchFilter.Builder setLevel(final Level level) {
+            this.level = level;
+            return this;
+        }
+
+        @Override
+        public LevelMatchFilter build() {
+            return new LevelMatchFilter(this.level, this.getOnMatch(), this.getOnMismatch());
+        }
+    }
+
+}
diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/filter/StringMatchFilter.java b/log4j-core/src/main/java/org/apache/logging/log4j/core/filter/StringMatchFilter.java
new file mode 100644
index 0000000..a273726
--- /dev/null
+++ b/log4j-core/src/main/java/org/apache/logging/log4j/core/filter/StringMatchFilter.java
@@ -0,0 +1,175 @@
+/*
+ * 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 org.apache.logging.log4j.Level;
+import org.apache.logging.log4j.Marker;
+import org.apache.logging.log4j.core.Filter;
+import org.apache.logging.log4j.core.LogEvent;
+import org.apache.logging.log4j.core.Logger;
+import org.apache.logging.log4j.message.Message;
+import org.apache.logging.log4j.plugins.Node;
+import org.apache.logging.log4j.plugins.Plugin;
+import org.apache.logging.log4j.plugins.PluginBuilderAttribute;
+import org.apache.logging.log4j.plugins.PluginFactory;
+import org.apache.logging.log4j.util.PerformanceSensitive;
+
+/**
+ * This filter returns the onMatch result if the logging level in the event matches the specified logging level
+ * exactly.
+ */
+@Plugin(name = "StringMatchFilter", category = Node.CATEGORY, elementType = Filter.ELEMENT_TYPE, printObject = true)
+@PerformanceSensitive("allocation")
+public final class StringMatchFilter extends AbstractFilter {
+
+    public static final String ATTR_MATCH = "match";
+    private final String text;
+
+    private StringMatchFilter(final String text, final Result onMatch, final Result onMismatch) {
+        super(onMatch, onMismatch);
+        this.text = text;
+    }
+
+    @Override
+    public Result filter(final Logger logger, final Level level, final Marker marker, final String msg,
+                         final Object... params) {
+        return filter(logger.getMessageFactory().newMessage(msg, params).getFormattedMessage());
+    }
+
+    @Override
+    public Result filter(final Logger logger, final Level level, final Marker marker, final Object msg,
+                         final Throwable t) {
+        return filter(logger.getMessageFactory().newMessage(msg).getFormattedMessage());
+    }
+
+    @Override
+    public Result filter(final Logger logger, final Level level, final Marker marker, final Message msg,
+                         final Throwable t) {
+        return filter(msg.getFormattedMessage());
+    }
+
+    @Override
+    public Result filter(final LogEvent event) {
+        return filter(event.getMessage().getFormattedMessage());
+    }
+
+    private Result filter(final String msg) {
+        return msg.contains(this.text) ? onMatch : onMismatch;
+    }
+
+    @Override
+    public Result filter(final Logger logger, final Level level, final Marker marker, final String msg,
+            final Object p0) {
+        return filter(logger.getMessageFactory().newMessage(msg, p0).getFormattedMessage());
+    }
+
+    @Override
+    public Result filter(final Logger logger, final Level level, final Marker marker, final String msg,
+            final Object p0, final Object p1) {
+        return filter(logger.getMessageFactory().newMessage(msg, p0, p1).getFormattedMessage());
+    }
+
+    @Override
+    public Result filter(final Logger logger, final Level level, final Marker marker, final String msg,
+            final Object p0, final Object p1, final Object p2) {
+        return filter(logger.getMessageFactory().newMessage(msg, p0, p1, p2).getFormattedMessage());
+    }
+
+    @Override
+    public Result filter(final Logger logger, final Level level, final Marker marker, final String msg,
+            final Object p0, final Object p1, final Object p2, final Object p3) {
+        return filter(logger.getMessageFactory().newMessage(msg, p0, p1, p2, p3).getFormattedMessage());
+    }
+
+    @Override
+    public Result filter(final Logger logger, final Level level, final Marker marker, final String msg,
+            final Object p0, final Object p1, final Object p2, final Object p3,
+            final Object p4) {
+        return filter(logger.getMessageFactory().newMessage(msg, p0, p1, p2, p3, p4).getFormattedMessage());
+    }
+
+    @Override
+    public Result filter(final Logger logger, final Level level, final Marker marker, final String msg,
+            final Object p0, final Object p1, final Object p2, final Object p3,
+            final Object p4, final Object p5) {
+        return filter(logger.getMessageFactory().newMessage(msg, p0, p1, p2, p3, p4, p5).getFormattedMessage());
+    }
+
+    @Override
+    public Result filter(final Logger logger, final Level level, final Marker marker, final String msg,
+            final Object p0, final Object p1, final Object p2, final Object p3,
+            final Object p4, final Object p5, final Object p6) {
+        return filter(logger.getMessageFactory().newMessage(msg, p0, p1, p2, p3, p4, p5, p6).getFormattedMessage());
+    }
+
+    @Override
+    public Result filter(final Logger logger, final Level level, final Marker marker, final String msg,
+            final Object p0, final Object p1, final Object p2, final Object p3,
+            final Object p4, final Object p5, final Object p6,
+            final Object p7) {
+        return filter(logger.getMessageFactory().newMessage(msg, p0, p1, p2, p3, p4, p5, p6, p7).getFormattedMessage());
+    }
+
+    @Override
+    public Result filter(final Logger logger, final Level level, final Marker marker, final String msg,
+            final Object p0, final Object p1, final Object p2, final Object p3,
+            final Object p4, final Object p5, final Object p6,
+            final Object p7, final Object p8) {
+        return filter(logger.getMessageFactory().newMessage(msg, p0, p1, p2, p3, p4, p5, p6, p7, p8)
+                .getFormattedMessage());
+    }
+
+    @Override
+    public Result filter(final Logger logger, final Level level, final Marker marker, final String msg,
+            final Object p0, final Object p1, final Object p2, final Object p3,
+            final Object p4, final Object p5, final Object p6,
+            final Object p7, final Object p8, final Object p9) {
+        return filter(logger.getMessageFactory().newMessage(msg, p0, p1, p2, p3, p4, p5, p6, p7, p8, p9)
+                .getFormattedMessage());
+    }
+
+    @Override
+    public String toString() {
+        return text;
+    }
+
+    @PluginFactory
+    public static StringMatchFilter.Builder newBuilder() {
+        return new StringMatchFilter.Builder();
+    }
+
+    public static class Builder extends AbstractFilterBuilder<StringMatchFilter.Builder> implements org.apache.logging.log4j.core.util.Builder<StringMatchFilter> {
+        @PluginBuilderAttribute
+        private String text = "";
+
+        /**
+         * Sets the logging level to use.
+         * @param level the logging level to use.
+         * @return this
+         */
+        public StringMatchFilter.Builder setMatchString(final String text) {
+            this.text = text;
+            return this;
+        }
+
+        @Override
+        public StringMatchFilter build() {
+            return new StringMatchFilter(this.text, this.getOnMatch(), this.getOnMismatch());
+        }
+    }
+
+}
diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/util/OptionConverter.java b/log4j-core/src/main/java/org/apache/logging/log4j/core/util/OptionConverter.java
index aad31be..eacd715 100644
--- a/log4j-core/src/main/java/org/apache/logging/log4j/core/util/OptionConverter.java
+++ b/log4j-core/src/main/java/org/apache/logging/log4j/core/util/OptionConverter.java
@@ -16,9 +16,11 @@
  */
 package org.apache.logging.log4j.core.util;
 
+import java.io.InterruptedIOException;
 import java.util.Locale;
 import java.util.Properties;
 
+import org.apache.logging.log4j.Level;
 import org.apache.logging.log4j.Logger;
 import org.apache.logging.log4j.status.StatusLogger;
 import org.apache.logging.log4j.util.PropertiesUtil;
@@ -154,6 +156,72 @@
         return defaultValue;
     }
 
+    public static Level toLevel(String value, Level defaultValue) {
+        if(value == null) {
+            return defaultValue;
+        }
+
+        value = value.trim();
+
+        int hashIndex = value.indexOf('#');
+        if (hashIndex == -1) {
+            if("NULL".equalsIgnoreCase(value)) {
+                return null;
+            } else {
+                // no class name specified : use standard Level class
+                return Level.toLevel(value, defaultValue);
+            }
+        }
+
+        Level result = defaultValue;
+
+        String clazz = value.substring(hashIndex+1);
+        String levelName = value.substring(0, hashIndex);
+
+        // This is degenerate case but you never know.
+        if("NULL".equalsIgnoreCase(levelName)) {
+            return null;
+        }
+
+        LOGGER.debug("toLevel" + ":class=[" + clazz + "]"
+                + ":pri=[" + levelName + "]");
+
+        try {
+            Class customLevel = Loader.loadClass(clazz);
+
+            // get a ref to the specified class' static method
+            // toLevel(String, org.apache.log4j.Level)
+            Class[] paramTypes = new Class[] { String.class, Level.class
+            };
+            java.lang.reflect.Method toLevelMethod =
+                    customLevel.getMethod("toLevel", paramTypes);
+
+            // now call the toLevel method, passing level string + default
+            Object[] params = new Object[] {levelName, defaultValue};
+            Object o = toLevelMethod.invoke(null, params);
+
+            result = (Level) o;
+        } catch(ClassNotFoundException e) {
+            LOGGER.warn("custom level class [" + clazz + "] not found.");
+        } catch(NoSuchMethodException e) {
+            LOGGER.warn("custom level class [" + clazz + "]"
+                    + " does not have a class function toLevel(String, Level)", e);
+        } catch(java.lang.reflect.InvocationTargetException e) {
+            if (e.getTargetException() instanceof InterruptedException
+                    || e.getTargetException() instanceof InterruptedIOException) {
+                Thread.currentThread().interrupt();
+            }
+            LOGGER.warn("custom level class [" + clazz + "]" + " could not be instantiated", e);
+        } catch(ClassCastException e) {
+            LOGGER.warn("class [" + clazz + "] is not a subclass of org.apache.log4j.Level", e);
+        } catch(IllegalAccessException e) {
+            LOGGER.warn("class ["+clazz+ "] cannot be instantiated due to access restrictions", e);
+        } catch(RuntimeException e) {
+            LOGGER.warn("class ["+clazz+"], level [" + levelName + "] conversion failed.", e);
+        }
+        return result;
+    }
+
     /**
      *
      * @param value The size of the file as a String.
diff --git a/log4j-layout-jackson-xml/pom.xml b/log4j-layout-jackson-xml/pom.xml
index 2f3dbb4..6b039b5 100644
--- a/log4j-layout-jackson-xml/pom.xml
+++ b/log4j-layout-jackson-xml/pom.xml
@@ -35,6 +35,11 @@
       <version>${project.version}</version>
     </dependency>
     <dependency>
+      <groupId>org.apache.logging.log4j</groupId>
+      <artifactId>log4j-1.2-api</artifactId>
+      <optional>true</optional>
+    </dependency>
+    <dependency>
       <groupId>com.fasterxml.jackson.dataformat</groupId>
       <artifactId>jackson-dataformat-xml</artifactId>
     </dependency>
diff --git a/log4j-layout-jackson-xml/src/main/java/org/apache/logging/log4j/jackson/xml/builders/layout/XmlLayoutBuilder.java b/log4j-layout-jackson-xml/src/main/java/org/apache/logging/log4j/jackson/xml/builders/layout/XmlLayoutBuilder.java
new file mode 100644
index 0000000..3175ed2
--- /dev/null
+++ b/log4j-layout-jackson-xml/src/main/java/org/apache/logging/log4j/jackson/xml/builders/layout/XmlLayoutBuilder.java
@@ -0,0 +1,57 @@
+/*
+ * 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.jackson.xml.builders.layout;
+
+import org.apache.log4j.Layout;
+import org.apache.log4j.bridge.LayoutWrapper;
+import org.apache.log4j.builders.BooleanHolder;
+import org.apache.log4j.builders.BuilderManager;
+import org.apache.log4j.builders.Holder;
+import org.apache.log4j.builders.layout.LayoutBuilder;
+import org.apache.log4j.xml.XmlConfigurationFactory;
+import org.apache.logging.log4j.Logger;
+import org.apache.logging.log4j.jackson.xml.layout.XmlLayout;
+import org.apache.logging.log4j.plugins.Plugin;
+import org.apache.logging.log4j.status.StatusLogger;
+import org.w3c.dom.Element;
+
+/**
+ * Build a Pattern Layout
+ */
+@Plugin(name = "org.apache.log4j.xml.XMLLayout", category = BuilderManager.CATEGORY)
+public class XmlLayoutBuilder implements LayoutBuilder {
+
+    private static final Logger LOGGER = StatusLogger.getLogger();
+
+
+    @Override
+    public Layout parseLayout(Element layoutElement, XmlConfigurationFactory factory) {
+        final Holder<Boolean> properties = new BooleanHolder();
+        final Holder<Boolean> locationInfo = new BooleanHolder();
+        XmlConfigurationFactory.forEachElement(layoutElement.getElementsByTagName("param"), (currentElement) -> {
+            if ("properties".equalsIgnoreCase(currentElement.getAttribute("name"))) {
+                properties.set(Boolean.parseBoolean(currentElement.getAttribute("value")));
+            } else if ("locationInfo".equalsIgnoreCase(currentElement.getAttribute("name"))) {
+                locationInfo.set(Boolean.parseBoolean(currentElement.getAttribute("value")));
+            }
+        });
+        return new LayoutWrapper(XmlLayout.newBuilder()
+                .setLocationInfo(locationInfo.get())
+                .setProperties(properties.get())
+                .build());
+    }
+}
diff --git a/log4j-perf/src/main/java/org/apache/logging/log4j/perf/jmh/LoggingDisabledBenchmark.java b/log4j-perf/src/main/java/org/apache/logging/log4j/perf/jmh/LoggingDisabledBenchmark.java
new file mode 100644
index 0000000..0f8805c
--- /dev/null
+++ b/log4j-perf/src/main/java/org/apache/logging/log4j/perf/jmh/LoggingDisabledBenchmark.java
@@ -0,0 +1,140 @@
+/*
+ * 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.perf.jmh;
+
+import java.io.File;
+import java.util.concurrent.TimeUnit;
+
+import org.apache.logging.log4j.LogManager;
+import org.apache.logging.log4j.Logger;
+import org.openjdk.jmh.annotations.Benchmark;
+import org.openjdk.jmh.annotations.BenchmarkMode;
+import org.openjdk.jmh.annotations.Mode;
+import org.openjdk.jmh.annotations.OutputTimeUnit;
+import org.openjdk.jmh.annotations.Scope;
+import org.openjdk.jmh.annotations.Setup;
+import org.openjdk.jmh.annotations.State;
+import org.openjdk.jmh.annotations.TearDown;
+import org.slf4j.LoggerFactory;
+
+//import com.newrelic.api.agent.Trace;
+
+/**
+ * Benchmark logging with logging disabled.
+ * // ============================== HOW TO RUN THIS TEST: ====================================
+ * //
+ * // single thread:
+ * // java -jar log4j-perf/target/benchmarks.jar ".*LoggingDisabledBenchmark.*" -f 1 -wi 5 -i 10
+ * //
+ * // multiple threads (for example, 4 threads):
+ * // java -jar log4j-perf/target/benchmarks.jar ".*LoggingDisabledBenchmark.*" -f 1 -wi 5 -i 10 -t 4 -si true
+ * //
+ * // Usage help:
+ * // java -jar log4j-perf/target/benchmarks.jar -help
+ * //
+ */
+@State(Scope.Thread)
+public class LoggingDisabledBenchmark {
+
+    Logger log4j2Logger;
+    org.slf4j.Logger slf4jLogger;
+    org.apache.log4j.Logger log4j1Logger;
+
+    @Setup
+    public void setUp() throws Exception {
+        System.setProperty("log4j.configurationFile", "log4j2-perf2.xml");
+        System.setProperty("log4j.configuration", "log4j12-perf2.xml");
+        System.setProperty("logback.configurationFile", "logback-perf2.xml");
+
+        deleteLogFiles();
+
+        log4j2Logger = LogManager.getLogger(FileAppenderWithLocationBenchmark.class);
+        slf4jLogger = LoggerFactory.getLogger(FileAppenderWithLocationBenchmark.class);
+        log4j1Logger = org.apache.log4j.Logger.getLogger(FileAppenderWithLocationBenchmark.class);
+    }
+
+    @TearDown
+    public void tearDown() {
+        System.clearProperty("log4j.configurationFile");
+        System.clearProperty("log4j.configuration");
+        System.clearProperty("logback.configurationFile");
+
+        deleteLogFiles();
+    }
+
+    private void deleteLogFiles() {
+        final File logbackFile = new File("target/testlogback.log");
+        logbackFile.delete();
+        final File log4jFile = new File ("target/testlog4j.log");
+        log4jFile.delete();
+        final File log4j2File = new File ("target/testlog4j2.log");
+        log4j2File.delete();
+    }
+
+    @Benchmark
+    @BenchmarkMode(Mode.AverageTime) @OutputTimeUnit(TimeUnit.NANOSECONDS)
+    public void baseline() {
+    }
+
+    /*
+      This benchmark tests the overhead of NewRelic on method calls. It is commented out so
+      that we don't have to include the dependency during a "normal" build. Uncomment and add
+      the New Relic Agent client dependency if you would like to test this.
+    @Benchmark
+    @BenchmarkMode(Mode.AverageTime) @OutputTimeUnit(TimeUnit.NANOSECONDS)
+    @Trace(dispatcher = true)
+    public void log4j2NewRelic() {
+        log4j2Logger.debug("This won't be logged");
+    } */
+
+    @Benchmark
+    @BenchmarkMode(Mode.AverageTime) @OutputTimeUnit(TimeUnit.NANOSECONDS)
+    public void log4j2() {
+        log4j2Logger.debug("This won't be logged");
+    }
+
+    @Benchmark
+    @BenchmarkMode(Mode.AverageTime) @OutputTimeUnit(TimeUnit.NANOSECONDS)
+    public void slf4j() {
+        slf4jLogger.debug("This won't be logged");
+    }
+
+    @Benchmark
+    @BenchmarkMode(Mode.AverageTime) @OutputTimeUnit(TimeUnit.NANOSECONDS)
+    public void log4j2IsDebugEnabled() {
+        if (log4j2Logger.isDebugEnabled()) {
+            log4j2Logger.debug("This won't be logged");
+        }
+    }
+
+    @Benchmark
+    @BenchmarkMode(Mode.AverageTime) @OutputTimeUnit(TimeUnit.NANOSECONDS)
+    public void slf4jIsDebugEnabled() {
+        if (slf4jLogger.isDebugEnabled()) {
+            slf4jLogger.debug("This won't be logged");
+        }
+    }
+
+    @Benchmark
+    @BenchmarkMode(Mode.AverageTime) @OutputTimeUnit(TimeUnit.NANOSECONDS)
+    public void log4j1IsDebugEnabled() {
+        if (log4j1Logger.isDebugEnabled()) {
+            log4j1Logger.debug("This won't be logged");
+        }
+    }
+
+}
diff --git a/log4j-perf/src/main/java/org/apache/logging/log4j/perf/jmh/ThreadsafeDateFormatBenchmark.java b/log4j-perf/src/main/java/org/apache/logging/log4j/perf/jmh/ThreadsafeDateFormatBenchmark.java
index 59ac56f..98af16b 100644
--- a/log4j-perf/src/main/java/org/apache/logging/log4j/perf/jmh/ThreadsafeDateFormatBenchmark.java
+++ b/log4j-perf/src/main/java/org/apache/logging/log4j/perf/jmh/ThreadsafeDateFormatBenchmark.java
@@ -18,6 +18,8 @@
 package org.apache.logging.log4j.perf.jmh;
 
 import java.text.SimpleDateFormat;
+import java.time.LocalDateTime;
+import java.time.format.DateTimeFormatter;
 import java.util.Date;
 import java.util.concurrent.TimeUnit;
 import java.util.concurrent.atomic.AtomicReference;
@@ -49,6 +51,7 @@
 public class ThreadsafeDateFormatBenchmark {
 
     private final Date date = new Date();
+    private final DateTimeFormatter dateTimeFormatter = DateTimeFormatter.ofPattern("HH:mm:ss.SSS");
     private final SimpleDateFormat simpleDateFormat = new SimpleDateFormat("HH:mm:ss.SSS");
     private final ThreadLocal<SimpleDateFormat> threadLocalSDFormat = new ThreadLocal<SimpleDateFormat>() {
         @Override
@@ -163,6 +166,14 @@
     @Benchmark
     @BenchmarkMode(Mode.SampleTime)
     @OutputTimeUnit(TimeUnit.NANOSECONDS)
+    public String dateTimeFormatter() {
+        final LocalDateTime now = LocalDateTime.now();
+        return dateTimeFormatter.format(now);
+    }
+
+    @Benchmark
+    @BenchmarkMode(Mode.SampleTime)
+    @OutputTimeUnit(TimeUnit.NANOSECONDS)
     public String threadLocalSimpleDateFmt() {
         final long timestamp = System.currentTimeMillis();
         return threadLocalSDFormat.get().format(timestamp);
diff --git a/pom.xml b/pom.xml
index 2b93411..ff8422a 100644
--- a/pom.xml
+++ b/pom.xml
@@ -347,6 +347,11 @@
       </dependency>

       <dependency>

         <groupId>org.apache.logging.log4j</groupId>

+        <artifactId>log4j--1.2-api</artifactId>

+        <version>${project.version}</version>

+      </dependency>

+      <dependency>

+        <groupId>org.apache.logging.log4j</groupId>

         <artifactId>log4j-plugins</artifactId>

         <version>${project.version}</version>

         <type>test-jar</type>