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

pkarwasz pushed a commit to branch fix/2.25.x/rfc5424-param-names
in repository https://gitbox.apache.org/repos/asf/logging-log4j2.git

commit 20f32dd98e9450e8588b465f9db09d8ec5735aa9
Author: Piotr P. Karwasz <[email protected]>
AuthorDate: Tue Mar 10 16:31:58 2026 +0100

    Restore support for documented `Rfc5424Layout` attributes
    
    In `2.21.0`, `Rfc5424Layout` was migrated from a factory method to the 
builder pattern. During this change, the recognized names of several 
configuration attributes unintentionally diverged from the documented ones.
    
    As a result, some documented attributes were no longer recognized, while 
new, undocumented names were introduced.
    
    This change restores support for the documented attribute names while 
continuing to accept the names introduced in `2.21.0` for backward 
compatibility.
    
    Fixes #4022
    
    Co-authored-by: Volkan Yazıcı <[email protected]>
---
 .../log4j/core/layout/Rfc5424LayoutTest.java       | 128 ++++++++++++++
 .../logging/log4j/core/layout/Rfc5424Layout.java   | 185 ++++++++++++++++++++-
 2 files changed, 304 insertions(+), 9 deletions(-)

diff --git 
a/log4j-core-test/src/test/java/org/apache/logging/log4j/core/layout/Rfc5424LayoutTest.java
 
b/log4j-core-test/src/test/java/org/apache/logging/log4j/core/layout/Rfc5424LayoutTest.java
index 87157ec68b..8ab0a4e420 100644
--- 
a/log4j-core-test/src/test/java/org/apache/logging/log4j/core/layout/Rfc5424LayoutTest.java
+++ 
b/log4j-core-test/src/test/java/org/apache/logging/log4j/core/layout/Rfc5424LayoutTest.java
@@ -29,8 +29,11 @@ import java.net.InetAddress;
 import java.net.UnknownHostException;
 import java.util.ArrayList;
 import java.util.Arrays;
+import java.util.HashMap;
 import java.util.List;
 import java.util.Locale;
+import java.util.Map;
+import java.util.stream.Stream;
 import org.apache.logging.log4j.Level;
 import org.apache.logging.log4j.MarkerManager;
 import org.apache.logging.log4j.ThreadContext;
@@ -57,6 +60,8 @@ import org.junit.jupiter.api.AfterAll;
 import org.junit.jupiter.api.BeforeAll;
 import org.junit.jupiter.api.Test;
 import org.junit.jupiter.params.ParameterizedTest;
+import org.junit.jupiter.params.provider.Arguments;
+import org.junit.jupiter.params.provider.MethodSource;
 import org.junit.jupiter.params.provider.ValueSource;
 
 @UsingAnyThreadContext
