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/07/11 20:41:47 UTC

[logging-log4j2] branch master updated: #335 Add nullEventDelimiterEnabled flag.

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ı <vo...@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}}"/>