JsonLayout - support expressions in AdditionalFields programatically

Added test from LOF4J2-1694


Project: http://git-wip-us.apache.org/repos/asf/logging-log4j2/repo
Commit: http://git-wip-us.apache.org/repos/asf/logging-log4j2/commit/bf42c2f1
Tree: http://git-wip-us.apache.org/repos/asf/logging-log4j2/tree/bf42c2f1
Diff: http://git-wip-us.apache.org/repos/asf/logging-log4j2/diff/bf42c2f1

Branch: refs/heads/master
Commit: bf42c2f17ef53342edc6e707f960569aae347b65
Parents: abf1c58
Author: Michal Dvorak <[email protected]>
Authored: Wed Sep 13 10:42:36 2017 +0200
Committer: Mikael StÃ¥ldal <[email protected]>
Committed: Tue Sep 26 21:53:52 2017 +0200

----------------------------------------------------------------------
 .../logging/log4j/core/layout/JsonLayout.java   | 66 +++++++++++++++-----
 .../log4j/core/layout/JsonLayoutTest.java       | 22 +++++++
 2 files changed, 73 insertions(+), 15 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/bf42c2f1/log4j-core/src/main/java/org/apache/logging/log4j/core/layout/JsonLayout.java
----------------------------------------------------------------------
diff --git 
a/log4j-core/src/main/java/org/apache/logging/log4j/core/layout/JsonLayout.java 
b/log4j-core/src/main/java/org/apache/logging/log4j/core/layout/JsonLayout.java
index 51f0b5b..1e9d431 100644
--- 
a/log4j-core/src/main/java/org/apache/logging/log4j/core/layout/JsonLayout.java
+++ 
b/log4j-core/src/main/java/org/apache/logging/log4j/core/layout/JsonLayout.java
@@ -20,7 +20,6 @@ import java.io.IOException;
 import java.io.Writer;
 import java.nio.charset.Charset;
 import java.nio.charset.StandardCharsets;
-import java.util.Collections;
 import java.util.HashMap;
 import java.util.LinkedHashMap;
 import java.util.Map;
@@ -36,6 +35,7 @@ import org.apache.logging.log4j.core.config.plugins.Plugin;
 import org.apache.logging.log4j.core.config.plugins.PluginBuilderAttribute;
 import org.apache.logging.log4j.core.config.plugins.PluginBuilderFactory;
 import org.apache.logging.log4j.core.config.plugins.PluginElement;
