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/21 20:34:50 UTC

[logging-log4j2] branch master updated (62804d5 -> 6703c50)

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

rgoers pushed a change to branch master
in repository https://gitbox.apache.org/repos/asf/logging-log4j2.git.


    from 62804d5  LOG4J2-3049 - Allow MapMessage and ThreadContext attributes to be prefixed
     new 0eecfd0  LOG4J2-3050 - Allow AdditionalFields to be ignored if their value is null or a zero-length String.
     new 6703c50  Make adding a prefix garbage free

The 2 revisions listed above as "new" are entirely new to this
repository and will be described in separate emails.  The revisions
listed as "add" were already present in the repository and have only
been added to this reference.


Summary of changes:
 .../org/apache/logging/log4j/util/Strings.java     | 34 ++++++++++++++++---
 .../org/apache/logging/log4j/util/StringsTest.java |  9 +++++
 .../logging/log4j/core/layout/GelfLayout.java      | 38 ++++++++++++++--------
 .../logging/log4j/core/layout/GelfLayout3Test.java | 13 +++++---
 log4j-core/src/test/resources/GelfLayout3Test.xml  |  3 +-
 src/changes/changes.xml                            |  3 ++
 src/site/asciidoc/manual/layouts.adoc              |  6 ++++
 7 files changed, 83 insertions(+), 23 deletions(-)

[logging-log4j2] 02/02: Make adding a prefix garbage free

Posted by rg...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

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

commit 6703c50be807b807b1b859bfb5ca33609c6b8036
Author: Ralph Goers <rg...@apache.org>
AuthorDate: Sun Mar 21 13:34:34 2021 -0700

    Make adding a prefix garbage free
---
 .../org/apache/logging/log4j/util/Strings.java     | 34 +++++++++++++++++++---
 .../org/apache/logging/log4j/util/StringsTest.java |  9 ++++++
 .../logging/log4j/core/layout/GelfLayout.java      |  2 +-
 3 files changed, 40 insertions(+), 5 deletions(-)

