diff --git a/log4j2-ecs-layout/src/main/java/co/elastic/logging/log4j2/EcsLayout.java b/log4j2-ecs-layout/src/main/java/co/elastic/logging/log4j2/EcsLayout.java index 80f106bc..fa3d9a6e 100644 --- a/log4j2-ecs-layout/src/main/java/co/elastic/logging/log4j2/EcsLayout.java +++ b/log4j2-ecs-layout/src/main/java/co/elastic/logging/log4j2/EcsLayout.java @@ -39,7 +39,9 @@ import org.apache.logging.log4j.core.layout.AbstractStringLayout; import org.apache.logging.log4j.core.layout.ByteBufferDestination; import org.apache.logging.log4j.core.layout.Encoder; +import org.apache.logging.log4j.core.layout.PatternLayout; import org.apache.logging.log4j.core.lookup.StrSubstitutor; +import org.apache.logging.log4j.core.pattern.PatternFormatter; import org.apache.logging.log4j.core.util.KeyValuePair; import org.apache.logging.log4j.message.Message; import org.apache.logging.log4j.message.MultiformatMessage; @@ -78,6 +80,7 @@ public void accept(final String key, final Object value, final StringBuilder str }; private final KeyValuePair[] additionalFields; + private final PatternFormatter[][] fieldValuePatternFormatter; private final Set topLevelLabels; private final boolean stackTraceAsArray; private String serviceName; @@ -93,6 +96,15 @@ private EcsLayout(Configuration config, String serviceName, boolean includeMarke this.topLevelLabels.add("trace.id"); this.topLevelLabels.add("transaction.id"); this.additionalFields = additionalFields; + fieldValuePatternFormatter = new PatternFormatter[additionalFields.length][]; + for (int i = 0; i < additionalFields.length; i++) { + KeyValuePair additionalField = additionalFields[i]; + if (additionalField.getValue().contains("%")) { + fieldValuePatternFormatter[i] = PatternLayout.createPatternParser(config) + .parse(additionalField.getValue()) + .toArray(new PatternFormatter[0]); + } + } } @PluginBuilderFactory @@ -132,12 +144,21 @@ private StringBuilder toText(LogEvent event, StringBuilder builder, boolean gcFr } private void serializeLabels(LogEvent event, StringBuilder builder) { - if (!event.getContextData().isEmpty() || additionalFields.length > 0) { - if (additionalFields.length > 0) { + final int length = additionalFields.length; + if (!event.getContextData().isEmpty() || length > 0) { + if (length > 0) { final StrSubstitutor strSubstitutor = getConfiguration().getStrSubstitutor(); - for (KeyValuePair additionalField : additionalFields) { + for (int i = 0; i < length; i++) { + KeyValuePair additionalField = additionalFields[i]; + PatternFormatter[] formatters = fieldValuePatternFormatter[i]; CharSequence value = null; - if (valueNeedsLookup(additionalField.getValue())) { + if (formatters != null) { + StringBuilder buffer = EcsJsonSerializer.getMessageStringBuilder(); + formatPattern(event, formatters, buffer); + if (buffer.length() > 0) { + value = buffer; + } + } else if (valueNeedsLookup(additionalField.getValue())) { StringBuilder lookupValue = EcsJsonSerializer.getMessageStringBuilder(); lookupValue.append(additionalField.getValue()); if (strSubstitutor.replaceIn(event, lookupValue)) { @@ -160,6 +181,13 @@ private void serializeLabels(LogEvent event, StringBuilder builder) { } } + private static void formatPattern(LogEvent event, PatternFormatter[] formatters, StringBuilder buffer) { + final int len = formatters.length; + for (int i = 0; i < len; i++) { + formatters[i].format(event, buffer); + } + } + private void serializeTags(LogEvent event, StringBuilder builder) { List contextStack = event.getContextStack().asList(); Marker marker = event.getMarker(); @@ -169,7 +197,8 @@ private void serializeTags(LogEvent event, StringBuilder builder) { } if (!contextStack.isEmpty()) { - for (int i = 0; i < contextStack.size(); i++) { + final int len = contextStack.size(); + for (int i = 0; i < len; i++) { builder.append('\"'); JsonUtils.quoteAsString(contextStack.get(i), builder); builder.append("\","); diff --git a/log4j2-ecs-layout/src/test/java/co/elastic/logging/log4j2/AbstractLog4j2EcsLayoutTest.java b/log4j2-ecs-layout/src/test/java/co/elastic/logging/log4j2/AbstractLog4j2EcsLayoutTest.java index a4516671..5fcde018 100644 --- a/log4j2-ecs-layout/src/test/java/co/elastic/logging/log4j2/AbstractLog4j2EcsLayoutTest.java +++ b/log4j2-ecs-layout/src/test/java/co/elastic/logging/log4j2/AbstractLog4j2EcsLayoutTest.java @@ -53,6 +53,9 @@ void globalLabels() throws Exception { debug("test"); assertThat(getLastLogLine().get("cluster.uuid").textValue()).isEqualTo("9fe9134b-20b0-465e-acf9-8cc09ac9053b"); assertThat(getLastLogLine().get("node.id").textValue()).isEqualTo("foo"); + assertThat(getLastLogLine().get("empty")).isNull(); + assertThat(getLastLogLine().get("emptyPattern")).isNull(); + assertThat(getLastLogLine().get("clazz").textValue()).startsWith(getClass().getPackageName()); assertThat(getLastLogLine().get("404")).isNull(); } diff --git a/log4j2-ecs-layout/src/test/java/co/elastic/logging/log4j2/Log4j2EcsLayoutTest.java b/log4j2-ecs-layout/src/test/java/co/elastic/logging/log4j2/Log4j2EcsLayoutTest.java index 8fbc03b0..8f8222f2 100644 --- a/log4j2-ecs-layout/src/test/java/co/elastic/logging/log4j2/Log4j2EcsLayoutTest.java +++ b/log4j2-ecs-layout/src/test/java/co/elastic/logging/log4j2/Log4j2EcsLayoutTest.java @@ -73,6 +73,8 @@ void setUp() { new KeyValuePair("cluster.uuid", "9fe9134b-20b0-465e-acf9-8cc09ac9053b"), new KeyValuePair("node.id", "${node.id}"), new KeyValuePair("empty", "${empty}"), + new KeyValuePair("clazz", "%C"), + new KeyValuePair("emptyPattern", "%notEmpty{%invalidPattern}"), }) .build(); diff --git a/log4j2-ecs-layout/src/test/resources/log4j2-test.xml b/log4j2-ecs-layout/src/test/resources/log4j2-test.xml index dc009041..8a9fa529 100644 --- a/log4j2-ecs-layout/src/test/resources/log4j2-test.xml +++ b/log4j2-ecs-layout/src/test/resources/log4j2-test.xml @@ -9,6 +9,8 @@ + +