+import org.apache.logging.log4j.core.lookup.StrSubstitutor;
 import org.apache.logging.log4j.core.util.KeyValuePair;
 
 /**
@@ -78,7 +78,7 @@ public final class JsonLayout extends AbstractJacksonLayout {
 
     static final String CONTENT_TYPE = "application/json";
 
-    protected final Map<String, Object> additionalFields;
+    protected final ResolvableKeyValuePair[] additionalFields;
 
     public static class Builder<B extends Builder<B>> extends 
AbstractJacksonLayout.Builder<B>
             implements org.apache.logging.log4j.core.util.Builder<JsonLayout> {
@@ -139,14 +139,14 @@ public final class JsonLayout extends 
AbstractJacksonLayout {
                 
PatternLayout.newSerializerBuilder().setConfiguration(config).setPattern(footerPattern).setDefaultPattern(DEFAULT_FOOTER).build(),
                 false);
 
-        additionalFields = null;
+        this.additionalFields = new ResolvableKeyValuePair[0];
     }
 
     private JsonLayout(final Configuration config, final boolean locationInfo, 
final boolean properties,
                        final boolean encodeThreadContextAsList,
                        final boolean complete, final boolean compact, final 
boolean eventEol,
-                       final String headerPattern,final String footerPattern, 
final Charset charset,
-                       final boolean includeStacktrace,final boolean 
stacktraceAsString,
+                       final String headerPattern, final String footerPattern, 
final Charset charset,
+                       final boolean includeStacktrace, final boolean 
stacktraceAsString,
                        final boolean includeNullDelimiter,
                        final KeyValuePair[] additionalFields) {
         super(config, new JacksonFactory.JSON(encodeThreadContextAsList, 
includeStacktrace, stacktraceAsString).newWriter(
@@ -157,15 +157,17 @@ public final class JsonLayout extends 
AbstractJacksonLayout {
                 includeNullDelimiter);
 
         if (additionalFields != null && additionalFields.length > 0) {
-            final Map<String, Object> additionalFieldsMap = new 
LinkedHashMap<>();
+            // Convert to specific class which already determines whether 
values needs lookup during serialization
+            final ResolvableKeyValuePair[] resolvableFields = new 
ResolvableKeyValuePair[additionalFields.length];
 
-            for (KeyValuePair pair : additionalFields) {
-                additionalFieldsMap.put(pair.getKey(), pair.getValue());
+            for (int i = 0; i < additionalFields.length; i++) {
+                resolvableFields[i] = new 
ResolvableKeyValuePair(additionalFields[i]);
             }
 
-            this.additionalFields = 
Collections.unmodifiableMap(additionalFieldsMap);
+            this.additionalFields = resolvableFields;
         } else {
-            this.additionalFields = null;
+            // No fields set
+            this.additionalFields = new ResolvableKeyValuePair[0];
         }
     }
 
@@ -300,19 +302,40 @@ public final class JsonLayout extends 
AbstractJacksonLayout {
     protected Object wrapLogEvent(LogEvent event) {
         Object result = super.wrapLogEvent(event);
 
-        if (additionalFields != null) {
-            return new LogEventWithAdditionalFields(result, additionalFields);
+        if (additionalFields.length > 0) {
+            // Construct map for serialization - note that we are 
intentionally using original LogEvent
+            Map<String, String> additionalFieldsMap = 
resolveAdditionalFields(event);
+            // This class combines LogEvent with AdditionalFields during 
serialization
+            return new LogEventWithAdditionalFields(result, 
additionalFieldsMap);
         } else {
+            // No additional fields, return original object
             return result;
         }
     }
 
+    private Map<String,String> resolveAdditionalFields(LogEvent logEvent) {
+        final Map<String,String> additionalFieldsMap = new LinkedHashMap<>();
+        final StrSubstitutor strSubstitutor = 
configuration.getStrSubstitutor();
+
+        for (ResolvableKeyValuePair pair : additionalFields) {
+            if (pair.valueNeedsLookup) {
+                // Resolve value
+                additionalFieldsMap.put(pair.key, 
strSubstitutor.replace(logEvent, pair.value));
+            } else {
+                // Plain text value
+                additionalFieldsMap.put(pair.key, pair.value);
+            }
+        }
+
+        return additionalFieldsMap;
+    }
+
     public static class LogEventWithAdditionalFields {
 
         private final Object logEvent;
-        private final Map<String, Object> additionalFields;
+        private final Map<String, String> additionalFields;
 
-        public LogEventWithAdditionalFields(Object logEvent, Map<String, 
Object> additionalFields) {
+        public LogEventWithAdditionalFields(Object logEvent, Map<String, 
String> additionalFields) {
             this.logEvent = logEvent;
             this.additionalFields = additionalFields;
         }
@@ -323,8 +346,21 @@ public final class JsonLayout extends 
AbstractJacksonLayout {
         }
 
         @JsonAnyGetter
-        public Map<String, Object> getAdditionalFields() {
+        public Map<String, String> getAdditionalFields() {
             return additionalFields;
         }
     }
+
+    private static class ResolvableKeyValuePair {
+
+        final String key;
+        final String value;
+        final boolean valueNeedsLookup;
+
+        ResolvableKeyValuePair(KeyValuePair pair) {
+            this.key = pair.getKey();
+            this.value = pair.getValue();
+            this.valueNeedsLookup = this.value != null && 
this.value.contains("${");
+        }
+    }
 }

http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/bf42c2f1/log4j-core/src/test/java/org/apache/logging/log4j/core/layout/JsonLayoutTest.java
----------------------------------------------------------------------
diff --git 
a/log4j-core/src/test/java/org/apache/logging/log4j/core/layout/JsonLayoutTest.java
 
b/log4j-core/src/test/java/org/apache/logging/log4j/core/layout/JsonLayoutTest.java
index 82c9eea..225a3b7 100644
--- 
a/log4j-core/src/test/java/org/apache/logging/log4j/core/layout/JsonLayoutTest.java
+++ 
b/log4j-core/src/test/java/org/apache/logging/log4j/core/layout/JsonLayoutTest.java
@@ -37,6 +37,8 @@ import org.apache.logging.log4j.core.config.Configuration;
 import org.apache.logging.log4j.core.config.ConfigurationFactory;
 import org.apache.logging.log4j.core.impl.Log4jLogEvent;
 import org.apache.logging.log4j.core.jackson.Log4jJsonObjectMapper;
+import org.apache.logging.log4j.core.lookup.JavaLookup;
+import org.apache.logging.log4j.core.util.KeyValuePair;
 import org.apache.logging.log4j.message.SimpleMessage;
 import org.apache.logging.log4j.spi.AbstractLogger;
 import org.apache.logging.log4j.test.appender.ListAppender;
@@ -346,6 +348,26 @@ public class JsonLayoutTest {
     }
 
     @Test
+    public void testAdditionalFields() throws Exception {
+        final AbstractJacksonLayout layout = JsonLayout.newBuilder()
+                .setLocationInfo(false)
+                .setProperties(false)
+                .setComplete(false)
+                .setCompact(true)
+                .setEventEol(false)
+                .setIncludeStacktrace(false)
+                .setAdditionalFields(new KeyValuePair[] {
+                    new KeyValuePair("KEY1", "VALUE1"),
+                    new KeyValuePair("KEY2", "${java:runtime}"), })
+                .setCharset(StandardCharsets.UTF_8)
+                .setConfiguration(ctx.getConfiguration())
+                .build();
+        final String str = 
layout.toSerializable(LogEventFixtures.createLogEvent());
+        assertTrue(str, str.contains("\"KEY1\":\"VALUE1\""));
+        assertTrue(str, str.contains("\"KEY2\":\"" + new 
JavaLookup().getRuntime() + "\""));
+    }
+
+    @Test
     public void testLocationOffCompactOffMdcOff() throws Exception {
         this.testAllFeatures(false, false, false, false, false, true);
     }

Reply via email to