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/30 09:00:43 UTC
[logging-log4j2] branch master updated: LOG4J2-2967 Fix JTL
parameter resolution for messages with too few parameters. (#443)
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 d728835 LOG4J2-2967 Fix JTL parameter resolution for messages with too few parameters. (#443)
d728835 is described below
commit d7288357f89e01d6d4877ba202e4153dcc31d723
Author: Carter Kozak <ck...@apache.org>
AuthorDate: Mon Nov 30 03:55:51 2020 -0500
LOG4J2-2967 Fix JTL parameter resolution for messages with too few parameters. (#443)
---
.../json/resolver/MessageParameterResolver.java | 14 +-
.../template/json/JsonTemplateLayoutTest.java | 282 ++++++++++++---------
src/changes/changes.xml | 3 +
3 files changed, 181 insertions(+), 118 deletions(-)
diff --git a/log4j-layout-template-json/src/main/java/org/apache/logging/log4j/layout/template/json/resolver/MessageParameterResolver.java b/log4j-layout-template-json/src/main/java/org/apache/logging/log4j/layout/template/json/resolver/MessageParameterResolver.java
index 059e5b1..866b3d1 100644
--- a/log4j-layout-template-json/src/main/java/org/apache/logging/log4j/layout/template/json/resolver/MessageParameterResolver.java
+++ b/log4j-layout-template-json/src/main/java/org/apache/logging/log4j/layout/template/json/resolver/MessageParameterResolver.java
@@ -111,8 +111,13 @@ final class MessageParameterResolver implements EventResolver {
// Short-circuit if there are no parameters.
final Object[] parameters = message.getParameters();
- if (parameters == null || parameters.length == 0) {
- jsonWriter.writeNull();
+ if (parameters == null || parameters.length == 0 || index >= parameters.length) {
+ if (index < 0) {
+ jsonWriter.writeArrayStart();
+ jsonWriter.writeArrayEnd();
+ } else {
+ jsonWriter.writeNull();
+ }
return;
}
@@ -162,12 +167,17 @@ final class MessageParameterResolver implements EventResolver {
if (arrayNeeded) {
jsonWriter.writeArrayStart();
}
+ final StringBuilder buf = jsonWriter.getStringBuilder();
+ final int startIndex = buf.length();
parameterConsumerState.resolver = this;
parameterConsumerState.jsonWriter = jsonWriter;
parameterVisitable.forEachParameter(
PARAMETER_CONSUMER, parameterConsumerState);
if (arrayNeeded) {
jsonWriter.writeArrayEnd();
+ } else if (startIndex == buf.length()) {
+ // Handle the case in which index was not present in the event.
+ jsonWriter.writeNull();
}
} finally {
parameterConsumerStateRecycler.release(parameterConsumerState);
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 a7503fc..f8d4990 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
@@ -225,10 +225,10 @@ class JsonTemplateLayoutTest {
final String timestampFieldName = "@timestamp";
final String staticFieldName = "staticFieldName";
final String staticFieldValue = "staticFieldValue";
- final String eventTemplate = writeJson(Map(
- timestampFieldName, Map(
+ final String eventTemplate = writeJson(asMap(
+ timestampFieldName, asMap(
"$resolver", "timestamp",
- "pattern", Map("timeZone", "Europe/Amsterdam")),
+ "pattern", asMap("timeZone", "Europe/Amsterdam")),
staticFieldName, staticFieldValue));
// Create the layout.
@@ -250,8 +250,8 @@ class JsonTemplateLayoutTest {
void test_log4j_deferred_runtime_resolver_for_MapMessage() {
// Create the event template.
- final String eventTemplate = writeJson(Map(
- "mapValue3", Map("$resolver", "message"),
+ final String eventTemplate = writeJson(asMap(
+ "mapValue3", asMap("$resolver", "message"),
"mapValue1", "${map:key1}",
"mapValue2", "${map:key2}",
"nestedLookupEmptyValue", "${map:noExist:-${map:noExist2:-${map:noExist3:-}}}",
@@ -291,8 +291,8 @@ class JsonTemplateLayoutTest {
void test_MapMessage_serialization() {
// Create the event template.
- final String eventTemplate = writeJson(Map(
- "message", Map("$resolver", "message")));
+ final String eventTemplate = writeJson(asMap(
+ "message", asMap("$resolver", "message")));
// Create the layout.
final JsonTemplateLayout layout = JsonTemplateLayout
@@ -328,11 +328,11 @@ class JsonTemplateLayoutTest {
// Create the event template.
final String key = "list";
- final String eventTemplate = writeJson(Map(
- "typedValue", Map(
+ final String eventTemplate = writeJson(asMap(
+ "typedValue", asMap(
"$resolver", "map",
"key", key),
- "stringifiedValue", Map(
+ "stringifiedValue", asMap(
"$resolver", "map",
"key", key,
"stringified", true)));
@@ -368,8 +368,8 @@ class JsonTemplateLayoutTest {
void test_message_fallbackKey() {
// Create the event template.
- final String eventTemplate = writeJson(Map(
- "message", Map(
+ final String eventTemplate = writeJson(asMap(
+ "message", asMap(
"$resolver", "message",
"fallbackKey", "formattedMessage")));
@@ -427,7 +427,7 @@ class JsonTemplateLayoutTest {
// Create the event template with property.
final String propertyName = "propertyName";
- final String eventTemplate = writeJson(Map(
+ final String eventTemplate = writeJson(asMap(
propertyName, "${" + propertyName + "}"));
// Create the layout with property.
@@ -463,24 +463,24 @@ class JsonTemplateLayoutTest {
.build();
// Create the event template.
- final String eventTemplate = writeJson(Map(
- "ex_class", Map(
+ final String eventTemplate = writeJson(asMap(
+ "ex_class", asMap(
"$resolver", "exception",
"field", "className"),
- "ex_message", Map(
+ "ex_message", asMap(
"$resolver", "exception",
"field", "message"),
- "ex_stacktrace", Map(
+ "ex_stacktrace", asMap(
"$resolver", "exception",
"field", "stackTrace",
"stringified", true),
- "root_ex_class", Map(
+ "root_ex_class", asMap(
"$resolver", "exceptionRootCause",
"field", "className"),
- "root_ex_message", Map(
+ "root_ex_message", asMap(
"$resolver", "exceptionRootCause",
"field", "message"),
- "root_ex_stacktrace", Map(
+ "root_ex_stacktrace", asMap(
"$resolver", "exceptionRootCause",
"field", "stackTrace",
"stringified", true)));
@@ -527,24 +527,24 @@ class JsonTemplateLayoutTest {
.build();
// Create the event template.
- final String eventTemplate = writeJson(Map(
- "ex_class", Map(
+ final String eventTemplate = writeJson(asMap(
+ "ex_class", asMap(
"$resolver", "exception",
"field", "className"),
- "ex_message", Map(
+ "ex_message", asMap(
"$resolver", "exception",
"field", "message"),
- "ex_stacktrace", Map(
+ "ex_stacktrace", asMap(
"$resolver", "exception",
"field", "stackTrace",
"stringified", true),
- "root_ex_class", Map(
+ "root_ex_class", asMap(
"$resolver", "exceptionRootCause",
"field", "className"),
- "root_ex_message", Map(
+ "root_ex_message", asMap(
"$resolver", "exceptionRootCause",
"field", "message"),
- "root_ex_stacktrace", Map(
+ "root_ex_stacktrace", asMap(
"$resolver", "exceptionRootCause",
"field", "stackTrace",
"stringified", true)));
@@ -593,9 +593,9 @@ class JsonTemplateLayoutTest {
// Create the event template.
final String messageKey = "message";
final String markerNameKey = "marker";
- final String eventTemplate = writeJson(Map(
- "message", Map("$resolver", "message"),
- "marker", Map(
+ final String eventTemplate = writeJson(asMap(
+ "message", asMap("$resolver", "message"),
+ "marker", asMap(
"$resolver", "marker",
"field", "name")));
@@ -671,14 +671,14 @@ class JsonTemplateLayoutTest {
.build();
// Create the template.
- final String template = writeJson(Map(
- "name", Map(
+ final String template = writeJson(asMap(
+ "name", asMap(
"$resolver", "main",
"key", kwKey),
- "positionArg", Map(
+ "positionArg", asMap(
"$resolver", "main",
"index", 2),
- "notFoundArg", Map(
+ "notFoundArg", asMap(
"$resolver", "main",
"key", missingKwKey)));
@@ -761,11 +761,11 @@ class JsonTemplateLayoutTest {
final String resolverName) {
// Create the event template.
- String eventTemplate = writeJson(Map(
- directlyAccessedKey, Map(
+ String eventTemplate = writeJson(asMap(
+ directlyAccessedKey, asMap(
"$resolver", resolverName,
"key", directlyAccessedKey),
- directlyAccessedNullPropertyKey, Map(
+ directlyAccessedNullPropertyKey, asMap(
"$resolver", resolverName,
"key", directlyAccessedNullPropertyKey)));
@@ -852,8 +852,8 @@ class JsonTemplateLayoutTest {
// Create the event template.
final String mapFieldName = "map";
- final String eventTemplate = writeJson(Map(
- mapFieldName, Map(
+ final String eventTemplate = writeJson(asMap(
+ mapFieldName, asMap(
"$resolver", resolverName,
"pattern", patternMatchedKey)));
@@ -940,11 +940,11 @@ class JsonTemplateLayoutTest {
// Create the event template.
final String prefix = "_map.";
- final String eventTemplate = writeJson(Map(
- "ignoredFieldName", Map(
+ final String eventTemplate = writeJson(asMap(
+ "ignoredFieldName", asMap(
"$resolver", resolverName,
"pattern", patternMatchedKey,
- "flatten", Map("prefix", prefix))));
+ "flatten", asMap("prefix", prefix))));
// Create the layout.
final JsonTemplateLayout layout = JsonTemplateLayout
@@ -975,11 +975,11 @@ class JsonTemplateLayoutTest {
.build();
// Create the event template node with map values.
- final String eventTemplate = writeJson(Map(
- "mapValue1", Map(
+ final String eventTemplate = writeJson(asMap(
+ "mapValue1", asMap(
"$resolver", "map",
"key", "key1"),
- "mapValue2", Map(
+ "mapValue2", asMap(
"$resolver", "map",
"key", "key?")));
@@ -1013,8 +1013,8 @@ class JsonTemplateLayoutTest {
.build();
// Create the event template.
- final String eventTemplate = writeJson(Map(
- "message", Map("$resolver", "message")));
+ final String eventTemplate = writeJson(asMap(
+ "message", asMap("$resolver", "message")));
// Create the layout.
final JsonTemplateLayout layout = JsonTemplateLayout
@@ -1051,8 +1051,8 @@ class JsonTemplateLayoutTest {
.build();
// Create the event template.
- final String eventTemplate = writeJson(Map(
- "message", Map("$resolver", "message")));
+ final String eventTemplate = writeJson(asMap(
+ "message", asMap("$resolver", "message")));
// Create the layout.
JsonTemplateLayout layout = JsonTemplateLayout
@@ -1078,24 +1078,24 @@ class JsonTemplateLayoutTest {
final String methodNameFieldName = "methodName";
final String fileNameFieldName = "fileName";
final String lineNumberFieldName = "lineNumber";
- final String stackTraceElementTemplate = writeJson(Map(
- classNameFieldName, Map(
+ final String stackTraceElementTemplate = writeJson(asMap(
+ classNameFieldName, asMap(
"$resolver", "stackTraceElement",
"field", "className"),
- methodNameFieldName, Map(
+ methodNameFieldName, asMap(
"$resolver", "stackTraceElement",
"field", "methodName"),
- fileNameFieldName, Map(
+ fileNameFieldName, asMap(
"$resolver", "stackTraceElement",
"field", "fileName"),
- lineNumberFieldName, Map(
+ lineNumberFieldName, asMap(
"$resolver", "stackTraceElement",
"field", "lineNumber")));
// Create the event template.
final String stackTraceFieldName = "stackTrace";
- final String eventTemplate = writeJson(Map(
- stackTraceFieldName, Map(
+ final String eventTemplate = writeJson(asMap(
+ stackTraceFieldName, asMap(
"$resolver", "exception",
"field", "stackTrace")));
@@ -1231,10 +1231,10 @@ class JsonTemplateLayoutTest {
final String excessiveKey = Strings.repeat("k", maxStringLength) + 'K';
final String excessiveValue = Strings.repeat("v", maxStringLength) + 'V';
final String nullValueKey = "nullValueKey";
- final String eventTemplate = writeJson(Map(
- messageKey, Map("$resolver", "message"),
+ final String eventTemplate = writeJson(asMap(
+ messageKey, asMap("$resolver", "message"),
excessiveKey, excessiveValue,
- nullValueKey, Map(
+ nullValueKey, asMap(
"$resolver", "exception",
"field", "message")));
@@ -1311,8 +1311,8 @@ class JsonTemplateLayoutTest {
.build();
// Create the event template.
- final String eventTemplate = writeJson(Map(
- "ex_stacktrace", Map(
+ final String eventTemplate = writeJson(asMap(
+ "ex_stacktrace", asMap(
"$resolver", "exception",
"field", "stackTrace",
"stringified", true)));
@@ -1392,7 +1392,7 @@ class JsonTemplateLayoutTest {
void test_timestamp_epoch_resolvers() {
final List<Map<String, Object>> testCases = Arrays.asList(
- Map(
+ asMap(
"epochSecs", new BigDecimal("1581082727.982123456"),
"epochSecsRounded", 1581082727,
"epochSecsNanos", 982123456,
@@ -1400,7 +1400,7 @@ class JsonTemplateLayoutTest {
"epochMillisRounded", 1581082727982L,
"epochMillisNanos", 123456,
"epochNanos", 1581082727982123456L),
- Map(
+ asMap(
"epochSecs", new BigDecimal("1591177590.005000001"),
"epochSecsRounded", 1591177590,
"epochSecsNanos", 5000001,
@@ -1410,32 +1410,32 @@ class JsonTemplateLayoutTest {
"epochNanos", 1591177590005000001L));
// Create the event template.
- final String eventTemplate = writeJson(Map(
- "epochSecs", Map(
+ final String eventTemplate = writeJson(asMap(
+ "epochSecs", asMap(
"$resolver", "timestamp",
- "epoch", Map("unit", "secs")),
- "epochSecsRounded", Map(
+ "epoch", asMap("unit", "secs")),
+ "epochSecsRounded", asMap(
"$resolver", "timestamp",
- "epoch", Map(
+ "epoch", asMap(
"unit", "secs",
"rounded", true)),
- "epochSecsNanos", Map(
+ "epochSecsNanos", asMap(
"$resolver", "timestamp",
- "epoch", Map("unit", "secs.nanos")),
- "epochMillis", Map(
+ "epoch", asMap("unit", "secs.nanos")),
+ "epochMillis", asMap(
"$resolver", "timestamp",
- "epoch", Map("unit", "millis")),
- "epochMillisRounded", Map(
+ "epoch", asMap("unit", "millis")),
+ "epochMillisRounded", asMap(
"$resolver", "timestamp",
- "epoch", Map(
+ "epoch", asMap(
"unit", "millis",
"rounded", true)),
- "epochMillisNanos", Map(
+ "epochMillisNanos", asMap(
"$resolver", "timestamp",
- "epoch", Map("unit", "millis.nanos")),
- "epochNanos", Map(
+ "epoch", asMap("unit", "millis.nanos")),
+ "epochNanos", asMap(
"$resolver", "timestamp",
- "epoch", Map("unit", "nanos"))));
+ "epoch", asMap("unit", "nanos"))));
// Create the layout.
final JsonTemplateLayout layout = JsonTemplateLayout
@@ -1491,10 +1491,10 @@ class JsonTemplateLayoutTest {
final LogEvent logEvent4 = createLogEventAtInstant(logEvent4FormattedInstant);
// Create the event template.
- final String eventTemplate = writeJson(Map(
- "timestamp", Map(
+ final String eventTemplate = writeJson(asMap(
+ "timestamp", asMap(
"$resolver", "timestamp",
- "pattern", Map(
+ "pattern", asMap(
"format", "yyyy-MM-dd'T'HH:mm:ss'Z'",
"timeZone", "UTC"))));
@@ -1544,15 +1544,15 @@ class JsonTemplateLayoutTest {
void test_level_severity() {
// Create the event template.
- final String eventTemplate = writeJson(Map(
- "severityKeyword", Map(
+ final String eventTemplate = writeJson(asMap(
+ "severityKeyword", asMap(
"$resolver", "level",
"field", "severity",
- "severity", Map("field", "keyword")),
- "severityCode", Map(
+ "severity", asMap("field", "keyword")),
+ "severityCode", asMap(
"$resolver", "level",
"field", "severity",
- "severity", Map("field", "code"))));
+ "severity", asMap("field", "code"))));
// Create the layout.
final JsonTemplateLayout layout = JsonTemplateLayout
@@ -1597,18 +1597,18 @@ class JsonTemplateLayoutTest {
.build();
// Create the event template.
- final String eventTemplate = writeJson(Map(
- "exStackTrace", Map(
+ final String eventTemplate = writeJson(asMap(
+ "exStackTrace", asMap(
"$resolver", "exception",
"field", "stackTrace"),
- "exStackTraceString", Map(
+ "exStackTraceString", asMap(
"$resolver", "exception",
"field", "stackTrace",
"stringified", true),
- "exRootCauseStackTrace", Map(
+ "exRootCauseStackTrace", asMap(
"$resolver", "exceptionRootCause",
"field", "stackTrace"),
- "exRootCauseStackTraceString", Map(
+ "exRootCauseStackTraceString", asMap(
"$resolver", "exceptionRootCause",
"field", "stackTrace",
"stringified", true),
@@ -1637,8 +1637,8 @@ class JsonTemplateLayoutTest {
void test_StackTraceTextResolver_with_maxStringLength() {
// Create the event template.
- final String eventTemplate = writeJson(Map(
- "stackTrace", Map(
+ final String eventTemplate = writeJson(asMap(
+ "stackTrace", asMap(
"$resolver", "exception",
"field", "stackTrace",
"stringified", true)));
@@ -1675,7 +1675,7 @@ class JsonTemplateLayoutTest {
void test_null_eventDelimiter() {
// Create the event template.
- final String eventTemplate = writeJson(Map("key", "val"));
+ final String eventTemplate = writeJson(asMap("key", "val"));
// Create the layout.
final JsonTemplateLayout layout = JsonTemplateLayout
@@ -1707,8 +1707,8 @@ class JsonTemplateLayoutTest {
final List<LogEvent> logEvents = createNastyLogEvents();
// Create the event template.
- final String eventTemplate = writeJson(Map(
- "message", Map("$resolver", "message")));
+ final String eventTemplate = writeJson(asMap(
+ "message", asMap("$resolver", "message")));
// Create the layout.
final JsonTemplateLayout layout = JsonTemplateLayout
@@ -1882,8 +1882,8 @@ class JsonTemplateLayoutTest {
void test_PatternResolver() {
// Create the event template.
- final String eventTemplate = writeJson(Map(
- "message", Map(
+ final String eventTemplate = writeJson(asMap(
+ "message", asMap(
"$resolver", "pattern",
"pattern", "%p:%m")));
@@ -1920,26 +1920,39 @@ class JsonTemplateLayoutTest {
}
@Test
+ void test_MessageParameterResolver_noParameters_with_ParameterizedMessageFactory() {
+ testMessageParameterResolverNoParameters(ParameterizedMessageFactory.INSTANCE);
+ }
+
+ @Test
void test_MessageParameterResolver_with_ReusableMessageFactory() {
testMessageParameterResolver(ReusableMessageFactory.INSTANCE);
}
- private static void testMessageParameterResolver(MessageFactory messageFactory) {
+ @Test
+ void test_MessageParameterResolver_noParameters_with_ReusableMessageFactory() {
+ testMessageParameterResolverNoParameters(ReusableMessageFactory.INSTANCE);
+ }
+
+ private static void testMessageParameterResolver(final MessageFactory messageFactory) {
// Create the event template.
- final String eventTemplate = writeJson(Map(
- "po*", Map(
+ final String eventTemplate = writeJson(asMap(
+ "po*", asMap(
"$resolver", "messageParameter"),
- "ps*", Map(
+ "ps*", asMap(
"$resolver", "messageParameter",
"stringified", true),
- "po2", Map(
+ "po2", asMap(
"$resolver", "messageParameter",
"index", 2),
- "ps2", Map(
+ "ps2", asMap(
"$resolver", "messageParameter",
"index", 2,
- "stringified", true)));
+ "stringified", true),
+ "po3", asMap(
+ "$resolver", "messageParameter",
+ "index", 3)));
// Create the layout.
final JsonTemplateLayout layout = JsonTemplateLayout
@@ -1969,6 +1982,43 @@ class JsonTemplateLayoutTest {
assertThat(accessor.getObject("ps*")).isEqualTo(stringifiedParameters);
assertThat(accessor.getObject("po2")).isEqualTo(parameters[2]);
assertThat(accessor.getString("ps2")).isEqualTo(stringifiedParameters.get(2));
+ assertThat(accessor.getString("ps3")).isNull();
+ });
+
+ }
+
+ private static void testMessageParameterResolverNoParameters(
+ final MessageFactory messageFactory) {
+
+ // Create the event template.
+ final String eventTemplate = writeJson(asMap(
+ "po*", asMap(
+ "$resolver", "messageParameter"),
+ "ps*", asMap(
+ "$resolver", "messageParameter",
+ "stringified", true)));
+
+ // Create the layout.
+ final JsonTemplateLayout layout = JsonTemplateLayout
+ .newBuilder()
+ .setConfiguration(CONFIGURATION)
+ .setEventTemplate(eventTemplate)
+ .build();
+
+ // Create the log event.
+ final Message message = messageFactory.newMessage("foo", new Object[0]);
+ final Level level = Level.FATAL;
+ final LogEvent logEvent = Log4jLogEvent
+ .newBuilder()
+ .setLoggerName(LOGGER_NAME)
+ .setMessage(message)
+ .setLevel(level)
+ .build();
+
+ // Check the serialized event.
+ usingSerializedLogEventAccessor(layout, logEvent, accessor -> {
+ assertThat(accessor.getObject("po*")).isEqualTo(Collections.emptyList());
+ assertThat(accessor.getObject("ps*")).isEqualTo(Collections.emptyList());
});
}
@@ -1977,26 +2027,26 @@ class JsonTemplateLayoutTest {
void test_unresolvable_nested_fields_are_skipped() {
// Create the event template.
- final String eventTemplate = writeJson(Map(
- "exception", Map(
- "message", Map(
+ final String eventTemplate = writeJson(asMap(
+ "exception", asMap(
+ "message", asMap(
"$resolver", "exception",
"field", "message"),
- "className", Map(
+ "className", asMap(
"$resolver", "exception",
"field", "className")),
- "exceptionRootCause", Map(
- "message", Map(
+ "exceptionRootCause", asMap(
+ "message", asMap(
"$resolver", "exceptionRootCause",
"field", "message"),
- "className", Map(
+ "className", asMap(
"$resolver", "exceptionRootCause",
"field", "className")),
- "source", Map(
- "lineNumber", Map(
+ "source", asMap(
+ "lineNumber", asMap(
"$resolver", "source",
"field", "lineNumber"),
- "fileName", Map(
+ "fileName", asMap(
"$resolver", "source",
"field", "fileName")),
"emptyMap", Collections.emptyMap(),
@@ -2061,7 +2111,7 @@ class JsonTemplateLayoutTest {
return JsonReader.read(json);
}
- private static Map<String, Object> Map(final Object... pairs) {
+ private static Map<String, Object> asMap(final Object... pairs) {
final Map<String, Object> map = new LinkedHashMap<>();
if (pairs.length % 2 != 0) {
throw new IllegalArgumentException("odd number of arguments");
diff --git a/src/changes/changes.xml b/src/changes/changes.xml
index 7099b53..40a192a 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-2967" dev="ckozak" type="fix">
+ Fix JsonTemplateLayout index based parameter resolution when messages contain too few parameters.
+ </action>
<action issue="LOG4J2-2962" dev="vy" type="add">
Enrich "map" resolver by unifying its backend with "mdc" resolver.
</action>