Merge pull request #314 from supertomcat/LOG4J2-2707

LOG4J2-2707: ArrayIndexOutOfBoundsException in UuidUtil, when MAC add…
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 e0e5aef..2608c6c 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
@@ -29,6 +29,7 @@
 import org.apache.log4j.or.RendererSupport;
 import org.apache.log4j.spi.LoggerFactory;
 import org.apache.log4j.spi.LoggingEvent;
+import org.apache.logging.log4j.message.MapMessage;
 import org.apache.logging.log4j.spi.ExtendedLogger;
 import org.apache.logging.log4j.spi.LoggerContext;
 import org.apache.logging.log4j.message.LocalizedMessage;
@@ -380,12 +381,15 @@
 
     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);
+            @SuppressWarnings("unchecked")
+            Message msg = message instanceof Message ? (Message) message : message instanceof Map ?
+                    new MapMessage((Map) message) : new ObjectMessage(message);
+            ((ExtendedLogger) logger).logMessage(fqcn, lvl, null, msg, t);
         } else {
+            ObjectRenderer renderer = get(message.getClass());
+            final Message msg = message instanceof Message ? (Message) message : renderer != null ?
+                    new RenderedMessage(renderer, message) : new ObjectMessage(message);
             logger.log(lvl, msg, t);
         }
     }
@@ -475,14 +479,16 @@
 
     public void log(final Priority priority, final Object message, final Throwable t) {
         if (isEnabledFor(priority)) {
-            final Message msg = new ObjectMessage(message);
+            @SuppressWarnings("unchecked")
+            final Message msg = message instanceof Map ? new MapMessage((Map) message) : 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);
+            @SuppressWarnings("unchecked")
+            final Message msg = message instanceof Map ? new MapMessage((Map) message) : new ObjectMessage(message);
             forcedLog(FQCN, priority, msg, null);
         }
     }
@@ -497,10 +503,12 @@
     private void maybeLog(final String fqcn, final org.apache.logging.log4j.Level level,
             final Object message, final Throwable throwable) {
         if (logger.isEnabled(level)) {
+            @SuppressWarnings("unchecked")
+            Message msg = message instanceof Map ? new MapMessage((Map) message) : new ObjectMessage(message);
             if (logger instanceof ExtendedLogger) {
-                ((ExtendedLogger) logger).logMessage(fqcn, level, null, new ObjectMessage(message), throwable);
+                ((ExtendedLogger) logger).logMessage(fqcn, level, null, msg, throwable);
             } else {
-                logger.log(level, message, throwable);
+                logger.log(level, msg, throwable);
             }
         }
     }
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
index 9ad4d27..9de0476 100644
--- 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
@@ -16,8 +16,6 @@
  */
 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;
@@ -29,6 +27,10 @@
 import org.apache.logging.log4j.spi.StandardLevel;
 import org.apache.logging.log4j.status.StatusLogger;
 
