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

vy pushed a commit to branch recycler-api-3.x
in repository https://gitbox.apache.org/repos/asf/logging-log4j2.git

commit baf4a9bcb29b41422237495bcf7c1ab2db459520
Author: Volkan Yazıcı <[email protected]>
AuthorDate: Thu Mar 23 18:14:19 2023 +0100

    Merge changes from `main`
---
 .../logging/log4j/message/MapMessageTest.java      |   9 +
 .../apache/logging/log4j/util/StringBuilders.java  |  33 ++-
 .../log4j/core/filter/CompositeFilterTest.java     |  97 ++++---
 .../pattern/SimpleLiteralPatternConverterTest.java |  93 ++++---
 .../logging/log4j/core/appender/WriterManager.java | 298 ++++++++++-----------
 .../appender/rolling/action/DeletingVisitor.java   | 219 ++++++++-------
 .../core/appender/rolling/action/PathSorter.java   |  53 ++--
 .../rolling/action/PathWithAttributes.java         | 117 ++++----
 .../appender/rolling/action/SortingVisitor.java    | 149 +++++------
 .../core/async/AsyncLoggerConfigDelegate.java      | 131 +++++----
 .../async/BasicAsyncLoggerContextSelector.java     |  88 +++---
 .../log4j/core/config/ReliabilityStrategy.java     | 189 +++++++------
 .../DefaultComponentAndConfigurationBuilder.java   |  94 +++----
 .../log4j/core/selector/CoreContextSelectors.java  |  62 ++---
 .../log4j/core/util/CloseShieldOutputStream.java   | 118 ++++----
 .../logging/log4j/core/util/CloseShieldWriter.java |  92 +++----
 .../apache/logging/log4j/core/util/IOUtils.java    | 260 +++++++++---------
 .../log4j/jeromq/appender/JeroMqAppender.java      |   5 +
 .../log4j/jeromq/appender/JeroMqManager.java       |  16 +-
 .../log4j/jeromq/appender/JeroMqAppenderTest.java  |  21 +-
 .../log4j/layout/template/json/TestHelpers.java    |   9 +-
 .../template/json/resolver/MarkerResolverTest.java |  94 +++++++
 .../template/json/resolver/MarkerResolver.java     |  41 ++-
 .../internal/util/HierarchicalCollections.java     |   7 +-
 log4j-to-slf4j/pom.xml                             |   2 +
 pom.xml                                            |   9 +-
 .../.2.x.x/1232_log4j-to-sfl4j-2-OSGiMetadata.xml  |  29 ++
 src/changelog/.2.x.x/1366_fix_java_sql_date.xml    |  28 ++
 .../asciidoc/manual/json-template-layout.adoc.vm   |  54 ++++
 29 files changed, 1356 insertions(+), 1061 deletions(-)

diff --git 
a/log4j-api-test/src/test/java/org/apache/logging/log4j/message/MapMessageTest.java
 
b/log4j-api-test/src/test/java/org/apache/logging/log4j/message/MapMessageTest.java
index 106cce2c97..b4e67f3bc2 100644
--- 
a/log4j-api-test/src/test/java/org/apache/logging/log4j/message/MapMessageTest.java
+++ 
b/log4j-api-test/src/test/java/org/apache/logging/log4j/message/MapMessageTest.java
@@ -17,6 +17,7 @@
 package org.apache.logging.log4j.message;
 
 import java.math.BigDecimal;
+import java.sql.Date;
 import java.sql.Time;
 import java.util.Arrays;
 import java.util.Collections;
@@ -320,6 +321,14 @@ public class MapMessageTest {
                 message.getFormattedMessage(), "Incorrect time format");
     }
 
+    @Test
+    public void testDate() {
+        final Date date = new Date(System.currentTimeMillis());
+        final ObjectMapMessage message = new ObjectMapMessage().with("date", 
date);
+        assertEquals("date=\"" + date + "\"",
+                message.getFormattedMessage(), "Incorrect date format");
+    }
+
     private static final class FormattableTestType implements 
