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 7b5aaa8 #335 Add nullEventDelimiterEnabled flag. 7b5aaa8 is described below commit 7b5aaa8434102307df4cb90540703b0deaec393b Author: Volkan Yazıcı <volkan.yaz...@gmail.com> AuthorDate: Sat Jul 11 22:38:09 2020 +0200 #335 Add nullEventDelimiterEnabled flag. --- .../layout/json/template/JsonTemplateLayout.java | 23 +++- .../json/template/JsonTemplateLayoutDefaults.java | 9 ++ .../JsonTemplateLayoutNullEventDelimiterTest.java | 127 +++++++++++++++++++++ .../resources/gcFreeJsonTemplateLayoutLogging.xml | 17 +++ ...nullEventDelimitedJsonTemplateLayoutLogging.xml | 39 +++++++ .../src/main/config-repo/log4j2.xml | 2 +- src/site/asciidoc/manual/json-template-layout.adoc | 6 + src/site/markdown/manual/cloud.md | 2 +- 8 files changed, 217 insertions(+), 8 deletions(-) diff --git a/log4j-layout-json-template/src/main/java/org/apache/logging/log4j/layout/json/template/JsonTemplateLayout.java b/log4j-layout-json-template/src/main/java/org/apache/logging/log4j/layout/json/template/JsonTemplateLayout.java index 7ff3ae9..355816e 100644 --- a/log4j-layout-json-template/src/main/java/org/apache/logging/log4j/layout/json/template/JsonTemplateLayout.java +++ b/log4j-layout-json-template/src/main/java/org/apache/logging/log4j/layout/json/template/JsonTemplateLayout.java @@ -92,7 +92,8 @@ public class JsonTemplateLayout implements StringLayout { private JsonTemplateLayout(final Builder builder) { this.charset = builder.charset; this.contentType = "application/json; charset=" + charset; - this.eventDelimiter = builder.eventDelimiter; + final String eventDelimiterSuffix = builder.isNullEventDelimiterEnabled() ? "\0" : ""; + this.eventDelimiter = builder.eventDelimiter + eventDelimiterSuffix; final Configuration configuration = builder.configuration; final StrSubstitutor substitutor = configuration.getStrSubstitutor(); final JsonWriter jsonWriter = JsonWriter @@ -215,11 +216,7 @@ public class JsonTemplateLayout implements StringLayout { final StringBuilder stringBuilder = jsonWriter.getStringBuilder(); try { eventResolver.resolve(event, jsonWriter); - if (eventDelimiter != null && eventDelimiter.equalsIgnoreCase("null")) { - stringBuilder.append('\0'); - } else { - stringBuilder.append(eventDelimiter); - } + stringBuilder.append(eventDelimiter); return stringBuilder.toString(); } finally { contextRecycler.release(context); @@ -340,6 +337,10 @@ public class JsonTemplateLayout implements StringLayout { private String eventDelimiter = JsonTemplateLayoutDefaults.getEventDelimiter(); @PluginBuilderAttribute + private boolean nullEventDelimiterEnabled = + JsonTemplateLayoutDefaults.isNullEventDelimiterEnabled(); + + @PluginBuilderAttribute private int maxStringLength = JsonTemplateLayoutDefaults.getMaxStringLength(); @PluginBuilderAttribute @@ -447,6 +448,16 @@ public class JsonTemplateLayout implements StringLayout { return this; } + public boolean isNullEventDelimiterEnabled() { + return nullEventDelimiterEnabled; + } + + public Builder setNullEventDelimiterEnabled( + final boolean nullEventDelimiterEnabled) { + this.nullEventDelimiterEnabled = nullEventDelimiterEnabled; + return this; + } + public int getMaxStringLength() { return maxStringLength; } diff --git a/log4j-layout-json-template/src/main/java/org/apache/logging/log4j/layout/json/template/JsonTemplateLayoutDefaults.java b/log4j-layout-json-template/src/main/java/org/apache/logging/log4j/layout/json/template/JsonTemplateLayoutDefaults.java index adfe760..7c28b9f 100644 --- a/log4j-layout-json-template/src/main/java/org/apache/logging/log4j/layout/json/template/JsonTemplateLayoutDefaults.java +++ b/log4j-layout-json-template/src/main/java/org/apache/logging/log4j/layout/json/template/JsonTemplateLayoutDefaults.java @@ -79,6 +79,11 @@ public enum JsonTemplateLayoutDefaults {; "log4j.layout.jsonTemplate.eventDelimiter", System.lineSeparator()); + private static final boolean NULL_EVENT_DELIMITER_ENABLED = + PROPERTIES.getBooleanProperty( + "log4j.layout.jsonTemplate.nullEventDelimiterEnabled", + false); + private static final int MAX_STRING_LENGTH = readMaxStringLength(); private static final String TRUNCATED_STRING_SUFFIX = @@ -189,6 +194,10 @@ public enum JsonTemplateLayoutDefaults {; return EVENT_DELIMITER; } + public static boolean isNullEventDelimiterEnabled() { + return NULL_EVENT_DELIMITER_ENABLED; + } + public static int getMaxStringLength() { return MAX_STRING_LENGTH; } diff --git a/log4j-layout-json-template/src/test/java/org/apache/logging/log4j/layout/json/template/JsonTemplateLayoutNullEventDelimiterTest.java b/log4j-layout-json-template/src/test/java/org/apache/logging/log4j/layout/json/template/JsonTemplateLayoutNullEventDelimiterTest.java new file mode 100644 index 0000000..2487b65 --- /dev/null +++ b/log4j-layout-json-template/src/test/java/org/apache/logging/log4j/layout/json/template/JsonTemplateLayoutNullEventDelimiterTest.java @@ -0,0 +1,127 @@ +package org.apache.logging.log4j.layout.json.template; + +import org.apache.logging.log4j.Level; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; +import org.assertj.core.api.Assertions; +import org.awaitility.Awaitility; +import org.junit.Test; + +import java.io.ByteArrayOutputStream; +import java.io.EOFException; +import java.io.IOException; +import java.io.InputStream; +import java.net.ServerSocket; +import java.net.Socket; +import java.time.Duration; + +public class JsonTemplateLayoutNullEventDelimiterTest { + + // Set the configuration. + static { + System.setProperty( + "log4j.configurationFile", + "nullEventDelimitedJsonTemplateLayoutLogging.xml"); + } + + // Note that this port is hardcoded in the configuration file too! + private static final int PORT = 50514; + + @Test + public void test() throws Exception { + + // Set the expected bytes. + final byte[] expectedBytes = { + '"', 'f', 'o', 'o', '"', '\0', + '"', 'b', 'a', 'r', '"', '\0' + }; + + // Start the TCP server. + try (final TcpServer server = new TcpServer(PORT)) { + + // Produce log events. + final Logger logger = LogManager.getLogger(JsonTemplateLayoutNullEventDelimiterTest.class); + logger.log(Level.INFO, "foo"); + logger.log(Level.INFO, "bar"); + + // Wait for the log events. + Awaitility + .await() + .atMost(Duration.ofSeconds(10)) + .pollDelay(Duration.ofSeconds(2)) + .until(() -> server.getTotalReadByteCount() >= expectedBytes.length); + + // Verify the received log events. + final byte[] actualBytes = server.getReceivedBytes(); + Assertions.assertThat(actualBytes).startsWith(expectedBytes); + + } + + } + + private static final class TcpServer extends Thread implements AutoCloseable { + + private final ServerSocket serverSocket; + + private final ByteArrayOutputStream outputStream; + + private volatile int totalReadByteCount = 0; + + private volatile boolean closed = false; + + private TcpServer(final int port) throws IOException { + this.serverSocket = new ServerSocket(port); + this.outputStream = new ByteArrayOutputStream(); + serverSocket.setReuseAddress(true); + serverSocket.setSoTimeout(5_000); + setDaemon(true); + start(); + } + + @Override + public void run() { + try { + try (final Socket socket = serverSocket.accept()) { + final InputStream inputStream = socket.getInputStream(); + final byte[] buffer = new byte[1024]; + // noinspection InfiniteLoopStatement + while (true) { + final int readByteCount = inputStream.read(buffer); + if (readByteCount > 0) { + synchronized (this) { + totalReadByteCount += readByteCount; + outputStream.write(buffer, 0, readByteCount); + } + } + } + } + } catch (final EOFException ignored) { + // Socket is closed. + } catch (final Exception error) { + if (!closed) { + throw new RuntimeException(error); + } + } + } + + public synchronized byte[] getReceivedBytes() { + return outputStream.toByteArray(); + } + + public synchronized int getTotalReadByteCount() { + return totalReadByteCount; + } + + @Override + public synchronized void close() throws InterruptedException { + if (closed) { + throw new IllegalStateException("shutdown has already been invoked"); + } + closed = true; + interrupt(); + join(3_000L); + } + + } + +} diff --git a/log4j-layout-json-template/src/test/resources/gcFreeJsonTemplateLayoutLogging.xml b/log4j-layout-json-template/src/test/resources/gcFreeJsonTemplateLayoutLogging.xml index 0362308..245ff06 100644 --- a/log4j-layout-json-template/src/test/resources/gcFreeJsonTemplateLayoutLogging.xml +++ b/log4j-layout-json-template/src/test/resources/gcFreeJsonTemplateLayoutLogging.xml @@ -1,4 +1,21 @@ <?xml version="1.0" encoding="UTF-8"?> +<!-- + 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. + +--> <Configuration status="OFF"> <Appenders> <Console name="Console" target="SYSTEM_OUT"> diff --git a/log4j-layout-json-template/src/test/resources/nullEventDelimitedJsonTemplateLayoutLogging.xml b/log4j-layout-json-template/src/test/resources/nullEventDelimitedJsonTemplateLayoutLogging.xml new file mode 100644 index 0000000..39d87a9 --- /dev/null +++ b/log4j-layout-json-template/src/test/resources/nullEventDelimitedJsonTemplateLayoutLogging.xml @@ -0,0 +1,39 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + 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. + +--> +<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> +</Configuration> 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 25c5ac8..a2afa70 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 @@ -106,7 +106,7 @@ port="12222" protocol="tcp" bufferedIo="true"> - <JsonTemplateLayout eventTemplateUri="classpath:EnhancedGelf.json" eventDelimiter="null"> + <JsonTemplateLayout eventTemplateUri="classpath:EnhancedGelf.json" nullEventDelimiterEnabled="true"> <EventTemplateAdditionalFields> <EventTemplateAdditionalField key="containerId" value="\${docker:containerId:-}"/> <EventTemplateAdditionalField key="application" value="\${lower:${spring:spring.application.name:-spring}}"/> diff --git a/src/site/asciidoc/manual/json-template-layout.adoc b/src/site/asciidoc/manual/json-template-layout.adoc index ff2904d..92ec635 100644 --- a/src/site/asciidoc/manual/json-template-layout.adoc +++ b/src/site/asciidoc/manual/json-template-layout.adoc @@ -207,6 +207,12 @@ appender.console.json.eventTemplateUri = classpath:LogstashJsonEventLayoutV1.jso `System.lineSeparator()` set by `log4j.layout.jsonTemplate.eventDelimiter` property) +| nullEventDelimiterEnabled +| boolean +| append `\0` (`null`) character to the end of every emitted `eventDelimiter` + (defaults to `false` set by + `log4j.layout.jsonTemplate.nullEventDelimiterEnabled` property) + | maxStringLength | int | truncate string values longer than the specified limit (defaults to 16384 set diff --git a/src/site/markdown/manual/cloud.md b/src/site/markdown/manual/cloud.md index e4ad5c1..84fb63f 100644 --- a/src/site/markdown/manual/cloud.md +++ b/src/site/markdown/manual/cloud.md @@ -220,7 +220,7 @@ The logging configuration to use this template would be port="12222" protocol="tcp" bufferedIo="true"> - <JsonTemplateLayout eventTemplateUri="classpath:EnhancedGelf.json" eventDelimiter="null"> + <JsonTemplateLayout eventTemplateUri="classpath:EnhancedGelf.json" nullEventDelimiterEnabled="true"> <EventTemplateAdditionalFields> <EventTemplateAdditionalField key="containerId" value="${docker:containerId:-}"/> <EventTemplateAdditionalField key="application" value="${lower:${spring:spring.application.name:-spring}}"/>