Browse Source

Add Configuration to PatternLayout (#97679)

in 2.17.2 (patch release) log4j has made a refactoring that requires a Configuration to be manually
passed into the created PatternLayout
If the Configuration is not passed, the System Variable lookup will not work
This results in cluster.name field not being populated in logs if ESv7 layout was used on ESv8 cluster (cloud)

This commit creates a PatternLayout with a DefaultConfiguration (the same was used previous to the refactoring)
Also adds back tests that verifies ESv8 cluster with ESv7 logging config. We still have that situation in the cloud so it is better to have tests around this.

relates apache/logging-log4j2#1551
Przemyslaw Gomulka 2 years ago
parent
commit
3516cdfe47

+ 5 - 0
docs/changelog/97679.yaml

@@ -0,0 +1,5 @@
+pr: 97679
+summary: Add Configuration to `PatternLayout`
+area: Infra/Logging
+type: bug
+issues: []

+ 27 - 1
qa/logging-config/build.gradle

@@ -5,8 +5,34 @@
  * in compliance with, at your election, the Elastic License 2.0 or the Server
  * Side Public License, v 1.
  */
+apply plugin: 'elasticsearch.build'
+apply plugin: 'elasticsearch.legacy-java-rest-test'
 
-apply plugin: 'elasticsearch.standalone-test'
+dependencies {
+  testImplementation project(":libs:elasticsearch-x-content")
+  testImplementation project(":test:framework")
+
+}
+
+testClusters.all {
+  setting 'xpack.security.enabled', 'false'
+}
+
+testClusters.matching { it.name == "javaRestTest" }.configureEach {
+  /**
+   * Provide a custom log4j configuration where layout is an old style pattern and confirm that Elasticsearch
+   * can successfully startup.
+   */
+  extraConfigFile 'log4j2.properties', file('es-v7-log4j2.properties')
+}
+
+tasks.named("javaRestTest").configure {
+  nonInputProperties.systemProperty 'tests.logfile',
+    "${-> testClusters.javaRestTest.singleNode().getServerLog().absolutePath.replaceAll("_server.json", ".log")}"
+
+  nonInputProperties.systemProperty 'tests.jsonLogfile',
+    "${-> testClusters.javaRestTest.singleNode().getServerLog()}"
+}
 
 tasks.named("test").configure {
   systemProperty 'tests.security.manager', 'false'

+ 187 - 0
qa/logging-config/es-v7-log4j2.properties

@@ -0,0 +1,187 @@
+#
+# Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+# or more contributor license agreements. Licensed under the Elastic License
+# 2.0 and the Server Side Public License, v 1; you may not use this file except
+# in compliance with, at your election, the Elastic License 2.0 or the Server
+# Side Public License, v 1.
+#
+
+status = error
+
+appender.console.type = Console
+appender.console.name = console
+appender.console.layout.type = PatternLayout
+appender.console.layout.pattern = [%d{ISO8601}][%-5p][%-25c{1.}] [%node_name]%marker %m%n
+
+######## Server JSON ############################
+appender.rolling.type = RollingFile
+appender.rolling.name = rolling
+appender.rolling.fileName = ${sys:es.logs.base_path}${sys:file.separator}${sys:es.logs.cluster_name}_server.json
+appender.rolling.layout.type = ESJsonLayout
+appender.rolling.layout.type_name = server
+
+appender.rolling.filePattern = ${sys:es.logs.base_path}${sys:file.separator}${sys:es.logs.cluster_name}-%d{yyyy-MM-dd}-%i.json.gz
+appender.rolling.policies.type = Policies
+appender.rolling.policies.time.type = TimeBasedTriggeringPolicy
+appender.rolling.policies.time.interval = 1
+appender.rolling.policies.time.modulate = true
+appender.rolling.policies.size.type = SizeBasedTriggeringPolicy
+appender.rolling.policies.size.size = 128MB
+appender.rolling.strategy.type = DefaultRolloverStrategy
+appender.rolling.strategy.fileIndex = nomax
+appender.rolling.strategy.action.type = Delete
+appender.rolling.strategy.action.basepath = ${sys:es.logs.base_path}
+appender.rolling.strategy.action.condition.type = IfFileName
+appender.rolling.strategy.action.condition.glob = ${sys:es.logs.cluster_name}-*
+appender.rolling.strategy.action.condition.nested_condition.type = IfAccumulatedFileSize
+appender.rolling.strategy.action.condition.nested_condition.exceeds = 2GB
+################################################
+######## Server -  old style pattern ###########
+appender.rolling_old.type = RollingFile
+appender.rolling_old.name = rolling_old
+appender.rolling_old.fileName = ${sys:es.logs.base_path}${sys:file.separator}${sys:es.logs.cluster_name}.log
+appender.rolling_old.layout.type = PatternLayout
+appender.rolling_old.layout.pattern = [%d{ISO8601}][%-5p][%-25c{1.}] [%node_name]%marker %m%n
+
+appender.rolling_old.filePattern = ${sys:es.logs.base_path}${sys:file.separator}${sys:es.logs.cluster_name}-%d{yyyy-MM-dd}-%i.log.gz
+appender.rolling_old.policies.type = Policies
+appender.rolling_old.policies.time.type = TimeBasedTriggeringPolicy
+appender.rolling_old.policies.time.interval = 1
+appender.rolling_old.policies.time.modulate = true
+appender.rolling_old.policies.size.type = SizeBasedTriggeringPolicy
+appender.rolling_old.policies.size.size = 128MB
+appender.rolling_old.strategy.type = DefaultRolloverStrategy
+appender.rolling_old.strategy.fileIndex = nomax
+appender.rolling_old.strategy.action.type = Delete
+appender.rolling_old.strategy.action.basepath = ${sys:es.logs.base_path}
+appender.rolling_old.strategy.action.condition.type = IfFileName
+appender.rolling_old.strategy.action.condition.glob = ${sys:es.logs.cluster_name}-*
+appender.rolling_old.strategy.action.condition.nested_condition.type = IfAccumulatedFileSize
+appender.rolling_old.strategy.action.condition.nested_condition.exceeds = 2GB
+################################################
+
+rootLogger.level = info
+rootLogger.appenderRef.console.ref = console
+rootLogger.appenderRef.rolling.ref = rolling
+rootLogger.appenderRef.rolling_old.ref = rolling_old
+
+######## Deprecation JSON #######################
+appender.deprecation_rolling.type = RollingFile
+appender.deprecation_rolling.name = deprecation_rolling
+appender.deprecation_rolling.fileName = ${sys:es.logs.base_path}${sys:file.separator}${sys:es.logs.cluster_name}_deprecation.json
+appender.deprecation_rolling.layout.type = ESJsonLayout
+appender.deprecation_rolling.layout.type_name = deprecation
+appender.deprecation_rolling.layout.esmessagefields=x-opaque-id
+appender.deprecation_rolling.filter.rate_limit.type = RateLimitingFilter
+
+appender.deprecation_rolling.filePattern = ${sys:es.logs.base_path}${sys:file.separator}${sys:es.logs.cluster_name}_deprecation-%i.json.gz
+appender.deprecation_rolling.policies.type = Policies
+appender.deprecation_rolling.policies.size.type = SizeBasedTriggeringPolicy
+appender.deprecation_rolling.policies.size.size = 1GB
+appender.deprecation_rolling.strategy.type = DefaultRolloverStrategy
+appender.deprecation_rolling.strategy.max = 4
+#################################################
+######## Deprecation -  old style pattern #######
+appender.deprecation_rolling_old.type = RollingFile
+appender.deprecation_rolling_old.name = deprecation_rolling_old
+appender.deprecation_rolling_old.fileName = ${sys:es.logs.base_path}${sys:file.separator}${sys:es.logs.cluster_name}_deprecation.log
+appender.deprecation_rolling_old.layout.type = PatternLayout
+appender.deprecation_rolling_old.layout.pattern = [%d{ISO8601}][%-5p][%-25c{1.}] [%node_name]%marker %m%n
+appender.deprecation_rolling_old.filter.rate_limit.type = RateLimitingFilter
+
+appender.deprecation_rolling_old.filePattern = ${sys:es.logs.base_path}${sys:file.separator}${sys:es.logs.cluster_name}\
+  _deprecation-%i.log.gz
+appender.deprecation_rolling_old.policies.type = Policies
+appender.deprecation_rolling_old.policies.size.type = SizeBasedTriggeringPolicy
+appender.deprecation_rolling_old.policies.size.size = 1GB
+appender.deprecation_rolling_old.strategy.type = DefaultRolloverStrategy
+appender.deprecation_rolling_old.strategy.max = 4
+#################################################
+appender.header_warning.type = HeaderWarningAppender
+appender.header_warning.name = header_warning
+#################################################
+logger.deprecation.name = org.elasticsearch.deprecation
+logger.deprecation.level = deprecation
+logger.deprecation.appenderRef.deprecation_rolling.ref = deprecation_rolling
+logger.deprecation.appenderRef.deprecation_rolling_old.ref = deprecation_rolling_old
+logger.deprecation.appenderRef.header_warning.ref = header_warning
+logger.deprecation.additivity = false
+
+######## Search slowlog JSON ####################
+appender.index_search_slowlog_rolling.type = RollingFile
+appender.index_search_slowlog_rolling.name = index_search_slowlog_rolling
+appender.index_search_slowlog_rolling.fileName = ${sys:es.logs.base_path}${sys:file.separator}${sys:es.logs\
+  .cluster_name}_index_search_slowlog.json
+appender.index_search_slowlog_rolling.layout.type = ESJsonLayout
+appender.index_search_slowlog_rolling.layout.type_name = index_search_slowlog
+appender.index_search_slowlog_rolling.layout.esmessagefields=message,took,took_millis,total_hits,types,stats,search_type,total_shards,source,id
+
+appender.index_search_slowlog_rolling.filePattern = ${sys:es.logs.base_path}${sys:file.separator}${sys:es.logs\
+  .cluster_name}_index_search_slowlog-%i.json.gz
+appender.index_search_slowlog_rolling.policies.type = Policies
+appender.index_search_slowlog_rolling.policies.size.type = SizeBasedTriggeringPolicy
+appender.index_search_slowlog_rolling.policies.size.size = 1GB
+appender.index_search_slowlog_rolling.strategy.type = DefaultRolloverStrategy
+appender.index_search_slowlog_rolling.strategy.max = 4
+#################################################
+######## Search slowlog -  old style pattern ####
+appender.index_search_slowlog_rolling_old.type = RollingFile
+appender.index_search_slowlog_rolling_old.name = index_search_slowlog_rolling_old
+appender.index_search_slowlog_rolling_old.fileName = ${sys:es.logs.base_path}${sys:file.separator}${sys:es.logs.cluster_name}\
+  _index_search_slowlog.log
+appender.index_search_slowlog_rolling_old.layout.type = PatternLayout
+appender.index_search_slowlog_rolling_old.layout.pattern = [%d{ISO8601}][%-5p][%-25c{1.}] [%node_name]%marker %m%n
+
+appender.index_search_slowlog_rolling_old.filePattern = ${sys:es.logs.base_path}${sys:file.separator}${sys:es.logs.cluster_name}\
+  _index_search_slowlog-%i.log.gz
+appender.index_search_slowlog_rolling_old.policies.type = Policies
+appender.index_search_slowlog_rolling_old.policies.size.type = SizeBasedTriggeringPolicy
+appender.index_search_slowlog_rolling_old.policies.size.size = 1GB
+appender.index_search_slowlog_rolling_old.strategy.type = DefaultRolloverStrategy
+appender.index_search_slowlog_rolling_old.strategy.max = 4
+#################################################
+logger.index_search_slowlog_rolling.name = index.search.slowlog
+logger.index_search_slowlog_rolling.level = trace
+logger.index_search_slowlog_rolling.appenderRef.index_search_slowlog_rolling.ref = index_search_slowlog_rolling
+logger.index_search_slowlog_rolling.appenderRef.index_search_slowlog_rolling_old.ref = index_search_slowlog_rolling_old
+logger.index_search_slowlog_rolling.additivity = false
+
+######## Indexing slowlog JSON ##################
+appender.index_indexing_slowlog_rolling.type = RollingFile
+appender.index_indexing_slowlog_rolling.name = index_indexing_slowlog_rolling
+appender.index_indexing_slowlog_rolling.fileName = ${sys:es.logs.base_path}${sys:file.separator}${sys:es.logs.cluster_name}\
+  _index_indexing_slowlog.json
+appender.index_indexing_slowlog_rolling.layout.type = ESJsonLayout
+appender.index_indexing_slowlog_rolling.layout.type_name = index_indexing_slowlog
+appender.index_indexing_slowlog_rolling.layout.esmessagefields=message,took,took_millis,doc_type,id,routing,source
+
+appender.index_indexing_slowlog_rolling.filePattern = ${sys:es.logs.base_path}${sys:file.separator}${sys:es.logs.cluster_name}\
+  _index_indexing_slowlog-%i.json.gz
+appender.index_indexing_slowlog_rolling.policies.type = Policies
+appender.index_indexing_slowlog_rolling.policies.size.type = SizeBasedTriggeringPolicy
+appender.index_indexing_slowlog_rolling.policies.size.size = 1GB
+appender.index_indexing_slowlog_rolling.strategy.type = DefaultRolloverStrategy
+appender.index_indexing_slowlog_rolling.strategy.max = 4
+#################################################
+######## Indexing slowlog -  old style pattern ##
+appender.index_indexing_slowlog_rolling_old.type = RollingFile
+appender.index_indexing_slowlog_rolling_old.name = index_indexing_slowlog_rolling_old
+appender.index_indexing_slowlog_rolling_old.fileName = ${sys:es.logs.base_path}${sys:file.separator}${sys:es.logs.cluster_name}\
+  _index_indexing_slowlog.log
+appender.index_indexing_slowlog_rolling_old.layout.type = PatternLayout
+appender.index_indexing_slowlog_rolling_old.layout.pattern = [%d{ISO8601}][%-5p][%-25c{1.}] [%node_name]%marker %m%n
+
+appender.index_indexing_slowlog_rolling_old.filePattern = ${sys:es.logs.base_path}${sys:file.separator}${sys:es.logs.cluster_name}\
+  _index_indexing_slowlog-%i.log.gz
+appender.index_indexing_slowlog_rolling_old.policies.type = Policies
+appender.index_indexing_slowlog_rolling_old.policies.size.type = SizeBasedTriggeringPolicy
+appender.index_indexing_slowlog_rolling_old.policies.size.size = 1GB
+appender.index_indexing_slowlog_rolling_old.strategy.type = DefaultRolloverStrategy
+appender.index_indexing_slowlog_rolling_old.strategy.max = 4
+#################################################
+
+logger.index_indexing_slowlog.name = index.indexing.slowlog.index
+logger.index_indexing_slowlog.level = trace
+logger.index_indexing_slowlog.appenderRef.index_indexing_slowlog_rolling.ref = index_indexing_slowlog_rolling
+logger.index_indexing_slowlog.appenderRef.index_indexing_slowlog_rolling_old.ref = index_indexing_slowlog_rolling_old
+logger.index_indexing_slowlog.additivity = false

+ 84 - 0
qa/logging-config/src/javaRestTest/java/org/elasticsearch/common/logging/CustomLoggingConfigIT.java

@@ -0,0 +1,84 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License
+ * 2.0 and the Server Side Public License, v 1; you may not use this file except
+ * in compliance with, at your election, the Elastic License 2.0 or the Server
+ * Side Public License, v 1.
+ */
+package org.elasticsearch.common.logging;
+
+import org.elasticsearch.core.SuppressForbidden;
+import org.elasticsearch.test.hamcrest.RegexMatcher;
+import org.elasticsearch.test.rest.ESRestTestCase;
+import org.hamcrest.Matchers;
+
+import java.io.IOException;
+import java.io.UncheckedIOException;
+import java.nio.charset.StandardCharsets;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.security.AccessController;
+import java.security.PrivilegedAction;
+import java.util.List;
+
+/**
+ * This test verifies that Elasticsearch can startup successfully with a custom logging config using variables introduced in
+ * <code>ESJsonLayout</code>
+ * The intention is to confirm that users can still run their Elasticsearch instances with previous configurations.
+ */
+public class CustomLoggingConfigIT extends ESRestTestCase {
+    // we are looking for a line where pattern contains:
+    // [2020-03-20T14:51:59,989][INFO ][o.e.g.GatewayService ] [integTest-0] recovered [0] indices into cluster_state
+    private static final String NODE_STARTED = ".*recovered.*cluster_state.*";
+
+    public void testSuccessfulStartupWithCustomConfig() throws Exception {
+        assertBusy(() -> {
+            List<String> lines = readAllLines(getPlaintextLogFile());
+            assertThat(lines, Matchers.hasItem(RegexMatcher.matches(NODE_STARTED)));
+        });
+    }
+
+    public void testParseAllV7JsonLines() throws Exception {
+        assertBusy(() -> {
+            List<String> lines = readAllLines(getJSONLogFile());
+            assertThat(lines, Matchers.hasItem(RegexMatcher.matches(NODE_STARTED)));
+        });
+    }
+
+    private List<String> readAllLines(Path logFile) {
+        return AccessController.doPrivileged((PrivilegedAction<List<String>>) () -> {
+            try {
+                return Files.readAllLines(logFile, StandardCharsets.UTF_8);
+            } catch (IOException e) {
+                throw new UncheckedIOException(e);
+            }
+        });
+    }
+
+    @SuppressForbidden(reason = "PathUtils doesn't have permission to read this file")
+    private Path getJSONLogFile() {
+        String logFileString = System.getProperty("tests.logfile");
+        if (logFileString == null) {
+            fail(
+                "tests.logfile must be set to run this test. It is automatically "
+                    + "set by gradle. If you must set it yourself then it should be the absolute path to the "
+                    + "log file."
+            );
+        }
+        return Paths.get(logFileString);
+    }
+
+    @SuppressForbidden(reason = "PathUtils doesn't have permission to read this file")
+    private Path getPlaintextLogFile() {
+        String logFileString = System.getProperty("tests.logfile");
+        if (logFileString == null) {
+            fail(
+                "tests.logfile must be set to run this test. It is automatically "
+                    + "set by gradle. If you must set it yourself then it should be the absolute path to the "
+                    + "log file."
+            );
+        }
+        return Paths.get(logFileString);
+    }
+}

+ 59 - 0
qa/logging-config/src/javaRestTest/java/org/elasticsearch/common/logging/ESJsonLogsConfigIT.java

@@ -0,0 +1,59 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License
+ * 2.0 and the Server Side Public License, v 1; you may not use this file except
+ * in compliance with, at your election, the Elastic License 2.0 or the Server
+ * Side Public License, v 1.
+ */
+package org.elasticsearch.common.logging;
+
+import org.elasticsearch.xcontent.ObjectParser;
+import org.hamcrest.Matcher;
+
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.nio.charset.StandardCharsets;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.security.AccessController;
+import java.security.PrivilegedAction;
+
+import static org.hamcrest.Matchers.is;
+
+/**
+ * Test to verify ES JSON log format. Used in ES v7. Some users might decide to keep that format.
+ */
+public class ESJsonLogsConfigIT extends JsonLogsIntegTestCase {
+    @Override
+    protected Matcher<String> nodeNameMatcher() {
+        return is("javaRestTest-0");
+    }
+
+    @Override
+    protected BufferedReader openReader(Path logFile) {
+        assumeFalse(
+            "Skipping test because it is being run against an external cluster.",
+            logFile.getFileName().toString().equals("--external--")
+        );
+
+        return AccessController.doPrivileged((PrivilegedAction<BufferedReader>) () -> {
+            try {
+                String temp = Files.readString(logFile);
+
+                return Files.newBufferedReader(logFile, StandardCharsets.UTF_8);
+            } catch (IOException e) {
+                throw new RuntimeException(e);
+            }
+        });
+    }
+
+    @Override
+    protected String getLogFileName() {
+        return System.getProperty("tests.jsonLogfile");
+    }
+
+    @Override
+    protected ObjectParser<JsonLogLine, Void> getParser() {
+        return JsonLogLine.ES_LOG_LINE;
+    }
+}

+ 5 - 0
qa/logging-config/src/javaRestTest/resources/plugin-security.policy

@@ -0,0 +1,5 @@
+grant {
+  // Needed to read the log file
+  permission java.io.FilePermission "${tests.logfile}", "read";
+  permission java.io.FilePermission "${tests.jsonLogfile}", "read";
+};

+ 7 - 1
qa/logging-config/src/test/java/org/elasticsearch/common/logging/ESJsonLayoutTests.java

@@ -7,7 +7,9 @@
  */
 package org.elasticsearch.common.logging;
 
+import org.apache.logging.log4j.core.impl.Log4jLogEvent;
 import org.elasticsearch.core.Strings;
+import org.elasticsearch.core.SuppressForbidden;
 import org.elasticsearch.test.ESTestCase;
 import org.hamcrest.Matchers;
 import org.junit.BeforeClass;
@@ -15,6 +17,7 @@ import org.junit.BeforeClass;
 public class ESJsonLayoutTests extends ESTestCase {
     @BeforeClass
     public static void initNodeName() {
+        assert "false".equals(System.getProperty("tests.security.manager")) : "-Dtests.security.manager=false has to be set";
         JsonLogsTestSetup.init();
     }
 
@@ -22,10 +25,11 @@ public class ESJsonLayoutTests extends ESTestCase {
         expectThrows(IllegalArgumentException.class, () -> ESJsonLayout.newBuilder().build());
     }
 
+    @SuppressForbidden(reason = "Need to test that a system property can be looked up in logs")
     public void testLayout() {
+        System.setProperty("es.logs.cluster_name", "cluster123");
         ESJsonLayout server = ESJsonLayout.newBuilder().setType("server").build();
         String conversionPattern = server.getPatternLayout().getConversionPattern();
-
         assertThat(conversionPattern, Matchers.equalTo(Strings.format("""
             {\
             "type": "server", \
@@ -37,6 +41,8 @@ public class ESJsonLayoutTests extends ESTestCase {
             "message": "%%notEmpty{%%enc{%%marker}{JSON} }%%enc{%%.-10000m}{JSON}"%%notEmpty{, \
             %%node_and_cluster_id }%%notEmpty{, %%CustomMapFields }%%exceptionAsJson \
             }%n""")));
+
+        assertThat(server.toSerializable(new Log4jLogEvent()), Matchers.containsString("\"cluster.name\": \"cluster123\""));
     }
 
     public void testLayoutWithAdditionalFieldOverride() {

+ 12 - 4
server/src/main/java/org/elasticsearch/common/logging/ESJsonLayout.java

@@ -10,10 +10,13 @@ package org.elasticsearch.common.logging;
 
 import org.apache.logging.log4j.core.Layout;
 import org.apache.logging.log4j.core.LogEvent;
+import org.apache.logging.log4j.core.config.Configuration;
+import org.apache.logging.log4j.core.config.DefaultConfiguration;
 import org.apache.logging.log4j.core.config.Node;
 import org.apache.logging.log4j.core.config.plugins.Plugin;
 import org.apache.logging.log4j.core.config.plugins.PluginAttribute;
 import org.apache.logging.log4j.core.config.plugins.PluginBuilderFactory;
+import org.apache.logging.log4j.core.config.plugins.PluginConfiguration;
 import org.apache.logging.log4j.core.config.plugins.PluginFactory;
 import org.apache.logging.log4j.core.layout.AbstractStringLayout;
 import org.apache.logging.log4j.core.layout.ByteBufferDestination;
@@ -64,12 +67,13 @@ public class ESJsonLayout extends AbstractStringLayout {
     private final PatternLayout patternLayout;
     private String esmessagefields;
 
-    protected ESJsonLayout(String typeName, Charset charset, String[] overrideFields) {
+    protected ESJsonLayout(String typeName, Charset charset, String[] overrideFields, final Configuration config) {
         super(charset);
         this.esmessagefields = String.join(",", overrideFields);
         this.patternLayout = PatternLayout.newBuilder()
             .withPattern(pattern(typeName, overrideFields))
             .withAlwaysWriteExceptions(false)
+            .withConfiguration(config)
             .build();
     }
 
@@ -135,8 +139,8 @@ public class ESJsonLayout extends AbstractStringLayout {
     }
 
     @PluginFactory
-    public static ESJsonLayout createLayout(String type, Charset charset, String[] overrideFields) {
-        return new ESJsonLayout(type, charset, overrideFields);
+    public static ESJsonLayout createLayout(String type, Charset charset, String[] overrideFields, Configuration configuration) {
+        return new ESJsonLayout(type, charset, overrideFields, configuration);
     }
 
     PatternLayout getPatternLayout() {
@@ -156,6 +160,9 @@ public class ESJsonLayout extends AbstractStringLayout {
         @PluginAttribute("esmessagefields")
         private String overrideFields;
 
+        @PluginConfiguration
+        private Configuration config;
+
         public Builder() {
             setCharset(StandardCharsets.UTF_8);
         }
@@ -163,7 +170,7 @@ public class ESJsonLayout extends AbstractStringLayout {
         @Override
         public ESJsonLayout build() {
             String[] split = Strings.isNullOrEmpty(overrideFields) ? new String[] {} : overrideFields.split(",");
-            return ESJsonLayout.createLayout(type, charset, split);
+            return ESJsonLayout.createLayout(type, charset, split, new DefaultConfiguration());
         }
 
         public Charset getCharset() {
@@ -192,6 +199,7 @@ public class ESJsonLayout extends AbstractStringLayout {
             this.overrideFields = overrideFields;
             return asBuilder();
         }
+
     }
 
     @PluginBuilderFactory