@@ -89,6 +94,10 @@ class Rfc5424LayoutTest {
             "[RequestContext@3692 ipAddress=\"192.168.0.120\" 
loginId=\"JohnDoe\"]";
     private static final String collectionEndOfLine = "Transfer Complete";
 
+    private static final String NEW_LINE_ESCAPE = "\\n";
+    private static final String INCLUDED_KEYS = "key1, key2, locale";
+    private static final String EXCLUDED_KEYS = "key3, key4";
+
     static ConfigurationFactory cf = new BasicConfigurationFactory();
 
     @BeforeAll
@@ -792,4 +801,123 @@ class Rfc5424LayoutTest {
         final Rfc5424Layout layout = Rfc5424Layout.newBuilder().build();
         assertThat(layout.getLocalHostName()).isEqualTo(fqdn);
     }
+
+    private static Map<String, String> attributeMap(String... keyValuePairs) {
+        Map<String, String> result = new HashMap<>();
+        for (int i = 0; i < keyValuePairs.length; i += 2) {
+            result.put(keyValuePairs[i], keyValuePairs[i + 1]);
+        }
+        return result;
+    }
+
+    private static Rfc5424Layout buildRfc5424Layout(Map<String, String> 
attributes) {
+        PluginManager manager = new PluginManager(Node.CATEGORY);
+        manager.collectPlugins();
+
+        Node node = new Node();
+        node.getAttributes().putAll(attributes);
+
+        Object object = new 
PluginBuilder(manager.getPluginType("Rfc5424Layout"))
+                .withConfigurationNode(node)
+                .withConfiguration(new DefaultConfiguration())
+                .build();
+
+        assertThat(object).isInstanceOf(Rfc5424Layout.class);
+        return (Rfc5424Layout) object;
+    }
+
+    private static Stream<Arguments> 
testAcceptsDocumentedAttributesAndCompatibilityAliases() {
+        return Stream.of(
+                Arguments.of(
+                        "documented attributes",
+                        attributeMap(
+                                "newLine",
+                                "true",
+                                "newLineEscape",
+                                NEW_LINE_ESCAPE,
+                                "useTlsMessageFormat",
+                                "true",
+                                "mdcRequired",
+                                INCLUDED_KEYS)),
+                Arguments.of(
+                        "compatibility aliases",
+                        attributeMap(
+                                "includeNL",
+                                "true",
+                                "escapeNL",
+                                NEW_LINE_ESCAPE,
+                                "useTLSMessageFormat",
+                                "true",
+                                "required",
+                                INCLUDED_KEYS)));
+    }
+
+    @ParameterizedTest
+    @MethodSource
+    void testAcceptsDocumentedAttributesAndCompatibilityAliases(
+            String ignoredDisplayName, Map<String, String> attributes) {
+
+        Rfc5424Layout layout = buildRfc5424Layout(attributes);
+
+        assertThat(layout.isIncludeNewLine()).isTrue();
+        // The field contains Matcher.quote() escaped value, so we expect the 
backslash to be escaped.
+        assertThat(layout.getEscapeNewLine()).isEqualTo("\\\\n");
+        assertThat(layout.isUseTlsMessageFormat()).isTrue();
+        assertThat(layout.getMdcRequired()).containsExactly("key1", "key2", 
"locale");
+    }
+
+    private static Stream<Arguments> 
testAcceptsIncludeAttributesAndCompatibilityAliases() {
+        return Stream.of(
+                Arguments.of("documented attributes", 
attributeMap("mdcIncludes", INCLUDED_KEYS)),
+                Arguments.of("compatibility aliases", attributeMap("includes", 
INCLUDED_KEYS)));
+    }
+
+    @ParameterizedTest
+    @MethodSource
+    void testAcceptsIncludeAttributesAndCompatibilityAliases(
+            String ignoredDisplayName, Map<String, String> attributes) {
+
+        Rfc5424Layout layout = buildRfc5424Layout(attributes);
+
+        assertThat(layout.getMdcIncludes()).containsExactly("key1", "key2", 
"locale");
+        assertThat(layout.getMdcExcludes()).isNullOrEmpty();
+    }
+
+    private static Stream<Arguments> 
testAcceptsExcludeAttributesAndCompatibilityAliases() {
+        return Stream.of(
+                Arguments.of("documented attributes", 
attributeMap("mdcExcludes", EXCLUDED_KEYS)),
+                Arguments.of("compatibility aliases", attributeMap("excludes", 
EXCLUDED_KEYS)));
+    }
+
+    @ParameterizedTest
+    @MethodSource
+    void testAcceptsExcludeAttributesAndCompatibilityAliases(
+            String ignoredDisplayName, Map<String, String> attributes) {
+
+        Rfc5424Layout layout = buildRfc5424Layout(attributes);
+
+        assertThat(layout.getMdcExcludes()).containsExactly("key3", "key4");
+        assertThat(layout.getMdcIncludes()).isNullOrEmpty();
+    }
+
+    private static Stream<Arguments> testRejectsIncludesAndExcludesTogether() {
+        return Stream.of(
+                Arguments.of(
+                        "documented attributes",
+                        attributeMap("mdcIncludes", INCLUDED_KEYS, 
"mdcExcludes", EXCLUDED_KEYS)),
+                Arguments.of(
+                        "compatibility aliases", attributeMap("includes", 
INCLUDED_KEYS, "excludes", EXCLUDED_KEYS)));
+    }
+
+    @ParameterizedTest
+    @MethodSource
+    void testRejectsIncludesAndExcludesTogether(String ignoredDisplayName, 
Map<String, String> attributes) {
+
+        Rfc5424Layout layout = buildRfc5424Layout(attributes);
+
+        // If both includes and excludes are specified, the layout will ignore 
the includes and log an error about the
+        // invalid configuration.
+        assertThat(layout.getMdcExcludes()).containsExactly("key3", "key4");
+        assertThat(layout.getMdcIncludes()).isNullOrEmpty();
+    }
 }
diff --git 
a/log4j-core/src/main/java/org/apache/logging/log4j/core/layout/Rfc5424Layout.java
 
b/log4j-core/src/main/java/org/apache/logging/log4j/core/layout/Rfc5424Layout.java
index 256ce20f28..78873d0640 100644
--- 
a/log4j-core/src/main/java/org/apache/logging/log4j/core/layout/Rfc5424Layout.java
+++ 
b/log4j-core/src/main/java/org/apache/logging/log4j/core/layout/Rfc5424Layout.java
@@ -24,6 +24,7 @@ import java.util.GregorianCalendar;
 import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
+import java.util.Objects;
 import java.util.SortedMap;
 import java.util.TreeMap;
 import java.util.regex.Matcher;