+import java.lang.reflect.Method;
+import java.util.Map;
+import java.util.Set;
+
 /**
  * Converts a Log4j 2 LogEvent into the components needed by a Log4j 1.x LoggingEvent.
  * This class requires Log4j 2.
@@ -43,13 +45,47 @@
         this.event = event;
     }
 
+    /**
+     * Returns the time when the application started, in milliseconds
+     * elapsed since 01.01.1970.
+     */
+    public static long getStartTime() {
+        return JVM_START_TIME;
+    }
+
+    /**
+     * 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();
+        }
+    }
+
     public LogEvent getEvent() {
         return this.event;
     }
 
     /**
-     Set the location information for this logging event. The collected
-     information is cached for future use.
+     * Set the location information for this logging event. The collected
+     * information is cached for future use.
      */
     @Override
     public LocationInfo getLocationInformation() {
@@ -58,7 +94,8 @@
 
     /**
      * Return the level of this event. Use this form instead of directly
-     * accessing the <code>level</code> field.  */
+     * accessing the <code>level</code> field.
+     */
     @Override
     public Level getLevel() {
         switch (StandardLevel.getStandardLevel(event.getLevel().intLevel())) {
@@ -70,8 +107,6 @@
                 return Level.INFO;
             case WARN:
                 return Level.WARN;
-            case ERROR:
-                return Level.ERROR;
             case FATAL:
                 return Level.FATAL;
             case OFF:
@@ -104,27 +139,23 @@
      Return the message for this logging event.
     */
     @Override
-    public
-    Object getMessage() {
-        return event.getMessage().getFormattedMessage();
+    public Object getMessage() {
+        return event.getMessage();
     }
 
     /*
      * This method returns the NDC for this event.
      */
     @Override
-    public
-    String getNDC() {
+    public String getNDC() {
         return event.getContextStack().toString();
     }
 
-
     /*
      Returns the the context corresponding to the <code>key</code> parameter.
      */
     @Override
-    public
-    Object getMDC(String key) {
+    public Object getMDC(String key) {
         if (event.getContextData() != null) {
             return event.getContextData().getValue(key);
         } else {
@@ -133,44 +164,34 @@
     }
 
     /**
-     Obtain a copy of this thread's MDC prior to serialization or
-     asynchronous logging.
+     * Obtain a copy of this thread's MDC prior to serialization or
+     * asynchronous logging.
      */
     @Override
-    public
-    void getMDCCopy() {
+    public void getMDCCopy() {
     }
 
     @Override
-    public
-    String getRenderedMessage() {
+    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() {
+    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 */
+     * 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() {
+    public ThrowableInformation getThrowableInformation() {
         if (event.getThrown() != null) {
             return new ThrowableInformation(event.getThrown());
         }
@@ -178,40 +199,28 @@
     }
 
     /**
-     Return this event's throwable's string[] representaion.
+     * Return this event's throwable's string[] representaion.
      */
     @Override
-    public
-    String[] getThrowableStrRep() {
+    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);
+    @Override
+    public String getProperty(final String key) {
+        return event.getContextData().getValue(key);
+    }
 
-            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();
-        }
+    @Override
+    public Set getPropertyKeySet() {
+        return event.getContextData().toMap().keySet();
+    }
+
+    @Override
+    public Map getProperties() {
+        return event.getContextData().toMap();
     }
 }
diff --git a/log4j-1.2-api/src/main/java/org/apache/log4j/bridge/LogEventWrapper.java b/log4j-1.2-api/src/main/java/org/apache/log4j/bridge/LogEventWrapper.java
new file mode 100644
index 0000000..1e46e12
--- /dev/null
+++ b/log4j-1.2-api/src/main/java/org/apache/log4j/bridge/LogEventWrapper.java
@@ -0,0 +1,216 @@
+/*
+ * 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.NDC;
+import org.apache.log4j.helpers.OptionConverter;
+import org.apache.log4j.spi.LocationInfo;
+import org.apache.log4j.spi.LoggingEvent;
+import org.apache.logging.log4j.Level;
+import org.apache.logging.log4j.Marker;
+import org.apache.logging.log4j.ThreadContext;
+import org.apache.logging.log4j.core.LogEvent;
+import org.apache.logging.log4j.core.impl.ThrowableProxy;
+import org.apache.logging.log4j.core.time.Instant;
+import org.apache.logging.log4j.core.time.MutableInstant;
+import org.apache.logging.log4j.message.Message;
+import org.apache.logging.log4j.message.SimpleMessage;
+import org.apache.logging.log4j.spi.MutableThreadContextStack;
+import org.apache.logging.log4j.util.BiConsumer;
+import org.apache.logging.log4j.util.ReadOnlyStringMap;
+import org.apache.logging.log4j.util.TriConsumer;
+
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * Exposes a Log4j 1 logging event as a Log4j 2 LogEvent.
+ */
+public class LogEventWrapper implements LogEvent {
+
+    private final LoggingEvent event;
+    private final ContextDataMap contextData;
+    private final MutableThreadContextStack contextStack;
+    private volatile Thread thread;
+
+    public LogEventWrapper(LoggingEvent event) {
+        this.event = event;
+        this.contextData = new ContextDataMap(event.getProperties());
+        this.contextStack = new MutableThreadContextStack(NDC.cloneStack());
+    }
+
+    @Override
+    public LogEvent toImmutable() {
+        return null;
+    }
+
+    @Override
+    public ReadOnlyStringMap getContextData() {
+        return contextData;
+    }
+
+    @Override
+    public ThreadContext.ContextStack getContextStack() {
+        return contextStack;
+    }
+
+    @Override
+    public String getLoggerFqcn() {
+        return null;
+    }
+
+    @Override
+    public Level getLevel() {
+        return OptionConverter.convertLevel(event.getLevel());
+    }
+
+    @Override
+    public String getLoggerName() {
+        return event.getLoggerName();
+    }
+
+    @Override
+    public Marker getMarker() {
+        return null;
+    }
+
+    @Override
+    public Message getMessage() {
+        return new SimpleMessage(event.getRenderedMessage());
+    }
+
+    @Override
+    public long getTimeMillis() {
+        return event.getTimeStamp();
+    }
+
+    @Override
+    public Instant getInstant() {
+        MutableInstant mutable = new MutableInstant();
+        mutable.initFromEpochMilli(event.getTimeStamp(), 0);
+        return mutable;
+    }
+
+    @Override
+    public StackTraceElement getSource() {
+        LocationInfo info = event.getLocationInformation();
+        return new StackTraceElement(info.getClassName(), info.getMethodName(), info.getFileName(),
+                Integer.parseInt(info.getLineNumber()));
+    }
+
+    @Override
+    public String getThreadName() {
+        return event.getThreadName();
+    }
+
+    @Override
+    public long getThreadId() {
+        Thread thread = getThread();
+        return thread != null ? thread.getId() : 0;
+    }
+
+    @Override
+    public int getThreadPriority() {
+            Thread thread = getThread();
+            return thread != null ? thread.getPriority() : 0;
+    }
+
+    private Thread getThread() {
+        if (thread == null) {
+            for (Thread thread : Thread.getAllStackTraces().keySet()) {
+                if (thread.getName().equals(event.getThreadName())) {
+                    this.thread = thread;
+                    return thread;
+                }
+            }
+        }
+        return null;
+    }
+
+    @Override
+    public Throwable getThrown() {
+        if (event.getThrowableInformation() != null) {
+            return event.getThrowableInformation().getThrowable();
+        }
+        return null;
+    }
+
+    @Override
+    public ThrowableProxy getThrownProxy() {
+        return null;
+    }
+
+    @Override
+    public boolean isEndOfBatch() {
+        return false;
+    }
+
+    @Override
+    public boolean isIncludeLocation() {
+        return false;
+    }
+
+    @Override
+    public void setEndOfBatch(boolean endOfBatch) {
+
+    }
+
+    @Override
+    public void setIncludeLocation(boolean locationRequired) {
+
+    }
+
+    @Override
+    public long getNanoTime() {
+        return 0;
+    }
+
+
+    private static class ContextDataMap extends HashMap<String, String> implements ReadOnlyStringMap {
+
+        ContextDataMap(Map<String, String> map) {
+            if (map != null) {
+                super.putAll(map);
+            }
+        }
+
+        @Override
+        public Map<String, String> toMap() {
+            return this;
+        }
+
+        @Override
+        public boolean containsKey(String key) {
+            return super.containsKey(key);
+        }
+
+        @Override
+        public <V> void forEach(BiConsumer<String, ? super V> action) {
+            super.forEach((k,v) -> action.accept(k, (V) v));
+        }
+
+        @Override
+        public <V, S> void forEach(TriConsumer<String, ? super V, S> action, S state) {
+            super.forEach((k,v) -> action.accept(k, (V) v, state));
+        }
+
+        @Override
+        public <V> V getValue(String key) {
+            return (V) super.get(key);
+        }
+    }
+}
diff --git a/log4j-1.2-api/src/main/java/org/apache/log4j/bridge/RewritePolicyAdapter.java b/log4j-1.2-api/src/main/java/org/apache/log4j/bridge/RewritePolicyAdapter.java
new file mode 100644
index 0000000..cf0b42b
--- /dev/null
+++ b/log4j-1.2-api/src/main/java/org/apache/log4j/bridge/RewritePolicyAdapter.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.rewrite.RewritePolicy;
+import org.apache.log4j.spi.LoggingEvent;
+import org.apache.logging.log4j.core.LogEvent;
+
+
+/**
+ * Binds a Log4j 1.x RewritePolicy to Log4j 2.
+ */
+public class RewritePolicyAdapter implements org.apache.logging.log4j.core.appender.rewrite.RewritePolicy {
+
+    private final RewritePolicy policy;
+
+    /**
+     * Constructor.
+     */
+    public RewritePolicyAdapter(RewritePolicy policy) {
+        this.policy = policy;
+    }
+
+    @Override
+    public LogEvent rewrite(LogEvent source) {
+        LoggingEvent event = policy.rewrite(new LogEventAdapter(source));
+        return event instanceof LogEventAdapter ? ((LogEventAdapter) event).getEvent() : new LogEventWrapper(event);
+    }
+
+    public RewritePolicy getPolicy() {
+        return this.policy;
+    }
+
+}
diff --git a/log4j-1.2-api/src/main/java/org/apache/log4j/bridge/RewritePolicyWrapper.java b/log4j-1.2-api/src/main/java/org/apache/log4j/bridge/RewritePolicyWrapper.java
new file mode 100644
index 0000000..e0994e1
--- /dev/null
+++ b/log4j-1.2-api/src/main/java/org/apache/log4j/bridge/RewritePolicyWrapper.java
@@ -0,0 +1,44 @@
+/*
+ * 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.LoggingEvent;
+import org.apache.logging.log4j.core.LogEvent;
+import org.apache.log4j.rewrite.RewritePolicy;
+
+/**
+ * Binds a Log4j 2 RewritePolicy to Log4j 1.
+ */
+public class RewritePolicyWrapper implements RewritePolicy {
+
+    private final org.apache.logging.log4j.core.appender.rewrite.RewritePolicy policy;
+
+    public RewritePolicyWrapper(org.apache.logging.log4j.core.appender.rewrite.RewritePolicy policy) {
+        this.policy = policy;
+    }
+
+    @Override
+    public LoggingEvent rewrite(LoggingEvent source) {
+        LogEvent event = source instanceof LogEventAdapter ? ((LogEventAdapter) source).getEvent() :
+                new LogEventWrapper(source);
+        return new LogEventAdapter(policy.rewrite(event));
+    }
+
+    public org.apache.logging.log4j.core.appender.rewrite.RewritePolicy getPolicy() {
+        return policy;
+    }
+}
diff --git a/log4j-1.2-api/src/main/java/org/apache/log4j/builders/BuilderManager.java b/log4j-1.2-api/src/main/java/org/apache/log4j/builders/BuilderManager.java
index d6465f0..3319bf0 100644
--- a/log4j-1.2-api/src/main/java/org/apache/log4j/builders/BuilderManager.java
+++ b/log4j-1.2-api/src/main/java/org/apache/log4j/builders/BuilderManager.java
@@ -21,7 +21,9 @@
 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.builders.rewrite.RewritePolicyBuilder;
 import org.apache.log4j.config.PropertiesConfiguration;
+import org.apache.log4j.rewrite.RewritePolicy;
 import org.apache.log4j.spi.Filter;
 import org.apache.log4j.xml.XmlConfiguration;
 import org.apache.logging.log4j.Logger;
@@ -72,7 +74,7 @@
         if (plugin != null) {
             AppenderBuilder builder = createBuilder(plugin, prefix, props);
             if (builder != null) {
-                return builder.parseAppender(name, layoutPrefix, filterPrefix, props, config);
+                return builder.parseAppender(name, prefix, layoutPrefix, filterPrefix, props, config);
             }
         }
         return null;
@@ -127,6 +129,30 @@
         return null;
     }
 
+    public RewritePolicy parseRewritePolicy(String className, Element rewriteElement, XmlConfiguration config) {
+        PluginType<?> plugin = plugins.get(className.toLowerCase());
+        if (plugin != null) {
+            try {
+                @SuppressWarnings("unchecked")
+                RewritePolicyBuilder builder = (RewritePolicyBuilder) LoaderUtil.newInstanceOf(plugin.getPluginClass());
+                return builder.parseRewritePolicy(rewriteElement, config);
+            } catch (InstantiationException | IllegalAccessException | InvocationTargetException ex) {
+                LOGGER.warn("Unable to load plugin: {} due to: {}", plugin.getKey(), ex.getMessage());
+            }
+        }
+        return null;
+    }
+    public RewritePolicy parseRewritePolicy(String className, String policyPrefix, Properties props, PropertiesConfiguration config) {
+        PluginType<?> plugin = plugins.get(className.toLowerCase());
+        if (plugin != null) {
+            RewritePolicyBuilder builder = createBuilder(plugin, policyPrefix, props);
+            if (builder != null) {
+                return builder.parseRewritePolicy(config);
+            }
+        }
+        return null;
+    }
+
     private <T extends AbstractBuilder> T createBuilder(PluginType<?> plugin, String prefix, Properties props) {
         try {
             Class<?> clazz = plugin.getPluginClass();
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
index 0572b86..bb7ff93 100644
--- 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
@@ -30,7 +30,7 @@
 
     Appender parseAppender(Element element, XmlConfiguration configuration);
 
-    Appender parseAppender(String name, String layoutPrefix, String filterPrefix, Properties props,
-            PropertiesConfiguration configuration);
+    Appender parseAppender(String name, String appenderPrefix, String layoutPrefix, String filterPrefix,
+            Properties props, PropertiesConfiguration configuration);
 
 }
diff --git a/log4j-1.2-api/src/main/java/org/apache/log4j/builders/appender/AsyncAppenderBuilder.java b/log4j-1.2-api/src/main/java/org/apache/log4j/builders/appender/AsyncAppenderBuilder.java
index 86969ed..e60d50b 100644
--- a/log4j-1.2-api/src/main/java/org/apache/log4j/builders/appender/AsyncAppenderBuilder.java
+++ b/log4j-1.2-api/src/main/java/org/apache/log4j/builders/appender/AsyncAppenderBuilder.java
@@ -126,7 +126,7 @@
     }
 
     @Override
-    public Appender parseAppender(final String name, final String layoutPrefix,
+    public Appender parseAppender(final String name, final String appenderPrefix, final String layoutPrefix,
             final String filterPrefix, final Properties props, final PropertiesConfiguration configuration) {
         String appenderRef = getProperty(APPENDER_REF_TAG);
         boolean blocking = getBooleanProperty(BLOCKING_PARAM);
diff --git a/log4j-1.2-api/src/main/java/org/apache/log4j/builders/appender/ConsoleAppenderBuilder.java b/log4j-1.2-api/src/main/java/org/apache/log4j/builders/appender/ConsoleAppenderBuilder.java
index 423fab7..119e6fb 100644
--- a/log4j-1.2-api/src/main/java/org/apache/log4j/builders/appender/ConsoleAppenderBuilder.java
+++ b/log4j-1.2-api/src/main/java/org/apache/log4j/builders/appender/ConsoleAppenderBuilder.java
@@ -129,7 +129,7 @@
     }
 
     @Override
-    public Appender parseAppender(final String name, final String layoutPrefix,
+    public Appender parseAppender(final String name, final String appenderPrefix, final String layoutPrefix,
             final String filterPrefix, final Properties props, final PropertiesConfiguration configuration) {
         Layout layout = configuration.parseLayout(layoutPrefix, name, props);
         Filter filter = configuration.parseAppenderFilters(props, filterPrefix, name);
diff --git a/log4j-1.2-api/src/main/java/org/apache/log4j/builders/appender/DailyRollingFileAppenderBuilder.java b/log4j-1.2-api/src/main/java/org/apache/log4j/builders/appender/DailyRollingFileAppenderBuilder.java
index 1dd1885..89c2dba 100644
--- a/log4j-1.2-api/src/main/java/org/apache/log4j/builders/appender/DailyRollingFileAppenderBuilder.java
+++ b/log4j-1.2-api/src/main/java/org/apache/log4j/builders/appender/DailyRollingFileAppenderBuilder.java
@@ -136,8 +136,8 @@
     }
 
     @Override
-    public Appender parseAppender(final String name, final String layoutPrefix, final String filterPrefix,
-            final Properties props, final PropertiesConfiguration configuration) {
+    public Appender parseAppender(final String name, final String appenderPrefix, final String layoutPrefix,
+            final String filterPrefix, final Properties props, final PropertiesConfiguration configuration) {
         Layout layout = configuration.parseLayout(layoutPrefix, name, props);
         Filter filter = configuration.parseAppenderFilters(props, filterPrefix, name);
         String fileName = getProperty(FILE_PARAM);
diff --git a/log4j-1.2-api/src/main/java/org/apache/log4j/builders/appender/FileAppenderBuilder.java b/log4j-1.2-api/src/main/java/org/apache/log4j/builders/appender/FileAppenderBuilder.java
index 9d108c2..8b7f467 100644
--- a/log4j-1.2-api/src/main/java/org/apache/log4j/builders/appender/FileAppenderBuilder.java
+++ b/log4j-1.2-api/src/main/java/org/apache/log4j/builders/appender/FileAppenderBuilder.java
@@ -132,8 +132,8 @@
 
 
     @Override
-    public Appender parseAppender(final String name, final String layoutPrefix, final String filterPrefix,
-            final Properties props, final PropertiesConfiguration configuration) {
+    public Appender parseAppender(final String name, final String appenderPrefix, final String layoutPrefix,
+            final String filterPrefix, final Properties props, final PropertiesConfiguration configuration) {
         Layout layout = configuration.parseLayout(layoutPrefix, name, props);
         Filter filter = configuration.parseAppenderFilters(props, filterPrefix, name);
         String level = getProperty(THRESHOLD_PARAM);
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
index 8b69d39..07f2fd5 100644
--- 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
@@ -46,7 +46,7 @@
 
 
     @Override
-    public Appender parseAppender(final String name, final String layoutPrefix,
+    public Appender parseAppender(final String name, final String appenderPrefix, final String layoutPrefix,
             final String filterPrefix, final Properties props, final PropertiesConfiguration configuration) {
         return new AppenderWrapper(NullAppender.createAppender(name));
     }
diff --git a/log4j-1.2-api/src/main/java/org/apache/log4j/builders/appender/RewriteAppenderBuilder.java b/log4j-1.2-api/src/main/java/org/apache/log4j/builders/appender/RewriteAppenderBuilder.java
new file mode 100644
index 0000000..37ac70f
--- /dev/null
+++ b/log4j-1.2-api/src/main/java/org/apache/log4j/builders/appender/RewriteAppenderBuilder.java
@@ -0,0 +1,152 @@
+/*
+ * 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.bridge.RewritePolicyAdapter;
+import org.apache.log4j.bridge.RewritePolicyWrapper;
+import org.apache.log4j.builders.AbstractBuilder;
+import org.apache.log4j.builders.Holder;
+import org.apache.log4j.config.Log4j1Configuration;
+import org.apache.log4j.config.PropertiesConfiguration;
+import org.apache.log4j.helpers.OptionConverter;
+import org.apache.log4j.rewrite.RewritePolicy;
+import org.apache.log4j.spi.Filter;
+import org.apache.log4j.xml.XmlConfiguration;
+import org.apache.logging.log4j.Logger;
+import org.apache.logging.log4j.core.appender.rewrite.RewriteAppender;
+import org.apache.logging.log4j.core.config.AppenderRef;
+import org.apache.logging.log4j.plugins.Plugin;
+import org.apache.logging.log4j.status.StatusLogger;
+import org.w3c.dom.Element;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Properties;
+
+import static org.apache.log4j.builders.BuilderManager.CATEGORY;
+import static org.apache.log4j.config.Log4j1Configuration.APPENDER_REF_TAG;
+import static org.apache.log4j.config.Log4j1Configuration.THRESHOLD_PARAM;
+import static org.apache.log4j.xml.XmlConfiguration.FILTER_TAG;
+import static org.apache.log4j.xml.XmlConfiguration.NAME_ATTR;
+import static org.apache.log4j.xml.XmlConfiguration.PARAM_TAG;
+import static org.apache.log4j.xml.XmlConfiguration.VALUE_ATTR;
+import static org.apache.log4j.xml.XmlConfiguration.forEachElement;
+
+
+/**
+ * Build an Asynch Appender
+ */
+@Plugin(name = "org.apache.log4j.rewrite.RewriteAppender", category = CATEGORY)
+public class RewriteAppenderBuilder extends AbstractBuilder implements AppenderBuilder {
+
+    private static final Logger LOGGER = StatusLogger.getLogger();
+    private static final String REWRITE_POLICY_TAG = "rewritePolicy";
+
+    public RewriteAppenderBuilder() {
+    }
+
+    public RewriteAppenderBuilder(String prefix, Properties props) {
+        super(prefix, props);
+    }
+
+    @Override
+    public Appender parseAppender(final Element appenderElement, final XmlConfiguration config) {
+        String name = appenderElement.getAttribute(NAME_ATTR);
+        Holder<List<String>> appenderRefs = new Holder<>(new ArrayList<>());
+        Holder<RewritePolicy> rewritePolicyHolder = new Holder<>();
+        Holder<String> level = new Holder<>();
+        Holder<Filter> filter = new Holder<>();
+        forEachElement(appenderElement.getChildNodes(), (currentElement) -> {
+            switch (currentElement.getTagName()) {
+                case APPENDER_REF_TAG:
+                    Appender appender = config.findAppenderByReference(currentElement);
+                    if (appender != null) {
+                        appenderRefs.get().add(appender.getName());
+                    }
+                    break;
+                case REWRITE_POLICY_TAG: {
+                    RewritePolicy policy = config.parseRewritePolicy(currentElement);
+                    if (policy != null) {
+                        rewritePolicyHolder.set(policy);
+                    }
+                    break;
+                }
+                case FILTER_TAG: {
+                    filter.set(config.parseFilters(currentElement));
+                    break;
+                }
+                case PARAM_TAG: {
+                    if (currentElement.getAttribute(NAME_ATTR).equalsIgnoreCase(THRESHOLD_PARAM)) {
+                        String value = currentElement.getAttribute(VALUE_ATTR);
+                        if (value == null) {
+                            LOGGER.warn("No value supplied for Threshold parameter, ignoring.");
+                        } else {
+                            level.set(value);
+                        }
+                    }
+                    break;
+                }
+            }
+        });
+        return createAppender(name, level.get(), appenderRefs.get().toArray(new String[0]), rewritePolicyHolder.get(),
+                filter.get(), config);
+    }
+
+    @Override
+    public Appender parseAppender(final String name, final String appenderPrefix, final String layoutPrefix,
+            final String filterPrefix, final Properties props, final PropertiesConfiguration configuration) {
+        String appenderRef = getProperty(APPENDER_REF_TAG);
+        Filter filter = configuration.parseAppenderFilters(props, filterPrefix, name);
+        String policyPrefix = appenderPrefix + ".rewritePolicy";
+        String className = getProperty(policyPrefix);
+        RewritePolicy policy = configuration.getBuilderManager().parseRewritePolicy(className, policyPrefix,
+                props, configuration);
+        String level = getProperty(THRESHOLD_PARAM);
+        if (appenderRef == null) {
+            LOGGER.warn("No appender references configured for AsyncAppender {}", name);
+            return null;
+        }
+        Appender appender = configuration.parseAppender(props, appenderRef);
+        if (appender == null) {
+            LOGGER.warn("Cannot locate Appender {}", appenderRef);
+            return null;
+        }
+        return createAppender(name, level, new String[] {appenderRef}, policy, filter, configuration);
+    }
+
+    private <T extends Log4j1Configuration> Appender createAppender(String name, String level,
+            String[] appenderRefs, RewritePolicy policy, Filter filter, T configuration) {
+        org.apache.logging.log4j.Level logLevel = OptionConverter.convertLevel(level,
+                org.apache.logging.log4j.Level.TRACE);
+        AppenderRef[] refs = new AppenderRef[appenderRefs.length];
+        int index = 0;
+        for (String appenderRef : appenderRefs) {
+            refs[index++] = AppenderRef.createAppenderRef(appenderRef, logLevel, null);
+        }
+        org.apache.logging.log4j.core.Filter rewriteFilter = buildFilters(level, filter);
+        org.apache.logging.log4j.core.appender.rewrite.RewritePolicy rewritePolicy;
+        if (policy instanceof RewritePolicyWrapper) {
+            rewritePolicy = ((RewritePolicyWrapper) policy).getPolicy();
+        } else {
+            rewritePolicy = new RewritePolicyAdapter(policy);
+        }
+        return new AppenderWrapper(RewriteAppender.createAppender(name, true, refs, configuration,
+                rewritePolicy, rewriteFilter));
+    }
+}
diff --git a/log4j-1.2-api/src/main/java/org/apache/log4j/builders/appender/RollingFileAppenderBuilder.java b/log4j-1.2-api/src/main/java/org/apache/log4j/builders/appender/RollingFileAppenderBuilder.java
index ccc8099..bac9425 100644
--- a/log4j-1.2-api/src/main/java/org/apache/log4j/builders/appender/RollingFileAppenderBuilder.java
+++ b/log4j-1.2-api/src/main/java/org/apache/log4j/builders/appender/RollingFileAppenderBuilder.java
@@ -158,7 +158,7 @@
 
 
     @Override
-    public Appender parseAppender(final String name, final String layoutPrefix,
+    public Appender parseAppender(final String name, final String appenderPrefix, final String layoutPrefix,
             final String filterPrefix, final Properties props, final PropertiesConfiguration configuration) {
         Layout layout = configuration.parseLayout(layoutPrefix, name, props);
         Filter filter = configuration.parseAppenderFilters(props, filterPrefix, name);
diff --git a/log4j-1.2-api/src/main/java/org/apache/log4j/builders/appender/SyslogAppenderBuilder.java b/log4j-1.2-api/src/main/java/org/apache/log4j/builders/appender/SyslogAppenderBuilder.java
new file mode 100644
index 0000000..c281ea9
--- /dev/null
+++ b/log4j-1.2-api/src/main/java/org/apache/log4j/builders/appender/SyslogAppenderBuilder.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.log4j.builders.appender;
+
+import org.apache.log4j.Appender;
+import org.apache.log4j.Layout;
+import org.apache.log4j.bridge.AppenderWrapper;
+import org.apache.log4j.bridge.LayoutAdapter;
+import org.apache.log4j.bridge.LayoutWrapper;
+import org.apache.log4j.builders.AbstractBuilder;
+import org.apache.log4j.builders.Holder;
+import org.apache.log4j.config.Log4j1Configuration;
+import org.apache.log4j.config.PropertiesConfiguration;
+import org.apache.log4j.spi.Filter;
+import org.apache.log4j.xml.XmlConfiguration;
+import org.apache.logging.log4j.Logger;
+import org.apache.logging.log4j.core.appender.SyslogAppender;
+import org.apache.logging.log4j.core.layout.SyslogLayout;
+import org.apache.logging.log4j.core.net.Facility;
+import org.apache.logging.log4j.core.net.Protocol;
+import org.apache.logging.log4j.plugins.Plugin;
+import org.apache.logging.log4j.status.StatusLogger;
+import org.w3c.dom.Element;
+
+import java.util.Properties;
+
+import static org.apache.log4j.builders.BuilderManager.CATEGORY;
+import static org.apache.log4j.config.Log4j1Configuration.THRESHOLD_PARAM;
+import static org.apache.log4j.xml.XmlConfiguration.FILTER_TAG;
+import static org.apache.log4j.xml.XmlConfiguration.LAYOUT_TAG;
+import static org.apache.log4j.xml.XmlConfiguration.NAME_ATTR;
+import static org.apache.log4j.xml.XmlConfiguration.PARAM_TAG;
+import static org.apache.log4j.xml.XmlConfiguration.VALUE_ATTR;
+import static org.apache.log4j.xml.XmlConfiguration.forEachElement;
+
+/**
+ * Build a File Appender
+ */
+@Plugin(name = "org.apache.log4j.net.SyslogAppender", category = CATEGORY)
+public class SyslogAppenderBuilder extends AbstractBuilder implements AppenderBuilder {
+
+    private static final Logger LOGGER = StatusLogger.getLogger();
+    private static final String FACILITY_PARAM = "Facility";
+    private static final String SYSLOG_HOST_PARAM = "SyslogHost";
+    private static int SYSLOG_PORT = 512;
+
+    public SyslogAppenderBuilder() {
+    }
+
+    public SyslogAppenderBuilder(String prefix, Properties props) {
+        super(prefix, props);
+    }
+
+    @Override
+    public Appender parseAppender(Element appenderElement, XmlConfiguration config) {
+        String name = appenderElement.getAttribute(NAME_ATTR);
+        Holder<Layout> layout = new Holder<>();
+        Holder<Filter> filter = new Holder<>();
+        Holder<String> facility = new Holder<>();
+        Holder<String> level = new Holder<>();
+        Holder<String> host = new Holder<>();
+        forEachElement(appenderElement.getChildNodes(), (currentElement) -> {
+            switch (currentElement.getTagName()) {
+                case LAYOUT_TAG:
+                    layout.set(config.parseLayout(currentElement));
+                    break;
+                case FILTER_TAG:
+                    filter.set(config.parseFilters(currentElement));
+                    break;
+                case PARAM_TAG: {
+                    switch (currentElement.getAttribute(NAME_ATTR)) {
+                        case SYSLOG_HOST_PARAM: {
+                            host.set(currentElement.getAttribute(VALUE_ATTR));
+                            break;
+                        }
+                        case FACILITY_PARAM:
+                            facility.set(currentElement.getAttribute(VALUE_ATTR));
+                            break;
+                        case THRESHOLD_PARAM: {
+                            String value = currentElement.getAttribute(VALUE_ATTR);
+                            if (value == null) {
+                                LOGGER.warn("No value supplied for Threshold parameter, ignoring.");
+                            } else {
+                                level.set(value);
+                            }
+                            break;
+                        }
+                    }
+                    break;
+                }
+            }
+        });
+
+        return createAppender(name, config, layout.get(), facility.get(), filter.get(), host.get(), level.get());
+    }
+
+
+    @Override
+    public Appender parseAppender(final String name, final String appenderPrefix, final String layoutPrefix,
+            final String filterPrefix, final Properties props, final PropertiesConfiguration configuration) {
+        Filter filter = configuration.parseAppenderFilters(props, filterPrefix, name);
+        Layout layout = configuration.parseLayout(layoutPrefix, name, props);
+        String level = getProperty(THRESHOLD_PARAM);
+        String facility = getProperty(FACILITY_PARAM, "LOCAL0");
+        String syslogHost = getProperty(SYSLOG_HOST_PARAM, "localhost:514");
+
+        return createAppender(name, configuration, layout, facility, filter, syslogHost, level);
+    }
+
+    private Appender createAppender(final String name, final Log4j1Configuration configuration, Layout layout,
+            String facility, final Filter filter, final String syslogHost, final String level) {
+        Holder<String> host = new Holder<>();
+        Holder<Integer> port = new Holder<>();
+        resolveSyslogHost(syslogHost, host, port);
+        org.apache.logging.log4j.core.Layout appenderLayout;
+        if (layout instanceof LayoutWrapper) {
+            appenderLayout = ((LayoutWrapper) layout).getLayout();
+        } else if (layout != null) {
+            appenderLayout = new LayoutAdapter(layout);
+        } else {
+            appenderLayout = SyslogLayout.newBuilder()
+                    .setFacility(Facility.toFacility(facility))
+                    .setConfiguration(configuration)
+                    .build();
+        }
+
+        org.apache.logging.log4j.core.Filter fileFilter = buildFilters(level, filter);
+        return new AppenderWrapper(SyslogAppender.newBuilder()
+                .setName(name)
+                .setConfiguration(configuration)
+                .setLayout(appenderLayout)
+                .setFilter(fileFilter)
+                .setPort(port.get())
+                .setProtocol(Protocol.TCP)
+                .setHost(host.get())
+                .build());
+    }
+
+    private void resolveSyslogHost(String syslogHost, Holder<String> host, Holder<Integer> port) {
+        int urlPort = -1;
+
+        //
+        //  If not an unbracketed IPv6 address then
+        //      parse as a URL
+        //
+        String[] parts = syslogHost.split(":");
+        if (parts.length == 1) {
+            host.set(parts[0]);
+            port.set(SYSLOG_PORT);
+        } else if (parts.length == 2) {
+            host.set(parts[0]);
+            port.set(Integer.parseInt(parts[1]));
+        } else {
+            LOGGER.warn("Invalid syslogHost setting: {}. Using default", syslogHost);
+            host.set("localhost");
+            port.set(SYSLOG_PORT);
+        }
+    }
+}
diff --git a/log4j-1.2-api/src/main/java/org/apache/log4j/builders/rewrite/RewritePolicyBuilder.java b/log4j-1.2-api/src/main/java/org/apache/log4j/builders/rewrite/RewritePolicyBuilder.java
new file mode 100644
index 0000000..5ca196a
--- /dev/null
+++ b/log4j-1.2-api/src/main/java/org/apache/log4j/builders/rewrite/RewritePolicyBuilder.java
@@ -0,0 +1,32 @@
+/*
+ * 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.rewrite;
+
+import org.apache.log4j.config.PropertiesConfiguration;
+import org.apache.log4j.rewrite.RewritePolicy;
+import org.apache.log4j.xml.XmlConfiguration;
+import org.w3c.dom.Element;
+
+/**
+ * Define a RewritePolicy Builder.
+ */
+public interface RewritePolicyBuilder {
+
+    RewritePolicy parseRewritePolicy(Element element, XmlConfiguration config);
+
+    RewritePolicy parseRewritePolicy(PropertiesConfiguration config);
+}
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
index 021afa3..9236006 100644
--- 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
@@ -16,6 +16,7 @@
  */
 package org.apache.log4j.config;
 
+import org.apache.log4j.builders.BuilderManager;
 import org.apache.logging.log4j.core.LoggerContext;
 import org.apache.logging.log4j.core.config.AbstractConfiguration;
 import org.apache.logging.log4j.core.config.Configuration;
@@ -35,12 +36,19 @@
 
     public static final String NULL = "null";
 
+    protected final BuilderManager manager;
+
     public Log4j1Configuration(final LoggerContext loggerContext, final ConfigurationSource source,
             int monitorIntervalSeconds) {
         super(loggerContext, source);
+        manager = new BuilderManager();
         initializeWatchers(this, source, monitorIntervalSeconds);
     }
 
+    public BuilderManager getBuilderManager() {
+        return manager;
+    }
+
     /**
      * Initialize the configuration.
      */
diff --git a/log4j-1.2-api/src/main/java/org/apache/log4j/config/PropertiesConfiguration.java b/log4j-1.2-api/src/main/java/org/apache/log4j/config/PropertiesConfiguration.java
index c3e995d..2f7ac58 100644
--- a/log4j-1.2-api/src/main/java/org/apache/log4j/config/PropertiesConfiguration.java
+++ b/log4j-1.2-api/src/main/java/org/apache/log4j/config/PropertiesConfiguration.java
@@ -66,8 +66,6 @@
 
     private final Map<String, Appender> registry;
 
-    private final BuilderManager manager;
-
     /**
      * No argument constructor.
      */
@@ -75,7 +73,6 @@
             int monitorIntervalSeconds) {
         super(loggerContext, source, monitorIntervalSeconds);
         registry = new HashMap<>();
-        manager = new BuilderManager();
     }
 
     public void doConfigure() {
diff --git a/log4j-1.2-api/src/main/java/org/apache/log4j/helpers/AppenderAttachableImpl.java b/log4j-1.2-api/src/main/java/org/apache/log4j/helpers/AppenderAttachableImpl.java
index fe9530d..e9e35f9 100644
--- a/log4j-1.2-api/src/main/java/org/apache/log4j/helpers/AppenderAttachableImpl.java
+++ b/log4j-1.2-api/src/main/java/org/apache/log4j/helpers/AppenderAttachableImpl.java
@@ -18,6 +18,7 @@
 
 import org.apache.log4j.Appender;
 import org.apache.log4j.spi.AppenderAttachable;
+import org.apache.log4j.spi.LoggingEvent;
 
 import java.util.Collections;
 import java.util.Enumeration;
@@ -67,4 +68,20 @@
     public void removeAppender(String name) {
         appenders.remove(name);
     }
+
+    /**
+     * Call the <code>doAppend</code> method on all attached appenders.
+     */
+    public int appendLoopOnAppenders(LoggingEvent event) {
+        for (Appender appender : appenders.values()) {
+            appender.doAppend(event);
+        }
+        return appenders.size();
+    }
+
+    public void close() {
+        for (Appender appender : appenders.values()) {
+            appender.close();
+        }
+    }
 }
diff --git a/log4j-1.2-api/src/main/java/org/apache/log4j/helpers/OptionConverter.java b/log4j-1.2-api/src/main/java/org/apache/log4j/helpers/OptionConverter.java
index e09e26b..5f910fa 100644
--- a/log4j-1.2-api/src/main/java/org/apache/log4j/helpers/OptionConverter.java
+++ b/log4j-1.2-api/src/main/java/org/apache/log4j/helpers/OptionConverter.java
@@ -20,6 +20,7 @@
 import org.apache.log4j.Level;
 import org.apache.logging.log4j.LogManager;
 import org.apache.logging.log4j.Logger;
+import org.apache.logging.log4j.spi.StandardLevel;
 import org.apache.logging.log4j.util.LoaderUtil;
 
 import java.io.InterruptedIOException;
@@ -358,6 +359,30 @@
         return org.apache.logging.log4j.Level.ALL;
     }
 
+    public static Level convertLevel(org.apache.logging.log4j.Level level) {
+        if (level == null) {
+            return Level.ERROR;
+        }
+        switch (level.getStandardLevel()) {
+            case FATAL:
+                return Level.FATAL;
+            case WARN:
+                return Level.WARN;
+            case INFO:
+                return Level.INFO;
+            case DEBUG:
+                return Level.DEBUG;
+            case TRACE:
+                return Level.TRACE;
+            case ALL:
+                return Level.ALL;
+            case OFF:
+                return Level.OFF;
+            default:
+                return Level.ERROR;
+        }
+    }
+
     /**
      * Find the value corresponding to <code>key</code> in
      * <code>props</code>. Then perform variable substitution on the
diff --git a/log4j-1.2-api/src/main/java/org/apache/log4j/rewrite/MapRewritePolicy.java b/log4j-1.2-api/src/main/java/org/apache/log4j/rewrite/MapRewritePolicy.java
new file mode 100644
index 0000000..4f53583
--- /dev/null
+++ b/log4j-1.2-api/src/main/java/org/apache/log4j/rewrite/MapRewritePolicy.java
@@ -0,0 +1,122 @@
+/*
+ * 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.rewrite;
+
+import org.apache.log4j.bridge.LogEventAdapter;
+import org.apache.log4j.helpers.OptionConverter;
+import org.apache.log4j.spi.LocationInfo;
+import org.apache.log4j.spi.LoggingEvent;
+import org.apache.logging.log4j.core.LogEvent;
+import org.apache.logging.log4j.core.impl.Log4jLogEvent;
+import org.apache.logging.log4j.message.MapMessage;
+import org.apache.logging.log4j.message.Message;
+import org.apache.logging.log4j.message.SimpleMessage;
+import org.apache.logging.log4j.util.SortedArrayStringMap;
+
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * This policy rewrites events where the message of the
+ * original event implements java.util.Map.
+ * All other events are passed through unmodified.
+ * If the map contains a "message" entry, the value will be
+ * used as the message for the rewritten event.  The rewritten
+ * event will have a property set that is the combination of the
+ * original property set and the other members of the message map.
+ * If both the original property set and the message map
+ * contain the same entry, the value from the message map
+ * will overwrite the original property set.
+ * <p>
+ * The combination of the RewriteAppender and this policy
+ * performs the same actions as the MapFilter from log4j 1.3.
+ */
+public class MapRewritePolicy implements RewritePolicy {
+    /**
+     * {@inheritDoc}
+     */
+    public LoggingEvent rewrite(final LoggingEvent source) {
+        Object msg = source.getMessage();
+        if (msg instanceof MapMessage || msg instanceof Map) {
+            Map<String, String> props = source.getProperties() != null ? new HashMap<>(source.getProperties())
+                    : new HashMap<>();
+            @SuppressWarnings("unchecked")
+            Map<String, Object> eventProps = msg instanceof Map ? (Map) msg : ((MapMessage) msg).getData();
+            //
+            //   if the map sent in the logging request
+            //      has "message" entry, use that as the message body
+            //      otherwise, use the entire map.
+            //
+            Message newMessage = null;
+            Object newMsg = eventProps.get("message");
+            if (newMsg != null) {
+                newMessage = new SimpleMessage(newMsg.toString());
+                for (Map.Entry<String, Object> entry : eventProps.entrySet()) {
+                    if (!("message".equals(entry.getKey()))) {
+                        props.put(entry.getKey(), entry.getValue().toString());
+                    }
+                }
+            } else {
+                return source;
+            }
+
+            LogEvent event;
+            if (source instanceof LogEventAdapter) {
+                event = new Log4jLogEvent.Builder(((LogEventAdapter) source).getEvent())
+                        .setMessage(newMessage)
+                        .setContextData(new SortedArrayStringMap(props))
+                        .build();
+            } else {
+                LocationInfo info = source.getLocationInformation();
+                StackTraceElement element = new StackTraceElement(info.getClassName(), info.getMethodName(),
+                        info.getFileName(), Integer.parseInt(info.getLineNumber()));
+                Thread thread = getThread(source.getThreadName());
+                long threadId = thread != null ? thread.getId() : 0;
+                int threadPriority = thread != null ? thread.getPriority() : 0;
+                event = Log4jLogEvent.newBuilder()
+                        .setContextData(new SortedArrayStringMap(props))
+                        .setLevel(OptionConverter.convertLevel(source.getLevel()))
+                        .setLoggerFqcn(source.getFQNOfLoggerClass())
+                        .setMarker(null)
+                        .setMessage(newMessage)
+                        .setSource(element)
+                        .setLoggerName(source.getLoggerName())
+                        .setThreadName(source.getThreadName())
+                        .setThreadId(threadId)
+                        .setThreadPriority(threadPriority)
+                        .setThrown(source.getThrowableInformation().getThrowable())
+                        .setTimeMillis(source.getTimeStamp())
+                        .setNanoTime(0)
+                        .setThrownProxy(null)
+                        .build();
+            }
+            return new LogEventAdapter(event);
+        } else {
+            return source;
+        }
+
+    }
+
+    private Thread getThread(String name) {
+        for (Thread thread : Thread.getAllStackTraces().keySet()) {
+            if (thread.getName().equals(name)) {
+                return thread;
+            }
+        }
+        return null;
+    }
+}
diff --git a/log4j-1.2-api/src/main/java/org/apache/log4j/rewrite/PropertyRewritePolicy.java b/log4j-1.2-api/src/main/java/org/apache/log4j/rewrite/PropertyRewritePolicy.java
new file mode 100644
index 0000000..b822a99
--- /dev/null
+++ b/log4j-1.2-api/src/main/java/org/apache/log4j/rewrite/PropertyRewritePolicy.java
@@ -0,0 +1,121 @@
+/*
+ * 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.rewrite;
+
+import org.apache.log4j.bridge.LogEventAdapter;
+import org.apache.log4j.helpers.OptionConverter;
+import org.apache.log4j.spi.LocationInfo;
+import org.apache.log4j.spi.LoggingEvent;
+import org.apache.logging.log4j.core.LogEvent;
+import org.apache.logging.log4j.core.impl.Log4jLogEvent;
+import org.apache.logging.log4j.message.SimpleMessage;
+import org.apache.logging.log4j.util.SortedArrayStringMap;
+
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.StringTokenizer;
+
+/**
+ * This policy rewrites events by adding
+ * a user-specified list of properties to the event.
+ * Existing properties are not modified.
+ * <p>
+ * The combination of the RewriteAppender and this policy
+ * performs the same actions as the PropertyFilter from log4j 1.3.
+ */
+
+public class PropertyRewritePolicy implements RewritePolicy {
+    private Map<String, String> properties = Collections.EMPTY_MAP;
+
+    public PropertyRewritePolicy() {
+    }
+
+    /**
+     * Set a string representing the property name/value pairs.
+     * <p>
+     * Form: propname1=propvalue1,propname2=propvalue2
+     *
+     * @param props
+     */
+    public void setProperties(String props) {
+        Map hashTable = new HashMap();
+        StringTokenizer pairs = new StringTokenizer(props, ",");
+        while (pairs.hasMoreTokens()) {
+            StringTokenizer entry = new StringTokenizer(pairs.nextToken(), "=");
+            hashTable.put(entry.nextElement().toString().trim(), entry.nextElement().toString().trim());
+        }
+        synchronized (this) {
+            properties = hashTable;
+        }
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public LoggingEvent rewrite(final LoggingEvent source) {
+        if (!properties.isEmpty()) {
+            Map<String, String> rewriteProps = source.getProperties() != null ? new HashMap<>(source.getProperties())
+                    : new HashMap<>();
+            for (Map.Entry<String, String> entry : properties.entrySet()) {
+                if (!rewriteProps.containsKey(entry.getKey())) {
+                    rewriteProps.put(entry.getKey(), entry.getValue());
+                }
+            }
+            LogEvent event;
+            if (source instanceof LogEventAdapter) {
+                event = new Log4jLogEvent.Builder(((LogEventAdapter) source).getEvent())
+                        .setContextData(new SortedArrayStringMap(rewriteProps))
+                        .build();
+            } else {
+                LocationInfo info = source.getLocationInformation();
+                StackTraceElement element = new StackTraceElement(info.getClassName(), info.getMethodName(),
+                        info.getFileName(), Integer.parseInt(info.getLineNumber()));
+                Thread thread = getThread(source.getThreadName());
+                long threadId = thread != null ? thread.getId() : 0;
+                int threadPriority = thread != null ? thread.getPriority() : 0;
+                event = Log4jLogEvent.newBuilder()
+                        .setContextData(new SortedArrayStringMap(rewriteProps))
+                        .setLevel(OptionConverter.convertLevel(source.getLevel()))
+                        .setLoggerFqcn(source.getFQNOfLoggerClass())
+                        .setMarker(null)
+                        .setMessage(new SimpleMessage(source.getRenderedMessage()))
+                        .setSource(element)
+                        .setLoggerName(source.getLoggerName())
+                        .setThreadName(source.getThreadName())
+                        .setThreadId(threadId)
+                        .setThreadPriority(threadPriority)
+                        .setThrown(source.getThrowableInformation().getThrowable())
+                        .setTimeMillis(source.getTimeStamp())
+                        .setNanoTime(0)
+                        .setThrownProxy(null)
+                        .build();
+            }
+            return new LogEventAdapter(event);
+        }
+        return source;
+    }
+
+    private Thread getThread(String name) {
+        for (Thread thread : Thread.getAllStackTraces().keySet()) {
+            if (thread.getName().equals(name)) {
+                return thread;
+            }
+        }
+        return null;
+    }
+}
diff --git a/log4j-1.2-api/src/main/java/org/apache/log4j/rewrite/RewritePolicy.java b/log4j-1.2-api/src/main/java/org/apache/log4j/rewrite/RewritePolicy.java
new file mode 100644
index 0000000..9570218
--- /dev/null
+++ b/log4j-1.2-api/src/main/java/org/apache/log4j/rewrite/RewritePolicy.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.rewrite;
+
+import org.apache.log4j.spi.LoggingEvent;
+
+/**
+ * This interface is implemented to provide a rewrite
+ * strategy for RewriteAppender.  RewriteAppender will
+ * call the rewrite method with a source logging event.
+ * The strategy may return that event, create a new event
+ * or return null to suppress the logging request.
+ */
+public interface RewritePolicy {
+    /**
+     * Rewrite a logging event.
+     * @param source a logging event that may be returned or
+     * used to create a new logging event.
+     * @return a logging event or null to suppress processing.
+     */
+    LoggingEvent rewrite(final LoggingEvent source);
+}
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 e9f57de..d7755a2 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
@@ -20,6 +20,9 @@
 import org.apache.log4j.Level;
 import org.apache.log4j.bridge.LogEventAdapter;
 
+import java.util.Map;
+import java.util.Set;
+
 /**
  *  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.
@@ -49,6 +52,14 @@
         return null;
     }
 
+    public String getFQNOfLoggerClass() {
+        return null;
+    }
+
+    public final long getTimeStamp() {
+        return 0;
+    }
+
     /**
      * Gets the logger of the event.
      * Use should be restricted to cloning events.
@@ -128,4 +139,24 @@
         return null;
     }
 
+    public void setProperty(final String propName,
+            final String propValue) {
+
+    }
+
+    public String getProperty(final String key) {
+        return null;
+    }
+
+    public Set getPropertyKeySet() {
+        return null;
+    }
+
+    public Map getProperties() {
+        return null;
+    }
+
+    public Object removeProperty(String propName) {
+        return null;
+    }
 }
diff --git a/log4j-1.2-api/src/main/java/org/apache/log4j/xml/XmlConfiguration.java b/log4j-1.2-api/src/main/java/org/apache/log4j/xml/XmlConfiguration.java
index c96c26d..a37f140 100644
--- a/log4j-1.2-api/src/main/java/org/apache/log4j/xml/XmlConfiguration.java
+++ b/log4j-1.2-api/src/main/java/org/apache/log4j/xml/XmlConfiguration.java
@@ -21,10 +21,10 @@
 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.rewrite.RewritePolicy;
 import org.apache.log4j.spi.AppenderAttachable;
 import org.apache.log4j.spi.ErrorHandler;
 import org.apache.log4j.spi.Filter;
@@ -99,8 +99,6 @@
      */
     protected static final String DEFAULT_PREFIX = "log4j";
 
-    private final BuilderManager manager;
-
     // key: appenderName, value: appender
     private Map<String, Appender> appenderMap;
 
@@ -110,7 +108,6 @@
             int monitorIntervalSeconds) {
         super(loggerContext, source, monitorIntervalSeconds);
         appenderMap = new HashMap<>();
-        manager = new BuilderManager();
     }
 
     public void addAppenderIfAbsent(Appender appender) {
@@ -415,6 +412,45 @@
         return null;
     }
 
+
+    public RewritePolicy parseRewritePolicy(Element rewritePolicyElement) {
+
+        String className = subst(rewritePolicyElement.getAttribute(CLASS_ATTR));
+        LOGGER.debug("Class name: [" + className + ']');
+        RewritePolicy policy = manager.parseRewritePolicy(className, rewritePolicyElement, this);
+        if (policy == null) {
+            policy = buildRewritePolicy(className, rewritePolicyElement);
+        }
+        return policy;
+    }
+
+    private RewritePolicy buildRewritePolicy(String className, Element element) {
+        try {
+            RewritePolicy policy = LoaderUtil.newInstanceOf(className);
+            PropertySetter propSetter = new PropertySetter(policy);
+
+            forEachElement(element.getChildNodes(), (currentElement) -> {
+                if (currentElement.getTagName().equalsIgnoreCase(PARAM_TAG)) {
+                    setParameter(currentElement, propSetter);
+                }
+            });
+            propSetter.activate();
+            return policy;
+        } catch (ConsumerException ex) {
+            Throwable t = ex.getCause();
+            if (t instanceof InterruptedException || t instanceof InterruptedIOException) {
+                Thread.currentThread().interrupt();
+            }
+            LOGGER.error("Could not create an RewritePolicy. Reported error follows.", t);
+        } catch (Exception oops) {
+            if (oops instanceof InterruptedException || oops instanceof InterruptedIOException) {
+                Thread.currentThread().interrupt();
+            }
+            LOGGER.error("Could not create an RewritePolicy. Reported error follows.", oops);
+        }
+        return null;
+    }
+
     /**
      * Used internally to parse an {@link ErrorHandler} element.
      */
diff --git a/log4j-1.2-api/src/test/java/org/apache/log4j/config/MapRewriteAppenderTest.java b/log4j-1.2-api/src/test/java/org/apache/log4j/config/MapRewriteAppenderTest.java
new file mode 100644
index 0000000..840971a
--- /dev/null
+++ b/log4j-1.2-api/src/test/java/org/apache/log4j/config/MapRewriteAppenderTest.java
@@ -0,0 +1,79 @@
+/*
+ * 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.logging.log4j.ThreadContext;
+import org.apache.logging.log4j.core.Appender;
+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.junit.After;
+import org.junit.BeforeClass;
+import org.junit.Test;
+
+import java.util.HashMap;
+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 RewriteAppender
+ */
+public class MapRewriteAppenderTest {
+
+    @BeforeClass
+    public static void beforeClass() {
+        System.setProperty(ConfigurationFactory.LOG4J1_CONFIGURATION_FILE_PROPERTY, "target/test-classes/log4j1-mapRewrite.xml");
+    }
+
+    @After
+    public void after() {
+        ThreadContext.clearMap();
+    }
+
+    @Test
+    public void testRewrite() throws Exception {
+        Logger logger = LogManager.getLogger("test");
+        Map<String, String> map = new HashMap<>();
+        map.put("message", "This is a test");
+        map.put("hello", "world");
+        logger.debug(map);
+        LoggerContext context = (LoggerContext) org.apache.logging.log4j.LogManager.getContext(false);
+        Configuration configuration = context.getConfiguration();
+        Map<String, Appender> appenders = configuration.getAppenders();
+        ListAppender eventAppender = null;
+        for (Map.Entry<String, Appender> entry : appenders.entrySet()) {
+            if (entry.getKey().equals("events")) {
+                eventAppender = (ListAppender) ((AppenderAdapter.Adapter) entry.getValue()).getAppender();
+            }
+        }
+        assertNotNull("No Event Appender", eventAppender);
+        List<LoggingEvent> events = eventAppender.getEvents();
+        assertTrue("No events", events != null && events.size() > 0);
+        assertNotNull("No properties in the event", events.get(0).getProperties());
+        assertTrue("Key was not inserted", events.get(0).getProperties().containsKey("hello"));
+        assertEquals("Key value is incorrect", "world", events.get(0).getProperties().get("hello"));
+    }
+}
diff --git a/log4j-1.2-api/src/test/java/org/apache/log4j/config/RewriteAppenderTest.java b/log4j-1.2-api/src/test/java/org/apache/log4j/config/RewriteAppenderTest.java
new file mode 100644
index 0000000..d7aca5c
--- /dev/null
+++ b/log4j-1.2-api/src/test/java/org/apache/log4j/config/RewriteAppenderTest.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.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.logging.log4j.ThreadContext;
+import org.apache.logging.log4j.core.Appender;
+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.junit.After;
+import org.junit.BeforeClass;
+import org.junit.Test;
+
+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 RewriteAppender
+ */
+public class RewriteAppenderTest {
+
+    @BeforeClass
+    public static void beforeClass() {
+        System.setProperty(ConfigurationFactory.LOG4J1_CONFIGURATION_FILE_PROPERTY, "target/test-classes/log4j1-rewrite.xml");
+    }
+
+    @After
+    public void after() {
+        ThreadContext.clearMap();
+    }
+
+    @Test
+    public void testRewrite() throws Exception {
+        Logger logger = LogManager.getLogger("test");
+        ThreadContext.put("key1", "This is a test");
+        ThreadContext.put("hello", "world");
+        logger.debug("Say hello");
+        LoggerContext context = (LoggerContext) org.apache.logging.log4j.LogManager.getContext(false);
+        Configuration configuration = context.getConfiguration();
+        Map<String, Appender> appenders = configuration.getAppenders();
+        ListAppender eventAppender = null;
+        for (Map.Entry<String, Appender> entry : appenders.entrySet()) {
+            if (entry.getKey().equals("events")) {
+                eventAppender = (ListAppender) ((AppenderAdapter.Adapter) entry.getValue()).getAppender();
+            }
+        }
+        assertNotNull("No Event Appender", eventAppender);
+        List<LoggingEvent> events = eventAppender.getEvents();
+        assertTrue("No events", events != null && events.size() > 0);
+        assertNotNull("No properties in the event", events.get(0).getProperties());
+        assertTrue("Key was not inserted", events.get(0).getProperties().containsKey("key2"));
+        assertEquals("Key value is incorrect", "Log4j", events.get(0).getProperties().get("key2"));
+    }
+}
diff --git a/log4j-1.2-api/src/test/java/org/apache/log4j/config/SyslogAppenderTest.java b/log4j-1.2-api/src/test/java/org/apache/log4j/config/SyslogAppenderTest.java
new file mode 100644
index 0000000..7ccb463
--- /dev/null
+++ b/log4j-1.2-api/src/test/java/org/apache/log4j/config/SyslogAppenderTest.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.logging.log4j.LogManager;
+import org.apache.logging.log4j.Logger;
+import org.apache.logging.log4j.core.net.mock.MockSyslogServer;
+import org.apache.logging.log4j.core.net.mock.MockSyslogServerFactory;
+import org.junit.After;
+import org.junit.Before;
+import org.junit.BeforeClass;
+import org.junit.Test;
+
+import java.io.IOException;
+import java.util.List;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.assertNotNull;
+
+
+/**
+ * Class Description goes here.
+ */
+public class SyslogAppenderTest {
+
+    private static final int PORTNUM = 9999;
+    private MockSyslogServer syslogServer;
+
+    @BeforeClass
+    public static void beforeClass() {
+        System.setProperty("log4j.configuration", "target/test-classes/log4j1-syslog.xml");
+    }
+
+    @Before
+    public void setUp() {
+    }
+
+    @After
+    public void teardown() {
+        if (syslogServer != null) {
+            syslogServer.shutdown();
+        }
+    }
+
+    @Test
+    public void sendMessage() throws Exception {
+        initTCPTestEnvironment(null);
+        Logger logger = LogManager.getLogger(SyslogAppenderTest.class);
+        logger.info("This is a test");
+        List<String> messages = null;
+        for (int i = 0; i < 5; ++i) {
+            Thread.sleep(250);
+            messages = syslogServer.getMessageList();
+            if (messages != null && messages.size() > 0) {
+                break;
+            }
+        }
+        assertNotNull("No messages received", messages);
+        assertEquals("Sent message not detected", 1, messages.size());
+    }
+
+
+    protected void initTCPTestEnvironment(final String messageFormat) throws IOException {
+        syslogServer = MockSyslogServerFactory.createTCPSyslogServer(1, PORTNUM);
+        syslogServer.start();
+    }
+}
diff --git a/log4j-1.2-api/src/test/resources/log4j1-maprewrite.xml b/log4j-1.2-api/src/test/resources/log4j1-maprewrite.xml
new file mode 100644
index 0000000..19973f4
--- /dev/null
+++ b/log4j-1.2-api/src/test/resources/log4j1-maprewrite.xml
@@ -0,0 +1,35 @@
+<?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="events" class="org.apache.log4j.ListAppender">
+  </appender>
+
+  <appender name="rewrite" class="org.apache.log4j.rewrite.RewriteAppender">
+    <appender-ref ref="events"/>
+    <rewritePolicy class="org.apache.log4j.rewrite.MapRewritePolicy">
+    </rewritePolicy>
+  </appender>
+
+  <root>
+    <priority value ="trace" />
+    <appender-ref ref="rewrite" />
+  </root>
+
+</log4j:configuration>
\ No newline at end of file
diff --git a/log4j-1.2-api/src/test/resources/log4j1-rewrite.xml b/log4j-1.2-api/src/test/resources/log4j1-rewrite.xml
new file mode 100644
index 0000000..25eb1b5
--- /dev/null
+++ b/log4j-1.2-api/src/test/resources/log4j1-rewrite.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="events" class="org.apache.log4j.ListAppender">
+  </appender>
+
+  <appender name="rewrite" class="org.apache.log4j.rewrite.RewriteAppender">
+    <appender-ref ref="events"/>
+    <rewritePolicy class="org.apache.log4j.rewrite.PropertyRewritePolicy">
+      <param name="properties" value="key2=Log4j"/>
+    </rewritePolicy>
+  </appender>
+
+  <root>
+    <priority value ="trace" />
+    <appender-ref ref="rewrite" />
+  </root>
+
+</log4j:configuration>
\ No newline at end of file
diff --git a/log4j-1.2-api/src/test/resources/log4j1-syslog.xml b/log4j-1.2-api/src/test/resources/log4j1-syslog.xml
new file mode 100644
index 0000000..4fd96a8
--- /dev/null
+++ b/log4j-1.2-api/src/test/resources/log4j1-syslog.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="syslog" class="org.apache.log4j.net.SyslogAppender">
+    <param name="SyslogHost" value="localhost:9999"/>
+    <param name="Facility" value="USER"/>
+    <param name="FacilityPrinting" value="true"/>
+    <param name="Threshold" value="DEBUG"/>
+    <layout class="org.apache.log4j.PatternLayout">
+      <param name="ConversionPattern" value="%d{yyyy-MM-dd HH:mm:ss} %-5p %c{1}:%L - %m%n" />
+    </layout>
+  </appender>
+
+  <root>
+    <priority value ="trace" />
+    <appender-ref ref="syslog" />
+  </root>
+
+</log4j:configuration>
\ No newline at end of file
diff --git a/src/changes/changes.xml b/src/changes/changes.xml
index 7871c47..81dae7d 100644
--- a/src/changes/changes.xml
+++ b/src/changes/changes.xml
@@ -157,6 +157,9 @@
       </action>
     </release>
     <release version="2.13.0" date="2019-MM-DD" description="GA Release 2.13.0">
+      <action issue="LOG4J2-63" dev="rgoers" type="add">
+        Add experimental support for Log4j 1 configuration files.
+      </action>
       <action issue="LOG4J2-2712" dev="rgoers" type="fix">
         The rolling file appenders would fail to compress the file after rollover if the file name matched the
         file pattern.
diff --git a/src/site/markdown/manual/compatibility.md b/src/site/markdown/manual/compatibility.md
index df0d728..69a1b30 100644
--- a/src/site/markdown/manual/compatibility.md
+++ b/src/site/markdown/manual/compatibility.md
@@ -28,17 +28,67 @@
 ## Configuration Compatibility
 
 Log4j 2 provides experimental support for Log4j 1 configuration files. Configuration of the Appenders, Layouts 
-and Filters that were provided in the Log4j 1 distribution will be redirected to their Log4j 2 counterparts. 
-This means that although the while the behavior of these components will be similar they may not be exactly the
-same. For example, the XML generated by the XMLLayout may not exactly match the XML generated by the Log4j 1
-XMLLayout. 
+and Filters that were provided in the Log4j 1 distribution will be redirected to their Log4j 2 counterparts - 
+with the exception of the implemented Rewrite Policies. This means that although the while the behavior of these 
+components will be similar they may not be exactly the same. For example, the XML generated by the XMLLayout may 
+not exactly match the XML generated by the Log4j 1XMLLayout. 
 
 In addition, Log4j 2 supports custom Log4j 1 Appenders, Filters, and Layouts with some constraints. Since the 
-original Log4j 1 components are not present in Log4j 2 custom components that extend them will fail. 
+original Log4j 1 components may not be present in Log4j 2, custom components that extend them will fail. 
 
 As support for Log4j 1 is an experimental feature one of the following steps must be taken to enable it:
 
 1. Set the system property "log4j1.compatibility" to a value of "true". Log4j 2 will then add log4j.properties,
 log4j-test.properties, log4j.xml and log4j-test.xml to the configuration files it searches for on the class path.
 1. Set the Log4j 1 system property "log4j.configuration" to the location of the log4j 1 configuration file. The 
-files must have a file extension of either ".properties" or ".xml".
\ No newline at end of file
+files must have a file extension of either ".properties" or ".xml".
+
+## Supported Components
+### Appenders
+
+* AsyncAppender
+* ConsoleAppender
+* DailyRollingFileAppender
+* FileAppender
+* NullAppender
+* RewriteAppender (limited)
+* RollingFileAppender
+* SyslogAppender
+
+## Filters
+
+* DenyAllFilter
+* LevelMatchFilter
+* LevelRangeFilter
+* StringMatchFilter
+
+## Layouts
+
+* HtmlLayout
+* PatternLayout
+* SimpleLayout
+* TTCCLayout
+* XmlLayout
+
+## Rewrite Policies
+
+* MapRewritePolicy
+* PropertyRewritePolicy
+
+## Unsupported or Unimplemented Components
+### Appenders
+
+* JDBCAppender (cannot be mapped to Log4j 2's JdbcAppender)
+* JMSAppender 
+* SMTPAppender
+* SocketAppender (Requires the use of the SerializedLayout which is a security risk)
+* SocketHubAppender (Requires the use of the SerializedLayout which is a securiy risk)
+* TelnetAppender (Security risk)
+
+## Rewrite Policies
+
+* ReflectionRewritePolicy
+* Custom rewrite policies since LoggingEvent is currently a no-op.
+
+### Renderers
+Log4j 2 currently will ignore renderers.
\ No newline at end of file