StringBuilderFormattable {
 
         @Override
diff --git 
a/log4j-api/src/main/java/org/apache/logging/log4j/util/StringBuilders.java 
b/log4j-api/src/main/java/org/apache/logging/log4j/util/StringBuilders.java
index d0c931d039..cc81a66105 100644
--- a/log4j-api/src/main/java/org/apache/logging/log4j/util/StringBuilders.java
+++ b/log4j-api/src/main/java/org/apache/logging/log4j/util/StringBuilders.java
@@ -16,8 +16,6 @@
  */
 package org.apache.logging.log4j.util;
 
-import java.lang.reflect.Array;
-import java.util.Collections;
 import java.util.Map.Entry;
 
 import static java.lang.Character.toLowerCase;
@@ -28,18 +26,28 @@ import static java.lang.Character.toLowerCase;
 @InternalApi
 public final class StringBuilders {
 
-    private static final Object timeClass;
+    private static final Object timeObj;
+    private static final Object dateObj;
 
     static {
+        Class<?> clazz;
         Object obj;
+        final long current = System.currentTimeMillis();
         try {
-            Class<?> clazz = Class.forName("java.sql.Time");
-            long current = System.currentTimeMillis();
+            clazz = Class.forName("java.sql.Time");
             obj = clazz.getDeclaredConstructor(Long.TYPE).newInstance(current);
         } catch(Exception ex) {
             obj = null;
         }
-        timeClass = obj;
+        timeObj = obj;
+
+        try {
+            clazz = Class.forName("java.sql.Date");
+            obj = clazz.getDeclaredConstructor(Long.TYPE).newInstance(current);
+        } catch(Exception ex) {
+            obj = null;
+        }
+        dateObj = obj;
     }
 
     private StringBuilders() {
@@ -135,7 +143,7 @@ public final class StringBuilders {
             stringBuilder.append(((Float) obj).floatValue());
         } else if (obj instanceof Byte) {
             stringBuilder.append(((Byte) obj).byteValue());
-        } else if (isTime(obj) || obj instanceof java.time.temporal.Temporal) {
+        } else if (isTime(obj) || isDate(obj) || obj instanceof 
java.time.temporal.Temporal) {
             stringBuilder.append(obj);
         } else {
             return false;
@@ -144,10 +152,17 @@ public final class StringBuilders {
     }
 
     /*
-        Check to see if obj is an instance of java.sql.time without requiring 
the java.sql module.
+        Check to see if obj is an instance of java.sql.Time without requiring 
the java.sql module.
      */
     private static boolean isTime(final Object obj) {
-        return obj.getClass().isInstance(timeClass);
+        return obj.getClass().isInstance(timeObj);
+    }
+
+    /*
+        Check to see if obj is an instance of java.sql.Date without requiring 
the java.sql module.
+    */
+    private static boolean isDate(final Object obj) {
+        return obj.getClass().isInstance(dateObj);
     }
 
     /**
diff --git 
a/log4j-core-test/src/test/java/org/apache/logging/log4j/core/filter/CompositeFilterTest.java
 
b/log4j-core-test/src/test/java/org/apache/logging/log4j/core/filter/CompositeFilterTest.java
index 0a5698b607..14d7f16d76 100644
--- 
a/log4j-core-test/src/test/java/org/apache/logging/log4j/core/filter/CompositeFilterTest.java
+++ 
b/log4j-core-test/src/test/java/org/apache/logging/log4j/core/filter/CompositeFilterTest.java
@@ -1,49 +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.logging.log4j.core.filter;
-
-import static org.junit.jupiter.api.Assertions.assertArrayEquals;
-import static org.junit.jupiter.api.Assertions.assertNotEquals;
-
-import org.apache.logging.log4j.core.Filter;
-import org.apache.logging.log4j.core.Filter.Result;
-import org.junit.jupiter.api.Test;
-
-public class CompositeFilterTest {
-
-    @Test
-    public void testConcatenation() {
-        final Filter a = 
DenyAllFilter.newBuilder().setOnMatch(Result.ACCEPT).build();
-        final Filter b = 
DenyAllFilter.newBuilder().setOnMatch(Result.NEUTRAL).build();
-        final Filter c = 
DenyAllFilter.newBuilder().setOnMatch(Result.DENY).build();
-        // The three values need to be distinguishable
-        assertNotEquals(a, b);
-        assertNotEquals(a,  c);
-        assertNotEquals(b, c);
-        final Filter[] expected = new Filter[] {a, b, c};
-        final CompositeFilter singleA = CompositeFilter.createFilters(new 
Filter[] {a});
-        final CompositeFilter singleB = CompositeFilter.createFilters(new 
Filter[] {b});
-        final CompositeFilter singleC = CompositeFilter.createFilters(new 
Filter[] {c});
-        // Concatenating one at a time
-        final CompositeFilter concat1 = singleA.addFilter(b).addFilter(c);
-        assertArrayEquals(expected, concat1.getFiltersArray());
-        // In reverse order
-        final CompositeFilter concat2 = 
singleA.addFilter(singleB.addFilter(singleC));
-        assertArrayEquals(expected, concat2.getFiltersArray());
-    }
-}
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache license, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.core.Filter;
+import org.apache.logging.log4j.core.Filter.Result;
+import org.junit.jupiter.api.Test;
+
+import static org.junit.jupiter.api.Assertions.assertArrayEquals;
+import static org.junit.jupiter.api.Assertions.assertNotEquals;
+
+public class CompositeFilterTest {
+
+    @Test
+    public void testConcatenation() {
+        final Filter a = 
DenyAllFilter.newBuilder().setOnMatch(Result.ACCEPT).build();
+        final Filter b = 
DenyAllFilter.newBuilder().setOnMatch(Result.NEUTRAL).build();
+        final Filter c = 
DenyAllFilter.newBuilder().setOnMatch(Result.DENY).build();
+        // The three values need to be distinguishable
+        assertNotEquals(a, b);
+        assertNotEquals(a,  c);
+        assertNotEquals(b, c);
+        final Filter[] expected = new Filter[] {a, b, c};
+        final CompositeFilter singleA = CompositeFilter.createFilters(new 
Filter[] {a});
+        final CompositeFilter singleB = CompositeFilter.createFilters(new 
Filter[] {b});
+        final CompositeFilter singleC = CompositeFilter.createFilters(new 
Filter[] {c});
+        // Concatenating one at a time
+        final CompositeFilter concat1 = singleA.addFilter(b).addFilter(c);
+        assertArrayEquals(expected, concat1.getFiltersArray());
+        // In reverse order
+        final CompositeFilter concat2 = 
singleA.addFilter(singleB.addFilter(singleC));
+        assertArrayEquals(expected, concat2.getFiltersArray());
+    }
+}
diff --git 
a/log4j-core-test/src/test/java/org/apache/logging/log4j/core/pattern/SimpleLiteralPatternConverterTest.java
 
b/log4j-core-test/src/test/java/org/apache/logging/log4j/core/pattern/SimpleLiteralPatternConverterTest.java
index ba6b402447..4b46e42b32 100644
--- 
a/log4j-core-test/src/test/java/org/apache/logging/log4j/core/pattern/SimpleLiteralPatternConverterTest.java
+++ 
b/log4j-core-test/src/test/java/org/apache/logging/log4j/core/pattern/SimpleLiteralPatternConverterTest.java
@@ -1,47 +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.logging.log4j.core.pattern;
-
-import org.junit.jupiter.api.Test;
-
-import static org.junit.jupiter.api.Assertions.assertEquals;
-
-public class SimpleLiteralPatternConverterTest {
-
-    @Test
-    public void testConvertBackslashes() {
-        String literal = "ABC\\tDEF\\nGHI\\rJKL\\'MNO\\f \\b \\\\DROPPED:\\x";
-        LogEventPatternConverter converter = 
SimpleLiteralPatternConverter.of(literal, true);
-        String actual = literal(converter);
-        assertEquals("ABC\tDEF\nGHI\rJKL\'MNO\f \b \\DROPPED:x", actual);
-    }
-
-    @Test
-    public void testDontConvertBackslashes() {
-        String literal = "ABC\\tDEF\\nGHI\\rJKL\\'MNO\\f \\b \\\\DROPPED:\\x";
-        LogEventPatternConverter converter = 
SimpleLiteralPatternConverter.of(literal, false);
-        String actual = literal(converter);
-        assertEquals(literal, actual);
-    }
-
-    private static String literal(LogEventPatternConverter converter) {
-        StringBuilder buffer = new StringBuilder();
-        converter.format(null, buffer);
-        return buffer.toString();
-    }
-}
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache license, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.pattern;
+
+import org.junit.jupiter.api.Test;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+
+public class SimpleLiteralPatternConverterTest {
+
+    @Test
+    public void testConvertBackslashes() {
+        String literal = "ABC\\tDEF\\nGHI\\rJKL\\'MNO\\f \\b \\\\DROPPED:\\x";
+        LogEventPatternConverter converter = 
SimpleLiteralPatternConverter.of(literal, true);
+        String actual = literal(converter);
+        assertEquals("ABC\tDEF\nGHI\rJKL\'MNO\f \b \\DROPPED:x", actual);
+    }
+
+    @Test
+    public void testDontConvertBackslashes() {
+        String literal = "ABC\\tDEF\\nGHI\\rJKL\\'MNO\\f \\b \\\\DROPPED:\\x";
+        LogEventPatternConverter converter = 
SimpleLiteralPatternConverter.of(literal, false);
+        String actual = literal(converter);
+        assertEquals(literal, actual);
+    }
+
+    private static String literal(LogEventPatternConverter converter) {
+        StringBuilder buffer = new StringBuilder();
+        converter.format(null, buffer);
+        return buffer.toString();
+    }
+}
diff --git 
a/log4j-core/src/main/java/org/apache/logging/log4j/core/appender/WriterManager.java
 
b/log4j-core/src/main/java/org/apache/logging/log4j/core/appender/WriterManager.java
index bf76e47b25..32c024fb0f 100644
--- 
a/log4j-core/src/main/java/org/apache/logging/log4j/core/appender/WriterManager.java
+++ 
b/log4j-core/src/main/java/org/apache/logging/log4j/core/appender/WriterManager.java
@@ -1,149 +1,149 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements. See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache license, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License. You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT 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.appender;
-
-import java.io.IOException;
-import java.io.Writer;
-import java.util.concurrent.TimeUnit;
-
-import org.apache.logging.log4j.core.StringLayout;
-
-/**
- * Manages a Writer so that it can be shared by multiple Appenders and will
- * allow appenders to reconfigure without requiring a new writer.
- */
-public class WriterManager extends AbstractManager {
-
-    /**
-     * Creates a Manager.
-     *
-     * @param name The name of the stream to manage.
-     * @param data The data to pass to the Manager.
-     * @param factory The factory to use to create the Manager.
-     * @param <T> The type of the WriterManager.
-     * @return A WriterManager.
-     */
-    public static <T> WriterManager getManager(final String name, final T data,
-                                                 final ManagerFactory<? 
extends WriterManager, T> factory) {
-        return AbstractManager.getManager(name, factory, data);
-    }
-    protected final StringLayout layout;
-
-    private volatile Writer writer;
-
-    public WriterManager(final Writer writer, final String streamName, final 
StringLayout layout,
-            final boolean writeHeader) {
-        super(null, streamName);
-        this.writer = writer;
-        this.layout = layout;
-        if (writeHeader && layout != null) {
-            final byte[] header = layout.getHeader();
-            if (header != null) {
-                try {
-                    this.writer.write(new String(header, layout.getCharset()));
-                } catch (final IOException e) {
-                    logError("Unable to write header", e);
-                }
-            }
-        }
-    }
-
-    protected synchronized void closeWriter() {
-        final Writer w = writer; // access volatile field only once per method
-        try {
-            w.close();
-        } catch (final IOException ex) {
-            logError("Unable to close stream", ex);
-        }
-    }
-
-    /**
-     * Flushes any buffers.
-     */
-    public synchronized void flush() {
-        try {
-            writer.flush();
-        } catch (final IOException ex) {
-            final String msg = "Error flushing stream " + getName();
-            throw new AppenderLoggingException(msg, ex);
-        }
-    }
-
-    protected Writer getWriter() {
-        return writer;
-    }
-
-    /**
-     * Returns the status of the stream.
-     * @return true if the stream is open, false if it is not.
-     */
-    public boolean isOpen() {
-        return getCount() > 0;
-    }
-
-    /**
-     * Default hook to write footer during close.
-     */
-    @Override
-    public boolean releaseSub(final long timeout, final TimeUnit timeUnit) {
-        writeFooter();
-        closeWriter();
-        return true;
-    }
-
-    protected void setWriter(final Writer writer) {
-        final byte[] header = layout.getHeader();
-        if (header != null) {
-            try {
-                writer.write(new String(header, layout.getCharset()));
-                this.writer = writer; // only update field if writer.write() 
succeeded
-            } catch (final IOException ioe) {
-                logError("Unable to write header", ioe);
-            }
-        } else {
-            this.writer = writer;
-        }
-    }
-
-    /**
-     * Some output streams synchronize writes while others do not. 
Synchronizing here insures that
-     * log events won't be intertwined.
-     * @param str the string to write
-     * @throws AppenderLoggingException if an error occurs.
-     */
-    protected synchronized void write(final String str)  {
-        try {
-            writer.write(str);
-        } catch (final IOException ex) {
-            final String msg = "Error writing to stream " + getName();
-            throw new AppenderLoggingException(msg, ex);
-        }
-    }
-
-    /**
-     * Writes the footer.
-     */
-    protected void writeFooter() {
-        if (layout == null) {
-            return;
-        }
-        final byte[] footer = layout.getFooter();
-        if (footer != null && footer.length > 0) {
-            write(new String(footer, layout.getCharset()));
-        }
-    }
-}
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache license, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.appender;
+
+import java.io.IOException;
+import java.io.Writer;
+import java.util.concurrent.TimeUnit;
+
+import org.apache.logging.log4j.core.StringLayout;
+
+/**
+ * Manages a Writer so that it can be shared by multiple Appenders and will
+ * allow appenders to reconfigure without requiring a new writer.
+ */
+public class WriterManager extends AbstractManager {
+
+    /**
+     * Creates a Manager.
+     *
+     * @param name The name of the stream to manage.
+     * @param data The data to pass to the Manager.
+     * @param factory The factory to use to create the Manager.
+     * @param <T> The type of the WriterManager.
+     * @return A WriterManager.
+     */
+    public static <T> WriterManager getManager(final String name, final T data,
+                                                 final ManagerFactory<? 
extends WriterManager, T> factory) {
+        return AbstractManager.getManager(name, factory, data);
+    }
+    protected final StringLayout layout;
+
+    private volatile Writer writer;
+
+    public WriterManager(final Writer writer, final String streamName, final 
StringLayout layout,
+            final boolean writeHeader) {
+        super(null, streamName);
+        this.writer = writer;
+        this.layout = layout;
+        if (writeHeader && layout != null) {
+            final byte[] header = layout.getHeader();
+            if (header != null) {
+                try {
+                    this.writer.write(new String(header, layout.getCharset()));
+                } catch (final IOException e) {
+                    logError("Unable to write header", e);
+                }
+            }
+        }
+    }
+
+    protected synchronized void closeWriter() {
+        final Writer w = writer; // access volatile field only once per method
+        try {
+            w.close();
+        } catch (final IOException ex) {
+            logError("Unable to close stream", ex);
+        }
+    }
+
+    /**
+     * Flushes any buffers.
+     */
+    public synchronized void flush() {
+        try {
+            writer.flush();
+        } catch (final IOException ex) {
+            final String msg = "Error flushing stream " + getName();
+            throw new AppenderLoggingException(msg, ex);
+        }
+    }
+
+    protected Writer getWriter() {
+        return writer;
+    }
+
+    /**
+     * Returns the status of the stream.
+     * @return true if the stream is open, false if it is not.
+     */
+    public boolean isOpen() {
+        return getCount() > 0;
+    }
+
+    /**
+     * Default hook to write footer during close.
+     */
+    @Override
+    public boolean releaseSub(final long timeout, final TimeUnit timeUnit) {
+        writeFooter();
+        closeWriter();
+        return true;
+    }
+
+    protected void setWriter(final Writer writer) {
+        final byte[] header = layout.getHeader();
+        if (header != null) {
+            try {
+                writer.write(new String(header, layout.getCharset()));
+                this.writer = writer; // only update field if writer.write() 
succeeded
+            } catch (final IOException ioe) {
+                logError("Unable to write header", ioe);
+            }
+        } else {
+            this.writer = writer;
+        }
+    }
+
+    /**
+     * Some output streams synchronize writes while others do not. 
Synchronizing here insures that
+     * log events won't be intertwined.
+     * @param str the string to write
+     * @throws AppenderLoggingException if an error occurs.
+     */
+    protected synchronized void write(final String str)  {
+        try {
+            writer.write(str);
+        } catch (final IOException ex) {
+            final String msg = "Error writing to stream " + getName();
+            throw new AppenderLoggingException(msg, ex);
+        }
+    }
+
+    /**
+     * Writes the footer.
+     */
+    protected void writeFooter() {
+        if (layout == null) {
+            return;
+        }
+        final byte[] footer = layout.getFooter();
+        if (footer != null && footer.length > 0) {
+            write(new String(footer, layout.getCharset()));
+        }
+    }
+}
diff --git 
a/log4j-core/src/main/java/org/apache/logging/log4j/core/appender/rolling/action/DeletingVisitor.java
 
b/log4j-core/src/main/java/org/apache/logging/log4j/core/appender/rolling/action/DeletingVisitor.java
index 69a59a7d8c..e433731cf2 100644
--- 
a/log4j-core/src/main/java/org/apache/logging/log4j/core/appender/rolling/action/DeletingVisitor.java
+++ 
b/log4j-core/src/main/java/org/apache/logging/log4j/core/appender/rolling/action/DeletingVisitor.java
@@ -1,110 +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.logging.log4j.core.appender.rolling.action;
-
-import java.io.IOException;
-import java.nio.file.FileVisitResult;
-import java.nio.file.Files;
-import java.nio.file.NoSuchFileException;
-import java.nio.file.Path;
-import java.nio.file.SimpleFileVisitor;
-import java.nio.file.attribute.BasicFileAttributes;
-import java.util.List;
-import java.util.Objects;
-
-import org.apache.logging.log4j.Logger;
-import org.apache.logging.log4j.status.StatusLogger;
-
-/**
- * FileVisitor that deletes files that are accepted by all PathFilters. 
Directories are ignored.
- */
-public class DeletingVisitor extends SimpleFileVisitor<Path> {
-    private static final Logger LOGGER = StatusLogger.getLogger();
-
-    private final Path basePath;
-    private final boolean testMode;
-    private final List<? extends PathCondition> pathConditions;
-
-    /**
-     * Constructs a new DeletingVisitor.
-     *
-     * @param basePath used to relativize paths
-     * @param pathConditions objects that need to confirm whether a file can 
be deleted
-     * @param testMode if true, files are not deleted but instead a message is 
printed to the <a
-     *            
href="http://logging.apache.org/log4j/2.x/manual/configuration.html#StatusMessages";>status
 logger</a>
-     *            at INFO level. Users can use this to do a dry run to test if 
their configuration works as expected.
-     */
-    public DeletingVisitor(final Path basePath, final List<? extends 
PathCondition> pathConditions,
-            final boolean testMode) {
-        this.testMode = testMode;
-        this.basePath = Objects.requireNonNull(basePath, "basePath");
-        this.pathConditions = Objects.requireNonNull(pathConditions, 
"pathConditions");
-        for (final PathCondition condition : pathConditions) {
-            condition.beforeFileTreeWalk();
-        }
-    }
-
-    @Override
-    public FileVisitResult visitFile(final Path file, final 
BasicFileAttributes attrs) throws IOException {
-        for (final PathCondition pathFilter : pathConditions) {
-            final Path relative = basePath.relativize(file);
-            if (!pathFilter.accept(basePath, relative, attrs)) {
-                LOGGER.trace("Not deleting base={}, relative={}", basePath, 
relative);
-                return FileVisitResult.CONTINUE;
-            }
-        }
-        if (isTestMode()) {
-            LOGGER.info("Deleting {} (TEST MODE: file not actually deleted)", 
file);
-        } else {
-            delete(file);
-        }
-        return FileVisitResult.CONTINUE;
-    }
-
-    @Override
-    public FileVisitResult visitFileFailed(final Path file, final IOException 
ioException) throws IOException {
-        // LOG4J2-2677: Appenders may rollover and purge in parallel. 
SimpleVisitor rethrows exceptions from
-        // failed attempts to load file attributes.
-        if (ioException instanceof NoSuchFileException) {
-            LOGGER.info("File {} could not be accessed, it has likely already 
been deleted", file, ioException);
-            return FileVisitResult.CONTINUE;
-        } else {
-            return super.visitFileFailed(file, ioException);
-        }
-    }
-
-    /**
-     * Deletes the specified file.
-     *
-     * @param file the file to delete
-     * @throws IOException if a problem occurred deleting the file
-     */
-    protected void delete(final Path file) throws IOException {
-        LOGGER.trace("Deleting {}", file);
-        Files.deleteIfExists(file);
-    }
-
-    /**
-     * Returns {@code true} if files are not deleted even when all conditions 
accept a path, {@code false} otherwise.
-     *
-     * @return {@code true} if files are not deleted even when all conditions 
accept a path, {@code false} otherwise
-     */
-    public boolean isTestMode() {
-        return testMode;
-    }
-}
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache license, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.appender.rolling.action;
+
+import java.io.IOException;
+import java.nio.file.FileVisitResult;
+import java.nio.file.Files;
+import java.nio.file.NoSuchFileException;
+import java.nio.file.Path;
+import java.nio.file.SimpleFileVisitor;
+import java.nio.file.attribute.BasicFileAttributes;
+import java.util.List;
+import java.util.Objects;
+
+import org.apache.logging.log4j.Logger;
+import org.apache.logging.log4j.status.StatusLogger;
+
+/**
+ * FileVisitor that deletes files that are accepted by all PathFilters. 
Directories are ignored.
+ */
+public class DeletingVisitor extends SimpleFileVisitor<Path> {
+    private static final Logger LOGGER = StatusLogger.getLogger();
+
+    private final Path basePath;
+    private final boolean testMode;
+    private final List<? extends PathCondition> pathConditions;
+
+    /**
+     * Constructs a new DeletingVisitor.
+     *
+     * @param basePath used to relativize paths
+     * @param pathConditions objects that need to confirm whether a file can 
be deleted
+     * @param testMode if true, files are not deleted but instead a message is 
printed to the <a
+     *            
href="http://logging.apache.org/log4j/2.x/manual/configuration.html#StatusMessages";>status
 logger</a>
+     *            at INFO level. Users can use this to do a dry run to test if 
their configuration works as expected.
+     */
+    public DeletingVisitor(final Path basePath, final List<? extends 
PathCondition> pathConditions,
+            final boolean testMode) {
+        this.testMode = testMode;
+        this.basePath = Objects.requireNonNull(basePath, "basePath");
+        this.pathConditions = Objects.requireNonNull(pathConditions, 
"pathConditions");
+        for (final PathCondition condition : pathConditions) {
+            condition.beforeFileTreeWalk();
+        }
+    }
+
+    @Override
+    public FileVisitResult visitFile(final Path file, final 
BasicFileAttributes attrs) throws IOException {
+        for (final PathCondition pathFilter : pathConditions) {
+            final Path relative = basePath.relativize(file);
+            if (!pathFilter.accept(basePath, relative, attrs)) {
+                LOGGER.trace("Not deleting base={}, relative={}", basePath, 
relative);
+                return FileVisitResult.CONTINUE;
+            }
+        }
+        if (isTestMode()) {
+            LOGGER.info("Deleting {} (TEST MODE: file not actually deleted)", 
file);
+        } else {
+            delete(file);
+        }
+        return FileVisitResult.CONTINUE;
+    }
+
+    @Override
+    public FileVisitResult visitFileFailed(final Path file, final IOException 
ioException) throws IOException {
+        // LOG4J2-2677: Appenders may rollover and purge in parallel. 
SimpleVisitor rethrows exceptions from
+        // failed attempts to load file attributes.
+        if (ioException instanceof NoSuchFileException) {
+            LOGGER.info("File {} could not be accessed, it has likely already 
been deleted", file, ioException);
+            return FileVisitResult.CONTINUE;
+        } else {
+            return super.visitFileFailed(file, ioException);
+        }
+    }
+
+    /**
+     * Deletes the specified file.
+     *
+     * @param file the file to delete
+     * @throws IOException if a problem occurred deleting the file
+     */
+    protected void delete(final Path file) throws IOException {
+        LOGGER.trace("Deleting {}", file);
+        Files.deleteIfExists(file);
+    }
+
+    /**
+     * Returns {@code true} if files are not deleted even when all conditions 
accept a path, {@code false} otherwise.
+     *
+     * @return {@code true} if files are not deleted even when all conditions 
accept a path, {@code false} otherwise
+     */
+    public boolean isTestMode() {
+        return testMode;
+    }
+}
diff --git 
a/log4j-core/src/main/java/org/apache/logging/log4j/core/appender/rolling/action/PathSorter.java
 
b/log4j-core/src/main/java/org/apache/logging/log4j/core/appender/rolling/action/PathSorter.java
index 2e765ab0b8..d8ea3c2d22 100644
--- 
a/log4j-core/src/main/java/org/apache/logging/log4j/core/appender/rolling/action/PathSorter.java
+++ 
b/log4j-core/src/main/java/org/apache/logging/log4j/core/appender/rolling/action/PathSorter.java
@@ -1,27 +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.logging.log4j.core.appender.rolling.action;
-
-import java.util.Comparator;
-
-/**
- * Defines the interface of classes that can sort Paths.
- */
-public interface PathSorter extends Comparator<PathWithAttributes>{
-
-}
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache license, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.appender.rolling.action;
+
+import java.util.Comparator;
+
+/**
+ * Defines the interface of classes that can sort Paths.
+ */
+public interface PathSorter extends Comparator<PathWithAttributes>{
+
+}
diff --git 
a/log4j-core/src/main/java/org/apache/logging/log4j/core/appender/rolling/action/PathWithAttributes.java
 
b/log4j-core/src/main/java/org/apache/logging/log4j/core/appender/rolling/action/PathWithAttributes.java
index d69835427b..674e784269 100644
--- 
a/log4j-core/src/main/java/org/apache/logging/log4j/core/appender/rolling/action/PathWithAttributes.java
+++ 
b/log4j-core/src/main/java/org/apache/logging/log4j/core/appender/rolling/action/PathWithAttributes.java
@@ -1,59 +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.logging.log4j.core.appender.rolling.action;
-
-import java.nio.file.Path;
-import java.nio.file.attribute.BasicFileAttributes;
-import java.util.Objects;
-
-/**
- * Tuple of a {@code Path} and {@code BasicFileAttributes}, used for sorting.
- */
-public class PathWithAttributes {
-
-    private final Path path;
-    private final BasicFileAttributes attributes;
-
-    public PathWithAttributes(final Path path, final BasicFileAttributes 
attributes) {
-        this.path = Objects.requireNonNull(path, "path");
-        this.attributes = Objects.requireNonNull(attributes, "attributes");
-    }
-
-    @Override
-    public String toString() {
-        return path + " (modified: " + attributes.lastModifiedTime() + ")";
-    }
-
-    /**
-     * Returns the path.
-     *
-     * @return the path
-     */
-    public Path getPath() {
-        return path;
-    }
-
-    /**
-     * Returns the attributes.
-     *
-     * @return the attributes
-     */
-    public BasicFileAttributes getAttributes() {
-        return attributes;
-    }
-}
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache license, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.appender.rolling.action;
+
+import java.nio.file.Path;
+import java.nio.file.attribute.BasicFileAttributes;
+import java.util.Objects;
+
+/**
+ * Tuple of a {@code Path} and {@code BasicFileAttributes}, used for sorting.
+ */
+public class PathWithAttributes {
+
+    private final Path path;
+    private final BasicFileAttributes attributes;
+
+    public PathWithAttributes(final Path path, final BasicFileAttributes 
attributes) {
+        this.path = Objects.requireNonNull(path, "path");
+        this.attributes = Objects.requireNonNull(attributes, "attributes");
+    }
+
+    @Override
+    public String toString() {
+        return path + " (modified: " + attributes.lastModifiedTime() + ")";
+    }
+
+    /**
+     * Returns the path.
+     *
+     * @return the path
+     */
+    public Path getPath() {
+        return path;
+    }
+
+    /**
+     * Returns the attributes.
+     *
+     * @return the attributes
+     */
+    public BasicFileAttributes getAttributes() {
+        return attributes;
+    }
+}
diff --git 
a/log4j-core/src/main/java/org/apache/logging/log4j/core/appender/rolling/action/SortingVisitor.java
 
b/log4j-core/src/main/java/org/apache/logging/log4j/core/appender/rolling/action/SortingVisitor.java
index ba7e00b601..c7ba94ec61 100644
--- 
a/log4j-core/src/main/java/org/apache/logging/log4j/core/appender/rolling/action/SortingVisitor.java
+++ 
b/log4j-core/src/main/java/org/apache/logging/log4j/core/appender/rolling/action/SortingVisitor.java
@@ -1,75 +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.logging.log4j.core.appender.rolling.action;
-
-import org.apache.logging.log4j.Logger;
-import org.apache.logging.log4j.status.StatusLogger;
-
-import java.io.IOException;
-import java.nio.file.FileVisitResult;
-import java.nio.file.NoSuchFileException;
-import java.nio.file.Path;
-import java.nio.file.SimpleFileVisitor;
-import java.nio.file.attribute.BasicFileAttributes;
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.List;
-import java.util.Objects;
-
-/**
- * FileVisitor that sorts files.
- */
-public class SortingVisitor extends SimpleFileVisitor<Path> {
-
-    private static final Logger LOGGER = StatusLogger.getLogger();
-    private final PathSorter sorter;
-    private final List<PathWithAttributes> collected = new ArrayList<>();
-
-    /**
-     * Constructs a new DeletingVisitor.
-     *
-     * @param basePath used to relativize paths
-     * @param pathFilters objects that need to confirm whether a file can be 
deleted
-     */
-    public SortingVisitor(final PathSorter sorter) {
-        this.sorter = Objects.requireNonNull(sorter, "sorter");
-    }
-
-    @Override
-    public FileVisitResult visitFile(final Path path, final 
BasicFileAttributes attrs) throws IOException {
-        collected.add(new PathWithAttributes(path, attrs));
-        return FileVisitResult.CONTINUE;
-    }
-
-    @Override
-    public FileVisitResult visitFileFailed(final Path file, final IOException 
ioException) throws IOException {
-        // LOG4J2-2677: Appenders may rollover and purge in parallel. 
SimpleVisitor rethrows exceptions from
-        // failed attempts to load file attributes.
-        if (ioException instanceof NoSuchFileException) {
-            LOGGER.info("File {} could not be accessed, it has likely already 
been deleted", file, ioException);
-            return FileVisitResult.CONTINUE;
-        } else {
-            return super.visitFileFailed(file, ioException);
-        }
-    }
-
-    public List<PathWithAttributes> getSortedPaths() {
-        Collections.sort(collected, sorter);
-        return collected;
-    }
-}
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache license, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.appender.rolling.action;
+
+import java.io.IOException;
+import java.nio.file.FileVisitResult;
+import java.nio.file.NoSuchFileException;
+import java.nio.file.Path;
+import java.nio.file.SimpleFileVisitor;
+import java.nio.file.attribute.BasicFileAttributes;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+import java.util.Objects;
+
+import org.apache.logging.log4j.Logger;
+import org.apache.logging.log4j.status.StatusLogger;
+
+/**
+ * FileVisitor that sorts files.
+ */
+public class SortingVisitor extends SimpleFileVisitor<Path> {
+
+    private static final Logger LOGGER = StatusLogger.getLogger();
+    private final PathSorter sorter;
+    private final List<PathWithAttributes> collected = new ArrayList<>();
+
+    /**
+     * Constructs a new DeletingVisitor.
+     *
+     * @param basePath used to relativize paths
+     * @param pathFilters objects that need to confirm whether a file can be 
deleted
+     */
+    public SortingVisitor(final PathSorter sorter) {
+        this.sorter = Objects.requireNonNull(sorter, "sorter");
+    }
+
+    @Override
+    public FileVisitResult visitFile(final Path path, final 
BasicFileAttributes attrs) throws IOException {
+        collected.add(new PathWithAttributes(path, attrs));
+        return FileVisitResult.CONTINUE;
+    }
+
+    @Override
+    public FileVisitResult visitFileFailed(final Path file, final IOException 
ioException) throws IOException {
+        // LOG4J2-2677: Appenders may rollover and purge in parallel. 
SimpleVisitor rethrows exceptions from
+        // failed attempts to load file attributes.
+        if (ioException instanceof NoSuchFileException) {
+            LOGGER.info("File {} could not be accessed, it has likely already 
been deleted", file, ioException);
+            return FileVisitResult.CONTINUE;
+        } else {
+            return super.visitFileFailed(file, ioException);
+        }
+    }
+
+    public List<PathWithAttributes> getSortedPaths() {
+        Collections.sort(collected, sorter);
+        return collected;
+    }
+}
diff --git 
a/log4j-core/src/main/java/org/apache/logging/log4j/core/async/AsyncLoggerConfigDelegate.java
 
b/log4j-core/src/main/java/org/apache/logging/log4j/core/async/AsyncLoggerConfigDelegate.java
index b791f0afe4..1cf0c8e66f 100644
--- 
a/log4j-core/src/main/java/org/apache/logging/log4j/core/async/AsyncLoggerConfigDelegate.java
+++ 
b/log4j-core/src/main/java/org/apache/logging/log4j/core/async/AsyncLoggerConfigDelegate.java
@@ -1,66 +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.logging.log4j.core.async;
-
-import org.apache.logging.log4j.Level;
-import org.apache.logging.log4j.core.LogEvent;
-import org.apache.logging.log4j.core.impl.LogEventFactory;
-import org.apache.logging.log4j.core.jmx.RingBufferAdmin;
-
-/**
- * Encapsulates the mechanism used to log asynchronously. There is one 
delegate per configuration, which is shared by
- * all AsyncLoggerConfig objects in the configuration.
- */
-public interface AsyncLoggerConfigDelegate {
-
-    /**
-     * Creates and returns a new {@code RingBufferAdmin} that instruments the 
ringbuffer of this
-     * {@code AsyncLoggerConfig}.
-     *
-     * @param contextName name of the {@code LoggerContext}
-     * @param loggerConfigName name of the logger config
-     * @return the RingBufferAdmin that instruments the ringbuffer
-     */
-    RingBufferAdmin createRingBufferAdmin(final String contextName, final 
String loggerConfigName);
-
-    /**
-     * Returns the {@code EventRoute} for the event with the specified level.
-     *
-     * @param level the level of the event to log
-     * @return the {@code EventRoute}
-     */
-    EventRoute getEventRoute(final Level level);
-
-    /**
-     * Enqueues the {@link LogEvent} on the mixed configuration ringbuffer.
-     * This method must only be used after {@link #tryEnqueue(LogEvent, 
AsyncLoggerConfig)} returns <code>false</code>
-     * indicating that the ringbuffer is full, otherwise it may incur 
unnecessary synchronization.
-     */
-    void enqueueEvent(LogEvent event, AsyncLoggerConfig asyncLoggerConfig);
-
-    boolean tryEnqueue(LogEvent event, AsyncLoggerConfig asyncLoggerConfig);
-
-    /**
-     * Notifies the delegate what LogEventFactory an AsyncLoggerConfig is 
using, so the delegate can determine
-     * whether to populate the ring buffer with mutable log events or not. 
This method may be invoked multiple times
-     * for all AsyncLoggerConfigs that use this delegate.
-     *
-     * @param logEventFactory the factory used
-     */
-    void setLogEventFactory(LogEventFactory logEventFactory);
-}
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache license, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.async;
+
+import org.apache.logging.log4j.Level;
+import org.apache.logging.log4j.core.LogEvent;
+import org.apache.logging.log4j.core.impl.LogEventFactory;
+import org.apache.logging.log4j.core.jmx.RingBufferAdmin;
+
+/**
+ * Encapsulates the mechanism used to log asynchronously. There is one 
delegate per configuration, which is shared by
+ * all AsyncLoggerConfig objects in the configuration.
+ */
+public interface AsyncLoggerConfigDelegate {
+
+    /**
+     * Creates and returns a new {@code RingBufferAdmin} that instruments the 
ringbuffer of this
+     * {@code AsyncLoggerConfig}.
+     *
+     * @param contextName name of the {@code LoggerContext}
+     * @param loggerConfigName name of the logger config
+     * @return the RingBufferAdmin that instruments the ringbuffer
+     */
+    RingBufferAdmin createRingBufferAdmin(final String contextName, final 
String loggerConfigName);
+
+    /**
+     * Returns the {@code EventRoute} for the event with the specified level.
+     *
+     * @param level the level of the event to log
+     * @return the {@code EventRoute}
+     */
+    EventRoute getEventRoute(final Level level);
+
+    /**
+     * Enqueues the {@link LogEvent} on the mixed configuration ringbuffer.
+     * This method must only be used after {@link #tryEnqueue(LogEvent, 
AsyncLoggerConfig)} returns <code>false</code>
+     * indicating that the ringbuffer is full, otherwise it may incur 
unnecessary synchronization.
+     */
+    void enqueueEvent(LogEvent event, AsyncLoggerConfig asyncLoggerConfig);
+
+    boolean tryEnqueue(LogEvent event, AsyncLoggerConfig asyncLoggerConfig);
+
+    /**
+     * Notifies the delegate what LogEventFactory an AsyncLoggerConfig is 
using, so the delegate can determine
+     * whether to populate the ring buffer with mutable log events or not. 
This method may be invoked multiple times
+     * for all AsyncLoggerConfigs that use this delegate.
+     *
+     * @param logEventFactory the factory used
+     */
+    void setLogEventFactory(LogEventFactory logEventFactory);
+}
diff --git 
a/log4j-core/src/main/java/org/apache/logging/log4j/core/async/BasicAsyncLoggerContextSelector.java
 
b/log4j-core/src/main/java/org/apache/logging/log4j/core/async/BasicAsyncLoggerContextSelector.java
index 9658f0302f..70f90ca3b6 100644
--- 
a/log4j-core/src/main/java/org/apache/logging/log4j/core/async/BasicAsyncLoggerContextSelector.java
+++ 
b/log4j-core/src/main/java/org/apache/logging/log4j/core/async/BasicAsyncLoggerContextSelector.java
@@ -1,44 +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.logging.log4j.core.async;
-
-import java.net.URI;
-
-import org.apache.logging.log4j.core.LoggerContext;
-import org.apache.logging.log4j.core.selector.BasicContextSelector;
-import org.apache.logging.log4j.plugins.Inject;
-import org.apache.logging.log4j.plugins.Singleton;
-import org.apache.logging.log4j.plugins.di.Injector;
-
-/**
- * Returns either this Thread's context or the default {@link 
AsyncLoggerContext}.
- * Single-application instances should prefer this implementation over the 
{@link AsyncLoggerContextSelector}
- * due to the reduced overhead avoiding classloader lookups.
- */
-@Singleton
-public class BasicAsyncLoggerContextSelector extends BasicContextSelector {
-
-    @Inject
-    public BasicAsyncLoggerContextSelector(Injector injector) {
-        super(injector);
-    }
-
-    @Override
-    protected LoggerContext createContext() {
-        return new AsyncLoggerContext("AsyncDefault", null, (URI) null, 
injector);
-    }
-}
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache license, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.async;
+
+import java.net.URI;
+
+import org.apache.logging.log4j.core.LoggerContext;
+import org.apache.logging.log4j.core.selector.BasicContextSelector;
+import org.apache.logging.log4j.plugins.Inject;
+import org.apache.logging.log4j.plugins.Singleton;
+import org.apache.logging.log4j.plugins.di.Injector;
+
+/**
+ * Returns either this Thread's context or the default {@link 
AsyncLoggerContext}.
+ * Single-application instances should prefer this implementation over the 
{@link AsyncLoggerContextSelector}
+ * due to the reduced overhead avoiding classloader lookups.
+ */
+@Singleton
+public class BasicAsyncLoggerContextSelector extends BasicContextSelector {
+
+    @Inject
+    public BasicAsyncLoggerContextSelector(Injector injector) {
+        super(injector);
+    }
+
+    @Override
+    protected LoggerContext createContext() {
+        return new AsyncLoggerContext("AsyncDefault", null, (URI) null, 
injector);
+    }
+}
diff --git 
a/log4j-core/src/main/java/org/apache/logging/log4j/core/config/ReliabilityStrategy.java
 
b/log4j-core/src/main/java/org/apache/logging/log4j/core/config/ReliabilityStrategy.java
index 1ccb74a6b1..b469a73b1d 100644
--- 
a/log4j-core/src/main/java/org/apache/logging/log4j/core/config/ReliabilityStrategy.java
+++ 
b/log4j-core/src/main/java/org/apache/logging/log4j/core/config/ReliabilityStrategy.java
@@ -1,95 +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.logging.log4j.core.config;
-
-import org.apache.logging.log4j.Level;
-import org.apache.logging.log4j.Marker;
-import org.apache.logging.log4j.core.LogEvent;
-import org.apache.logging.log4j.message.Message;
-import org.apache.logging.log4j.util.Supplier;
-
-/**
- * Interface for objects that know how to ensure delivery of log events to the 
appropriate appenders, even during and
- * after the configuration has been modified while the system is actively used.
- */
-public interface ReliabilityStrategy {
-
-    /**
-     * Logs an event.
-     *
-     * @param reconfigured supplies the next LoggerConfig if the strategy's 
LoggerConfig is no longer active
-     * @param loggerName The name of the Logger.
-     * @param fqcn The fully qualified class name of the caller.
-     * @param marker A Marker or null if none is present.
-     * @param level The event Level.
-     * @param data The Message.
-     * @param t A Throwable or null.
-     */
-    void log(Supplier<LoggerConfig> reconfigured, String loggerName, String 
fqcn, Marker marker, Level level,
-            Message data, Throwable t);
-    /**
-     * Logs an event.
-     *
-     * @param reconfigured supplies the next LoggerConfig if the strategy's 
LoggerConfig is no longer active
-     * @param loggerName The name of the Logger.
-     * @param fqcn The fully qualified class name of the caller.
-     * @param location The location of the caller or null.
-     * @param marker A Marker or null if none is present.
-     * @param level The event Level.
-     * @param data The Message.
-     * @param t A Throwable or null.
-     * @since 3.0
-     */
-    default void log(final Supplier<LoggerConfig> reconfigured, final String 
loggerName, final String fqcn, final StackTraceElement location,
-                     final Marker marker, final Level level, final Message 
data, final Throwable t) {
-    }
-
-    /**
-     * Logs an event.
-     *
-     * @param reconfigured supplies the next LoggerConfig if the strategy's 
LoggerConfig is no longer active
-     * @param event The log event.
-     */
-    void log(Supplier<LoggerConfig> reconfigured, LogEvent event);
-
-    /**
-     * For internal use by the ReliabilityStrategy; returns the LoggerConfig 
to use.
-     *
-     * @param next supplies the next LoggerConfig if the strategy's 
LoggerConfig is no longer active
-     * @return the currently active LoggerConfig
-     */
-    LoggerConfig getActiveLoggerConfig(Supplier<LoggerConfig> next);
-
-    /**
-     * Called after a log event was logged.
-     */
-    void afterLogEvent();
-
-    /**
-     * Called before all appenders are stopped.
-     */
-    void beforeStopAppenders();
-
-    /**
-     * Called before the configuration is stopped.
-     *
-     * @param configuration the configuration that will be stopped
-     */
-    void beforeStopConfiguration(Configuration configuration);
-
-}
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache license, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.config;
+
+import org.apache.logging.log4j.Level;
+import org.apache.logging.log4j.Marker;
+import org.apache.logging.log4j.core.LogEvent;
+import org.apache.logging.log4j.message.Message;
+import org.apache.logging.log4j.util.Supplier;
+
+/**
+ * Interface for objects that know how to ensure delivery of log events to the 
appropriate appenders, even during and
+ * after the configuration has been modified while the system is actively used.
+ */
+public interface ReliabilityStrategy {
+
+    /**
+     * Logs an event.
+     *
+     * @param reconfigured supplies the next LoggerConfig if the strategy's 
LoggerConfig is no longer active
+     * @param loggerName The name of the Logger.
+     * @param fqcn The fully qualified class name of the caller.
+     * @param marker A Marker or null if none is present.
+     * @param level The event Level.
+     * @param data The Message.
+     * @param t A Throwable or null.
+     */
+    void log(Supplier<LoggerConfig> reconfigured, String loggerName, String 
fqcn, Marker marker, Level level,
+            Message data, Throwable t);
+    /**
+     * Logs an event.
+     *
+     * @param reconfigured supplies the next LoggerConfig if the strategy's 
LoggerConfig is no longer active
+     * @param loggerName The name of the Logger.
+     * @param fqcn The fully qualified class name of the caller.
+     * @param location The location of the caller or null.
+     * @param marker A Marker or null if none is present.
+     * @param level The event Level.
+     * @param data The Message.
+     * @param t A Throwable or null.
+     * @since 3.0
+     */
+    default void log(final Supplier<LoggerConfig> reconfigured, final String 
loggerName, final String fqcn, final StackTraceElement location,
+                     final Marker marker, final Level level, final Message 
data, final Throwable t) {
+    }
+
+    /**
+     * Logs an event.
+     *
+     * @param reconfigured supplies the next LoggerConfig if the strategy's 
LoggerConfig is no longer active
+     * @param event The log event.
+     */
+    void log(Supplier<LoggerConfig> reconfigured, LogEvent event);
+
+    /**
+     * For internal use by the ReliabilityStrategy; returns the LoggerConfig 
to use.
+     *
+     * @param next supplies the next LoggerConfig if the strategy's 
LoggerConfig is no longer active
+     * @return the currently active LoggerConfig
+     */
+    LoggerConfig getActiveLoggerConfig(Supplier<LoggerConfig> next);
+
+    /**
+     * Called after a log event was logged.
+     */
+    void afterLogEvent();
+
+    /**
+     * Called before all appenders are stopped.
+     */
+    void beforeStopAppenders();
+
+    /**
+     * Called before the configuration is stopped.
+     *
+     * @param configuration the configuration that will be stopped
+     */
+    void beforeStopConfiguration(Configuration configuration);
+
+}
diff --git 
a/log4j-core/src/main/java/org/apache/logging/log4j/core/config/builder/impl/DefaultComponentAndConfigurationBuilder.java
 
b/log4j-core/src/main/java/org/apache/logging/log4j/core/config/builder/impl/DefaultComponentAndConfigurationBuilder.java
index f6e7e1e6bb..2ca603dabe 100644
--- 
a/log4j-core/src/main/java/org/apache/logging/log4j/core/config/builder/impl/DefaultComponentAndConfigurationBuilder.java
+++ 
b/log4j-core/src/main/java/org/apache/logging/log4j/core/config/builder/impl/DefaultComponentAndConfigurationBuilder.java
@@ -1,47 +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.logging.log4j.core.config.builder.impl;
-
-import org.apache.logging.log4j.core.config.Configuration;
-import org.apache.logging.log4j.core.config.builder.api.ComponentBuilder;
-
-/**
- * Extends {@code DefaultComponentBuilder} to specify
- * {@code DefaultConfigurationBuilder<? extends Configuration>} as the
- * {@code ConfigurationBuilder} type.
- *
- * @since 2.4
- */
-class DefaultComponentAndConfigurationBuilder<T extends ComponentBuilder<T>>
-        extends DefaultComponentBuilder<T, DefaultConfigurationBuilder<? 
extends Configuration>> {
-
-    DefaultComponentAndConfigurationBuilder(final 
DefaultConfigurationBuilder<? extends Configuration> builder, final String name,
-            final String type, final String value) {
-        super(builder, name, type, value);
-    }
-
-    DefaultComponentAndConfigurationBuilder(final 
DefaultConfigurationBuilder<? extends Configuration> builder, final String name,
-            final String type) {
-        super(builder, name, type);
-    }
-
-    public DefaultComponentAndConfigurationBuilder(final 
DefaultConfigurationBuilder<? extends Configuration> builder,
-            final String type) {
-        super(builder, type);
-    }
-
-}
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache license, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.config.builder.impl;
+
+import org.apache.logging.log4j.core.config.Configuration;
+import org.apache.logging.log4j.core.config.builder.api.ComponentBuilder;
+
+/**
+ * Extends {@code DefaultComponentBuilder} to specify
+ * {@code DefaultConfigurationBuilder<? extends Configuration>} as the
+ * {@code ConfigurationBuilder} type.
+ *
+ * @since 2.4
+ */
+class DefaultComponentAndConfigurationBuilder<T extends ComponentBuilder<T>>
+        extends DefaultComponentBuilder<T, DefaultConfigurationBuilder<? 
extends Configuration>> {
+
+    DefaultComponentAndConfigurationBuilder(final 
DefaultConfigurationBuilder<? extends Configuration> builder, final String name,
+            final String type, final String value) {
+        super(builder, name, type, value);
+    }
+
+    DefaultComponentAndConfigurationBuilder(final 
DefaultConfigurationBuilder<? extends Configuration> builder, final String name,
+            final String type) {
+        super(builder, name, type);
+    }
+
+    public DefaultComponentAndConfigurationBuilder(final 
DefaultConfigurationBuilder<? extends Configuration> builder,
+            final String type) {
+        super(builder, type);
+    }
+
+}
diff --git 
a/log4j-core/src/main/java/org/apache/logging/log4j/core/selector/CoreContextSelectors.java
 
b/log4j-core/src/main/java/org/apache/logging/log4j/core/selector/CoreContextSelectors.java
index 2ee6b0d4f6..1588d2999d 100644
--- 
a/log4j-core/src/main/java/org/apache/logging/log4j/core/selector/CoreContextSelectors.java
+++ 
b/log4j-core/src/main/java/org/apache/logging/log4j/core/selector/CoreContextSelectors.java
@@ -1,31 +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.
- */
-package org.apache.logging.log4j.core.selector;
-
-import org.apache.logging.log4j.core.async.AsyncLoggerContextSelector;
-import org.apache.logging.log4j.core.async.BasicAsyncLoggerContextSelector;
-
-public class CoreContextSelectors {
-
-    public static final Class<?>[] CLASSES = new Class<?>[] {
-            ClassLoaderContextSelector.class,
-            BasicContextSelector.class,
-            AsyncLoggerContextSelector.class,
-            BasicAsyncLoggerContextSelector.class
-    };
-
-}
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache license, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.selector;
+
+import org.apache.logging.log4j.core.async.AsyncLoggerContextSelector;
+import org.apache.logging.log4j.core.async.BasicAsyncLoggerContextSelector;
+
+public class CoreContextSelectors {
+
+    public static final Class<?>[] CLASSES = new Class<?>[] {
+            ClassLoaderContextSelector.class,
+            BasicContextSelector.class,
+            AsyncLoggerContextSelector.class,
+            BasicAsyncLoggerContextSelector.class
+    };
+
+}
diff --git 
a/log4j-core/src/main/java/org/apache/logging/log4j/core/util/CloseShieldOutputStream.java
 
b/log4j-core/src/main/java/org/apache/logging/log4j/core/util/CloseShieldOutputStream.java
index 1139bd20f9..946e0c29b4 100644
--- 
a/log4j-core/src/main/java/org/apache/logging/log4j/core/util/CloseShieldOutputStream.java
+++ 
b/log4j-core/src/main/java/org/apache/logging/log4j/core/util/CloseShieldOutputStream.java
@@ -1,60 +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.logging.log4j.core.util;
-
-import java.io.IOException;
-import java.io.OutputStream;
-
-/**
- * A delegating OutputStream that does not close its delegate.
- */
-public class CloseShieldOutputStream extends OutputStream {
-
-    private final OutputStream delegate;
-
-    public CloseShieldOutputStream(final OutputStream delegate) {
-        this.delegate = delegate;
-    }
-
-    /**
-     * Does nothing.
-     */
-    @Override
-    public void close() {
-        // do not close delegate
-    }
-
-    @Override
-    public void flush() throws IOException {
-        delegate.flush();
-    }
-
-    @Override
-    public void write(final byte[] b) throws IOException {
-        delegate.write(b);
-    }
-
-    @Override
-    public void write(final byte[] b, final int off, final int len) throws 
IOException {
-        delegate.write(b, off, len);
-    }
-
-    @Override
-    public void write(final int b) throws IOException {
-        delegate.write(b);
-    }
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache license, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.util;
+
+import java.io.IOException;
+import java.io.OutputStream;
+
+/**
+ * A delegating OutputStream that does not close its delegate.
+ */
+public class CloseShieldOutputStream extends OutputStream {
+
+    private final OutputStream delegate;
+
+    public CloseShieldOutputStream(final OutputStream delegate) {
+        this.delegate = delegate;
+    }
+
+    /**
+     * Does nothing.
+     */
+    @Override
+    public void close() {
+        // do not close delegate
+    }
+
+    @Override
+    public void flush() throws IOException {
+        delegate.flush();
+    }
+
+    @Override
+    public void write(final byte[] b) throws IOException {
+        delegate.write(b);
+    }
+
+    @Override
+    public void write(final byte[] b, final int off, final int len) throws 
IOException {
+        delegate.write(b, off, len);
+    }
+
+    @Override
+    public void write(final int b) throws IOException {
+        delegate.write(b);
+    }
 }
diff --git 
a/log4j-core/src/main/java/org/apache/logging/log4j/core/util/CloseShieldWriter.java
 
b/log4j-core/src/main/java/org/apache/logging/log4j/core/util/CloseShieldWriter.java
index c29cdf624e..b2f6fdcd9d 100644
--- 
a/log4j-core/src/main/java/org/apache/logging/log4j/core/util/CloseShieldWriter.java
+++ 
b/log4j-core/src/main/java/org/apache/logging/log4j/core/util/CloseShieldWriter.java
@@ -1,46 +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.logging.log4j.core.util;
-
-import java.io.IOException;
-import java.io.Writer;
-
-public class CloseShieldWriter extends Writer {
-
-    private final Writer delegate;
-
-    public CloseShieldWriter(final Writer delegate) {
-        this.delegate = delegate;
-    }
-
-    @Override
-    public void close() throws IOException {
-        // do not close delegate
-    }
-
-    @Override
-    public void flush() throws IOException {
-        delegate.flush();
-
-    }
-
-    @Override
-    public void write(final char[] cbuf, final int off, final int len) throws 
IOException {
-        delegate.write(cbuf, off, len);
-    }
-
-}
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache license, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.util;
+
+import java.io.IOException;
+import java.io.Writer;
+
+public class CloseShieldWriter extends Writer {
+
+    private final Writer delegate;
+
+    public CloseShieldWriter(final Writer delegate) {
+        this.delegate = delegate;
+    }
+
+    @Override
+    public void close() throws IOException {
+        // do not close delegate
+    }
+
+    @Override
+    public void flush() throws IOException {
+        delegate.flush();
+
+    }
+
+    @Override
+    public void write(final char[] cbuf, final int off, final int len) throws 
IOException {
+        delegate.write(cbuf, off, len);
+    }
+
+}
diff --git 
a/log4j-core/src/main/java/org/apache/logging/log4j/core/util/IOUtils.java 
b/log4j-core/src/main/java/org/apache/logging/log4j/core/util/IOUtils.java
index ffb8615cd3..07e5c79316 100644
--- a/log4j-core/src/main/java/org/apache/logging/log4j/core/util/IOUtils.java
+++ b/log4j-core/src/main/java/org/apache/logging/log4j/core/util/IOUtils.java
@@ -1,130 +1,130 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements. See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache license, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License. You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT 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.util;
-
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.OutputStream;
-import java.io.Reader;
-import java.io.Writer;
-
-/**
- * Copied from Apache Commons IO revision 1686747.
- */
-public class IOUtils {
-
-    /**
-     * The default buffer size ({@value}) to use for
-     * {@link #copyLarge(InputStream, OutputStream)}
-     * and
-     * {@link #copyLarge(Reader, Writer)}
-     */
-    private static final int DEFAULT_BUFFER_SIZE = 1024 * 4;
-
-    /**
-     * Represents the end-of-file (or stream).
-     */
-    public static final int EOF = -1;
-
-    /**
-     * Copies chars from a <code>Reader</code> to a <code>Writer</code>.
-     * <p/>
-     * This method buffers the input internally, so there is no need to use a
-     * <code>BufferedReader</code>.
-     * <p/>
-     * Large streams (over 2GB) will return a chars copied value of
-     * <code>-1</code> after the copy has completed since the correct
-     * number of chars cannot be returned as an int. For large streams
-     * use the <code>copyLarge(Reader, Writer)</code> method.
-     *
-     * @param input the <code>Reader</code> to read from
-     * @param output the <code>Writer</code> to write to
-     * @return the number of characters copied, or -1 if &gt; Integer.MAX_VALUE
-     * @throws NullPointerException if the input or output is null
-     * @throws IOException          if an I/O error occurs
-     * @since 1.1
-     */
-    public static int copy(final Reader input, final Writer output) throws 
IOException {
-        final long count = copyLarge(input, output);
-        if (count > Integer.MAX_VALUE) {
-            return -1;
-        }
-        return (int) count;
-    }
-
-    /**
-     * Copies chars from a large (over 2GB) <code>Reader</code> to a 
<code>Writer</code>.
-     * <p/>
-     * This method buffers the input internally, so there is no need to use a
-     * <code>BufferedReader</code>.
-     * <p/>
-     * The buffer size is given by {@link #DEFAULT_BUFFER_SIZE}.
-     *
-     * @param input the <code>Reader</code> to read from
-     * @param output the <code>Writer</code> to write to
-     * @return the number of characters copied
-     * @throws NullPointerException if the input or output is null
-     * @throws IOException          if an I/O error occurs
-     * @since 1.3
-     */
-    public static long copyLarge(final Reader input, final Writer output) 
throws IOException {
-        return copyLarge(input, output, new char[DEFAULT_BUFFER_SIZE]);
-    }
-
-    /**
-     * Copies chars from a large (over 2GB) <code>Reader</code> to a 
<code>Writer</code>.
-     * <p/>
-     * This method uses the provided buffer, so there is no need to use a
-     * <code>BufferedReader</code>.
-     * <p/>
-     *
-     * @param input the <code>Reader</code> to read from
-     * @param output the <code>Writer</code> to write to
-     * @param buffer the buffer to be used for the copy
-     * @return the number of characters copied
-     * @throws NullPointerException if the input or output is null
-     * @throws IOException          if an I/O error occurs
-     * @since 2.2
-     */
-    public static long copyLarge(final Reader input, final Writer output, 
final char[] buffer) throws IOException {
-        long count = 0;
-        int n;
-        while (EOF != (n = input.read(buffer))) {
-            output.write(buffer, 0, n);
-            count += n;
-        }
-        return count;
-    }
-
-    /**
-     * Gets the contents of a <code>Reader</code> as a String.
-     * <p/>
-     * This method buffers the input internally, so there is no need to use a
-     * <code>BufferedReader</code>.
-     *
-     * @param input the <code>Reader</code> to read from
-     * @return the requested String
-     * @throws NullPointerException if the input is null
-     * @throws IOException          if an I/O error occurs
-     */
-    public static String toString(final Reader input) throws IOException {
-        final StringBuilderWriter sw = new StringBuilderWriter();
-        copy(input, sw);
-        return sw.toString();
-    }
-
-}
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache license, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.util;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.io.Reader;
+import java.io.Writer;
+
+/**
+ * Copied from Apache Commons IO revision 1686747.
+ */
+public class IOUtils {
+
+    /**
+     * The default buffer size ({@value}) to use for
+     * {@link #copyLarge(InputStream, OutputStream)}
+     * and
+     * {@link #copyLarge(Reader, Writer)}
+     */
+    private static final int DEFAULT_BUFFER_SIZE = 1024 * 4;
+
+    /**
+     * Represents the end-of-file (or stream).
+     */
+    public static final int EOF = -1;
+
+    /**
+     * Copies chars from a <code>Reader</code> to a <code>Writer</code>.
+     * <p/>
+     * This method buffers the input internally, so there is no need to use a
+     * <code>BufferedReader</code>.
+     * <p/>
+     * Large streams (over 2GB) will return a chars copied value of
+     * <code>-1</code> after the copy has completed since the correct
+     * number of chars cannot be returned as an int. For large streams
+     * use the <code>copyLarge(Reader, Writer)</code> method.
+     *
+     * @param input the <code>Reader</code> to read from
+     * @param output the <code>Writer</code> to write to
+     * @return the number of characters copied, or -1 if &gt; Integer.MAX_VALUE
+     * @throws NullPointerException if the input or output is null
+     * @throws IOException          if an I/O error occurs
+     * @since 1.1
+     */
+    public static int copy(final Reader input, final Writer output) throws 
IOException {
+        final long count = copyLarge(input, output);
+        if (count > Integer.MAX_VALUE) {
+            return -1;
+        }
+        return (int) count;
+    }
+
+    /**
+     * Copies chars from a large (over 2GB) <code>Reader</code> to a 
<code>Writer</code>.
+     * <p/>
+     * This method buffers the input internally, so there is no need to use a
+     * <code>BufferedReader</code>.
+     * <p/>
+     * The buffer size is given by {@link #DEFAULT_BUFFER_SIZE}.
+     *
+     * @param input the <code>Reader</code> to read from
+     * @param output the <code>Writer</code> to write to
+     * @return the number of characters copied
+     * @throws NullPointerException if the input or output is null
+     * @throws IOException          if an I/O error occurs
+     * @since 1.3
+     */
+    public static long copyLarge(final Reader input, final Writer output) 
throws IOException {
+        return copyLarge(input, output, new char[DEFAULT_BUFFER_SIZE]);
+    }
+
+    /**
+     * Copies chars from a large (over 2GB) <code>Reader</code> to a 
<code>Writer</code>.
+     * <p/>
+     * This method uses the provided buffer, so there is no need to use a
+     * <code>BufferedReader</code>.
+     * <p/>
+     *
+     * @param input the <code>Reader</code> to read from
+     * @param output the <code>Writer</code> to write to
+     * @param buffer the buffer to be used for the copy
+     * @return the number of characters copied
+     * @throws NullPointerException if the input or output is null
+     * @throws IOException          if an I/O error occurs
+     * @since 2.2
+     */
+    public static long copyLarge(final Reader input, final Writer output, 
final char[] buffer) throws IOException {
+        long count = 0;
+        int n;
+        while (EOF != (n = input.read(buffer))) {
+            output.write(buffer, 0, n);
+            count += n;
+        }
+        return count;
+    }
+
+    /**
+     * Gets the contents of a <code>Reader</code> as a String.
+     * <p/>
+     * This method buffers the input internally, so there is no need to use a
+     * <code>BufferedReader</code>.
+     *
+     * @param input the <code>Reader</code> to read from
+     * @return the requested String
+     * @throws NullPointerException if the input is null
+     * @throws IOException          if an I/O error occurs
+     */
+    public static String toString(final Reader input) throws IOException {
+        final StringBuilderWriter sw = new StringBuilderWriter();
+        copy(input, sw);
+        return sw.toString();
+    }
+
+}
diff --git 
a/log4j-jeromq/src/main/java/org/apache/logging/log4j/jeromq/appender/JeroMqAppender.java
 
b/log4j-jeromq/src/main/java/org/apache/logging/log4j/jeromq/appender/JeroMqAppender.java
index c28021a82f..98047a0cff 100644
--- 
a/log4j-jeromq/src/main/java/org/apache/logging/log4j/jeromq/appender/JeroMqAppender.java
+++ 
b/log4j-jeromq/src/main/java/org/apache/logging/log4j/jeromq/appender/JeroMqAppender.java
@@ -172,6 +172,11 @@ public final class JeroMqAppender extends AbstractAppender 
{
         sendRcTrue = sendRcFalse = 0;
     }
 
+    // not public, handy for testing
+    byte[] recv(int timeoutMs) {
+        return manager.recv(timeoutMs);
+    }
+
     @Override
     public String toString() {
         return "JeroMqAppender{" +
diff --git 
a/log4j-jeromq/src/main/java/org/apache/logging/log4j/jeromq/appender/JeroMqManager.java
 
b/log4j-jeromq/src/main/java/org/apache/logging/log4j/jeromq/appender/JeroMqManager.java
index e50972df03..51aaf1b840 100644
--- 
a/log4j-jeromq/src/main/java/org/apache/logging/log4j/jeromq/appender/JeroMqManager.java
+++ 
b/log4j-jeromq/src/main/java/org/apache/logging/log4j/jeromq/appender/JeroMqManager.java
@@ -26,6 +26,7 @@ import org.apache.logging.log4j.core.appender.ManagerFactory;
 import org.apache.logging.log4j.core.util.Cancellable;
 import org.apache.logging.log4j.core.util.ShutdownCallbackRegistry;
 import org.apache.logging.log4j.util.PropertiesUtil;
+import org.zeromq.SocketType;
 import org.zeromq.ZMQ;
 
 /**
@@ -60,7 +61,7 @@ public class JeroMqManager extends AbstractManager {
 
         final boolean enableShutdownHook = 
PropertiesUtil.getProperties().getBooleanProperty(
             SYS_PROPERTY_ENABLE_SHUTDOWN_HOOK, true);
-        if (enableShutdownHook) {
+        if (enableShutdownHook && LogManager.getFactory() instanceof 
ShutdownCallbackRegistry) {
             SHUTDOWN_HOOK = ((ShutdownCallbackRegistry) 
LogManager.getFactory()).addShutdownCallback(CONTEXT::close);
         } else {
             SHUTDOWN_HOOK = null;
@@ -71,7 +72,7 @@ public class JeroMqManager extends AbstractManager {
 
     private JeroMqManager(final String name, final JeroMqConfiguration config) 
{
         super(null, name);
-        publisher = CONTEXT.socket(ZMQ.PUB);
+        publisher = CONTEXT.socket(SocketType.XPUB);
         publisher.setAffinity(config.affinity);
         publisher.setBacklog(config.backlog);
         publisher.setDelayAttachOnConnect(config.delayAttachOnConnect);
@@ -104,6 +105,17 @@ public class JeroMqManager extends AbstractManager {
         return publisher.send(data);
     }
 
+    // not public, handy for testing
+    byte[] recv(int timeoutMs) {
+        int oldTimeoutMs = publisher.getReceiveTimeOut();
+        try {
+            publisher.setReceiveTimeOut(timeoutMs);
+            return publisher.recv();
+        } finally {
+            publisher.setReceiveTimeOut(oldTimeoutMs);
+        }
+    }
+
     @Override
     protected boolean releaseSub(final long timeout, final TimeUnit timeUnit) {
         publisher.close();
diff --git 
a/log4j-jeromq/src/test/java/org/apache/logging/log4j/jeromq/appender/JeroMqAppenderTest.java
 
b/log4j-jeromq/src/test/java/org/apache/logging/log4j/jeromq/appender/JeroMqAppenderTest.java
index e582559281..aafe354a5d 100644
--- 
a/log4j-jeromq/src/test/java/org/apache/logging/log4j/jeromq/appender/JeroMqAppenderTest.java
+++ 
b/log4j-jeromq/src/test/java/org/apache/logging/log4j/jeromq/appender/JeroMqAppenderTest.java
@@ -21,6 +21,7 @@ import java.util.concurrent.ExecutorService;
 import java.util.concurrent.Executors;
 import java.util.concurrent.Future;
 import java.util.concurrent.TimeUnit;
+import java.util.concurrent.TimeoutException;
 
 import org.apache.logging.log4j.ThreadContext;
 import org.apache.logging.log4j.core.Logger;
@@ -31,13 +32,12 @@ import org.junit.jupiter.api.Tag;
 import org.junit.jupiter.api.Test;
 import org.junit.jupiter.api.Timeout;
 
+import static org.assertj.core.api.Assertions.fail;
 import static org.junit.jupiter.api.Assertions.assertEquals;
 import static org.junit.jupiter.api.Assertions.assertNotNull;
-import static org.junit.jupiter.api.Assertions.fail;
 
 @Tag("zeromq")
-@Tag("sleepy")
-@Timeout(value = 200)
+@Timeout(value = 10, unit = TimeUnit.SECONDS)
 @LoggerContextSource(value = "JeroMqAppenderTest.xml", timeout = 60)
 public class JeroMqAppenderTest {
 
@@ -61,7 +61,7 @@ public class JeroMqAppenderTest {
         final ExecutorService executor = Executors.newSingleThreadExecutor();
         try {
             final Future<List<String>> future = executor.submit(client);
-            Thread.sleep(100);
+            waitForSubscription(appender, 1000);
             appender.resetSendRcs();
             logger.info("Hello");
             logger.info("Again");
@@ -87,7 +87,7 @@ public class JeroMqAppenderTest {
         final ExecutorService executor = Executors.newSingleThreadExecutor();
         try {
             final Future<List<String>> future = executor.submit(client);
-            Thread.sleep(100);
+            waitForSubscription(appender, 1000);
             appender.resetSendRcs();
             final ExecutorService fixedThreadPool = 
Executors.newFixedThreadPool(nThreads);
             for (int i = 0; i < 10.; i++) {
@@ -129,4 +129,15 @@ public class JeroMqAppenderTest {
         assertEquals(2, appender.getSendRcTrue());
         assertEquals(0, appender.getSendRcFalse());
     }
+
+    private void waitForSubscription(JeroMqAppender appender, int timeoutMs) 
throws Exception {
+        final long start = System.currentTimeMillis();
+        while (System.currentTimeMillis() - start < timeoutMs) {
+            byte[] msg = appender.recv(timeoutMs);
+            if (msg != null && msg.length > 0 && msg[0] == 1) {
+                return;
+            }
+        }
+        throw new TimeoutException();
+    }
 }
diff --git 
a/log4j-layout-template-json-test/src/test/java/org/apache/logging/log4j/layout/template/json/TestHelpers.java
 
b/log4j-layout-template-json-test/src/test/java/org/apache/logging/log4j/layout/template/json/TestHelpers.java
index 07f3234060..6a8a4a90e9 100644
--- 
a/log4j-layout-template-json-test/src/test/java/org/apache/logging/log4j/layout/template/json/TestHelpers.java
+++ 
b/log4j-layout-template-json-test/src/test/java/org/apache/logging/log4j/layout/template/json/TestHelpers.java
@@ -75,14 +75,17 @@ public final class TestHelpers {
             final Consumer<MapAccessor> accessorConsumer) {
         final String serializedLogEventJson = layout.toSerializable(logEvent);
         @SuppressWarnings("unchecked")
-        final Map<String, Object> deserializedLogEvent =
-                (Map<String, Object>) readJson(serializedLogEventJson);
+        final Map<String, Object> deserializedLogEvent = (Map<String, Object>) 
readJson(serializedLogEventJson);
         final MapAccessor serializedLogEventAccessor = new 
MapAccessor(deserializedLogEvent);
         accessorConsumer.accept(serializedLogEventAccessor);
     }
 
     public static Object readJson(final String json) {
-        return JsonReader.read(json);
+        try {
+            return JsonReader.read(json);
+        } catch (final Exception error) {
+            throw new RuntimeException("failed to deserialize the JSON: " + 
json, error);
+        }
     }
 
     public static Map<String, Object> asMap(final Object... pairs) {
diff --git 
a/log4j-layout-template-json-test/src/test/java/org/apache/logging/log4j/layout/template/json/resolver/MarkerResolverTest.java
 
b/log4j-layout-template-json-test/src/test/java/org/apache/logging/log4j/layout/template/json/resolver/MarkerResolverTest.java
new file mode 100644
index 0000000000..8eeb6f7f4a
--- /dev/null
+++ 
b/log4j-layout-template-json-test/src/test/java/org/apache/logging/log4j/layout/template/json/resolver/MarkerResolverTest.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.logging.log4j.layout.template.json.resolver;
+
+import org.apache.logging.log4j.Marker;
+import org.apache.logging.log4j.MarkerManager;
+import org.apache.logging.log4j.core.LogEvent;
+import org.apache.logging.log4j.core.impl.Log4jLogEvent;
+import org.apache.logging.log4j.layout.template.json.JsonTemplateLayout;
+import org.junit.jupiter.api.Test;
+
+import static org.apache.logging.log4j.layout.template.json.TestHelpers.*;
+import static org.assertj.core.api.Assertions.assertThat;
+
+class MarkerResolverTest {
+
+    @Test
+    void should_have_a_marker_name() {
+
+        // Create the event template
+        final String eventTemplate = writeJson(asMap(
+                "marker", asMap(
+                        "$resolver", "marker",
+                        "field", "name")));
+
+        // Create the layout.
+        final JsonTemplateLayout layout = JsonTemplateLayout
+                .newBuilder()
+                .setConfiguration(CONFIGURATION)
+                .setEventTemplate(eventTemplate)
+                .build();
+
+        // Create the log event.
+        final Marker marker = MarkerManager.getMarker("MARKER");
+        final LogEvent logEvent = Log4jLogEvent
+                .newBuilder()
+                .setMarker(marker)
+                .build();
+
+        // Check the serialized event.
+        usingSerializedLogEventAccessor(layout, logEvent, accessor ->
+                assertThat(accessor.getString("marker")).isEqualTo("MARKER"));
+
+    }
+
+    @Test
+    void should_list_parents_as_array() {
+
+        // Create the event template
+        final String eventTemplate = writeJson(asMap(
+                "parents", asMap(
+                        "$resolver", "marker",
+                        "field", "parents")));
+
+        // Create the layout.
+        final JsonTemplateLayout layout = JsonTemplateLayout
+                .newBuilder()
+                .setConfiguration(CONFIGURATION)
+                .setEventTemplate(eventTemplate)
+                .build();
+
+        // Create the log event.
+        final Marker parentMarker1 = 
MarkerManager.getMarker("PARENT_MARKER_NAME_1");
+        final Marker parentMarker2 = 
MarkerManager.getMarker("PARENT_MARKER_NAME_2");
+        final Marker childMarker = 
MarkerManager.getMarker("CHILD_MARKER_NAME");
+        childMarker.setParents(parentMarker1, parentMarker2);
+
+        final LogEvent logEvent = Log4jLogEvent
+                .newBuilder()
+                .setMarker(childMarker)
+                .build();
+
+        // Check the serialized event.
+        usingSerializedLogEventAccessor(layout, logEvent, accessor ->
+                assertThat(accessor.getList("parents", String.class))
+                        .containsOnly(parentMarker1.getName(), 
parentMarker2.getName()));
+
+    }
+
+}
diff --git 
a/log4j-layout-template-json/src/main/java/org/apache/logging/log4j/layout/template/json/resolver/MarkerResolver.java
 
b/log4j-layout-template-json/src/main/java/org/apache/logging/log4j/layout/template/json/resolver/MarkerResolver.java
index fc1c30c9bc..4975fd2a15 100644
--- 
a/log4j-layout-template-json/src/main/java/org/apache/logging/log4j/layout/template/json/resolver/MarkerResolver.java
+++ 
b/log4j-layout-template-json/src/main/java/org/apache/logging/log4j/layout/template/json/resolver/MarkerResolver.java
@@ -26,7 +26,7 @@ import 
org.apache.logging.log4j.layout.template.json.util.JsonWriter;
  * <h3>Configuration</h3>
  *
  * <pre>
- * config = "field" -> "name"
+ * config = "field" -> ( "name" | "parents" )
  * </pre>
  *
  * <h3>Examples</h3>
@@ -39,6 +39,15 @@ import 
org.apache.logging.log4j.layout.template.json.util.JsonWriter;
  *   "field": "name"
  * }
  * </pre>
+ *
+ * Resolve the names of the marker's parents:
+ *
+ * <pre>
+ * {
+ *   "$resolver": "marker",
+ *   "field": "parents"
+ * }
+ * </pre>
  */
 public final class MarkerResolver implements EventResolver {
 
@@ -52,6 +61,30 @@ public final class MarkerResolver implements EventResolver {
                 }
             };
 
+    private static final TemplateResolver<LogEvent> PARENTS_RESOLVER =
+            (final LogEvent logEvent, final JsonWriter jsonWriter) -> {
+
+                // Short-circuit if there are no parents
+                final Marker marker = logEvent.getMarker();
+                if (marker == null || !marker.hasParents()) {
+                    jsonWriter.writeNull();
+                    return;
+                }
+
+                // Write parents
+                final Marker[] parents = marker.getParents();
+                jsonWriter.writeArrayStart();
+                for (int parentIndex = 0; parentIndex < parents.length; 
parentIndex++) {
+                    if (parentIndex > 0) {
+                        jsonWriter.writeSeparator();
+                    }
+                    final Marker parentMarker = parents[parentIndex];
+                    jsonWriter.writeString(parentMarker.getName());
+                }
+                jsonWriter.writeArrayEnd();
+
+            };
+
     private final TemplateResolver<LogEvent> internalResolver;
 
     MarkerResolver(final TemplateResolverConfig config) {
@@ -61,9 +94,15 @@ public final class MarkerResolver implements EventResolver {
     private TemplateResolver<LogEvent> createInternalResolver(
             final TemplateResolverConfig config) {
         final String fieldName = config.getString("field");
+
         if ("name".equals(fieldName)) {
             return NAME_RESOLVER;
         }
+
+        if ("parents".equals(fieldName)) {
+            return PARENTS_RESOLVER;
+        }
+
         throw new IllegalArgumentException("unknown field: " + config);
     }
 
diff --git 
a/log4j-plugins/src/main/java/org/apache/logging/log4j/plugins/internal/util/HierarchicalCollections.java
 
b/log4j-plugins/src/main/java/org/apache/logging/log4j/plugins/internal/util/HierarchicalCollections.java
index dbeb78232d..18d0093b88 100644
--- 
a/log4j-plugins/src/main/java/org/apache/logging/log4j/plugins/internal/util/HierarchicalCollections.java
+++ 
b/log4j-plugins/src/main/java/org/apache/logging/log4j/plugins/internal/util/HierarchicalCollections.java
@@ -16,12 +16,7 @@
  */
 package org.apache.logging.log4j.plugins.internal.util;
 
-import java.util.AbstractMap;
-import java.util.AbstractSet;
-import java.util.Iterator;
-import java.util.Map;
-import java.util.NoSuchElementException;
-import java.util.Set;
+import java.util.*;
 import java.util.concurrent.ConcurrentHashMap;
 
 class HierarchicalCollections {
diff --git a/log4j-to-slf4j/pom.xml b/log4j-to-slf4j/pom.xml
index 66fe3e4a8d..f7543705b0 100644
--- a/log4j-to-slf4j/pom.xml
+++ b/log4j-to-slf4j/pom.xml
@@ -31,6 +31,7 @@
     <docLabel>SLF4J Documentation</docLabel>
     <projectDir>/log4j-to-slf4j</projectDir>
     <module.name>org.apache.logging.slf4j</module.name>
+    <slf4j.support.bound>3</slf4j.support.bound>
   </properties>
   <dependencies>
     <dependency>
@@ -91,6 +92,7 @@
         <configuration>
           <instructions>
             <Export-Package>org.apache.logging.slf4j</Export-Package>
+            
<Import-Package>org.slf4j*;version="${range;[==,${slf4j.support.bound})}",*</Import-Package>
           </instructions>
         </configuration>
       </plugin>
diff --git a/pom.xml b/pom.xml
index 786d2c52aa..9f6cb8412f 100644
--- a/pom.xml
+++ b/pom.xml
@@ -434,6 +434,7 @@
     <jetty.version>9.4.50.v20221201</jetty.version>
     <jmdns.version>3.5.8</jmdns.version>
     <jmh.version>1.36</jmh.version>
+    <jna.version>5.12.1</jna.version>
     <json-unit.version>2.36.0</json-unit.version>
     <junit.version>4.13.2</junit.version>
     <junit-jupiter.version>5.9.1</junit-jupiter.version>
@@ -982,6 +983,12 @@
         <version>${jmh.version}</version>
       </dependency>
 
+      <dependency>
+        <groupId>net.java.dev.jna</groupId>
+        <artifactId>jna</artifactId>
+        <version>${jna.version}</version>
+      </dependency>
+
       <dependency>
         <groupId>net.javacrumbs.json-unit</groupId>
         <artifactId>json-unit</artifactId>
@@ -1353,7 +1360,7 @@
         </plugin>
 
         <plugin>
-          <groupId>org.apache.felix</groupId>
+          <groupId>org.apache.maven.plugins</groupId>
           <artifactId>maven-artifact-plugin</artifactId>
           <version>${maven-artifact-plugin.version}</version>
         </plugin>
diff --git a/src/changelog/.2.x.x/1232_log4j-to-sfl4j-2-OSGiMetadata.xml 
b/src/changelog/.2.x.x/1232_log4j-to-sfl4j-2-OSGiMetadata.xml
new file mode 100644
index 0000000000..b166412c83
--- /dev/null
+++ b/src/changelog/.2.x.x/1232_log4j-to-sfl4j-2-OSGiMetadata.xml
@@ -0,0 +1,29 @@
+<?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.
+-->
+<entry xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance";
+       xmlns="http://logging.apache.org/log4j/changelog";
+       xsi:schemaLocation="http://logging.apache.org/log4j/changelog 
https://logging.apache.org/log4j/changelog-0.1.0.xsd";
+       type="fixed">
+  <issue id="1232" 
link="https://github.com/apache/logging-log4j2/issues/1232"/>
+  <author id="hanneswell"/>
+  <author name="Hannes Wellmann"/>
+  <description format="asciidoc">
+    Adapt the OSGi metadata of log4j-to-slf4j to work with slf4j 1 and 2.
+    To achieve that use a version range of `[1.7,3)` for the imported slf4j 
packages.  
+  </description>
+</entry>
diff --git a/src/changelog/.2.x.x/1366_fix_java_sql_date.xml 
b/src/changelog/.2.x.x/1366_fix_java_sql_date.xml
new file mode 100644
index 0000000000..8a28a5bd2e
--- /dev/null
+++ b/src/changelog/.2.x.x/1366_fix_java_sql_date.xml
@@ -0,0 +1,28 @@
+<?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.
+-->
+<entry xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance";
+       xmlns="http://logging.apache.org/log4j/changelog";
+       xsi:schemaLocation="http://logging.apache.org/log4j/changelog 
https://logging.apache.org/log4j/changelog-0.1.1.xsd";
+       type="fixed">
+  <issue id="1366" link="https://github.com/apache/logging-log4j2/pull/1366"/>
+  <author id="Hikarikun92"/>
+  <author name="Lucas Souza"/>
+  <description format="asciidoc">
+    Fixed logging of java.sql.Date objects by appending it before Log4J tries 
to call java.util.Date.toInstant() on it.
+  </description>
+</entry>
diff --git a/src/site/asciidoc/manual/json-template-layout.adoc.vm 
b/src/site/asciidoc/manual/json-template-layout.adoc.vm
index f1bb103a21..ad61105033 100644
--- a/src/site/asciidoc/manual/json-template-layout.adoc.vm
+++ b/src/site/asciidoc/manual/json-template-layout.adoc.vm
@@ -743,6 +743,8 @@ Each `pointMatcherRegexes` item triggers a 
`Pattern#matcher()` call, which is
 not garbage-free either.
 ====
 
+====== Examples
+
 Resolve `logEvent.getThrown().getClass().getCanonicalName()`:
 
 [source,json]
@@ -849,6 +851,8 @@ severity-field = "field" -> ( "keyword" | "code" )
 
 Resolves the fields of the `logEvent.getLevel()`.
 
+====== Examples
+
 Resolve the level name:
 
 [source,json]
@@ -897,6 +901,8 @@ config = "field" -> ( "name" | "fqcn" )
 
 Resolves `logEvent.getLoggerFqcn()` and `logEvent.getLoggerName()`.
 
+====== Examples
+
 Resolve the logger name:
 
 [source,json]
@@ -930,6 +936,8 @@ key    = "key" -> string
 Performs link:lookups.html#AppMainArgsLookup[Main Argument Lookup] for the
 given `index` or `key`.
 
+====== Examples
+
 Resolve the 1st `main()` method argument:
 
 [source,json]
@@ -956,6 +964,38 @@ Resolve the argument coming right after `--userId`:
 Resolves ``MapMessage``s. See link:#map-resolver-template[Map Resolver 
Template]
 for details.
 
+[#event-template-resolver-marker]
+===== `marker`
+
+[source]
+----
+config = "field" -> ( "name" | "parents" )
+----
+
+Resolves `logEvent.getMarker()`.
+
+====== Examples
+
+Resolve the marker name:
+
+[source,json]
+----
+{
+  "$resolver": "marker",
+  "field": "name"
+}
+----
+
+Resolve the names of the marker's parents:
+
+[source,json]
+----
+{
+  "$resolver": "marker",
+  "field": "parents"
+}
+----
+
 [#event-template-resolver-mdc]
 ===== `mdc`
 
@@ -986,6 +1026,8 @@ For simple string messages, the resolution is performed 
without allocations.
 For ``ObjectMessage``s and ``MultiformatMessage``s, it depends.
 ====
 
+====== Examples
+
 Resolve the message into a string:
 
 [source,json]
@@ -1047,6 +1089,8 @@ Regarding garbage footprint, `stringified` flag 
translates to
 which is the case if `log4j2.enableThreadLocals` property set to true.
 ====
 
+====== Examples
+
 Resolve the message parameters into an array:
 
 [source,json]
@@ -1099,6 +1143,8 @@ pattern = "pattern" -> string
 Resolves the Nested Diagnostic Context (NDC), aka. Thread Context Stack,
 `String[]` returned by `logEvent.getContextStack()`.
 
+====== Examples
+
 Resolve all NDC values into a list:
 
 [source,json]
@@ -1133,6 +1179,8 @@ Resolver delegating to 
link:layouts.html#PatternLayout[`PatternLayout`].
 The default value of `stackTraceEnabled` is inherited from the parent
 `JsonTemplateLayout`.
 
+====== Examples
+
 Resolve the string produced by `%p %c{1.} [%t] %X{userId} %X %m%ex` pattern:
 
 [source,json]
@@ -1161,6 +1209,8 @@ Resolves the fields of the `StackTraceElement` returned by
 Note that this resolver is toggled by
 `log4j.layout.jsonTemplate.locationInfoEnabled` property.
 
+====== Examples
+
 Resolve the line number:
 
 [source,json]
@@ -1182,6 +1232,8 @@ config = "field" -> ( "name" | "id" | "priority" )
 Resolves `logEvent.getThreadId()`, `logEvent.getThreadName()`,
 `logEvent.getThreadPriority()`.
 
+====== Examples
+
 Resolve the thread name:
 
 [source,json]
@@ -1221,6 +1273,8 @@ rounded       = "rounded" -> boolean
 
 Resolves `logEvent.getInstant()` in various forms.
 
+====== Examples
+
 .`timestamp` template resolver examples
 [cols="5,2m"]
 |===

Reply via email to