@@ -467,6 +468,26 @@ public final class Rfc5424Layout extends 
AbstractStringLayout {
         return mdcIncludes;
     }
 
+    // Test-only
+    List<String> getMdcRequired() {
+        return mdcRequired;
+    }
+
+    // Test-only
+    boolean isIncludeNewLine() {
+        return includeNewLine;
+    }
+
+    // Test-only
+    String getEscapeNewLine() {
+        return escapeNewLine;
+    }
+
+    // Test-only
+    boolean isUseTlsMessageFormat() {
+        return useTlsMessageFormat;
+    }
+
     private String computeTimeStampString(final long now) {
         long last;
         synchronized (this) {
@@ -639,7 +660,7 @@ public final class Rfc5424Layout extends 
AbstractStringLayout {
      * @param loggerFields Container for the KeyValuePairs containing the 
patterns
      * @param config The Configuration. Some Converters require access to the 
Interpolator.
      * @return An Rfc5424Layout.
-     * @deprecated Use {@link Rfc5424LayoutBuilder instead}
+     * @deprecated Since 2.21.0 use {@link Rfc5424LayoutBuilder instead}
      */
     @Deprecated
     public static Rfc5424Layout createLayout(
@@ -689,65 +710,208 @@ public final class Rfc5424Layout extends 
AbstractStringLayout {
                 .build();
     }
 
+    /**
+     * @since 2.21.0
+     */
     @PluginBuilderFactory
     public static Rfc5424LayoutBuilder newBuilder() {
         return new Rfc5424LayoutBuilder();
     }
 
+    /**
+     * @since 2.21.0
+     */
     public static class Rfc5424LayoutBuilder extends 
AbstractStringLayout.Builder<Rfc5424LayoutBuilder>
             implements 
org.apache.logging.log4j.core.util.Builder<Rfc5424Layout> {
 
+        /**
+         * The name of the {@link Facility} as described in RFC 5424
+         *
+         * <p>The matching is case-insensitive. Defaults to {@code LOCAL0}.</p>
+         */
         @PluginBuilderAttribute
         private Facility facility = Facility.LOCAL0;
 
+        /**
+         * The default {@code SD-ID} as described in RFC 5424.
+         */
         @PluginBuilderAttribute
         private String id;
 
+        /**
+         * The enterprise number to include in {@code SD-ID} identifiers.
+         *
+         * <p>Can contain multiple integers separated by a dot, for example 
{@code 32473.1}</p>
+         *
+         * <p>Defaults to {@value #DEFAULT_ENTERPRISE_NUMBER}.</p>
+         */
         @PluginBuilderAttribute
         private String ein = String.valueOf(DEFAULT_ENTERPRISE_NUMBER);
 
+        /**
+         * The enterprise number to include in {@code SD-ID} identifiers.
+         *
+         * <p>Limited to a single integer.</p>
+         *
+         * <p>Defaults to {@value #DEFAULT_ENTERPRISE_NUMBER}.</p>
+         */
         @PluginBuilderAttribute
         private Integer enterpriseNumber;
 
+        /**
+         * Indicates whether data from the context map will be included as RFC 
5424 {@code SD-ELEMENT}.
+         *
+         * <p>Defaults to {@code true}.</p>
+         */
         @PluginBuilderAttribute
         private boolean includeMDC = true;
 
+        /**
+         * If {@code true}, a newline will be appended to the end of the 
syslog record.
+         *
+         * <p>Default is {@code false}.</p>
+         */
+        @SuppressWarnings("log4j.public.setter")
+        @PluginBuilderAttribute
+        private boolean newLine;
+
+        /**
+         * Same as {@code newLine}.
+         *
+         * <p>Erroneously introduced in version 2.21.0, but kept for 
compatibility.</p>
+         */
         @PluginBuilderAttribute
         private boolean includeNL;
 
+        /**
+         * If set, this string will be used to replace new lines within the 
message text.
+         *
+         * <p>By default, new lines are not escaped.</p>
+         */
+        @SuppressWarnings("log4j.public.setter")
+        @PluginBuilderAttribute
+        private String newLineEscape;
+
+        /**
+         * Same as {@code newLineEscape}.
+         *
+         * <p>Erroneously introduced in version 2.21.0, but kept for 
compatibility.</p>
+         */
         @PluginBuilderAttribute
         private String escapeNL;
 
+        /**
+         * The id to use for the MDC Structured Data Element.
+         *
+         * <p>Defaults to {@value #DEFAULT_MDCID}.</p>
+         */
         @PluginBuilderAttribute
         private String mdcId = DEFAULT_MDCID;
 
+        /**
+         * A prefix to add to MDC key names when formatting them as structured 
data parameters.
+         */
         @PluginBuilderAttribute
         private String mdcPrefix;
 
+        /**
+         * A prefix to add to event key names when formatting {@link 
StructuredDataMessage} fields.
+         */
         @PluginBuilderAttribute
         private String eventPrefix;
 
+        /**
+         * The value to use as the {@code APP-NAME} in the RFC 5424 syslog 
record.
+         */
         @PluginBuilderAttribute
         private String appName;
 
+        /**
+         * The default value to be used in the {@code MSGID} field of RFC 5424 
syslog records.
+         *
+         * <p>If the log event contains a {@link StructuredDataMessage}, the 
id from that message will be used
+         * instead.</p>
+         */
         @PluginBuilderAttribute
         private String messageId;
 
+        /**
+         * A comma separated list of MDC keys that should be excluded from the 
LogEvent.
+         *
+         * <p>Mutually exclusive with {@link #mdcIncludes}.</p>
+         */
+        @SuppressWarnings("log4j.public.setter")
+        @PluginBuilderAttribute
+        private String mdcExcludes;
+
+        /**
+         * Same as {@code mdcExcludes}.
+         *
+         * <p>Erroneously introduced in version 2.21.0, but kept for 
compatibility.</p>
+         */
         @PluginBuilderAttribute
         private String excludes;
 
+        /**
+         * A comma separated list of MDC keys that should be included in the 
LogEvent.
+         *
+         * <p>Mutually exclusive with {@link #mdcExcludes}.</p>
+         */
+        @SuppressWarnings("log4j.public.setter")
+        @PluginBuilderAttribute
+        private String mdcIncludes;
+
+        /**
+         * Same as {@code mdcIncludes}.
+         *
+         * <p>Erroneously introduced in version 2.21.0, but kept for 
compatibility.</p>
+         */
         @PluginBuilderAttribute
         private String includes;
 
+        /**
+         * A comma separated list of MDC keys that must be present in the MDC.
+         */
+        @SuppressWarnings("log4j.public.setter")
+        @PluginBuilderAttribute
+        private String mdcRequired;
+
+        /**
+         * Same as {@code mdcRequired}.
+         *
+         * <p>Erroneously introduced in version 2.21.0, but kept for 
compatibility.</p>
+         */
         @PluginBuilderAttribute
         private String required;
 
+        /**
+         * The pattern used to format exceptions appended to the syslog 
message.
+         */
         @PluginBuilderAttribute
         private String exceptionPattern;
 
+        /**
+         * If true the message will be formatted according to RFC 5425.
+         *
+         * <p>Default is {@code false}.</p>
+         */
+        @SuppressWarnings("log4j.public.setter")
+        @PluginBuilderAttribute
+        private boolean useTlsMessageFormat;
+
+        /**
+         * Same as {@code useTlsMessageFormat}.
+         *
+         * <p>Erroneously introduced in version 2.21.0, but kept for 
compatibility.</p>
+         */
         @PluginBuilderAttribute
         private boolean useTLSMessageFormat;
 
+        /**
+         * Optional additional {@code SD-ELEMENT}s.
+         *
+         * <p>Each {@link LoggerFields} entry contains a set of key/value 
patterns to produce structured data parameters.</p>
+         */
         @PluginElement(value = "loggerFields")
         private LoggerFields[] loggerFields;
 
@@ -860,9 +1024,12 @@ public final class Rfc5424Layout extends 
AbstractStringLayout {
 
         @Override
         public Rfc5424Layout build() {
-            if (includes != null && excludes != null) {
+            String effectiveIncludes = Objects.toString(mdcIncludes, includes);
+            String effectiveExcludes = Objects.toString(mdcExcludes, excludes);
+
+            if (effectiveIncludes != null && effectiveExcludes != null) {
                 LOGGER.error("mdcIncludes and mdcExcludes are mutually 
exclusive. Includes wil be ignored");
-                includes = null;
+                effectiveIncludes = null;
             }
 
             if (enterpriseNumber != null) {
@@ -880,19 +1047,19 @@ public final class Rfc5424Layout extends 
AbstractStringLayout {
                     id,
                     ein,
                     includeMDC,
-                    includeNL,
-                    escapeNL,
+                    newLine || includeNL,
+                    Objects.toString(newLineEscape, escapeNL),
                     mdcId,
                     mdcPrefix,
                     eventPrefix,
                     appName,
                     messageId,
-                    excludes,
-                    includes,
-                    required,
+                    effectiveExcludes,
+                    effectiveIncludes,
+                    Objects.toString(mdcRequired, required),
                     charset != null ? charset : StandardCharsets.UTF_8,
                     exceptionPattern,
-                    useTLSMessageFormat,
+                    useTlsMessageFormat || useTLSMessageFormat,
                     loggerFields);
         }
     }

Reply via email to