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:51 UTC
[logging-log4j2] 01/02: LOG4J2-3050 - Allow AdditionalFields to be
ignored if their value is null or a zero-length String.
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