You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@logging.apache.org by vy...@apache.org on 2020/11/13 15:19:24 UTC

[logging-log4j2] branch master updated: LOG4J2-2961 Fix reading of JsonTemplateLayout event additional fields from config.

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

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


The following commit(s) were added to refs/heads/master by this push:
     new 2ab61df  LOG4J2-2961 Fix reading of JsonTemplateLayout event additional fields from config.
2ab61df is described below

commit 2ab61df7be86ba57012e915d1fddfb00b63f34b1
Author: Volkan Yazıcı <vo...@gmail.com>
AuthorDate: Fri Nov 13 16:19:07 2020 +0100

    LOG4J2-2961 Fix reading of JsonTemplateLayout event additional fields from config.
---
 .../layout/template/json/JsonTemplateLayout.java   | 95 +++-------------------
 .../template/json/resolver/ExceptionResolver.java  |  2 +-
 .../log4j/layout/template/json/EcsLayoutTest.java  | 29 +++----
 .../log4j/layout/template/json/GelfLayoutTest.java | 19 ++---
 .../JsonTemplateLayoutAdditionalFieldTest.java     | 73 +++++++++++++++++
 .../template/json/JsonTemplateLayoutTest.java      |  7 +-
 .../log4j/layout/template/json/LogstashIT.java     | 50 +++++-------
 ...onalFieldEnrichedJsonTemplateLayoutLogging.xml} | 35 ++++----
 .../resources/gcFreeJsonTemplateLayoutLogging.xml  | 39 +++++----
 ...nullEventDelimitedJsonTemplateLayoutLogging.xml | 39 +++++----
 .../json/JsonTemplateLayoutBenchmarkState.java     | 45 +++++-----
 .../src/main/config-repo/log4j2.xml                | 34 ++++----
 src/changes/changes.xml                            |  3 +
 .../asciidoc/manual/json-template-layout.adoc.vm   |  9 +-
 src/site/markdown/manual/cloud.md                  | 83 +++++++++----------
 15 files changed, 262 insertions(+), 300 deletions(-)

diff --git a/log4j-layout-template-json/src/main/java/org/apache/logging/log4j/layout/template/json/JsonTemplateLayout.java b/log4j-layout-template-json/src/main/java/org/apache/logging/log4j/layout/template/json/JsonTemplateLayout.java
index f7c77ab..9b65f6d 100644
--- a/log4j-layout-template-json/src/main/java/org/apache/logging/log4j/layout/template/json/JsonTemplateLayout.java
+++ b/log4j-layout-template-json/src/main/java/org/apache/logging/log4j/layout/template/json/JsonTemplateLayout.java
@@ -43,7 +43,6 @@ import org.apache.logging.log4j.plugins.Plugin;
 import org.apache.logging.log4j.util.Strings;
 
 import java.nio.charset.Charset;
-import java.util.Arrays;
 import java.util.Collections;
 import java.util.Map;
 import java.util.Objects;
