You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@logging.apache.org by rg...@apache.org on 2021/03/13 07:35:09 UTC

[logging-log4j2] branch release-2.x updated: LOG4J2-3041 - Allow GelfLayout to use PatternSelectors

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

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


The following commit(s) were added to refs/heads/release-2.x by this push:
     new 6f781e2  LOG4J2-3041 - Allow GelfLayout to use PatternSelectors
6f781e2 is described below

commit 6f781e2d3e82f9abfc55af819bdf9a52ff4b41f9
Author: Ralph Goers <rg...@apache.org>
AuthorDate: Sat Mar 13 00:34:47 2021 -0700

    LOG4J2-3041 - Allow GelfLayout to use PatternSelectors
---
 .../logging/log4j/core/layout/GelfLayout.java      |  23 +++
 .../{GelfLayoutTest2.java => GelfLayout2Test.java} |   4 +-
 .../{GelfLayoutTest3.java => GelfLayout3Test.java} |   6 +-
 ...st3.java => GelfLayoutPatternSelectorTest.java} |  38 +++-
 .../{GelfLayoutTest2.xml => GelfLayout2Test.xml}   |   0
 .../{GelfLayoutTest3.xml => GelfLayout3Test.xml}   |   0
 ...Test3.xml => GelfLayoutPatternSelectorTest.xml} |   6 +-
 src/changes/changes.xml                            |   4 +-
 src/site/xdoc/manual/layouts.xml.vm                | 221 +++++++++++----------
 9 files changed, 180 insertions(+), 122 deletions(-)

diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/layout/GelfLayout.java b/log4j-core/src/main/java/org/apache/logging/log4j/core/layout/GelfLayout.java
index 83a2550..43622e6 100644
--- a/log4j-core/src/main/java/org/apache/logging/log4j/core/layout/GelfLayout.java
+++ b/log4j-core/src/main/java/org/apache/logging/log4j/core/layout/GelfLayout.java
@@ -145,6 +145,8 @@ public final class GelfLayout extends AbstractStringLayout {
         @PluginBuilderAttribute
         private String messagePattern = null;
 
+        @PluginElement("PatternSelector")
+        private PatternSelector patternSelector = null;
 
         public Builder() {
             setCharset(StandardCharsets.UTF_8);
@@ -177,12 +179,23 @@ public final class GelfLayout extends AbstractStringLayout {
                 checker = ListChecker.NOOP_CHECKER;
             }
             PatternLayout patternLayout = null;
+            if (messagePattern != null && patternSelector != null) {
+                LOGGER.error("A message pattern and PatternSelector cannot both be specified on GelfLayout, "
+                        + "ignoring message pattern");
+                messagePattern = null;
+            }
             if (messagePattern != null) {
                 patternLayout = PatternLayout.newBuilder().withPattern(messagePattern)
                         .withAlwaysWriteExceptions(includeStacktrace)
                         .withConfiguration(getConfiguration())
                         .build();
             }
+            if (patternSelector != null) {
+                patternLayout = PatternLayout.newBuilder().withPatternSelector(patternSelector)
+                        .withAlwaysWriteExceptions(includeStacktrace)
+                        .withConfiguration(getConfiguration())
+                        .build();
+            }
             return new GelfLayout(getConfiguration(), host, additionalFields, compressionType, compressionThreshold,
                     includeStacktrace, includeThreadContext, includeNullDelimiter, includeNewLineDelimiter, checker,
                     patternLayout);
@@ -311,6 +324,16 @@ public final class GelfLayout extends AbstractStringLayout {
         }
 
         /**
+         * The PatternSelector to use to format the message.
+         * @param patternSelector the PatternSelector.
+         * @return this builder
+         */
+        public B setPatternSelector(final PatternSelector patternSelector) {
+            this.patternSelector = patternSelector;
+            return asBuilder();
+        }
+
+        /**
          * A comma separated list of thread context keys to include;
          * @param mdcIncludes the list of keys.
          * @return this builder
diff --git a/log4j-core/src/test/java/org/apache/logging/log4j/core/layout/GelfLayoutTest2.java b/log4j-core/src/test/java/org/apache/logging/log4j/core/layout/GelfLayout2Test.java
similarity index 96%
rename from log4j-core/src/test/java/org/apache/logging/log4j/core/layout/GelfLayoutTest2.java
rename to log4j-core/src/test/java/org/apache/logging/log4j/core/layout/GelfLayout2Test.java
index 2b3f429..f9f44f7 100644
--- a/log4j-core/src/test/java/org/apache/logging/log4j/core/layout/GelfLayoutTest2.java
+++ b/log4j-core/src/test/java/org/apache/logging/log4j/core/layout/GelfLayout2Test.java
@@ -30,8 +30,8 @@ import org.junit.jupiter.api.Test;
 
 import static org.junit.jupiter.api.Assertions.*;
 
-@LoggerContextSource("GelfLayoutTest2.xml")
-public class GelfLayoutTest2 {
+@LoggerContextSource("GelfLayout2Test.xml")
+public class GelfLayout2Test {
 
     @Test
     public void gelfLayout(final LoggerContext context, @Named final ListAppender list) throws IOException {
diff --git a/log4j-core/src/test/java/org/apache/logging/log4j/core/layout/GelfLayoutTest3.java b/log4j-core/src/test/java/org/apache/logging/log4j/core/layout/GelfLayout3Test.java
similarity index 94%
copy from log4j-core/src/test/java/org/apache/logging/log4j/core/layout/GelfLayoutTest3.java
copy to log4j-core/src/test/java/org/apache/logging/log4j/core/layout/GelfLayout3Test.java
index fc9a404..6ebd27c 100644
--- a/log4j-core/src/test/java/org/apache/logging/log4j/core/layout/GelfLayoutTest3.java
+++ b/log4j-core/src/test/java/org/apache/logging/log4j/core/layout/GelfLayout3Test.java
@@ -32,10 +32,10 @@ import java.io.IOException;
 
 import static org.junit.jupiter.api.Assertions.*;
 
-@LoggerContextSource("GelfLayoutTest3.xml")
+@LoggerContextSource("GelfLayout3Test.xml")
 @UsingAnyThreadContext
 @Tag("json")
-public class GelfLayoutTest3 {
+public class GelfLayout3Test {
 
     @Test
     public void gelfLayout(final LoggerContext context, @Named final ListAppender list) throws IOException {
@@ -55,7 +55,7 @@ public class GelfLayoutTest3 {
         assertNull(json.get("_requestId"));
         String message = json.get("full_message").asText();
         assertTrue(message.contains("loginId=rgoers"));
-        assertTrue(message.contains("GelfLayoutTest3"));
+        assertTrue(message.contains("GelfLayout3Test"));
     }
 
 }
diff --git a/log4j-core/src/test/java/org/apache/logging/log4j/core/layout/GelfLayoutTest3.java b/log4j-core/src/test/java/org/apache/logging/log4j/core/layout/GelfLayoutPatternSelectorTest.java
similarity index 62%
rename from log4j-core/src/test/java/org/apache/logging/log4j/core/layout/GelfLayoutTest3.java
rename to log4j-core/src/test/java/org/apache/logging/log4j/core/layout/GelfLayoutPatternSelectorTest.java
index fc9a404..f3f5712 100644
--- a/log4j-core/src/test/java/org/apache/logging/log4j/core/layout/GelfLayoutTest3.java
+++ b/log4j-core/src/test/java/org/apache/logging/log4j/core/layout/GelfLayoutPatternSelectorTest.java
@@ -16,26 +16,32 @@
  */
 package org.apache.logging.log4j.core.layout;
 
-import com.fasterxml.jackson.databind.JsonNode;
-import com.fasterxml.jackson.databind.ObjectMapper;
+import java.io.IOException;
+
 import org.apache.logging.log4j.Logger;
 import org.apache.logging.log4j.ThreadContext;
 import org.apache.logging.log4j.core.LoggerContext;
 import org.apache.logging.log4j.junit.LoggerContextSource;
 import org.apache.logging.log4j.junit.Named;
 import org.apache.logging.log4j.junit.UsingAnyThreadContext;
+import org.apache.logging.log4j.spi.AbstractLogger;
 import org.apache.logging.log4j.test.appender.ListAppender;
 import org.junit.jupiter.api.Tag;
 import org.junit.jupiter.api.Test;
 
-import java.io.IOException;
+import com.fasterxml.jackson.databind.JsonNode;
+import com.fasterxml.jackson.databind.ObjectMapper;
 
-import static org.junit.jupiter.api.Assertions.*;
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertFalse;
+import static org.junit.jupiter.api.Assertions.assertNotNull;
+import static org.junit.jupiter.api.Assertions.assertNull;
+import static org.junit.jupiter.api.Assertions.assertTrue;
 
-@LoggerContextSource("GelfLayoutTest3.xml")
+@LoggerContextSource("GelfLayoutPatternSelectorTest.xml")
 @UsingAnyThreadContext
 @Tag("json")
-public class GelfLayoutTest3 {
+public class GelfLayoutPatternSelectorTest {
 
     @Test
     public void gelfLayout(final LoggerContext context, @Named final ListAppender list) throws IOException {
@@ -44,9 +50,10 @@ public class GelfLayoutTest3 {
         ThreadContext.put("loginId", "rgoers");
         ThreadContext.put("internalId", "12345");
         logger.info("My Test Message");
-        final String gelf = list.getMessages().get(0);
+        logger.info(AbstractLogger.FLOW_MARKER, "My Test Message");
+        String gelf = list.getMessages().get(0);
         final ObjectMapper mapper = new ObjectMapper();
-        final JsonNode json = mapper.readTree(gelf);
+        JsonNode json = mapper.readTree(gelf);
         assertEquals("My Test Message", json.get("short_message").asText());
         assertEquals("myhost", json.get("host").asText());
         assertNotNull(json.get("_loginId"));
@@ -54,8 +61,21 @@ public class GelfLayoutTest3 {
         assertNull(json.get("_internalId"));
         assertNull(json.get("_requestId"));
         String message = json.get("full_message").asText();
+        assertFalse(message.contains("====="));
+        assertTrue(message.contains("loginId=rgoers"));
+        assertTrue(message.contains("GelfLayoutPatternSelectorTest"));
+        gelf = list.getMessages().get(1);
+        json = mapper.readTree(gelf);
+        assertEquals("My Test Message", json.get("short_message").asText());
+        assertEquals("myhost", json.get("host").asText());
+        assertNotNull(json.get("_loginId"));
+        assertEquals("rgoers", json.get("_loginId").asText());
+        assertNull(json.get("_internalId"));
+        assertNull(json.get("_requestId"));
+        message = json.get("full_message").asText();
+        assertTrue(message.contains("====="));
         assertTrue(message.contains("loginId=rgoers"));
-        assertTrue(message.contains("GelfLayoutTest3"));
+        assertTrue(message.contains("GelfLayoutPatternSelectorTest"));
     }
 
 }
diff --git a/log4j-core/src/test/resources/GelfLayoutTest2.xml b/log4j-core/src/test/resources/GelfLayout2Test.xml
similarity index 100%
rename from log4j-core/src/test/resources/GelfLayoutTest2.xml
rename to log4j-core/src/test/resources/GelfLayout2Test.xml
diff --git a/log4j-core/src/test/resources/GelfLayoutTest3.xml b/log4j-core/src/test/resources/GelfLayout3Test.xml
similarity index 100%
copy from log4j-core/src/test/resources/GelfLayoutTest3.xml
copy to log4j-core/src/test/resources/GelfLayout3Test.xml
diff --git a/log4j-core/src/test/resources/GelfLayoutTest3.xml b/log4j-core/src/test/resources/GelfLayoutPatternSelectorTest.xml
similarity index 78%
rename from log4j-core/src/test/resources/GelfLayoutTest3.xml
rename to log4j-core/src/test/resources/GelfLayoutPatternSelectorTest.xml
index fcb23d3..7fe50f6 100644
--- a/log4j-core/src/test/resources/GelfLayoutTest3.xml
+++ b/log4j-core/src/test/resources/GelfLayoutPatternSelectorTest.xml
@@ -16,11 +16,13 @@
  limitations under the License.
 
 -->
-<Configuration status="WARN" name="GelfLayoutTest3">
+<Configuration status="WARN" name="GelfLayoutPatternSelectorTest">
   <Appenders>
     <List name="list">
       <GelfLayout host="myhost" includeThreadContext="true" threadContextIncludes="requestId,loginId">
-        <MessagePattern>%d [%t] %-5p %X{requestId, loginId} %C{1.}.%M:%L - %m%n"</MessagePattern>
+        <MarkerPatternSelector defaultPattern="%d [%t] %-5p %X{requestId, loginId} %C{1.}.%M:%L - %m%n">
+          <PatternMatch key="FLOW" pattern="[%-5level] %X{requestId, loginId} ====== %C{1.}.%M:%L %msg ======%n"/>
+        </MarkerPatternSelector>
         <KeyValuePair key="foo" value="FOO"/>
         <KeyValuePair key="runtime" value="$${java:runtime}"/>
       </GelfLayout>
diff --git a/src/changes/changes.xml b/src/changes/changes.xml
index 2bd31fc..1c4ffad 100644
--- a/src/changes/changes.xml
+++ b/src/changes/changes.xml
@@ -30,7 +30,9 @@
          - "remove" - Removed
     -->
     <release version="2.15.0" date="2021-MM-DD" description="GA Release 2.15.0">
-
+      <action issue="LOG4J2-3041" dev="rgoers" type="update">
+        Allow a PatternSelector to be specified on GelfLayout.
+      </action>
     </release>
     <release version="2.14.1" date="2021-03-06" description="GA Release 2.14.1">
       <!-- FIXES -->
diff --git a/src/site/xdoc/manual/layouts.xml.vm b/src/site/xdoc/manual/layouts.xml.vm
index 52b415d..e8f6368 100644
--- a/src/site/xdoc/manual/layouts.xml.vm
+++ b/src/site/xdoc/manual/layouts.xml.vm
@@ -186,107 +186,6 @@ logger.debug("one={}, two={}, three={}", 1, 2, 3);
             Additional <a href="../runtime-dependencies.html">runtime dependencies</a> are required for using CSV layouts.
           </p>
         </subsection>
-        <a name="JsonTemplateLayout"/>
-        <subsection name="JSON Template Layout">
-          <p>
-            <code>JsonTemplateLayout</code> is a customizable, efficient, and
-            garbage-free JSON emitting layout. It encodes <code>LogEvent</code>s
-            according to the structure described by the JSON template provided.
-            For instance, given the following JSON template modelling the
-            <a href="https://github.com/logstash/log4j-jsonevent-layout">the
-            official Logstash <code>JSONEventLayoutV1</code></a>
-          </p>
-          <pre class="prettyprint linenums"><![CDATA[
-{
-  "mdc": {
-    "${dollar}resolver": "mdc"
-  },
-  "exception": {
-    "exception_class": {
-      "${dollar}resolver": "exception",
-      "field": "className"
-    },
-    "exception_message": {
-      "${dollar}resolver": "exception",
-      "field": "message",
-      "stringified": true
-    },
-    "stacktrace": {
-      "${dollar}resolver": "exception",
-      "field": "stackTrace",
-      "stringified": true
-    }
-  },
-  "line_number": {
-    "${dollar}resolver": "source",
-    "field": "lineNumber"
-  },
-  "class": {
-    "${dollar}resolver": "source",
-    "field": "className"
-  },
-  "@version": 1,
-  "source_host": "${dollar}{hostName}",
-  "message": {
-    "${dollar}resolver": "message",
-    "stringified": true
-  },
-  "thread_name": {
-    "${dollar}resolver": "thread",
-    "field": "name"
-  },
-  "@timestamp": {
-    "${dollar}resolver": "timestamp"
-  },
-  "level": {
-    "${dollar}resolver": "level",
-    "field": "name"
-  },
-  "file": {
-    "${dollar}resolver": "source",
-    "field": "fileName"
-  },
-  "method": {
-    "${dollar}resolver": "source",
-    "field": "methodName"
-  },
-  "logger_name": {
-    "${dollar}resolver": "logger",
-    "field": "name"
-  }
-}]]></pre>
-          <p>
-            in combination with the below Log4j configuration:
-          </p>
-          <pre class="prettyprint linenums"><![CDATA[
-<JsonTemplateLayout eventTemplateUri="classpath:LogstashJsonEventLayoutV1.json"/>]]></pre>
-          <p>
-            JSON Template Layout will render JSON documents as follows:
-          </p>
-          <pre class="prettyprint linenums"><![CDATA[
-{
-  "exception": {
-    "exception_class": "java.lang.RuntimeException",
-    "exception_message": "test",
-    "stacktrace": "java.lang.RuntimeException: test\n\tat org.apache.logging.log4j.JsonTemplateLayoutDemo.main(JsonTemplateLayoutDemo.java:11)\n"
-  },
-  "line_number": 12,
-  "class": "org.apache.logging.log4j.JsonTemplateLayoutDemo",
-  "@version": 1,
-  "source_host": "varlik",
-  "message": "Hello, error!",
-  "thread_name": "main",
-  "@timestamp": "2017-05-25T19:56:23.370+02:00",
-  "level": "ERROR",
-  "file": "JsonTemplateLayoutDemo.java",
-  "method": "main",
-  "logger_name": "org.apache.logging.log4j.JsonTemplateLayoutDemo"
-}]]></pre>
-          <p>
-            See <a href="json-template-layout.html">JSON Template Layout</a>
-            page for the complete documentation.
-          </p>
-        </subsection>
         <a name="GELFLayout"/>
         <subsection name="GELF Layout">
           <!-- From Javadoc of org.apache.logging.log4j.core.layout.GelfLayout -->
@@ -359,9 +258,20 @@ logger.debug("one={}, two={}, three={}", 1, 2, 3);
             <tr>
               <td>messagePattern</td>
               <td>String</td>
-              <td>The pattern to use to format the String. If not supplied only the text derived from the logging
-              message will be used. See <a href="#PatternLayout">PatternLayout</a> for information on the pattern
-              strings.</td>
+              <td>The pattern to use to format the String. A messagePattern and patternSelector cannot both be
+                specified. If both are present the message pattern will be ignored and an error will be logged.
+                If not supplied only the text derived from the logging message will be used. See
+                <a href="#PatternLayout">PatternLayout</a> for information on the pattern strings.</td>
+            </tr>
+            <tr>
+              <td>patternSelector</td>
+              <td>PatternSelector</td>
+              <td>The PatternSelector to use to format the String. A messagePattern and patternSelector cannot both be
+                specified. If both are present the message pattern will be ignored and an error will be logged.
+                If not supplied only the text derived from the logging message will be used.
+                See <a href="#PatternSelectors">PatternSelectors</a> for information on how to specify a
+                PatternSelector.
+                See <a href="#PatternLayout">PatternLayout</a> for information on the pattern strings.</td>
             </tr>
             <tr>
               <td>threadContextExcludes</td>
@@ -657,6 +567,107 @@ logger.debug("one={}, two={}, three={}", 1, 2, 3);
             Additional <a href="../runtime-dependencies.html">runtime dependencies</a> are required for using JsonLayout.
           </p>
         </subsection>
+        <a name="JsonTemplateLayout"/>
+        <subsection name="JSON Template Layout">
+          <p>
+            <code>JsonTemplateLayout</code> is a customizable, efficient, and
+            garbage-free JSON emitting layout. It encodes <code>LogEvent</code>s
+            according to the structure described by the JSON template provided.
+            For instance, given the following JSON template modelling the
+            <a href="https://github.com/logstash/log4j-jsonevent-layout">the
+              official Logstash <code>JSONEventLayoutV1</code></a>
+          </p>
+          <pre class="prettyprint linenums"><![CDATA[
+{
+  "mdc": {
+    "${dollar}resolver": "mdc"
+  },
+  "exception": {
+    "exception_class": {
+      "${dollar}resolver": "exception",
+      "field": "className"
+    },
+    "exception_message": {
+      "${dollar}resolver": "exception",
+      "field": "message",
+      "stringified": true
+    },
+    "stacktrace": {
+      "${dollar}resolver": "exception",
+      "field": "stackTrace",
+      "stringified": true
+    }
+  },
+  "line_number": {
+    "${dollar}resolver": "source",
+    "field": "lineNumber"
+  },
+  "class": {
+    "${dollar}resolver": "source",
+    "field": "className"
+  },
+  "@version": 1,
+  "source_host": "${dollar}{hostName}",
+  "message": {
+    "${dollar}resolver": "message",
+    "stringified": true
+  },
+  "thread_name": {
+    "${dollar}resolver": "thread",
+    "field": "name"
+  },
+  "@timestamp": {
+    "${dollar}resolver": "timestamp"
+  },
+  "level": {
+    "${dollar}resolver": "level",
+    "field": "name"
+  },
+  "file": {
+    "${dollar}resolver": "source",
+    "field": "fileName"
+  },
+  "method": {
+    "${dollar}resolver": "source",
+    "field": "methodName"
+  },
+  "logger_name": {
+    "${dollar}resolver": "logger",
+    "field": "name"
+  }
+}]]></pre>
+          <p>
+            in combination with the below Log4j configuration:
+          </p>
+          <pre class="prettyprint linenums"><![CDATA[
+<JsonTemplateLayout eventTemplateUri="classpath:LogstashJsonEventLayoutV1.json"/>]]></pre>
+          <p>
+            JSON Template Layout will render JSON documents as follows:
+          </p>
+          <pre class="prettyprint linenums"><![CDATA[
+{
+  "exception": {
+    "exception_class": "java.lang.RuntimeException",
+    "exception_message": "test",
+    "stacktrace": "java.lang.RuntimeException: test\n\tat org.apache.logging.log4j.JsonTemplateLayoutDemo.main(JsonTemplateLayoutDemo.java:11)\n"
+  },
+  "line_number": 12,
+  "class": "org.apache.logging.log4j.JsonTemplateLayoutDemo",
+  "@version": 1,
+  "source_host": "varlik",
+  "message": "Hello, error!",
+  "thread_name": "main",
+  "@timestamp": "2017-05-25T19:56:23.370+02:00",
+  "level": "ERROR",
+  "file": "JsonTemplateLayoutDemo.java",
+  "method": "main",
+  "logger_name": "org.apache.logging.log4j.JsonTemplateLayoutDemo"
+}]]></pre>
+          <p>
+            See <a href="json-template-layout.html">JSON Template Layout</a>
+            page for the complete documentation.
+          </p>
+        </subsection>
         <a name="PatternLayout"/>
         <subsection name="Pattern Layout">
           <p>A flexible layout configurable with pattern string. The goal of this class is to format a LogEvent and
@@ -2138,7 +2149,7 @@ at org.apache.logging.log4j.core.pattern.ExtendedThrowableTest.testException(Ext
           <pre class="prettyprint linenums"><![CDATA[<PatternLayout>
   <pattern>%d %highlight{%p} %style{%C{1.} [%t] %m}{bold,green}%n</pattern>
 </PatternLayout>]]></pre>
-
+        <a name="PatternSelectors"/>
         <h4>Pattern Selectors</h4>
         <p>
           The PatternLayout can be configured with a PatternSelector to allow it to choose a pattern to use based on