diff --git a/log4j-api/src/main/java/org/apache/logging/log4j/util/Strings.java b/log4j-api/src/main/java/org/apache/logging/log4j/util/Strings.java
index be50ab8..1a1cded 100644
--- a/log4j-api/src/main/java/org/apache/logging/log4j/util/Strings.java
+++ b/log4j-api/src/main/java/org/apache/logging/log4j/util/Strings.java
@@ -28,6 +28,8 @@ import java.util.Optional;
  */
 public final class Strings {
 
+    private static final ThreadLocal<StringBuilder> tempStr = ThreadLocal.withInitial(StringBuilder::new);
+
     /**
      * The empty string.
      */
@@ -296,6 +298,26 @@ public final class Strings {
     }
 
     /**
+     * Concatenates 2 Strings without allocation.
+     * @param str1 the first string.
+     * @param str2 the second string.
+     * @return the concatenated String.
+     */
+    public static String concat(String str1, String str2) {
+        if (isEmpty(str1)) {
+            return str2;
+        } else if (isEmpty(str2)) {
+            return str1;
+        }
+        StringBuilder sb = tempStr.get();
+        try {
+            return sb.append(str1).append(str2).toString();
+        } finally {
+            sb.setLength(0);
+        }
+    }
+
+    /**
      * Creates a new string repeating given {@code str} {@code count} times.
      * @param str input string
      * @param count the repetition count
@@ -307,11 +329,15 @@ public final class Strings {
         if (count < 0) {
             throw new IllegalArgumentException("count");
         }
-        StringBuilder sb = new StringBuilder(str.length() * count);
-        for (int index = 0; index < count; index++) {
-            sb.append(str);
+        StringBuilder sb = tempStr.get();
+        try {
+            for (int index = 0; index < count; index++) {
+                sb.append(str);
+            }
+            return sb.toString();
+        } finally {
+            sb.setLength(0);
         }
-        return sb.toString();
     }
 
 }
diff --git a/log4j-api/src/test/java/org/apache/logging/log4j/util/StringsTest.java b/log4j-api/src/test/java/org/apache/logging/log4j/util/StringsTest.java
index cfd5127..78b797a 100644
--- a/log4j-api/src/test/java/org/apache/logging/log4j/util/StringsTest.java
+++ b/log4j-api/src/test/java/org/apache/logging/log4j/util/StringsTest.java
@@ -56,6 +56,15 @@ public class StringsTest {
     }
 
     @Test
+    public void testConcat() {
+        assertEquals("ab", Strings.concat("a", "b"));
+        assertEquals("a", Strings.concat("a", ""));
+        assertEquals("a", Strings.concat("a", null));
+        assertEquals("b", Strings.concat("", "b"));
+        assertEquals("b", Strings.concat(null, "b"));
+    }
+
+    @Test
     public void testJoin() {
         assertNull(Strings.join((Iterable<?>) null, '.'));
         assertNull(Strings.join((Iterator<?>) null, '.'));
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 3f87622..ab5a63b 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
@@ -654,7 +654,7 @@ public final class GelfLayout extends AbstractStringLayout {
             String stringValue = String.valueOf(value);
             if (checker.check(key) && (Strings.isNotEmpty(stringValue) || !omitEmptyFields)) {
                 stringBuilder.append(QU);
-                JsonUtils.quoteAsString(prefix + key, stringBuilder);
+                JsonUtils.quoteAsString(Strings.concat(prefix, key), stringBuilder);
                 stringBuilder.append("\":\"");
                 JsonUtils.quoteAsString(toNullSafeString(stringValue), stringBuilder);
                 stringBuilder.append(QC);

[logging-log4j2] 01/02: LOG4J2-3050 - Allow AdditionalFields to be ignored if their value is null or a zero-length String.

Posted by rg...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

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

commit 0eecfd0955d353cb4125d94ee433549750a06531
Author: Ralph Goers <rg...@apache.org>
AuthorDate: Sun Mar 21 10:25:12 2021 -0700

    LOG4J2-3050 - Allow AdditionalFields to be ignored if their value is null or a zero-length String.
---
 .../logging/log4j/core/layout/GelfLayout.java      | 36 ++++++++++++++--------
 .../logging/log4j/core/layout/GelfLayout3Test.java | 13 +++++---
 log4j-core/src/test/resources/GelfLayout3Test.xml  |  3 +-
 src/changes/changes.xml                            |  3 ++
 src/site/asciidoc/manual/layouts.adoc              |  6 ++++
 5 files changed, 43 insertions(+), 18 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 fb52cf1..3f87622 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
@@ -107,6 +107,7 @@ public final class GelfLayout extends AbstractStringLayout {
     private final boolean includeMapMessage;
     private final boolean includeNullDelimiter;
     private final boolean includeNewLineDelimiter;
+    private final boolean omitEmptyFields;
     private final PatternLayout layout;
     private final FieldWriter mdcWriter;
     private final FieldWriter mapWriter;
@@ -154,6 +155,9 @@ public final class GelfLayout extends AbstractStringLayout {
         private boolean includeMapMessage = true;
 
         @PluginBuilderAttribute
+        private boolean omitEmptyFields = false;
+
+        @PluginBuilderAttribute
         private String messagePattern = null;
 
         @PluginBuilderAttribute
@@ -194,7 +198,8 @@ public final class GelfLayout extends AbstractStringLayout {
             }
             return new GelfLayout(getConfiguration(), host, additionalFields, compressionType, compressionThreshold,
                     includeStacktrace, includeThreadContext, includeMapMessage, includeNullDelimiter,
-                    includeNewLineDelimiter, mdcChecker, mapChecker, patternLayout, threadContextPrefix, mapPrefix);
+                    includeNewLineDelimiter, omitEmptyFields, mdcChecker, mapChecker, patternLayout,
+                    threadContextPrefix, mapPrefix);
         }
 
         private ListChecker createChecker(String excludes, String includes) {
@@ -435,8 +440,9 @@ public final class GelfLayout extends AbstractStringLayout {
     private GelfLayout(final Configuration config, final String host, final KeyValuePair[] additionalFields,
             final CompressionType compressionType, final int compressionThreshold, final boolean includeStacktrace,
             final boolean includeThreadContext, final boolean includeMapMessage, final boolean includeNullDelimiter,
-            final boolean includeNewLineDelimiter, final ListChecker mdcChecker, final ListChecker mapChecker,
-            final PatternLayout patternLayout, final String mdcPrefix, final String mapPrefix) {
+            final boolean includeNewLineDelimiter, final boolean omitEmptyFields, final ListChecker mdcChecker,
+            final ListChecker mapChecker, final PatternLayout patternLayout, final String mdcPrefix,
+            final String mapPrefix) {
         super(config, StandardCharsets.UTF_8, null, null);
         this.host = host != null ? host : NetUtils.getLocalHostname();
         this.additionalFields = additionalFields != null ? additionalFields : new KeyValuePair[0];
@@ -454,6 +460,7 @@ public final class GelfLayout extends AbstractStringLayout {
         this.includeMapMessage = includeMapMessage;
         this.includeNullDelimiter = includeNullDelimiter;
         this.includeNewLineDelimiter = includeNewLineDelimiter;
+        this.omitEmptyFields = omitEmptyFields;
         if (includeNullDelimiter && compressionType != CompressionType.OFF) {
             throw new IllegalArgumentException("null delimiter cannot be used with compression");
         }
@@ -568,14 +575,16 @@ public final class GelfLayout extends AbstractStringLayout {
         if (additionalFields.length > 0) {
             final StrSubstitutor strSubstitutor = getConfiguration().getStrSubstitutor();
             for (final KeyValuePair additionalField : additionalFields) {
-                builder.append(QU);
-                JsonUtils.quoteAsString(additionalField.getKey(), builder);
-                builder.append("\":\"");
                 final String value = valueNeedsLookup(additionalField.getValue())
-                    ? strSubstitutor.replace(event, additionalField.getValue())
-                    : additionalField.getValue();
-                JsonUtils.quoteAsString(toNullSafeString(value), builder);
-                builder.append(QC);
+                        ? strSubstitutor.replace(event, additionalField.getValue())
+                        : additionalField.getValue();
+                if (Strings.isNotEmpty(value) || !omitEmptyFields) {
+                    builder.append(QU);
+                    JsonUtils.quoteAsString(additionalField.getKey(), builder);
+                    builder.append("\":\"");
+                    JsonUtils.quoteAsString(toNullSafeString(value), builder);
+                    builder.append(QC);
+                }
             }
         }
         if (includeThreadContext) {
@@ -631,7 +640,7 @@ public final class GelfLayout extends AbstractStringLayout {
         return value != null && value.contains("${");
     }
 
-    private static class FieldWriter implements TriConsumer<String, Object, StringBuilder> {
+    private class FieldWriter implements TriConsumer<String, Object, StringBuilder> {
         private final ListChecker checker;
         private final String prefix;
 
@@ -642,11 +651,12 @@ public final class GelfLayout extends AbstractStringLayout {
 
         @Override
         public void accept(final String key, final Object value, final StringBuilder stringBuilder) {
-            if (checker.check(key)) {
+            String stringValue = String.valueOf(value);
+            if (checker.check(key) && (Strings.isNotEmpty(stringValue) || !omitEmptyFields)) {
                 stringBuilder.append(QU);
                 JsonUtils.quoteAsString(prefix + key, stringBuilder);
                 stringBuilder.append("\":\"");
-                JsonUtils.quoteAsString(toNullSafeString(String.valueOf(value)), stringBuilder);
+                JsonUtils.quoteAsString(toNullSafeString(stringValue), stringBuilder);
                 stringBuilder.append(QC);
             }
         }
diff --git a/log4j-core/src/test/java/org/apache/logging/log4j/core/layout/GelfLayout3Test.java b/log4j-core/src/test/java/org/apache/logging/log4j/core/layout/GelfLayout3Test.java
index 7bd367b..e7f1b1d 100644
--- a/log4j-core/src/test/java/org/apache/logging/log4j/core/layout/GelfLayout3Test.java
+++ b/log4j-core/src/test/java/org/apache/logging/log4j/core/layout/GelfLayout3Test.java
@@ -57,8 +57,10 @@ public class GelfLayout3Test {
         String message = json.get("full_message").asText();
         assertTrue(message.contains("loginId=rgoers"));
         assertTrue(message.contains("GelfLayout3Test"));
-        assertNull(json.get("arg1"));
-        assertNull(json.get("arg2"));
+        assertNull(json.get("_map.arg1"));
+        assertNull(json.get("_map.arg2"));
+        assertNull(json.get("_empty"));
+        assertEquals("FOO", json.get("_foo").asText());
     }
 
     @Test
@@ -69,13 +71,14 @@ public class GelfLayout3Test {
         ThreadContext.put("internalId", "12345");
         StringMapMessage message = new StringMapMessage();
         message.put("arg1", "test1");
+        message.put("arg2", "");
         message.put("arg3", "test3");
         message.put("message", "My Test Message");
         logger.info(message);
         final String gelf = list.getMessages().get(0);
         final ObjectMapper mapper = new ObjectMapper();
         final JsonNode json = mapper.readTree(gelf);
-        assertEquals("arg1=\"test1\" arg3=\"test3\" message=\"My Test Message\"",
+        assertEquals("arg1=\"test1\" arg2=\"\" arg3=\"test3\" message=\"My Test Message\"",
                 json.get("short_message").asText());
         assertEquals("myhost", json.get("host").asText());
         assertNotNull(json.get("_mdc.loginId"));
@@ -86,9 +89,11 @@ public class GelfLayout3Test {
         assertTrue(msg.contains("loginId=rgoers"));
         assertTrue(msg.contains("GelfLayout3Test"));
         assertTrue(msg.contains("arg1=\"test1\""));
-        assertNull(json.get("map.arg2"));
+        assertNull(json.get("_map.arg2"));
         assertEquals("test1", json.get("_map.arg1").asText());
         assertEquals("test3", json.get("_map.arg3").asText());
+        assertNull(json.get("_empty"));
+        assertEquals("FOO", json.get("_foo").asText());
     }
 
 }
diff --git a/log4j-core/src/test/resources/GelfLayout3Test.xml b/log4j-core/src/test/resources/GelfLayout3Test.xml
index 6851ae1..d30622f 100644
--- a/log4j-core/src/test/resources/GelfLayout3Test.xml
+++ b/log4j-core/src/test/resources/GelfLayout3Test.xml
@@ -20,10 +20,11 @@
   <Appenders>
     <List name="list">
       <GelfLayout host="myhost" includeThreadContext="true" threadContextIncludes="requestId,loginId"
-                  mapMessageIncludes="arg1,arg2,arg3" threadContextPrefix="mdc." mapPrefix="map.">
+             mapMessageIncludes="arg1,arg2,arg3" threadContextPrefix="mdc." mapPrefix="map." omitEmptyFields="true">
         <MessagePattern>%d [%t] %-5p %X{requestId, loginId} %C{1.}.%M:%L - %m%n"</MessagePattern>
         <KeyValuePair key="foo" value="FOO"/>
         <KeyValuePair key="runtime" value="$${java:runtime}"/>
+        <KeyValuePair key="empty" value="${test:-}"/>
       </GelfLayout>
     </List>
   </Appenders>
diff --git a/src/changes/changes.xml b/src/changes/changes.xml
index ca116b0..5d947bb 100644
--- a/src/changes/changes.xml
+++ b/src/changes/changes.xml
@@ -169,6 +169,9 @@
       </action>
     </release>
     <release version="2.15.0" date="2021-MM-DD" description="GA Release 2.15.0">
+      <action issue="LOG4J2-3050" dev="rgoers" type="add">
+        Allow AdditionalFields to be ignored if their value is null or a zero-length String.
+      </action>
       <action issue="LOG4J2-3049" dev="rgoers" type="add">
         Allow MapMessage and ThreadContext attributes to be prefixed.
       </action>
diff --git a/src/site/asciidoc/manual/layouts.adoc b/src/site/asciidoc/manual/layouts.adoc
index 6ae5df8..95ec2db 100644
--- a/src/site/asciidoc/manual/layouts.adoc
+++ b/src/site/asciidoc/manual/layouts.adoc
@@ -254,6 +254,12 @@ specified. If both are present the message pattern will be ignored and an error
 If not supplied only the text derived from the logging message will be used. See
 link:#PatternLayout[PatternLayout]  for information on the pattern strings.
 
+|omitEmptyFields
+|boolean
+|If true fields which are null or are zero-length strings will not be included as a field in
+the Gelf JSON. This setting will not affect whether those fields appear in the message fields. The
+default value is false.
+
 |patternSelector
 |PatternSelector
 |The PatternSelector to use to format the String. A messagePattern and patternSelector cannot both be