@@ -140,6 +139,10 @@ public class JsonTemplateLayout implements StringLayout {
         final int maxStringByteCount =
                 Math.toIntExact(Math.round(
                         maxByteCountPerChar * builder.maxStringLength));
+        final EventTemplateAdditionalField[] eventTemplateAdditionalFields =
+                builder.eventTemplateAdditionalFields != null
+                        ? builder.eventTemplateAdditionalFields
+                        : new EventTemplateAdditionalField[0];
         final EventResolverContext resolverContext = EventResolverContext
                 .newBuilder()
                 .setConfiguration(configuration)
@@ -151,7 +154,7 @@ public class JsonTemplateLayout implements StringLayout {
                 .setLocationInfoEnabled(builder.locationInfoEnabled)
                 .setStackTraceEnabled(builder.stackTraceEnabled)
                 .setStackTraceElementObjectResolver(stackTraceElementObjectResolver)
-                .setEventTemplateAdditionalFields(builder.eventTemplateAdditionalFields.additionalFields)
+                .setEventTemplateAdditionalFields(eventTemplateAdditionalFields)
                 .build();
         return TemplateResolvers.ofTemplate(resolverContext, eventTemplate);
     }
@@ -320,9 +323,8 @@ public class JsonTemplateLayout implements StringLayout {
         private String eventTemplateUri =
                 JsonTemplateLayoutDefaults.getEventTemplateUri();
 
-        @PluginElement("EventTemplateAdditionalFields")
-        private EventTemplateAdditionalFields eventTemplateAdditionalFields
-                = EventTemplateAdditionalFields.EMPTY;
+        @PluginElement("EventTemplateAdditionalField")
+        private EventTemplateAdditionalField[] eventTemplateAdditionalFields;
 
         @PluginBuilderAttribute
         private String stackTraceElementTemplate =
@@ -408,12 +410,12 @@ public class JsonTemplateLayout implements StringLayout {
             return this;
         }
 
-        public EventTemplateAdditionalFields getEventTemplateAdditionalFields() {
+        public EventTemplateAdditionalField[] getEventTemplateAdditionalFields() {
             return eventTemplateAdditionalFields;
         }
 
         public Builder setEventTemplateAdditionalFields(
-                final EventTemplateAdditionalFields eventTemplateAdditionalFields) {
+                final EventTemplateAdditionalField[] eventTemplateAdditionalFields) {
             this.eventTemplateAdditionalFields = eventTemplateAdditionalFields;
             return this;
         }
@@ -496,7 +498,6 @@ public class JsonTemplateLayout implements StringLayout {
                     throw new IllegalArgumentException(
                             "both eventTemplate and eventTemplateUri are blank");
             }
-            Objects.requireNonNull(eventTemplateAdditionalFields, "eventTemplateAdditionalFields");
             if (stackTraceEnabled &&
                     Strings.isBlank(stackTraceElementTemplate)
                     && Strings.isBlank(stackTraceElementTemplateUri)) {
@@ -514,78 +515,6 @@ public class JsonTemplateLayout implements StringLayout {
 
     }
 
-    // We need this ugly model and its builder just to be able to allow
-    // key-value pairs in a dedicated element.
-    @SuppressWarnings({"unused", "WeakerAccess"})
-    @Plugin(name = "EventTemplateAdditionalFields",
-            category = Node.CATEGORY,
-            printObject = true)
-    public static final class EventTemplateAdditionalFields {
-
-        private static final EventTemplateAdditionalFields EMPTY = newBuilder().build();
-
-        private final EventTemplateAdditionalField[] additionalFields;
-
-        private EventTemplateAdditionalFields(final Builder builder) {
-            this.additionalFields = builder.additionalFields != null
-                    ? builder.additionalFields
-                    : new EventTemplateAdditionalField[0];
-        }
-
-        public EventTemplateAdditionalField[] getAdditionalFields() {
-            return additionalFields;
-        }
-
-        @Override
-        public boolean equals(Object object) {
-            if (this == object) return true;
-            if (object == null || getClass() != object.getClass()) return false;
-            EventTemplateAdditionalFields that = (EventTemplateAdditionalFields) object;
-            return Arrays.equals(additionalFields, that.additionalFields);
-        }
-
-        @Override
-        public int hashCode() {
-            return Arrays.hashCode(additionalFields);
-        }
-
-        @Override
-        public String toString() {
-            return Arrays.toString(additionalFields);
-        }
-
-        @PluginBuilderFactory
-        public static Builder newBuilder() {
-            return new Builder();
-        }
-
-        public static class Builder
-                implements org.apache.logging.log4j.core.util.Builder<EventTemplateAdditionalFields> {
-
-            @PluginElement("AdditionalField")
-            private EventTemplateAdditionalField[] additionalFields;
-
-            private Builder() {}
-
-            public EventTemplateAdditionalField[] getAdditionalFields() {
-                return additionalFields;
-            }
-
-            public Builder setAdditionalFields(
-                    final EventTemplateAdditionalField[] additionalFields) {
-                this.additionalFields = additionalFields;
-                return this;
-            }
-
-            @Override
-            public EventTemplateAdditionalFields build() {
-                return new EventTemplateAdditionalFields(this);
-            }
-
-        }
-
-    }
-
     @Plugin(name = "EventTemplateAdditionalField",
             category = Node.CATEGORY,
             printObject = true)
@@ -648,13 +577,13 @@ public class JsonTemplateLayout implements StringLayout {
         public static class Builder
                 implements org.apache.logging.log4j.core.util.Builder<EventTemplateAdditionalField> {
 
-            @org.apache.logging.log4j.plugins.PluginBuilderAttribute
+            @PluginBuilderAttribute
             private String key;
 
-            @org.apache.logging.log4j.plugins.PluginBuilderAttribute
+            @PluginBuilderAttribute
             private String value;
 
-            @org.apache.logging.log4j.plugins.PluginBuilderAttribute
+            @PluginBuilderAttribute
             private Type type = Type.STRING;
 
             public Builder setKey(final String key) {
diff --git a/log4j-layout-template-json/src/main/java/org/apache/logging/log4j/layout/template/json/resolver/ExceptionResolver.java b/log4j-layout-template-json/src/main/java/org/apache/logging/log4j/layout/template/json/resolver/ExceptionResolver.java
index 4cd9b62..415104a 100644
--- a/log4j-layout-template-json/src/main/java/org/apache/logging/log4j/layout/template/json/resolver/ExceptionResolver.java
+++ b/log4j-layout-template-json/src/main/java/org/apache/logging/log4j/layout/template/json/resolver/ExceptionResolver.java
@@ -22,7 +22,7 @@ import org.apache.logging.log4j.layout.template.json.util.JsonWriter;
 
 /**
  * Exception resolver.
- *
+ * <p>
  * Note that this resolver is toggled by {@link
  * JsonTemplateLayout.Builder#setStackTraceEnabled(boolean)}.
  *
diff --git a/log4j-layout-template-json/src/test/java/org/apache/logging/log4j/layout/template/json/EcsLayoutTest.java b/log4j-layout-template-json/src/test/java/org/apache/logging/log4j/layout/template/json/EcsLayoutTest.java
index 21a87aa..6d3e89a 100644
--- a/log4j-layout-template-json/src/test/java/org/apache/logging/log4j/layout/template/json/EcsLayoutTest.java
+++ b/log4j-layout-template-json/src/test/java/org/apache/logging/log4j/layout/template/json/EcsLayoutTest.java
@@ -27,23 +27,18 @@ public class EcsLayoutTest {
             .setConfiguration(CONFIGURATION)
             .setEventTemplateUri("classpath:EcsLayout.json")
             .setEventTemplateAdditionalFields(
-                    JsonTemplateLayout
-                            .EventTemplateAdditionalFields
-                            .newBuilder()
-                            .setAdditionalFields(
-                                    new EventTemplateAdditionalField[]{
-                                            EventTemplateAdditionalField
-                                                    .newBuilder()
-                                                    .setKey("service.name")
-                                                    .setValue(SERVICE_NAME)
-                                                    .build(),
-                                            EventTemplateAdditionalField
-                                                    .newBuilder()
-                                                    .setKey("event.dataset")
-                                                    .setValue(EVENT_DATASET)
-                                                    .build()
-                                    })
-                            .build())
+                    new EventTemplateAdditionalField[]{
+                            EventTemplateAdditionalField
+                                    .newBuilder()
+                                    .setKey("service.name")
+                                    .setValue(SERVICE_NAME)
+                                    .build(),
+                            EventTemplateAdditionalField
+                                    .newBuilder()
+                                    .setKey("event.dataset")
+                                    .setValue(EVENT_DATASET)
+                                    .build()
+                    })
             .build();
 
     private static final EcsLayout ECS_LAYOUT = EcsLayout
diff --git a/log4j-layout-template-json/src/test/java/org/apache/logging/log4j/layout/template/json/GelfLayoutTest.java b/log4j-layout-template-json/src/test/java/org/apache/logging/log4j/layout/template/json/GelfLayoutTest.java
index 03ded3c..b13f939 100644
--- a/log4j-layout-template-json/src/test/java/org/apache/logging/log4j/layout/template/json/GelfLayoutTest.java
+++ b/log4j-layout-template-json/src/test/java/org/apache/logging/log4j/layout/template/json/GelfLayoutTest.java
@@ -27,18 +27,13 @@ public class GelfLayoutTest {
             .setConfiguration(CONFIGURATION)
             .setEventTemplateUri("classpath:GelfLayout.json")
             .setEventTemplateAdditionalFields(
-                    JsonTemplateLayout
-                            .EventTemplateAdditionalFields
-                            .newBuilder()
-                            .setAdditionalFields(
-                                    new EventTemplateAdditionalField[]{
-                                            EventTemplateAdditionalField
-                                                    .newBuilder()
-                                                    .setKey("host")
-                                                    .setValue(HOST_NAME)
-                                                    .build()
-                                    })
-                            .build())
+                    new EventTemplateAdditionalField[]{
+                            EventTemplateAdditionalField
+                                    .newBuilder()
+                                    .setKey("host")
+                                    .setValue(HOST_NAME)
+                                    .build()
+                    })
             .build();
 
     private static final GelfLayout GELF_LAYOUT = GelfLayout
diff --git a/log4j-layout-template-json/src/test/java/org/apache/logging/log4j/layout/template/json/JsonTemplateLayoutAdditionalFieldTest.java b/log4j-layout-template-json/src/test/java/org/apache/logging/log4j/layout/template/json/JsonTemplateLayoutAdditionalFieldTest.java
new file mode 100644
index 0000000..465bcc6
--- /dev/null
+++ b/log4j-layout-template-json/src/test/java/org/apache/logging/log4j/layout/template/json/JsonTemplateLayoutAdditionalFieldTest.java
@@ -0,0 +1,73 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache license, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the license for the specific language governing permissions and
+ * limitations under the license.
+ */
+package org.apache.logging.log4j.layout.template.json;
+
+import org.apache.logging.log4j.Logger;
+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.layout.template.json.util.JsonReader;
+import org.apache.logging.log4j.test.appender.ListAppender;
+import org.assertj.core.api.Assertions;
+import org.junit.jupiter.api.Test;
+
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
+import java.util.Map;
+
+@LoggerContextSource("additionalFieldEnrichedJsonTemplateLayoutLogging.xml")
+class JsonTemplateLayoutAdditionalFieldTest {
+
+    @Test
+    void test_additional_fields_are_resolved(
+            final LoggerContext loggerContext,
+            @Named("List") final ListAppender appender) {
+
+        // Log an event.
+        final Logger logger =
+                loggerContext.getLogger(
+                        JsonTemplateLayoutAdditionalFieldTest.class);
+        logger.info("trigger");
+
+        // Verify that the appender has logged the event.
+        final List<byte[]> serializedEvents = appender.getData();
+        Assertions.assertThat(serializedEvents).hasSize(1);
+
+        // Deserialize the serialized event.
+        final byte[] serializedEvent = serializedEvents.get(0);
+        final String serializedEventJson =
+                new String(
+                        serializedEvent,
+                        JsonTemplateLayoutDefaults.getCharset());
+        final Object serializedEventObject = JsonReader.read(serializedEventJson);
+        Assertions.assertThat(serializedEventObject).isInstanceOf(Map.class);
+        @SuppressWarnings("unchecked")
+        final Map<String, Object> serializedEventMap =
+                (Map<String, Object>) serializedEventObject;
+
+        // Verify the serialized additional fields.
+        Assertions
+                .assertThat(serializedEventMap)
+                .containsEntry("stringField", "string")
+                .containsEntry("numberField", 1)
+                .containsEntry("objectField", Collections.singletonMap("numberField", 1))
+                .containsEntry("listField", Arrays.asList(1, "two"));
+
+    }
+
+}
diff --git a/log4j-layout-template-json/src/test/java/org/apache/logging/log4j/layout/template/json/JsonTemplateLayoutTest.java b/log4j-layout-template-json/src/test/java/org/apache/logging/log4j/layout/template/json/JsonTemplateLayoutTest.java
index 2da177e..ec881c3 100644
--- a/log4j-layout-template-json/src/test/java/org/apache/logging/log4j/layout/template/json/JsonTemplateLayoutTest.java
+++ b/log4j-layout-template-json/src/test/java/org/apache/logging/log4j/layout/template/json/JsonTemplateLayoutTest.java
@@ -34,7 +34,6 @@ import org.apache.logging.log4j.core.lookup.MainMapLookup;
 import org.apache.logging.log4j.core.net.Severity;
 import org.apache.logging.log4j.core.time.MutableInstant;
 import org.apache.logging.log4j.layout.template.json.JsonTemplateLayout.EventTemplateAdditionalField;
-import org.apache.logging.log4j.layout.template.json.JsonTemplateLayout.EventTemplateAdditionalFields;
 import org.apache.logging.log4j.layout.template.json.util.JsonReader;
 import org.apache.logging.log4j.layout.template.json.util.JsonWriter;
 import org.apache.logging.log4j.layout.template.json.util.MapAccessor;
@@ -1221,7 +1220,7 @@ public class JsonTemplateLayoutTest {
         final String eventTemplate = "{}";
 
         // Create the layout.
-        final EventTemplateAdditionalField[] additionalFieldPairs = {
+        final EventTemplateAdditionalField[] additionalFields = {
                 EventTemplateAdditionalField
                         .newBuilder()
                         .setKey("number")
@@ -1240,10 +1239,6 @@ public class JsonTemplateLayoutTest {
                         .setType(EventTemplateAdditionalField.Type.JSON)
                         .build()
         };
-        final EventTemplateAdditionalFields additionalFields = EventTemplateAdditionalFields
-                .newBuilder()
-                .setAdditionalFields(additionalFieldPairs)
-                .build();
         final JsonTemplateLayout layout = JsonTemplateLayout
                 .newBuilder()
                 .setConfiguration(CONFIGURATION)
diff --git a/log4j-layout-template-json/src/test/java/org/apache/logging/log4j/layout/template/json/LogstashIT.java b/log4j-layout-template-json/src/test/java/org/apache/logging/log4j/layout/template/json/LogstashIT.java
index c803fc2..2e59743 100644
--- a/log4j-layout-template-json/src/test/java/org/apache/logging/log4j/layout/template/json/LogstashIT.java
+++ b/log4j-layout-template-json/src/test/java/org/apache/logging/log4j/layout/template/json/LogstashIT.java
@@ -80,18 +80,14 @@ public class LogstashIT {
             .setCharset(CHARSET)
             .setEventTemplateUri("classpath:GelfLayout.json")
             .setEventDelimiter("\0")
-            .setEventTemplateAdditionalFields(JsonTemplateLayout
-                    .EventTemplateAdditionalFields
-                    .newBuilder()
-                    .setAdditionalFields(
-                            new EventTemplateAdditionalField[]{
-                                    EventTemplateAdditionalField
-                                            .newBuilder()
-                                            .setKey("host")
-                                            .setValue(HOST_NAME)
-                                            .build()
-                            })
-                    .build())
+            .setEventTemplateAdditionalFields(
+                    new EventTemplateAdditionalField[]{
+                            EventTemplateAdditionalField
+                                    .newBuilder()
+                                    .setKey("host")
+                                    .setValue(HOST_NAME)
+                                    .build()
+                    })
             .build();
 
     private static final EcsLayout ECS_LAYOUT = EcsLayout
@@ -108,23 +104,19 @@ public class LogstashIT {
             .setCharset(CHARSET)
             .setEventTemplateUri("classpath:EcsLayout.json")
             .setRecyclerFactory(ThreadLocalRecyclerFactory.getInstance())
-            .setEventTemplateAdditionalFields(JsonTemplateLayout
-                    .EventTemplateAdditionalFields
-                    .newBuilder()
-                    .setAdditionalFields(
-                            new EventTemplateAdditionalField[]{
-                                    EventTemplateAdditionalField
-                                            .newBuilder()
-                                            .setKey("service.name")
-                                            .setValue(SERVICE_NAME)
-                                            .build(),
-                                    EventTemplateAdditionalField
-                                            .newBuilder()
-                                            .setKey("event.dataset")
-                                            .setValue(EVENT_DATASET)
-                                            .build()
-                            })
-                    .build())
+            .setEventTemplateAdditionalFields(
+                    new EventTemplateAdditionalField[]{
+                            EventTemplateAdditionalField
+                                    .newBuilder()
+                                    .setKey("service.name")
+                                    .setValue(SERVICE_NAME)
+                                    .build(),
+                            EventTemplateAdditionalField
+                                    .newBuilder()
+                                    .setKey("event.dataset")
+                                    .setValue(EVENT_DATASET)
+                                    .build()
+                    })
             .build();
 
     private static final int LOG_EVENT_COUNT = 100;
diff --git a/log4j-layout-template-json/src/test/resources/nullEventDelimitedJsonTemplateLayoutLogging.xml b/log4j-layout-template-json/src/test/resources/additionalFieldEnrichedJsonTemplateLayoutLogging.xml
similarity index 57%
copy from log4j-layout-template-json/src/test/resources/nullEventDelimitedJsonTemplateLayoutLogging.xml
copy to log4j-layout-template-json/src/test/resources/additionalFieldEnrichedJsonTemplateLayoutLogging.xml
index 39d87a9..3449c6d 100644
--- a/log4j-layout-template-json/src/test/resources/nullEventDelimitedJsonTemplateLayoutLogging.xml
+++ b/log4j-layout-template-json/src/test/resources/additionalFieldEnrichedJsonTemplateLayoutLogging.xml
@@ -14,26 +14,21 @@
  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  See the License for the specific language governing permissions and
  limitations under the License.
-
 -->
 <Configuration status="OFF">
-    <Appenders>
-        <Socket name="Socket"
-                host="localhost"
-                port="50514"
-                protocol="TCP"
-                ignoreExceptions="false"
-                reconnectionDelay="100"
-                immediateFlush="true">
-            <JsonTemplateLayout eventTemplate='{"$resolver": "message"}'
-                                eventDelimiter=""
-                                nullEventDelimiterEnabled="true"
-                                charset="US-ASCII"/>
-        </Socket>
-    </Appenders>
-    <Loggers>
-        <Root level="TRACE">
-            <AppenderRef ref="Socket"/>
-        </Root>
-    </Loggers>
+  <Appenders>
+    <List name="List" raw="true">
+      <JsonTemplateLayout>
+        <EventTemplateAdditionalField key="stringField" value="string"/>
+        <EventTemplateAdditionalField key="numberField" value="1" type="JSON"/>
+        <EventTemplateAdditionalField key="objectField" value='{"numberField":1}' type="JSON"/>
+        <EventTemplateAdditionalField key="listField" value='[1, "two"]' type="JSON"/>
+      </JsonTemplateLayout>
+    </List>
+  </Appenders>
+  <Loggers>
+    <Root level="TRACE">
+      <AppenderRef ref="List"/>
+    </Root>
+  </Loggers>
 </Configuration>
diff --git a/log4j-layout-template-json/src/test/resources/gcFreeJsonTemplateLayoutLogging.xml b/log4j-layout-template-json/src/test/resources/gcFreeJsonTemplateLayoutLogging.xml
index 245ff06..2a2bc82 100644
--- a/log4j-layout-template-json/src/test/resources/gcFreeJsonTemplateLayoutLogging.xml
+++ b/log4j-layout-template-json/src/test/resources/gcFreeJsonTemplateLayoutLogging.xml
@@ -14,26 +14,25 @@
  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  See the License for the specific language governing permissions and
  limitations under the License.
-
 -->
 <Configuration status="OFF">
-    <Appenders>
-        <Console name="Console" target="SYSTEM_OUT">
-            <PatternLayout pattern="%p %c{1.} [%t] %X{aKey} %X %m%ex%n" />
-        </Console>
-        <File name="File"
-              fileName="target/gcFreeJsonTemplateLayoutLogging.log"
-              bufferedIO="false"
-              append="false">
-            <JsonTemplateLayout recyclerFactory="threadLocal"/>
-        </File>
-    </Appenders>
-    <Loggers>
-        <Root level="trace" includeLocation="false">
-            <Property name="prop1">value1</Property>
-            <Property name="prop2">value2</Property>
-            <appender-ref ref="Console" level="FATAL"/>
-            <appender-ref ref="File"/>
-        </Root>
-    </Loggers>
+  <Appenders>
+    <Console name="Console" target="SYSTEM_OUT">
+      <PatternLayout pattern="%p %c{1.} [%t] %X{aKey} %X %m%ex%n"/>
+    </Console>
+    <File name="File"
+          fileName="target/gcFreeJsonTemplateLayoutLogging.log"
+          bufferedIO="false"
+          append="false">
+      <JsonTemplateLayout recyclerFactory="threadLocal"/>
+    </File>
+  </Appenders>
+  <Loggers>
+    <Root level="trace" includeLocation="false">
+      <Property name="prop1">value1</Property>
+      <Property name="prop2">value2</Property>
+      <appender-ref ref="Console" level="FATAL"/>
+      <appender-ref ref="File"/>
+    </Root>
+  </Loggers>
 </Configuration>
diff --git a/log4j-layout-template-json/src/test/resources/nullEventDelimitedJsonTemplateLayoutLogging.xml b/log4j-layout-template-json/src/test/resources/nullEventDelimitedJsonTemplateLayoutLogging.xml
index 39d87a9..547aac8 100644
--- a/log4j-layout-template-json/src/test/resources/nullEventDelimitedJsonTemplateLayoutLogging.xml
+++ b/log4j-layout-template-json/src/test/resources/nullEventDelimitedJsonTemplateLayoutLogging.xml
@@ -14,26 +14,25 @@
  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  See the License for the specific language governing permissions and
  limitations under the License.
-
 -->
 <Configuration status="OFF">
-    <Appenders>
-        <Socket name="Socket"
-                host="localhost"
-                port="50514"
-                protocol="TCP"
-                ignoreExceptions="false"
-                reconnectionDelay="100"
-                immediateFlush="true">
-            <JsonTemplateLayout eventTemplate='{"$resolver": "message"}'
-                                eventDelimiter=""
-                                nullEventDelimiterEnabled="true"
-                                charset="US-ASCII"/>
-        </Socket>
-    </Appenders>
-    <Loggers>
-        <Root level="TRACE">
-            <AppenderRef ref="Socket"/>
-        </Root>
-    </Loggers>
+  <Appenders>
+    <Socket name="Socket"
+            host="localhost"
+            port="50514"
+            protocol="TCP"
+            ignoreExceptions="false"
+            reconnectionDelay="100"
+            immediateFlush="true">
+      <JsonTemplateLayout eventTemplate='{"$resolver": "message"}'
+                          eventDelimiter=""
+                          nullEventDelimiterEnabled="true"
+                          charset="US-ASCII"/>
+    </Socket>
+  </Appenders>
+  <Loggers>
+    <Root level="TRACE">
+      <AppenderRef ref="Socket"/>
+    </Root>
+  </Loggers>
 </Configuration>
diff --git a/log4j-perf/src/main/java/org/apache/logging/log4j/layout/template/json/JsonTemplateLayoutBenchmarkState.java b/log4j-perf/src/main/java/org/apache/logging/log4j/layout/template/json/JsonTemplateLayoutBenchmarkState.java
index 067e510..7940550 100644
--- a/log4j-perf/src/main/java/org/apache/logging/log4j/layout/template/json/JsonTemplateLayoutBenchmarkState.java
+++ b/log4j-perf/src/main/java/org/apache/logging/log4j/layout/template/json/JsonTemplateLayoutBenchmarkState.java
@@ -26,7 +26,6 @@ import org.apache.logging.log4j.core.util.KeyValuePair;
 import org.apache.logging.log4j.core.util.NetUtils;
 import org.apache.logging.log4j.jackson.json.layout.JsonLayout;
 import org.apache.logging.log4j.layout.template.json.JsonTemplateLayout.EventTemplateAdditionalField;
-import org.apache.logging.log4j.layout.template.json.JsonTemplateLayout.EventTemplateAdditionalFields;
 import org.apache.logging.log4j.layout.template.json.util.ThreadLocalRecyclerFactory;
 import org.openjdk.jmh.annotations.Scope;
 import org.openjdk.jmh.annotations.State;
@@ -87,17 +86,14 @@ public class JsonTemplateLayoutBenchmarkState {
     }
 
     private static JsonTemplateLayout createJsonTemplateLayout4EcsLayout() {
-        final EventTemplateAdditionalFields additionalFields = EventTemplateAdditionalFields
-                .newBuilder()
-                .setAdditionalFields(
-                        new EventTemplateAdditionalField[]{
-                                EventTemplateAdditionalField
-                                        .newBuilder()
-                                        .setKey("service.name")
-                                        .setValue("benchmark")
-                                        .build()
-                        })
-                .build();
+        final EventTemplateAdditionalField[] additionalFields =
+                new EventTemplateAdditionalField[]{
+                        EventTemplateAdditionalField
+                                .newBuilder()
+                                .setKey("service.name")
+                                .setValue("benchmark")
+                                .build()
+                };
         return JsonTemplateLayout
                 .newBuilder()
                 .setConfiguration(CONFIGURATION)
@@ -115,20 +111,17 @@ public class JsonTemplateLayoutBenchmarkState {
                 .setCharset(CHARSET)
                 .setEventTemplateUri("classpath:GelfLayout.json")
                 .setRecyclerFactory(ThreadLocalRecyclerFactory.getInstance())
-                .setEventTemplateAdditionalFields(EventTemplateAdditionalFields
-                        .newBuilder()
-                        .setAdditionalFields(
-                                new EventTemplateAdditionalField[]{
-                                        // Adding "host" as a constant rather than using
-                                        // the "hostName" property lookup at runtime, which
-                                        // is what GelfLayout does as well.
-                                        EventTemplateAdditionalField
-                                                .newBuilder()
-                                                .setKey("host")
-                                                .setValue(NetUtils.getLocalHostname())
-                                                .build()
-                                })
-                        .build())
+                .setEventTemplateAdditionalFields(
+                        new EventTemplateAdditionalField[]{
+                                // Adding "host" as a constant rather than using
+                                // the "hostName" property lookup at runtime, which
+                                // is what GelfLayout does as well.
+                                EventTemplateAdditionalField
+                                        .newBuilder()
+                                        .setKey("host")
+                                        .setValue(NetUtils.getLocalHostname())
+                                        .build()
+                        })
                 .build();
     }
 
diff --git a/log4j-spring-cloud-config/log4j-spring-cloud-config-samples/log4j-spring-cloud-config-sample-server/src/main/config-repo/log4j2.xml b/log4j-spring-cloud-config/log4j-spring-cloud-config-samples/log4j-spring-cloud-config-sample-server/src/main/config-repo/log4j2.xml
index a2afa70..631399f 100644
--- a/log4j-spring-cloud-config/log4j-spring-cloud-config-samples/log4j-spring-cloud-config-sample-server/src/main/config-repo/log4j2.xml
+++ b/log4j-spring-cloud-config/log4j-spring-cloud-config-samples/log4j-spring-cloud-config-sample-server/src/main/config-repo/log4j2.xml
@@ -107,24 +107,22 @@
             protocol="tcp"
             bufferedIo="true">
       <JsonTemplateLayout eventTemplateUri="classpath:EnhancedGelf.json" nullEventDelimiterEnabled="true">
-        <EventTemplateAdditionalFields>
-          <EventTemplateAdditionalField key="containerId" value="\${docker:containerId:-}"/>
-          <EventTemplateAdditionalField key="application" value="\${lower:${spring:spring.application.name:-spring}}"/>
-          <EventTemplateAdditionalField key="kubernetes.serviceAccountName" value="\${k8s:accountName:-}"/>
-          <EventTemplateAdditionalField key="kubernetes.containerId" value="\${k8s:containerId:-}"/>
-          <EventTemplateAdditionalField key="kubernetes.containerName" value="\${k8s:containerName:-}"/>
-          <EventTemplateAdditionalField key="kubernetes.host" value="\${k8s:host:-}"/>
-          <EventTemplateAdditionalField key="kubernetes.labels.app" value="\${k8s:labels.app:-}"/>
-          <EventTemplateAdditionalField key="kubernetes.labels.pod-template-hash" value="\${k8s:labels.podTemplateHash:-}"/>
-          <EventTemplateAdditionalField key="kubernetes.master_url" value="\${k8s:masterUrl:-}"/>
-          <EventTemplateAdditionalField key="kubernetes.namespaceId" value="\${k8s:namespaceId:-}"/>
-          <EventTemplateAdditionalField key="kubernetes.namespaceName" value="\${k8s:namespaceName:-}"/>
-          <EventTemplateAdditionalField key="kubernetes.podID" value="\${k8s:podId:-}"/>
-          <EventTemplateAdditionalField key="kubernetes.podIP" value="\${k8s:podIp:-}"/>
-          <EventTemplateAdditionalField key="kubernetes.podName" value="\${k8s:podName:-}"/>
-          <EventTemplateAdditionalField key="kubernetes.imageId" value="\${k8s:imageId:-}"/>
-          <EventTemplateAdditionalField key="kubernetes.imageName" value="\${k8s:imageName:-}"/>
-        </EventTemplateAdditionalFields>
+        <EventTemplateAdditionalField key="containerId" value="\${docker:containerId:-}"/>
+        <EventTemplateAdditionalField key="application" value="\${lower:${spring:spring.application.name:-spring}}"/>
+        <EventTemplateAdditionalField key="kubernetes.serviceAccountName" value="\${k8s:accountName:-}"/>
+        <EventTemplateAdditionalField key="kubernetes.containerId" value="\${k8s:containerId:-}"/>
+        <EventTemplateAdditionalField key="kubernetes.containerName" value="\${k8s:containerName:-}"/>
+        <EventTemplateAdditionalField key="kubernetes.host" value="\${k8s:host:-}"/>
+        <EventTemplateAdditionalField key="kubernetes.labels.app" value="\${k8s:labels.app:-}"/>
+        <EventTemplateAdditionalField key="kubernetes.labels.pod-template-hash" value="\${k8s:labels.podTemplateHash:-}"/>
+        <EventTemplateAdditionalField key="kubernetes.master_url" value="\${k8s:masterUrl:-}"/>
+        <EventTemplateAdditionalField key="kubernetes.namespaceId" value="\${k8s:namespaceId:-}"/>
+        <EventTemplateAdditionalField key="kubernetes.namespaceName" value="\${k8s:namespaceName:-}"/>
+        <EventTemplateAdditionalField key="kubernetes.podID" value="\${k8s:podId:-}"/>
+        <EventTemplateAdditionalField key="kubernetes.podIP" value="\${k8s:podIp:-}"/>
+        <EventTemplateAdditionalField key="kubernetes.podName" value="\${k8s:podName:-}"/>
+        <EventTemplateAdditionalField key="kubernetes.imageId" value="\${k8s:imageId:-}"/>
+        <EventTemplateAdditionalField key="kubernetes.imageName" value="\${k8s:imageName:-}"/>
       </JsonTemplateLayout>
     </Socket>
     <!--<Socket name="Elastic" host="\${sys:logstash.search.host:-localhost}" port="12222" protocol="tcp" bufferedIo="true" ignoreExceptions="false">
diff --git a/src/changes/changes.xml b/src/changes/changes.xml
index e191147..9d1f949 100644
--- a/src/changes/changes.xml
+++ b/src/changes/changes.xml
@@ -31,6 +31,9 @@
          - "remove" - Removed
     -->
     <release version="3.0.0" date="2019-xx-xx" description="GA Release 3.0.0">
+      <action issue="LOG4J2-2961" dev="vy" type="fix">
+        Fix reading of JsonTemplateLayout event additional fields from config.
+      </action>
       <action issue="LOG4J2-2916" dev="vy" type="fix" due-to="wuqian0808">
         Avoid redundant Kafka producer instantiation causing thread leaks.
       </action>
diff --git a/src/site/asciidoc/manual/json-template-layout.adoc.vm b/src/site/asciidoc/manual/json-template-layout.adoc.vm
index d90f607..2536e9b 100644
--- a/src/site/asciidoc/manual/json-template-layout.adoc.vm
+++ b/src/site/asciidoc/manual/json-template-layout.adoc.vm
@@ -242,14 +242,13 @@ custom fields:
 [source,xml]
 ----
 <JsonTemplateLayout eventTemplateUri="classpath:GelfLayout.json">
-    <EventTemplateAdditionalFields>
-        <EventTemplateAdditionalField key="host" value="www.apache.org"/>
-        <EventTemplateAdditionalField key="_serviceName" value="auth-service"/>
-        <EventTemplateAdditionalField key="_containerId" value="6ede3f0ca7d9"/>
-    </EventTemplateAdditionalFields>
+  <EventTemplateAdditionalField key="host" value="www.apache.org"/>
+  <EventTemplateAdditionalField key="_serviceName" value="auth-service"/>
+  <EventTemplateAdditionalField key="_containerId" value="6ede3f0ca7d9"/>
 </JsonTemplateLayout>
 ----
 
+The default `type` for the added new fields are `String`.
 One can also pass JSON literals into additional fields:
 
 [source,xml]
diff --git a/src/site/markdown/manual/cloud.md b/src/site/markdown/manual/cloud.md
index 84fb63f..02b868b 100644
--- a/src/site/markdown/manual/cloud.md
+++ b/src/site/markdown/manual/cloud.md
@@ -140,28 +140,26 @@ will be formatted without newlines.
             port="12345"
             protocol="tcp"
             bufferedIo="true">
-        <JsonTemplateLayout eventTemplateUri="classpath:EcsLayout.json">
-            <EventTemplateAdditionalFields>
-                <EventTemplateAdditionalField key="containerId" value="${docker:containerId:-}"/>
-                <EventTemplateAdditionalField key="application" value="${lower:${spring:spring.application.name:-spring}}"/>
-                <EventTemplateAdditionalField key="kubernetes.serviceAccountName" value="${k8s:accountName:-}"/>
-                <EventTemplateAdditionalField key="kubernetes.containerId" value="${k8s:containerId:-}"/>
-                <EventTemplateAdditionalField key="kubernetes.containerName" value="${k8s:containerName:-}"/>
-                <EventTemplateAdditionalField key="kubernetes.host" value="${k8s:host:-}"/>
-                <EventTemplateAdditionalField key="kubernetes.labels.app" value="${k8s:labels.app:-}"/>
-                <EventTemplateAdditionalField key="kubernetes.labels.pod-template-hash" value="${k8s:labels.podTemplateHash:-}"/>
-                <EventTemplateAdditionalField key="kubernetes.master_url" value="${k8s:masterUrl:-}"/>
-                <EventTemplateAdditionalField key="kubernetes.namespaceId" value="${k8s:namespaceId:-}"/>
-                <EventTemplateAdditionalField key="kubernetes.namespaceName" value="${k8s:namespaceName:-}"/>
-                <EventTemplateAdditionalField key="kubernetes.podID" value="${k8s:podId:-}"/>
-                <EventTemplateAdditionalField key="kubernetes.podIP" value="${k8s:podIp:-}"/>
-                <EventTemplateAdditionalField key="kubernetes.podName" value="${k8s:podName:-}"/>
-                <EventTemplateAdditionalField key="kubernetes.imageId" value="${k8s:imageId:-}"/>
-                <EventTemplateAdditionalField key="kubernetes.imageName" value="${k8s:imageName:-}"/>
-            </EventTemplateAdditionalFields>
-        </JsonTemplateLayout>
+      <JsonTemplateLayout eventTemplateUri="classpath:EcsLayout.json">
+        <EventTemplateAdditionalField key="containerId" value="${docker:containerId:-}"/>
+        <EventTemplateAdditionalField key="application" value="${lower:${spring:spring.application.name:-spring}}"/>
+        <EventTemplateAdditionalField key="kubernetes.serviceAccountName" value="${k8s:accountName:-}"/>
+        <EventTemplateAdditionalField key="kubernetes.containerId" value="${k8s:containerId:-}"/>
+        <EventTemplateAdditionalField key="kubernetes.containerName" value="${k8s:containerName:-}"/>
+        <EventTemplateAdditionalField key="kubernetes.host" value="${k8s:host:-}"/>
+        <EventTemplateAdditionalField key="kubernetes.labels.app" value="${k8s:labels.app:-}"/>
+        <EventTemplateAdditionalField key="kubernetes.labels.pod-template-hash" value="${k8s:labels.podTemplateHash:-}"/>
+        <EventTemplateAdditionalField key="kubernetes.master_url" value="${k8s:masterUrl:-}"/>
+        <EventTemplateAdditionalField key="kubernetes.namespaceId" value="${k8s:namespaceId:-}"/>
+        <EventTemplateAdditionalField key="kubernetes.namespaceName" value="${k8s:namespaceName:-}"/>
+        <EventTemplateAdditionalField key="kubernetes.podID" value="${k8s:podId:-}"/>
+        <EventTemplateAdditionalField key="kubernetes.podIP" value="${k8s:podIp:-}"/>
+        <EventTemplateAdditionalField key="kubernetes.podName" value="${k8s:podName:-}"/>
+        <EventTemplateAdditionalField key="kubernetes.imageId" value="${k8s:imageId:-}"/>
+        <EventTemplateAdditionalField key="kubernetes.imageName" value="${k8s:imageName:-}"/>
+      </JsonTemplateLayout>
     </Socket>
-    
+
 The JsonTemplateLayout can also be used to generate JSON that matches the GELF specification which can     
 format the message attribute using a pattern in accordance with the PatternLayout. For example, the following
 template, named EnhancedGelf.json, can be used to generate GELF-compliant data that can be passed to Logstash. 
@@ -212,7 +210,7 @@ can be queried.
             "stringified": true
         }
     }
-    
+
 The logging configuration to use this template would be    
 
     <Socket name="Elastic"
@@ -221,33 +219,32 @@ The logging configuration to use this template would be
             protocol="tcp"
             bufferedIo="true">
       <JsonTemplateLayout eventTemplateUri="classpath:EnhancedGelf.json" nullEventDelimiterEnabled="true">
-        <EventTemplateAdditionalFields>
-          <EventTemplateAdditionalField key="containerId" value="${docker:containerId:-}"/>
-          <EventTemplateAdditionalField key="application" value="${lower:${spring:spring.application.name:-spring}}"/>
-          <EventTemplateAdditionalField key="kubernetes.serviceAccountName" value="${k8s:accountName:-}"/>
-          <EventTemplateAdditionalField key="kubernetes.containerId" value="${k8s:containerId:-}"/>
-          <EventTemplateAdditionalField key="kubernetes.containerName" value="${k8s:containerName:-}"/>
-          <EventTemplateAdditionalField key="kubernetes.host" value="${k8s:host:-}"/>
-          <EventTemplateAdditionalField key="kubernetes.labels.app" value="${k8s:labels.app:-}"/>
-          <EventTemplateAdditionalField key="kubernetes.labels.pod-template-hash" value="${k8s:labels.podTemplateHash:-}"/>
-          <EventTemplateAdditionalField key="kubernetes.master_url" value="${k8s:masterUrl:-}"/>
-          <EventTemplateAdditionalField key="kubernetes.namespaceId" value="${k8s:namespaceId:-}"/>
-          <EventTemplateAdditionalField key="kubernetes.namespaceName" value="${k8s:namespaceName:-}"/>
-          <EventTemplateAdditionalField key="kubernetes.podID" value="${k8s:podId:-}"/>
-          <EventTemplateAdditionalField key="kubernetes.podIP" value="${k8s:podIp:-}"/>
-          <EventTemplateAdditionalField key="kubernetes.podName" value="${k8s:podName:-}"/>
-          <EventTemplateAdditionalField key="kubernetes.imageId" value="${k8s:imageId:-}"/>
-          <EventTemplateAdditionalField key="kubernetes.imageName" value="${k8s:imageName:-}"/>
-        </EventTemplateAdditionalFields>
+        <EventTemplateAdditionalField key="containerId" value="${docker:containerId:-}"/>
+        <EventTemplateAdditionalField key="application" value="${lower:${spring:spring.application.name:-spring}}"/>
+        <EventTemplateAdditionalField key="kubernetes.serviceAccountName" value="${k8s:accountName:-}"/>
+        <EventTemplateAdditionalField key="kubernetes.containerId" value="${k8s:containerId:-}"/>
+        <EventTemplateAdditionalField key="kubernetes.containerName" value="${k8s:containerName:-}"/>
+        <EventTemplateAdditionalField key="kubernetes.host" value="${k8s:host:-}"/>
+        <EventTemplateAdditionalField key="kubernetes.labels.app" value="${k8s:labels.app:-}"/>
+        <EventTemplateAdditionalField key="kubernetes.labels.pod-template-hash" value="${k8s:labels.podTemplateHash:-}"/>
+        <EventTemplateAdditionalField key="kubernetes.master_url" value="${k8s:masterUrl:-}"/>
+        <EventTemplateAdditionalField key="kubernetes.namespaceId" value="${k8s:namespaceId:-}"/>
+        <EventTemplateAdditionalField key="kubernetes.namespaceName" value="${k8s:namespaceName:-}"/>
+        <EventTemplateAdditionalField key="kubernetes.podID" value="${k8s:podId:-}"/>
+        <EventTemplateAdditionalField key="kubernetes.podIP" value="${k8s:podIp:-}"/>
+        <EventTemplateAdditionalField key="kubernetes.podName" value="${k8s:podName:-}"/>
+        <EventTemplateAdditionalField key="kubernetes.imageId" value="${k8s:imageId:-}"/>
+        <EventTemplateAdditionalField key="kubernetes.imageName" value="${k8s:imageName:-}"/>
       </JsonTemplateLayout>
     </Socket>
+
 The significant difference with this configuration from the first example is that it references the 
 custom template and it specifies an event delimiter of a null character ('\0');   
-    
+
 Note: The level being passed with the above template does not strictly conform to the GELF spec as the
 Level being passed is the Log4j Level NOT the Level defined in the GELF spec. However, testing has shown 
 that Logstash, Elk, and Kibana are pretty tolerant of whatever data is passed to it.    
-    
+
 Finally, the GelfLayout can be used to generate GELF compliant output. Unlike the JsonTemplateLayout it 
 adheres closely to the GELF spec.    
 
@@ -299,7 +296,7 @@ and then forward these to (either console and/or) an Elasticsearch server.
       }
 
     }
-    
+
 When one of the GELF compliant formats is used Logstash should be configured as 
 
    gelf {