Logging delimiter parametrized (#4745)

Signed-off-by: Maxim Nesen <maxim.nesen@oracle.com>
diff --git a/core-common/src/main/java/org/glassfish/jersey/logging/ClientLoggingFilter.java b/core-common/src/main/java/org/glassfish/jersey/logging/ClientLoggingFilter.java
index 1cd5cdb..07c3dbe 100644
--- a/core-common/src/main/java/org/glassfish/jersey/logging/ClientLoggingFilter.java
+++ b/core-common/src/main/java/org/glassfish/jersey/logging/ClientLoggingFilter.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2016, 2019 Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2016, 2021 Oracle and/or its affiliates. All rights reserved.
  *
  * This program and the accompanying materials are made available under the
  * terms of the Eclipse Public License v. 2.0, which is available at
@@ -53,18 +53,20 @@
 final class ClientLoggingFilter extends LoggingInterceptor implements ClientRequestFilter, ClientResponseFilter {
 
     /**
-     * Create a logging filter with custom logger and custom settings of entity
+     * Create a logging filter using builder instance with custom logger and custom settings of entity
      * logging.
      *
-     * @param logger        the logger to log messages to.
-     * @param level         level at which the messages will be logged.
-     * @param verbosity     verbosity of the logged messages. See {@link Verbosity}.
-     * @param maxEntitySize maximum number of entity bytes to be logged (and buffered) - if the entity is larger,
+     * @param builder       loggingFeatureBuilder which contains values for:
+     *  logger         the logger to log messages to.
+     *  level          level at which the messages will be logged.
+     *  verbosity      verbosity of the logged messages. See {@link Verbosity}.
+     *  maxEntitySize  maximum number of entity bytes to be logged (and buffered) - if the entity is larger,
      *                      logging filter will print (and buffer in memory) only the specified number of bytes
      *                      and print "...more..." string at the end. Negative values are interpreted as zero.
+     *  separator      delimiter for particular log lines. Default is Linux new line delimiter
      */
-    public ClientLoggingFilter(final Logger logger, final Level level, final Verbosity verbosity, final int maxEntitySize) {
-        super(logger, level, verbosity, maxEntitySize);
+    public ClientLoggingFilter(LoggingFeature.LoggingFeatureBuilder builder) {
+        super(builder);
     }
 
     @Override
diff --git a/core-common/src/main/java/org/glassfish/jersey/logging/LoggingFeature.java b/core-common/src/main/java/org/glassfish/jersey/logging/LoggingFeature.java
index e4e2b01..e6ed421 100644
--- a/core-common/src/main/java/org/glassfish/jersey/logging/LoggingFeature.java
+++ b/core-common/src/main/java/org/glassfish/jersey/logging/LoggingFeature.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2016, 2019 Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2016, 2021 Oracle and/or its affiliates. All rights reserved.
  *
  * This program and the accompanying materials are made available under the
  * terms of the Eclipse Public License v. 2.0, which is available at
@@ -40,6 +40,7 @@
  * <li>{@link #LOGGING_FEATURE_LOGGER_LEVEL}</li>
  * <li>{@link #LOGGING_FEATURE_VERBOSITY}</li>
  * <li>{@link #LOGGING_FEATURE_MAX_ENTITY_SIZE}</li>
+ * <li>{@link #LOGGING_FEATURE_SEPARATOR}</li>
  * </ul>
  * <p>
  * If any of the configuration value is not set, following default values are applied:
@@ -56,6 +57,7 @@
  * <li>{@link #LOGGING_FEATURE_LOGGER_LEVEL_SERVER}</li>
  * <li>{@link #LOGGING_FEATURE_VERBOSITY_SERVER}</li>
  * <li>{@link #LOGGING_FEATURE_MAX_ENTITY_SIZE_SERVER}</li>
+ * <li>{@link #LOGGING_FEATURE_SEPARATOR_SERVER}</li>
  * </ul>
  * Client configurable properties:
  * <ul>
@@ -63,6 +65,7 @@
  * <li>{@link #LOGGING_FEATURE_LOGGER_LEVEL_CLIENT}</li>
  * <li>{@link #LOGGING_FEATURE_VERBOSITY_CLIENT}</li>
  * <li>{@link #LOGGING_FEATURE_MAX_ENTITY_SIZE_CLIENT}</li>
+ * <li>{@link #LOGGING_FEATURE_SEPARATOR_CLIENT}</li>
  * </ul>
  *
  * @author Ondrej Kosatka
@@ -86,11 +89,16 @@
      * Default verbosity for entity logging. See {@link Verbosity}.
      */
     public static final Verbosity DEFAULT_VERBOSITY = Verbosity.PAYLOAD_TEXT;
+    /**
+     * Default separator for entity logging.
+     */
+    public static final String DEFAULT_SEPARATOR = "\n";
 
     private static final String LOGGER_NAME_POSTFIX = ".logger.name";
     private static final String LOGGER_LEVEL_POSTFIX = ".logger.level";
     private static final String VERBOSITY_POSTFIX = ".verbosity";
     private static final String MAX_ENTITY_POSTFIX = ".entity.maxSize";
+    private static final String SEPARATOR_POSTFIX = ".separator";
     private static final String LOGGING_FEATURE_COMMON_PREFIX = "jersey.config.logging";
     /**
      * Common logger name property.
@@ -108,6 +116,10 @@
      * Common property for configuring a maximum number of bytes of entity to be logged.
      */
     public static final String LOGGING_FEATURE_MAX_ENTITY_SIZE = LOGGING_FEATURE_COMMON_PREFIX + MAX_ENTITY_POSTFIX;
+    /**
+     * Common property for configuring logging separator.
+     */
+    public static final String LOGGING_FEATURE_SEPARATOR = LOGGING_FEATURE_COMMON_PREFIX + SEPARATOR_POSTFIX;
 
     private static final String LOGGING_FEATURE_SERVER_PREFIX = "jersey.config.server.logging";
     /**
@@ -126,6 +138,10 @@
      * Server property for configuring a maximum number of bytes of entity to be logged.
      */
     public static final String LOGGING_FEATURE_MAX_ENTITY_SIZE_SERVER = LOGGING_FEATURE_SERVER_PREFIX + MAX_ENTITY_POSTFIX;
+    /**
+     * Server property for configuring separator.
+     */
+    public static final String LOGGING_FEATURE_SEPARATOR_SERVER = LOGGING_FEATURE_SERVER_PREFIX + SEPARATOR_POSTFIX;
 
     private static final String LOGGING_FEATURE_CLIENT_PREFIX = "jersey.config.client.logging";
     /**
@@ -144,11 +160,12 @@
      * Client property for configuring a maximum number of bytes of entity to be logged.
      */
     public static final String LOGGING_FEATURE_MAX_ENTITY_SIZE_CLIENT = LOGGING_FEATURE_CLIENT_PREFIX + MAX_ENTITY_POSTFIX;
+    /**
+     * Client property for logging separator.
+     */
+    public static final String LOGGING_FEATURE_SEPARATOR_CLIENT = LOGGING_FEATURE_CLIENT_PREFIX + SEPARATOR_POSTFIX;
 
-    private final Logger filterLogger;
-    private final Verbosity verbosity;
-    private final Integer maxEntitySize;
-    private final Level level;
+    private final LoggingFeatureBuilder builder;
 
     /**
      * Creates the feature with default values.
@@ -199,32 +216,62 @@
      *                      and print "...more..." string at the end. Negative values are interpreted as zero.
      */
     public LoggingFeature(Logger logger, Level level, Verbosity verbosity, Integer maxEntitySize) {
-        this.filterLogger = logger;
-        this.level = level;
-        this.verbosity = verbosity;
-        this.maxEntitySize = maxEntitySize;
+
+        this(LoggingFeature.builder()
+                .withLogger(logger)
+                .level(level)
+                .verbosity(verbosity)
+                .maxEntitySize(maxEntitySize)
+                .separator(DEFAULT_SEPARATOR)
+        );
+
+    }
+
+    /**
+     * Constructor based on logging feature builder. All parameters are passed through a builder instance.
+     *
+     * @param builder instance of a builder with required logging feature parameters
+     */
+    public LoggingFeature(LoggingFeatureBuilder builder) {
+        this.builder = builder;
     }
 
     @Override
     public boolean configure(FeatureContext context) {
-        boolean enabled = false;
+        boolean enabled = context.getConfiguration().getRuntimeType() != null;
 
-        if (context.getConfiguration().getRuntimeType() == RuntimeType.CLIENT) {
-            ClientLoggingFilter clientLoggingFilter = (ClientLoggingFilter) createLoggingFilter(context, RuntimeType.CLIENT);
-            context.register(clientLoggingFilter);
-            enabled = true;
+        if (enabled) {
+            context.register(createLoggingFilter(context, context.getConfiguration().getRuntimeType()));
         }
-        if (context.getConfiguration().getRuntimeType() == RuntimeType.SERVER) {
-            ServerLoggingFilter serverClientFilter = (ServerLoggingFilter) createLoggingFilter(context, RuntimeType.SERVER);
-            context.register(serverClientFilter);
-            enabled = true;
-        }
+
         return enabled;
     }
 
+    /**
+     * builder method to create  LoggingFeature with required settings
+     *
+     * @return Builder for LoggingFeature
+     */
+    public static LoggingFeatureBuilder builder() {
+        return new LoggingFeatureBuilder();
+    }
+
     private LoggingInterceptor createLoggingFilter(FeatureContext context, RuntimeType runtimeType) {
-        Map properties = context.getConfiguration().getProperties();
-        String filterLoggerName = CommonProperties.getValue(
+
+        final LoggingFeatureBuilder loggingBuilder =
+                configureBuilderParameters(builder, context, runtimeType);
+
+        return (runtimeType == RuntimeType.SERVER)
+                ? new ServerLoggingFilter(loggingBuilder)
+                : new ClientLoggingFilter(loggingBuilder);
+    }
+
+    private static LoggingFeatureBuilder configureBuilderParameters(LoggingFeatureBuilder builder,
+                                                   FeatureContext context, RuntimeType runtimeType) {
+
+        final Map properties = context.getConfiguration().getProperties();
+        //get values from properties (if any)
+        final String filterLoggerName = CommonProperties.getValue(
                 properties,
                 runtimeType == RuntimeType.SERVER ? LOGGING_FEATURE_LOGGER_NAME_SERVER : LOGGING_FEATURE_LOGGER_NAME_CLIENT,
                 CommonProperties.getValue(
@@ -232,14 +279,21 @@
                         LOGGING_FEATURE_LOGGER_NAME,
                         DEFAULT_LOGGER_NAME
                 ));
-        String filterLevel = CommonProperties.getValue(
+        final String filterLevel = CommonProperties.getValue(
                 properties,
                 runtimeType == RuntimeType.SERVER ? LOGGING_FEATURE_LOGGER_LEVEL_SERVER : LOGGING_FEATURE_LOGGER_LEVEL_CLIENT,
                 CommonProperties.getValue(
                         context.getConfiguration().getProperties(),
                         LOGGING_FEATURE_LOGGER_LEVEL,
                         DEFAULT_LOGGER_LEVEL));
-        Verbosity filterVerbosity = CommonProperties.getValue(
+        final String filterSeparator = CommonProperties.getValue(
+                properties,
+                runtimeType == RuntimeType.SERVER ? LOGGING_FEATURE_SEPARATOR_SERVER : LOGGING_FEATURE_SEPARATOR_CLIENT,
+                CommonProperties.getValue(
+                        context.getConfiguration().getProperties(),
+                        LOGGING_FEATURE_SEPARATOR,
+                        DEFAULT_SEPARATOR));
+        final Verbosity filterVerbosity = CommonProperties.getValue(
                 properties,
                 runtimeType == RuntimeType.SERVER ? LOGGING_FEATURE_VERBOSITY_SERVER : LOGGING_FEATURE_VERBOSITY_CLIENT,
                 CommonProperties.getValue(
@@ -257,19 +311,16 @@
                         DEFAULT_MAX_ENTITY_SIZE
                 ));
 
-        Level loggerLevel = Level.parse(filterLevel);
+        final Level loggerLevel = Level.parse(filterLevel);
 
-        if (runtimeType == RuntimeType.SERVER) {
-            return new ServerLoggingFilter(filterLogger != null ? filterLogger : Logger.getLogger(filterLoggerName),
-                    level != null ? level : loggerLevel,
-                    verbosity != null ? verbosity : filterVerbosity,
-                    maxEntitySize != null ? maxEntitySize : filterMaxEntitySize);
-        } else {
-            return new ClientLoggingFilter(filterLogger != null ? filterLogger : Logger.getLogger(filterLoggerName),
-                    level != null ? level : loggerLevel,
-                    verbosity != null ? verbosity : filterVerbosity,
-                    maxEntitySize != null ? maxEntitySize : filterMaxEntitySize);
-        }
+        //configure builder vs properties values
+        builder.filterLogger = builder.filterLogger == null ? Logger.getLogger(filterLoggerName) : builder.filterLogger;
+        builder.verbosity = builder.verbosity == null ? filterVerbosity : builder.verbosity;
+        builder.maxEntitySize = builder.maxEntitySize == null ? filterMaxEntitySize : builder.maxEntitySize;
+        builder.level = builder.level == null ? loggerLevel : builder.level;
+        builder.separator = builder.separator == null ? filterSeparator : builder.separator;
+
+        return builder;
     }
 
     /**
@@ -313,4 +364,45 @@
          */
         PAYLOAD_ANY
     }
-}
+
+    /**
+     * Builder class for logging feature configuration. Accepts parameters for the filter logger, verbosity, max
+     * entity size, level, and separator.
+     */
+    public static class LoggingFeatureBuilder {
+
+        Logger filterLogger;
+        Verbosity verbosity;
+        Integer maxEntitySize;
+        Level level;
+        String separator;
+
+        public LoggingFeatureBuilder() {
+
+        }
+        public LoggingFeatureBuilder withLogger(Logger logger) {
+            this.filterLogger = logger;
+            return this;
+        }
+        public LoggingFeatureBuilder verbosity(Verbosity verbosity) {
+            this.verbosity = verbosity;
+            return this;
+        }
+        public LoggingFeatureBuilder maxEntitySize(Integer maxEntitySize) {
+            this.maxEntitySize = maxEntitySize;
+            return this;
+        }
+        public LoggingFeatureBuilder level(Level level) {
+            this.level = level;
+            return this;
+        }
+        public LoggingFeatureBuilder separator(String separator) {
+            this.separator = separator;
+            return this;
+        }
+
+        public LoggingFeature build() {
+            return new LoggingFeature(this);
+        }
+    }
+}
\ No newline at end of file
diff --git a/core-common/src/main/java/org/glassfish/jersey/logging/LoggingFeatureAutoDiscoverable.java b/core-common/src/main/java/org/glassfish/jersey/logging/LoggingFeatureAutoDiscoverable.java
index 205ed08..01e61a5 100644
--- a/core-common/src/main/java/org/glassfish/jersey/logging/LoggingFeatureAutoDiscoverable.java
+++ b/core-common/src/main/java/org/glassfish/jersey/logging/LoggingFeatureAutoDiscoverable.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2016, 2019 Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2016, 2021 Oracle and/or its affiliates. All rights reserved.
  *
  * This program and the accompanying materials are made available under the
  * terms of the Eclipse Public License v. 2.0, which is available at
@@ -34,6 +34,9 @@
 import static org.glassfish.jersey.logging.LoggingFeature.LOGGING_FEATURE_MAX_ENTITY_SIZE;
 import static org.glassfish.jersey.logging.LoggingFeature.LOGGING_FEATURE_MAX_ENTITY_SIZE_CLIENT;
 import static org.glassfish.jersey.logging.LoggingFeature.LOGGING_FEATURE_MAX_ENTITY_SIZE_SERVER;
+import static org.glassfish.jersey.logging.LoggingFeature.LOGGING_FEATURE_SEPARATOR;
+import static org.glassfish.jersey.logging.LoggingFeature.LOGGING_FEATURE_SEPARATOR_CLIENT;
+import static org.glassfish.jersey.logging.LoggingFeature.LOGGING_FEATURE_SEPARATOR_SERVER;
 import static org.glassfish.jersey.logging.LoggingFeature.LOGGING_FEATURE_VERBOSITY;
 import static org.glassfish.jersey.logging.LoggingFeature.LOGGING_FEATURE_VERBOSITY_CLIENT;
 import static org.glassfish.jersey.logging.LoggingFeature.LOGGING_FEATURE_VERBOSITY_SERVER;
@@ -71,20 +74,23 @@
         return properties.containsKey(LOGGING_FEATURE_LOGGER_NAME)
                 || properties.containsKey(LOGGING_FEATURE_LOGGER_LEVEL)
                 || properties.containsKey(LOGGING_FEATURE_VERBOSITY)
-                || properties.containsKey(LOGGING_FEATURE_MAX_ENTITY_SIZE);
+                || properties.containsKey(LOGGING_FEATURE_MAX_ENTITY_SIZE)
+                || properties.containsKey(LOGGING_FEATURE_SEPARATOR);
     }
 
     private boolean clientConfigured(Map properties) {
         return properties.containsKey(LOGGING_FEATURE_LOGGER_NAME_CLIENT)
                 || properties.containsKey(LOGGING_FEATURE_LOGGER_LEVEL_CLIENT)
                 || properties.containsKey(LOGGING_FEATURE_VERBOSITY_CLIENT)
-                || properties.containsKey(LOGGING_FEATURE_MAX_ENTITY_SIZE_CLIENT);
+                || properties.containsKey(LOGGING_FEATURE_MAX_ENTITY_SIZE_CLIENT)
+                || properties.containsKey(LOGGING_FEATURE_SEPARATOR_CLIENT);
     }
 
     private boolean serverConfigured(Map properties) {
         return properties.containsKey(LOGGING_FEATURE_LOGGER_NAME_SERVER)
                 || properties.containsKey(LOGGING_FEATURE_LOGGER_LEVEL_SERVER)
                 || properties.containsKey(LOGGING_FEATURE_VERBOSITY_SERVER)
-                || properties.containsKey(LOGGING_FEATURE_MAX_ENTITY_SIZE_SERVER);
+                || properties.containsKey(LOGGING_FEATURE_MAX_ENTITY_SIZE_SERVER)
+                || properties.containsKey(LOGGING_FEATURE_SEPARATOR_SERVER);
     }
 }
diff --git a/core-common/src/main/java/org/glassfish/jersey/logging/LoggingInterceptor.java b/core-common/src/main/java/org/glassfish/jersey/logging/LoggingInterceptor.java
index 7ad26ec..601d6b2 100644
--- a/core-common/src/main/java/org/glassfish/jersey/logging/LoggingInterceptor.java
+++ b/core-common/src/main/java/org/glassfish/jersey/logging/LoggingInterceptor.java
@@ -103,23 +103,28 @@
     final AtomicLong _id = new AtomicLong(0);
     final Verbosity verbosity;
     final int maxEntitySize;
+    final String separator;
 
     /**
-     * Creates a logging filter with custom logger and entity logging turned on, but potentially limiting the size
-     * of entity to be buffered and logged.
+     * Creates a logging filter using builder instance with custom logger and entity logging turned on,
+     * but potentially limiting the size of entity to be buffered and logged.
      *
-     * @param logger        the logger to log messages to.
-     * @param level         level at which the messages will be logged.
-     * @param verbosity     verbosity of the logged messages. See {@link Verbosity}.
-     * @param maxEntitySize maximum number of entity bytes to be logged (and buffered) - if the entity is larger,
+     * @param builder       loggingFeatureBuilder which contains values for:
+     *  logger         the logger to log messages to.
+     *  level          level at which the messages will be logged.
+     *  verbosity      verbosity of the logged messages. See {@link Verbosity}.
+     *  maxEntitySize  maximum number of entity bytes to be logged (and buffered) - if the entity is larger,
      *                      logging filter will print (and buffer in memory) only the specified number of bytes
      *                      and print "...more..." string at the end. Negative values are interpreted as zero.
+     *  separator      delimiter for particular log lines. Default is Linux new line delimiter
      */
-    LoggingInterceptor(final Logger logger, final Level level, final Verbosity verbosity, final int maxEntitySize) {
-        this.logger = logger;
-        this.level = level;
-        this.verbosity = verbosity;
-        this.maxEntitySize = Math.max(0, maxEntitySize);
+
+    LoggingInterceptor(LoggingFeature.LoggingFeatureBuilder builder) {
+        this.logger = builder.filterLogger;
+        this.level = builder.level;
+        this.verbosity = builder.verbosity;
+        this.maxEntitySize = Math.max(0, builder.maxEntitySize);
+        this.separator = builder.separator;
     }
 
     /**
@@ -142,18 +147,18 @@
         prefixId(b, id).append(NOTIFICATION_PREFIX)
                 .append(note)
                 .append(" on thread ").append(Thread.currentThread().getName())
-                .append("\n");
+                .append(separator);
         prefixId(b, id).append(REQUEST_PREFIX).append(method).append(" ")
-                .append(uri.toASCIIString()).append("\n");
+                .append(uri.toASCIIString()).append(separator);
     }
 
     void printResponseLine(final StringBuilder b, final String note, final long id, final int status) {
         prefixId(b, id).append(NOTIFICATION_PREFIX)
                 .append(note)
-                .append(" on thread ").append(Thread.currentThread().getName()).append("\n");
+                .append(" on thread ").append(Thread.currentThread().getName()).append(separator);
         prefixId(b, id).append(RESPONSE_PREFIX)
                 .append(Integer.toString(status))
-                .append("\n");
+                .append(separator);
     }
 
     void printPrefixedHeaders(final StringBuilder b,
@@ -165,7 +170,7 @@
             final String header = headerEntry.getKey();
 
             if (val.size() == 1) {
-                prefixId(b, id).append(prefix).append(header).append(": ").append(val.get(0)).append("\n");
+                prefixId(b, id).append(prefix).append(header).append(": ").append(val.get(0)).append(separator);
             } else {
                 final StringBuilder sb = new StringBuilder();
                 boolean add = false;
@@ -176,7 +181,7 @@
                     add = true;
                     sb.append(s);
                 }
-                prefixId(b, id).append(prefix).append(header).append(": ").append(sb.toString()).append("\n");
+                prefixId(b, id).append(prefix).append(header).append(": ").append(sb.toString()).append(separator);
             }
         }
     }
diff --git a/core-common/src/main/java/org/glassfish/jersey/logging/ServerLoggingFilter.java b/core-common/src/main/java/org/glassfish/jersey/logging/ServerLoggingFilter.java
index 8405edf..b1c2c7b 100644
--- a/core-common/src/main/java/org/glassfish/jersey/logging/ServerLoggingFilter.java
+++ b/core-common/src/main/java/org/glassfish/jersey/logging/ServerLoggingFilter.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2016, 2019 Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2016, 2021 Oracle and/or its affiliates. All rights reserved.
  *
  * This program and the accompanying materials are made available under the
  * terms of the Eclipse Public License v. 2.0, which is available at
@@ -53,18 +53,20 @@
 final class ServerLoggingFilter extends LoggingInterceptor implements ContainerRequestFilter, ContainerResponseFilter {
 
     /**
-     * Create a logging filter with custom logger and custom settings of entity
+     * Create a logging filter using builder instance with custom logger and custom settings of entity
      * logging.
      *
-     * @param logger        the logger to log messages to.
-     * @param level         level at which the messages will be logged.
-     * @param verbosity     verbosity of the logged messages. See {@link Verbosity}.
-     * @param maxEntitySize maximum number of entity bytes to be logged (and buffered) - if the entity is larger,
+     * @param builder       loggingFeatureBuilder which contains values for:
+     *  logger         the logger to log messages to.
+     *  level          level at which the messages will be logged.
+     *  verbosity      verbosity of the logged messages. See {@link Verbosity}.
+     *  maxEntitySize  maximum number of entity bytes to be logged (and buffered) - if the entity is larger,
      *                      logging filter will print (and buffer in memory) only the specified number of bytes
      *                      and print "...more..." string at the end. Negative values are interpreted as zero.
+     *  separator      delimiter for particular log lines. Default is Linux new line delimiter
      */
-    public ServerLoggingFilter(final Logger logger, final Level level, final Verbosity verbosity, final int maxEntitySize) {
-        super(logger, level, verbosity, maxEntitySize);
+    public ServerLoggingFilter(final LoggingFeature.LoggingFeatureBuilder builder) {
+        super(builder);
     }
 
     @Override
diff --git a/core-common/src/test/java/org/glassfish/jersey/logging/LoggingInterceptorTest.java b/core-common/src/test/java/org/glassfish/jersey/logging/LoggingInterceptorTest.java
index 5516273..c19cb19 100644
--- a/core-common/src/test/java/org/glassfish/jersey/logging/LoggingInterceptorTest.java
+++ b/core-common/src/test/java/org/glassfish/jersey/logging/LoggingInterceptorTest.java
@@ -126,7 +126,7 @@
     @Test
     public void testLogInboundEntityMockedStream() throws Exception {
         int maxEntitySize = 20;
-        LoggingInterceptor loggingInterceptor = new LoggingInterceptor(null, null, null, maxEntitySize) {};
+        LoggingInterceptor loggingInterceptor = new LoggingInterceptor(LoggingFeature.builder().maxEntitySize(maxEntitySize)) {};
 
         StringBuilder buffer = new StringBuilder();
         InputStream stream = mock(InputStream.class);
@@ -162,7 +162,7 @@
         int maxEntitySize = 2000;
         String inputString = getRandomString(maxEntitySize * 2);
 
-        LoggingInterceptor loggingInterceptor = new LoggingInterceptor(null, null, null, maxEntitySize) {};
+        LoggingInterceptor loggingInterceptor = new LoggingInterceptor(LoggingFeature.builder().maxEntitySize(maxEntitySize)) {};
         StringBuilder buffer = new StringBuilder();
         InputStream stream = new ByteArrayInputStream(inputString.getBytes());
 
diff --git a/docs/src/main/docbook/appendix-properties.xml b/docs/src/main/docbook/appendix-properties.xml
index 3eecfaf..6011f89 100644
--- a/docs/src/main/docbook/appendix-properties.xml
+++ b/docs/src/main/docbook/appendix-properties.xml
@@ -143,6 +143,17 @@
                             See <link linkend="logging.xml">logging</link> chapter for more information.
                         </entry>
                     </row>
+                    <row>
+                        <entry>&jersey.logging.LoggingFeature.LOGGING_FEATURE_SEPARATOR;
+                        </entry>
+                        <entry>
+                            <literal>jersey.config.logging.entity.separator</literal>
+                        </entry>
+                        <entry>
+                            Custom logging delimiter for new lines separation.
+                            See <link linkend="logging.xml">logging</link> chapter for more information.
+                        </entry>
+                    </row>
                 </tbody>
             </tgroup>
         </table>
@@ -584,6 +595,17 @@
                             See <link linkend="logging.xml">logging</link> chapter for more information.
                         </entry>
                     </row>
+                    <row>
+                        <entry>&jersey.logging.LoggingFeature.LOGGING_FEATURE_SEPARATOR_SERVER;
+                        </entry>
+                        <entry>
+                            <literal>jersey.config.server.logging.entity.separator</literal>
+                        </entry>
+                        <entry>
+                            Custom delimiter for new lines separation.
+                            See <link linkend="logging.xml">logging</link> chapter for more information.
+                        </entry>
+                    </row>
                 </tbody>
             </tgroup>
         </table>
@@ -911,6 +933,17 @@
                             See <link linkend="logging_chapter">logging</link> chapter for more information.
                         </entry>
                     </row>
+                    <row>
+                        <entry>&jersey.logging.LoggingFeature.LOGGING_FEATURE_SEPARATOR_CLIENT;
+                        </entry>
+                        <entry>
+                            <literal>jersey.config.client.logging.entity.separator</literal>
+                        </entry>
+                        <entry>
+                            New line delimiter property (client side).
+                            See <link linkend="logging_chapter">logging</link> chapter for more information.
+                        </entry>
+                    </row>
                 </tbody>
             </tgroup>
         </table>
diff --git a/docs/src/main/docbook/jersey.ent b/docs/src/main/docbook/jersey.ent
index 9ebf7c2..fd87f28 100644
--- a/docs/src/main/docbook/jersey.ent
+++ b/docs/src/main/docbook/jersey.ent
@@ -1,7 +1,7 @@
 <?xml version="1.0" encoding="iso-8859-1" ?>
 <!--
 
-    Copyright (c) 2010, 2020 Oracle and/or its affiliates. All rights reserved.
+    Copyright (c) 2010, 2021 Oracle and/or its affiliates. All rights reserved.
 
     This program and the accompanying materials are made available under the
     terms of the Eclipse Public License v. 2.0, which is available at
@@ -409,14 +409,17 @@
 <!ENTITY jersey.logging.LoggingFeature.LOGGING_FEATURE_LOGGER_LEVEL "<link xlink:href='&jersey.javadoc.uri.prefix;/logging/LoggingFeature.html#LOGGING_FEATURE_LOGGER_LEVEL'>LoggingFeature.LOGGING_FEATURE_LOGGER_LEVEL</link>">
 <!ENTITY jersey.logging.LoggingFeature.LOGGING_FEATURE_VERBOSITY "<link xlink:href='&jersey.javadoc.uri.prefix;/logging/LoggingFeature.html#LOGGING_FEATURE_VERBOSITY'>LoggingFeature.LOGGING_FEATURE_VERBOSITY</link>">
 <!ENTITY jersey.logging.LoggingFeature.LOGGING_FEATURE_MAX_ENTITY_SIZE "<link xlink:href='&jersey.javadoc.uri.prefix;/logging/LoggingFeature.html#LOGGING_FEATURE_MAX_ENTITY_SIZE'>LoggingFeature.LOGGING_FEATURE_MAX_ENTITY_SIZE</link>">
+<!ENTITY jersey.logging.LoggingFeature.LOGGING_FEATURE_SEPARATOR "<link xlink:href='&jersey.javadoc.uri.prefix;/logging/LoggingFeature.html#LOGGING_FEATURE_SEPARATOR'>LoggingFeature.LOGGING_FEATURE_SEPARATOR</link>">
 <!ENTITY jersey.logging.LoggingFeature.LOGGING_FEATURE_LOGGER_NAME_CLIENT "<link xlink:href='&jersey.javadoc.uri.prefix;/logging/LoggingFeature.html#LOGGING_FEATURE_LOGGER_NAME_CLIENT'>LoggingFeature.LOGGING_FEATURE_LOGGER_NAME_CLIENT</link>">
 <!ENTITY jersey.logging.LoggingFeature.LOGGING_FEATURE_LOGGER_LEVEL_CLIENT "<link xlink:href='&jersey.javadoc.uri.prefix;/logging/LoggingFeature.html#LOGGING_FEATURE_LOGGER_LEVEL_CLIENT'>LoggingFeature.LOGGING_FEATURE_LOGGER_LEVEL_CLIENT</link>">
 <!ENTITY jersey.logging.LoggingFeature.LOGGING_FEATURE_VERBOSITY_CLIENT "<link xlink:href='&jersey.javadoc.uri.prefix;/logging/LoggingFeature.html#LOGGING_FEATURE_VERBOSITY_CLIENT'>LoggingFeature.LOGGING_FEATURE_VERBOSITY_CLIENT</link>">
 <!ENTITY jersey.logging.LoggingFeature.LOGGING_FEATURE_MAX_ENTITY_SIZE_CLIENT "<link xlink:href='&jersey.javadoc.uri.prefix;/logging/LoggingFeature.html#LOGGING_FEATURE_MAX_ENTITY_SIZE_CLIENT'>LoggingFeature.LOGGING_FEATURE_MAX_ENTITY_SIZE_CLIENT</link>">
+<!ENTITY jersey.logging.LoggingFeature.LOGGING_FEATURE_SEPARATOR_CLIENT "<link xlink:href='&jersey.javadoc.uri.prefix;/logging/LoggingFeature.html#LOGGING_FEATURE_MAX_ENTITY_SIZE_CLIENT'>LoggingFeature.LOGGING_FEATURE_SEPARATOR_CLIENT</link>">
 <!ENTITY jersey.logging.LoggingFeature.LOGGING_FEATURE_LOGGER_NAME_SERVER "<link xlink:href='&jersey.javadoc.uri.prefix;/logging/LoggingFeature.html#LOGGING_FEATURE_LOGGER_NAME_SERVER'>LoggingFeature.LOGGING_FEATURE_LOGGER_NAME_SERVER</link>">
 <!ENTITY jersey.logging.LoggingFeature.LOGGING_FEATURE_LOGGER_LEVEL_SERVER "<link xlink:href='&jersey.javadoc.uri.prefix;/logging/LoggingFeature.html#LOGGING_FEATURE_LOGGER_LEVEL_SERVER'>LoggingFeature.LOGGING_FEATURE_LOGGER_LEVEL_SERVER</link>">
 <!ENTITY jersey.logging.LoggingFeature.LOGGING_FEATURE_VERBOSITY_SERVER "<link xlink:href='&jersey.javadoc.uri.prefix;/logging/LoggingFeature.html#LOGGING_FEATURE_VERBOSITY_SERVER'>LoggingFeature.LOGGING_FEATURE_VERBOSITY_SERVER</link>">
 <!ENTITY jersey.logging.LoggingFeature.LOGGING_FEATURE_MAX_ENTITY_SIZE_SERVER "<link xlink:href='&jersey.javadoc.uri.prefix;/logging/LoggingFeature.html#LOGGING_FEATURE_MAX_ENTITY_SIZE_SERVER'>LoggingFeature.LOGGING_FEATURE_MAX_ENTITY_SIZE_SERVER</link>">
+<!ENTITY jersey.logging.LoggingFeature.LOGGING_FEATURE_SEPARATOR_SERVER "<link xlink:href='&jersey.javadoc.uri.prefix;/logging/LoggingFeature.html#LOGGING_FEATURE_MAX_ENTITY_SIZE_SERVER'>LoggingFeature.LOGGING_FEATURE_SEPARATOR_SERVER</link>">
 <!ENTITY jersey.logging.LoggingFeature.Verbosity "<link xlink:href='&jersey.javadoc.uri.prefix;/logging/LoggingFeature.Verbosity.html'>LoggingFeature.Verbosity</link>">
 <!ENTITY jersey.logging.LoggingFeature.Verbosity.HEADERS_ONLY "<link xlink:href='&jersey.javadoc.uri.prefix;/logging/LoggingFeature.Verbosity.html#HEADERS_ONLY'>LoggingFeature.Verbosity.HEADERS_ONLY</link>">
 <!ENTITY jersey.logging.LoggingFeature.Verbosity.PAYLOAD_ANY "<link xlink:href='&jersey.javadoc.uri.prefix;/logging/LoggingFeature.Verbosity.html#PAYLOAD_ANY'>LoggingFeature.Verbosity.PAYLOAD_ANY</link>">
@@ -892,14 +895,17 @@
 <!ENTITY lit.jersey.logging.LoggingFeature.LOGGING_FEATURE_LOGGER_LEVEL "<literal>LoggingFeature.LOGGING_FEATURE_LOGGER_LEVEL</literal>">
 <!ENTITY lit.jersey.logging.LoggingFeature.LOGGING_FEATURE_VERBOSITY "<literal>LoggingFeature.LOGGING_FEATURE_VERBOSITY</literal>">
 <!ENTITY lit.jersey.logging.LoggingFeature.LOGGING_FEATURE_MAX_ENTITY_SIZE "<literal>LoggingFeature.LOGGING_FEATURE_MAX_ENTITY_SIZE</literal>">
+<!ENTITY lit.jersey.logging.LoggingFeature.LOGGING_FEATURE_SEPARATOR "<literal>LoggingFeature.LOGGING_FEATURE_SEPARATOR</literal>">
 <!ENTITY lit.jersey.logging.LoggingFeature.LOGGING_FEATURE_LOGGER_NAME_CLIENT "<literal>LoggingFeature.LOGGING_FEATURE_LOGGER_NAME_CLIENT</literal>">
 <!ENTITY lit.jersey.logging.LoggingFeature.LOGGING_FEATURE_LOGGER_LEVEL_CLIENT "<literal>LoggingFeature.LOGGING_FEATURE_LOGGER_LEVEL_CLIENT</literal>">
 <!ENTITY lit.jersey.logging.LoggingFeature.LOGGING_FEATURE_VERBOSITY_CLIENT "<literal>LoggingFeature.LOGGING_FEATURE_VERBOSITY_CLIENT</literal>">
 <!ENTITY lit.jersey.logging.LoggingFeature.LOGGING_FEATURE_MAX_ENTITY_SIZE_CLIENT "<literal>LoggingFeature.LOGGING_FEATURE_MAX_ENTITY_SIZE_CLIENT</literal>">
+<!ENTITY lit.jersey.logging.LoggingFeature.LOGGING_FEATURE_SEPARATOR_CLIENT "<literal>LoggingFeature.LOGGING_FEATURE_SEPARATORE_CLIENT</literal>">
 <!ENTITY lit.jersey.logging.LoggingFeature.LOGGING_FEATURE_LOGGER_NAME_SERVER "<literal>LoggingFeature.LOGGING_FEATURE_LOGGER_NAME_SERVER</literal>">
 <!ENTITY lit.jersey.logging.LoggingFeature.LOGGING_FEATURE_LOGGER_LEVEL_SERVER "<literal>LoggingFeature.LOGGING_FEATURE_LOGGER_LEVEL_SERVER</literal>">
 <!ENTITY lit.jersey.logging.LoggingFeature.LOGGING_FEATURE_VERBOSITY_SERVER "<literal>LoggingFeature.LOGGING_FEATURE_VERBOSITY_SERVER</literal>">
 <!ENTITY lit.jersey.logging.LoggingFeature.LOGGING_FEATURE_MAX_ENTITY_SIZE_SERVER "<literal>LoggingFeature.LOGGING_FEATURE_MAX_ENTITY_SIZE_SERVER</literal>">
+<!ENTITY lit.jersey.logging.LoggingFeature.LOGGING_FEATURE_SEPARATOR_SERVER "<literal>LoggingFeature.LOGGING_FEATURE_SEPARATOR_SERVER</literal>">
 <!ENTITY lit.jersey.logging.LoggingFeature.Verbosity "<literal>LoggingFeature.Verbosity</literal>">
 <!ENTITY lit.jersey.logging.LoggingFeature.Verbosity.HEADERS_ONLY "<literal>LoggingFeature.Verbosity.HEADERS_ONLY</literal>">
 <!ENTITY lit.jersey.logging.LoggingFeature.Verbosity.PAYLOAD_ANY "<literal>LoggingFeature.Verbosity.PAYLOAD_ANY</literal>">
@@ -1044,4 +1050,4 @@
 <!ENTITY lit.jersey.test.util.LoopBackConnectorProvider "<literal>LoopBackConnectorProvider</literal>">
 <!ENTITY lit.jersey.test.util.ContainerRequestBuilder "<literal>ContainerRequestBuilder</literal>">
 
-<!ENTITY lit.jsonb.Jsonb "<literal>Jsonb</literal>">
\ No newline at end of file
+<!ENTITY lit.jsonb.Jsonb "<literal>Jsonb</literal>">
diff --git a/tests/e2e/src/test/java/org/glassfish/jersey/tests/e2e/common/LoggingFeatureTest.java b/tests/e2e/src/test/java/org/glassfish/jersey/tests/e2e/common/LoggingFeatureTest.java
index 280e94d..b85b8df 100644
--- a/tests/e2e/src/test/java/org/glassfish/jersey/tests/e2e/common/LoggingFeatureTest.java
+++ b/tests/e2e/src/test/java/org/glassfish/jersey/tests/e2e/common/LoggingFeatureTest.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2013, 2020 Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2013, 2021 Oracle and/or its affiliates. All rights reserved.
  *
  * This program and the accompanying materials are made available under the
  * terms of the Eclipse Public License v. 2.0, which is available at
@@ -76,6 +76,7 @@
     private static final String BINARY_MEDIA_TYPE = "application/binary";
     private static final String TEXT_MEDIA_TYPE = MediaType.TEXT_PLAIN;
     private static final String ENTITY = "This entity must (not) be logged";
+    private static final String SEPARATOR = "!-------!";
 
     @Path("/")
     public static class MyResource {
@@ -245,6 +246,44 @@
             assertThat(getLoggingFilterLogRecord(getLoggedRecords()).get(0).getMessage(), containsString(ENTITY));
         }
 
+        @Test
+        public void testLoggingFeatureBuilderSeparator() {
+            final Response response = target("/text")
+                    .register(LoggingFeature
+                            .builder()
+                            .withLogger(Logger.getLogger(LOGGER_NAME))
+                            .verbosity(LoggingFeature.Verbosity.PAYLOAD_ANY)
+                            .separator(SEPARATOR)
+                            .build()
+                    ).request()
+                    .post(Entity.text(ENTITY));
+
+            // Correct response status.
+            assertThat(response.getStatus(), is(Response.Status.OK.getStatusCode()));
+            // Check logs for proper separator.
+            final LogRecord record = getLoggingFilterResponseLogRecord(getLoggedRecords());
+            assertThat(record.getMessage(), containsString(SEPARATOR));
+
+        }
+
+        @Test
+        public void testLoggingFeatureBuilderProperty() {
+            final Response response = target("/text")
+                    .register(LoggingFeature
+                            .builder()
+                            .withLogger(Logger.getLogger(LOGGER_NAME))
+                            .build()
+                    ).property(LoggingFeature.LOGGING_FEATURE_SEPARATOR, SEPARATOR)
+                    .request()
+                    .post(Entity.text(ENTITY));
+
+            // Correct response status.
+            assertThat(response.getStatus(), is(Response.Status.OK.getStatusCode()));
+            // Check logs for proper separator.
+            final LogRecord record = getLoggingFilterResponseLogRecord(getLoggedRecords());
+            assertThat(record.getMessage(), containsString(SEPARATOR));
+        }
+
     }
 
     /**