From 36ac0928d9de0d46f99897dd9c3ac40c6ecb7b79 Mon Sep 17 00:00:00 2001 From: Rory Hunter Date: Wed, 17 Jun 2020 11:47:59 +0100 Subject: [PATCH 01/48] Introduce DeprecationIndexingService --- .../logging/DeprecationIndexingService.java | 96 +++++++++++++++++++ .../common/logging/DeprecationLogger.java | 13 ++- .../common/logging/RateLimiter.java | 51 ++++++++++ .../ThrottlingAndHeaderWarningLogger.java | 45 +++++++-- .../common/logging/ThrottlingLogger.java | 79 --------------- .../common/settings/ClusterSettings.java | 3 + .../java/org/elasticsearch/node/Node.java | 6 ++ 7 files changed, 206 insertions(+), 87 deletions(-) create mode 100644 server/src/main/java/org/elasticsearch/common/logging/DeprecationIndexingService.java create mode 100644 server/src/main/java/org/elasticsearch/common/logging/RateLimiter.java delete mode 100644 server/src/main/java/org/elasticsearch/common/logging/ThrottlingLogger.java diff --git a/server/src/main/java/org/elasticsearch/common/logging/DeprecationIndexingService.java b/server/src/main/java/org/elasticsearch/common/logging/DeprecationIndexingService.java new file mode 100644 index 0000000000000..ff06da60fdac3 --- /dev/null +++ b/server/src/main/java/org/elasticsearch/common/logging/DeprecationIndexingService.java @@ -0,0 +1,96 @@ +package org.elasticsearch.common.logging; + +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; +import org.elasticsearch.action.ActionListener; +import org.elasticsearch.action.DocWriteRequest; +import org.elasticsearch.action.index.IndexAction; +import org.elasticsearch.action.index.IndexRequestBuilder; +import org.elasticsearch.action.index.IndexResponse; +import org.elasticsearch.client.Client; +import org.elasticsearch.client.OriginSettingClient; +import org.elasticsearch.cluster.ClusterChangedEvent; +import org.elasticsearch.cluster.ClusterStateListener; +import org.elasticsearch.cluster.service.ClusterService; +import org.elasticsearch.common.settings.Setting; + +import java.time.Instant; +import java.util.HashMap; +import java.util.Map; + +import static org.elasticsearch.common.Strings.isNullOrEmpty; + +public class DeprecationIndexingService implements ClusterStateListener { + private static final Logger LOGGER = LogManager.getLogger(DeprecationIndexingService.class); + private static final String DATA_STREAM_NAME = "deprecation-elasticsearch-default"; + private static final String DEPRECATION_ORIGIN = "deprecation"; + + public static final Setting WRITE_DEPRECATION_LOGS_TO_INDEX = Setting.boolSetting( + "cluster.deprecation_logs.write_to_index", + true, + Setting.Property.NodeScope, + Setting.Property.Dynamic + ); + + private final Client client; + private boolean isEnabled = true; + + public DeprecationIndexingService(ClusterService clusterService, Client client) { + this.client = new OriginSettingClient(client, DEPRECATION_ORIGIN); + + clusterService.addListener(this); + + // TODO create data stream template + } + + /** + * Indexes a deprecation message in an ECS format + * + * @param key the key that was used to determine if this deprecation should have been be logged. + * Useful when aggregating the recorded messages. + * @param message the message to log + * @param xOpaqueId the associated "X-Opaque-ID" header value, if any + * @param params parameters to the message, if any + */ + public void writeMessage(String key, String message, String xOpaqueId, Object[] params) { + if (this.isEnabled == false) { + return; + } + + Map payload = new HashMap<>(); + + payload.put("@timestamp", Instant.now().toString()); + payload.put("message", message); + payload.put("tags", new String[] { key }); + + if (isNullOrEmpty(xOpaqueId) == false) { + // This seems to be the most appropriate location. There's also `transaction.id` + // Or would it be clearer to just use 'x-opaque-id'? + payload.put("trace.id", xOpaqueId); + } + + if (params != null && params.length > 0) { + payload.put("params", params); + } + + new IndexRequestBuilder(client, IndexAction.INSTANCE).setIndex(DATA_STREAM_NAME) + .setOpType(DocWriteRequest.OpType.CREATE) + .setSource(payload) + .execute(new ActionListener<>() { + @Override + public void onResponse(IndexResponse indexResponse) { + // Nothing to do + } + + @Override + public void onFailure(Exception e) { + LOGGER.error("Failed to index deprecation message", e); + } + }); + } + + @Override + public void clusterChanged(ClusterChangedEvent event) { + this.isEnabled = WRITE_DEPRECATION_LOGS_TO_INDEX.get(event.state().getMetadata().settings()); + } +} diff --git a/server/src/main/java/org/elasticsearch/common/logging/DeprecationLogger.java b/server/src/main/java/org/elasticsearch/common/logging/DeprecationLogger.java index 3a5abf622b0cf..a0b77890df820 100644 --- a/server/src/main/java/org/elasticsearch/common/logging/DeprecationLogger.java +++ b/server/src/main/java/org/elasticsearch/common/logging/DeprecationLogger.java @@ -37,6 +37,7 @@ * @see ThrottlingAndHeaderWarningLogger for throttling and header warnings implementation details */ public class DeprecationLogger { + private static DeprecationIndexingService indexingService; private final ThrottlingAndHeaderWarningLogger deprecationLogger; /** @@ -45,8 +46,8 @@ public class DeprecationLogger { * it replaces "org.elasticsearch" with "org.elasticsearch.deprecation" to maintain * the "org.elasticsearch" namespace. */ - private DeprecationLogger(Logger parentLogger) { - deprecationLogger = new ThrottlingAndHeaderWarningLogger(parentLogger); + public DeprecationLogger(Logger parentLogger) { + deprecationLogger = new ThrottlingAndHeaderWarningLogger(deprecatedLoggerName(parentLogger), indexingService); } public static DeprecationLogger getLogger(Class aClass) { @@ -79,6 +80,14 @@ public static void removeThreadContext(ThreadContext threadContext) { HeaderWarning.removeThreadContext(threadContext); } + public static void setIndexingService(DeprecationIndexingService indexingService) { + DeprecationLogger.indexingService = indexingService; + } + + public static void removeIndexingService() { + DeprecationLogger.indexingService = null; + } + /** * Logs a deprecation message, adding a formatted warning message as a response header on the thread context. * The deprecation message will be throttled to deprecation log. diff --git a/server/src/main/java/org/elasticsearch/common/logging/RateLimiter.java b/server/src/main/java/org/elasticsearch/common/logging/RateLimiter.java new file mode 100644 index 0000000000000..b4a39068f85b3 --- /dev/null +++ b/server/src/main/java/org/elasticsearch/common/logging/RateLimiter.java @@ -0,0 +1,51 @@ +/* + * Licensed to Elasticsearch under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch 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. + */ + +package org.elasticsearch.common.logging; + +import java.util.Collections; +import java.util.LinkedHashMap; +import java.util.Map; +import java.util.Set; + +/** + *

+ * This class limits the number of times that actions are carried out. + *

+ * The throttling algorithm relies on a LRU set of keys, which evicts entries when its size exceeds 128. + * When a {@code key} is seen for the first time, the {@code runnable} will be executed, but then will not be + * executed again for that key until the key is removed from the set. + */ +class RateLimiter { + + // LRU set of keys used to determine if a message should be emitted to the logs + private final Set keys = Collections.newSetFromMap(Collections.synchronizedMap(new LinkedHashMap<>() { + @Override + protected boolean removeEldestEntry(final Map.Entry eldest) { + return size() > 128; + } + })); + + void limit(String key, Runnable runnable) { + boolean shouldRun = keys.add(key); + if (shouldRun) { + runnable.run(); + } + } +} diff --git a/server/src/main/java/org/elasticsearch/common/logging/ThrottlingAndHeaderWarningLogger.java b/server/src/main/java/org/elasticsearch/common/logging/ThrottlingAndHeaderWarningLogger.java index 0dec2b45d0244..84bab3ee29d64 100644 --- a/server/src/main/java/org/elasticsearch/common/logging/ThrottlingAndHeaderWarningLogger.java +++ b/server/src/main/java/org/elasticsearch/common/logging/ThrottlingAndHeaderWarningLogger.java @@ -20,16 +20,32 @@ package org.elasticsearch.common.logging; import org.apache.logging.log4j.Logger; +import org.apache.logging.log4j.message.Message; +import org.elasticsearch.common.SuppressLoggerChecks; +import org.elasticsearch.common.util.concurrent.ThreadContext; + +import java.security.AccessController; +import java.security.PrivilegedAction; /** - * This class wraps both HeaderWarningLogger and ThrottlingLogger - * which is a common use case across Elasticsearch + * This class composes {@link HeaderWarning}, {@link DeprecationIndexingService} and {@link Logger}, + * in order to apply a single message to multiple destination. + *

+ * Logging and indexing are throttled in order to avoid filling the destination with duplicates. + * Throttling is implemented using a mandatory per-message key combined with any X-Opaque-Id + * HTTP header value. This header allows throttling per user. This value is set in {@link ThreadContext}. + *

+ * TODO wrapping logging this way limits the usage of %location. It will think this is used from that class. */ class ThrottlingAndHeaderWarningLogger { - private final ThrottlingLogger throttlingLogger; + private final Logger logger; + private final RateLimiter rateLimiter; + private final DeprecationIndexingService indexingService; - ThrottlingAndHeaderWarningLogger(Logger logger) { - this.throttlingLogger = new ThrottlingLogger(logger); + ThrottlingAndHeaderWarningLogger(Logger logger, DeprecationIndexingService indexingService) { + this.logger = logger; + this.rateLimiter = new RateLimiter(); + this.indexingService = indexingService; } /** @@ -43,7 +59,24 @@ void throttleLogAndAddWarning(final String key, ESLogMessage message) { String messagePattern = message.getMessagePattern(); Object[] arguments = message.getArguments(); HeaderWarning.addWarning(messagePattern, arguments); - throttlingLogger.throttleLog(key, message); + + String xOpaqueId = HeaderWarning.getXOpaqueId(); + this.rateLimiter.limit(xOpaqueId + key, () -> { + log(message); + if (indexingService != null) { + indexingService.writeMessage(key, messagePattern, xOpaqueId, arguments); + } + }); } + private void log(Message message) { + AccessController.doPrivileged(new PrivilegedAction() { + @SuppressLoggerChecks(reason = "safely delegates to logger") + @Override + public Void run() { + logger.warn(message); + return null; + } + }); + } } diff --git a/server/src/main/java/org/elasticsearch/common/logging/ThrottlingLogger.java b/server/src/main/java/org/elasticsearch/common/logging/ThrottlingLogger.java deleted file mode 100644 index 072f7c5c05a0c..0000000000000 --- a/server/src/main/java/org/elasticsearch/common/logging/ThrottlingLogger.java +++ /dev/null @@ -1,79 +0,0 @@ -/* - * Licensed to Elasticsearch under one or more contributor - * license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright - * ownership. Elasticsearch 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. - */ - -package org.elasticsearch.common.logging; - -import org.apache.logging.log4j.Logger; -import org.apache.logging.log4j.message.Message; -import org.elasticsearch.common.SuppressLoggerChecks; - -import java.security.AccessController; -import java.security.PrivilegedAction; -import java.util.Collections; -import java.util.LinkedHashMap; -import java.util.Map; -import java.util.Set; - -/** - * TODO wrapping logging this way limits the usage of %location. It will think this is used from that class. - *

- * This is a wrapper around a logger that allows to throttle log messages. - * In order to throttle a key has to be used and throttling happens per each key combined with X-Opaque-Id. - * X-Opaque-Id allows throttling per user. This value is set in ThreadContext from X-Opaque-Id HTTP header. - *

- * The throttling algorithm is relying on LRU set of keys which evicts entries when its size is > 128. - * When a log with a key is emitted, it won't be logged again until the set reaches size 128 and the key is removed from the set. - * - * @see HeaderWarning - */ -class ThrottlingLogger { - - // LRU set of keys used to determine if a message should be emitted to the logs - private final Set keys = Collections.newSetFromMap(Collections.synchronizedMap(new LinkedHashMap() { - @Override - protected boolean removeEldestEntry(final Map.Entry eldest) { - return size() > 128; - } - })); - - private final Logger logger; - - ThrottlingLogger(Logger logger) { - this.logger = logger; - } - - void throttleLog(String key, Message message) { - String xOpaqueId = HeaderWarning.getXOpaqueId(); - boolean shouldLog = keys.add(xOpaqueId + key); - if (shouldLog) { - log(message); - } - } - - private void log(Message message) { - AccessController.doPrivileged(new PrivilegedAction() { - @SuppressLoggerChecks(reason = "safely delegates to logger") - @Override - public Void run() { - logger.warn(message); - return null; - } - }); - } -} diff --git a/server/src/main/java/org/elasticsearch/common/settings/ClusterSettings.java b/server/src/main/java/org/elasticsearch/common/settings/ClusterSettings.java index 74a80076c98c8..5bcb40d73470f 100644 --- a/server/src/main/java/org/elasticsearch/common/settings/ClusterSettings.java +++ b/server/src/main/java/org/elasticsearch/common/settings/ClusterSettings.java @@ -59,6 +59,8 @@ import org.elasticsearch.cluster.service.ClusterApplierService; import org.elasticsearch.cluster.service.ClusterService; import org.elasticsearch.cluster.service.MasterService; +import org.elasticsearch.common.logging.DeprecationIndexingService; +import org.elasticsearch.common.logging.DeprecationLogger; import org.elasticsearch.common.logging.Loggers; import org.elasticsearch.common.network.NetworkModule; import org.elasticsearch.common.network.NetworkService; @@ -197,6 +199,7 @@ public void apply(Settings value, Settings current, Settings previous) { ClusterRebalanceAllocationDecider.CLUSTER_ROUTING_ALLOCATION_ALLOW_REBALANCE_SETTING, ConcurrentRebalanceAllocationDecider.CLUSTER_ROUTING_ALLOCATION_CLUSTER_CONCURRENT_REBALANCE_SETTING, DanglingIndicesState.AUTO_IMPORT_DANGLING_INDICES_SETTING, + DeprecationIndexingService.WRITE_DEPRECATION_LOGS_TO_INDEX, EnableAllocationDecider.CLUSTER_ROUTING_ALLOCATION_ENABLE_SETTING, EnableAllocationDecider.CLUSTER_ROUTING_REBALANCE_ENABLE_SETTING, FilterAllocationDecider.CLUSTER_ROUTING_INCLUDE_GROUP_SETTING, diff --git a/server/src/main/java/org/elasticsearch/node/Node.java b/server/src/main/java/org/elasticsearch/node/Node.java index 68d1960ac41c6..4661550ac4c9c 100644 --- a/server/src/main/java/org/elasticsearch/node/Node.java +++ b/server/src/main/java/org/elasticsearch/node/Node.java @@ -70,6 +70,8 @@ import org.elasticsearch.common.inject.ModulesBuilder; import org.elasticsearch.common.io.stream.NamedWriteableRegistry; import org.elasticsearch.common.lease.Releasables; +import org.elasticsearch.common.logging.DeprecationIndexingService; +import org.elasticsearch.common.logging.DeprecationLogger; import org.elasticsearch.common.logging.HeaderWarning; import org.elasticsearch.common.logging.NodeAndClusterIdStateListener; import org.elasticsearch.common.network.NetworkAddress; @@ -388,6 +390,10 @@ protected Node(final Environment initialEnvironment, final ClusterInfoService clusterInfoService = newClusterInfoService(settings, clusterService, threadPool, client); final UsageService usageService = new UsageService(); + final DeprecationIndexingService deprecationIndexingService = new DeprecationIndexingService(clusterService, client); + DeprecationLogger.setIndexingService(deprecationIndexingService); + resourcesToClose.add(DeprecationLogger::removeIndexingService); + ModulesBuilder modules = new ModulesBuilder(); final MonitorService monitorService = new MonitorService(settings, nodeEnvironment, threadPool, clusterInfoService); ClusterModule clusterModule = new ClusterModule(settings, clusterService, clusterPlugins, clusterInfoService); From cf5a677427b163ee61f0cb0ce0026deb7fbcb603 Mon Sep 17 00:00:00 2001 From: Rory Hunter Date: Wed, 17 Jun 2020 15:50:42 +0100 Subject: [PATCH 02/48] First go at ensuring the data stream template is loaded --- .../logging/DeprecationIndexingService.java | 63 ++++++++++++++++--- ...cation-elasticsearch-default-template.json | 33 ++++++++++ 2 files changed, 87 insertions(+), 9 deletions(-) create mode 100644 server/src/main/resources/org/elasticsearch/common/logging/deprecation-elasticsearch-default-template.json diff --git a/server/src/main/java/org/elasticsearch/common/logging/DeprecationIndexingService.java b/server/src/main/java/org/elasticsearch/common/logging/DeprecationIndexingService.java index ff06da60fdac3..06edca6f00bb6 100644 --- a/server/src/main/java/org/elasticsearch/common/logging/DeprecationIndexingService.java +++ b/server/src/main/java/org/elasticsearch/common/logging/DeprecationIndexingService.java @@ -4,16 +4,24 @@ import org.apache.logging.log4j.Logger; import org.elasticsearch.action.ActionListener; import org.elasticsearch.action.DocWriteRequest; +import org.elasticsearch.action.admin.indices.template.put.PutComposableIndexTemplateAction; import org.elasticsearch.action.index.IndexAction; import org.elasticsearch.action.index.IndexRequestBuilder; import org.elasticsearch.action.index.IndexResponse; +import org.elasticsearch.action.support.master.AcknowledgedResponse; import org.elasticsearch.client.Client; import org.elasticsearch.client.OriginSettingClient; import org.elasticsearch.cluster.ClusterChangedEvent; import org.elasticsearch.cluster.ClusterStateListener; +import org.elasticsearch.cluster.metadata.ComposableIndexTemplate; import org.elasticsearch.cluster.service.ClusterService; import org.elasticsearch.common.settings.Setting; +import org.elasticsearch.common.xcontent.NamedXContentRegistry; +import org.elasticsearch.common.xcontent.XContentParser; +import org.elasticsearch.common.xcontent.json.JsonXContent; +import java.io.IOException; +import java.io.InputStream; import java.time.Instant; import java.util.HashMap; import java.util.Map; @@ -22,6 +30,8 @@ public class DeprecationIndexingService implements ClusterStateListener { private static final Logger LOGGER = LogManager.getLogger(DeprecationIndexingService.class); + private static final String TEMPLATE_NAME = "deprecation-elasticsearch-default-template"; + private static final String TEMPLATE_MAPPING = TEMPLATE_NAME + ".json"; private static final String DATA_STREAM_NAME = "deprecation-elasticsearch-default"; private static final String DEPRECATION_ORIGIN = "deprecation"; @@ -34,18 +44,16 @@ public class DeprecationIndexingService implements ClusterStateListener { private final Client client; private boolean isEnabled = true; + private boolean hasTriedToLoadTemplate = false; public DeprecationIndexingService(ClusterService clusterService, Client client) { this.client = new OriginSettingClient(client, DEPRECATION_ORIGIN); clusterService.addListener(this); - - // TODO create data stream template } /** - * Indexes a deprecation message in an ECS format - * + * Indexes a deprecation message. * @param key the key that was used to determine if this deprecation should have been be logged. * Useful when aggregating the recorded messages. * @param message the message to log @@ -58,15 +66,12 @@ public void writeMessage(String key, String message, String xOpaqueId, Object[] } Map payload = new HashMap<>(); - payload.put("@timestamp", Instant.now().toString()); + payload.put("key", key); payload.put("message", message); - payload.put("tags", new String[] { key }); if (isNullOrEmpty(xOpaqueId) == false) { - // This seems to be the most appropriate location. There's also `transaction.id` - // Or would it be clearer to just use 'x-opaque-id'? - payload.put("trace.id", xOpaqueId); + payload.put("x-opaque-id", xOpaqueId); } if (params != null && params.length > 0) { @@ -92,5 +97,45 @@ public void onFailure(Exception e) { @Override public void clusterChanged(ClusterChangedEvent event) { this.isEnabled = WRITE_DEPRECATION_LOGS_TO_INDEX.get(event.state().getMetadata().settings()); + + if (this.isEnabled == false || this.hasTriedToLoadTemplate == true) { + return; + } + + // We only ever try to load the template once, because if there's a problem, we'll spam + // the log with the failure on every cluster state update + this.hasTriedToLoadTemplate = true; + + if (event.state().getMetadata().templatesV2().containsKey(TEMPLATE_NAME)) { + return; + } + + loadTemplate(); + } + + private void loadTemplate() { + try (InputStream is = getClass().getResourceAsStream(TEMPLATE_MAPPING)) { + final XContentParser parser = JsonXContent.jsonXContent.createParser(NamedXContentRegistry.EMPTY, null, is); + + PutComposableIndexTemplateAction.Request request = new PutComposableIndexTemplateAction.Request(TEMPLATE_NAME); + request.cause("auto (deprecation indexing service)"); + request.indexTemplate(ComposableIndexTemplate.parse(parser)); + + this.client.execute(PutComposableIndexTemplateAction.INSTANCE, request, new ActionListener<>() { + @Override + public void onResponse(AcknowledgedResponse acknowledgedResponse) { + if (acknowledgedResponse.isAcknowledged() == false) { + LOGGER.error("The attempt to create a deprecations index template was not acknowledged."); + } + } + + @Override + public void onFailure(Exception e) { + LOGGER.error("Failed to create the deprecations index template: " + e.getMessage(), e); + } + }); + } catch (IOException e) { + LOGGER.error("Failed to load " + TEMPLATE_MAPPING + ": " + e.getMessage(), e); + } } } diff --git a/server/src/main/resources/org/elasticsearch/common/logging/deprecation-elasticsearch-default-template.json b/server/src/main/resources/org/elasticsearch/common/logging/deprecation-elasticsearch-default-template.json new file mode 100644 index 0000000000000..73d0c10c43785 --- /dev/null +++ b/server/src/main/resources/org/elasticsearch/common/logging/deprecation-elasticsearch-default-template.json @@ -0,0 +1,33 @@ +{ + "index_patterns": [ + "deprecation-elasticsearch-default" + ], + "template": { + "mappings": { + "properties": { + "@timestamp": { + "type": "date" + }, + "key": { + "ignore_above": 1024, + "type": "keyword" + }, + "message": { + "norms": false, + "type": "text" + }, + "x-opaque-id": { + "ignore_above": 1024, + "type": "keyword" + }, + "params": { + "ignore_above": 1024, + "type": "keyword" + } + } + } + }, + "data_stream": { + "timestamp_field": "@timestamp" + } +} From 86c3d7d58832b165ccb6cc25168b3277a7f10f01 Mon Sep 17 00:00:00 2001 From: Rory Hunter Date: Tue, 30 Jun 2020 16:38:36 +0100 Subject: [PATCH 03/48] Tweaks index name --- .../common/logging/DeprecationIndexingService.java | 6 ++++-- .../org/elasticsearch/common/logging/DeprecationLogger.java | 2 +- ...te.json => logs-deprecation-elasticsearch-template.json} | 0 3 files changed, 5 insertions(+), 3 deletions(-) rename server/src/main/resources/org/elasticsearch/common/logging/{deprecation-elasticsearch-default-template.json => logs-deprecation-elasticsearch-template.json} (100%) diff --git a/server/src/main/java/org/elasticsearch/common/logging/DeprecationIndexingService.java b/server/src/main/java/org/elasticsearch/common/logging/DeprecationIndexingService.java index 06edca6f00bb6..9edb528769207 100644 --- a/server/src/main/java/org/elasticsearch/common/logging/DeprecationIndexingService.java +++ b/server/src/main/java/org/elasticsearch/common/logging/DeprecationIndexingService.java @@ -30,9 +30,11 @@ public class DeprecationIndexingService implements ClusterStateListener { private static final Logger LOGGER = LogManager.getLogger(DeprecationIndexingService.class); - private static final String TEMPLATE_NAME = "deprecation-elasticsearch-default-template"; + + private static final String DATA_STREAM_NAME = "logs-deprecation-elasticsearch"; + private static final String TEMPLATE_NAME = DATA_STREAM_NAME + "-template"; private static final String TEMPLATE_MAPPING = TEMPLATE_NAME + ".json"; - private static final String DATA_STREAM_NAME = "deprecation-elasticsearch-default"; + private static final String DEPRECATION_ORIGIN = "deprecation"; public static final Setting WRITE_DEPRECATION_LOGS_TO_INDEX = Setting.boolSetting( diff --git a/server/src/main/java/org/elasticsearch/common/logging/DeprecationLogger.java b/server/src/main/java/org/elasticsearch/common/logging/DeprecationLogger.java index a0b77890df820..dc9f0d7f1e385 100644 --- a/server/src/main/java/org/elasticsearch/common/logging/DeprecationLogger.java +++ b/server/src/main/java/org/elasticsearch/common/logging/DeprecationLogger.java @@ -47,7 +47,7 @@ public class DeprecationLogger { * the "org.elasticsearch" namespace. */ public DeprecationLogger(Logger parentLogger) { - deprecationLogger = new ThrottlingAndHeaderWarningLogger(deprecatedLoggerName(parentLogger), indexingService); + deprecationLogger = new ThrottlingAndHeaderWarningLogger(deprecatedLoggerName(parentLogger.getName()), indexingService); } public static DeprecationLogger getLogger(Class aClass) { diff --git a/server/src/main/resources/org/elasticsearch/common/logging/deprecation-elasticsearch-default-template.json b/server/src/main/resources/org/elasticsearch/common/logging/logs-deprecation-elasticsearch-template.json similarity index 100% rename from server/src/main/resources/org/elasticsearch/common/logging/deprecation-elasticsearch-default-template.json rename to server/src/main/resources/org/elasticsearch/common/logging/logs-deprecation-elasticsearch-template.json From d74dc9af9391aefecd9229c67463c63ad083c386 Mon Sep 17 00:00:00 2001 From: Rory Hunter Date: Thu, 2 Jul 2020 11:33:54 +0100 Subject: [PATCH 04/48] WIP - Adding tests for DeprecationIndexingService --- .../logging/DeprecationIndexingService.java | 17 +- .../DeprecationIndexingServiceTests.java | 154 ++++++++++++++++++ 2 files changed, 168 insertions(+), 3 deletions(-) create mode 100644 server/src/test/java/org/elasticsearch/common/logging/DeprecationIndexingServiceTests.java diff --git a/server/src/main/java/org/elasticsearch/common/logging/DeprecationIndexingService.java b/server/src/main/java/org/elasticsearch/common/logging/DeprecationIndexingService.java index 9edb528769207..a501b22e4d1d9 100644 --- a/server/src/main/java/org/elasticsearch/common/logging/DeprecationIndexingService.java +++ b/server/src/main/java/org/elasticsearch/common/logging/DeprecationIndexingService.java @@ -28,6 +28,11 @@ import static org.elasticsearch.common.Strings.isNullOrEmpty; +/** + * This service is responsible for writing deprecation messages to a data stream. It also creates + * the data stream if necessary. The writing of messages can be toggled using the + * {@link #WRITE_DEPRECATION_LOGS_TO_INDEX} setting. + */ public class DeprecationIndexingService implements ClusterStateListener { private static final Logger LOGGER = LogManager.getLogger(DeprecationIndexingService.class); @@ -38,15 +43,15 @@ public class DeprecationIndexingService implements ClusterStateListener { private static final String DEPRECATION_ORIGIN = "deprecation"; public static final Setting WRITE_DEPRECATION_LOGS_TO_INDEX = Setting.boolSetting( - "cluster.deprecation_logs.write_to_index", + "cluster.deprecation_indexing.enabled", true, Setting.Property.NodeScope, Setting.Property.Dynamic ); private final Client client; - private boolean isEnabled = true; private boolean hasTriedToLoadTemplate = false; + private volatile boolean isEnabled = false; public DeprecationIndexingService(ClusterService clusterService, Client client) { this.client = new OriginSettingClient(client, DEPRECATION_ORIGIN); @@ -59,7 +64,7 @@ public DeprecationIndexingService(ClusterService clusterService, Client client) * @param key the key that was used to determine if this deprecation should have been be logged. * Useful when aggregating the recorded messages. * @param message the message to log - * @param xOpaqueId the associated "X-Opaque-ID" header value, if any + * @param xOpaqueId the associated "X-Opaque-ID" header value if any, or null * @param params parameters to the message, if any */ public void writeMessage(String key, String message, String xOpaqueId, Object[] params) { @@ -96,6 +101,9 @@ public void onFailure(Exception e) { }); } + /** + * Listens for changes to the cluster state, in order to know whether to toggle indexing. + */ @Override public void clusterChanged(ClusterChangedEvent event) { this.isEnabled = WRITE_DEPRECATION_LOGS_TO_INDEX.get(event.state().getMetadata().settings()); @@ -115,6 +123,9 @@ public void clusterChanged(ClusterChangedEvent event) { loadTemplate(); } + /* + * Attempts to load a template for the deprecation logs data stream + */ private void loadTemplate() { try (InputStream is = getClass().getResourceAsStream(TEMPLATE_MAPPING)) { final XContentParser parser = JsonXContent.jsonXContent.createParser(NamedXContentRegistry.EMPTY, null, is); diff --git a/server/src/test/java/org/elasticsearch/common/logging/DeprecationIndexingServiceTests.java b/server/src/test/java/org/elasticsearch/common/logging/DeprecationIndexingServiceTests.java new file mode 100644 index 0000000000000..0b2cf48faabda --- /dev/null +++ b/server/src/test/java/org/elasticsearch/common/logging/DeprecationIndexingServiceTests.java @@ -0,0 +1,154 @@ +/* + * Licensed to Elasticsearch under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch 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. + */ + +package org.elasticsearch.common.logging; + +import org.elasticsearch.action.ActionListener; +import org.elasticsearch.action.ActionRequest; +import org.elasticsearch.action.ActionResponse; +import org.elasticsearch.action.ActionType; +import org.elasticsearch.action.admin.indices.template.put.PutComposableIndexTemplateAction; +import org.elasticsearch.action.index.IndexAction; +import org.elasticsearch.action.index.IndexRequest; +import org.elasticsearch.action.index.IndexResponse; +import org.elasticsearch.action.support.master.AcknowledgedResponse; +import org.elasticsearch.client.Client; +import org.elasticsearch.client.support.AbstractClient; +import org.elasticsearch.cluster.ClusterChangedEvent; +import org.elasticsearch.cluster.ClusterName; +import org.elasticsearch.cluster.ClusterState; +import org.elasticsearch.cluster.metadata.Metadata; +import org.elasticsearch.cluster.service.ClusterService; +import org.elasticsearch.common.settings.Settings; +import org.elasticsearch.ingest.IngestDocument; +import org.elasticsearch.test.ESTestCase; +import org.elasticsearch.test.client.NoOpClient; +import org.junit.After; +import org.junit.Before; +import org.junit.Test; +import org.mockito.ArgumentCaptor; +import org.mockito.Mockito; + +import java.lang.reflect.MalformedParametersException; + +import static org.elasticsearch.common.logging.DeprecationIndexingService.WRITE_DEPRECATION_LOGS_TO_INDEX; +import static org.junit.Assert.*; +import static org.mockito.Matchers.any; +import static org.mockito.Matchers.anyBoolean; +import static org.mockito.Matchers.argThat; +import static org.mockito.Matchers.eq; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.spy; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +public class DeprecationIndexingServiceTests extends ESTestCase { + + private DeprecationIndexingService service; + private ClusterService clusterService; + private Client client; + + @Before + public void initialize() { + clusterService = mock(ClusterService.class); + client = spy(new MyNoOpClient()); + service = new DeprecationIndexingService(clusterService, client); + } + + @After + public void cleanup() { + client.close(); + } + + public void testClusterStateListenerRegistered() { + verify(clusterService).addListener(service); + } + + public void testCreatesTemplateOnDemand() { + service.clusterChanged(getEvent(true)); + + verify(client).execute(eq(PutComposableIndexTemplateAction.INSTANCE), any(), anyListener()); + } + + public void testDoesNotWriteMessageWhenServiceDisabled() { + service.writeMessage("key", "message", "xOpaqueId", null); + + verify(client, never()).execute(any(), any(), anyListener()); + } + + public void testWritesMessageWhenServiceEnabled() { + service.clusterChanged(getEvent(true)); + + service.writeMessage("key", "message", "xOpaqueId", null); + + ArgumentCaptor argument = ArgumentCaptor.forClass(IndexRequest.class); + + verify(client).execute(eq(IndexAction.INSTANCE), argument.capture(), anyListener()); + + final IndexRequest request = argument.getValue(); + + final String s = request.source().utf8ToString(); + + logger + .warn(""); + } + + public void testWritesMessageInExpectedFormat() { + service.clusterChanged(getEvent(true)); + + service.writeMessage("key", "message", "xOpaqueId", null); + + verify(client).execute(eq(IndexAction.INSTANCE), any(), anyListener()); + } + + private ClusterChangedEvent getEvent(boolean enabled) { + Settings settings = Settings.builder().put(WRITE_DEPRECATION_LOGS_TO_INDEX.getKey(), enabled).build(); + final Metadata metadata = Metadata.builder().transientSettings(settings).build(); + final ClusterState clusterState = ClusterState.builder(new ClusterName("test")).metadata(metadata).build(); + + return new ClusterChangedEvent("test", clusterState, clusterState); + } + + // This exists to silence a generics warning + private ActionListener anyListener() { + return any(); + } + + private class MyNoOpClient extends NoOpClient { + public MyNoOpClient() { + super(DeprecationIndexingServiceTests.this.getTestName()); + } + + @SuppressWarnings("unchecked") + @Override + protected void doExecute( + ActionType action, + Request request, + ActionListener listener + ) { + if (request instanceof PutComposableIndexTemplateAction.Request) { + listener.onResponse(((Response) new AcknowledgedResponse(true))); + return; + } + + super.doExecute(action, request, listener); + } + } +} From 6457f97ee2b404d26838e3e23a3b592c85cb188d Mon Sep 17 00:00:00 2001 From: Rory Hunter Date: Thu, 2 Jul 2020 14:53:56 +0100 Subject: [PATCH 05/48] More tests --- .../DeprecationIndexingServiceTests.java | 126 +++++++++++++++--- .../common/logging/RateLimiterTests.java | 81 +++++++++++ 2 files changed, 186 insertions(+), 21 deletions(-) create mode 100644 server/src/test/java/org/elasticsearch/common/logging/RateLimiterTests.java diff --git a/server/src/test/java/org/elasticsearch/common/logging/DeprecationIndexingServiceTests.java b/server/src/test/java/org/elasticsearch/common/logging/DeprecationIndexingServiceTests.java index 0b2cf48faabda..1d99430536e36 100644 --- a/server/src/test/java/org/elasticsearch/common/logging/DeprecationIndexingServiceTests.java +++ b/server/src/test/java/org/elasticsearch/common/logging/DeprecationIndexingServiceTests.java @@ -26,38 +26,35 @@ import org.elasticsearch.action.admin.indices.template.put.PutComposableIndexTemplateAction; import org.elasticsearch.action.index.IndexAction; import org.elasticsearch.action.index.IndexRequest; -import org.elasticsearch.action.index.IndexResponse; import org.elasticsearch.action.support.master.AcknowledgedResponse; import org.elasticsearch.client.Client; -import org.elasticsearch.client.support.AbstractClient; import org.elasticsearch.cluster.ClusterChangedEvent; import org.elasticsearch.cluster.ClusterName; import org.elasticsearch.cluster.ClusterState; import org.elasticsearch.cluster.metadata.Metadata; import org.elasticsearch.cluster.service.ClusterService; import org.elasticsearch.common.settings.Settings; -import org.elasticsearch.ingest.IngestDocument; import org.elasticsearch.test.ESTestCase; import org.elasticsearch.test.client.NoOpClient; import org.junit.After; import org.junit.Before; -import org.junit.Test; import org.mockito.ArgumentCaptor; -import org.mockito.Mockito; -import java.lang.reflect.MalformedParametersException; +import java.util.List; +import java.util.Map; import static org.elasticsearch.common.logging.DeprecationIndexingService.WRITE_DEPRECATION_LOGS_TO_INDEX; -import static org.junit.Assert.*; +import static org.hamcrest.Matchers.equalTo; +import static org.hamcrest.Matchers.hasEntry; +import static org.hamcrest.Matchers.hasKey; +import static org.hamcrest.Matchers.not; import static org.mockito.Matchers.any; -import static org.mockito.Matchers.anyBoolean; -import static org.mockito.Matchers.argThat; import static org.mockito.Matchers.eq; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.never; import static org.mockito.Mockito.spy; +import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.when; public class DeprecationIndexingServiceTests extends ESTestCase { @@ -77,45 +74,108 @@ public void cleanup() { client.close(); } + /** + * Checks that the service registers a cluster state listener, so that the service + * can be enabled and disabled. + */ public void testClusterStateListenerRegistered() { verify(clusterService).addListener(service); } + /** + * Checks that the service ensures that the data stream template is created when + * the service is enabled. + */ public void testCreatesTemplateOnDemand() { service.clusterChanged(getEvent(true)); verify(client).execute(eq(PutComposableIndexTemplateAction.INSTANCE), any(), anyListener()); } + /** + * Checks that the service only creates the data stream template once, + * and not every time it processes the cluster state. + */ + public void testOnlyCreatesTemplateOnce() { + service.clusterChanged(getEvent(true)); + service.clusterChanged(getEvent(true)); + + verify(client, times(1)).execute(eq(PutComposableIndexTemplateAction.INSTANCE), any(), anyListener()); + } + + /** + * Checks that the service only ensures that the data stream template is created once, + * even if it fails the first time, in order to prevent a flood of failure messages + * to the log. + */ + public void testOnlyCreatesTemplateOnceEvenOnFailure() { + ((MyNoOpClient) client).setAcknowledged(false); + service.clusterChanged(getEvent(true)); + service.clusterChanged(getEvent(true)); + + verify(client, times(1)).execute(eq(PutComposableIndexTemplateAction.INSTANCE), any(), anyListener()); + } + + /** + * Checks that the service does not attempt to index messages when the service + * is disabled. + */ public void testDoesNotWriteMessageWhenServiceDisabled() { service.writeMessage("key", "message", "xOpaqueId", null); verify(client, never()).execute(any(), any(), anyListener()); } - public void testWritesMessageWhenServiceEnabled() { + /** + * Checks that the service can be disabled after being enabled. + */ + public void testDoesNotWriteMessageWhenServiceEnabledAndDisabled() { service.clusterChanged(getEvent(true)); + service.clusterChanged(getEvent(false)); service.writeMessage("key", "message", "xOpaqueId", null); - ArgumentCaptor argument = ArgumentCaptor.forClass(IndexRequest.class); + verify(client, never()).execute(eq(IndexAction.INSTANCE), any(), anyListener()); + } - verify(client).execute(eq(IndexAction.INSTANCE), argument.capture(), anyListener()); + /** + * Checks that messages are indexed in the correct shape when the service is enabled. + */ + public void testWritesMessageWhenServiceEnabled() { + service.clusterChanged(getEvent(true)); - final IndexRequest request = argument.getValue(); + final Map payloadMap = getWriteRequest("a key", "a message", null, null); - final String s = request.source().utf8ToString(); + assertThat(payloadMap, hasKey("@timestamp")); + assertThat(payloadMap, hasEntry("key", "a key")); + assertThat(payloadMap, hasEntry("message", "a message")); + // Neither of these should exist since we passed null when writing the message + assertThat(payloadMap, not(hasKey("x-opaque-id"))); + assertThat(payloadMap, not(hasKey("params"))); + } + + /** + * Check that if an xOpaqueId is set, then it is added to the index request payload. + */ + public void testMessageIncludesOpaqueIdWhenSupplied() { + service.clusterChanged(getEvent(true)); + + final Map payloadMap = getWriteRequest("a key", "a message", "an ID", null); - logger - .warn(""); + assertThat(payloadMap, hasEntry("x-opaque-id", "an ID")); } - public void testWritesMessageInExpectedFormat() { + /** + * Check that if any params are set, then they are added to the index request payload. + */ + public void testMessageIncludesParamsWhenSupplied() { service.clusterChanged(getEvent(true)); - service.writeMessage("key", "message", "xOpaqueId", null); + final Map payloadMap = getWriteRequest("a key", "a message", null, new Object[] { "first", "second" }); - verify(client).execute(eq(IndexAction.INSTANCE), any(), anyListener()); + // I can't get this to work as a one-liner. Curse you, Hamcrest. + assertThat(payloadMap, hasKey("params")); + assertThat(payloadMap.get("params"), equalTo(List.of("first", "second"))); } private ClusterChangedEvent getEvent(boolean enabled) { @@ -131,11 +191,21 @@ private ActionListener anyListener() { return any(); } + /* + * A client that does nothing, except for requests of type + * PutComposableIndexTemplateAction.Request, in which case return an AcknowledgedResponse + */ private class MyNoOpClient extends NoOpClient { + private boolean isAcknowledged = true; + public MyNoOpClient() { super(DeprecationIndexingServiceTests.this.getTestName()); } + public void setAcknowledged(boolean acknowledged) { + isAcknowledged = acknowledged; + } + @SuppressWarnings("unchecked") @Override protected void doExecute( @@ -144,11 +214,25 @@ protected void ActionListener listener ) { if (request instanceof PutComposableIndexTemplateAction.Request) { - listener.onResponse(((Response) new AcknowledgedResponse(true))); + listener.onResponse(((Response) new AcknowledgedResponse(isAcknowledged))); return; } super.doExecute(action, request, listener); } + + } + + /* + * Wraps up the steps for extracting an index request payload from the mocks. + */ + private Map getWriteRequest(String key, String message, String xOpaqueId, Object[] params) { + service.writeMessage(key, message, xOpaqueId, params); + + ArgumentCaptor argument = ArgumentCaptor.forClass(IndexRequest.class); + + verify(client).execute(eq(IndexAction.INSTANCE), argument.capture(), anyListener()); + + return argument.getValue().sourceAsMap(); } } diff --git a/server/src/test/java/org/elasticsearch/common/logging/RateLimiterTests.java b/server/src/test/java/org/elasticsearch/common/logging/RateLimiterTests.java new file mode 100644 index 0000000000000..da556367ad501 --- /dev/null +++ b/server/src/test/java/org/elasticsearch/common/logging/RateLimiterTests.java @@ -0,0 +1,81 @@ +/* + * Licensed to Elasticsearch under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch 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. + */ + +package org.elasticsearch.common.logging; + +import org.elasticsearch.test.ESTestCase; + +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; + +public class RateLimiterTests extends ESTestCase { + + /** + * Check that the limiter will only execute a runnable once when + * execute twice with the same key. + */ + public void testOnlyRunsOnceForSameKey() { + final RateLimiter rateLimiter = new RateLimiter(); + + Runnable runnable = mock(Runnable.class); + + rateLimiter.limit("a key", runnable); + rateLimiter.limit("a key", runnable); + + verify(runnable, times(1)).run(); + } + + /** + * Check that the limiter will execute a runnable more than once when + * executed with different keys. + */ + public void testRunsMoreThanOnceForDifferentKeys() { + final RateLimiter rateLimiter = new RateLimiter(); + + Runnable runnable = mock(Runnable.class); + + rateLimiter.limit("a key", runnable); + rateLimiter.limit("another key", runnable); + + verify(runnable, times(2)).run(); + } + + /** + * Check that the limiter will execute a runnable again for a given key if that + * key is evicted from the key cache. + */ + public void testKeyCacheEntryEventuallyExpires() { + final RateLimiter rateLimiter = new RateLimiter(); + + Runnable runnable = mock(Runnable.class); + Runnable otherRunnable = mock(Runnable.class); + + rateLimiter.limit("key0", runnable); + + // Fill the key cache so that "key0" is evicted + for (int i = 1; i <= 128; i++) { + rateLimiter.limit("key" + i, otherRunnable); + } + + rateLimiter.limit("key0", runnable); + + verify(runnable, times(2)).run(); + } +} From f0e0192326136bffed4a1900494b1670640d52bc Mon Sep 17 00:00:00 2001 From: Rory Hunter Date: Thu, 2 Jul 2020 14:56:44 +0100 Subject: [PATCH 06/48] Fix template index pattern --- .../common/logging/logs-deprecation-elasticsearch-template.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/server/src/main/resources/org/elasticsearch/common/logging/logs-deprecation-elasticsearch-template.json b/server/src/main/resources/org/elasticsearch/common/logging/logs-deprecation-elasticsearch-template.json index 73d0c10c43785..3618aaa97f59a 100644 --- a/server/src/main/resources/org/elasticsearch/common/logging/logs-deprecation-elasticsearch-template.json +++ b/server/src/main/resources/org/elasticsearch/common/logging/logs-deprecation-elasticsearch-template.json @@ -1,6 +1,6 @@ { "index_patterns": [ - "deprecation-elasticsearch-default" + "logs-deprecation-elasticsearch" ], "template": { "mappings": { From 14c929a5fa3c3c7c1972b1de181118f97336406f Mon Sep 17 00:00:00 2001 From: Rory Hunter Date: Fri, 3 Jul 2020 13:04:37 +0100 Subject: [PATCH 07/48] Rework deprecation code so that log handlers are more generic --- .../common/logging/DeprecatedLogHandler.java | 24 ++++++++++++++ .../logging/DeprecationIndexingService.java | 16 +++++---- .../common/logging/DeprecationLogger.java | 33 ++++++++++++------- ...ngLogger.java => HeaderWarningLogger.java} | 21 +++--------- .../java/org/elasticsearch/node/Node.java | 4 +-- 5 files changed, 61 insertions(+), 37 deletions(-) create mode 100644 server/src/main/java/org/elasticsearch/common/logging/DeprecatedLogHandler.java rename server/src/main/java/org/elasticsearch/common/logging/{ThrottlingAndHeaderWarningLogger.java => HeaderWarningLogger.java} (77%) diff --git a/server/src/main/java/org/elasticsearch/common/logging/DeprecatedLogHandler.java b/server/src/main/java/org/elasticsearch/common/logging/DeprecatedLogHandler.java new file mode 100644 index 0000000000000..023f8d36d6dcd --- /dev/null +++ b/server/src/main/java/org/elasticsearch/common/logging/DeprecatedLogHandler.java @@ -0,0 +1,24 @@ +/* + * Licensed to Elasticsearch under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch 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. + */ + +package org.elasticsearch.common.logging; + +public interface DeprecatedLogHandler { + void log(String key, ESLogMessage message); +} diff --git a/server/src/main/java/org/elasticsearch/common/logging/DeprecationIndexingService.java b/server/src/main/java/org/elasticsearch/common/logging/DeprecationIndexingService.java index a501b22e4d1d9..a8361266b72b0 100644 --- a/server/src/main/java/org/elasticsearch/common/logging/DeprecationIndexingService.java +++ b/server/src/main/java/org/elasticsearch/common/logging/DeprecationIndexingService.java @@ -33,7 +33,7 @@ * the data stream if necessary. The writing of messages can be toggled using the * {@link #WRITE_DEPRECATION_LOGS_TO_INDEX} setting. */ -public class DeprecationIndexingService implements ClusterStateListener { +public class DeprecationIndexingService implements ClusterStateListener, DeprecatedLogHandler { private static final Logger LOGGER = LogManager.getLogger(DeprecationIndexingService.class); private static final String DATA_STREAM_NAME = "logs-deprecation-elasticsearch"; @@ -61,17 +61,19 @@ public DeprecationIndexingService(ClusterService clusterService, Client client) /** * Indexes a deprecation message. - * @param key the key that was used to determine if this deprecation should have been be logged. - * Useful when aggregating the recorded messages. - * @param message the message to log - * @param xOpaqueId the associated "X-Opaque-ID" header value if any, or null - * @param params parameters to the message, if any + * @param key the key that was used to determine if this deprecation should have been be logged. + * Useful when aggregating the recorded messages. + * @param esLogMessage the message to log */ - public void writeMessage(String key, String message, String xOpaqueId, Object[] params) { + public void log(String key, ESLogMessage esLogMessage) { if (this.isEnabled == false) { return; } + String message = esLogMessage.getMessagePattern(); + String xOpaqueId = HeaderWarning.getXOpaqueId(); + Object[] params = esLogMessage.getArguments(); + Map payload = new HashMap<>(); payload.put("@timestamp", Instant.now().toString()); payload.put("key", key); diff --git a/server/src/main/java/org/elasticsearch/common/logging/DeprecationLogger.java b/server/src/main/java/org/elasticsearch/common/logging/DeprecationLogger.java index dc9f0d7f1e385..6e14a939bcee2 100644 --- a/server/src/main/java/org/elasticsearch/common/logging/DeprecationLogger.java +++ b/server/src/main/java/org/elasticsearch/common/logging/DeprecationLogger.java @@ -23,6 +23,9 @@ import org.apache.logging.log4j.Logger; import org.elasticsearch.common.util.concurrent.ThreadContext; +import java.util.ArrayList; +import java.util.List; + /** * A logger that logs deprecation notices. Logger should be initialized with a parent logger which name will be used * for deprecation logger. For instance new DeprecationLogger("org.elasticsearch.test.SomeClass") will @@ -34,11 +37,13 @@ * X-Opaque-Id. This allows to throttle deprecations per client usage. * deprecationLogger.deprecate("key","message {}", "param"); * - * @see ThrottlingAndHeaderWarningLogger for throttling and header warnings implementation details + * @see HeaderWarningLogger for throttling and header warnings implementation details */ public class DeprecationLogger { - private static DeprecationIndexingService indexingService; - private final ThrottlingAndHeaderWarningLogger deprecationLogger; + private static final List additionalHandlers = new ArrayList<>(); + + private final List handlers; + private final RateLimiter rateLimiter; /** * Creates a new deprecation logger based on the parent logger. Automatically @@ -47,7 +52,10 @@ public class DeprecationLogger { * the "org.elasticsearch" namespace. */ public DeprecationLogger(Logger parentLogger) { - deprecationLogger = new ThrottlingAndHeaderWarningLogger(deprecatedLoggerName(parentLogger.getName()), indexingService); + this.handlers = new ArrayList<>(additionalHandlers); + this.rateLimiter = new RateLimiter(); + + handlers.add(new HeaderWarningLogger(deprecatedLoggerName(parentLogger.getName()))); } public static DeprecationLogger getLogger(Class aClass) { @@ -80,12 +88,12 @@ public static void removeThreadContext(ThreadContext threadContext) { HeaderWarning.removeThreadContext(threadContext); } - public static void setIndexingService(DeprecationIndexingService indexingService) { - DeprecationLogger.indexingService = indexingService; + public static void addHandler(DeprecatedLogHandler handler) { + DeprecationLogger.additionalHandlers.add(handler); } - public static void removeIndexingService() { - DeprecationLogger.indexingService = null; + public static void removeHandler(DeprecatedLogHandler handler) { + DeprecationLogger.additionalHandlers.remove(handler); } /** @@ -102,10 +110,13 @@ public class DeprecationLoggerBuilder { public DeprecationLoggerBuilder withDeprecation(String key, String msg, Object[] params) { String opaqueId = HeaderWarning.getXOpaqueId(); - ESLogMessage deprecationMessage = DeprecatedMessage.of(opaqueId, msg, params); - deprecationLogger.throttleLogAndAddWarning(key, deprecationMessage); + rateLimiter.limit(opaqueId + key, () -> { + ESLogMessage deprecationMessage = DeprecatedMessage.of(opaqueId, msg, params); + for (DeprecatedLogHandler handler : handlers) { + handler.log(key, deprecationMessage); + } + }); return this; } - } } diff --git a/server/src/main/java/org/elasticsearch/common/logging/ThrottlingAndHeaderWarningLogger.java b/server/src/main/java/org/elasticsearch/common/logging/HeaderWarningLogger.java similarity index 77% rename from server/src/main/java/org/elasticsearch/common/logging/ThrottlingAndHeaderWarningLogger.java rename to server/src/main/java/org/elasticsearch/common/logging/HeaderWarningLogger.java index 84bab3ee29d64..2432b7f0603e8 100644 --- a/server/src/main/java/org/elasticsearch/common/logging/ThrottlingAndHeaderWarningLogger.java +++ b/server/src/main/java/org/elasticsearch/common/logging/HeaderWarningLogger.java @@ -37,15 +37,11 @@ *

* TODO wrapping logging this way limits the usage of %location. It will think this is used from that class. */ -class ThrottlingAndHeaderWarningLogger { +class HeaderWarningLogger implements DeprecatedLogHandler { private final Logger logger; - private final RateLimiter rateLimiter; - private final DeprecationIndexingService indexingService; - ThrottlingAndHeaderWarningLogger(Logger logger, DeprecationIndexingService indexingService) { + HeaderWarningLogger(Logger logger) { this.logger = logger; - this.rateLimiter = new RateLimiter(); - this.indexingService = indexingService; } /** @@ -55,21 +51,12 @@ class ThrottlingAndHeaderWarningLogger { * @param key the key used to determine if this message should be logged * @param message the message to log */ - void throttleLogAndAddWarning(final String key, ESLogMessage message) { + @Override + public void log(final String key, ESLogMessage message) { String messagePattern = message.getMessagePattern(); Object[] arguments = message.getArguments(); HeaderWarning.addWarning(messagePattern, arguments); - String xOpaqueId = HeaderWarning.getXOpaqueId(); - this.rateLimiter.limit(xOpaqueId + key, () -> { - log(message); - if (indexingService != null) { - indexingService.writeMessage(key, messagePattern, xOpaqueId, arguments); - } - }); - } - - private void log(Message message) { AccessController.doPrivileged(new PrivilegedAction() { @SuppressLoggerChecks(reason = "safely delegates to logger") @Override diff --git a/server/src/main/java/org/elasticsearch/node/Node.java b/server/src/main/java/org/elasticsearch/node/Node.java index 4661550ac4c9c..fa4925594a24e 100644 --- a/server/src/main/java/org/elasticsearch/node/Node.java +++ b/server/src/main/java/org/elasticsearch/node/Node.java @@ -391,8 +391,8 @@ protected Node(final Environment initialEnvironment, final UsageService usageService = new UsageService(); final DeprecationIndexingService deprecationIndexingService = new DeprecationIndexingService(clusterService, client); - DeprecationLogger.setIndexingService(deprecationIndexingService); - resourcesToClose.add(DeprecationLogger::removeIndexingService); + DeprecationLogger.addHandler(deprecationIndexingService); + resourcesToClose.add(() -> DeprecationLogger.removeHandler(deprecationIndexingService)); ModulesBuilder modules = new ModulesBuilder(); final MonitorService monitorService = new MonitorService(settings, nodeEnvironment, threadPool, clusterInfoService); From e902fb9eeb168291d055bdfbd915f041e3438174 Mon Sep 17 00:00:00 2001 From: Rory Hunter Date: Fri, 3 Jul 2020 16:44:07 +0100 Subject: [PATCH 08/48] Move DeprecationIndexingService to x-pack depreacation plugin --- .../common/logging/DeprecatedLogHandler.java | 2 +- .../common/logging/DeprecationLogger.java | 17 +++- .../common/logging/HeaderWarningLogger.java | 19 +--- .../common/settings/ClusterSettings.java | 3 - .../java/org/elasticsearch/node/Node.java | 5 - .../xpack/deprecation/Deprecation.java | 40 ++++++++ .../DeprecationIndexingService.java | 87 ++++++---------- .../DeprecationTemplateRegistry.java | 4 + .../DeprecationIndexingServiceTests.java | 99 +++++-------------- 9 files changed, 115 insertions(+), 161 deletions(-) rename {server/src/main/java/org/elasticsearch/common/logging => x-pack/plugin/deprecation/src/main/java/org/elasticsearch/xpack/deprecation}/DeprecationIndexingService.java (54%) create mode 100644 x-pack/plugin/deprecation/src/main/java/org/elasticsearch/xpack/deprecation/DeprecationTemplateRegistry.java rename {server/src/test/java/org/elasticsearch/common/logging => x-pack/plugin/deprecation/src/test/java/org/elasticsearch/xpack/deprecation}/DeprecationIndexingServiceTests.java (63%) diff --git a/server/src/main/java/org/elasticsearch/common/logging/DeprecatedLogHandler.java b/server/src/main/java/org/elasticsearch/common/logging/DeprecatedLogHandler.java index 023f8d36d6dcd..e8650f524b053 100644 --- a/server/src/main/java/org/elasticsearch/common/logging/DeprecatedLogHandler.java +++ b/server/src/main/java/org/elasticsearch/common/logging/DeprecatedLogHandler.java @@ -20,5 +20,5 @@ package org.elasticsearch.common.logging; public interface DeprecatedLogHandler { - void log(String key, ESLogMessage message); + void log(String key, String xOpaqueId, ESLogMessage message); } diff --git a/server/src/main/java/org/elasticsearch/common/logging/DeprecationLogger.java b/server/src/main/java/org/elasticsearch/common/logging/DeprecationLogger.java index 6e14a939bcee2..9b6e6987bd443 100644 --- a/server/src/main/java/org/elasticsearch/common/logging/DeprecationLogger.java +++ b/server/src/main/java/org/elasticsearch/common/logging/DeprecationLogger.java @@ -31,18 +31,22 @@ * for deprecation logger. For instance new DeprecationLogger("org.elasticsearch.test.SomeClass") will * result in a deprecation logger with name org.elasticsearch.deprecation.test.SomeClass. This allows to use * deprecation logger defined in log4j2.properties. - * + *

* Deprecation logs are written to deprecation log file - defined in log4j2.properties, as well as warnings added to a response header. * All deprecation usages are throttled basing on a key. Key is a string provided in an argument and can be prefixed with * X-Opaque-Id. This allows to throttle deprecations per client usage. * deprecationLogger.deprecate("key","message {}", "param"); - * - * @see HeaderWarningLogger for throttling and header warnings implementation details + *

+ * Additional log handlers can be registered via {@link #addHandler(DeprecatedLogHandler)}. Subject to rate limiting, + * each additional handler will also be called with a deprecated message and its key. */ public class DeprecationLogger { + /** Log handlers are are not tied to this specific DeprecationLogger instance */ private static final List additionalHandlers = new ArrayList<>(); + /** Log handlers are are tied to this specific DeprecationLogger instance */ private final List handlers; + private final RateLimiter rateLimiter; /** @@ -52,7 +56,7 @@ public class DeprecationLogger { * the "org.elasticsearch" namespace. */ public DeprecationLogger(Logger parentLogger) { - this.handlers = new ArrayList<>(additionalHandlers); + this.handlers = new ArrayList<>(); this.rateLimiter = new RateLimiter(); handlers.add(new HeaderWarningLogger(deprecatedLoggerName(parentLogger.getName()))); @@ -113,7 +117,10 @@ public DeprecationLoggerBuilder withDeprecation(String key, String msg, Object[] rateLimiter.limit(opaqueId + key, () -> { ESLogMessage deprecationMessage = DeprecatedMessage.of(opaqueId, msg, params); for (DeprecatedLogHandler handler : handlers) { - handler.log(key, deprecationMessage); + handler.log(key, opaqueId, deprecationMessage); + } + for (DeprecatedLogHandler handler : additionalHandlers) { + handler.log(key, opaqueId, deprecationMessage); } }); return this; diff --git a/server/src/main/java/org/elasticsearch/common/logging/HeaderWarningLogger.java b/server/src/main/java/org/elasticsearch/common/logging/HeaderWarningLogger.java index 2432b7f0603e8..5fe18596448a3 100644 --- a/server/src/main/java/org/elasticsearch/common/logging/HeaderWarningLogger.java +++ b/server/src/main/java/org/elasticsearch/common/logging/HeaderWarningLogger.java @@ -20,20 +20,14 @@ package org.elasticsearch.common.logging; import org.apache.logging.log4j.Logger; -import org.apache.logging.log4j.message.Message; import org.elasticsearch.common.SuppressLoggerChecks; -import org.elasticsearch.common.util.concurrent.ThreadContext; import java.security.AccessController; import java.security.PrivilegedAction; /** - * This class composes {@link HeaderWarning}, {@link DeprecationIndexingService} and {@link Logger}, - * in order to apply a single message to multiple destination. - *

- * Logging and indexing are throttled in order to avoid filling the destination with duplicates. - * Throttling is implemented using a mandatory per-message key combined with any X-Opaque-Id - * HTTP header value. This header allows throttling per user. This value is set in {@link ThreadContext}. + * This class composes {@link HeaderWarning} and {@link Logger}, in order to both log a message + * and add a header to an API response. *

* TODO wrapping logging this way limits the usage of %location. It will think this is used from that class. */ @@ -45,14 +39,11 @@ class HeaderWarningLogger implements DeprecatedLogHandler { } /** - * Adds a formatted warning message as a response header on the thread context, and logs a message if the associated key has - * not recently been seen. - * - * @param key the key used to determine if this message should be logged - * @param message the message to log + * Logs a deprecation message and adds a formatted warning message as a response + * header on the thread context. */ @Override - public void log(final String key, ESLogMessage message) { + public void log(final String key, String xOpaqueId, ESLogMessage message) { String messagePattern = message.getMessagePattern(); Object[] arguments = message.getArguments(); HeaderWarning.addWarning(messagePattern, arguments); diff --git a/server/src/main/java/org/elasticsearch/common/settings/ClusterSettings.java b/server/src/main/java/org/elasticsearch/common/settings/ClusterSettings.java index 5bcb40d73470f..74a80076c98c8 100644 --- a/server/src/main/java/org/elasticsearch/common/settings/ClusterSettings.java +++ b/server/src/main/java/org/elasticsearch/common/settings/ClusterSettings.java @@ -59,8 +59,6 @@ import org.elasticsearch.cluster.service.ClusterApplierService; import org.elasticsearch.cluster.service.ClusterService; import org.elasticsearch.cluster.service.MasterService; -import org.elasticsearch.common.logging.DeprecationIndexingService; -import org.elasticsearch.common.logging.DeprecationLogger; import org.elasticsearch.common.logging.Loggers; import org.elasticsearch.common.network.NetworkModule; import org.elasticsearch.common.network.NetworkService; @@ -199,7 +197,6 @@ public void apply(Settings value, Settings current, Settings previous) { ClusterRebalanceAllocationDecider.CLUSTER_ROUTING_ALLOCATION_ALLOW_REBALANCE_SETTING, ConcurrentRebalanceAllocationDecider.CLUSTER_ROUTING_ALLOCATION_CLUSTER_CONCURRENT_REBALANCE_SETTING, DanglingIndicesState.AUTO_IMPORT_DANGLING_INDICES_SETTING, - DeprecationIndexingService.WRITE_DEPRECATION_LOGS_TO_INDEX, EnableAllocationDecider.CLUSTER_ROUTING_ALLOCATION_ENABLE_SETTING, EnableAllocationDecider.CLUSTER_ROUTING_REBALANCE_ENABLE_SETTING, FilterAllocationDecider.CLUSTER_ROUTING_INCLUDE_GROUP_SETTING, diff --git a/server/src/main/java/org/elasticsearch/node/Node.java b/server/src/main/java/org/elasticsearch/node/Node.java index fa4925594a24e..f1ea251612ed3 100644 --- a/server/src/main/java/org/elasticsearch/node/Node.java +++ b/server/src/main/java/org/elasticsearch/node/Node.java @@ -70,7 +70,6 @@ import org.elasticsearch.common.inject.ModulesBuilder; import org.elasticsearch.common.io.stream.NamedWriteableRegistry; import org.elasticsearch.common.lease.Releasables; -import org.elasticsearch.common.logging.DeprecationIndexingService; import org.elasticsearch.common.logging.DeprecationLogger; import org.elasticsearch.common.logging.HeaderWarning; import org.elasticsearch.common.logging.NodeAndClusterIdStateListener; @@ -390,10 +389,6 @@ protected Node(final Environment initialEnvironment, final ClusterInfoService clusterInfoService = newClusterInfoService(settings, clusterService, threadPool, client); final UsageService usageService = new UsageService(); - final DeprecationIndexingService deprecationIndexingService = new DeprecationIndexingService(clusterService, client); - DeprecationLogger.addHandler(deprecationIndexingService); - resourcesToClose.add(() -> DeprecationLogger.removeHandler(deprecationIndexingService)); - ModulesBuilder modules = new ModulesBuilder(); final MonitorService monitorService = new MonitorService(settings, nodeEnvironment, threadPool, clusterInfoService); ClusterModule clusterModule = new ClusterModule(settings, clusterService, clusterPlugins, clusterInfoService); diff --git a/x-pack/plugin/deprecation/src/main/java/org/elasticsearch/xpack/deprecation/Deprecation.java b/x-pack/plugin/deprecation/src/main/java/org/elasticsearch/xpack/deprecation/Deprecation.java index 6d17f634c9a78..6d019334eb8aa 100644 --- a/x-pack/plugin/deprecation/src/main/java/org/elasticsearch/xpack/deprecation/Deprecation.java +++ b/x-pack/plugin/deprecation/src/main/java/org/elasticsearch/xpack/deprecation/Deprecation.java @@ -8,23 +8,39 @@ import org.elasticsearch.action.ActionRequest; import org.elasticsearch.action.ActionResponse; +import org.elasticsearch.client.Client; import org.elasticsearch.cluster.metadata.IndexNameExpressionResolver; import org.elasticsearch.cluster.node.DiscoveryNodes; +import org.elasticsearch.cluster.service.ClusterService; +import org.elasticsearch.common.io.stream.NamedWriteableRegistry; +import org.elasticsearch.common.logging.DeprecationLogger; import org.elasticsearch.common.settings.ClusterSettings; import org.elasticsearch.common.settings.IndexScopedSettings; +import org.elasticsearch.common.settings.Setting; import org.elasticsearch.common.settings.Settings; import org.elasticsearch.common.settings.SettingsFilter; +import org.elasticsearch.common.xcontent.NamedXContentRegistry; +import org.elasticsearch.env.Environment; +import org.elasticsearch.env.NodeEnvironment; +import org.elasticsearch.index.IndexModule; import org.elasticsearch.plugins.ActionPlugin; import org.elasticsearch.plugins.Plugin; +import org.elasticsearch.repositories.RepositoriesService; import org.elasticsearch.rest.RestController; import org.elasticsearch.rest.RestHandler; +import org.elasticsearch.script.ScriptService; +import org.elasticsearch.threadpool.ThreadPool; +import org.elasticsearch.watcher.ResourceWatcherService; import org.elasticsearch.xpack.core.deprecation.DeprecationInfoAction; import org.elasticsearch.xpack.core.deprecation.NodesDeprecationCheckAction; +import java.util.Collection; import java.util.Collections; import java.util.List; import java.util.function.Supplier; +import static org.elasticsearch.xpack.deprecation.DeprecationIndexingService.WRITE_DEPRECATION_LOGS_TO_INDEX; + /** * The plugin class for the Deprecation API */ @@ -46,4 +62,28 @@ public List getRestHandlers(Settings settings, RestController restC return Collections.singletonList(new RestDeprecationInfoAction()); } + + @Override + public Collection createComponents( + Client client, + ClusterService clusterService, + ThreadPool threadPool, + ResourceWatcherService resourceWatcherService, + ScriptService scriptService, + NamedXContentRegistry xContentRegistry, + Environment environment, + NodeEnvironment nodeEnvironment, + NamedWriteableRegistry namedWriteableRegistry, + IndexNameExpressionResolver indexNameExpressionResolver, + Supplier repositoriesServiceSupplier + ) { + DeprecationIndexingService service = new DeprecationIndexingService(clusterService, client); + + return List.of(service); + } + + @Override + public List> getSettings() { + return List.of(WRITE_DEPRECATION_LOGS_TO_INDEX); + } } diff --git a/server/src/main/java/org/elasticsearch/common/logging/DeprecationIndexingService.java b/x-pack/plugin/deprecation/src/main/java/org/elasticsearch/xpack/deprecation/DeprecationIndexingService.java similarity index 54% rename from server/src/main/java/org/elasticsearch/common/logging/DeprecationIndexingService.java rename to x-pack/plugin/deprecation/src/main/java/org/elasticsearch/xpack/deprecation/DeprecationIndexingService.java index a8361266b72b0..1a053adee1992 100644 --- a/server/src/main/java/org/elasticsearch/common/logging/DeprecationIndexingService.java +++ b/x-pack/plugin/deprecation/src/main/java/org/elasticsearch/xpack/deprecation/DeprecationIndexingService.java @@ -1,27 +1,30 @@ -package org.elasticsearch.common.logging; +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +package org.elasticsearch.xpack.deprecation; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; import org.elasticsearch.action.ActionListener; import org.elasticsearch.action.DocWriteRequest; -import org.elasticsearch.action.admin.indices.template.put.PutComposableIndexTemplateAction; import org.elasticsearch.action.index.IndexAction; import org.elasticsearch.action.index.IndexRequestBuilder; import org.elasticsearch.action.index.IndexResponse; -import org.elasticsearch.action.support.master.AcknowledgedResponse; import org.elasticsearch.client.Client; import org.elasticsearch.client.OriginSettingClient; import org.elasticsearch.cluster.ClusterChangedEvent; import org.elasticsearch.cluster.ClusterStateListener; -import org.elasticsearch.cluster.metadata.ComposableIndexTemplate; import org.elasticsearch.cluster.service.ClusterService; +import org.elasticsearch.common.component.AbstractLifecycleComponent; +import org.elasticsearch.common.logging.DeprecatedLogHandler; +import org.elasticsearch.common.logging.DeprecationLogger; +import org.elasticsearch.common.logging.ESLogMessage; +import org.elasticsearch.common.logging.HeaderWarning; import org.elasticsearch.common.settings.Setting; -import org.elasticsearch.common.xcontent.NamedXContentRegistry; -import org.elasticsearch.common.xcontent.XContentParser; -import org.elasticsearch.common.xcontent.json.JsonXContent; -import java.io.IOException; -import java.io.InputStream; import java.time.Instant; import java.util.HashMap; import java.util.Map; @@ -29,16 +32,14 @@ import static org.elasticsearch.common.Strings.isNullOrEmpty; /** - * This service is responsible for writing deprecation messages to a data stream. It also creates - * the data stream if necessary. The writing of messages can be toggled using the + * This service is responsible for writing deprecation messages to a data stream. + * The writing of messages can be toggled using the * {@link #WRITE_DEPRECATION_LOGS_TO_INDEX} setting. */ -public class DeprecationIndexingService implements ClusterStateListener, DeprecatedLogHandler { +public class DeprecationIndexingService extends AbstractLifecycleComponent implements ClusterStateListener, DeprecatedLogHandler { private static final Logger LOGGER = LogManager.getLogger(DeprecationIndexingService.class); private static final String DATA_STREAM_NAME = "logs-deprecation-elasticsearch"; - private static final String TEMPLATE_NAME = DATA_STREAM_NAME + "-template"; - private static final String TEMPLATE_MAPPING = TEMPLATE_NAME + ".json"; private static final String DEPRECATION_ORIGIN = "deprecation"; @@ -50,7 +51,6 @@ public class DeprecationIndexingService implements ClusterStateListener, Depreca ); private final Client client; - private boolean hasTriedToLoadTemplate = false; private volatile boolean isEnabled = false; public DeprecationIndexingService(ClusterService clusterService, Client client) { @@ -65,13 +65,16 @@ public DeprecationIndexingService(ClusterService clusterService, Client client) * Useful when aggregating the recorded messages. * @param esLogMessage the message to log */ - public void log(String key, ESLogMessage esLogMessage) { + public void log(String key, String xOpaqueId, ESLogMessage esLogMessage) { + if (this.lifecycle.started() == false) { + return; + } + if (this.isEnabled == false) { return; } String message = esLogMessage.getMessagePattern(); - String xOpaqueId = HeaderWarning.getXOpaqueId(); Object[] params = esLogMessage.getArguments(); Map payload = new HashMap<>(); @@ -109,48 +112,20 @@ public void onFailure(Exception e) { @Override public void clusterChanged(ClusterChangedEvent event) { this.isEnabled = WRITE_DEPRECATION_LOGS_TO_INDEX.get(event.state().getMetadata().settings()); - - if (this.isEnabled == false || this.hasTriedToLoadTemplate == true) { - return; - } - - // We only ever try to load the template once, because if there's a problem, we'll spam - // the log with the failure on every cluster state update - this.hasTriedToLoadTemplate = true; - - if (event.state().getMetadata().templatesV2().containsKey(TEMPLATE_NAME)) { - return; - } - - loadTemplate(); } - /* - * Attempts to load a template for the deprecation logs data stream - */ - private void loadTemplate() { - try (InputStream is = getClass().getResourceAsStream(TEMPLATE_MAPPING)) { - final XContentParser parser = JsonXContent.jsonXContent.createParser(NamedXContentRegistry.EMPTY, null, is); - - PutComposableIndexTemplateAction.Request request = new PutComposableIndexTemplateAction.Request(TEMPLATE_NAME); - request.cause("auto (deprecation indexing service)"); - request.indexTemplate(ComposableIndexTemplate.parse(parser)); + @Override + protected void doStart() { + DeprecationLogger.addHandler(this); + } - this.client.execute(PutComposableIndexTemplateAction.INSTANCE, request, new ActionListener<>() { - @Override - public void onResponse(AcknowledgedResponse acknowledgedResponse) { - if (acknowledgedResponse.isAcknowledged() == false) { - LOGGER.error("The attempt to create a deprecations index template was not acknowledged."); - } - } + @Override + protected void doStop() { + DeprecationLogger.removeHandler(this); + } - @Override - public void onFailure(Exception e) { - LOGGER.error("Failed to create the deprecations index template: " + e.getMessage(), e); - } - }); - } catch (IOException e) { - LOGGER.error("Failed to load " + TEMPLATE_MAPPING + ": " + e.getMessage(), e); - } + @Override + protected void doClose() { + DeprecationLogger.removeHandler(this); } } diff --git a/x-pack/plugin/deprecation/src/main/java/org/elasticsearch/xpack/deprecation/DeprecationTemplateRegistry.java b/x-pack/plugin/deprecation/src/main/java/org/elasticsearch/xpack/deprecation/DeprecationTemplateRegistry.java new file mode 100644 index 0000000000000..14870add8bc87 --- /dev/null +++ b/x-pack/plugin/deprecation/src/main/java/org/elasticsearch/xpack/deprecation/DeprecationTemplateRegistry.java @@ -0,0 +1,4 @@ +package org.elasticsearch.xpack.deprecation; + +public class DeprecationTemplateRegistry { +} diff --git a/server/src/test/java/org/elasticsearch/common/logging/DeprecationIndexingServiceTests.java b/x-pack/plugin/deprecation/src/test/java/org/elasticsearch/xpack/deprecation/DeprecationIndexingServiceTests.java similarity index 63% rename from server/src/test/java/org/elasticsearch/common/logging/DeprecationIndexingServiceTests.java rename to x-pack/plugin/deprecation/src/test/java/org/elasticsearch/xpack/deprecation/DeprecationIndexingServiceTests.java index 1d99430536e36..50863e0b767fe 100644 --- a/server/src/test/java/org/elasticsearch/common/logging/DeprecationIndexingServiceTests.java +++ b/x-pack/plugin/deprecation/src/test/java/org/elasticsearch/xpack/deprecation/DeprecationIndexingServiceTests.java @@ -17,22 +17,18 @@ * under the License. */ -package org.elasticsearch.common.logging; +package org.elasticsearch.xpack.deprecation; import org.elasticsearch.action.ActionListener; -import org.elasticsearch.action.ActionRequest; -import org.elasticsearch.action.ActionResponse; -import org.elasticsearch.action.ActionType; -import org.elasticsearch.action.admin.indices.template.put.PutComposableIndexTemplateAction; import org.elasticsearch.action.index.IndexAction; import org.elasticsearch.action.index.IndexRequest; -import org.elasticsearch.action.support.master.AcknowledgedResponse; import org.elasticsearch.client.Client; import org.elasticsearch.cluster.ClusterChangedEvent; import org.elasticsearch.cluster.ClusterName; import org.elasticsearch.cluster.ClusterState; import org.elasticsearch.cluster.metadata.Metadata; import org.elasticsearch.cluster.service.ClusterService; +import org.elasticsearch.common.logging.ESLogMessage; import org.elasticsearch.common.settings.Settings; import org.elasticsearch.test.ESTestCase; import org.elasticsearch.test.client.NoOpClient; @@ -43,7 +39,7 @@ import java.util.List; import java.util.Map; -import static org.elasticsearch.common.logging.DeprecationIndexingService.WRITE_DEPRECATION_LOGS_TO_INDEX; +import static org.elasticsearch.xpack.deprecation.DeprecationIndexingService.WRITE_DEPRECATION_LOGS_TO_INDEX; import static org.hamcrest.Matchers.equalTo; import static org.hamcrest.Matchers.hasEntry; import static org.hamcrest.Matchers.hasKey; @@ -53,7 +49,6 @@ import static org.mockito.Mockito.mock; import static org.mockito.Mockito.never; import static org.mockito.Mockito.spy; -import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; public class DeprecationIndexingServiceTests extends ESTestCase { @@ -65,7 +60,7 @@ public class DeprecationIndexingServiceTests extends ESTestCase { @Before public void initialize() { clusterService = mock(ClusterService.class); - client = spy(new MyNoOpClient()); + client = spy(new NoOpClient(this.getTestName())); service = new DeprecationIndexingService(clusterService, client); } @@ -83,37 +78,13 @@ public void testClusterStateListenerRegistered() { } /** - * Checks that the service ensures that the data stream template is created when - * the service is enabled. + * Checks that the service does not attempt to index messages when it had not reach the + * "started" lifecycle state. */ - public void testCreatesTemplateOnDemand() { - service.clusterChanged(getEvent(true)); - - verify(client).execute(eq(PutComposableIndexTemplateAction.INSTANCE), any(), anyListener()); - } - - /** - * Checks that the service only creates the data stream template once, - * and not every time it processes the cluster state. - */ - public void testOnlyCreatesTemplateOnce() { - service.clusterChanged(getEvent(true)); - service.clusterChanged(getEvent(true)); - - verify(client, times(1)).execute(eq(PutComposableIndexTemplateAction.INSTANCE), any(), anyListener()); - } - - /** - * Checks that the service only ensures that the data stream template is created once, - * even if it fails the first time, in order to prevent a flood of failure messages - * to the log. - */ - public void testOnlyCreatesTemplateOnceEvenOnFailure() { - ((MyNoOpClient) client).setAcknowledged(false); - service.clusterChanged(getEvent(true)); - service.clusterChanged(getEvent(true)); + public void testDoesNotWriteMessageWhenServiceNotStarted() { + service.log("a key", "xOpaqueId", new ESLogMessage("a message")); - verify(client, times(1)).execute(eq(PutComposableIndexTemplateAction.INSTANCE), any(), anyListener()); + verify(client, never()).execute(any(), any(), anyListener()); } /** @@ -121,7 +92,9 @@ public void testOnlyCreatesTemplateOnceEvenOnFailure() { * is disabled. */ public void testDoesNotWriteMessageWhenServiceDisabled() { - service.writeMessage("key", "message", "xOpaqueId", null); + service.start(); + + service.log("a key", "xOpaqueId", new ESLogMessage("a message")); verify(client, never()).execute(any(), any(), anyListener()); } @@ -130,10 +103,11 @@ public void testDoesNotWriteMessageWhenServiceDisabled() { * Checks that the service can be disabled after being enabled. */ public void testDoesNotWriteMessageWhenServiceEnabledAndDisabled() { + service.start(); service.clusterChanged(getEvent(true)); service.clusterChanged(getEvent(false)); - service.writeMessage("key", "message", "xOpaqueId", null); + service.log("a key", "xOpaqueId", new ESLogMessage("a message")); verify(client, never()).execute(eq(IndexAction.INSTANCE), any(), anyListener()); } @@ -142,9 +116,10 @@ public void testDoesNotWriteMessageWhenServiceEnabledAndDisabled() { * Checks that messages are indexed in the correct shape when the service is enabled. */ public void testWritesMessageWhenServiceEnabled() { + service.start(); service.clusterChanged(getEvent(true)); - final Map payloadMap = getWriteRequest("a key", "a message", null, null); + final Map payloadMap = getWriteRequest("a key", null, new ESLogMessage("a message")); assertThat(payloadMap, hasKey("@timestamp")); assertThat(payloadMap, hasEntry("key", "a key")); @@ -158,9 +133,10 @@ public void testWritesMessageWhenServiceEnabled() { * Check that if an xOpaqueId is set, then it is added to the index request payload. */ public void testMessageIncludesOpaqueIdWhenSupplied() { + service.start(); service.clusterChanged(getEvent(true)); - final Map payloadMap = getWriteRequest("a key", "a message", "an ID", null); + final Map payloadMap = getWriteRequest("a key", "an ID", new ESLogMessage("a message")); assertThat(payloadMap, hasEntry("x-opaque-id", "an ID")); } @@ -169,9 +145,10 @@ public void testMessageIncludesOpaqueIdWhenSupplied() { * Check that if any params are set, then they are added to the index request payload. */ public void testMessageIncludesParamsWhenSupplied() { + service.start(); service.clusterChanged(getEvent(true)); - final Map payloadMap = getWriteRequest("a key", "a message", null, new Object[] { "first", "second" }); + final Map payloadMap = getWriteRequest("a key", null, new ESLogMessage("a message", "first", "second")); // I can't get this to work as a one-liner. Curse you, Hamcrest. assertThat(payloadMap, hasKey("params")); @@ -191,43 +168,11 @@ private ActionListener anyListener() { return any(); } - /* - * A client that does nothing, except for requests of type - * PutComposableIndexTemplateAction.Request, in which case return an AcknowledgedResponse - */ - private class MyNoOpClient extends NoOpClient { - private boolean isAcknowledged = true; - - public MyNoOpClient() { - super(DeprecationIndexingServiceTests.this.getTestName()); - } - - public void setAcknowledged(boolean acknowledged) { - isAcknowledged = acknowledged; - } - - @SuppressWarnings("unchecked") - @Override - protected void doExecute( - ActionType action, - Request request, - ActionListener listener - ) { - if (request instanceof PutComposableIndexTemplateAction.Request) { - listener.onResponse(((Response) new AcknowledgedResponse(isAcknowledged))); - return; - } - - super.doExecute(action, request, listener); - } - - } - /* * Wraps up the steps for extracting an index request payload from the mocks. */ - private Map getWriteRequest(String key, String message, String xOpaqueId, Object[] params) { - service.writeMessage(key, message, xOpaqueId, params); + private Map getWriteRequest(String key, String xOpaqueId, ESLogMessage message) { + service.log(key, xOpaqueId, message); ArgumentCaptor argument = ArgumentCaptor.forClass(IndexRequest.class); From 40bea2c3a47cc488474d9b4af3b3af2287291ce2 Mon Sep 17 00:00:00 2001 From: Rory Hunter Date: Tue, 7 Jul 2020 11:06:06 +0100 Subject: [PATCH 09/48] Don't need a custom template any more :tada: --- ...gs-deprecation-elasticsearch-template.json | 33 ------------------- .../DeprecationTemplateRegistry.java | 4 --- 2 files changed, 37 deletions(-) delete mode 100644 server/src/main/resources/org/elasticsearch/common/logging/logs-deprecation-elasticsearch-template.json delete mode 100644 x-pack/plugin/deprecation/src/main/java/org/elasticsearch/xpack/deprecation/DeprecationTemplateRegistry.java diff --git a/server/src/main/resources/org/elasticsearch/common/logging/logs-deprecation-elasticsearch-template.json b/server/src/main/resources/org/elasticsearch/common/logging/logs-deprecation-elasticsearch-template.json deleted file mode 100644 index 3618aaa97f59a..0000000000000 --- a/server/src/main/resources/org/elasticsearch/common/logging/logs-deprecation-elasticsearch-template.json +++ /dev/null @@ -1,33 +0,0 @@ -{ - "index_patterns": [ - "logs-deprecation-elasticsearch" - ], - "template": { - "mappings": { - "properties": { - "@timestamp": { - "type": "date" - }, - "key": { - "ignore_above": 1024, - "type": "keyword" - }, - "message": { - "norms": false, - "type": "text" - }, - "x-opaque-id": { - "ignore_above": 1024, - "type": "keyword" - }, - "params": { - "ignore_above": 1024, - "type": "keyword" - } - } - } - }, - "data_stream": { - "timestamp_field": "@timestamp" - } -} diff --git a/x-pack/plugin/deprecation/src/main/java/org/elasticsearch/xpack/deprecation/DeprecationTemplateRegistry.java b/x-pack/plugin/deprecation/src/main/java/org/elasticsearch/xpack/deprecation/DeprecationTemplateRegistry.java deleted file mode 100644 index 14870add8bc87..0000000000000 --- a/x-pack/plugin/deprecation/src/main/java/org/elasticsearch/xpack/deprecation/DeprecationTemplateRegistry.java +++ /dev/null @@ -1,4 +0,0 @@ -package org.elasticsearch.xpack.deprecation; - -public class DeprecationTemplateRegistry { -} From 013bf55adccc54eb8905f98af5cc59252d5765c3 Mon Sep 17 00:00:00 2001 From: Rory Hunter Date: Tue, 7 Jul 2020 15:19:48 +0100 Subject: [PATCH 10/48] Remove unused import --- server/src/main/java/org/elasticsearch/node/Node.java | 1 - 1 file changed, 1 deletion(-) diff --git a/server/src/main/java/org/elasticsearch/node/Node.java b/server/src/main/java/org/elasticsearch/node/Node.java index f1ea251612ed3..68d1960ac41c6 100644 --- a/server/src/main/java/org/elasticsearch/node/Node.java +++ b/server/src/main/java/org/elasticsearch/node/Node.java @@ -70,7 +70,6 @@ import org.elasticsearch.common.inject.ModulesBuilder; import org.elasticsearch.common.io.stream.NamedWriteableRegistry; import org.elasticsearch.common.lease.Releasables; -import org.elasticsearch.common.logging.DeprecationLogger; import org.elasticsearch.common.logging.HeaderWarning; import org.elasticsearch.common.logging.NodeAndClusterIdStateListener; import org.elasticsearch.common.network.NetworkAddress; From d61b14506b9d68535cfca5b547565eda6f4981cc Mon Sep 17 00:00:00 2001 From: Rory Hunter Date: Tue, 7 Jul 2020 15:37:17 +0100 Subject: [PATCH 11/48] License header fix --- .../DeprecationIndexingServiceTests.java | 19 +++---------------- 1 file changed, 3 insertions(+), 16 deletions(-) diff --git a/x-pack/plugin/deprecation/src/test/java/org/elasticsearch/xpack/deprecation/DeprecationIndexingServiceTests.java b/x-pack/plugin/deprecation/src/test/java/org/elasticsearch/xpack/deprecation/DeprecationIndexingServiceTests.java index 50863e0b767fe..44a0b2b766972 100644 --- a/x-pack/plugin/deprecation/src/test/java/org/elasticsearch/xpack/deprecation/DeprecationIndexingServiceTests.java +++ b/x-pack/plugin/deprecation/src/test/java/org/elasticsearch/xpack/deprecation/DeprecationIndexingServiceTests.java @@ -1,20 +1,7 @@ /* - * Licensed to Elasticsearch under one or more contributor - * license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright - * ownership. Elasticsearch 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. + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. */ package org.elasticsearch.xpack.deprecation; From ae62d06ff8461bb10f5f8448dd745f3523806d75 Mon Sep 17 00:00:00 2001 From: Rory Hunter Date: Tue, 7 Jul 2020 15:46:51 +0100 Subject: [PATCH 12/48] Remove unused imports --- .../java/org/elasticsearch/xpack/deprecation/Deprecation.java | 2 -- .../xpack/deprecation/DeprecationIndexingService.java | 1 - 2 files changed, 3 deletions(-) diff --git a/x-pack/plugin/deprecation/src/main/java/org/elasticsearch/xpack/deprecation/Deprecation.java b/x-pack/plugin/deprecation/src/main/java/org/elasticsearch/xpack/deprecation/Deprecation.java index 6d019334eb8aa..94439d801f561 100644 --- a/x-pack/plugin/deprecation/src/main/java/org/elasticsearch/xpack/deprecation/Deprecation.java +++ b/x-pack/plugin/deprecation/src/main/java/org/elasticsearch/xpack/deprecation/Deprecation.java @@ -13,7 +13,6 @@ import org.elasticsearch.cluster.node.DiscoveryNodes; import org.elasticsearch.cluster.service.ClusterService; import org.elasticsearch.common.io.stream.NamedWriteableRegistry; -import org.elasticsearch.common.logging.DeprecationLogger; import org.elasticsearch.common.settings.ClusterSettings; import org.elasticsearch.common.settings.IndexScopedSettings; import org.elasticsearch.common.settings.Setting; @@ -22,7 +21,6 @@ import org.elasticsearch.common.xcontent.NamedXContentRegistry; import org.elasticsearch.env.Environment; import org.elasticsearch.env.NodeEnvironment; -import org.elasticsearch.index.IndexModule; import org.elasticsearch.plugins.ActionPlugin; import org.elasticsearch.plugins.Plugin; import org.elasticsearch.repositories.RepositoriesService; diff --git a/x-pack/plugin/deprecation/src/main/java/org/elasticsearch/xpack/deprecation/DeprecationIndexingService.java b/x-pack/plugin/deprecation/src/main/java/org/elasticsearch/xpack/deprecation/DeprecationIndexingService.java index 1a053adee1992..ed1507481a396 100644 --- a/x-pack/plugin/deprecation/src/main/java/org/elasticsearch/xpack/deprecation/DeprecationIndexingService.java +++ b/x-pack/plugin/deprecation/src/main/java/org/elasticsearch/xpack/deprecation/DeprecationIndexingService.java @@ -22,7 +22,6 @@ import org.elasticsearch.common.logging.DeprecatedLogHandler; import org.elasticsearch.common.logging.DeprecationLogger; import org.elasticsearch.common.logging.ESLogMessage; -import org.elasticsearch.common.logging.HeaderWarning; import org.elasticsearch.common.settings.Setting; import java.time.Instant; From 32d8316caf342eccb10c0131841a11bf742fd722 Mon Sep 17 00:00:00 2001 From: Rory Hunter Date: Tue, 7 Jul 2020 15:52:23 +0100 Subject: [PATCH 13/48] Add Javadoc --- .../common/logging/DeprecatedLogHandler.java | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/server/src/main/java/org/elasticsearch/common/logging/DeprecatedLogHandler.java b/server/src/main/java/org/elasticsearch/common/logging/DeprecatedLogHandler.java index e8650f524b053..37314f534f7da 100644 --- a/server/src/main/java/org/elasticsearch/common/logging/DeprecatedLogHandler.java +++ b/server/src/main/java/org/elasticsearch/common/logging/DeprecatedLogHandler.java @@ -19,6 +19,18 @@ package org.elasticsearch.common.logging; +/** + * Classes that want to receive deprecation logs must implement this interface. + */ public interface DeprecatedLogHandler { + + /** + * Handle a single deprecation log message. + * @param key a value that should uniquely identify the deprecation + * @param xOpaqueId the value of any X-Opaque-Id header that was submitted + * with a REST request. This allows deprecation messages to be linked to + * whatever triggered the deprecation behaviour in the first place. + * @param message the deprecation message itself + */ void log(String key, String xOpaqueId, ESLogMessage message); } From 25ea88ffe8c26ea087b8247ee8130295dc173fcb Mon Sep 17 00:00:00 2001 From: Rory Hunter Date: Tue, 7 Jul 2020 16:02:50 +0100 Subject: [PATCH 14/48] Silence log checker error --- .../xpack/deprecation/DeprecationIndexingServiceTests.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/x-pack/plugin/deprecation/src/test/java/org/elasticsearch/xpack/deprecation/DeprecationIndexingServiceTests.java b/x-pack/plugin/deprecation/src/test/java/org/elasticsearch/xpack/deprecation/DeprecationIndexingServiceTests.java index 44a0b2b766972..3bb7311d0e79b 100644 --- a/x-pack/plugin/deprecation/src/test/java/org/elasticsearch/xpack/deprecation/DeprecationIndexingServiceTests.java +++ b/x-pack/plugin/deprecation/src/test/java/org/elasticsearch/xpack/deprecation/DeprecationIndexingServiceTests.java @@ -135,7 +135,7 @@ public void testMessageIncludesParamsWhenSupplied() { service.start(); service.clusterChanged(getEvent(true)); - final Map payloadMap = getWriteRequest("a key", null, new ESLogMessage("a message", "first", "second")); + final Map payloadMap = getWriteRequest("a key", null, new ESLogMessage("a {} and {} message", "first", "second")); // I can't get this to work as a one-liner. Curse you, Hamcrest. assertThat(payloadMap, hasKey("params")); From 3482d3169ef5726bfc1d4a6bfc54431e1a99a1b2 Mon Sep 17 00:00:00 2001 From: Rory Hunter Date: Wed, 8 Jul 2020 15:15:44 +0100 Subject: [PATCH 15/48] Subtitute log message params, simplify things --- .../common/logging/DeprecationLogger.java | 44 +++++++++++-------- ...rWarningLogger.java => WarningLogger.java} | 18 ++------ .../DeprecationIndexingService.java | 12 +++-- .../DeprecationIndexingServiceTests.java | 9 ++-- 4 files changed, 38 insertions(+), 45 deletions(-) rename server/src/main/java/org/elasticsearch/common/logging/{HeaderWarningLogger.java => WarningLogger.java} (69%) diff --git a/server/src/main/java/org/elasticsearch/common/logging/DeprecationLogger.java b/server/src/main/java/org/elasticsearch/common/logging/DeprecationLogger.java index 9b6e6987bd443..89063f2d928c6 100644 --- a/server/src/main/java/org/elasticsearch/common/logging/DeprecationLogger.java +++ b/server/src/main/java/org/elasticsearch/common/logging/DeprecationLogger.java @@ -24,6 +24,7 @@ import org.elasticsearch.common.util.concurrent.ThreadContext; import java.util.ArrayList; +import java.util.Collections; import java.util.List; /** @@ -41,13 +42,11 @@ * each additional handler will also be called with a deprecated message and its key. */ public class DeprecationLogger { - /** Log handlers are are not tied to this specific DeprecationLogger instance */ - private static final List additionalHandlers = new ArrayList<>(); + /** Log handlers are not tied to this specific DeprecationLogger instance */ + private static final List additionalHandlers = Collections.synchronizedList(new ArrayList<>()); + private static final RateLimiter rateLimiter = new RateLimiter(); - /** Log handlers are are tied to this specific DeprecationLogger instance */ - private final List handlers; - - private final RateLimiter rateLimiter; + private final WarningLogger warningLogger; /** * Creates a new deprecation logger based on the parent logger. Automatically @@ -56,10 +55,7 @@ public class DeprecationLogger { * the "org.elasticsearch" namespace. */ public DeprecationLogger(Logger parentLogger) { - this.handlers = new ArrayList<>(); - this.rateLimiter = new RateLimiter(); - - handlers.add(new HeaderWarningLogger(deprecatedLoggerName(parentLogger.getName()))); + this.warningLogger = new WarningLogger(deprecatedLoggerName(parentLogger.getName())); } public static DeprecationLogger getLogger(Class aClass) { @@ -93,11 +89,11 @@ public static void removeThreadContext(ThreadContext threadContext) { } public static void addHandler(DeprecatedLogHandler handler) { - DeprecationLogger.additionalHandlers.add(handler); + additionalHandlers.add(handler); } public static void removeHandler(DeprecatedLogHandler handler) { - DeprecationLogger.additionalHandlers.remove(handler); + additionalHandlers.remove(handler); } /** @@ -106,24 +102,36 @@ public static void removeHandler(DeprecatedLogHandler handler) { * method returns a builder as more methods are expected to be chained. */ public DeprecationLoggerBuilder deprecate(final String key, final String msg, final Object... params) { - return new DeprecationLoggerBuilder() - .withDeprecation(key, msg, params); + return new DeprecationLoggerBuilder().withDeprecation(key, msg, params); } public class DeprecationLoggerBuilder { public DeprecationLoggerBuilder withDeprecation(String key, String msg, Object[] params) { String opaqueId = HeaderWarning.getXOpaqueId(); + ESLogMessage deprecationMessage = DeprecatedMessage.of(opaqueId, msg, params); + + // This is never rate-limited - we always add deprecation messages to response headers. + addHeaderWarning(deprecationMessage); + rateLimiter.limit(opaqueId + key, () -> { - ESLogMessage deprecationMessage = DeprecatedMessage.of(opaqueId, msg, params); - for (DeprecatedLogHandler handler : handlers) { - handler.log(key, opaqueId, deprecationMessage); - } + warningLogger.log(deprecationMessage); + for (DeprecatedLogHandler handler : additionalHandlers) { handler.log(key, opaqueId, deprecationMessage); } }); return this; } + + /** + * Adds a formatted warning message as a response header on the thread context. + */ + private void addHeaderWarning(ESLogMessage deprecationMessage) { + String messagePattern = deprecationMessage.getMessagePattern(); + Object[] arguments = deprecationMessage.getArguments(); + + HeaderWarning.addWarning(messagePattern, arguments); + } } } diff --git a/server/src/main/java/org/elasticsearch/common/logging/HeaderWarningLogger.java b/server/src/main/java/org/elasticsearch/common/logging/WarningLogger.java similarity index 69% rename from server/src/main/java/org/elasticsearch/common/logging/HeaderWarningLogger.java rename to server/src/main/java/org/elasticsearch/common/logging/WarningLogger.java index 5fe18596448a3..dd560d561ca42 100644 --- a/server/src/main/java/org/elasticsearch/common/logging/HeaderWarningLogger.java +++ b/server/src/main/java/org/elasticsearch/common/logging/WarningLogger.java @@ -26,28 +26,18 @@ import java.security.PrivilegedAction; /** - * This class composes {@link HeaderWarning} and {@link Logger}, in order to both log a message - * and add a header to an API response. + * This class writes deprecation messages to a specified logger at WARN level. *

* TODO wrapping logging this way limits the usage of %location. It will think this is used from that class. */ -class HeaderWarningLogger implements DeprecatedLogHandler { +class WarningLogger { private final Logger logger; - HeaderWarningLogger(Logger logger) { + WarningLogger(Logger logger) { this.logger = logger; } - /** - * Logs a deprecation message and adds a formatted warning message as a response - * header on the thread context. - */ - @Override - public void log(final String key, String xOpaqueId, ESLogMessage message) { - String messagePattern = message.getMessagePattern(); - Object[] arguments = message.getArguments(); - HeaderWarning.addWarning(messagePattern, arguments); - + public void log(ESLogMessage message) { AccessController.doPrivileged(new PrivilegedAction() { @SuppressLoggerChecks(reason = "safely delegates to logger") @Override diff --git a/x-pack/plugin/deprecation/src/main/java/org/elasticsearch/xpack/deprecation/DeprecationIndexingService.java b/x-pack/plugin/deprecation/src/main/java/org/elasticsearch/xpack/deprecation/DeprecationIndexingService.java index ed1507481a396..e2444cc7ac415 100644 --- a/x-pack/plugin/deprecation/src/main/java/org/elasticsearch/xpack/deprecation/DeprecationIndexingService.java +++ b/x-pack/plugin/deprecation/src/main/java/org/elasticsearch/xpack/deprecation/DeprecationIndexingService.java @@ -22,6 +22,7 @@ import org.elasticsearch.common.logging.DeprecatedLogHandler; import org.elasticsearch.common.logging.DeprecationLogger; import org.elasticsearch.common.logging.ESLogMessage; +import org.elasticsearch.common.logging.LoggerMessageFormat; import org.elasticsearch.common.settings.Setting; import java.time.Instant; @@ -65,7 +66,7 @@ public DeprecationIndexingService(ClusterService clusterService, Client client) * @param esLogMessage the message to log */ public void log(String key, String xOpaqueId, ESLogMessage esLogMessage) { - if (this.lifecycle.started() == false) { + if (this.lifecycle.started() == false) { return; } @@ -73,8 +74,9 @@ public void log(String key, String xOpaqueId, ESLogMessage esLogMessage) { return; } - String message = esLogMessage.getMessagePattern(); - Object[] params = esLogMessage.getArguments(); + String messagePattern = esLogMessage.getMessagePattern(); + Object[] arguments = esLogMessage.getArguments(); + String message = LoggerMessageFormat.format(messagePattern, arguments); Map payload = new HashMap<>(); payload.put("@timestamp", Instant.now().toString()); @@ -85,10 +87,6 @@ public void log(String key, String xOpaqueId, ESLogMessage esLogMessage) { payload.put("x-opaque-id", xOpaqueId); } - if (params != null && params.length > 0) { - payload.put("params", params); - } - new IndexRequestBuilder(client, IndexAction.INSTANCE).setIndex(DATA_STREAM_NAME) .setOpType(DocWriteRequest.OpType.CREATE) .setSource(payload) diff --git a/x-pack/plugin/deprecation/src/test/java/org/elasticsearch/xpack/deprecation/DeprecationIndexingServiceTests.java b/x-pack/plugin/deprecation/src/test/java/org/elasticsearch/xpack/deprecation/DeprecationIndexingServiceTests.java index 3bb7311d0e79b..4ddd7d1038735 100644 --- a/x-pack/plugin/deprecation/src/test/java/org/elasticsearch/xpack/deprecation/DeprecationIndexingServiceTests.java +++ b/x-pack/plugin/deprecation/src/test/java/org/elasticsearch/xpack/deprecation/DeprecationIndexingServiceTests.java @@ -113,7 +113,6 @@ public void testWritesMessageWhenServiceEnabled() { assertThat(payloadMap, hasEntry("message", "a message")); // Neither of these should exist since we passed null when writing the message assertThat(payloadMap, not(hasKey("x-opaque-id"))); - assertThat(payloadMap, not(hasKey("params"))); } /** @@ -129,17 +128,15 @@ public void testMessageIncludesOpaqueIdWhenSupplied() { } /** - * Check that if any params are set, then they are added to the index request payload. + * Check that if any arguments are set, then they substituted in the log message */ - public void testMessageIncludesParamsWhenSupplied() { + public void testMessageSubstitutesArgumentsWhenSupplied() { service.start(); service.clusterChanged(getEvent(true)); final Map payloadMap = getWriteRequest("a key", null, new ESLogMessage("a {} and {} message", "first", "second")); - // I can't get this to work as a one-liner. Curse you, Hamcrest. - assertThat(payloadMap, hasKey("params")); - assertThat(payloadMap.get("params"), equalTo(List.of("first", "second"))); + assertThat(payloadMap, hasEntry("message", "a first and second message")); } private ClusterChangedEvent getEvent(boolean enabled) { From f7f9ea7d9d8d7d7e8a21919c01228f9c7446d931 Mon Sep 17 00:00:00 2001 From: Rory Hunter Date: Wed, 8 Jul 2020 15:51:43 +0100 Subject: [PATCH 16/48] Fixes --- .../org/elasticsearch/common/logging/DeprecationLogger.java | 2 +- .../xpack/deprecation/DeprecationIndexingService.java | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/server/src/main/java/org/elasticsearch/common/logging/DeprecationLogger.java b/server/src/main/java/org/elasticsearch/common/logging/DeprecationLogger.java index 89063f2d928c6..52d59804cc22b 100644 --- a/server/src/main/java/org/elasticsearch/common/logging/DeprecationLogger.java +++ b/server/src/main/java/org/elasticsearch/common/logging/DeprecationLogger.java @@ -55,7 +55,7 @@ public class DeprecationLogger { * the "org.elasticsearch" namespace. */ public DeprecationLogger(Logger parentLogger) { - this.warningLogger = new WarningLogger(deprecatedLoggerName(parentLogger.getName())); + this.warningLogger = new WarningLogger(parentLogger); } public static DeprecationLogger getLogger(Class aClass) { diff --git a/x-pack/plugin/deprecation/src/main/java/org/elasticsearch/xpack/deprecation/DeprecationIndexingService.java b/x-pack/plugin/deprecation/src/main/java/org/elasticsearch/xpack/deprecation/DeprecationIndexingService.java index e2444cc7ac415..21126ef7b0ea7 100644 --- a/x-pack/plugin/deprecation/src/main/java/org/elasticsearch/xpack/deprecation/DeprecationIndexingService.java +++ b/x-pack/plugin/deprecation/src/main/java/org/elasticsearch/xpack/deprecation/DeprecationIndexingService.java @@ -45,7 +45,7 @@ public class DeprecationIndexingService extends AbstractLifecycleComponent imple public static final Setting WRITE_DEPRECATION_LOGS_TO_INDEX = Setting.boolSetting( "cluster.deprecation_indexing.enabled", - true, + false, Setting.Property.NodeScope, Setting.Property.Dynamic ); From 46c5aca6851c990f607403967cbd443fae26b385 Mon Sep 17 00:00:00 2001 From: Rory Hunter Date: Thu, 9 Jul 2020 10:09:13 +0100 Subject: [PATCH 17/48] Use BulkProcessor in DeprecationIndexingService In order to make the service easily testable, the details of the processor and thread pool etc are bundled up into a Consumer, which hides it from the service. --- .../common/logging/EvilLoggerTests.java | 8 +- .../xpack/deprecation/Deprecation.java | 80 ++++++++++++++++--- .../DeprecationIndexingService.java | 37 ++------- .../DeprecationIndexingServiceTests.java | 35 +++----- 4 files changed, 94 insertions(+), 66 deletions(-) diff --git a/qa/evil-tests/src/test/java/org/elasticsearch/common/logging/EvilLoggerTests.java b/qa/evil-tests/src/test/java/org/elasticsearch/common/logging/EvilLoggerTests.java index fdd64ef1e73d1..8411305422523 100644 --- a/qa/evil-tests/src/test/java/org/elasticsearch/common/logging/EvilLoggerTests.java +++ b/qa/evil-tests/src/test/java/org/elasticsearch/common/logging/EvilLoggerTests.java @@ -181,7 +181,7 @@ public void testConcurrentDeprecationLogger() throws IOException, UserException, assertLogLine( deprecationEvents.get(i), Level.WARN, - "org.elasticsearch.common.logging.ThrottlingLogger\\$2\\.run", + "org.elasticsearch.common.logging.WarningLogger\\$1\\.run", "This is a maybe logged deprecation message" + i); } @@ -223,13 +223,13 @@ public void testDeprecationLoggerMaybeLog() throws IOException, UserException { assertLogLine( deprecationEvents.get(0), Level.WARN, - "org.elasticsearch.common.logging.ThrottlingLogger\\$2\\.run", + "org.elasticsearch.common.logging.WarningLogger\\$1\\.run", "This is a maybe logged deprecation message"); for (int k = 0; k < 128; k++) { assertLogLine( deprecationEvents.get(1 + k), Level.WARN, - "org.elasticsearch.common.logging.ThrottlingLogger\\$2\\.run", + "org.elasticsearch.common.logging.WarningLogger\\$1\\.run", "This is a maybe logged deprecation message" + k); } } @@ -257,7 +257,7 @@ public void testDeprecatedSettings() throws IOException, UserException { assertLogLine( deprecationEvents.get(0), Level.WARN, - "org.elasticsearch.common.logging.ThrottlingLogger\\$2\\.run", + "org.elasticsearch.common.logging.WarningLogger\\$1\\.run", "\\[deprecated.foo\\] setting was deprecated in Elasticsearch and will be removed in a future release! " + "See the breaking changes documentation for the next major version."); } diff --git a/x-pack/plugin/deprecation/src/main/java/org/elasticsearch/xpack/deprecation/Deprecation.java b/x-pack/plugin/deprecation/src/main/java/org/elasticsearch/xpack/deprecation/Deprecation.java index 94439d801f561..7c16ccfe9e292 100644 --- a/x-pack/plugin/deprecation/src/main/java/org/elasticsearch/xpack/deprecation/Deprecation.java +++ b/x-pack/plugin/deprecation/src/main/java/org/elasticsearch/xpack/deprecation/Deprecation.java @@ -5,10 +5,16 @@ */ package org.elasticsearch.xpack.deprecation; - +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; import org.elasticsearch.action.ActionRequest; import org.elasticsearch.action.ActionResponse; +import org.elasticsearch.action.bulk.BulkProcessor; +import org.elasticsearch.action.bulk.BulkRequest; +import org.elasticsearch.action.bulk.BulkResponse; +import org.elasticsearch.action.index.IndexRequest; import org.elasticsearch.client.Client; +import org.elasticsearch.client.OriginSettingClient; import org.elasticsearch.cluster.metadata.IndexNameExpressionResolver; import org.elasticsearch.cluster.node.DiscoveryNodes; import org.elasticsearch.cluster.service.ClusterService; @@ -18,6 +24,7 @@ import org.elasticsearch.common.settings.Setting; import org.elasticsearch.common.settings.Settings; import org.elasticsearch.common.settings.SettingsFilter; +import org.elasticsearch.common.unit.TimeValue; import org.elasticsearch.common.xcontent.NamedXContentRegistry; import org.elasticsearch.env.Environment; import org.elasticsearch.env.NodeEnvironment; @@ -29,12 +36,14 @@ import org.elasticsearch.script.ScriptService; import org.elasticsearch.threadpool.ThreadPool; import org.elasticsearch.watcher.ResourceWatcherService; +import org.elasticsearch.xpack.core.ClientHelper; import org.elasticsearch.xpack.core.deprecation.DeprecationInfoAction; import org.elasticsearch.xpack.core.deprecation.NodesDeprecationCheckAction; import java.util.Collection; import java.util.Collections; import java.util.List; +import java.util.function.Consumer; import java.util.function.Supplier; import static org.elasticsearch.xpack.deprecation.DeprecationIndexingService.WRITE_DEPRECATION_LOGS_TO_INDEX; @@ -44,19 +53,26 @@ */ public class Deprecation extends Plugin implements ActionPlugin { + private static final Logger logger = LogManager.getLogger(Deprecation.class); + @Override public List> getActions() { return List.of( - new ActionHandler<>(DeprecationInfoAction.INSTANCE, TransportDeprecationInfoAction.class), - new ActionHandler<>(NodesDeprecationCheckAction.INSTANCE, TransportNodeDeprecationCheckAction.class)); + new ActionHandler<>(DeprecationInfoAction.INSTANCE, TransportDeprecationInfoAction.class), + new ActionHandler<>(NodesDeprecationCheckAction.INSTANCE, TransportNodeDeprecationCheckAction.class) + ); } @Override - public List getRestHandlers(Settings settings, RestController restController, ClusterSettings clusterSettings, - IndexScopedSettings indexScopedSettings, SettingsFilter settingsFilter, - IndexNameExpressionResolver indexNameExpressionResolver, - Supplier nodesInCluster) { - + public List getRestHandlers( + Settings settings, + RestController restController, + ClusterSettings clusterSettings, + IndexScopedSettings indexScopedSettings, + SettingsFilter settingsFilter, + IndexNameExpressionResolver indexNameExpressionResolver, + Supplier nodesInCluster + ) { return Collections.singletonList(new RestDeprecationInfoAction()); } @@ -75,7 +91,7 @@ public Collection createComponents( IndexNameExpressionResolver indexNameExpressionResolver, Supplier repositoriesServiceSupplier ) { - DeprecationIndexingService service = new DeprecationIndexingService(clusterService, client); + DeprecationIndexingService service = new DeprecationIndexingService(clusterService, buildIndexRequestConsumer(threadPool, client)); return List.of(service); } @@ -84,4 +100,50 @@ public Collection createComponents( public List> getSettings() { return List.of(WRITE_DEPRECATION_LOGS_TO_INDEX); } + + /** + * Constructs a {@link Consumer} that knows what to do with the {@link IndexRequest} instances that the + * {@link DeprecationIndexingService} creates. This logic is separated from the service in order to make + * testing significantly easier, and to separate concerns. + *

+ * Writes are done via {@link BulkProcessor}, which handles batching up writes and retries. + * + * @param threadPool due to #50440, + * extra care must be taken to avoid blocking the thread that writes a deprecation message. + * @param client the client to pass to {@link BulkProcessor} + * @return a consumer that accepts an index request and handles all the details of writing it + * into the cluster + */ + private Consumer buildIndexRequestConsumer(ThreadPool threadPool, Client client) { + final OriginSettingClient originSettingClient = new OriginSettingClient(client, ClientHelper.DEPRECATION_ORIGIN); + + final BulkProcessor.Listener listener = new BulkProcessor.Listener() { + @Override + public void beforeBulk(long executionId, BulkRequest request) {} + + @Override + public void afterBulk(long executionId, BulkRequest request, BulkResponse response) {} + + @Override + public void afterBulk(long executionId, BulkRequest request, Throwable failure) { + logger.error("Bulk write of deprecation logs failed: " + failure.getMessage(), failure); + } + }; + + BulkProcessor processor = BulkProcessor.builder(originSettingClient::bulk, listener) + .setBulkActions(100) + .setFlushInterval(TimeValue.timeValueSeconds(5)) + .build(); + + return indexRequest -> { + try { + // TODO: remove the threadpool wrapping when the .add call is non-blocking + // (it can currently execute the bulk request occasionally) + // see: https://github.com/elastic/elasticsearch/issues/50440 + threadPool.executor(ThreadPool.Names.GENERIC).execute(() -> processor.add(indexRequest)); + } catch (Exception e) { + logger.error("Failed to queue deprecation message index request: " + e.getMessage(), e); + } + }; + } } diff --git a/x-pack/plugin/deprecation/src/main/java/org/elasticsearch/xpack/deprecation/DeprecationIndexingService.java b/x-pack/plugin/deprecation/src/main/java/org/elasticsearch/xpack/deprecation/DeprecationIndexingService.java index 21126ef7b0ea7..1f32b3ab9fff2 100644 --- a/x-pack/plugin/deprecation/src/main/java/org/elasticsearch/xpack/deprecation/DeprecationIndexingService.java +++ b/x-pack/plugin/deprecation/src/main/java/org/elasticsearch/xpack/deprecation/DeprecationIndexingService.java @@ -6,15 +6,8 @@ package org.elasticsearch.xpack.deprecation; -import org.apache.logging.log4j.LogManager; -import org.apache.logging.log4j.Logger; -import org.elasticsearch.action.ActionListener; import org.elasticsearch.action.DocWriteRequest; -import org.elasticsearch.action.index.IndexAction; -import org.elasticsearch.action.index.IndexRequestBuilder; -import org.elasticsearch.action.index.IndexResponse; -import org.elasticsearch.client.Client; -import org.elasticsearch.client.OriginSettingClient; +import org.elasticsearch.action.index.IndexRequest; import org.elasticsearch.cluster.ClusterChangedEvent; import org.elasticsearch.cluster.ClusterStateListener; import org.elasticsearch.cluster.service.ClusterService; @@ -28,6 +21,7 @@ import java.time.Instant; import java.util.HashMap; import java.util.Map; +import java.util.function.Consumer; import static org.elasticsearch.common.Strings.isNullOrEmpty; @@ -37,24 +31,20 @@ * {@link #WRITE_DEPRECATION_LOGS_TO_INDEX} setting. */ public class DeprecationIndexingService extends AbstractLifecycleComponent implements ClusterStateListener, DeprecatedLogHandler { - private static final Logger LOGGER = LogManager.getLogger(DeprecationIndexingService.class); - private static final String DATA_STREAM_NAME = "logs-deprecation-elasticsearch"; - private static final String DEPRECATION_ORIGIN = "deprecation"; - public static final Setting WRITE_DEPRECATION_LOGS_TO_INDEX = Setting.boolSetting( "cluster.deprecation_indexing.enabled", false, Setting.Property.NodeScope, Setting.Property.Dynamic ); + private final Consumer requestConsumer; - private final Client client; private volatile boolean isEnabled = false; - public DeprecationIndexingService(ClusterService clusterService, Client client) { - this.client = new OriginSettingClient(client, DEPRECATION_ORIGIN); + public DeprecationIndexingService(ClusterService clusterService, Consumer requestConsumer) { + this.requestConsumer = requestConsumer; clusterService.addListener(this); } @@ -87,20 +77,9 @@ public void log(String key, String xOpaqueId, ESLogMessage esLogMessage) { payload.put("x-opaque-id", xOpaqueId); } - new IndexRequestBuilder(client, IndexAction.INSTANCE).setIndex(DATA_STREAM_NAME) - .setOpType(DocWriteRequest.OpType.CREATE) - .setSource(payload) - .execute(new ActionListener<>() { - @Override - public void onResponse(IndexResponse indexResponse) { - // Nothing to do - } - - @Override - public void onFailure(Exception e) { - LOGGER.error("Failed to index deprecation message", e); - } - }); + final IndexRequest request = new IndexRequest(DATA_STREAM_NAME).source(payload).opType(DocWriteRequest.OpType.CREATE); + + this.requestConsumer.accept(request); } /** diff --git a/x-pack/plugin/deprecation/src/test/java/org/elasticsearch/xpack/deprecation/DeprecationIndexingServiceTests.java b/x-pack/plugin/deprecation/src/test/java/org/elasticsearch/xpack/deprecation/DeprecationIndexingServiceTests.java index 4ddd7d1038735..5cef9038d1a63 100644 --- a/x-pack/plugin/deprecation/src/test/java/org/elasticsearch/xpack/deprecation/DeprecationIndexingServiceTests.java +++ b/x-pack/plugin/deprecation/src/test/java/org/elasticsearch/xpack/deprecation/DeprecationIndexingServiceTests.java @@ -6,10 +6,7 @@ package org.elasticsearch.xpack.deprecation; -import org.elasticsearch.action.ActionListener; -import org.elasticsearch.action.index.IndexAction; import org.elasticsearch.action.index.IndexRequest; -import org.elasticsearch.client.Client; import org.elasticsearch.cluster.ClusterChangedEvent; import org.elasticsearch.cluster.ClusterName; import org.elasticsearch.cluster.ClusterState; @@ -18,42 +15,37 @@ import org.elasticsearch.common.logging.ESLogMessage; import org.elasticsearch.common.settings.Settings; import org.elasticsearch.test.ESTestCase; -import org.elasticsearch.test.client.NoOpClient; -import org.junit.After; import org.junit.Before; import org.mockito.ArgumentCaptor; -import java.util.List; import java.util.Map; +import java.util.function.Consumer; import static org.elasticsearch.xpack.deprecation.DeprecationIndexingService.WRITE_DEPRECATION_LOGS_TO_INDEX; -import static org.hamcrest.Matchers.equalTo; import static org.hamcrest.Matchers.hasEntry; import static org.hamcrest.Matchers.hasKey; import static org.hamcrest.Matchers.not; import static org.mockito.Matchers.any; -import static org.mockito.Matchers.eq; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.never; -import static org.mockito.Mockito.spy; import static org.mockito.Mockito.verify; public class DeprecationIndexingServiceTests extends ESTestCase { private DeprecationIndexingService service; private ClusterService clusterService; - private Client client; + private Consumer consumer; @Before public void initialize() { + consumer = getConsumer(); clusterService = mock(ClusterService.class); - client = spy(new NoOpClient(this.getTestName())); - service = new DeprecationIndexingService(clusterService, client); + service = new DeprecationIndexingService(clusterService, consumer); } - @After - public void cleanup() { - client.close(); + @SuppressWarnings("unchecked") + private Consumer getConsumer() { + return mock(Consumer.class); } /** @@ -71,7 +63,7 @@ public void testClusterStateListenerRegistered() { public void testDoesNotWriteMessageWhenServiceNotStarted() { service.log("a key", "xOpaqueId", new ESLogMessage("a message")); - verify(client, never()).execute(any(), any(), anyListener()); + verify(consumer, never()).accept(any()); } /** @@ -83,7 +75,7 @@ public void testDoesNotWriteMessageWhenServiceDisabled() { service.log("a key", "xOpaqueId", new ESLogMessage("a message")); - verify(client, never()).execute(any(), any(), anyListener()); + verify(consumer, never()).accept(any()); } /** @@ -96,7 +88,7 @@ public void testDoesNotWriteMessageWhenServiceEnabledAndDisabled() { service.log("a key", "xOpaqueId", new ESLogMessage("a message")); - verify(client, never()).execute(eq(IndexAction.INSTANCE), any(), anyListener()); + verify(consumer, never()).accept(any()); } /** @@ -147,11 +139,6 @@ private ClusterChangedEvent getEvent(boolean enabled) { return new ClusterChangedEvent("test", clusterState, clusterState); } - // This exists to silence a generics warning - private ActionListener anyListener() { - return any(); - } - /* * Wraps up the steps for extracting an index request payload from the mocks. */ @@ -160,7 +147,7 @@ private Map getWriteRequest(String key, String xOpaqueId, ESLogM ArgumentCaptor argument = ArgumentCaptor.forClass(IndexRequest.class); - verify(client).execute(eq(IndexAction.INSTANCE), argument.capture(), anyListener()); + verify(consumer).accept(argument.capture()); return argument.getValue().sourceAsMap(); } From c8d8086c3809ad8a6c5399e1d41845b6b70ca436 Mon Sep 17 00:00:00 2001 From: Rory Hunter Date: Thu, 9 Jul 2020 15:36:19 +0100 Subject: [PATCH 18/48] Use the opaque ID from the EsLogMessage --- .../common/logging/DeprecatedLogHandler.java | 5 +---- .../common/logging/DeprecatedMessage.java | 2 +- .../common/logging/DeprecationLogger.java | 2 +- .../DeprecationIndexingService.java | 5 ++++- .../DeprecationIndexingServiceTests.java | 20 +++++++++++-------- 5 files changed, 19 insertions(+), 15 deletions(-) diff --git a/server/src/main/java/org/elasticsearch/common/logging/DeprecatedLogHandler.java b/server/src/main/java/org/elasticsearch/common/logging/DeprecatedLogHandler.java index 37314f534f7da..557e978af7149 100644 --- a/server/src/main/java/org/elasticsearch/common/logging/DeprecatedLogHandler.java +++ b/server/src/main/java/org/elasticsearch/common/logging/DeprecatedLogHandler.java @@ -27,10 +27,7 @@ public interface DeprecatedLogHandler { /** * Handle a single deprecation log message. * @param key a value that should uniquely identify the deprecation - * @param xOpaqueId the value of any X-Opaque-Id header that was submitted - * with a REST request. This allows deprecation messages to be linked to - * whatever triggered the deprecation behaviour in the first place. * @param message the deprecation message itself */ - void log(String key, String xOpaqueId, ESLogMessage message); + void log(String key, ESLogMessage message); } diff --git a/server/src/main/java/org/elasticsearch/common/logging/DeprecatedMessage.java b/server/src/main/java/org/elasticsearch/common/logging/DeprecatedMessage.java index 2cc882d61695a..95dd0755a25a0 100644 --- a/server/src/main/java/org/elasticsearch/common/logging/DeprecatedMessage.java +++ b/server/src/main/java/org/elasticsearch/common/logging/DeprecatedMessage.java @@ -28,7 +28,7 @@ * Carries x-opaque-id field if provided in the headers. Will populate the x-opaque-id field in JSON logs. */ public class DeprecatedMessage { - private static final String X_OPAQUE_ID_FIELD_NAME = "x-opaque-id"; + public static final String X_OPAQUE_ID_FIELD_NAME = "x-opaque-id"; @SuppressLoggerChecks(reason = "safely delegates to logger") public static ESLogMessage of(String xOpaqueId, String messagePattern, Object... args){ diff --git a/server/src/main/java/org/elasticsearch/common/logging/DeprecationLogger.java b/server/src/main/java/org/elasticsearch/common/logging/DeprecationLogger.java index 52d59804cc22b..793309c37061f 100644 --- a/server/src/main/java/org/elasticsearch/common/logging/DeprecationLogger.java +++ b/server/src/main/java/org/elasticsearch/common/logging/DeprecationLogger.java @@ -118,7 +118,7 @@ public DeprecationLoggerBuilder withDeprecation(String key, String msg, Object[] warningLogger.log(deprecationMessage); for (DeprecatedLogHandler handler : additionalHandlers) { - handler.log(key, opaqueId, deprecationMessage); + handler.log(key, deprecationMessage); } }); return this; diff --git a/x-pack/plugin/deprecation/src/main/java/org/elasticsearch/xpack/deprecation/DeprecationIndexingService.java b/x-pack/plugin/deprecation/src/main/java/org/elasticsearch/xpack/deprecation/DeprecationIndexingService.java index 1f32b3ab9fff2..6ed580d37933c 100644 --- a/x-pack/plugin/deprecation/src/main/java/org/elasticsearch/xpack/deprecation/DeprecationIndexingService.java +++ b/x-pack/plugin/deprecation/src/main/java/org/elasticsearch/xpack/deprecation/DeprecationIndexingService.java @@ -24,6 +24,7 @@ import java.util.function.Consumer; import static org.elasticsearch.common.Strings.isNullOrEmpty; +import static org.elasticsearch.common.logging.DeprecatedMessage.X_OPAQUE_ID_FIELD_NAME; /** * This service is responsible for writing deprecation messages to a data stream. @@ -55,7 +56,7 @@ public DeprecationIndexingService(ClusterService clusterService, Consumer payloadMap = getWriteRequest("a key", null, new ESLogMessage("a message")); + final Map payloadMap = getWriteRequest("a key", DeprecatedMessage.of(null, "a message")); assertThat(payloadMap, hasKey("@timestamp")); assertThat(payloadMap, hasEntry("key", "a key")); @@ -114,7 +115,7 @@ public void testMessageIncludesOpaqueIdWhenSupplied() { service.start(); service.clusterChanged(getEvent(true)); - final Map payloadMap = getWriteRequest("a key", "an ID", new ESLogMessage("a message")); + final Map payloadMap = getWriteRequest("a key", DeprecatedMessage.of("an ID", "a message")); assertThat(payloadMap, hasEntry("x-opaque-id", "an ID")); } @@ -126,7 +127,10 @@ public void testMessageSubstitutesArgumentsWhenSupplied() { service.start(); service.clusterChanged(getEvent(true)); - final Map payloadMap = getWriteRequest("a key", null, new ESLogMessage("a {} and {} message", "first", "second")); + final Map payloadMap = getWriteRequest( + "a key", + DeprecatedMessage.of(null, "a {} and {} message", "first", "second") + ); assertThat(payloadMap, hasEntry("message", "a first and second message")); } @@ -142,8 +146,8 @@ private ClusterChangedEvent getEvent(boolean enabled) { /* * Wraps up the steps for extracting an index request payload from the mocks. */ - private Map getWriteRequest(String key, String xOpaqueId, ESLogMessage message) { - service.log(key, xOpaqueId, message); + private Map getWriteRequest(String key, ESLogMessage message) { + service.log(key, message); ArgumentCaptor argument = ArgumentCaptor.forClass(IndexRequest.class); From 4bf9b344d3748c52528c8a057199ced29eac0a3a Mon Sep 17 00:00:00 2001 From: Rory Hunter Date: Mon, 13 Jul 2020 14:41:31 +0100 Subject: [PATCH 19/48] Record cluster and node IDs --- .../deprecation/DeprecationIndexingService.java | 16 +++++++++++++--- .../DeprecationIndexingServiceTests.java | 9 +++++++-- 2 files changed, 20 insertions(+), 5 deletions(-) diff --git a/x-pack/plugin/deprecation/src/main/java/org/elasticsearch/xpack/deprecation/DeprecationIndexingService.java b/x-pack/plugin/deprecation/src/main/java/org/elasticsearch/xpack/deprecation/DeprecationIndexingService.java index 6ed580d37933c..7414d4718b04b 100644 --- a/x-pack/plugin/deprecation/src/main/java/org/elasticsearch/xpack/deprecation/DeprecationIndexingService.java +++ b/x-pack/plugin/deprecation/src/main/java/org/elasticsearch/xpack/deprecation/DeprecationIndexingService.java @@ -9,6 +9,7 @@ import org.elasticsearch.action.DocWriteRequest; import org.elasticsearch.action.index.IndexRequest; import org.elasticsearch.cluster.ClusterChangedEvent; +import org.elasticsearch.cluster.ClusterState; import org.elasticsearch.cluster.ClusterStateListener; import org.elasticsearch.cluster.service.ClusterService; import org.elasticsearch.common.component.AbstractLifecycleComponent; @@ -40,13 +41,15 @@ public class DeprecationIndexingService extends AbstractLifecycleComponent imple Setting.Property.NodeScope, Setting.Property.Dynamic ); + private final Consumer requestConsumer; + private String clusterUUID; + private String nodeId; private volatile boolean isEnabled = false; public DeprecationIndexingService(ClusterService clusterService, Consumer requestConsumer) { this.requestConsumer = requestConsumer; - clusterService.addListener(this); } @@ -73,6 +76,8 @@ public void log(String key, ESLogMessage esLogMessage) { payload.put("@timestamp", Instant.now().toString()); payload.put("key", key); payload.put("message", message); + payload.put("cluster.uuid", clusterUUID); + payload.put("node.id", nodeId); String xOpaqueId = esLogMessage.get(X_OPAQUE_ID_FIELD_NAME); @@ -86,11 +91,16 @@ public void log(String key, ESLogMessage esLogMessage) { } /** - * Listens for changes to the cluster state, in order to know whether to toggle indexing. + * Listens for changes to the cluster state, in order to know whether to toggle indexing + * and to set the cluster UUID and node ID. These can't be set in the constructor because + * the initial cluster state won't be set yet. */ @Override public void clusterChanged(ClusterChangedEvent event) { - this.isEnabled = WRITE_DEPRECATION_LOGS_TO_INDEX.get(event.state().getMetadata().settings()); + final ClusterState state = event.state(); + this.isEnabled = WRITE_DEPRECATION_LOGS_TO_INDEX.get(state.getMetadata().settings()); + this.clusterUUID = state.getMetadata().clusterUUID(); + this.nodeId = state.nodes().getLocalNodeId(); } @Override diff --git a/x-pack/plugin/deprecation/src/test/java/org/elasticsearch/xpack/deprecation/DeprecationIndexingServiceTests.java b/x-pack/plugin/deprecation/src/test/java/org/elasticsearch/xpack/deprecation/DeprecationIndexingServiceTests.java index f361958588657..ba67b9244fd43 100644 --- a/x-pack/plugin/deprecation/src/test/java/org/elasticsearch/xpack/deprecation/DeprecationIndexingServiceTests.java +++ b/x-pack/plugin/deprecation/src/test/java/org/elasticsearch/xpack/deprecation/DeprecationIndexingServiceTests.java @@ -11,6 +11,7 @@ import org.elasticsearch.cluster.ClusterName; import org.elasticsearch.cluster.ClusterState; import org.elasticsearch.cluster.metadata.Metadata; +import org.elasticsearch.cluster.node.DiscoveryNodes; import org.elasticsearch.cluster.service.ClusterService; import org.elasticsearch.common.logging.DeprecatedMessage; import org.elasticsearch.common.logging.ESLogMessage; @@ -104,6 +105,8 @@ public void testWritesMessageWhenServiceEnabled() { assertThat(payloadMap, hasKey("@timestamp")); assertThat(payloadMap, hasEntry("key", "a key")); assertThat(payloadMap, hasEntry("message", "a message")); + assertThat(payloadMap, hasEntry("cluster.uuid", "cluster-uuid")); + assertThat(payloadMap, hasEntry("node.id", "local-node-id")); // Neither of these should exist since we passed null when writing the message assertThat(payloadMap, not(hasKey("x-opaque-id"))); } @@ -137,8 +140,10 @@ public void testMessageSubstitutesArgumentsWhenSupplied() { private ClusterChangedEvent getEvent(boolean enabled) { Settings settings = Settings.builder().put(WRITE_DEPRECATION_LOGS_TO_INDEX.getKey(), enabled).build(); - final Metadata metadata = Metadata.builder().transientSettings(settings).build(); - final ClusterState clusterState = ClusterState.builder(new ClusterName("test")).metadata(metadata).build(); + final Metadata metadata = Metadata.builder().clusterUUID("cluster-uuid").transientSettings(settings).build(); + final DiscoveryNodes nodes = DiscoveryNodes.builder().localNodeId("local-node-id").build(); + + final ClusterState clusterState = ClusterState.builder(new ClusterName("test")).metadata(metadata).nodes(nodes).build(); return new ClusterChangedEvent("test", clusterState, clusterState); } From 75eac35265927aaf0d4b7da6f27a9ea13bd77fa5 Mon Sep 17 00:00:00 2001 From: Rory Hunter Date: Tue, 14 Jul 2020 10:04:49 +0100 Subject: [PATCH 20/48] Tweaks --- .../DeprecationIndexingServiceTests.java | 15 ++++++--------- 1 file changed, 6 insertions(+), 9 deletions(-) diff --git a/x-pack/plugin/deprecation/src/test/java/org/elasticsearch/xpack/deprecation/DeprecationIndexingServiceTests.java b/x-pack/plugin/deprecation/src/test/java/org/elasticsearch/xpack/deprecation/DeprecationIndexingServiceTests.java index ba67b9244fd43..fce7fe3bdd389 100644 --- a/x-pack/plugin/deprecation/src/test/java/org/elasticsearch/xpack/deprecation/DeprecationIndexingServiceTests.java +++ b/x-pack/plugin/deprecation/src/test/java/org/elasticsearch/xpack/deprecation/DeprecationIndexingServiceTests.java @@ -39,17 +39,13 @@ public class DeprecationIndexingServiceTests extends ESTestCase { private Consumer consumer; @Before + @SuppressWarnings("unchecked") public void initialize() { - consumer = getConsumer(); + consumer = mock(Consumer.class); clusterService = mock(ClusterService.class); service = new DeprecationIndexingService(clusterService, consumer); } - @SuppressWarnings("unchecked") - private Consumer getConsumer() { - return mock(Consumer.class); - } - /** * Checks that the service registers a cluster state listener, so that the service * can be enabled and disabled. @@ -138,8 +134,8 @@ public void testMessageSubstitutesArgumentsWhenSupplied() { assertThat(payloadMap, hasEntry("message", "a first and second message")); } - private ClusterChangedEvent getEvent(boolean enabled) { - Settings settings = Settings.builder().put(WRITE_DEPRECATION_LOGS_TO_INDEX.getKey(), enabled).build(); + private ClusterChangedEvent getEvent(boolean shouldWriteDeprecationLogs) { + Settings settings = Settings.builder().put(WRITE_DEPRECATION_LOGS_TO_INDEX.getKey(), shouldWriteDeprecationLogs).build(); final Metadata metadata = Metadata.builder().clusterUUID("cluster-uuid").transientSettings(settings).build(); final DiscoveryNodes nodes = DiscoveryNodes.builder().localNodeId("local-node-id").build(); @@ -158,6 +154,7 @@ private Map getWriteRequest(String key, ESLogMessage message) { verify(consumer).accept(argument.capture()); - return argument.getValue().sourceAsMap(); + final IndexRequest indexRequest = argument.getValue(); + return indexRequest.sourceAsMap(); } } From 8e4339f361cdcfa52896ab150b2716ae60b7bea4 Mon Sep 17 00:00:00 2001 From: Rory Hunter Date: Wed, 15 Jul 2020 15:21:22 +0100 Subject: [PATCH 21/48] WIP - trying a log4j implementation --- distribution/src/config/log4j2.properties | 2 +- modules/deprecation-logger/build.gradle | 23 +++ .../deprecation/DeprecationComponent.java | 49 ++++++ .../deprecation/DeprecationPlugin.java | 57 +++++++ .../deprecation/HeaderWarningAppender.java | 49 ++++++ .../common/logging/DeprecationLogger.java | 70 +++------ .../common/logging/RateLimiter.java | 4 +- .../common/logging/WarningLogger.java | 50 ------ .../xpack/deprecation/Deprecation.java | 69 +-------- .../DeprecationIndexingService.java | 120 -------------- .../logging/DeprecationIndexingAppender.java | 99 ++++++++++++ .../logging/DeprecationIndexingComponent.java | 146 ++++++++++++++++++ ... => DeprecationIndexingAppenderTests.java} | 80 ++++------ 13 files changed, 475 insertions(+), 343 deletions(-) create mode 100644 modules/deprecation-logger/build.gradle create mode 100644 modules/deprecation-logger/src/main/java/org/elasticsearch/deprecation/DeprecationComponent.java create mode 100644 modules/deprecation-logger/src/main/java/org/elasticsearch/deprecation/DeprecationPlugin.java create mode 100644 modules/deprecation-logger/src/main/java/org/elasticsearch/deprecation/HeaderWarningAppender.java delete mode 100644 server/src/main/java/org/elasticsearch/common/logging/WarningLogger.java delete mode 100644 x-pack/plugin/deprecation/src/main/java/org/elasticsearch/xpack/deprecation/DeprecationIndexingService.java create mode 100644 x-pack/plugin/deprecation/src/main/java/org/elasticsearch/xpack/deprecation/logging/DeprecationIndexingAppender.java create mode 100644 x-pack/plugin/deprecation/src/main/java/org/elasticsearch/xpack/deprecation/logging/DeprecationIndexingComponent.java rename x-pack/plugin/deprecation/src/test/java/org/elasticsearch/xpack/deprecation/{DeprecationIndexingServiceTests.java => DeprecationIndexingAppenderTests.java} (58%) diff --git a/distribution/src/config/log4j2.properties b/distribution/src/config/log4j2.properties index 2aac6f58dc4e6..0dfd19a2a8c20 100644 --- a/distribution/src/config/log4j2.properties +++ b/distribution/src/config/log4j2.properties @@ -73,7 +73,7 @@ appender.deprecation_rolling.strategy.max = 4 ################################################# logger.deprecation.name = org.elasticsearch.deprecation -logger.deprecation.level = warn +logger.deprecation.level = deprecation logger.deprecation.appenderRef.deprecation_rolling.ref = deprecation_rolling logger.deprecation.additivity = false diff --git a/modules/deprecation-logger/build.gradle b/modules/deprecation-logger/build.gradle new file mode 100644 index 0000000000000..9e39ca7bfa87d --- /dev/null +++ b/modules/deprecation-logger/build.gradle @@ -0,0 +1,23 @@ +/* + * Licensed to Elasticsearch under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch 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. + */ + +esplugin { + description 'Plugin for logging deprecation messages' + classname 'org.elasticsearch.kibana.DeprecationPlugin' +} diff --git a/modules/deprecation-logger/src/main/java/org/elasticsearch/deprecation/DeprecationComponent.java b/modules/deprecation-logger/src/main/java/org/elasticsearch/deprecation/DeprecationComponent.java new file mode 100644 index 0000000000000..d110063f507e5 --- /dev/null +++ b/modules/deprecation-logger/src/main/java/org/elasticsearch/deprecation/DeprecationComponent.java @@ -0,0 +1,49 @@ +/* + * Licensed to Elasticsearch under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch 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. + */ + +package org.elasticsearch.deprecation; + +import org.apache.logging.log4j.LogManager; +import org.elasticsearch.common.component.AbstractLifecycleComponent; +import org.elasticsearch.common.logging.Loggers; + +import static org.elasticsearch.common.logging.DeprecationLogger.DEPRECATION_ONLY; + +public class DeprecationComponent extends AbstractLifecycleComponent { + private final HeaderWarningAppender appender; + + public DeprecationComponent() { + this.appender = new HeaderWarningAppender("HeaderWarning", DEPRECATION_ONLY); + } + + @Override + protected void doStart() { + Loggers.addAppender(LogManager.getLogger("org.elasticsearch.deprecation"), this.appender); + } + + @Override + protected void doStop() { + Loggers.removeAppender(LogManager.getLogger("org.elasticsearch.deprecation"), this.appender); + } + + @Override + protected void doClose() { + // Nothing to do at present + } +} diff --git a/modules/deprecation-logger/src/main/java/org/elasticsearch/deprecation/DeprecationPlugin.java b/modules/deprecation-logger/src/main/java/org/elasticsearch/deprecation/DeprecationPlugin.java new file mode 100644 index 0000000000000..e04bdcf9c9044 --- /dev/null +++ b/modules/deprecation-logger/src/main/java/org/elasticsearch/deprecation/DeprecationPlugin.java @@ -0,0 +1,57 @@ +/* + * Licensed to Elasticsearch under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch 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. + */ + +package org.elasticsearch.deprecation; + +import org.elasticsearch.client.Client; +import org.elasticsearch.cluster.metadata.IndexNameExpressionResolver; +import org.elasticsearch.cluster.service.ClusterService; +import org.elasticsearch.common.io.stream.NamedWriteableRegistry; +import org.elasticsearch.common.xcontent.NamedXContentRegistry; +import org.elasticsearch.env.Environment; +import org.elasticsearch.env.NodeEnvironment; +import org.elasticsearch.plugins.Plugin; +import org.elasticsearch.repositories.RepositoriesService; +import org.elasticsearch.script.ScriptService; +import org.elasticsearch.threadpool.ThreadPool; +import org.elasticsearch.watcher.ResourceWatcherService; + +import java.util.Collection; +import java.util.List; +import java.util.function.Supplier; + +public class DeprecationPlugin extends Plugin { + + @Override + public Collection createComponents( + Client client, + ClusterService clusterService, + ThreadPool threadPool, + ResourceWatcherService resourceWatcherService, + ScriptService scriptService, + NamedXContentRegistry xContentRegistry, + Environment environment, + NodeEnvironment nodeEnvironment, + NamedWriteableRegistry namedWriteableRegistry, + IndexNameExpressionResolver indexNameExpressionResolver, + Supplier repositoriesServiceSupplier + ) { + return List.of(new DeprecationComponent()); + } +} diff --git a/modules/deprecation-logger/src/main/java/org/elasticsearch/deprecation/HeaderWarningAppender.java b/modules/deprecation-logger/src/main/java/org/elasticsearch/deprecation/HeaderWarningAppender.java new file mode 100644 index 0000000000000..84eb8d3551da4 --- /dev/null +++ b/modules/deprecation-logger/src/main/java/org/elasticsearch/deprecation/HeaderWarningAppender.java @@ -0,0 +1,49 @@ +/* + * Licensed to Elasticsearch under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch 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. + */ + +package org.elasticsearch.deprecation; + +import org.apache.logging.log4j.core.Filter; +import org.apache.logging.log4j.core.LogEvent; +import org.apache.logging.log4j.core.appender.AbstractAppender; +import org.apache.logging.log4j.core.config.plugins.Plugin; +import org.apache.logging.log4j.message.Message; +import org.elasticsearch.common.logging.ESLogMessage; +import org.elasticsearch.common.logging.HeaderWarning; + +@Plugin(name = "HeaderWarning", category = "Elastic") +public class HeaderWarningAppender extends AbstractAppender { + public HeaderWarningAppender(String name, Filter filter) { + super(name, filter, null); + } + + @Override + public void append(LogEvent event) { + final Message message = event.getMessage(); + + if (message instanceof ESLogMessage) { + final ESLogMessage esLogMessage = (ESLogMessage) message; + + String messagePattern = esLogMessage.getMessagePattern(); + Object[] arguments = esLogMessage.getArguments(); + + HeaderWarning.addWarning(messagePattern, arguments); + } + } +} diff --git a/server/src/main/java/org/elasticsearch/common/logging/DeprecationLogger.java b/server/src/main/java/org/elasticsearch/common/logging/DeprecationLogger.java index 793309c37061f..99af0529e9236 100644 --- a/server/src/main/java/org/elasticsearch/common/logging/DeprecationLogger.java +++ b/server/src/main/java/org/elasticsearch/common/logging/DeprecationLogger.java @@ -19,13 +19,11 @@ package org.elasticsearch.common.logging; +import org.apache.logging.log4j.Level; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; -import org.elasticsearch.common.util.concurrent.ThreadContext; - -import java.util.ArrayList; -import java.util.Collections; -import java.util.List; +import org.apache.logging.log4j.core.Filter; +import org.apache.logging.log4j.core.filter.LevelRangeFilter; /** * A logger that logs deprecation notices. Logger should be initialized with a parent logger which name will be used @@ -37,16 +35,19 @@ * All deprecation usages are throttled basing on a key. Key is a string provided in an argument and can be prefixed with * X-Opaque-Id. This allows to throttle deprecations per client usage. * deprecationLogger.deprecate("key","message {}", "param"); - *

- * Additional log handlers can be registered via {@link #addHandler(DeprecatedLogHandler)}. Subject to rate limiting, - * each additional handler will also be called with a deprecated message and its key. */ public class DeprecationLogger { - /** Log handlers are not tied to this specific DeprecationLogger instance */ - private static final List additionalHandlers = Collections.synchronizedList(new ArrayList<>()); - private static final RateLimiter rateLimiter = new RateLimiter(); + public static Level DEPRECATION = Level.forName("DEPRECATION", Level.WARN.intLevel() + 1); - private final WarningLogger warningLogger; + // Only handle log events with the custom DEPRECATION level + public static final LevelRangeFilter DEPRECATION_ONLY = LevelRangeFilter.createFilter( + DEPRECATION, + DEPRECATION, + Filter.Result.ACCEPT, + Filter.Result.DENY + ); + + private final Logger logger; /** * Creates a new deprecation logger based on the parent logger. Automatically @@ -55,7 +56,7 @@ public class DeprecationLogger { * the "org.elasticsearch" namespace. */ public DeprecationLogger(Logger parentLogger) { - this.warningLogger = new WarningLogger(parentLogger); + this.logger = parentLogger; } public static DeprecationLogger getLogger(Class aClass) { @@ -63,10 +64,10 @@ public static DeprecationLogger getLogger(Class aClass) { } public static DeprecationLogger getLogger(String name) { - return new DeprecationLogger(deprecatedLoggerName(name)); + return new DeprecationLogger(getDeprecatedLoggerForName(name)); } - private static Logger deprecatedLoggerName(String name) { + private static Logger getDeprecatedLoggerForName(String name) { if (name.startsWith("org.elasticsearch")) { name = name.replace("org.elasticsearch.", "org.elasticsearch.deprecation."); } else { @@ -80,22 +81,6 @@ private static String toLoggerName(final Class cls) { return canonicalName != null ? canonicalName : cls.getName(); } - public static void setThreadContext(ThreadContext threadContext) { - HeaderWarning.setThreadContext(threadContext); - } - - public static void removeThreadContext(ThreadContext threadContext) { - HeaderWarning.removeThreadContext(threadContext); - } - - public static void addHandler(DeprecatedLogHandler handler) { - additionalHandlers.add(handler); - } - - public static void removeHandler(DeprecatedLogHandler handler) { - additionalHandlers.remove(handler); - } - /** * Logs a deprecation message, adding a formatted warning message as a response header on the thread context. * The deprecation message will be throttled to deprecation log. @@ -108,30 +93,11 @@ public DeprecationLoggerBuilder deprecate(final String key, final String msg, fi public class DeprecationLoggerBuilder { public DeprecationLoggerBuilder withDeprecation(String key, String msg, Object[] params) { - String opaqueId = HeaderWarning.getXOpaqueId(); - ESLogMessage deprecationMessage = DeprecatedMessage.of(opaqueId, msg, params); - - // This is never rate-limited - we always add deprecation messages to response headers. - addHeaderWarning(deprecationMessage); + ESLogMessage deprecationMessage = DeprecatedMessage.of(HeaderWarning.getXOpaqueId(), msg, params).field("x-key", key); - rateLimiter.limit(opaqueId + key, () -> { - warningLogger.log(deprecationMessage); + logger.log(DEPRECATION, deprecationMessage); - for (DeprecatedLogHandler handler : additionalHandlers) { - handler.log(key, deprecationMessage); - } - }); return this; } - - /** - * Adds a formatted warning message as a response header on the thread context. - */ - private void addHeaderWarning(ESLogMessage deprecationMessage) { - String messagePattern = deprecationMessage.getMessagePattern(); - Object[] arguments = deprecationMessage.getArguments(); - - HeaderWarning.addWarning(messagePattern, arguments); - } } } diff --git a/server/src/main/java/org/elasticsearch/common/logging/RateLimiter.java b/server/src/main/java/org/elasticsearch/common/logging/RateLimiter.java index b4a39068f85b3..144e6cce57163 100644 --- a/server/src/main/java/org/elasticsearch/common/logging/RateLimiter.java +++ b/server/src/main/java/org/elasticsearch/common/logging/RateLimiter.java @@ -32,7 +32,7 @@ * When a {@code key} is seen for the first time, the {@code runnable} will be executed, but then will not be * executed again for that key until the key is removed from the set. */ -class RateLimiter { +public class RateLimiter { // LRU set of keys used to determine if a message should be emitted to the logs private final Set keys = Collections.newSetFromMap(Collections.synchronizedMap(new LinkedHashMap<>() { @@ -42,7 +42,7 @@ protected boolean removeEldestEntry(final Map.Entry eldest) { } })); - void limit(String key, Runnable runnable) { + public void limit(String key, Runnable runnable) { boolean shouldRun = keys.add(key); if (shouldRun) { runnable.run(); diff --git a/server/src/main/java/org/elasticsearch/common/logging/WarningLogger.java b/server/src/main/java/org/elasticsearch/common/logging/WarningLogger.java deleted file mode 100644 index dd560d561ca42..0000000000000 --- a/server/src/main/java/org/elasticsearch/common/logging/WarningLogger.java +++ /dev/null @@ -1,50 +0,0 @@ -/* - * Licensed to Elasticsearch under one or more contributor - * license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright - * ownership. Elasticsearch 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. - */ - -package org.elasticsearch.common.logging; - -import org.apache.logging.log4j.Logger; -import org.elasticsearch.common.SuppressLoggerChecks; - -import java.security.AccessController; -import java.security.PrivilegedAction; - -/** - * This class writes deprecation messages to a specified logger at WARN level. - *

- * TODO wrapping logging this way limits the usage of %location. It will think this is used from that class. - */ -class WarningLogger { - private final Logger logger; - - WarningLogger(Logger logger) { - this.logger = logger; - } - - public void log(ESLogMessage message) { - AccessController.doPrivileged(new PrivilegedAction() { - @SuppressLoggerChecks(reason = "safely delegates to logger") - @Override - public Void run() { - logger.warn(message); - return null; - } - }); - } -} diff --git a/x-pack/plugin/deprecation/src/main/java/org/elasticsearch/xpack/deprecation/Deprecation.java b/x-pack/plugin/deprecation/src/main/java/org/elasticsearch/xpack/deprecation/Deprecation.java index 7c16ccfe9e292..d518ca62bf65c 100644 --- a/x-pack/plugin/deprecation/src/main/java/org/elasticsearch/xpack/deprecation/Deprecation.java +++ b/x-pack/plugin/deprecation/src/main/java/org/elasticsearch/xpack/deprecation/Deprecation.java @@ -5,16 +5,9 @@ */ package org.elasticsearch.xpack.deprecation; -import org.apache.logging.log4j.LogManager; -import org.apache.logging.log4j.Logger; import org.elasticsearch.action.ActionRequest; import org.elasticsearch.action.ActionResponse; -import org.elasticsearch.action.bulk.BulkProcessor; -import org.elasticsearch.action.bulk.BulkRequest; -import org.elasticsearch.action.bulk.BulkResponse; -import org.elasticsearch.action.index.IndexRequest; import org.elasticsearch.client.Client; -import org.elasticsearch.client.OriginSettingClient; import org.elasticsearch.cluster.metadata.IndexNameExpressionResolver; import org.elasticsearch.cluster.node.DiscoveryNodes; import org.elasticsearch.cluster.service.ClusterService; @@ -24,7 +17,6 @@ import org.elasticsearch.common.settings.Setting; import org.elasticsearch.common.settings.Settings; import org.elasticsearch.common.settings.SettingsFilter; -import org.elasticsearch.common.unit.TimeValue; import org.elasticsearch.common.xcontent.NamedXContentRegistry; import org.elasticsearch.env.Environment; import org.elasticsearch.env.NodeEnvironment; @@ -36,25 +28,21 @@ import org.elasticsearch.script.ScriptService; import org.elasticsearch.threadpool.ThreadPool; import org.elasticsearch.watcher.ResourceWatcherService; -import org.elasticsearch.xpack.core.ClientHelper; import org.elasticsearch.xpack.core.deprecation.DeprecationInfoAction; import org.elasticsearch.xpack.core.deprecation.NodesDeprecationCheckAction; +import org.elasticsearch.xpack.deprecation.logging.DeprecationIndexingComponent; import java.util.Collection; -import java.util.Collections; import java.util.List; -import java.util.function.Consumer; import java.util.function.Supplier; -import static org.elasticsearch.xpack.deprecation.DeprecationIndexingService.WRITE_DEPRECATION_LOGS_TO_INDEX; +import static org.elasticsearch.xpack.deprecation.logging.DeprecationIndexingComponent.WRITE_DEPRECATION_LOGS_TO_INDEX; /** * The plugin class for the Deprecation API */ public class Deprecation extends Plugin implements ActionPlugin { - private static final Logger logger = LogManager.getLogger(Deprecation.class); - @Override public List> getActions() { return List.of( @@ -73,8 +61,7 @@ public List getRestHandlers( IndexNameExpressionResolver indexNameExpressionResolver, Supplier nodesInCluster ) { - - return Collections.singletonList(new RestDeprecationInfoAction()); + return List.of(new RestDeprecationInfoAction()); } @Override @@ -91,59 +78,13 @@ public Collection createComponents( IndexNameExpressionResolver indexNameExpressionResolver, Supplier repositoriesServiceSupplier ) { - DeprecationIndexingService service = new DeprecationIndexingService(clusterService, buildIndexRequestConsumer(threadPool, client)); + DeprecationIndexingComponent component = new DeprecationIndexingComponent(clusterService, threadPool, client); - return List.of(service); + return List.of(component); } @Override public List> getSettings() { return List.of(WRITE_DEPRECATION_LOGS_TO_INDEX); } - - /** - * Constructs a {@link Consumer} that knows what to do with the {@link IndexRequest} instances that the - * {@link DeprecationIndexingService} creates. This logic is separated from the service in order to make - * testing significantly easier, and to separate concerns. - *

- * Writes are done via {@link BulkProcessor}, which handles batching up writes and retries. - * - * @param threadPool due to #50440, - * extra care must be taken to avoid blocking the thread that writes a deprecation message. - * @param client the client to pass to {@link BulkProcessor} - * @return a consumer that accepts an index request and handles all the details of writing it - * into the cluster - */ - private Consumer buildIndexRequestConsumer(ThreadPool threadPool, Client client) { - final OriginSettingClient originSettingClient = new OriginSettingClient(client, ClientHelper.DEPRECATION_ORIGIN); - - final BulkProcessor.Listener listener = new BulkProcessor.Listener() { - @Override - public void beforeBulk(long executionId, BulkRequest request) {} - - @Override - public void afterBulk(long executionId, BulkRequest request, BulkResponse response) {} - - @Override - public void afterBulk(long executionId, BulkRequest request, Throwable failure) { - logger.error("Bulk write of deprecation logs failed: " + failure.getMessage(), failure); - } - }; - - BulkProcessor processor = BulkProcessor.builder(originSettingClient::bulk, listener) - .setBulkActions(100) - .setFlushInterval(TimeValue.timeValueSeconds(5)) - .build(); - - return indexRequest -> { - try { - // TODO: remove the threadpool wrapping when the .add call is non-blocking - // (it can currently execute the bulk request occasionally) - // see: https://github.com/elastic/elasticsearch/issues/50440 - threadPool.executor(ThreadPool.Names.GENERIC).execute(() -> processor.add(indexRequest)); - } catch (Exception e) { - logger.error("Failed to queue deprecation message index request: " + e.getMessage(), e); - } - }; - } } diff --git a/x-pack/plugin/deprecation/src/main/java/org/elasticsearch/xpack/deprecation/DeprecationIndexingService.java b/x-pack/plugin/deprecation/src/main/java/org/elasticsearch/xpack/deprecation/DeprecationIndexingService.java deleted file mode 100644 index 7414d4718b04b..0000000000000 --- a/x-pack/plugin/deprecation/src/main/java/org/elasticsearch/xpack/deprecation/DeprecationIndexingService.java +++ /dev/null @@ -1,120 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License; - * you may not use this file except in compliance with the Elastic License. - */ - -package org.elasticsearch.xpack.deprecation; - -import org.elasticsearch.action.DocWriteRequest; -import org.elasticsearch.action.index.IndexRequest; -import org.elasticsearch.cluster.ClusterChangedEvent; -import org.elasticsearch.cluster.ClusterState; -import org.elasticsearch.cluster.ClusterStateListener; -import org.elasticsearch.cluster.service.ClusterService; -import org.elasticsearch.common.component.AbstractLifecycleComponent; -import org.elasticsearch.common.logging.DeprecatedLogHandler; -import org.elasticsearch.common.logging.DeprecationLogger; -import org.elasticsearch.common.logging.ESLogMessage; -import org.elasticsearch.common.logging.LoggerMessageFormat; -import org.elasticsearch.common.settings.Setting; - -import java.time.Instant; -import java.util.HashMap; -import java.util.Map; -import java.util.function.Consumer; - -import static org.elasticsearch.common.Strings.isNullOrEmpty; -import static org.elasticsearch.common.logging.DeprecatedMessage.X_OPAQUE_ID_FIELD_NAME; - -/** - * This service is responsible for writing deprecation messages to a data stream. - * The writing of messages can be toggled using the - * {@link #WRITE_DEPRECATION_LOGS_TO_INDEX} setting. - */ -public class DeprecationIndexingService extends AbstractLifecycleComponent implements ClusterStateListener, DeprecatedLogHandler { - private static final String DATA_STREAM_NAME = "logs-deprecation-elasticsearch"; - - public static final Setting WRITE_DEPRECATION_LOGS_TO_INDEX = Setting.boolSetting( - "cluster.deprecation_indexing.enabled", - false, - Setting.Property.NodeScope, - Setting.Property.Dynamic - ); - - private final Consumer requestConsumer; - private String clusterUUID; - private String nodeId; - - private volatile boolean isEnabled = false; - - public DeprecationIndexingService(ClusterService clusterService, Consumer requestConsumer) { - this.requestConsumer = requestConsumer; - clusterService.addListener(this); - } - - /** - * Indexes a deprecation message. - * @param key the key that was used to determine if this deprecation should have been be logged. - * Useful when aggregating the recorded messages. - * @param esLogMessage the message to log - */ - public void log(String key, ESLogMessage esLogMessage) { - if (this.lifecycle.started() == false) { - return; - } - - if (this.isEnabled == false) { - return; - } - - String messagePattern = esLogMessage.getMessagePattern(); - Object[] arguments = esLogMessage.getArguments(); - String message = LoggerMessageFormat.format(messagePattern, arguments); - - Map payload = new HashMap<>(); - payload.put("@timestamp", Instant.now().toString()); - payload.put("key", key); - payload.put("message", message); - payload.put("cluster.uuid", clusterUUID); - payload.put("node.id", nodeId); - - String xOpaqueId = esLogMessage.get(X_OPAQUE_ID_FIELD_NAME); - - if (isNullOrEmpty(xOpaqueId) == false) { - payload.put("x-opaque-id", xOpaqueId); - } - - final IndexRequest request = new IndexRequest(DATA_STREAM_NAME).source(payload).opType(DocWriteRequest.OpType.CREATE); - - this.requestConsumer.accept(request); - } - - /** - * Listens for changes to the cluster state, in order to know whether to toggle indexing - * and to set the cluster UUID and node ID. These can't be set in the constructor because - * the initial cluster state won't be set yet. - */ - @Override - public void clusterChanged(ClusterChangedEvent event) { - final ClusterState state = event.state(); - this.isEnabled = WRITE_DEPRECATION_LOGS_TO_INDEX.get(state.getMetadata().settings()); - this.clusterUUID = state.getMetadata().clusterUUID(); - this.nodeId = state.nodes().getLocalNodeId(); - } - - @Override - protected void doStart() { - DeprecationLogger.addHandler(this); - } - - @Override - protected void doStop() { - DeprecationLogger.removeHandler(this); - } - - @Override - protected void doClose() { - DeprecationLogger.removeHandler(this); - } -} diff --git a/x-pack/plugin/deprecation/src/main/java/org/elasticsearch/xpack/deprecation/logging/DeprecationIndexingAppender.java b/x-pack/plugin/deprecation/src/main/java/org/elasticsearch/xpack/deprecation/logging/DeprecationIndexingAppender.java new file mode 100644 index 0000000000000..e2aa034254128 --- /dev/null +++ b/x-pack/plugin/deprecation/src/main/java/org/elasticsearch/xpack/deprecation/logging/DeprecationIndexingAppender.java @@ -0,0 +1,99 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +package org.elasticsearch.xpack.deprecation.logging; + +import org.apache.logging.log4j.core.Filter; +import org.apache.logging.log4j.core.LogEvent; +import org.apache.logging.log4j.core.appender.AbstractAppender; +import org.apache.logging.log4j.message.Message; +import org.elasticsearch.action.DocWriteRequest; +import org.elasticsearch.action.index.IndexRequest; +import org.elasticsearch.common.logging.ESLogMessage; +import org.elasticsearch.common.logging.LoggerMessageFormat; +import org.elasticsearch.common.logging.RateLimiter; + +import java.time.Instant; +import java.util.HashMap; +import java.util.Map; +import java.util.function.Consumer; + +import static org.elasticsearch.common.Strings.isNullOrEmpty; +import static org.elasticsearch.common.logging.DeprecatedMessage.X_OPAQUE_ID_FIELD_NAME; + +public class DeprecationIndexingAppender extends AbstractAppender { + private static final String DATA_STREAM_NAME = "logs-deprecation-elasticsearch"; + + private final Consumer requestConsumer; + private final RateLimiter rateLimiter; + private String clusterUUID; + private String nodeId; + + private volatile boolean isEnabled = false; + + public DeprecationIndexingAppender( + Consumer requestConsumer, + String name, + Filter filter + ) { + super(name, filter, null); + this.requestConsumer = requestConsumer; + this.rateLimiter = new RateLimiter(); + } + + /** + * Indexes a deprecation message. + */ + @Override + public void append(LogEvent event) { + if (this.isEnabled == false) { + return; + } + + final Message message = event.getMessage(); + if ((message instanceof ESLogMessage) == false) { + return; + } + + final ESLogMessage esLogMessage = (ESLogMessage) message; + + String xOpaqueId = esLogMessage.get(X_OPAQUE_ID_FIELD_NAME); + final String key = esLogMessage.get("x-key"); + + this.rateLimiter.limit(xOpaqueId + key, () -> { + String messagePattern = esLogMessage.getMessagePattern(); + Object[] arguments = esLogMessage.getArguments(); + String formattedMessage = LoggerMessageFormat.format(messagePattern, arguments); + + Map payload = new HashMap<>(); + payload.put("@timestamp", Instant.now().toString()); + payload.put("key", key); + payload.put("message", formattedMessage); + payload.put("cluster.uuid", clusterUUID); + payload.put("node.id", nodeId); + + if (isNullOrEmpty(xOpaqueId) == false) { + payload.put("x-opaque-id", xOpaqueId); + } + + final IndexRequest request = new IndexRequest(DATA_STREAM_NAME).source(payload).opType(DocWriteRequest.OpType.CREATE); + + this.requestConsumer.accept(request); + }); + } + + public void setEnabled(boolean isEnabled) { + this.isEnabled = isEnabled; + } + + public void setClusterUUID(String clusterUUID) { + this.clusterUUID = clusterUUID; + } + + public void setNodeId(String nodeId) { + this.nodeId = nodeId; + } +} diff --git a/x-pack/plugin/deprecation/src/main/java/org/elasticsearch/xpack/deprecation/logging/DeprecationIndexingComponent.java b/x-pack/plugin/deprecation/src/main/java/org/elasticsearch/xpack/deprecation/logging/DeprecationIndexingComponent.java new file mode 100644 index 0000000000000..283794ac33bee --- /dev/null +++ b/x-pack/plugin/deprecation/src/main/java/org/elasticsearch/xpack/deprecation/logging/DeprecationIndexingComponent.java @@ -0,0 +1,146 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +/* + * Licensed to Elasticsearch under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch 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. + */ + +package org.elasticsearch.xpack.deprecation.logging; + +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; +import org.elasticsearch.action.bulk.BulkProcessor; +import org.elasticsearch.action.bulk.BulkRequest; +import org.elasticsearch.action.bulk.BulkResponse; +import org.elasticsearch.action.index.IndexRequest; +import org.elasticsearch.client.Client; +import org.elasticsearch.client.OriginSettingClient; +import org.elasticsearch.cluster.ClusterChangedEvent; +import org.elasticsearch.cluster.ClusterState; +import org.elasticsearch.cluster.ClusterStateListener; +import org.elasticsearch.cluster.service.ClusterService; +import org.elasticsearch.common.component.AbstractLifecycleComponent; +import org.elasticsearch.common.logging.Loggers; +import org.elasticsearch.common.settings.Setting; +import org.elasticsearch.common.unit.TimeValue; +import org.elasticsearch.threadpool.ThreadPool; +import org.elasticsearch.xpack.core.ClientHelper; + +import java.util.function.Consumer; + +import static org.elasticsearch.common.logging.DeprecationLogger.DEPRECATION_ONLY; + +public class DeprecationIndexingComponent extends AbstractLifecycleComponent implements ClusterStateListener { + private static final Logger logger = LogManager.getLogger(DeprecationIndexingComponent.class); + + public static final Setting WRITE_DEPRECATION_LOGS_TO_INDEX = Setting.boolSetting( + "cluster.deprecation_indexing.enabled", + false, + Setting.Property.NodeScope, + Setting.Property.Dynamic + ); + + private final DeprecationIndexingAppender appender; + + public DeprecationIndexingComponent(ClusterService clusterService, ThreadPool threadPool, Client client) { + this.appender = new DeprecationIndexingAppender( + buildIndexRequestConsumer(threadPool, client), + "DeprecationIndexer", + DEPRECATION_ONLY + ); + + clusterService.addListener(this); + } + + @Override + protected void doStart() { + Loggers.addAppender(LogManager.getLogger("org.elasticsearch.deprecation"), this.appender); + } + + @Override + protected void doStop() { + Loggers.addAppender(LogManager.getLogger("org.elasticsearch.deprecation"), this.appender); + } + + @Override + protected void doClose() { + // Nothing to do at present + } + + /** + * Listens for changes to the cluster state, in order to know whether to toggle indexing + * and to set the cluster UUID and node ID. These can't be set in the constructor because + * the initial cluster state won't be set yet. + */ + @Override + public void clusterChanged(ClusterChangedEvent event) { + final ClusterState state = event.state(); + appender.setEnabled(WRITE_DEPRECATION_LOGS_TO_INDEX.get(state.getMetadata().settings())); + appender.setClusterUUID(state.getMetadata().clusterUUID()); + appender.setNodeId(state.nodes().getLocalNodeId()); + } + + /** + * Constructs a {@link Consumer} that knows what to do with the {@link IndexRequest} instances that the + * {@link DeprecationIndexingAppender} creates. This logic is separated from the service in order to make + * testing significantly easier, and to separate concerns. + *

+ * Writes are done via {@link BulkProcessor}, which handles batching up writes and retries. + * + * @param threadPool due to #50440, + * extra care must be taken to avoid blocking the thread that writes a deprecation message. + * @param client the client to pass to {@link BulkProcessor} + * @return a consumer that accepts an index request and handles all the details of writing it + * into the cluster + */ + private Consumer buildIndexRequestConsumer(ThreadPool threadPool, Client client) { + final OriginSettingClient originSettingClient = new OriginSettingClient(client, ClientHelper.DEPRECATION_ORIGIN); + + final BulkProcessor.Listener listener = new BulkProcessor.Listener() { + @Override + public void beforeBulk(long executionId, BulkRequest request) {} + + @Override + public void afterBulk(long executionId, BulkRequest request, BulkResponse response) {} + + @Override + public void afterBulk(long executionId, BulkRequest request, Throwable failure) { + logger.error("Bulk write of deprecation logs failed: " + failure.getMessage(), failure); + } + }; + + BulkProcessor processor = BulkProcessor.builder(originSettingClient::bulk, listener) + .setBulkActions(100) + .setFlushInterval(TimeValue.timeValueSeconds(5)) + .build(); + + return indexRequest -> { + try { + // TODO: remove the threadpool wrapping when the .add call is non-blocking + // (it can currently execute the bulk request occasionally) + // see: https://github.com/elastic/elasticsearch/issues/50440 + threadPool.executor(ThreadPool.Names.GENERIC).execute(() -> processor.add(indexRequest)); + } catch (Exception e) { + logger.error("Failed to queue deprecation message index request: " + e.getMessage(), e); + } + }; + } +} diff --git a/x-pack/plugin/deprecation/src/test/java/org/elasticsearch/xpack/deprecation/DeprecationIndexingServiceTests.java b/x-pack/plugin/deprecation/src/test/java/org/elasticsearch/xpack/deprecation/DeprecationIndexingAppenderTests.java similarity index 58% rename from x-pack/plugin/deprecation/src/test/java/org/elasticsearch/xpack/deprecation/DeprecationIndexingServiceTests.java rename to x-pack/plugin/deprecation/src/test/java/org/elasticsearch/xpack/deprecation/DeprecationIndexingAppenderTests.java index fce7fe3bdd389..268b84573524b 100644 --- a/x-pack/plugin/deprecation/src/test/java/org/elasticsearch/xpack/deprecation/DeprecationIndexingServiceTests.java +++ b/x-pack/plugin/deprecation/src/test/java/org/elasticsearch/xpack/deprecation/DeprecationIndexingAppenderTests.java @@ -6,24 +6,18 @@ package org.elasticsearch.xpack.deprecation; +import org.apache.logging.log4j.core.LogEvent; import org.elasticsearch.action.index.IndexRequest; -import org.elasticsearch.cluster.ClusterChangedEvent; -import org.elasticsearch.cluster.ClusterName; -import org.elasticsearch.cluster.ClusterState; -import org.elasticsearch.cluster.metadata.Metadata; -import org.elasticsearch.cluster.node.DiscoveryNodes; -import org.elasticsearch.cluster.service.ClusterService; import org.elasticsearch.common.logging.DeprecatedMessage; import org.elasticsearch.common.logging.ESLogMessage; -import org.elasticsearch.common.settings.Settings; import org.elasticsearch.test.ESTestCase; +import org.elasticsearch.xpack.deprecation.logging.DeprecationIndexingAppender; import org.junit.Before; import org.mockito.ArgumentCaptor; import java.util.Map; import java.util.function.Consumer; -import static org.elasticsearch.xpack.deprecation.DeprecationIndexingService.WRITE_DEPRECATION_LOGS_TO_INDEX; import static org.hamcrest.Matchers.hasEntry; import static org.hamcrest.Matchers.hasKey; import static org.hamcrest.Matchers.not; @@ -31,37 +25,21 @@ import static org.mockito.Mockito.mock; import static org.mockito.Mockito.never; import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; -public class DeprecationIndexingServiceTests extends ESTestCase { +public class DeprecationIndexingAppenderTests extends ESTestCase { - private DeprecationIndexingService service; - private ClusterService clusterService; + private DeprecationIndexingAppender appender; private Consumer consumer; @Before @SuppressWarnings("unchecked") public void initialize() { consumer = mock(Consumer.class); - clusterService = mock(ClusterService.class); - service = new DeprecationIndexingService(clusterService, consumer); - } - - /** - * Checks that the service registers a cluster state listener, so that the service - * can be enabled and disabled. - */ - public void testClusterStateListenerRegistered() { - verify(clusterService).addListener(service); - } + appender = new DeprecationIndexingAppender(consumer, "a name", null); - /** - * Checks that the service does not attempt to index messages when it had not reach the - * "started" lifecycle state. - */ - public void testDoesNotWriteMessageWhenServiceNotStarted() { - service.log("a key", DeprecatedMessage.of(null, "a message")); - - verify(consumer, never()).accept(any()); + appender.setClusterUUID("cluster-uuid"); + appender.setNodeId("local-node-id"); } /** @@ -69,9 +47,7 @@ public void testDoesNotWriteMessageWhenServiceNotStarted() { * is disabled. */ public void testDoesNotWriteMessageWhenServiceDisabled() { - service.start(); - - service.log("a key", DeprecatedMessage.of(null, "a message")); + appender.append(buildEvent()); verify(consumer, never()).accept(any()); } @@ -80,11 +56,10 @@ public void testDoesNotWriteMessageWhenServiceDisabled() { * Checks that the service can be disabled after being enabled. */ public void testDoesNotWriteMessageWhenServiceEnabledAndDisabled() { - service.start(); - service.clusterChanged(getEvent(true)); - service.clusterChanged(getEvent(false)); + appender.setEnabled(true); + appender.setEnabled(false); - service.log("a key", DeprecatedMessage.of(null, "a message")); + appender.append(buildEvent()); verify(consumer, never()).accept(any()); } @@ -93,8 +68,7 @@ public void testDoesNotWriteMessageWhenServiceEnabledAndDisabled() { * Checks that messages are indexed in the correct shape when the service is enabled. */ public void testWritesMessageWhenServiceEnabled() { - service.start(); - service.clusterChanged(getEvent(true)); + appender.setEnabled(true); final Map payloadMap = getWriteRequest("a key", DeprecatedMessage.of(null, "a message")); @@ -111,8 +85,7 @@ public void testWritesMessageWhenServiceEnabled() { * Check that if an xOpaqueId is set, then it is added to the index request payload. */ public void testMessageIncludesOpaqueIdWhenSupplied() { - service.start(); - service.clusterChanged(getEvent(true)); + appender.setEnabled(true); final Map payloadMap = getWriteRequest("a key", DeprecatedMessage.of("an ID", "a message")); @@ -123,8 +96,7 @@ public void testMessageIncludesOpaqueIdWhenSupplied() { * Check that if any arguments are set, then they substituted in the log message */ public void testMessageSubstitutesArgumentsWhenSupplied() { - service.start(); - service.clusterChanged(getEvent(true)); + appender.setEnabled(true); final Map payloadMap = getWriteRequest( "a key", @@ -134,21 +106,15 @@ public void testMessageSubstitutesArgumentsWhenSupplied() { assertThat(payloadMap, hasEntry("message", "a first and second message")); } - private ClusterChangedEvent getEvent(boolean shouldWriteDeprecationLogs) { - Settings settings = Settings.builder().put(WRITE_DEPRECATION_LOGS_TO_INDEX.getKey(), shouldWriteDeprecationLogs).build(); - final Metadata metadata = Metadata.builder().clusterUUID("cluster-uuid").transientSettings(settings).build(); - final DiscoveryNodes nodes = DiscoveryNodes.builder().localNodeId("local-node-id").build(); - - final ClusterState clusterState = ClusterState.builder(new ClusterName("test")).metadata(metadata).nodes(nodes).build(); - - return new ClusterChangedEvent("test", clusterState, clusterState); - } - /* * Wraps up the steps for extracting an index request payload from the mocks. */ private Map getWriteRequest(String key, ESLogMessage message) { - service.log(key, message); + message.field("x-key", key); + LogEvent logEvent = mock(LogEvent.class); + when(logEvent.getMessage()).thenReturn(message); + + appender.append(logEvent); ArgumentCaptor argument = ArgumentCaptor.forClass(IndexRequest.class); @@ -157,4 +123,10 @@ private Map getWriteRequest(String key, ESLogMessage message) { final IndexRequest indexRequest = argument.getValue(); return indexRequest.sourceAsMap(); } + + private LogEvent buildEvent() { + LogEvent logEvent = mock(LogEvent.class); + when(logEvent.getMessage()).thenReturn(DeprecatedMessage.of(null, "a message").field("x-key", "a key")); + return logEvent; + } } From d33b72b8eeeac90bf8695b58d00290aa48c3bdea Mon Sep 17 00:00:00 2001 From: Rory Hunter Date: Fri, 17 Jul 2020 17:28:30 +0100 Subject: [PATCH 22/48] Reimplement deprecation logging using log4j --- distribution/src/config/log4j2.properties | 1 + modules/deprecation-logger/build.gradle | 2 +- .../deprecation/DeprecationComponent.java | 8 +- .../common/logging/DeprecationLogger.java | 6 +- .../common/logging/RateLimiter.java | 51 --- .../common/logging/RateLimitingFilter.java | 90 +++++ .../common/logging/UnionFilter.java | 346 ++++++++++++++++++ .../common/logging/RateLimiterTests.java | 81 ---- .../xpack/deprecation/Deprecation.java | 4 +- .../logging/DeprecationIndexingAppender.java | 35 +- .../logging/DeprecationIndexingComponent.java | 21 +- .../DeprecationIndexingAppenderTests.java | 4 +- 12 files changed, 478 insertions(+), 171 deletions(-) delete mode 100644 server/src/main/java/org/elasticsearch/common/logging/RateLimiter.java create mode 100644 server/src/main/java/org/elasticsearch/common/logging/RateLimitingFilter.java create mode 100644 server/src/main/java/org/elasticsearch/common/logging/UnionFilter.java delete mode 100644 server/src/test/java/org/elasticsearch/common/logging/RateLimiterTests.java diff --git a/distribution/src/config/log4j2.properties b/distribution/src/config/log4j2.properties index 0dfd19a2a8c20..d7b980f6ade40 100644 --- a/distribution/src/config/log4j2.properties +++ b/distribution/src/config/log4j2.properties @@ -63,6 +63,7 @@ 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 = ECSJsonLayout appender.deprecation_rolling.layout.type_name = deprecation +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 diff --git a/modules/deprecation-logger/build.gradle b/modules/deprecation-logger/build.gradle index 9e39ca7bfa87d..cee4f0429c2d5 100644 --- a/modules/deprecation-logger/build.gradle +++ b/modules/deprecation-logger/build.gradle @@ -19,5 +19,5 @@ esplugin { description 'Plugin for logging deprecation messages' - classname 'org.elasticsearch.kibana.DeprecationPlugin' + classname 'org.elasticsearch.deprecation.DeprecationPlugin' } diff --git a/modules/deprecation-logger/src/main/java/org/elasticsearch/deprecation/DeprecationComponent.java b/modules/deprecation-logger/src/main/java/org/elasticsearch/deprecation/DeprecationComponent.java index d110063f507e5..26f178edb4b3e 100644 --- a/modules/deprecation-logger/src/main/java/org/elasticsearch/deprecation/DeprecationComponent.java +++ b/modules/deprecation-logger/src/main/java/org/elasticsearch/deprecation/DeprecationComponent.java @@ -20,16 +20,18 @@ package org.elasticsearch.deprecation; import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.core.Appender; import org.elasticsearch.common.component.AbstractLifecycleComponent; import org.elasticsearch.common.logging.Loggers; -import static org.elasticsearch.common.logging.DeprecationLogger.DEPRECATION_ONLY; +import static org.elasticsearch.common.logging.DeprecationLogger.DEPRECATION_ONLY_FILTER; public class DeprecationComponent extends AbstractLifecycleComponent { - private final HeaderWarningAppender appender; + private final Appender appender; public DeprecationComponent() { - this.appender = new HeaderWarningAppender("HeaderWarning", DEPRECATION_ONLY); + this.appender = new HeaderWarningAppender("HeaderWarning", DEPRECATION_ONLY_FILTER); + this.appender.start(); } @Override diff --git a/server/src/main/java/org/elasticsearch/common/logging/DeprecationLogger.java b/server/src/main/java/org/elasticsearch/common/logging/DeprecationLogger.java index 99af0529e9236..24516dfed0e6e 100644 --- a/server/src/main/java/org/elasticsearch/common/logging/DeprecationLogger.java +++ b/server/src/main/java/org/elasticsearch/common/logging/DeprecationLogger.java @@ -40,7 +40,7 @@ public class DeprecationLogger { public static Level DEPRECATION = Level.forName("DEPRECATION", Level.WARN.intLevel() + 1); // Only handle log events with the custom DEPRECATION level - public static final LevelRangeFilter DEPRECATION_ONLY = LevelRangeFilter.createFilter( + public static final LevelRangeFilter DEPRECATION_ONLY_FILTER = LevelRangeFilter.createFilter( DEPRECATION, DEPRECATION, Filter.Result.ACCEPT, @@ -55,7 +55,7 @@ public class DeprecationLogger { * it replaces "org.elasticsearch" with "org.elasticsearch.deprecation" to maintain * the "org.elasticsearch" namespace. */ - public DeprecationLogger(Logger parentLogger) { + private DeprecationLogger(Logger parentLogger) { this.logger = parentLogger; } @@ -93,7 +93,7 @@ public DeprecationLoggerBuilder deprecate(final String key, final String msg, fi public class DeprecationLoggerBuilder { public DeprecationLoggerBuilder withDeprecation(String key, String msg, Object[] params) { - ESLogMessage deprecationMessage = DeprecatedMessage.of(HeaderWarning.getXOpaqueId(), msg, params).field("x-key", key); + ESLogMessage deprecationMessage = DeprecatedMessage.of(HeaderWarning.getXOpaqueId(), msg, params).field("key", key); logger.log(DEPRECATION, deprecationMessage); diff --git a/server/src/main/java/org/elasticsearch/common/logging/RateLimiter.java b/server/src/main/java/org/elasticsearch/common/logging/RateLimiter.java deleted file mode 100644 index 144e6cce57163..0000000000000 --- a/server/src/main/java/org/elasticsearch/common/logging/RateLimiter.java +++ /dev/null @@ -1,51 +0,0 @@ -/* - * Licensed to Elasticsearch under one or more contributor - * license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright - * ownership. Elasticsearch 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. - */ - -package org.elasticsearch.common.logging; - -import java.util.Collections; -import java.util.LinkedHashMap; -import java.util.Map; -import java.util.Set; - -/** - *

- * This class limits the number of times that actions are carried out. - *

- * The throttling algorithm relies on a LRU set of keys, which evicts entries when its size exceeds 128. - * When a {@code key} is seen for the first time, the {@code runnable} will be executed, but then will not be - * executed again for that key until the key is removed from the set. - */ -public class RateLimiter { - - // LRU set of keys used to determine if a message should be emitted to the logs - private final Set keys = Collections.newSetFromMap(Collections.synchronizedMap(new LinkedHashMap<>() { - @Override - protected boolean removeEldestEntry(final Map.Entry eldest) { - return size() > 128; - } - })); - - public void limit(String key, Runnable runnable) { - boolean shouldRun = keys.add(key); - if (shouldRun) { - runnable.run(); - } - } -} diff --git a/server/src/main/java/org/elasticsearch/common/logging/RateLimitingFilter.java b/server/src/main/java/org/elasticsearch/common/logging/RateLimitingFilter.java new file mode 100644 index 0000000000000..66dd3448f8003 --- /dev/null +++ b/server/src/main/java/org/elasticsearch/common/logging/RateLimitingFilter.java @@ -0,0 +1,90 @@ +/* + * Licensed to Elasticsearch under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch 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. + */ + +package org.elasticsearch.common.logging; + +import org.apache.logging.log4j.Level; +import org.apache.logging.log4j.Marker; +import org.apache.logging.log4j.core.Filter; +import org.apache.logging.log4j.core.LogEvent; +import org.apache.logging.log4j.core.Logger; +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.PluginFactory; +import org.apache.logging.log4j.core.filter.AbstractFilter; +import org.apache.logging.log4j.message.Message; + +import java.util.Collections; +import java.util.LinkedHashMap; +import java.util.Map; +import java.util.Set; + +import static org.elasticsearch.common.logging.DeprecatedMessage.X_OPAQUE_ID_FIELD_NAME; + +@Plugin(name = "RateLimitingFilter", category = Node.CATEGORY, elementType = Filter.ELEMENT_TYPE) +public class RateLimitingFilter extends AbstractFilter { + + private final Set lruKeyCache = Collections.newSetFromMap(Collections.synchronizedMap(new LinkedHashMap<>() { + @Override + protected boolean removeEldestEntry(final Map.Entry eldest) { + return size() > 128; + } + })); + + public RateLimitingFilter() { + this(Result.ACCEPT, Result.DENY); + } + + public RateLimitingFilter(Result onMatch, Result onMismatch) { + super(onMatch, onMismatch); + } + + public Result filter(Message message) { + if (message instanceof ESLogMessage) { + final ESLogMessage esLogMessage = (ESLogMessage) message; + + String xOpaqueId = esLogMessage.get(X_OPAQUE_ID_FIELD_NAME); + final String key = esLogMessage.get("key"); + + return lruKeyCache.add(xOpaqueId + key) ? Result.ACCEPT : Result.DENY; + + } else { + return Result.NEUTRAL; + } + } + + @Override + public Result filter(LogEvent event) { + return filter(event.getMessage()); + } + + @Override + public Result filter(Logger logger, Level level, Marker marker, Message msg, Throwable t) { + return filter(msg); + } + + @PluginFactory + public static RateLimitingFilter createFilter( + @PluginAttribute("onMatch") final Result match, + @PluginAttribute("onMismatch") final Result mismatch + ) { + return new RateLimitingFilter(match, mismatch); + } +} diff --git a/server/src/main/java/org/elasticsearch/common/logging/UnionFilter.java b/server/src/main/java/org/elasticsearch/common/logging/UnionFilter.java new file mode 100644 index 0000000000000..0c435770d24b2 --- /dev/null +++ b/server/src/main/java/org/elasticsearch/common/logging/UnionFilter.java @@ -0,0 +1,346 @@ +/* + * 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. + */ + +package org.elasticsearch.common.logging; + +import org.apache.logging.log4j.Level; +import org.apache.logging.log4j.Marker; +import org.apache.logging.log4j.core.Filter; +import org.apache.logging.log4j.core.LifeCycle2; +import org.apache.logging.log4j.core.LogEvent; +import org.apache.logging.log4j.core.Logger; +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.PluginElement; +import org.apache.logging.log4j.core.config.plugins.PluginFactory; +import org.apache.logging.log4j.core.filter.AbstractFilter; +import org.apache.logging.log4j.message.Message; +import org.apache.logging.log4j.util.PerformanceSensitive; + +import java.util.Objects; +import java.util.concurrent.TimeUnit; + +/** + * This is a fork of {@link org.apache.logging.log4j.core.filter.CompositeFilter}. This class + * also composes and invokes one or more filters, but unlike CompositeFilter, all + * composed filters will be called to check for one that answers {@link Result#DENY}. + */ +@Plugin(name = "filters", category = Node.CATEGORY, printObject = true) +@PerformanceSensitive("allocation") +public final class UnionFilter extends AbstractFilter { + + private final Filter[] filters; + + private UnionFilter(final Filter[] filters) { + this.filters = Objects.requireNonNull(filters, "filters cannot be null"); + } + + @Override + public void start() { + this.setStarting(); + for (final Filter filter : filters) { + filter.start(); + } + this.setStarted(); + } + + @Override + public boolean stop(final long timeout, final TimeUnit timeUnit) { + this.setStopping(); + for (final Filter filter : filters) { + if (filter instanceof LifeCycle2) { + ((LifeCycle2) filter).stop(timeout, timeUnit); + } else { + filter.stop(); + } + } + setStopped(); + return true; + } + + @Override + public Result filter(final Logger logger, final Level level, final Marker marker, final String msg, final Object... params) { + Result result = Result.NEUTRAL; + for (int i = 0; i < filters.length; i++) { + result = filters[i].filter(logger, level, marker, msg, params); + if (result == Result.DENY) { + return result; + } + } + return result; + } + + @Override + public Result filter(final Logger logger, final Level level, final Marker marker, final String msg, final Object p0) { + Result result = Result.NEUTRAL; + for (int i = 0; i < filters.length; i++) { + result = filters[i].filter(logger, level, marker, msg, p0); + if (result == Result.DENY) { + return result; + } + } + return result; + } + + @Override + public Result filter(final Logger logger, final Level level, final Marker marker, final String msg, final Object p0, final Object p1) { + Result result = Result.NEUTRAL; + for (int i = 0; i < filters.length; i++) { + result = filters[i].filter(logger, level, marker, msg, p0, p1); + if (result == Result.DENY) { + return result; + } + } + return result; + } + + @Override + public Result filter( + final Logger logger, + final Level level, + final Marker marker, + final String msg, + final Object p0, + final Object p1, + final Object p2 + ) { + Result result = Result.NEUTRAL; + for (int i = 0; i < filters.length; i++) { + result = filters[i].filter(logger, level, marker, msg, p0, p1, p2); + if (result == Result.DENY) { + return result; + } + } + return result; + } + + @Override + public Result filter( + final Logger logger, + final Level level, + final Marker marker, + final String msg, + final Object p0, + final Object p1, + final Object p2, + final Object p3 + ) { + Result result = Result.NEUTRAL; + for (int i = 0; i < filters.length; i++) { + result = filters[i].filter(logger, level, marker, msg, p0, p1, p2, p3); + if (result == Result.DENY) { + return result; + } + } + return result; + } + + @Override + public Result filter( + final Logger logger, + final Level level, + final Marker marker, + final String msg, + final Object p0, + final Object p1, + final Object p2, + final Object p3, + final Object p4 + ) { + Result result = Result.NEUTRAL; + for (int i = 0; i < filters.length; i++) { + result = filters[i].filter(logger, level, marker, msg, p0, p1, p2, p3, p4); + if (result == Result.DENY) { + return result; + } + } + return result; + } + + @Override + public Result filter( + final Logger logger, + final Level level, + final Marker marker, + final String msg, + final Object p0, + final Object p1, + final Object p2, + final Object p3, + final Object p4, + final Object p5 + ) { + Result result = Result.NEUTRAL; + for (int i = 0; i < filters.length; i++) { + result = filters[i].filter(logger, level, marker, msg, p0, p1, p2, p3, p4, p5); + if (result == Result.DENY) { + return result; + } + } + return result; + } + + @Override + public Result filter( + final Logger logger, + final Level level, + final Marker marker, + final String msg, + final Object p0, + final Object p1, + final Object p2, + final Object p3, + final Object p4, + final Object p5, + final Object p6 + ) { + Result result = Result.NEUTRAL; + for (int i = 0; i < filters.length; i++) { + result = filters[i].filter(logger, level, marker, msg, p0, p1, p2, p3, p4, p5, p6); + if (result == Result.DENY) { + return result; + } + } + return result; + } + + @Override + public Result filter( + final Logger logger, + final Level level, + final Marker marker, + final String msg, + final Object p0, + final Object p1, + final Object p2, + final Object p3, + final Object p4, + final Object p5, + final Object p6, + final Object p7 + ) { + Result result = Result.NEUTRAL; + for (int i = 0; i < filters.length; i++) { + result = filters[i].filter(logger, level, marker, msg, p0, p1, p2, p3, p4, p5, p6, p7); + if (result == Result.DENY) { + return result; + } + } + return result; + } + + @Override + public Result filter( + final Logger logger, + final Level level, + final Marker marker, + final String msg, + final Object p0, + final Object p1, + final Object p2, + final Object p3, + final Object p4, + final Object p5, + final Object p6, + final Object p7, + final Object p8 + ) { + Result result = Result.NEUTRAL; + for (int i = 0; i < filters.length; i++) { + result = filters[i].filter(logger, level, marker, msg, p0, p1, p2, p3, p4, p5, p6, p7, p8); + if (result == Result.DENY) { + return result; + } + } + return result; + } + + @Override + public Result filter( + final Logger logger, + final Level level, + final Marker marker, + final String msg, + final Object p0, + final Object p1, + final Object p2, + final Object p3, + final Object p4, + final Object p5, + final Object p6, + final Object p7, + final Object p8, + final Object p9 + ) { + Result result = Result.NEUTRAL; + for (int i = 0; i < filters.length; i++) { + result = filters[i].filter(logger, level, marker, msg, p0, p1, p2, p3, p4, p5, p6, p7, p8, p9); + if (result == Result.DENY) { + return result; + } + } + return result; + } + + @Override + public Result filter(final Logger logger, final Level level, final Marker marker, final Object msg, final Throwable t) { + Result result = Result.NEUTRAL; + for (int i = 0; i < filters.length; i++) { + result = filters[i].filter(logger, level, marker, msg, t); + if (result == Result.DENY) { + return result; + } + } + return result; + } + + @Override + public Result filter(final Logger logger, final Level level, final Marker marker, final Message msg, final Throwable t) { + Result result = Result.NEUTRAL; + for (int i = 0; i < filters.length; i++) { + result = filters[i].filter(logger, level, marker, msg, t); + if (result == Result.DENY) { + return result; + } + } + return result; + } + + @Override + public Result filter(final LogEvent event) { + Result result = Result.NEUTRAL; + for (int i = 0; i < filters.length; i++) { + result = filters[i].filter(event); + if (result == Result.DENY) { + return result; + } + } + return result; + } + + /** + * Creates a UnionFilter. + * + * @param filters An array of Filters to call. + * @return The UnionFilter. + */ + @PluginFactory + public static UnionFilter createFilters(@PluginElement("Filters") final Filter... filters) { + return new UnionFilter(filters); + } + +} diff --git a/server/src/test/java/org/elasticsearch/common/logging/RateLimiterTests.java b/server/src/test/java/org/elasticsearch/common/logging/RateLimiterTests.java deleted file mode 100644 index da556367ad501..0000000000000 --- a/server/src/test/java/org/elasticsearch/common/logging/RateLimiterTests.java +++ /dev/null @@ -1,81 +0,0 @@ -/* - * Licensed to Elasticsearch under one or more contributor - * license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright - * ownership. Elasticsearch 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. - */ - -package org.elasticsearch.common.logging; - -import org.elasticsearch.test.ESTestCase; - -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.times; -import static org.mockito.Mockito.verify; - -public class RateLimiterTests extends ESTestCase { - - /** - * Check that the limiter will only execute a runnable once when - * execute twice with the same key. - */ - public void testOnlyRunsOnceForSameKey() { - final RateLimiter rateLimiter = new RateLimiter(); - - Runnable runnable = mock(Runnable.class); - - rateLimiter.limit("a key", runnable); - rateLimiter.limit("a key", runnable); - - verify(runnable, times(1)).run(); - } - - /** - * Check that the limiter will execute a runnable more than once when - * executed with different keys. - */ - public void testRunsMoreThanOnceForDifferentKeys() { - final RateLimiter rateLimiter = new RateLimiter(); - - Runnable runnable = mock(Runnable.class); - - rateLimiter.limit("a key", runnable); - rateLimiter.limit("another key", runnable); - - verify(runnable, times(2)).run(); - } - - /** - * Check that the limiter will execute a runnable again for a given key if that - * key is evicted from the key cache. - */ - public void testKeyCacheEntryEventuallyExpires() { - final RateLimiter rateLimiter = new RateLimiter(); - - Runnable runnable = mock(Runnable.class); - Runnable otherRunnable = mock(Runnable.class); - - rateLimiter.limit("key0", runnable); - - // Fill the key cache so that "key0" is evicted - for (int i = 1; i <= 128; i++) { - rateLimiter.limit("key" + i, otherRunnable); - } - - rateLimiter.limit("key0", runnable); - - verify(runnable, times(2)).run(); - } -} diff --git a/x-pack/plugin/deprecation/src/main/java/org/elasticsearch/xpack/deprecation/Deprecation.java b/x-pack/plugin/deprecation/src/main/java/org/elasticsearch/xpack/deprecation/Deprecation.java index d518ca62bf65c..0d21cf6e175ce 100644 --- a/x-pack/plugin/deprecation/src/main/java/org/elasticsearch/xpack/deprecation/Deprecation.java +++ b/x-pack/plugin/deprecation/src/main/java/org/elasticsearch/xpack/deprecation/Deprecation.java @@ -78,7 +78,9 @@ public Collection createComponents( IndexNameExpressionResolver indexNameExpressionResolver, Supplier repositoriesServiceSupplier ) { - DeprecationIndexingComponent component = new DeprecationIndexingComponent(clusterService, threadPool, client); + DeprecationIndexingComponent component = new DeprecationIndexingComponent(threadPool, client); + + clusterService.addListener(component); return List.of(component); } diff --git a/x-pack/plugin/deprecation/src/main/java/org/elasticsearch/xpack/deprecation/logging/DeprecationIndexingAppender.java b/x-pack/plugin/deprecation/src/main/java/org/elasticsearch/xpack/deprecation/logging/DeprecationIndexingAppender.java index e2aa034254128..2a7072301bc2c 100644 --- a/x-pack/plugin/deprecation/src/main/java/org/elasticsearch/xpack/deprecation/logging/DeprecationIndexingAppender.java +++ b/x-pack/plugin/deprecation/src/main/java/org/elasticsearch/xpack/deprecation/logging/DeprecationIndexingAppender.java @@ -14,7 +14,6 @@ import org.elasticsearch.action.index.IndexRequest; import org.elasticsearch.common.logging.ESLogMessage; import org.elasticsearch.common.logging.LoggerMessageFormat; -import org.elasticsearch.common.logging.RateLimiter; import java.time.Instant; import java.util.HashMap; @@ -28,7 +27,6 @@ public class DeprecationIndexingAppender extends AbstractAppender { private static final String DATA_STREAM_NAME = "logs-deprecation-elasticsearch"; private final Consumer requestConsumer; - private final RateLimiter rateLimiter; private String clusterUUID; private String nodeId; @@ -41,7 +39,6 @@ public DeprecationIndexingAppender( ) { super(name, filter, null); this.requestConsumer = requestConsumer; - this.rateLimiter = new RateLimiter(); } /** @@ -61,28 +58,26 @@ public void append(LogEvent event) { final ESLogMessage esLogMessage = (ESLogMessage) message; String xOpaqueId = esLogMessage.get(X_OPAQUE_ID_FIELD_NAME); - final String key = esLogMessage.get("x-key"); + final String key = esLogMessage.get("key"); - this.rateLimiter.limit(xOpaqueId + key, () -> { - String messagePattern = esLogMessage.getMessagePattern(); - Object[] arguments = esLogMessage.getArguments(); - String formattedMessage = LoggerMessageFormat.format(messagePattern, arguments); + String messagePattern = esLogMessage.getMessagePattern(); + Object[] arguments = esLogMessage.getArguments(); + String formattedMessage = LoggerMessageFormat.format(messagePattern, arguments); - Map payload = new HashMap<>(); - payload.put("@timestamp", Instant.now().toString()); - payload.put("key", key); - payload.put("message", formattedMessage); - payload.put("cluster.uuid", clusterUUID); - payload.put("node.id", nodeId); + Map payload = new HashMap<>(); + payload.put("@timestamp", Instant.now().toString()); + payload.put("key", key); + payload.put("message", formattedMessage); + payload.put("cluster.uuid", clusterUUID); + payload.put("node.id", nodeId); - if (isNullOrEmpty(xOpaqueId) == false) { - payload.put("x-opaque-id", xOpaqueId); - } + if (isNullOrEmpty(xOpaqueId) == false) { + payload.put("x-opaque-id", xOpaqueId); + } - final IndexRequest request = new IndexRequest(DATA_STREAM_NAME).source(payload).opType(DocWriteRequest.OpType.CREATE); + final IndexRequest request = new IndexRequest(DATA_STREAM_NAME).source(payload).opType(DocWriteRequest.OpType.CREATE); - this.requestConsumer.accept(request); - }); + this.requestConsumer.accept(request); } public void setEnabled(boolean isEnabled) { diff --git a/x-pack/plugin/deprecation/src/main/java/org/elasticsearch/xpack/deprecation/logging/DeprecationIndexingComponent.java b/x-pack/plugin/deprecation/src/main/java/org/elasticsearch/xpack/deprecation/logging/DeprecationIndexingComponent.java index 283794ac33bee..78f074808dd98 100644 --- a/x-pack/plugin/deprecation/src/main/java/org/elasticsearch/xpack/deprecation/logging/DeprecationIndexingComponent.java +++ b/x-pack/plugin/deprecation/src/main/java/org/elasticsearch/xpack/deprecation/logging/DeprecationIndexingComponent.java @@ -27,6 +27,8 @@ import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; +import org.apache.logging.log4j.core.Filter; +import org.apache.logging.log4j.core.filter.CompositeFilter; import org.elasticsearch.action.bulk.BulkProcessor; import org.elasticsearch.action.bulk.BulkRequest; import org.elasticsearch.action.bulk.BulkResponse; @@ -36,9 +38,10 @@ import org.elasticsearch.cluster.ClusterChangedEvent; import org.elasticsearch.cluster.ClusterState; import org.elasticsearch.cluster.ClusterStateListener; -import org.elasticsearch.cluster.service.ClusterService; import org.elasticsearch.common.component.AbstractLifecycleComponent; import org.elasticsearch.common.logging.Loggers; +import org.elasticsearch.common.logging.RateLimitingFilter; +import org.elasticsearch.common.logging.UnionFilter; import org.elasticsearch.common.settings.Setting; import org.elasticsearch.common.unit.TimeValue; import org.elasticsearch.threadpool.ThreadPool; @@ -46,7 +49,7 @@ import java.util.function.Consumer; -import static org.elasticsearch.common.logging.DeprecationLogger.DEPRECATION_ONLY; +import static org.elasticsearch.common.logging.DeprecationLogger.DEPRECATION_ONLY_FILTER; public class DeprecationIndexingComponent extends AbstractLifecycleComponent implements ClusterStateListener { private static final Logger logger = LogManager.getLogger(DeprecationIndexingComponent.class); @@ -60,24 +63,24 @@ public class DeprecationIndexingComponent extends AbstractLifecycleComponent imp private final DeprecationIndexingAppender appender; - public DeprecationIndexingComponent(ClusterService clusterService, ThreadPool threadPool, Client client) { - this.appender = new DeprecationIndexingAppender( - buildIndexRequestConsumer(threadPool, client), - "DeprecationIndexer", - DEPRECATION_ONLY - ); + public DeprecationIndexingComponent(ThreadPool threadPool, Client client) { + final Consumer consumer = buildIndexRequestConsumer(threadPool, client); + final Filter filter = UnionFilter.createFilters(DEPRECATION_ONLY_FILTER, new RateLimitingFilter()); + filter.start(); - clusterService.addListener(this); + this.appender = new DeprecationIndexingAppender(consumer, "DeprecationIndexer", filter); } @Override protected void doStart() { + this.appender.start(); Loggers.addAppender(LogManager.getLogger("org.elasticsearch.deprecation"), this.appender); } @Override protected void doStop() { Loggers.addAppender(LogManager.getLogger("org.elasticsearch.deprecation"), this.appender); + this.appender.stop(); } @Override diff --git a/x-pack/plugin/deprecation/src/test/java/org/elasticsearch/xpack/deprecation/DeprecationIndexingAppenderTests.java b/x-pack/plugin/deprecation/src/test/java/org/elasticsearch/xpack/deprecation/DeprecationIndexingAppenderTests.java index 268b84573524b..fceee2fa38e63 100644 --- a/x-pack/plugin/deprecation/src/test/java/org/elasticsearch/xpack/deprecation/DeprecationIndexingAppenderTests.java +++ b/x-pack/plugin/deprecation/src/test/java/org/elasticsearch/xpack/deprecation/DeprecationIndexingAppenderTests.java @@ -110,7 +110,7 @@ public void testMessageSubstitutesArgumentsWhenSupplied() { * Wraps up the steps for extracting an index request payload from the mocks. */ private Map getWriteRequest(String key, ESLogMessage message) { - message.field("x-key", key); + message.field("key", key); LogEvent logEvent = mock(LogEvent.class); when(logEvent.getMessage()).thenReturn(message); @@ -126,7 +126,7 @@ private Map getWriteRequest(String key, ESLogMessage message) { private LogEvent buildEvent() { LogEvent logEvent = mock(LogEvent.class); - when(logEvent.getMessage()).thenReturn(DeprecatedMessage.of(null, "a message").field("x-key", "a key")); + when(logEvent.getMessage()).thenReturn(DeprecatedMessage.of(null, "a message").field("key", "a key")); return logEvent; } } From 4aead94523d9a12a302f6231bf810af73f5f0995 Mon Sep 17 00:00:00 2001 From: Rory Hunter Date: Mon, 20 Jul 2020 21:09:32 +0100 Subject: [PATCH 23/48] Reimplement using log4j abstractions --- distribution/src/config/log4j2.properties | 4 ++ modules/deprecation-logger/build.gradle | 23 ------ .../deprecation/DeprecationComponent.java | 51 -------------- .../deprecation/DeprecationPlugin.java | 57 --------------- .../common/logging/EvilLoggerTests.java | 65 ++++------------- .../logging/deprecation/log4j2.properties | 7 +- .../common/logging/settings/log4j2.properties | 7 +- .../common/logging/JsonLoggerTests.java | 54 +++++++------- .../logging/json_layout/log4j2.properties | 8 ++- .../logging}/HeaderWarningAppender.java | 22 ++++-- .../common/logging/UnionFilter.java | 5 +- .../logging/DeprecationLoggerTests.java | 61 ---------------- .../logging/RateLimitingFilterTests.java | 70 +++++++++++++++++++ .../src/main/resources/log4j2-test.properties | 7 ++ .../logging/DeprecationIndexingComponent.java | 1 - 15 files changed, 164 insertions(+), 278 deletions(-) delete mode 100644 modules/deprecation-logger/build.gradle delete mode 100644 modules/deprecation-logger/src/main/java/org/elasticsearch/deprecation/DeprecationComponent.java delete mode 100644 modules/deprecation-logger/src/main/java/org/elasticsearch/deprecation/DeprecationPlugin.java rename {modules/deprecation-logger/src/main/java/org/elasticsearch/deprecation => server/src/main/java/org/elasticsearch/common/logging}/HeaderWarningAppender.java (66%) create mode 100644 server/src/test/java/org/elasticsearch/common/logging/RateLimitingFilterTests.java diff --git a/distribution/src/config/log4j2.properties b/distribution/src/config/log4j2.properties index d7b980f6ade40..c3867747d69e2 100644 --- a/distribution/src/config/log4j2.properties +++ b/distribution/src/config/log4j2.properties @@ -71,11 +71,15 @@ 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 + +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.header_warning.ref = header_warning logger.deprecation.additivity = false ######## Search slowlog JSON #################### diff --git a/modules/deprecation-logger/build.gradle b/modules/deprecation-logger/build.gradle deleted file mode 100644 index cee4f0429c2d5..0000000000000 --- a/modules/deprecation-logger/build.gradle +++ /dev/null @@ -1,23 +0,0 @@ -/* - * Licensed to Elasticsearch under one or more contributor - * license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright - * ownership. Elasticsearch 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. - */ - -esplugin { - description 'Plugin for logging deprecation messages' - classname 'org.elasticsearch.deprecation.DeprecationPlugin' -} diff --git a/modules/deprecation-logger/src/main/java/org/elasticsearch/deprecation/DeprecationComponent.java b/modules/deprecation-logger/src/main/java/org/elasticsearch/deprecation/DeprecationComponent.java deleted file mode 100644 index 26f178edb4b3e..0000000000000 --- a/modules/deprecation-logger/src/main/java/org/elasticsearch/deprecation/DeprecationComponent.java +++ /dev/null @@ -1,51 +0,0 @@ -/* - * Licensed to Elasticsearch under one or more contributor - * license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright - * ownership. Elasticsearch 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. - */ - -package org.elasticsearch.deprecation; - -import org.apache.logging.log4j.LogManager; -import org.apache.logging.log4j.core.Appender; -import org.elasticsearch.common.component.AbstractLifecycleComponent; -import org.elasticsearch.common.logging.Loggers; - -import static org.elasticsearch.common.logging.DeprecationLogger.DEPRECATION_ONLY_FILTER; - -public class DeprecationComponent extends AbstractLifecycleComponent { - private final Appender appender; - - public DeprecationComponent() { - this.appender = new HeaderWarningAppender("HeaderWarning", DEPRECATION_ONLY_FILTER); - this.appender.start(); - } - - @Override - protected void doStart() { - Loggers.addAppender(LogManager.getLogger("org.elasticsearch.deprecation"), this.appender); - } - - @Override - protected void doStop() { - Loggers.removeAppender(LogManager.getLogger("org.elasticsearch.deprecation"), this.appender); - } - - @Override - protected void doClose() { - // Nothing to do at present - } -} diff --git a/modules/deprecation-logger/src/main/java/org/elasticsearch/deprecation/DeprecationPlugin.java b/modules/deprecation-logger/src/main/java/org/elasticsearch/deprecation/DeprecationPlugin.java deleted file mode 100644 index e04bdcf9c9044..0000000000000 --- a/modules/deprecation-logger/src/main/java/org/elasticsearch/deprecation/DeprecationPlugin.java +++ /dev/null @@ -1,57 +0,0 @@ -/* - * Licensed to Elasticsearch under one or more contributor - * license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright - * ownership. Elasticsearch 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. - */ - -package org.elasticsearch.deprecation; - -import org.elasticsearch.client.Client; -import org.elasticsearch.cluster.metadata.IndexNameExpressionResolver; -import org.elasticsearch.cluster.service.ClusterService; -import org.elasticsearch.common.io.stream.NamedWriteableRegistry; -import org.elasticsearch.common.xcontent.NamedXContentRegistry; -import org.elasticsearch.env.Environment; -import org.elasticsearch.env.NodeEnvironment; -import org.elasticsearch.plugins.Plugin; -import org.elasticsearch.repositories.RepositoriesService; -import org.elasticsearch.script.ScriptService; -import org.elasticsearch.threadpool.ThreadPool; -import org.elasticsearch.watcher.ResourceWatcherService; - -import java.util.Collection; -import java.util.List; -import java.util.function.Supplier; - -public class DeprecationPlugin extends Plugin { - - @Override - public Collection createComponents( - Client client, - ClusterService clusterService, - ThreadPool threadPool, - ResourceWatcherService resourceWatcherService, - ScriptService scriptService, - NamedXContentRegistry xContentRegistry, - Environment environment, - NodeEnvironment nodeEnvironment, - NamedWriteableRegistry namedWriteableRegistry, - IndexNameExpressionResolver indexNameExpressionResolver, - Supplier repositoriesServiceSupplier - ) { - return List.of(new DeprecationComponent()); - } -} diff --git a/qa/evil-tests/src/test/java/org/elasticsearch/common/logging/EvilLoggerTests.java b/qa/evil-tests/src/test/java/org/elasticsearch/common/logging/EvilLoggerTests.java index 8411305422523..473073987bfd2 100644 --- a/qa/evil-tests/src/test/java/org/elasticsearch/common/logging/EvilLoggerTests.java +++ b/qa/evil-tests/src/test/java/org/elasticsearch/common/logging/EvilLoggerTests.java @@ -52,11 +52,14 @@ import java.util.Set; import java.util.concurrent.BrokenBarrierException; import java.util.concurrent.CyclicBarrier; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.TimeoutException; import java.util.regex.Matcher; import java.util.regex.Pattern; import java.util.stream.Collectors; import java.util.stream.IntStream; +import static org.elasticsearch.common.logging.DeprecationLogger.DEPRECATION; import static org.hamcrest.Matchers.endsWith; import static org.hamcrest.Matchers.equalTo; import static org.hamcrest.Matchers.hasItem; @@ -115,11 +118,12 @@ public void testConcurrentDeprecationLogger() throws IOException, UserException, final List threads = new ArrayList<>(); final int iterations = randomIntBetween(1, 4); for (int i = 0; i < numberOfThreads; i++) { + logger.warn("Kicking off thread " + i); final Thread thread = new Thread(() -> { final List ids = IntStream.range(0, 128).boxed().collect(Collectors.toList()); Randomness.shuffle(ids); final ThreadContext threadContext = new ThreadContext(Settings.EMPTY); - DeprecationLogger.setThreadContext(threadContext); + HeaderWarning.setThreadContext(threadContext); try { barrier.await(); } catch (final BrokenBarrierException | InterruptedException e) { @@ -137,6 +141,7 @@ public void testConcurrentDeprecationLogger() throws IOException, UserException, * on the other threads. */ final List warnings = threadContext.getResponseHeaders().get("Warning"); + assertNotNull(warnings); final Set actualWarningValues = warnings.stream().map(s -> HeaderWarning.extractWarningValueFromWarningHeader(s, true)) .collect(Collectors.toSet()); @@ -147,8 +152,9 @@ public void testConcurrentDeprecationLogger() throws IOException, UserException, } try { - barrier.await(); - } catch (final BrokenBarrierException | InterruptedException e) { + logger.warn("About to await barrier"); + barrier.await(1, TimeUnit.SECONDS); + } catch (final BrokenBarrierException | InterruptedException | TimeoutException e) { throw new RuntimeException(e); } }); @@ -157,9 +163,11 @@ public void testConcurrentDeprecationLogger() throws IOException, UserException, } // synchronize the start of all threads + logger.warn("synchronize the start of all threads"); barrier.await(); // wait for all threads to complete their iterations + logger.warn("wait for all threads to complete their iterations"); barrier.await(); final String deprecationPath = @@ -180,8 +188,8 @@ public void testConcurrentDeprecationLogger() throws IOException, UserException, for (int i = 0; i < 128; i++) { assertLogLine( deprecationEvents.get(i), - Level.WARN, - "org.elasticsearch.common.logging.WarningLogger\\$1\\.run", + DEPRECATION, + "org.elasticsearch.common.logging.DeprecationLogger\\$DeprecationLoggerBuilder.withDeprecation", "This is a maybe logged deprecation message" + i); } @@ -191,49 +199,6 @@ public void testConcurrentDeprecationLogger() throws IOException, UserException, } - public void testDeprecationLoggerMaybeLog() throws IOException, UserException { - setupLogging("deprecation"); - - final DeprecationLogger deprecationLogger = DeprecationLogger.getLogger("deprecation"); - - final int iterations = randomIntBetween(1, 16); - - for (int i = 0; i < iterations; i++) { - deprecationLogger.deprecate("key", "This is a maybe logged deprecation message"); - assertWarnings("This is a maybe logged deprecation message"); - } - for (int k = 0; k < 128; k++) { - for (int i = 0; i < iterations; i++) { - deprecationLogger.deprecate("key" + k, "This is a maybe logged deprecation message" + k); - assertWarnings("This is a maybe logged deprecation message" + k); - } - } - for (int i = 0; i < iterations; i++) { - deprecationLogger.deprecate("key", "This is a maybe logged deprecation message"); - assertWarnings("This is a maybe logged deprecation message"); - } - - final String deprecationPath = - System.getProperty("es.logs.base_path") + - System.getProperty("file.separator") + - System.getProperty("es.logs.cluster_name") + - "_deprecation.log"; - final List deprecationEvents = Files.readAllLines(PathUtils.get(deprecationPath)); - assertThat(deprecationEvents.size(), equalTo(1 + 128 + 1)); - assertLogLine( - deprecationEvents.get(0), - Level.WARN, - "org.elasticsearch.common.logging.WarningLogger\\$1\\.run", - "This is a maybe logged deprecation message"); - for (int k = 0; k < 128; k++) { - assertLogLine( - deprecationEvents.get(1 + k), - Level.WARN, - "org.elasticsearch.common.logging.WarningLogger\\$1\\.run", - "This is a maybe logged deprecation message" + k); - } - } - public void testDeprecatedSettings() throws IOException, UserException { setupLogging("settings"); @@ -256,8 +221,8 @@ public void testDeprecatedSettings() throws IOException, UserException { assertThat(deprecationEvents.size(), equalTo(1)); assertLogLine( deprecationEvents.get(0), - Level.WARN, - "org.elasticsearch.common.logging.WarningLogger\\$1\\.run", + DEPRECATION, + "org.elasticsearch.common.logging.DeprecationLogger\\$DeprecationLoggerBuilder.withDeprecation", "\\[deprecated.foo\\] setting was deprecated in Elasticsearch and will be removed in a future release! " + "See the breaking changes documentation for the next major version."); } diff --git a/qa/evil-tests/src/test/resources/org/elasticsearch/common/logging/deprecation/log4j2.properties b/qa/evil-tests/src/test/resources/org/elasticsearch/common/logging/deprecation/log4j2.properties index 388c9f9b2fc64..e488da2f95f58 100644 --- a/qa/evil-tests/src/test/resources/org/elasticsearch/common/logging/deprecation/log4j2.properties +++ b/qa/evil-tests/src/test/resources/org/elasticsearch/common/logging/deprecation/log4j2.properties @@ -18,8 +18,13 @@ appender.deprecation_file.name = deprecation_file appender.deprecation_file.fileName = ${sys:es.logs.base_path}${sys:file.separator}${sys:es.logs.cluster_name}_deprecation.log appender.deprecation_file.layout.type = PatternLayout appender.deprecation_file.layout.pattern = [%p][%l] [%test_thread_info]%marker %m%n +appender.deprecation_file.filter.rate_limit.type = RateLimitingFilter + +appender.header_warning.type = HeaderWarningAppender +appender.header_warning.name = header_warning logger.deprecation.name = deprecation -logger.deprecation.level = warn +logger.deprecation.level = deprecation logger.deprecation.appenderRef.deprecation_file.ref = deprecation_file +logger.deprecation.appenderRef.header_warning.ref = header_warning logger.deprecation.additivity = false diff --git a/qa/evil-tests/src/test/resources/org/elasticsearch/common/logging/settings/log4j2.properties b/qa/evil-tests/src/test/resources/org/elasticsearch/common/logging/settings/log4j2.properties index abe4a279dc820..284b7bda1f7b0 100644 --- a/qa/evil-tests/src/test/resources/org/elasticsearch/common/logging/settings/log4j2.properties +++ b/qa/evil-tests/src/test/resources/org/elasticsearch/common/logging/settings/log4j2.properties @@ -18,9 +18,14 @@ appender.deprecation_file.name = deprecation_file appender.deprecation_file.fileName = ${sys:es.logs.base_path}${sys:file.separator}${sys:es.logs.cluster_name}_deprecation.log appender.deprecation_file.layout.type = PatternLayout appender.deprecation_file.layout.pattern = [%p][%l] [%test_thread_info]%marker %m%n +appender.deprecation_file.filter.rate_limit.type = RateLimitingFilter + +appender.header_warning.type = HeaderWarningAppender +appender.header_warning.name = header_warning logger.deprecation.name = org.elasticsearch.deprecation.common.settings -logger.deprecation.level = warn +logger.deprecation.level = deprecation logger.deprecation.appenderRef.deprecation_console.ref = console logger.deprecation.appenderRef.deprecation_file.ref = deprecation_file +logger.deprecation.appenderRef.header_warning.ref = header_warning logger.deprecation.additivity = false diff --git a/qa/logging-config/src/test/java/org/elasticsearch/common/logging/JsonLoggerTests.java b/qa/logging-config/src/test/java/org/elasticsearch/common/logging/JsonLoggerTests.java index c2a467212c084..f35f4c46a7ac8 100644 --- a/qa/logging-config/src/test/java/org/elasticsearch/common/logging/JsonLoggerTests.java +++ b/qa/logging-config/src/test/java/org/elasticsearch/common/logging/JsonLoggerTests.java @@ -48,6 +48,7 @@ import java.util.stream.Collectors; import java.util.stream.Stream; +import static org.elasticsearch.common.logging.DeprecationLogger.DEPRECATION; import static org.hamcrest.Matchers.allOf; import static org.hamcrest.Matchers.contains; import static org.hamcrest.Matchers.hasEntry; @@ -87,10 +88,13 @@ public void tearDown() throws Exception { public void testDeprecatedMessage() throws IOException { final Logger testLogger = LogManager.getLogger("deprecation.test"); - testLogger.info(DeprecatedMessage.of("someId","deprecated message1")); + testLogger.log(DEPRECATION, DeprecatedMessage.of("someId","deprecated message1")); final Path path = PathUtils.get(System.getProperty("es.logs.base_path"), System.getProperty("es.logs.cluster_name") + "_deprecated.json"); + + assertTrue(path.toFile().exists()); + try (Stream> stream = JsonLogsStream.mapStreamFrom(path)) { List> jsonLogs = stream .collect(Collectors.toList()); @@ -98,7 +102,7 @@ public void testDeprecatedMessage() throws IOException { assertThat(jsonLogs, contains( allOf( hasEntry("type", "deprecation"), - hasEntry("log.level", "INFO"), + hasEntry("log.level", "DEPRECATION"), hasEntry("log.logger", "deprecation.test"), hasEntry("cluster.name", "elasticsearch"), hasEntry("node.name", "sample-name"), @@ -107,6 +111,8 @@ public void testDeprecatedMessage() throws IOException { ) ); } + + assertWarnings("deprecated message1"); } public void testBuildingMessage() throws IOException { @@ -169,10 +175,11 @@ public void testCustomMessageWithMultipleFields() throws IOException { public void testDeprecatedMessageWithoutXOpaqueId() throws IOException { final Logger testLogger = LogManager.getLogger("deprecation.test"); - testLogger.info( DeprecatedMessage.of("someId","deprecated message1")); - testLogger.info( DeprecatedMessage.of("","deprecated message2")); - testLogger.info( DeprecatedMessage.of(null,"deprecated message3")); - testLogger.info("deprecated message4"); + testLogger.log(DEPRECATION, DeprecatedMessage.of("someOtherId","deprecated message1").field("key", "a key")); + testLogger.log(DEPRECATION, DeprecatedMessage.of("","deprecated message2").field("key", "a key")); + // This next message will be filtered out, because a null opaque ID has the same effect as an empty ID. + testLogger.log(DEPRECATION, DeprecatedMessage.of(null,"deprecated message3").field("key", "a key")); + testLogger.log(DEPRECATION,"deprecated message4"); final Path path = PathUtils.get(System.getProperty("es.logs.base_path"), System.getProperty("es.logs.cluster_name") + "_deprecated.json"); @@ -183,15 +190,15 @@ public void testDeprecatedMessageWithoutXOpaqueId() throws IOException { assertThat(jsonLogs, contains( allOf( hasEntry("type", "deprecation"), - hasEntry("log.level", "INFO"), + hasEntry("log.level", "DEPRECATION"), hasEntry("log.logger", "deprecation.test"), hasEntry("cluster.name", "elasticsearch"), hasEntry("node.name", "sample-name"), hasEntry("message", "deprecated message1"), - hasEntry("x-opaque-id", "someId")), + hasEntry("x-opaque-id", "someOtherId")), allOf( hasEntry("type", "deprecation"), - hasEntry("log.level", "INFO"), + hasEntry("log.level", "DEPRECATION"), hasEntry("log.logger", "deprecation.test"), hasEntry("cluster.name", "elasticsearch"), hasEntry("node.name", "sample-name"), @@ -200,16 +207,7 @@ public void testDeprecatedMessageWithoutXOpaqueId() throws IOException { ), allOf( hasEntry("type", "deprecation"), - hasEntry("log.level", "INFO"), - hasEntry("log.logger", "deprecation.test"), - hasEntry("cluster.name", "elasticsearch"), - hasEntry("node.name", "sample-name"), - hasEntry("message", "deprecated message3"), - not(hasKey("x-opaque-id")) - ), - allOf( - hasEntry("type", "deprecation"), - hasEntry("log.level", "INFO"), + hasEntry("log.level", "DEPRECATION"), hasEntry("log.logger", "deprecation.test"), hasEntry("cluster.name", "elasticsearch"), hasEntry("node.name", "sample-name"), @@ -218,7 +216,11 @@ public void testDeprecatedMessageWithoutXOpaqueId() throws IOException { ) ) ); + + assertThat(jsonLogs, not(contains(hasEntry("message", "deprecated message3")))); } + + assertWarnings("deprecated message1", "deprecated message2", "deprecated message3", "deprecated message4"); } public void testJsonLayout() throws IOException { @@ -342,7 +344,7 @@ public void testDuplicateLogMessages() throws IOException { ThreadContext threadContext = new ThreadContext(Settings.EMPTY); try (ThreadContext.StoredContext ignore = threadContext.stashContext()) { threadContext.putHeader(Task.X_OPAQUE_ID, "ID1"); - DeprecationLogger.setThreadContext(threadContext); + HeaderWarning.setThreadContext(threadContext); deprecationLogger.deprecate("key", "message1"); deprecationLogger.deprecate("key", "message2"); assertWarnings("message1", "message2"); @@ -356,7 +358,7 @@ public void testDuplicateLogMessages() throws IOException { assertThat(jsonLogs, contains( allOf( hasEntry("type", "deprecation"), - hasEntry("log.level", "WARN"), + hasEntry("log.level", "DEPRECATION"), hasEntry("log.logger", "deprecation.test"), hasEntry("cluster.name", "elasticsearch"), hasEntry("node.name", "sample-name"), @@ -366,7 +368,7 @@ public void testDuplicateLogMessages() throws IOException { ); } } finally { - DeprecationLogger.removeThreadContext(threadContext); + HeaderWarning.removeThreadContext(threadContext); } @@ -374,7 +376,7 @@ public void testDuplicateLogMessages() throws IOException { //continuing with message1-ID1 in logs already, adding a new deprecation log line with message2-ID2 try (ThreadContext.StoredContext ignore = threadContext.stashContext()) { threadContext.putHeader(Task.X_OPAQUE_ID, "ID2"); - DeprecationLogger.setThreadContext(threadContext); + HeaderWarning.setThreadContext(threadContext); deprecationLogger.deprecate("key", "message1"); deprecationLogger.deprecate("key", "message2"); assertWarnings("message1", "message2"); @@ -388,7 +390,7 @@ public void testDuplicateLogMessages() throws IOException { assertThat(jsonLogs, contains( allOf( hasEntry("type", "deprecation"), - hasEntry("log.level", "WARN"), + hasEntry("log.level", "DEPRECATION"), hasEntry("log.logger", "deprecation.test"), hasEntry("cluster.name", "elasticsearch"), hasEntry("node.name", "sample-name"), @@ -397,7 +399,7 @@ public void testDuplicateLogMessages() throws IOException { ), allOf( hasEntry("type", "deprecation"), - hasEntry("log.level", "WARN"), + hasEntry("log.level", "DEPRECATION"), hasEntry("log.logger", "deprecation.test"), hasEntry("cluster.name", "elasticsearch"), hasEntry("node.name", "sample-name"), @@ -408,7 +410,7 @@ public void testDuplicateLogMessages() throws IOException { ); } } finally { - DeprecationLogger.removeThreadContext(threadContext); + HeaderWarning.removeThreadContext(threadContext); } } diff --git a/qa/logging-config/src/test/resources/org/elasticsearch/common/logging/json_layout/log4j2.properties b/qa/logging-config/src/test/resources/org/elasticsearch/common/logging/json_layout/log4j2.properties index 46c91d4d89b8e..af9a0428d4f10 100644 --- a/qa/logging-config/src/test/resources/org/elasticsearch/common/logging/json_layout/log4j2.properties +++ b/qa/logging-config/src/test/resources/org/elasticsearch/common/logging/json_layout/log4j2.properties @@ -15,11 +15,13 @@ appender.deprecated.name = deprecated appender.deprecated.fileName = ${sys:es.logs.base_path}${sys:file.separator}${sys:es.logs.cluster_name}_deprecated.json appender.deprecated.layout.type = ECSJsonLayout appender.deprecated.layout.type_name = deprecation +appender.deprecated.filter.rate_limit.type = RateLimitingFilter appender.deprecatedconsole.type = Console appender.deprecatedconsole.name = deprecatedconsole appender.deprecatedconsole.layout.type = ECSJsonLayout appender.deprecatedconsole.layout.type_name = deprecation +appender.deprecatedconsole.filter.rate_limit.type = RateLimitingFilter rootLogger.level = info @@ -30,12 +32,16 @@ appender.plaintext.name = plaintext appender.plaintext.fileName = ${sys:es.logs.base_path}${sys:file.separator}${sys:es.logs.cluster_name}_plaintext.json appender.plaintext.layout.type = PatternLayout appender.plaintext.layout.pattern =%m%n +appender.plaintext.filter.rate_limit.type = RateLimitingFilter +appender.header_warning.type = HeaderWarningAppender +appender.header_warning.name = header_warning logger.deprecation.name = deprecation.test -logger.deprecation.level = trace +logger.deprecation.level = deprecation logger.deprecation.appenderRef.deprecation_rolling.ref = deprecated logger.deprecation.appenderRef.deprecatedconsole.ref = deprecatedconsole +logger.deprecation.appenderRef.header_warning.ref = header_warning logger.deprecation.additivity = false diff --git a/modules/deprecation-logger/src/main/java/org/elasticsearch/deprecation/HeaderWarningAppender.java b/server/src/main/java/org/elasticsearch/common/logging/HeaderWarningAppender.java similarity index 66% rename from modules/deprecation-logger/src/main/java/org/elasticsearch/deprecation/HeaderWarningAppender.java rename to server/src/main/java/org/elasticsearch/common/logging/HeaderWarningAppender.java index 84eb8d3551da4..d793b31e69562 100644 --- a/modules/deprecation-logger/src/main/java/org/elasticsearch/deprecation/HeaderWarningAppender.java +++ b/server/src/main/java/org/elasticsearch/common/logging/HeaderWarningAppender.java @@ -17,17 +17,20 @@ * under the License. */ -package org.elasticsearch.deprecation; +package org.elasticsearch.common.logging; +import org.apache.logging.log4j.core.Appender; +import org.apache.logging.log4j.core.Core; import org.apache.logging.log4j.core.Filter; import org.apache.logging.log4j.core.LogEvent; import org.apache.logging.log4j.core.appender.AbstractAppender; 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.PluginElement; +import org.apache.logging.log4j.core.config.plugins.PluginFactory; import org.apache.logging.log4j.message.Message; -import org.elasticsearch.common.logging.ESLogMessage; -import org.elasticsearch.common.logging.HeaderWarning; -@Plugin(name = "HeaderWarning", category = "Elastic") +@Plugin(name = "HeaderWarningAppender", category = Core.CATEGORY_NAME, elementType = Appender.ELEMENT_TYPE) public class HeaderWarningAppender extends AbstractAppender { public HeaderWarningAppender(String name, Filter filter) { super(name, filter, null); @@ -44,6 +47,17 @@ public void append(LogEvent event) { Object[] arguments = esLogMessage.getArguments(); HeaderWarning.addWarning(messagePattern, arguments); + } else { + final String formattedMessage = event.getMessage().getFormattedMessage(); + HeaderWarning.addWarning(formattedMessage); } } + + @PluginFactory + public static HeaderWarningAppender createAppender( + @PluginAttribute("name") String name, + @PluginElement("filter") Filter filter + ) { + return new HeaderWarningAppender(name, filter); + } } diff --git a/server/src/main/java/org/elasticsearch/common/logging/UnionFilter.java b/server/src/main/java/org/elasticsearch/common/logging/UnionFilter.java index 0c435770d24b2..85cf4f57a30a3 100644 --- a/server/src/main/java/org/elasticsearch/common/logging/UnionFilter.java +++ b/server/src/main/java/org/elasticsearch/common/logging/UnionFilter.java @@ -1,4 +1,4 @@ -/* +/* @notice * 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. @@ -37,7 +37,8 @@ /** * This is a fork of {@link org.apache.logging.log4j.core.filter.CompositeFilter}. This class * also composes and invokes one or more filters, but unlike CompositeFilter, all - * composed filters will be called to check for one that answers {@link Result#DENY}. + * composed filters will be called to check for one that answers {@link Result#DENY}. The + * implementation of CompositeFilter makes it impractical to extend. */ @Plugin(name = "filters", category = Node.CATEGORY, printObject = true) @PerformanceSensitive("allocation") diff --git a/server/src/test/java/org/elasticsearch/common/logging/DeprecationLoggerTests.java b/server/src/test/java/org/elasticsearch/common/logging/DeprecationLoggerTests.java index 54e62bae4710f..8747f16d06651 100644 --- a/server/src/test/java/org/elasticsearch/common/logging/DeprecationLoggerTests.java +++ b/server/src/test/java/org/elasticsearch/common/logging/DeprecationLoggerTests.java @@ -20,72 +20,11 @@ package org.elasticsearch.common.logging; import org.apache.logging.log4j.LogManager; -import org.apache.logging.log4j.simple.SimpleLoggerContext; -import org.apache.logging.log4j.simple.SimpleLoggerContextFactory; -import org.apache.logging.log4j.spi.ExtendedLogger; -import org.apache.logging.log4j.spi.LoggerContext; -import org.apache.logging.log4j.spi.LoggerContextFactory; -import org.elasticsearch.common.SuppressLoggerChecks; import org.elasticsearch.test.ESTestCase; -import java.net.URI; -import java.security.AccessControlContext; -import java.security.AccessController; -import java.security.Permissions; -import java.security.PrivilegedAction; -import java.security.ProtectionDomain; -import java.util.concurrent.atomic.AtomicBoolean; - import static org.hamcrest.Matchers.equalTo; -import static org.hamcrest.core.Is.is; -import static org.mockito.Matchers.any; -import static org.mockito.Mockito.doAnswer; -import static org.mockito.Mockito.mock; public class DeprecationLoggerTests extends ESTestCase { - @SuppressLoggerChecks(reason = "Safe as this is using mockito") - public void testLogPermissions() { - AtomicBoolean supplierCalled = new AtomicBoolean(false); - - // mocking the logger used inside DeprecationLogger requires heavy hacking... - ExtendedLogger mockLogger = mock(ExtendedLogger.class); - doAnswer(invocationOnMock -> { - supplierCalled.set(true); - createTempDir(); // trigger file permission, like rolling logs would - return null; - }).when(mockLogger).warn(DeprecatedMessage.of(any(), "foo")); - final LoggerContext context = new SimpleLoggerContext() { - @Override - public ExtendedLogger getLogger(String name) { - return mockLogger; - } - }; - - final LoggerContextFactory originalFactory = LogManager.getFactory(); - try { - LogManager.setFactory(new SimpleLoggerContextFactory() { - @Override - public LoggerContext getContext(String fqcn, ClassLoader loader, Object externalContext, boolean currentContext, - URI configLocation, String name) { - return context; - } - }); - DeprecationLogger deprecationLogger = DeprecationLogger.getLogger("logger"); - - AccessControlContext noPermissionsAcc = new AccessControlContext( - new ProtectionDomain[]{new ProtectionDomain(null, new Permissions())} - ); - AccessController.doPrivileged((PrivilegedAction) () -> { - deprecationLogger.deprecate("testLogPermissions_key", "foo {}", "bar"); - return null; - }, noPermissionsAcc); - assertThat("supplier called", supplierCalled.get(), is(true)); - - assertWarnings("foo bar"); - } finally { - LogManager.setFactory(originalFactory); - } - } public void testMultipleSlowLoggersUseSingleLog4jLogger() { org.apache.logging.log4j.core.LoggerContext context = (org.apache.logging.log4j.core.LoggerContext) LogManager.getContext(false); diff --git a/server/src/test/java/org/elasticsearch/common/logging/RateLimitingFilterTests.java b/server/src/test/java/org/elasticsearch/common/logging/RateLimitingFilterTests.java new file mode 100644 index 0000000000000..19161c6bdc78c --- /dev/null +++ b/server/src/test/java/org/elasticsearch/common/logging/RateLimitingFilterTests.java @@ -0,0 +1,70 @@ +/* + * Licensed to Elasticsearch under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch 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. + */ + +package org.elasticsearch.common.logging; + +import org.apache.logging.log4j.message.Message; +import org.apache.logging.log4j.message.SimpleMessage; +import org.elasticsearch.test.ESTestCase; +import org.junit.After; +import org.junit.Before; + +import static org.apache.logging.log4j.core.Filter.Result; +import static org.hamcrest.Matchers.equalTo; + +public class RateLimitingFilterTests extends ESTestCase { + + private RateLimitingFilter filter; + + @Before + public void setup() { + this.filter = new RateLimitingFilter(); + filter.start(); + } + + @After + public void cleanup() { + this.filter.stop(); + } + + public void testRateLimitingFilter() { + // Fill up the cache + for (int i = 0; i < 128; i++) { + Message message = DeprecatedMessage.of("", "msg " + i).field("key", "key" + i); + assertThat("Expected key" + i + " to be accepted", filter.filter(message), equalTo(Result.ACCEPT)); + } + + // Should be rate-limited because it's still in the cache + Message message = DeprecatedMessage.of("", "msg " + 0).field("key", "key" + 0); + assertThat(filter.filter(message), equalTo(Result.DENY)); + + // Filter a message with a previously unseen key, in order to evict key0 as it's the oldest + message = DeprecatedMessage.of("", "msg " + 129).field("key", "key" + 129); + assertThat(filter.filter(message), equalTo(Result.ACCEPT)); + + // Should be allowed because key0 was evicted from the cache + message = DeprecatedMessage.of("", "msg " + 0).field("key", "key" + 0); + assertThat(filter.filter(message), equalTo(Result.ACCEPT)); + } + + public void testOnlyEsMessagesAreFiltered() { + Message message = new SimpleMessage("a message"); + assertThat(filter.filter(message), equalTo(Result.NEUTRAL)); + } +} diff --git a/test/framework/src/main/resources/log4j2-test.properties b/test/framework/src/main/resources/log4j2-test.properties index 842c9c79d7963..3d9c084267a84 100644 --- a/test/framework/src/main/resources/log4j2-test.properties +++ b/test/framework/src/main/resources/log4j2-test.properties @@ -5,3 +5,10 @@ appender.console.layout.pattern = [%d{ISO8601}][%-5p][%-25c{1.}] [%test_thread_i rootLogger.level = ${sys:tests.es.logger.level:-info} rootLogger.appenderRef.console.ref = console + +appender.header_warning.type = HeaderWarningAppender +appender.header_warning.name = header_warning + +logger.deprecation.name = org.elasticsearch.deprecation +logger.deprecation.level = deprecation +JsonLoggerTestslogger.deprecation.appenderRef.header_warning.ref = header_warning diff --git a/x-pack/plugin/deprecation/src/main/java/org/elasticsearch/xpack/deprecation/logging/DeprecationIndexingComponent.java b/x-pack/plugin/deprecation/src/main/java/org/elasticsearch/xpack/deprecation/logging/DeprecationIndexingComponent.java index 78f074808dd98..2665152a75831 100644 --- a/x-pack/plugin/deprecation/src/main/java/org/elasticsearch/xpack/deprecation/logging/DeprecationIndexingComponent.java +++ b/x-pack/plugin/deprecation/src/main/java/org/elasticsearch/xpack/deprecation/logging/DeprecationIndexingComponent.java @@ -28,7 +28,6 @@ import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; import org.apache.logging.log4j.core.Filter; -import org.apache.logging.log4j.core.filter.CompositeFilter; import org.elasticsearch.action.bulk.BulkProcessor; import org.elasticsearch.action.bulk.BulkRequest; import org.elasticsearch.action.bulk.BulkResponse; From e8d246b44ce4923801f7428f4f5980f1e6489520 Mon Sep 17 00:00:00 2001 From: Rory Hunter Date: Mon, 20 Jul 2020 21:54:20 +0100 Subject: [PATCH 24/48] Polishing --- .../common/logging/EvilLoggerTests.java | 9 +-- .../common/logging/JsonLoggerTests.java | 2 - .../common/logging/DeprecatedLogHandler.java | 33 -------- .../common/logging/DeprecationLogger.java | 43 ++++++---- .../logging/DeprecationIndexingAppender.java | 79 +++++++++++++------ .../logging/DeprecationIndexingComponent.java | 29 ++----- .../DeprecationIndexingAppenderTests.java | 2 +- 7 files changed, 92 insertions(+), 105 deletions(-) delete mode 100644 server/src/main/java/org/elasticsearch/common/logging/DeprecatedLogHandler.java diff --git a/qa/evil-tests/src/test/java/org/elasticsearch/common/logging/EvilLoggerTests.java b/qa/evil-tests/src/test/java/org/elasticsearch/common/logging/EvilLoggerTests.java index 473073987bfd2..7858367d6ae63 100644 --- a/qa/evil-tests/src/test/java/org/elasticsearch/common/logging/EvilLoggerTests.java +++ b/qa/evil-tests/src/test/java/org/elasticsearch/common/logging/EvilLoggerTests.java @@ -118,7 +118,6 @@ public void testConcurrentDeprecationLogger() throws IOException, UserException, final List threads = new ArrayList<>(); final int iterations = randomIntBetween(1, 4); for (int i = 0; i < numberOfThreads; i++) { - logger.warn("Kicking off thread " + i); final Thread thread = new Thread(() -> { final List ids = IntStream.range(0, 128).boxed().collect(Collectors.toList()); Randomness.shuffle(ids); @@ -141,7 +140,6 @@ public void testConcurrentDeprecationLogger() throws IOException, UserException, * on the other threads. */ final List warnings = threadContext.getResponseHeaders().get("Warning"); - assertNotNull(warnings); final Set actualWarningValues = warnings.stream().map(s -> HeaderWarning.extractWarningValueFromWarningHeader(s, true)) .collect(Collectors.toSet()); @@ -152,9 +150,8 @@ public void testConcurrentDeprecationLogger() throws IOException, UserException, } try { - logger.warn("About to await barrier"); - barrier.await(1, TimeUnit.SECONDS); - } catch (final BrokenBarrierException | InterruptedException | TimeoutException e) { + barrier.await(); + } catch (final BrokenBarrierException | InterruptedException e) { throw new RuntimeException(e); } }); @@ -163,11 +160,9 @@ public void testConcurrentDeprecationLogger() throws IOException, UserException, } // synchronize the start of all threads - logger.warn("synchronize the start of all threads"); barrier.await(); // wait for all threads to complete their iterations - logger.warn("wait for all threads to complete their iterations"); barrier.await(); final String deprecationPath = diff --git a/qa/logging-config/src/test/java/org/elasticsearch/common/logging/JsonLoggerTests.java b/qa/logging-config/src/test/java/org/elasticsearch/common/logging/JsonLoggerTests.java index f35f4c46a7ac8..bb87d386c27eb 100644 --- a/qa/logging-config/src/test/java/org/elasticsearch/common/logging/JsonLoggerTests.java +++ b/qa/logging-config/src/test/java/org/elasticsearch/common/logging/JsonLoggerTests.java @@ -93,8 +93,6 @@ public void testDeprecatedMessage() throws IOException { final Path path = PathUtils.get(System.getProperty("es.logs.base_path"), System.getProperty("es.logs.cluster_name") + "_deprecated.json"); - assertTrue(path.toFile().exists()); - try (Stream> stream = JsonLogsStream.mapStreamFrom(path)) { List> jsonLogs = stream .collect(Collectors.toList()); diff --git a/server/src/main/java/org/elasticsearch/common/logging/DeprecatedLogHandler.java b/server/src/main/java/org/elasticsearch/common/logging/DeprecatedLogHandler.java deleted file mode 100644 index 557e978af7149..0000000000000 --- a/server/src/main/java/org/elasticsearch/common/logging/DeprecatedLogHandler.java +++ /dev/null @@ -1,33 +0,0 @@ -/* - * Licensed to Elasticsearch under one or more contributor - * license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright - * ownership. Elasticsearch 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. - */ - -package org.elasticsearch.common.logging; - -/** - * Classes that want to receive deprecation logs must implement this interface. - */ -public interface DeprecatedLogHandler { - - /** - * Handle a single deprecation log message. - * @param key a value that should uniquely identify the deprecation - * @param message the deprecation message itself - */ - void log(String key, ESLogMessage message); -} diff --git a/server/src/main/java/org/elasticsearch/common/logging/DeprecationLogger.java b/server/src/main/java/org/elasticsearch/common/logging/DeprecationLogger.java index 24516dfed0e6e..bc4b508fb8fa4 100644 --- a/server/src/main/java/org/elasticsearch/common/logging/DeprecationLogger.java +++ b/server/src/main/java/org/elasticsearch/common/logging/DeprecationLogger.java @@ -27,19 +27,28 @@ /** * A logger that logs deprecation notices. Logger should be initialized with a parent logger which name will be used - * for deprecation logger. For instance new DeprecationLogger("org.elasticsearch.test.SomeClass") will - * result in a deprecation logger with name org.elasticsearch.deprecation.test.SomeClass. This allows to use + * for deprecation logger. For instance DeprecationLogger.getLogger("org.elasticsearch.test.SomeClass") will + * result in a deprecation logger with name org.elasticsearch.deprecation.test.SomeClass. This allows to use a * deprecation logger defined in log4j2.properties. *

- * Deprecation logs are written to deprecation log file - defined in log4j2.properties, as well as warnings added to a response header. - * All deprecation usages are throttled basing on a key. Key is a string provided in an argument and can be prefixed with - * X-Opaque-Id. This allows to throttle deprecations per client usage. - * deprecationLogger.deprecate("key","message {}", "param"); + * Logs are emitted at the custom {@link #DEPRECATION} level, and routed wherever they need to go using log4j. For example, + * to disk using a rolling file appender, or added as a response header using {@link HeaderWarningAppender}. + *

+ * Deprecation messages include a key, which is used for rate-limiting purposes. The log4j configuration + * uses {@link RateLimitingFilter} to prevent the same message being logged repeatedly in a short span of time. This + * key is combined with the X-Opaque-Id request header value, if supplied, which allows for per-client + * message limiting. */ public class DeprecationLogger { + + /** + * Deprecation messages are logged at this level. + */ public static Level DEPRECATION = Level.forName("DEPRECATION", Level.WARN.intLevel() + 1); - // Only handle log events with the custom DEPRECATION level + /** + * A level filter that only accepts messages written at the {@link #DEPRECATION} level. + */ public static final LevelRangeFilter DEPRECATION_ONLY_FILTER = LevelRangeFilter.createFilter( DEPRECATION, DEPRECATION, @@ -49,20 +58,24 @@ public class DeprecationLogger { private final Logger logger; - /** - * Creates a new deprecation logger based on the parent logger. Automatically - * prefixes the logger name with "deprecation", if it starts with "org.elasticsearch.", - * it replaces "org.elasticsearch" with "org.elasticsearch.deprecation" to maintain - * the "org.elasticsearch" namespace. - */ private DeprecationLogger(Logger parentLogger) { this.logger = parentLogger; } + /** + * Creates a new deprecation logger for the supplied class. Internally, it delegates to + * {@link #getLogger(String)}, passing the full class name. + */ public static DeprecationLogger getLogger(Class aClass) { return getLogger(toLoggerName(aClass)); } + /** + * Creates a new deprecation logger based on the parent logger. Automatically + * prefixes the logger name with "deprecation", if it starts with "org.elasticsearch.", + * it replaces "org.elasticsearch" with "org.elasticsearch.deprecation" to maintain + * the "org.elasticsearch" namespace. + */ public static DeprecationLogger getLogger(String name) { return new DeprecationLogger(getDeprecatedLoggerForName(name)); } @@ -82,9 +95,7 @@ private static String toLoggerName(final Class cls) { } /** - * Logs a deprecation message, adding a formatted warning message as a response header on the thread context. - * The deprecation message will be throttled to deprecation log. - * method returns a builder as more methods are expected to be chained. + * Logs a message at the {@link #DEPRECATION} level. */ public DeprecationLoggerBuilder deprecate(final String key, final String msg, final Object... params) { return new DeprecationLoggerBuilder().withDeprecation(key, msg, params); diff --git a/x-pack/plugin/deprecation/src/main/java/org/elasticsearch/xpack/deprecation/logging/DeprecationIndexingAppender.java b/x-pack/plugin/deprecation/src/main/java/org/elasticsearch/xpack/deprecation/logging/DeprecationIndexingAppender.java index 2a7072301bc2c..9d6e6f3296328 100644 --- a/x-pack/plugin/deprecation/src/main/java/org/elasticsearch/xpack/deprecation/logging/DeprecationIndexingAppender.java +++ b/x-pack/plugin/deprecation/src/main/java/org/elasticsearch/xpack/deprecation/logging/DeprecationIndexingAppender.java @@ -6,9 +6,12 @@ package org.elasticsearch.xpack.deprecation.logging; +import org.apache.logging.log4j.core.Appender; +import org.apache.logging.log4j.core.Core; import org.apache.logging.log4j.core.Filter; import org.apache.logging.log4j.core.LogEvent; import org.apache.logging.log4j.core.appender.AbstractAppender; +import org.apache.logging.log4j.core.config.plugins.Plugin; import org.apache.logging.log4j.message.Message; import org.elasticsearch.action.DocWriteRequest; import org.elasticsearch.action.index.IndexRequest; @@ -18,11 +21,18 @@ import java.time.Instant; import java.util.HashMap; import java.util.Map; +import java.util.Objects; import java.util.function.Consumer; import static org.elasticsearch.common.Strings.isNullOrEmpty; import static org.elasticsearch.common.logging.DeprecatedMessage.X_OPAQUE_ID_FIELD_NAME; +/** + * This log4j appender writes deprecation log messages to an index. It does not perform the actual + * writes, but instead constructs an {@link IndexRequest} for the log message and passes that + * to a callback. + */ +@Plugin(name = "DeprecationIndexingAppender", category = Core.CATEGORY_NAME, elementType = Appender.ELEMENT_TYPE) public class DeprecationIndexingAppender extends AbstractAppender { private static final String DATA_STREAM_NAME = "logs-deprecation-elasticsearch"; @@ -30,19 +40,26 @@ public class DeprecationIndexingAppender extends AbstractAppender { private String clusterUUID; private String nodeId; + /** + * You apparently can't start and stop an appender to toggle it, so this flag reflects whether + * writes should in fact be carried out. + */ private volatile boolean isEnabled = false; - public DeprecationIndexingAppender( - Consumer requestConsumer, - String name, - Filter filter - ) { + /** + * Creates a new appender. + * @param name the appender's name + * @param filter any filter to apply directly on the appender + * @param requestConsumer a callback to handle the actual indexing of the log message. + */ + public DeprecationIndexingAppender(String name, Filter filter, Consumer requestConsumer) { super(name, filter, null); - this.requestConsumer = requestConsumer; + this.requestConsumer = Objects.requireNonNull(requestConsumer, "requestConsumer cannot be null"); } /** - * Indexes a deprecation message. + * Constructs an index request for a deprecation message, and supplies it to the callback that was + * supplied to {@link #DeprecationIndexingAppender(String, Filter, Consumer)}. */ @Override public void append(LogEvent event) { @@ -51,28 +68,30 @@ public void append(LogEvent event) { } final Message message = event.getMessage(); - if ((message instanceof ESLogMessage) == false) { - return; - } - - final ESLogMessage esLogMessage = (ESLogMessage) message; - - String xOpaqueId = esLogMessage.get(X_OPAQUE_ID_FIELD_NAME); - final String key = esLogMessage.get("key"); - - String messagePattern = esLogMessage.getMessagePattern(); - Object[] arguments = esLogMessage.getArguments(); - String formattedMessage = LoggerMessageFormat.format(messagePattern, arguments); - Map payload = new HashMap<>(); + final Map payload = new HashMap<>(); payload.put("@timestamp", Instant.now().toString()); - payload.put("key", key); - payload.put("message", formattedMessage); payload.put("cluster.uuid", clusterUUID); payload.put("node.id", nodeId); - if (isNullOrEmpty(xOpaqueId) == false) { - payload.put("x-opaque-id", xOpaqueId); + if (message instanceof ESLogMessage) { + final ESLogMessage esLogMessage = (ESLogMessage) message; + + String xOpaqueId = esLogMessage.get(X_OPAQUE_ID_FIELD_NAME); + final String key = esLogMessage.get("key"); + + String messagePattern = esLogMessage.getMessagePattern(); + Object[] arguments = esLogMessage.getArguments(); + String formattedMessage = LoggerMessageFormat.format(messagePattern, arguments); + + payload.put("key", key); + payload.put("message", formattedMessage); + + if (isNullOrEmpty(xOpaqueId) == false) { + payload.put("x-opaque-id", xOpaqueId); + } + } else { + payload.put("message", message.getFormattedMessage()); } final IndexRequest request = new IndexRequest(DATA_STREAM_NAME).source(payload).opType(DocWriteRequest.OpType.CREATE); @@ -80,14 +99,26 @@ public void append(LogEvent event) { this.requestConsumer.accept(request); } + /** + * Sets whether this appender is enabled or disabled. + * @param isEnabled the enabled status of the appender. + */ public void setEnabled(boolean isEnabled) { this.isEnabled = isEnabled; } + /** + * Sets the current cluster UUID. This is included in the indexed document. + * @param clusterUUID the cluster UUID to set. + */ public void setClusterUUID(String clusterUUID) { this.clusterUUID = clusterUUID; } + /** + * Sets the current node ID. This is included in the indexed document. + * @param nodeId the node ID to set. + */ public void setNodeId(String nodeId) { this.nodeId = nodeId; } diff --git a/x-pack/plugin/deprecation/src/main/java/org/elasticsearch/xpack/deprecation/logging/DeprecationIndexingComponent.java b/x-pack/plugin/deprecation/src/main/java/org/elasticsearch/xpack/deprecation/logging/DeprecationIndexingComponent.java index 2665152a75831..b7e5566fb7298 100644 --- a/x-pack/plugin/deprecation/src/main/java/org/elasticsearch/xpack/deprecation/logging/DeprecationIndexingComponent.java +++ b/x-pack/plugin/deprecation/src/main/java/org/elasticsearch/xpack/deprecation/logging/DeprecationIndexingComponent.java @@ -4,25 +4,6 @@ * you may not use this file except in compliance with the Elastic License. */ -/* - * Licensed to Elasticsearch under one or more contributor - * license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright - * ownership. Elasticsearch 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. - */ - package org.elasticsearch.xpack.deprecation.logging; import org.apache.logging.log4j.LogManager; @@ -50,6 +31,10 @@ import static org.elasticsearch.common.logging.DeprecationLogger.DEPRECATION_ONLY_FILTER; +/** + * This component manages the construction and lifecycle of the {@link DeprecationIndexingAppender}. + * It also starts and stops the appender + */ public class DeprecationIndexingComponent extends AbstractLifecycleComponent implements ClusterStateListener { private static final Logger logger = LogManager.getLogger(DeprecationIndexingComponent.class); @@ -65,9 +50,8 @@ public class DeprecationIndexingComponent extends AbstractLifecycleComponent imp public DeprecationIndexingComponent(ThreadPool threadPool, Client client) { final Consumer consumer = buildIndexRequestConsumer(threadPool, client); final Filter filter = UnionFilter.createFilters(DEPRECATION_ONLY_FILTER, new RateLimitingFilter()); - filter.start(); - this.appender = new DeprecationIndexingAppender(consumer, "DeprecationIndexer", filter); + this.appender = new DeprecationIndexingAppender("DeprecationIndexer", filter, consumer); } @Override @@ -79,7 +63,6 @@ protected void doStart() { @Override protected void doStop() { Loggers.addAppender(LogManager.getLogger("org.elasticsearch.deprecation"), this.appender); - this.appender.stop(); } @Override @@ -91,6 +74,8 @@ protected void doClose() { * Listens for changes to the cluster state, in order to know whether to toggle indexing * and to set the cluster UUID and node ID. These can't be set in the constructor because * the initial cluster state won't be set yet. + * + * @param event the cluster state event to process */ @Override public void clusterChanged(ClusterChangedEvent event) { diff --git a/x-pack/plugin/deprecation/src/test/java/org/elasticsearch/xpack/deprecation/DeprecationIndexingAppenderTests.java b/x-pack/plugin/deprecation/src/test/java/org/elasticsearch/xpack/deprecation/DeprecationIndexingAppenderTests.java index fceee2fa38e63..1ccc3c7f7d58e 100644 --- a/x-pack/plugin/deprecation/src/test/java/org/elasticsearch/xpack/deprecation/DeprecationIndexingAppenderTests.java +++ b/x-pack/plugin/deprecation/src/test/java/org/elasticsearch/xpack/deprecation/DeprecationIndexingAppenderTests.java @@ -36,7 +36,7 @@ public class DeprecationIndexingAppenderTests extends ESTestCase { @SuppressWarnings("unchecked") public void initialize() { consumer = mock(Consumer.class); - appender = new DeprecationIndexingAppender(consumer, "a name", null); + appender = new DeprecationIndexingAppender("a name", null, consumer); appender.setClusterUUID("cluster-uuid"); appender.setNodeId("local-node-id"); From 95e3fc3daab8fd66580303c5f804780393dec71c Mon Sep 17 00:00:00 2001 From: Rory Hunter Date: Tue, 21 Jul 2020 15:47:01 +0100 Subject: [PATCH 25/48] More polishing --- .../common/logging/EvilLoggerTests.java | 2 - .../logging/RateLimitingFilterTests.java | 75 +++++- .../common/logging/UnionFilterTests.java | 219 ++++++++++++++++++ .../xpack/deprecation/Deprecation.java | 23 +- .../logging/DeprecationIndexingAppender.java | 2 +- 5 files changed, 304 insertions(+), 17 deletions(-) create mode 100644 server/src/test/java/org/elasticsearch/common/logging/UnionFilterTests.java diff --git a/qa/evil-tests/src/test/java/org/elasticsearch/common/logging/EvilLoggerTests.java b/qa/evil-tests/src/test/java/org/elasticsearch/common/logging/EvilLoggerTests.java index 7858367d6ae63..d0d5f062cf6ef 100644 --- a/qa/evil-tests/src/test/java/org/elasticsearch/common/logging/EvilLoggerTests.java +++ b/qa/evil-tests/src/test/java/org/elasticsearch/common/logging/EvilLoggerTests.java @@ -52,8 +52,6 @@ import java.util.Set; import java.util.concurrent.BrokenBarrierException; import java.util.concurrent.CyclicBarrier; -import java.util.concurrent.TimeUnit; -import java.util.concurrent.TimeoutException; import java.util.regex.Matcher; import java.util.regex.Pattern; import java.util.stream.Collectors; diff --git a/server/src/test/java/org/elasticsearch/common/logging/RateLimitingFilterTests.java b/server/src/test/java/org/elasticsearch/common/logging/RateLimitingFilterTests.java index 19161c6bdc78c..5a919a59ce5fe 100644 --- a/server/src/test/java/org/elasticsearch/common/logging/RateLimitingFilterTests.java +++ b/server/src/test/java/org/elasticsearch/common/logging/RateLimitingFilterTests.java @@ -43,7 +43,10 @@ public void cleanup() { this.filter.stop(); } - public void testRateLimitingFilter() { + /** + * Check that messages are rate-limited by their key. + */ + public void testMessagesAreRateLimitedByKey() { // Fill up the cache for (int i = 0; i < 128; i++) { Message message = DeprecatedMessage.of("", "msg " + i).field("key", "key" + i); @@ -63,6 +66,76 @@ public void testRateLimitingFilter() { assertThat(filter.filter(message), equalTo(Result.ACCEPT)); } + /** + * Check that messages are rate-limited by their x-opaque-id value + */ + public void testMessagesAreRateLimitedByXOpaqueId() { + // Fill up the cache + for (int i = 0; i < 128; i++) { + Message message = DeprecatedMessage.of("key " + i, "msg " + i); + assertThat("Expected key" + i + " to be accepted", filter.filter(message), equalTo(Result.ACCEPT)); + } + + // Should be rate-limited because it's still in the cache + Message message = DeprecatedMessage.of("key 0", "msg 0"); + assertThat(filter.filter(message), equalTo(Result.DENY)); + + // Filter a message with a previously unseen key, in order to evict key0 as it's the oldest + message = DeprecatedMessage.of("key 129", "msg 129"); + assertThat(filter.filter(message), equalTo(Result.ACCEPT)); + + // Should be allowed because key0 was evicted from the cache + message = DeprecatedMessage.of("key 0", "msg 0"); + assertThat(filter.filter(message), equalTo(Result.ACCEPT)); + } + + /** + * Check that messages are rate-limited by their key and x-opaque-id value + */ + public void testMessagesAreRateLimitedByKeyAndXOpaqueId() { + // Fill up the cache + for (int i = 0; i < 128; i++) { + Message message = DeprecatedMessage.of("opaque-id " + i, "msg " + i).field("key", "key " + i); + assertThat("Expected key" + i + " to be accepted", filter.filter(message), equalTo(Result.ACCEPT)); + } + + // Should be rate-limited because it's still in the cache + Message message = DeprecatedMessage.of("opaque-id 0", "msg 0").field("key", "key 0"); + assertThat(filter.filter(message), equalTo(Result.DENY)); + + // Filter a message with a previously unseen key, in order to evict key0 as it's the oldest + message = DeprecatedMessage.of("opaque-id 129", "msg 129").field("key", "key 129"); + assertThat(filter.filter(message), equalTo(Result.ACCEPT)); + + // Should be allowed because key0 was evicted from the cache + message = DeprecatedMessage.of("opaque-id 0", "msg 0").field("key", "key 0"); + assertThat(filter.filter(message), equalTo(Result.ACCEPT)); + } + + /** + * Check that it is the combination of key and x-opaque-id that rate-limits messages, by varying each + * independently and checking that a message is not filtered. + */ + public void testVariationsInKeyAndXOpaqueId() { + Message message = DeprecatedMessage.of("opaque-id 0", "msg 0").field("key", "key 0"); + assertThat(filter.filter(message), equalTo(Result.ACCEPT)); + + message = DeprecatedMessage.of("opaque-id 0", "msg 0").field("key", "key 0"); + // Rejected because the "x-opaque-id" and "key" values are the same as above + assertThat(filter.filter(message), equalTo(Result.DENY)); + + message = DeprecatedMessage.of("opaque-id 0", "msg 0").field("key", "key 1"); + // Accepted because the "key" value is different + assertThat(filter.filter(message), equalTo(Result.ACCEPT)); + + message = DeprecatedMessage.of("opaque-id 1", "msg 0").field("key", "key 0"); + // Accepted because the "x-opaque-id" value is different + assertThat(filter.filter(message), equalTo(Result.ACCEPT)); + } + + /** + * Check that rate-limiting is not applied to messages if they are not an EsLogMessage. + */ public void testOnlyEsMessagesAreFiltered() { Message message = new SimpleMessage("a message"); assertThat(filter.filter(message), equalTo(Result.NEUTRAL)); diff --git a/server/src/test/java/org/elasticsearch/common/logging/UnionFilterTests.java b/server/src/test/java/org/elasticsearch/common/logging/UnionFilterTests.java new file mode 100644 index 0000000000000..f204b0b594734 --- /dev/null +++ b/server/src/test/java/org/elasticsearch/common/logging/UnionFilterTests.java @@ -0,0 +1,219 @@ +/* + * Licensed to Elasticsearch under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch 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. + */ + +package org.elasticsearch.common.logging; + +import org.apache.logging.log4j.Level; +import org.apache.logging.log4j.Marker; +import org.apache.logging.log4j.core.Filter; +import org.apache.logging.log4j.core.LogEvent; +import org.apache.logging.log4j.core.Logger; +import org.apache.logging.log4j.core.filter.AbstractFilter; +import org.apache.logging.log4j.message.Message; +import org.elasticsearch.test.ESTestCase; + +import static org.hamcrest.Matchers.equalTo; +import static org.mockito.Mockito.mock; + +public class UnionFilterTests extends ESTestCase { + + /** + * Check that if all child filters accepts an event, then the union filter will accept it. + */ + public void testLogEventFilterAcceptsIfAllChildFiltersAccepts() { + Filter unionFilter = UnionFilter.createFilters(new ConstantFilter(Filter.Result.ACCEPT), new ConstantFilter(Filter.Result.ACCEPT)); + assertThat(unionFilter.filter(mock(LogEvent.class)), equalTo(Filter.Result.ACCEPT)); + } + + /** + * Check that if all child filters accepts a message, then the union filter will accept it. + */ + public void testMessageFilterAcceptsIfAllChildFiltersAccepts() { + Filter unionFilter = UnionFilter.createFilters(new ConstantFilter(Filter.Result.ACCEPT), new ConstantFilter(Filter.Result.ACCEPT)); + assertThat( + unionFilter.filter(mock(Logger.class), Level.WARN, mock(Marker.class), mock(Message.class), mock(Throwable.class)), + equalTo(Filter.Result.ACCEPT) + ); + } + + /** + * Check that if one child filter rejects an event, then the union filter will reject it. + */ + public void testLogEventFilterRejectsIfOneChildFiltersAccepts() { + Filter unionFilter = UnionFilter.createFilters(new ConstantFilter(Filter.Result.ACCEPT), new ConstantFilter(Filter.Result.DENY)); + assertThat(unionFilter.filter(mock(LogEvent.class)), equalTo(Filter.Result.DENY)); + } + + /** + * Check that if one child filters rejects a message, then the union filter will reject it. + */ + public void testMessageFilterRejectsIfOneChildFiltersAccepts() { + Filter unionFilter = UnionFilter.createFilters(new ConstantFilter(Filter.Result.ACCEPT), new ConstantFilter(Filter.Result.DENY)); + assertThat( + unionFilter.filter(mock(Logger.class), Level.WARN, mock(Marker.class), mock(Message.class), mock(Throwable.class)), + equalTo(Filter.Result.DENY) + ); + } + + /** + * A simple filter for testing that always yields the same result. + */ + private static class ConstantFilter extends AbstractFilter { + private final Result result; + + public ConstantFilter(Result result) { + this.result = result; + } + + @Override + public Result filter(LogEvent event) { + return this.result; + } + + @Override + public Result filter(Logger logger, Level level, Marker marker, Message msg, Throwable t) { + return this.result; + } + + @Override + public Result filter(Logger logger, Level level, Marker marker, Object msg, Throwable t) { + return this.result; + } + + @Override + public Result filter(Logger logger, Level level, Marker marker, String msg, Object... params) { + return this.result; + } + + @Override + public Result filter(Logger logger, Level level, Marker marker, String msg, Object p0) { + return this.result; + } + + @Override + public Result filter(Logger logger, Level level, Marker marker, String msg, Object p0, Object p1) { + return this.result; + } + + @Override + public Result filter(Logger logger, Level level, Marker marker, String msg, Object p0, Object p1, Object p2) { + return this.result; + } + + @Override + public Result filter(Logger logger, Level level, Marker marker, String msg, Object p0, Object p1, Object p2, Object p3) { + return this.result; + } + + @Override + public Result filter(Logger logger, Level level, Marker marker, String msg, Object p0, Object p1, Object p2, Object p3, Object p4) { + return this.result; + } + + @Override + public Result filter( + Logger logger, + Level level, + Marker marker, + String msg, + Object p0, + Object p1, + Object p2, + Object p3, + Object p4, + Object p5 + ) { + return this.result; + } + + @Override + public Result filter( + Logger logger, + Level level, + Marker marker, + String msg, + Object p0, + Object p1, + Object p2, + Object p3, + Object p4, + Object p5, + Object p6 + ) { + return this.result; + } + + @Override + public Result filter( + Logger logger, + Level level, + Marker marker, + String msg, + Object p0, + Object p1, + Object p2, + Object p3, + Object p4, + Object p5, + Object p6, + Object p7 + ) { + return this.result; + } + + @Override + public Result filter( + Logger logger, + Level level, + Marker marker, + String msg, + Object p0, + Object p1, + Object p2, + Object p3, + Object p4, + Object p5, + Object p6, + Object p7, + Object p8 + ) { + return this.result; + } + + @Override + public Result filter( + Logger logger, + Level level, + Marker marker, + String msg, + Object p0, + Object p1, + Object p2, + Object p3, + Object p4, + Object p5, + Object p6, + Object p7, + Object p8, + Object p9 + ) { + return this.result; + } + } +} diff --git a/x-pack/plugin/deprecation/src/main/java/org/elasticsearch/xpack/deprecation/Deprecation.java b/x-pack/plugin/deprecation/src/main/java/org/elasticsearch/xpack/deprecation/Deprecation.java index 0d21cf6e175ce..2415c71fddf02 100644 --- a/x-pack/plugin/deprecation/src/main/java/org/elasticsearch/xpack/deprecation/Deprecation.java +++ b/x-pack/plugin/deprecation/src/main/java/org/elasticsearch/xpack/deprecation/Deprecation.java @@ -33,6 +33,7 @@ import org.elasticsearch.xpack.deprecation.logging.DeprecationIndexingComponent; import java.util.Collection; +import java.util.Collections; import java.util.List; import java.util.function.Supplier; @@ -46,22 +47,18 @@ public class Deprecation extends Plugin implements ActionPlugin { @Override public List> getActions() { return List.of( - new ActionHandler<>(DeprecationInfoAction.INSTANCE, TransportDeprecationInfoAction.class), - new ActionHandler<>(NodesDeprecationCheckAction.INSTANCE, TransportNodeDeprecationCheckAction.class) - ); + new ActionHandler<>(DeprecationInfoAction.INSTANCE, TransportDeprecationInfoAction.class), + new ActionHandler<>(NodesDeprecationCheckAction.INSTANCE, TransportNodeDeprecationCheckAction.class)); } @Override - public List getRestHandlers( - Settings settings, - RestController restController, - ClusterSettings clusterSettings, - IndexScopedSettings indexScopedSettings, - SettingsFilter settingsFilter, - IndexNameExpressionResolver indexNameExpressionResolver, - Supplier nodesInCluster - ) { - return List.of(new RestDeprecationInfoAction()); + public List getRestHandlers(Settings settings, RestController restController, ClusterSettings clusterSettings, + IndexScopedSettings indexScopedSettings, SettingsFilter settingsFilter, + IndexNameExpressionResolver indexNameExpressionResolver, + Supplier nodesInCluster) { + + + return Collections.singletonList(new RestDeprecationInfoAction()); } @Override diff --git a/x-pack/plugin/deprecation/src/main/java/org/elasticsearch/xpack/deprecation/logging/DeprecationIndexingAppender.java b/x-pack/plugin/deprecation/src/main/java/org/elasticsearch/xpack/deprecation/logging/DeprecationIndexingAppender.java index 9d6e6f3296328..0f286f92a3416 100644 --- a/x-pack/plugin/deprecation/src/main/java/org/elasticsearch/xpack/deprecation/logging/DeprecationIndexingAppender.java +++ b/x-pack/plugin/deprecation/src/main/java/org/elasticsearch/xpack/deprecation/logging/DeprecationIndexingAppender.java @@ -41,7 +41,7 @@ public class DeprecationIndexingAppender extends AbstractAppender { private String nodeId; /** - * You apparently can't start and stop an appender to toggle it, so this flag reflects whether + * You can't start and stop an appender to toggle it, so this flag reflects whether * writes should in fact be carried out. */ private volatile boolean isEnabled = false; From c0b8f65573ae591f624cd03213ceba1b9009c7b8 Mon Sep 17 00:00:00 2001 From: Rory Hunter Date: Tue, 21 Jul 2020 16:33:27 +0100 Subject: [PATCH 26/48] Include key in DeprecatedMessage.of(...) --- .../common/logging/JsonLoggerTests.java | 8 ++--- .../common/logging/DeprecatedMessage.java | 5 +-- .../common/logging/DeprecationLogger.java | 2 +- .../logging/RateLimitingFilterTests.java | 34 +++++++++---------- .../logging/DeprecationIndexingAppender.java | 10 ++---- .../DeprecationIndexingAppenderTests.java | 12 +++---- 6 files changed, 33 insertions(+), 38 deletions(-) diff --git a/qa/logging-config/src/test/java/org/elasticsearch/common/logging/JsonLoggerTests.java b/qa/logging-config/src/test/java/org/elasticsearch/common/logging/JsonLoggerTests.java index bb87d386c27eb..2cd1b98d3bbaa 100644 --- a/qa/logging-config/src/test/java/org/elasticsearch/common/logging/JsonLoggerTests.java +++ b/qa/logging-config/src/test/java/org/elasticsearch/common/logging/JsonLoggerTests.java @@ -88,7 +88,7 @@ public void tearDown() throws Exception { public void testDeprecatedMessage() throws IOException { final Logger testLogger = LogManager.getLogger("deprecation.test"); - testLogger.log(DEPRECATION, DeprecatedMessage.of("someId","deprecated message1")); + testLogger.log(DEPRECATION, DeprecatedMessage.of("someKey", "someId","deprecated message1")); final Path path = PathUtils.get(System.getProperty("es.logs.base_path"), System.getProperty("es.logs.cluster_name") + "_deprecated.json"); @@ -173,10 +173,10 @@ public void testCustomMessageWithMultipleFields() throws IOException { public void testDeprecatedMessageWithoutXOpaqueId() throws IOException { final Logger testLogger = LogManager.getLogger("deprecation.test"); - testLogger.log(DEPRECATION, DeprecatedMessage.of("someOtherId","deprecated message1").field("key", "a key")); - testLogger.log(DEPRECATION, DeprecatedMessage.of("","deprecated message2").field("key", "a key")); + testLogger.log(DEPRECATION, DeprecatedMessage.of("a key", "someOtherId","deprecated message1")); + testLogger.log(DEPRECATION, DeprecatedMessage.of("a key", "", "deprecated message2")); // This next message will be filtered out, because a null opaque ID has the same effect as an empty ID. - testLogger.log(DEPRECATION, DeprecatedMessage.of(null,"deprecated message3").field("key", "a key")); + testLogger.log(DEPRECATION, DeprecatedMessage.of("a key", null,"deprecated message3")); testLogger.log(DEPRECATION,"deprecated message4"); final Path path = PathUtils.get(System.getProperty("es.logs.base_path"), diff --git a/server/src/main/java/org/elasticsearch/common/logging/DeprecatedMessage.java b/server/src/main/java/org/elasticsearch/common/logging/DeprecatedMessage.java index 95dd0755a25a0..b6d3cf3d7eedf 100644 --- a/server/src/main/java/org/elasticsearch/common/logging/DeprecatedMessage.java +++ b/server/src/main/java/org/elasticsearch/common/logging/DeprecatedMessage.java @@ -31,9 +31,9 @@ public class DeprecatedMessage { public static final String X_OPAQUE_ID_FIELD_NAME = "x-opaque-id"; @SuppressLoggerChecks(reason = "safely delegates to logger") - public static ESLogMessage of(String xOpaqueId, String messagePattern, Object... args){ + public static ESLogMessage of(String key, String xOpaqueId, String messagePattern, Object... args){ if (Strings.isNullOrEmpty(xOpaqueId)) { - return new ESLogMessage(messagePattern, args); + return new ESLogMessage(messagePattern, args).field("key", key); } Object value = new Object() { @@ -44,6 +44,7 @@ public String toString() { } }; return new ESLogMessage(messagePattern, args) + .field("key", key) .field("message", value) .field(X_OPAQUE_ID_FIELD_NAME, xOpaqueId); } diff --git a/server/src/main/java/org/elasticsearch/common/logging/DeprecationLogger.java b/server/src/main/java/org/elasticsearch/common/logging/DeprecationLogger.java index bc4b508fb8fa4..866602f448216 100644 --- a/server/src/main/java/org/elasticsearch/common/logging/DeprecationLogger.java +++ b/server/src/main/java/org/elasticsearch/common/logging/DeprecationLogger.java @@ -104,7 +104,7 @@ public DeprecationLoggerBuilder deprecate(final String key, final String msg, fi public class DeprecationLoggerBuilder { public DeprecationLoggerBuilder withDeprecation(String key, String msg, Object[] params) { - ESLogMessage deprecationMessage = DeprecatedMessage.of(HeaderWarning.getXOpaqueId(), msg, params).field("key", key); + ESLogMessage deprecationMessage = DeprecatedMessage.of(key, HeaderWarning.getXOpaqueId(), msg, params); logger.log(DEPRECATION, deprecationMessage); diff --git a/server/src/test/java/org/elasticsearch/common/logging/RateLimitingFilterTests.java b/server/src/test/java/org/elasticsearch/common/logging/RateLimitingFilterTests.java index 5a919a59ce5fe..ded057b9f4162 100644 --- a/server/src/test/java/org/elasticsearch/common/logging/RateLimitingFilterTests.java +++ b/server/src/test/java/org/elasticsearch/common/logging/RateLimitingFilterTests.java @@ -49,20 +49,20 @@ public void cleanup() { public void testMessagesAreRateLimitedByKey() { // Fill up the cache for (int i = 0; i < 128; i++) { - Message message = DeprecatedMessage.of("", "msg " + i).field("key", "key" + i); + Message message = DeprecatedMessage.of("key " + i, "", "msg " + i); assertThat("Expected key" + i + " to be accepted", filter.filter(message), equalTo(Result.ACCEPT)); } // Should be rate-limited because it's still in the cache - Message message = DeprecatedMessage.of("", "msg " + 0).field("key", "key" + 0); + Message message = DeprecatedMessage.of("key 0", "", "msg " + 0); assertThat(filter.filter(message), equalTo(Result.DENY)); // Filter a message with a previously unseen key, in order to evict key0 as it's the oldest - message = DeprecatedMessage.of("", "msg " + 129).field("key", "key" + 129); + message = DeprecatedMessage.of("key 129", "", "msg " + 129); assertThat(filter.filter(message), equalTo(Result.ACCEPT)); // Should be allowed because key0 was evicted from the cache - message = DeprecatedMessage.of("", "msg " + 0).field("key", "key" + 0); + message = DeprecatedMessage.of("key 0", "", "msg " + 0); assertThat(filter.filter(message), equalTo(Result.ACCEPT)); } @@ -72,20 +72,20 @@ public void testMessagesAreRateLimitedByKey() { public void testMessagesAreRateLimitedByXOpaqueId() { // Fill up the cache for (int i = 0; i < 128; i++) { - Message message = DeprecatedMessage.of("key " + i, "msg " + i); + Message message = DeprecatedMessage.of("", "id " + i, "msg " + i); assertThat("Expected key" + i + " to be accepted", filter.filter(message), equalTo(Result.ACCEPT)); } // Should be rate-limited because it's still in the cache - Message message = DeprecatedMessage.of("key 0", "msg 0"); + Message message = DeprecatedMessage.of("", "id 0", "msg 0"); assertThat(filter.filter(message), equalTo(Result.DENY)); // Filter a message with a previously unseen key, in order to evict key0 as it's the oldest - message = DeprecatedMessage.of("key 129", "msg 129"); + message = DeprecatedMessage.of("", "id 129", "msg 129"); assertThat(filter.filter(message), equalTo(Result.ACCEPT)); // Should be allowed because key0 was evicted from the cache - message = DeprecatedMessage.of("key 0", "msg 0"); + message = DeprecatedMessage.of("", "id 0", "msg 0"); assertThat(filter.filter(message), equalTo(Result.ACCEPT)); } @@ -95,20 +95,20 @@ public void testMessagesAreRateLimitedByXOpaqueId() { public void testMessagesAreRateLimitedByKeyAndXOpaqueId() { // Fill up the cache for (int i = 0; i < 128; i++) { - Message message = DeprecatedMessage.of("opaque-id " + i, "msg " + i).field("key", "key " + i); + Message message = DeprecatedMessage.of("key " + i, "opaque-id " + i, "msg " + i); assertThat("Expected key" + i + " to be accepted", filter.filter(message), equalTo(Result.ACCEPT)); } // Should be rate-limited because it's still in the cache - Message message = DeprecatedMessage.of("opaque-id 0", "msg 0").field("key", "key 0"); + Message message = DeprecatedMessage.of("key 0", "opaque-id 0", "msg 0"); assertThat(filter.filter(message), equalTo(Result.DENY)); // Filter a message with a previously unseen key, in order to evict key0 as it's the oldest - message = DeprecatedMessage.of("opaque-id 129", "msg 129").field("key", "key 129"); + message = DeprecatedMessage.of("key 129", "opaque-id 129", "msg 129"); assertThat(filter.filter(message), equalTo(Result.ACCEPT)); - // Should be allowed because key0 was evicted from the cache - message = DeprecatedMessage.of("opaque-id 0", "msg 0").field("key", "key 0"); + // Should be allowed because key 0 was evicted from the cache + message = DeprecatedMessage.of("key 0", "opaque-id 0", "msg 0"); assertThat(filter.filter(message), equalTo(Result.ACCEPT)); } @@ -117,18 +117,18 @@ public void testMessagesAreRateLimitedByKeyAndXOpaqueId() { * independently and checking that a message is not filtered. */ public void testVariationsInKeyAndXOpaqueId() { - Message message = DeprecatedMessage.of("opaque-id 0", "msg 0").field("key", "key 0"); + Message message = DeprecatedMessage.of("key 0", "opaque-id 0", "msg 0"); assertThat(filter.filter(message), equalTo(Result.ACCEPT)); - message = DeprecatedMessage.of("opaque-id 0", "msg 0").field("key", "key 0"); + message = DeprecatedMessage.of("key 0", "opaque-id 0", "msg 0"); // Rejected because the "x-opaque-id" and "key" values are the same as above assertThat(filter.filter(message), equalTo(Result.DENY)); - message = DeprecatedMessage.of("opaque-id 0", "msg 0").field("key", "key 1"); + message = DeprecatedMessage.of("key 1", "opaque-id 0", "msg 0"); // Accepted because the "key" value is different assertThat(filter.filter(message), equalTo(Result.ACCEPT)); - message = DeprecatedMessage.of("opaque-id 1", "msg 0").field("key", "key 0"); + message = DeprecatedMessage.of("key 0", "opaque-id 1", "msg 0"); // Accepted because the "x-opaque-id" value is different assertThat(filter.filter(message), equalTo(Result.ACCEPT)); } diff --git a/x-pack/plugin/deprecation/src/main/java/org/elasticsearch/xpack/deprecation/logging/DeprecationIndexingAppender.java b/x-pack/plugin/deprecation/src/main/java/org/elasticsearch/xpack/deprecation/logging/DeprecationIndexingAppender.java index 0f286f92a3416..792de3e785c59 100644 --- a/x-pack/plugin/deprecation/src/main/java/org/elasticsearch/xpack/deprecation/logging/DeprecationIndexingAppender.java +++ b/x-pack/plugin/deprecation/src/main/java/org/elasticsearch/xpack/deprecation/logging/DeprecationIndexingAppender.java @@ -13,6 +13,7 @@ import org.apache.logging.log4j.core.appender.AbstractAppender; import org.apache.logging.log4j.core.config.plugins.Plugin; import org.apache.logging.log4j.message.Message; +import org.apache.logging.log4j.message.ParameterizedMessage; import org.elasticsearch.action.DocWriteRequest; import org.elasticsearch.action.index.IndexRequest; import org.elasticsearch.common.logging.ESLogMessage; @@ -78,14 +79,9 @@ public void append(LogEvent event) { final ESLogMessage esLogMessage = (ESLogMessage) message; String xOpaqueId = esLogMessage.get(X_OPAQUE_ID_FIELD_NAME); - final String key = esLogMessage.get("key"); - String messagePattern = esLogMessage.getMessagePattern(); - Object[] arguments = esLogMessage.getArguments(); - String formattedMessage = LoggerMessageFormat.format(messagePattern, arguments); - - payload.put("key", key); - payload.put("message", formattedMessage); + payload.put("key", esLogMessage.get("key")); + payload.put("message", esLogMessage.get("message")); if (isNullOrEmpty(xOpaqueId) == false) { payload.put("x-opaque-id", xOpaqueId); diff --git a/x-pack/plugin/deprecation/src/test/java/org/elasticsearch/xpack/deprecation/DeprecationIndexingAppenderTests.java b/x-pack/plugin/deprecation/src/test/java/org/elasticsearch/xpack/deprecation/DeprecationIndexingAppenderTests.java index 1ccc3c7f7d58e..cec5c28ba0160 100644 --- a/x-pack/plugin/deprecation/src/test/java/org/elasticsearch/xpack/deprecation/DeprecationIndexingAppenderTests.java +++ b/x-pack/plugin/deprecation/src/test/java/org/elasticsearch/xpack/deprecation/DeprecationIndexingAppenderTests.java @@ -70,7 +70,7 @@ public void testDoesNotWriteMessageWhenServiceEnabledAndDisabled() { public void testWritesMessageWhenServiceEnabled() { appender.setEnabled(true); - final Map payloadMap = getWriteRequest("a key", DeprecatedMessage.of(null, "a message")); + final Map payloadMap = getWriteRequest(DeprecatedMessage.of("a key", null, "a message")); assertThat(payloadMap, hasKey("@timestamp")); assertThat(payloadMap, hasEntry("key", "a key")); @@ -87,7 +87,7 @@ public void testWritesMessageWhenServiceEnabled() { public void testMessageIncludesOpaqueIdWhenSupplied() { appender.setEnabled(true); - final Map payloadMap = getWriteRequest("a key", DeprecatedMessage.of("an ID", "a message")); + final Map payloadMap = getWriteRequest(DeprecatedMessage.of("a key", "an ID", "a message")); assertThat(payloadMap, hasEntry("x-opaque-id", "an ID")); } @@ -99,8 +99,7 @@ public void testMessageSubstitutesArgumentsWhenSupplied() { appender.setEnabled(true); final Map payloadMap = getWriteRequest( - "a key", - DeprecatedMessage.of(null, "a {} and {} message", "first", "second") + DeprecatedMessage.of("a key", null, "a {} and {} message", "first", "second") ); assertThat(payloadMap, hasEntry("message", "a first and second message")); @@ -109,8 +108,7 @@ public void testMessageSubstitutesArgumentsWhenSupplied() { /* * Wraps up the steps for extracting an index request payload from the mocks. */ - private Map getWriteRequest(String key, ESLogMessage message) { - message.field("key", key); + private Map getWriteRequest(ESLogMessage message) { LogEvent logEvent = mock(LogEvent.class); when(logEvent.getMessage()).thenReturn(message); @@ -126,7 +124,7 @@ private Map getWriteRequest(String key, ESLogMessage message) { private LogEvent buildEvent() { LogEvent logEvent = mock(LogEvent.class); - when(logEvent.getMessage()).thenReturn(DeprecatedMessage.of(null, "a message").field("key", "a key")); + when(logEvent.getMessage()).thenReturn(DeprecatedMessage.of("a key", null, "a message")); return logEvent; } } From d6a75bdcd845cd5f97689049c775e7d6bfff623b Mon Sep 17 00:00:00 2001 From: Rory Hunter Date: Wed, 22 Jul 2020 16:18:34 +0100 Subject: [PATCH 27/48] Strip out UnionFilter --- .../common/logging/DeprecationLogger.java | 10 - .../common/logging/UnionFilter.java | 347 ------------------ .../common/logging/UnionFilterTests.java | 219 ----------- .../logging/DeprecationIndexingComponent.java | 10 +- 4 files changed, 3 insertions(+), 583 deletions(-) delete mode 100644 server/src/main/java/org/elasticsearch/common/logging/UnionFilter.java delete mode 100644 server/src/test/java/org/elasticsearch/common/logging/UnionFilterTests.java diff --git a/server/src/main/java/org/elasticsearch/common/logging/DeprecationLogger.java b/server/src/main/java/org/elasticsearch/common/logging/DeprecationLogger.java index 866602f448216..988be8c5410d0 100644 --- a/server/src/main/java/org/elasticsearch/common/logging/DeprecationLogger.java +++ b/server/src/main/java/org/elasticsearch/common/logging/DeprecationLogger.java @@ -46,16 +46,6 @@ public class DeprecationLogger { */ public static Level DEPRECATION = Level.forName("DEPRECATION", Level.WARN.intLevel() + 1); - /** - * A level filter that only accepts messages written at the {@link #DEPRECATION} level. - */ - public static final LevelRangeFilter DEPRECATION_ONLY_FILTER = LevelRangeFilter.createFilter( - DEPRECATION, - DEPRECATION, - Filter.Result.ACCEPT, - Filter.Result.DENY - ); - private final Logger logger; private DeprecationLogger(Logger parentLogger) { diff --git a/server/src/main/java/org/elasticsearch/common/logging/UnionFilter.java b/server/src/main/java/org/elasticsearch/common/logging/UnionFilter.java deleted file mode 100644 index 85cf4f57a30a3..0000000000000 --- a/server/src/main/java/org/elasticsearch/common/logging/UnionFilter.java +++ /dev/null @@ -1,347 +0,0 @@ -/* @notice - * 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. - */ - -package org.elasticsearch.common.logging; - -import org.apache.logging.log4j.Level; -import org.apache.logging.log4j.Marker; -import org.apache.logging.log4j.core.Filter; -import org.apache.logging.log4j.core.LifeCycle2; -import org.apache.logging.log4j.core.LogEvent; -import org.apache.logging.log4j.core.Logger; -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.PluginElement; -import org.apache.logging.log4j.core.config.plugins.PluginFactory; -import org.apache.logging.log4j.core.filter.AbstractFilter; -import org.apache.logging.log4j.message.Message; -import org.apache.logging.log4j.util.PerformanceSensitive; - -import java.util.Objects; -import java.util.concurrent.TimeUnit; - -/** - * This is a fork of {@link org.apache.logging.log4j.core.filter.CompositeFilter}. This class - * also composes and invokes one or more filters, but unlike CompositeFilter, all - * composed filters will be called to check for one that answers {@link Result#DENY}. The - * implementation of CompositeFilter makes it impractical to extend. - */ -@Plugin(name = "filters", category = Node.CATEGORY, printObject = true) -@PerformanceSensitive("allocation") -public final class UnionFilter extends AbstractFilter { - - private final Filter[] filters; - - private UnionFilter(final Filter[] filters) { - this.filters = Objects.requireNonNull(filters, "filters cannot be null"); - } - - @Override - public void start() { - this.setStarting(); - for (final Filter filter : filters) { - filter.start(); - } - this.setStarted(); - } - - @Override - public boolean stop(final long timeout, final TimeUnit timeUnit) { - this.setStopping(); - for (final Filter filter : filters) { - if (filter instanceof LifeCycle2) { - ((LifeCycle2) filter).stop(timeout, timeUnit); - } else { - filter.stop(); - } - } - setStopped(); - return true; - } - - @Override - public Result filter(final Logger logger, final Level level, final Marker marker, final String msg, final Object... params) { - Result result = Result.NEUTRAL; - for (int i = 0; i < filters.length; i++) { - result = filters[i].filter(logger, level, marker, msg, params); - if (result == Result.DENY) { - return result; - } - } - return result; - } - - @Override - public Result filter(final Logger logger, final Level level, final Marker marker, final String msg, final Object p0) { - Result result = Result.NEUTRAL; - for (int i = 0; i < filters.length; i++) { - result = filters[i].filter(logger, level, marker, msg, p0); - if (result == Result.DENY) { - return result; - } - } - return result; - } - - @Override - public Result filter(final Logger logger, final Level level, final Marker marker, final String msg, final Object p0, final Object p1) { - Result result = Result.NEUTRAL; - for (int i = 0; i < filters.length; i++) { - result = filters[i].filter(logger, level, marker, msg, p0, p1); - if (result == Result.DENY) { - return result; - } - } - return result; - } - - @Override - public Result filter( - final Logger logger, - final Level level, - final Marker marker, - final String msg, - final Object p0, - final Object p1, - final Object p2 - ) { - Result result = Result.NEUTRAL; - for (int i = 0; i < filters.length; i++) { - result = filters[i].filter(logger, level, marker, msg, p0, p1, p2); - if (result == Result.DENY) { - return result; - } - } - return result; - } - - @Override - public Result filter( - final Logger logger, - final Level level, - final Marker marker, - final String msg, - final Object p0, - final Object p1, - final Object p2, - final Object p3 - ) { - Result result = Result.NEUTRAL; - for (int i = 0; i < filters.length; i++) { - result = filters[i].filter(logger, level, marker, msg, p0, p1, p2, p3); - if (result == Result.DENY) { - return result; - } - } - return result; - } - - @Override - public Result filter( - final Logger logger, - final Level level, - final Marker marker, - final String msg, - final Object p0, - final Object p1, - final Object p2, - final Object p3, - final Object p4 - ) { - Result result = Result.NEUTRAL; - for (int i = 0; i < filters.length; i++) { - result = filters[i].filter(logger, level, marker, msg, p0, p1, p2, p3, p4); - if (result == Result.DENY) { - return result; - } - } - return result; - } - - @Override - public Result filter( - final Logger logger, - final Level level, - final Marker marker, - final String msg, - final Object p0, - final Object p1, - final Object p2, - final Object p3, - final Object p4, - final Object p5 - ) { - Result result = Result.NEUTRAL; - for (int i = 0; i < filters.length; i++) { - result = filters[i].filter(logger, level, marker, msg, p0, p1, p2, p3, p4, p5); - if (result == Result.DENY) { - return result; - } - } - return result; - } - - @Override - public Result filter( - final Logger logger, - final Level level, - final Marker marker, - final String msg, - final Object p0, - final Object p1, - final Object p2, - final Object p3, - final Object p4, - final Object p5, - final Object p6 - ) { - Result result = Result.NEUTRAL; - for (int i = 0; i < filters.length; i++) { - result = filters[i].filter(logger, level, marker, msg, p0, p1, p2, p3, p4, p5, p6); - if (result == Result.DENY) { - return result; - } - } - return result; - } - - @Override - public Result filter( - final Logger logger, - final Level level, - final Marker marker, - final String msg, - final Object p0, - final Object p1, - final Object p2, - final Object p3, - final Object p4, - final Object p5, - final Object p6, - final Object p7 - ) { - Result result = Result.NEUTRAL; - for (int i = 0; i < filters.length; i++) { - result = filters[i].filter(logger, level, marker, msg, p0, p1, p2, p3, p4, p5, p6, p7); - if (result == Result.DENY) { - return result; - } - } - return result; - } - - @Override - public Result filter( - final Logger logger, - final Level level, - final Marker marker, - final String msg, - final Object p0, - final Object p1, - final Object p2, - final Object p3, - final Object p4, - final Object p5, - final Object p6, - final Object p7, - final Object p8 - ) { - Result result = Result.NEUTRAL; - for (int i = 0; i < filters.length; i++) { - result = filters[i].filter(logger, level, marker, msg, p0, p1, p2, p3, p4, p5, p6, p7, p8); - if (result == Result.DENY) { - return result; - } - } - return result; - } - - @Override - public Result filter( - final Logger logger, - final Level level, - final Marker marker, - final String msg, - final Object p0, - final Object p1, - final Object p2, - final Object p3, - final Object p4, - final Object p5, - final Object p6, - final Object p7, - final Object p8, - final Object p9 - ) { - Result result = Result.NEUTRAL; - for (int i = 0; i < filters.length; i++) { - result = filters[i].filter(logger, level, marker, msg, p0, p1, p2, p3, p4, p5, p6, p7, p8, p9); - if (result == Result.DENY) { - return result; - } - } - return result; - } - - @Override - public Result filter(final Logger logger, final Level level, final Marker marker, final Object msg, final Throwable t) { - Result result = Result.NEUTRAL; - for (int i = 0; i < filters.length; i++) { - result = filters[i].filter(logger, level, marker, msg, t); - if (result == Result.DENY) { - return result; - } - } - return result; - } - - @Override - public Result filter(final Logger logger, final Level level, final Marker marker, final Message msg, final Throwable t) { - Result result = Result.NEUTRAL; - for (int i = 0; i < filters.length; i++) { - result = filters[i].filter(logger, level, marker, msg, t); - if (result == Result.DENY) { - return result; - } - } - return result; - } - - @Override - public Result filter(final LogEvent event) { - Result result = Result.NEUTRAL; - for (int i = 0; i < filters.length; i++) { - result = filters[i].filter(event); - if (result == Result.DENY) { - return result; - } - } - return result; - } - - /** - * Creates a UnionFilter. - * - * @param filters An array of Filters to call. - * @return The UnionFilter. - */ - @PluginFactory - public static UnionFilter createFilters(@PluginElement("Filters") final Filter... filters) { - return new UnionFilter(filters); - } - -} diff --git a/server/src/test/java/org/elasticsearch/common/logging/UnionFilterTests.java b/server/src/test/java/org/elasticsearch/common/logging/UnionFilterTests.java deleted file mode 100644 index f204b0b594734..0000000000000 --- a/server/src/test/java/org/elasticsearch/common/logging/UnionFilterTests.java +++ /dev/null @@ -1,219 +0,0 @@ -/* - * Licensed to Elasticsearch under one or more contributor - * license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright - * ownership. Elasticsearch 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. - */ - -package org.elasticsearch.common.logging; - -import org.apache.logging.log4j.Level; -import org.apache.logging.log4j.Marker; -import org.apache.logging.log4j.core.Filter; -import org.apache.logging.log4j.core.LogEvent; -import org.apache.logging.log4j.core.Logger; -import org.apache.logging.log4j.core.filter.AbstractFilter; -import org.apache.logging.log4j.message.Message; -import org.elasticsearch.test.ESTestCase; - -import static org.hamcrest.Matchers.equalTo; -import static org.mockito.Mockito.mock; - -public class UnionFilterTests extends ESTestCase { - - /** - * Check that if all child filters accepts an event, then the union filter will accept it. - */ - public void testLogEventFilterAcceptsIfAllChildFiltersAccepts() { - Filter unionFilter = UnionFilter.createFilters(new ConstantFilter(Filter.Result.ACCEPT), new ConstantFilter(Filter.Result.ACCEPT)); - assertThat(unionFilter.filter(mock(LogEvent.class)), equalTo(Filter.Result.ACCEPT)); - } - - /** - * Check that if all child filters accepts a message, then the union filter will accept it. - */ - public void testMessageFilterAcceptsIfAllChildFiltersAccepts() { - Filter unionFilter = UnionFilter.createFilters(new ConstantFilter(Filter.Result.ACCEPT), new ConstantFilter(Filter.Result.ACCEPT)); - assertThat( - unionFilter.filter(mock(Logger.class), Level.WARN, mock(Marker.class), mock(Message.class), mock(Throwable.class)), - equalTo(Filter.Result.ACCEPT) - ); - } - - /** - * Check that if one child filter rejects an event, then the union filter will reject it. - */ - public void testLogEventFilterRejectsIfOneChildFiltersAccepts() { - Filter unionFilter = UnionFilter.createFilters(new ConstantFilter(Filter.Result.ACCEPT), new ConstantFilter(Filter.Result.DENY)); - assertThat(unionFilter.filter(mock(LogEvent.class)), equalTo(Filter.Result.DENY)); - } - - /** - * Check that if one child filters rejects a message, then the union filter will reject it. - */ - public void testMessageFilterRejectsIfOneChildFiltersAccepts() { - Filter unionFilter = UnionFilter.createFilters(new ConstantFilter(Filter.Result.ACCEPT), new ConstantFilter(Filter.Result.DENY)); - assertThat( - unionFilter.filter(mock(Logger.class), Level.WARN, mock(Marker.class), mock(Message.class), mock(Throwable.class)), - equalTo(Filter.Result.DENY) - ); - } - - /** - * A simple filter for testing that always yields the same result. - */ - private static class ConstantFilter extends AbstractFilter { - private final Result result; - - public ConstantFilter(Result result) { - this.result = result; - } - - @Override - public Result filter(LogEvent event) { - return this.result; - } - - @Override - public Result filter(Logger logger, Level level, Marker marker, Message msg, Throwable t) { - return this.result; - } - - @Override - public Result filter(Logger logger, Level level, Marker marker, Object msg, Throwable t) { - return this.result; - } - - @Override - public Result filter(Logger logger, Level level, Marker marker, String msg, Object... params) { - return this.result; - } - - @Override - public Result filter(Logger logger, Level level, Marker marker, String msg, Object p0) { - return this.result; - } - - @Override - public Result filter(Logger logger, Level level, Marker marker, String msg, Object p0, Object p1) { - return this.result; - } - - @Override - public Result filter(Logger logger, Level level, Marker marker, String msg, Object p0, Object p1, Object p2) { - return this.result; - } - - @Override - public Result filter(Logger logger, Level level, Marker marker, String msg, Object p0, Object p1, Object p2, Object p3) { - return this.result; - } - - @Override - public Result filter(Logger logger, Level level, Marker marker, String msg, Object p0, Object p1, Object p2, Object p3, Object p4) { - return this.result; - } - - @Override - public Result filter( - Logger logger, - Level level, - Marker marker, - String msg, - Object p0, - Object p1, - Object p2, - Object p3, - Object p4, - Object p5 - ) { - return this.result; - } - - @Override - public Result filter( - Logger logger, - Level level, - Marker marker, - String msg, - Object p0, - Object p1, - Object p2, - Object p3, - Object p4, - Object p5, - Object p6 - ) { - return this.result; - } - - @Override - public Result filter( - Logger logger, - Level level, - Marker marker, - String msg, - Object p0, - Object p1, - Object p2, - Object p3, - Object p4, - Object p5, - Object p6, - Object p7 - ) { - return this.result; - } - - @Override - public Result filter( - Logger logger, - Level level, - Marker marker, - String msg, - Object p0, - Object p1, - Object p2, - Object p3, - Object p4, - Object p5, - Object p6, - Object p7, - Object p8 - ) { - return this.result; - } - - @Override - public Result filter( - Logger logger, - Level level, - Marker marker, - String msg, - Object p0, - Object p1, - Object p2, - Object p3, - Object p4, - Object p5, - Object p6, - Object p7, - Object p8, - Object p9 - ) { - return this.result; - } - } -} diff --git a/x-pack/plugin/deprecation/src/main/java/org/elasticsearch/xpack/deprecation/logging/DeprecationIndexingComponent.java b/x-pack/plugin/deprecation/src/main/java/org/elasticsearch/xpack/deprecation/logging/DeprecationIndexingComponent.java index b7e5566fb7298..e0566d6a674c7 100644 --- a/x-pack/plugin/deprecation/src/main/java/org/elasticsearch/xpack/deprecation/logging/DeprecationIndexingComponent.java +++ b/x-pack/plugin/deprecation/src/main/java/org/elasticsearch/xpack/deprecation/logging/DeprecationIndexingComponent.java @@ -8,7 +8,6 @@ import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; -import org.apache.logging.log4j.core.Filter; import org.elasticsearch.action.bulk.BulkProcessor; import org.elasticsearch.action.bulk.BulkRequest; import org.elasticsearch.action.bulk.BulkResponse; @@ -21,7 +20,6 @@ import org.elasticsearch.common.component.AbstractLifecycleComponent; import org.elasticsearch.common.logging.Loggers; import org.elasticsearch.common.logging.RateLimitingFilter; -import org.elasticsearch.common.logging.UnionFilter; import org.elasticsearch.common.settings.Setting; import org.elasticsearch.common.unit.TimeValue; import org.elasticsearch.threadpool.ThreadPool; @@ -29,8 +27,6 @@ import java.util.function.Consumer; -import static org.elasticsearch.common.logging.DeprecationLogger.DEPRECATION_ONLY_FILTER; - /** * This component manages the construction and lifecycle of the {@link DeprecationIndexingAppender}. * It also starts and stops the appender @@ -49,9 +45,8 @@ public class DeprecationIndexingComponent extends AbstractLifecycleComponent imp public DeprecationIndexingComponent(ThreadPool threadPool, Client client) { final Consumer consumer = buildIndexRequestConsumer(threadPool, client); - final Filter filter = UnionFilter.createFilters(DEPRECATION_ONLY_FILTER, new RateLimitingFilter()); - this.appender = new DeprecationIndexingAppender("DeprecationIndexer", filter, consumer); + this.appender = new DeprecationIndexingAppender("DeprecationIndexer", new RateLimitingFilter(), consumer); } @Override @@ -62,7 +57,8 @@ protected void doStart() { @Override protected void doStop() { - Loggers.addAppender(LogManager.getLogger("org.elasticsearch.deprecation"), this.appender); + Loggers.removeAppender(LogManager.getLogger("org.elasticsearch.deprecation"), this.appender); + this.appender.stop(); } @Override From 074ef8d4632a48151bb41d41407830ef27788280 Mon Sep 17 00:00:00 2001 From: Rory Hunter Date: Wed, 22 Jul 2020 16:57:52 +0100 Subject: [PATCH 28/48] Build deprecation msg docs using EcsJsonLayout --- .../logging/DeprecationIndexingAppender.java | 66 +++--------------- .../logging/DeprecationIndexingComponent.java | 13 +++- .../DeprecationIndexingAppenderTests.java | 69 +++---------------- 3 files changed, 32 insertions(+), 116 deletions(-) diff --git a/x-pack/plugin/deprecation/src/main/java/org/elasticsearch/xpack/deprecation/logging/DeprecationIndexingAppender.java b/x-pack/plugin/deprecation/src/main/java/org/elasticsearch/xpack/deprecation/logging/DeprecationIndexingAppender.java index 792de3e785c59..d3ae739055c47 100644 --- a/x-pack/plugin/deprecation/src/main/java/org/elasticsearch/xpack/deprecation/logging/DeprecationIndexingAppender.java +++ b/x-pack/plugin/deprecation/src/main/java/org/elasticsearch/xpack/deprecation/logging/DeprecationIndexingAppender.java @@ -9,25 +9,17 @@ import org.apache.logging.log4j.core.Appender; import org.apache.logging.log4j.core.Core; import org.apache.logging.log4j.core.Filter; +import org.apache.logging.log4j.core.Layout; import org.apache.logging.log4j.core.LogEvent; import org.apache.logging.log4j.core.appender.AbstractAppender; import org.apache.logging.log4j.core.config.plugins.Plugin; -import org.apache.logging.log4j.message.Message; -import org.apache.logging.log4j.message.ParameterizedMessage; import org.elasticsearch.action.DocWriteRequest; import org.elasticsearch.action.index.IndexRequest; -import org.elasticsearch.common.logging.ESLogMessage; -import org.elasticsearch.common.logging.LoggerMessageFormat; +import org.elasticsearch.common.xcontent.XContentType; -import java.time.Instant; -import java.util.HashMap; -import java.util.Map; import java.util.Objects; import java.util.function.Consumer; -import static org.elasticsearch.common.Strings.isNullOrEmpty; -import static org.elasticsearch.common.logging.DeprecatedMessage.X_OPAQUE_ID_FIELD_NAME; - /** * This log4j appender writes deprecation log messages to an index. It does not perform the actual * writes, but instead constructs an {@link IndexRequest} for the log message and passes that @@ -38,8 +30,6 @@ public class DeprecationIndexingAppender extends AbstractAppender { private static final String DATA_STREAM_NAME = "logs-deprecation-elasticsearch"; private final Consumer requestConsumer; - private String clusterUUID; - private String nodeId; /** * You can't start and stop an appender to toggle it, so this flag reflects whether @@ -50,17 +40,18 @@ public class DeprecationIndexingAppender extends AbstractAppender { /** * Creates a new appender. * @param name the appender's name - * @param filter any filter to apply directly on the appender + * @param filter a filter to apply directly on the appender + * @param layout the layout to use for formatting message. It must return a JSON string. * @param requestConsumer a callback to handle the actual indexing of the log message. */ - public DeprecationIndexingAppender(String name, Filter filter, Consumer requestConsumer) { - super(name, filter, null); + public DeprecationIndexingAppender(String name, Filter filter, Layout layout, Consumer requestConsumer) { + super(name, filter, layout); this.requestConsumer = Objects.requireNonNull(requestConsumer, "requestConsumer cannot be null"); } /** - * Constructs an index request for a deprecation message, and supplies it to the callback that was - * supplied to {@link #DeprecationIndexingAppender(String, Filter, Consumer)}. + * Constructs an index request for a deprecation message, and passes it to the callback that was + * supplied to {@link #DeprecationIndexingAppender(String, Filter, Layout, Consumer)}. */ @Override public void append(LogEvent event) { @@ -68,29 +59,10 @@ public void append(LogEvent event) { return; } - final Message message = event.getMessage(); - - final Map payload = new HashMap<>(); - payload.put("@timestamp", Instant.now().toString()); - payload.put("cluster.uuid", clusterUUID); - payload.put("node.id", nodeId); - - if (message instanceof ESLogMessage) { - final ESLogMessage esLogMessage = (ESLogMessage) message; - - String xOpaqueId = esLogMessage.get(X_OPAQUE_ID_FIELD_NAME); + final byte[] payload = this.getLayout().toByteArray(event); - payload.put("key", esLogMessage.get("key")); - payload.put("message", esLogMessage.get("message")); - - if (isNullOrEmpty(xOpaqueId) == false) { - payload.put("x-opaque-id", xOpaqueId); - } - } else { - payload.put("message", message.getFormattedMessage()); - } - - final IndexRequest request = new IndexRequest(DATA_STREAM_NAME).source(payload).opType(DocWriteRequest.OpType.CREATE); + final IndexRequest request = new IndexRequest(DATA_STREAM_NAME).source(payload, XContentType.JSON) + .opType(DocWriteRequest.OpType.CREATE); this.requestConsumer.accept(request); } @@ -102,20 +74,4 @@ public void append(LogEvent event) { public void setEnabled(boolean isEnabled) { this.isEnabled = isEnabled; } - - /** - * Sets the current cluster UUID. This is included in the indexed document. - * @param clusterUUID the cluster UUID to set. - */ - public void setClusterUUID(String clusterUUID) { - this.clusterUUID = clusterUUID; - } - - /** - * Sets the current node ID. This is included in the indexed document. - * @param nodeId the node ID to set. - */ - public void setNodeId(String nodeId) { - this.nodeId = nodeId; - } } diff --git a/x-pack/plugin/deprecation/src/main/java/org/elasticsearch/xpack/deprecation/logging/DeprecationIndexingComponent.java b/x-pack/plugin/deprecation/src/main/java/org/elasticsearch/xpack/deprecation/logging/DeprecationIndexingComponent.java index e0566d6a674c7..d572bf2760162 100644 --- a/x-pack/plugin/deprecation/src/main/java/org/elasticsearch/xpack/deprecation/logging/DeprecationIndexingComponent.java +++ b/x-pack/plugin/deprecation/src/main/java/org/elasticsearch/xpack/deprecation/logging/DeprecationIndexingComponent.java @@ -6,8 +6,11 @@ package org.elasticsearch.xpack.deprecation.logging; +import co.elastic.logging.log4j2.EcsLayout; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; +import org.apache.logging.log4j.core.LoggerContext; +import org.apache.logging.log4j.core.config.Configuration; import org.elasticsearch.action.bulk.BulkProcessor; import org.elasticsearch.action.bulk.BulkRequest; import org.elasticsearch.action.bulk.BulkResponse; @@ -18,6 +21,7 @@ import org.elasticsearch.cluster.ClusterState; import org.elasticsearch.cluster.ClusterStateListener; import org.elasticsearch.common.component.AbstractLifecycleComponent; +import org.elasticsearch.common.logging.ECSJsonLayout; import org.elasticsearch.common.logging.Loggers; import org.elasticsearch.common.logging.RateLimitingFilter; import org.elasticsearch.common.settings.Setting; @@ -46,7 +50,12 @@ public class DeprecationIndexingComponent extends AbstractLifecycleComponent imp public DeprecationIndexingComponent(ThreadPool threadPool, Client client) { final Consumer consumer = buildIndexRequestConsumer(threadPool, client); - this.appender = new DeprecationIndexingAppender("DeprecationIndexer", new RateLimitingFilter(), consumer); + final LoggerContext context = (LoggerContext) LogManager.getContext(false); + final Configuration configuration = context.getConfiguration(); + + final EcsLayout ecsLayout = ECSJsonLayout.newBuilder().setType("deprecation").setConfiguration(configuration).build(); + + this.appender = new DeprecationIndexingAppender("deprecation_indexing_appender", new RateLimitingFilter(), ecsLayout, consumer); } @Override @@ -77,8 +86,6 @@ protected void doClose() { public void clusterChanged(ClusterChangedEvent event) { final ClusterState state = event.state(); appender.setEnabled(WRITE_DEPRECATION_LOGS_TO_INDEX.get(state.getMetadata().settings())); - appender.setClusterUUID(state.getMetadata().clusterUUID()); - appender.setNodeId(state.nodes().getLocalNodeId()); } /** diff --git a/x-pack/plugin/deprecation/src/test/java/org/elasticsearch/xpack/deprecation/DeprecationIndexingAppenderTests.java b/x-pack/plugin/deprecation/src/test/java/org/elasticsearch/xpack/deprecation/DeprecationIndexingAppenderTests.java index cec5c28ba0160..5b13e662dc4e3 100644 --- a/x-pack/plugin/deprecation/src/test/java/org/elasticsearch/xpack/deprecation/DeprecationIndexingAppenderTests.java +++ b/x-pack/plugin/deprecation/src/test/java/org/elasticsearch/xpack/deprecation/DeprecationIndexingAppenderTests.java @@ -6,10 +6,9 @@ package org.elasticsearch.xpack.deprecation; +import org.apache.logging.log4j.core.Layout; import org.apache.logging.log4j.core.LogEvent; import org.elasticsearch.action.index.IndexRequest; -import org.elasticsearch.common.logging.DeprecatedMessage; -import org.elasticsearch.common.logging.ESLogMessage; import org.elasticsearch.test.ESTestCase; import org.elasticsearch.xpack.deprecation.logging.DeprecationIndexingAppender; import org.junit.Before; @@ -19,8 +18,6 @@ import java.util.function.Consumer; import static org.hamcrest.Matchers.hasEntry; -import static org.hamcrest.Matchers.hasKey; -import static org.hamcrest.Matchers.not; import static org.mockito.Matchers.any; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.never; @@ -30,16 +27,15 @@ public class DeprecationIndexingAppenderTests extends ESTestCase { private DeprecationIndexingAppender appender; + private Layout layout; private Consumer consumer; @Before @SuppressWarnings("unchecked") public void initialize() { + layout = mock(Layout.class); consumer = mock(Consumer.class); - appender = new DeprecationIndexingAppender("a name", null, consumer); - - appender.setClusterUUID("cluster-uuid"); - appender.setNodeId("local-node-id"); + appender = new DeprecationIndexingAppender("a name", null, layout, consumer); } /** @@ -47,7 +43,7 @@ public void initialize() { * is disabled. */ public void testDoesNotWriteMessageWhenServiceDisabled() { - appender.append(buildEvent()); + appender.append(mock(LogEvent.class)); verify(consumer, never()).accept(any()); } @@ -59,72 +55,29 @@ public void testDoesNotWriteMessageWhenServiceEnabledAndDisabled() { appender.setEnabled(true); appender.setEnabled(false); - appender.append(buildEvent()); + appender.append(mock(LogEvent.class)); verify(consumer, never()).accept(any()); } /** * Checks that messages are indexed in the correct shape when the service is enabled. + * Formatted is handled entirely by the configured Layout, so that is not verified here. */ public void testWritesMessageWhenServiceEnabled() { appender.setEnabled(true); - final Map payloadMap = getWriteRequest(DeprecatedMessage.of("a key", null, "a message")); - - assertThat(payloadMap, hasKey("@timestamp")); - assertThat(payloadMap, hasEntry("key", "a key")); - assertThat(payloadMap, hasEntry("message", "a message")); - assertThat(payloadMap, hasEntry("cluster.uuid", "cluster-uuid")); - assertThat(payloadMap, hasEntry("node.id", "local-node-id")); - // Neither of these should exist since we passed null when writing the message - assertThat(payloadMap, not(hasKey("x-opaque-id"))); - } - - /** - * Check that if an xOpaqueId is set, then it is added to the index request payload. - */ - public void testMessageIncludesOpaqueIdWhenSupplied() { - appender.setEnabled(true); - - final Map payloadMap = getWriteRequest(DeprecatedMessage.of("a key", "an ID", "a message")); - - assertThat(payloadMap, hasEntry("x-opaque-id", "an ID")); - } - - /** - * Check that if any arguments are set, then they substituted in the log message - */ - public void testMessageSubstitutesArgumentsWhenSupplied() { - appender.setEnabled(true); - - final Map payloadMap = getWriteRequest( - DeprecatedMessage.of("a key", null, "a {} and {} message", "first", "second") - ); + when(layout.toByteArray(any())).thenReturn("{ \"some key\": \"some value\" }".getBytes()); - assertThat(payloadMap, hasEntry("message", "a first and second message")); - } - - /* - * Wraps up the steps for extracting an index request payload from the mocks. - */ - private Map getWriteRequest(ESLogMessage message) { - LogEvent logEvent = mock(LogEvent.class); - when(logEvent.getMessage()).thenReturn(message); - - appender.append(logEvent); + appender.append(mock(LogEvent.class)); ArgumentCaptor argument = ArgumentCaptor.forClass(IndexRequest.class); verify(consumer).accept(argument.capture()); final IndexRequest indexRequest = argument.getValue(); - return indexRequest.sourceAsMap(); - } + final Map payloadMap = indexRequest.sourceAsMap(); - private LogEvent buildEvent() { - LogEvent logEvent = mock(LogEvent.class); - when(logEvent.getMessage()).thenReturn(DeprecatedMessage.of("a key", null, "a message")); - return logEvent; + assertThat(payloadMap, hasEntry("some key", "some value")); } } From 516ce6dd6ecf60bfc31ddd01abe46a02eeda7659 Mon Sep 17 00:00:00 2001 From: Rory Hunter Date: Wed, 22 Jul 2020 17:08:09 +0100 Subject: [PATCH 29/48] Checkstyle --- .../org/elasticsearch/common/logging/DeprecationLogger.java | 2 -- .../xpack/deprecation/DeprecationIndexingAppenderTests.java | 3 ++- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/server/src/main/java/org/elasticsearch/common/logging/DeprecationLogger.java b/server/src/main/java/org/elasticsearch/common/logging/DeprecationLogger.java index 988be8c5410d0..07ee55a3c97d4 100644 --- a/server/src/main/java/org/elasticsearch/common/logging/DeprecationLogger.java +++ b/server/src/main/java/org/elasticsearch/common/logging/DeprecationLogger.java @@ -22,8 +22,6 @@ import org.apache.logging.log4j.Level; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; -import org.apache.logging.log4j.core.Filter; -import org.apache.logging.log4j.core.filter.LevelRangeFilter; /** * A logger that logs deprecation notices. Logger should be initialized with a parent logger which name will be used diff --git a/x-pack/plugin/deprecation/src/test/java/org/elasticsearch/xpack/deprecation/DeprecationIndexingAppenderTests.java b/x-pack/plugin/deprecation/src/test/java/org/elasticsearch/xpack/deprecation/DeprecationIndexingAppenderTests.java index 5b13e662dc4e3..5475efcf4c7e4 100644 --- a/x-pack/plugin/deprecation/src/test/java/org/elasticsearch/xpack/deprecation/DeprecationIndexingAppenderTests.java +++ b/x-pack/plugin/deprecation/src/test/java/org/elasticsearch/xpack/deprecation/DeprecationIndexingAppenderTests.java @@ -14,6 +14,7 @@ import org.junit.Before; import org.mockito.ArgumentCaptor; +import java.nio.charset.StandardCharsets; import java.util.Map; import java.util.function.Consumer; @@ -67,7 +68,7 @@ public void testDoesNotWriteMessageWhenServiceEnabledAndDisabled() { public void testWritesMessageWhenServiceEnabled() { appender.setEnabled(true); - when(layout.toByteArray(any())).thenReturn("{ \"some key\": \"some value\" }".getBytes()); + when(layout.toByteArray(any())).thenReturn("{ \"some key\": \"some value\" }".getBytes(StandardCharsets.UTF_8)); appender.append(mock(LogEvent.class)); From a750f498561d3936fa182ca86ed2e7b5d073782f Mon Sep 17 00:00:00 2001 From: Rory Hunter Date: Wed, 22 Jul 2020 17:08:18 +0100 Subject: [PATCH 30/48] Update docker log4j configs --- distribution/docker/src/docker/config/log4j2.properties | 5 +++++ distribution/docker/src/docker/config/oss/log4j2.properties | 5 +++++ 2 files changed, 10 insertions(+) diff --git a/distribution/docker/src/docker/config/log4j2.properties b/distribution/docker/src/docker/config/log4j2.properties index 94701d312686d..555c2b6edc233 100644 --- a/distribution/docker/src/docker/config/log4j2.properties +++ b/distribution/docker/src/docker/config/log4j2.properties @@ -12,10 +12,15 @@ appender.deprecation_rolling.type = Console appender.deprecation_rolling.name = deprecation_rolling appender.deprecation_rolling.layout.type = ECSJsonLayout appender.deprecation_rolling.layout.type_name = deprecation +appender.deprecation_rolling.filter.rate_limit.type = RateLimitingFilter + +appender.header_warning.type = HeaderWarningAppender +appender.header_warning.name = header_warning logger.deprecation.name = org.elasticsearch.deprecation logger.deprecation.level = warn logger.deprecation.appenderRef.deprecation_rolling.ref = deprecation_rolling +logger.deprecation.appenderRef.header_warning.ref = header_warning logger.deprecation.additivity = false appender.index_search_slowlog_rolling.type = Console diff --git a/distribution/docker/src/docker/config/oss/log4j2.properties b/distribution/docker/src/docker/config/oss/log4j2.properties index a4e175edf431d..e4dfe0ffb5f50 100644 --- a/distribution/docker/src/docker/config/oss/log4j2.properties +++ b/distribution/docker/src/docker/config/oss/log4j2.properties @@ -12,10 +12,15 @@ appender.deprecation_rolling.type = Console appender.deprecation_rolling.name = deprecation_rolling appender.deprecation_rolling.layout.type = ECSJsonLayout appender.deprecation_rolling.layout.type_name = deprecation +appender.deprecation_rolling.filter.rate_limit.type = RateLimitingFilter + +appender.header_warning.type = HeaderWarningAppender +appender.header_warning.name = header_warning logger.deprecation.name = org.elasticsearch.deprecation logger.deprecation.level = warn logger.deprecation.appenderRef.deprecation_rolling.ref = deprecation_rolling +logger.deprecation.appenderRef.header_warning.ref = header_warning logger.deprecation.additivity = false appender.index_search_slowlog_rolling.type = Console From 2352a20a225d2cd11b6047f8f058df5e08a9779d Mon Sep 17 00:00:00 2001 From: Rory Hunter Date: Thu, 23 Jul 2020 10:45:45 +0100 Subject: [PATCH 31/48] Fix libs tests --- libs/x-content/src/main/resources/log4j2.properties | 8 ++++++++ .../org/elasticsearch/common/ParseFieldTests.java | 12 ++++++++++++ .../common/xcontent/ObjectParserTests.java | 13 +++++++++++++ 3 files changed, 33 insertions(+) create mode 100644 libs/x-content/src/main/resources/log4j2.properties diff --git a/libs/x-content/src/main/resources/log4j2.properties b/libs/x-content/src/main/resources/log4j2.properties new file mode 100644 index 0000000000000..eb969b2b8c20b --- /dev/null +++ b/libs/x-content/src/main/resources/log4j2.properties @@ -0,0 +1,8 @@ +appender.header_warning.type = HeaderWarningAppender +appender.header_warning.name = header_warning + +logger.deprecation.name = deprecation +logger.deprecation.level = deprecation +logger.deprecation.appenderRef.deprecation_file.ref = deprecation_file +logger.deprecation.appenderRef.header_warning.ref = header_warning +logger.deprecation.additivity = false diff --git a/libs/x-content/src/test/java/org/elasticsearch/common/ParseFieldTests.java b/libs/x-content/src/test/java/org/elasticsearch/common/ParseFieldTests.java index 6f63f1566c50b..83ab525db2b6a 100644 --- a/libs/x-content/src/test/java/org/elasticsearch/common/ParseFieldTests.java +++ b/libs/x-content/src/test/java/org/elasticsearch/common/ParseFieldTests.java @@ -18,8 +18,12 @@ */ package org.elasticsearch.common; +import org.apache.logging.log4j.LogManager; +import org.elasticsearch.common.logging.HeaderWarningAppender; +import org.elasticsearch.common.logging.Loggers; import org.elasticsearch.common.xcontent.LoggingDeprecationHandler; import org.elasticsearch.test.ESTestCase; +import org.junit.BeforeClass; import static org.hamcrest.CoreMatchers.is; import static org.hamcrest.CoreMatchers.not; @@ -27,6 +31,14 @@ import static org.hamcrest.Matchers.arrayContainingInAnyOrder; public class ParseFieldTests extends ESTestCase { + @BeforeClass + public static void setup() { + final HeaderWarningAppender appender = HeaderWarningAppender.createAppender("header_warning", null); + appender.start(); + + Loggers.addAppender(LogManager.getLogger("org.elasticsearch.deprecation"), appender); + } + public void testParse() { String name = "foo_bar"; ParseField field = new ParseField(name); diff --git a/libs/x-content/src/test/java/org/elasticsearch/common/xcontent/ObjectParserTests.java b/libs/x-content/src/test/java/org/elasticsearch/common/xcontent/ObjectParserTests.java index 088b9989dbf1e..20188fd5bbf76 100644 --- a/libs/x-content/src/test/java/org/elasticsearch/common/xcontent/ObjectParserTests.java +++ b/libs/x-content/src/test/java/org/elasticsearch/common/xcontent/ObjectParserTests.java @@ -18,18 +18,26 @@ */ package org.elasticsearch.common.xcontent; +import org.apache.logging.log4j.LogManager; +import org.elasticsearch.cli.UserException; import org.elasticsearch.common.CheckedFunction; import org.elasticsearch.common.ParseField; import org.elasticsearch.common.Strings; +import org.elasticsearch.common.logging.HeaderWarningAppender; +import org.elasticsearch.common.logging.LogConfigurator; +import org.elasticsearch.common.logging.Loggers; +import org.elasticsearch.common.settings.Settings; import org.elasticsearch.common.xcontent.ObjectParser.NamedObjectParser; import org.elasticsearch.common.xcontent.ObjectParser.ValueType; import org.elasticsearch.common.xcontent.json.JsonXContent; +import org.elasticsearch.env.Environment; import org.elasticsearch.test.ESTestCase; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.UncheckedIOException; import java.net.URI; +import java.nio.file.Path; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; @@ -214,6 +222,11 @@ public void setTest(int test) { } public void testDeprecationWarnings() throws IOException { + final HeaderWarningAppender appender = HeaderWarningAppender.createAppender("header_warning", null); + appender.start(); + + Loggers.addAppender(LogManager.getLogger("org.elasticsearch.deprecation"), appender); + class TestStruct { public String test; } From 3b80fdd22f7b44119a49c7d041583e8d46d29b72 Mon Sep 17 00:00:00 2001 From: Rory Hunter Date: Thu, 23 Jul 2020 11:24:32 +0100 Subject: [PATCH 32/48] Configure HeaderWarningAppender in ESTestCase --- .../org/elasticsearch/common/ParseFieldTests.java | 12 ------------ .../common/xcontent/ObjectParserTests.java | 13 ------------- .../java/org/elasticsearch/test/ESTestCase.java | 15 +++++++++++++++ 3 files changed, 15 insertions(+), 25 deletions(-) diff --git a/libs/x-content/src/test/java/org/elasticsearch/common/ParseFieldTests.java b/libs/x-content/src/test/java/org/elasticsearch/common/ParseFieldTests.java index 83ab525db2b6a..6f63f1566c50b 100644 --- a/libs/x-content/src/test/java/org/elasticsearch/common/ParseFieldTests.java +++ b/libs/x-content/src/test/java/org/elasticsearch/common/ParseFieldTests.java @@ -18,12 +18,8 @@ */ package org.elasticsearch.common; -import org.apache.logging.log4j.LogManager; -import org.elasticsearch.common.logging.HeaderWarningAppender; -import org.elasticsearch.common.logging.Loggers; import org.elasticsearch.common.xcontent.LoggingDeprecationHandler; import org.elasticsearch.test.ESTestCase; -import org.junit.BeforeClass; import static org.hamcrest.CoreMatchers.is; import static org.hamcrest.CoreMatchers.not; @@ -31,14 +27,6 @@ import static org.hamcrest.Matchers.arrayContainingInAnyOrder; public class ParseFieldTests extends ESTestCase { - @BeforeClass - public static void setup() { - final HeaderWarningAppender appender = HeaderWarningAppender.createAppender("header_warning", null); - appender.start(); - - Loggers.addAppender(LogManager.getLogger("org.elasticsearch.deprecation"), appender); - } - public void testParse() { String name = "foo_bar"; ParseField field = new ParseField(name); diff --git a/libs/x-content/src/test/java/org/elasticsearch/common/xcontent/ObjectParserTests.java b/libs/x-content/src/test/java/org/elasticsearch/common/xcontent/ObjectParserTests.java index 20188fd5bbf76..088b9989dbf1e 100644 --- a/libs/x-content/src/test/java/org/elasticsearch/common/xcontent/ObjectParserTests.java +++ b/libs/x-content/src/test/java/org/elasticsearch/common/xcontent/ObjectParserTests.java @@ -18,26 +18,18 @@ */ package org.elasticsearch.common.xcontent; -import org.apache.logging.log4j.LogManager; -import org.elasticsearch.cli.UserException; import org.elasticsearch.common.CheckedFunction; import org.elasticsearch.common.ParseField; import org.elasticsearch.common.Strings; -import org.elasticsearch.common.logging.HeaderWarningAppender; -import org.elasticsearch.common.logging.LogConfigurator; -import org.elasticsearch.common.logging.Loggers; -import org.elasticsearch.common.settings.Settings; import org.elasticsearch.common.xcontent.ObjectParser.NamedObjectParser; import org.elasticsearch.common.xcontent.ObjectParser.ValueType; import org.elasticsearch.common.xcontent.json.JsonXContent; -import org.elasticsearch.env.Environment; import org.elasticsearch.test.ESTestCase; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.UncheckedIOException; import java.net.URI; -import java.nio.file.Path; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; @@ -222,11 +214,6 @@ public void setTest(int test) { } public void testDeprecationWarnings() throws IOException { - final HeaderWarningAppender appender = HeaderWarningAppender.createAppender("header_warning", null); - appender.start(); - - Loggers.addAppender(LogManager.getLogger("org.elasticsearch.deprecation"), appender); - class TestStruct { public String test; } diff --git a/test/framework/src/main/java/org/elasticsearch/test/ESTestCase.java b/test/framework/src/main/java/org/elasticsearch/test/ESTestCase.java index 2eaae7792b2a7..3c71dba77fd87 100644 --- a/test/framework/src/main/java/org/elasticsearch/test/ESTestCase.java +++ b/test/framework/src/main/java/org/elasticsearch/test/ESTestCase.java @@ -65,6 +65,7 @@ import org.elasticsearch.common.io.stream.StreamInput; import org.elasticsearch.common.io.stream.Writeable; import org.elasticsearch.common.logging.HeaderWarning; +import org.elasticsearch.common.logging.HeaderWarningAppender; import org.elasticsearch.common.logging.LogConfigurator; import org.elasticsearch.common.logging.Loggers; import org.elasticsearch.common.settings.Setting; @@ -179,6 +180,7 @@ public abstract class ESTestCase extends LuceneTestCase { private static final AtomicInteger portGenerator = new AtomicInteger(); private static final Collection nettyLoggedLeaks = new ArrayList<>(); + private HeaderWarningAppender headerWarningAppender; @AfterClass public static void resetPortCounter() { @@ -334,6 +336,19 @@ public static void ensureSupportedLocale() { } } + @Before + public void setHeaderWarningAppender() { + this.headerWarningAppender = HeaderWarningAppender.createAppender("header_warning", null); + this.headerWarningAppender.start(); + Loggers.addAppender(LogManager.getLogger("org.elasticsearch.deprecation"), this.headerWarningAppender); + } + + @After + public void removeHeaderWarningAppender() { + Loggers.removeAppender(LogManager.getLogger("org.elasticsearch.deprecation"), this.headerWarningAppender); + this.headerWarningAppender = null; + } + @Before public final void before() { logger.info("{}before test", getTestParamsForLogging()); From 1a3f2dab81be6159d7d383cc6afe508178e0959c Mon Sep 17 00:00:00 2001 From: Rory Hunter Date: Thu, 23 Jul 2020 11:34:12 +0100 Subject: [PATCH 33/48] Fix docker log4j deprecation levels --- distribution/docker/src/docker/config/log4j2.properties | 2 +- distribution/docker/src/docker/config/oss/log4j2.properties | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/distribution/docker/src/docker/config/log4j2.properties b/distribution/docker/src/docker/config/log4j2.properties index 555c2b6edc233..d731dc0074543 100644 --- a/distribution/docker/src/docker/config/log4j2.properties +++ b/distribution/docker/src/docker/config/log4j2.properties @@ -18,7 +18,7 @@ appender.header_warning.type = HeaderWarningAppender appender.header_warning.name = header_warning logger.deprecation.name = org.elasticsearch.deprecation -logger.deprecation.level = warn +logger.deprecation.level = deprecation logger.deprecation.appenderRef.deprecation_rolling.ref = deprecation_rolling logger.deprecation.appenderRef.header_warning.ref = header_warning logger.deprecation.additivity = false diff --git a/distribution/docker/src/docker/config/oss/log4j2.properties b/distribution/docker/src/docker/config/oss/log4j2.properties index e4dfe0ffb5f50..8ab751ec5e470 100644 --- a/distribution/docker/src/docker/config/oss/log4j2.properties +++ b/distribution/docker/src/docker/config/oss/log4j2.properties @@ -18,7 +18,7 @@ appender.header_warning.type = HeaderWarningAppender appender.header_warning.name = header_warning logger.deprecation.name = org.elasticsearch.deprecation -logger.deprecation.level = warn +logger.deprecation.level = deprecation logger.deprecation.appenderRef.deprecation_rolling.ref = deprecation_rolling logger.deprecation.appenderRef.header_warning.ref = header_warning logger.deprecation.additivity = false From 3db6c7a29322eebefff795ada0c61175134441ef Mon Sep 17 00:00:00 2001 From: Rory Hunter Date: Thu, 23 Jul 2020 16:51:24 +0100 Subject: [PATCH 34/48] Test fixes --- .../org/elasticsearch/common/logging/config/log4j2.properties | 4 ++++ .../common/logging/no_node_name/log4j2.properties | 4 ++++ qa/logging-config/custom-log4j2.properties | 4 ++++ test/framework/src/main/resources/log4j2-test.properties | 2 +- .../java/org/elasticsearch/query/DeprecatedQueryBuilder.java | 2 +- 5 files changed, 14 insertions(+), 2 deletions(-) diff --git a/qa/evil-tests/src/test/resources/org/elasticsearch/common/logging/config/log4j2.properties b/qa/evil-tests/src/test/resources/org/elasticsearch/common/logging/config/log4j2.properties index 2c9f48a359a46..310f991a05797 100644 --- a/qa/evil-tests/src/test/resources/org/elasticsearch/common/logging/config/log4j2.properties +++ b/qa/evil-tests/src/test/resources/org/elasticsearch/common/logging/config/log4j2.properties @@ -25,7 +25,11 @@ appender.deprecation_file.fileName = ${sys:es.logs.base_path}${sys:file.separato appender.deprecation_file.layout.type = PatternLayout appender.deprecation_file.layout.pattern = [%p][%l] [%test_thread_info]%marker %m%n +appender.header_warning.type = HeaderWarningAppender +appender.header_warning.name = header_warning + logger.deprecation.name = deprecation logger.deprecation.level = warn logger.deprecation.appenderRef.deprecation_file.ref = deprecation_file +logger.deprecation.appenderRef.header_warning.ref = header_warning logger.deprecation.additivity = false diff --git a/qa/evil-tests/src/test/resources/org/elasticsearch/common/logging/no_node_name/log4j2.properties b/qa/evil-tests/src/test/resources/org/elasticsearch/common/logging/no_node_name/log4j2.properties index fd7af2ce73136..cdd33e965d20e 100644 --- a/qa/evil-tests/src/test/resources/org/elasticsearch/common/logging/no_node_name/log4j2.properties +++ b/qa/evil-tests/src/test/resources/org/elasticsearch/common/logging/no_node_name/log4j2.properties @@ -19,7 +19,11 @@ appender.deprecation_file.fileName = ${sys:es.logs.base_path}${sys:file.separato appender.deprecation_file.layout.type = PatternLayout appender.deprecation_file.layout.pattern = [%p][%l] %marker%m%n +appender.header_warning.type = HeaderWarningAppender +appender.header_warning.name = header_warning + logger.deprecation.name = deprecation logger.deprecation.level = warn logger.deprecation.appenderRef.deprecation_file.ref = deprecation_file +logger.deprecation.appenderRef.header_warning.ref = header_warning logger.deprecation.additivity = false diff --git a/qa/logging-config/custom-log4j2.properties b/qa/logging-config/custom-log4j2.properties index be4ffc03f20ef..c084928b7fe0e 100644 --- a/qa/logging-config/custom-log4j2.properties +++ b/qa/logging-config/custom-log4j2.properties @@ -87,10 +87,14 @@ 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 = warn 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 #################### diff --git a/test/framework/src/main/resources/log4j2-test.properties b/test/framework/src/main/resources/log4j2-test.properties index 3d9c084267a84..dae00805cb35f 100644 --- a/test/framework/src/main/resources/log4j2-test.properties +++ b/test/framework/src/main/resources/log4j2-test.properties @@ -11,4 +11,4 @@ appender.header_warning.name = header_warning logger.deprecation.name = org.elasticsearch.deprecation logger.deprecation.level = deprecation -JsonLoggerTestslogger.deprecation.appenderRef.header_warning.ref = header_warning +logger.deprecation.appenderRef.header_warning.ref = header_warning diff --git a/x-pack/plugin/async-search/qa/rest/src/main/java/org/elasticsearch/query/DeprecatedQueryBuilder.java b/x-pack/plugin/async-search/qa/rest/src/main/java/org/elasticsearch/query/DeprecatedQueryBuilder.java index 0ce331e793d09..9cbf5f9bc97ad 100644 --- a/x-pack/plugin/async-search/qa/rest/src/main/java/org/elasticsearch/query/DeprecatedQueryBuilder.java +++ b/x-pack/plugin/async-search/qa/rest/src/main/java/org/elasticsearch/query/DeprecatedQueryBuilder.java @@ -21,7 +21,7 @@ import java.io.IOException; public class DeprecatedQueryBuilder extends AbstractQueryBuilder { - private static final DeprecationLogger deprecationLogger = DeprecationLogger.getLogger("Deprecated"); + private static final DeprecationLogger deprecationLogger = DeprecationLogger.getLogger(DeprecatedQueryBuilder.class); public static final String NAME = "deprecated"; From 55299127306f388a93a3e53bbaf1ef36b967f9c6 Mon Sep 17 00:00:00 2001 From: Rory Hunter Date: Mon, 3 Aug 2020 10:15:31 +0100 Subject: [PATCH 35/48] WIP - trying to write an integration test --- .../test/ESSingleNodeTestCase.java | 2 + x-pack/plugin/deprecation/build.gradle | 1 + .../xpack/deprecation/DeprecationHttpIT.java | 46 ++++++++++++++++++- .../logging/DeprecationIndexingAppender.java | 4 +- 4 files changed, 50 insertions(+), 3 deletions(-) diff --git a/test/framework/src/main/java/org/elasticsearch/test/ESSingleNodeTestCase.java b/test/framework/src/main/java/org/elasticsearch/test/ESSingleNodeTestCase.java index 4d00f33000990..26d860aadbcaa 100644 --- a/test/framework/src/main/java/org/elasticsearch/test/ESSingleNodeTestCase.java +++ b/test/framework/src/main/java/org/elasticsearch/test/ESSingleNodeTestCase.java @@ -32,6 +32,7 @@ import org.elasticsearch.cluster.routing.allocation.DiskThresholdSettings; import org.elasticsearch.common.Priority; import org.elasticsearch.common.Strings; +import org.elasticsearch.common.logging.LogConfigurator; import org.elasticsearch.common.settings.Settings; import org.elasticsearch.common.unit.TimeValue; import org.elasticsearch.common.util.BigArrays; @@ -229,6 +230,7 @@ private Node newNode() { plugins.add(MockHttpTransport.TestPlugin.class); } plugins.add(MockScriptService.TestPlugin.class); + LogConfigurator.setNodeName(Node.NODE_NAME_SETTING.get(settings)); Node node = new MockNode(settings, plugins, forbidPrivateIndexSettings()); try { node.start(); diff --git a/x-pack/plugin/deprecation/build.gradle b/x-pack/plugin/deprecation/build.gradle index 96a69520a76ca..742cfa5b4b07e 100644 --- a/x-pack/plugin/deprecation/build.gradle +++ b/x-pack/plugin/deprecation/build.gradle @@ -11,6 +11,7 @@ archivesBaseName = 'x-pack-deprecation' dependencies { compileOnly project(":x-pack:plugin:core") + compileOnly project(':x-pack:plugin:stack') } integTest.enabled = false diff --git a/x-pack/plugin/deprecation/src/internalClusterTest/java/org/elasticsearch/xpack/deprecation/DeprecationHttpIT.java b/x-pack/plugin/deprecation/src/internalClusterTest/java/org/elasticsearch/xpack/deprecation/DeprecationHttpIT.java index 8040366134c1c..f0e7f88f593e9 100644 --- a/x-pack/plugin/deprecation/src/internalClusterTest/java/org/elasticsearch/xpack/deprecation/DeprecationHttpIT.java +++ b/x-pack/plugin/deprecation/src/internalClusterTest/java/org/elasticsearch/xpack/deprecation/DeprecationHttpIT.java @@ -12,7 +12,12 @@ import org.apache.http.entity.StringEntity; import org.elasticsearch.action.admin.cluster.node.info.NodeInfo; import org.elasticsearch.action.admin.cluster.node.info.NodesInfoResponse; +import org.elasticsearch.action.admin.cluster.settings.ClusterUpdateSettingsRequest; +import org.elasticsearch.action.admin.cluster.settings.ClusterUpdateSettingsRequestBuilder; import org.elasticsearch.action.admin.indices.refresh.RefreshRequest; +import org.elasticsearch.action.search.SearchRequest; +import org.elasticsearch.action.search.SearchResponse; +import org.elasticsearch.action.search.SearchType; import org.elasticsearch.client.Client; import org.elasticsearch.client.Request; import org.elasticsearch.client.Response; @@ -31,9 +36,11 @@ import org.elasticsearch.core.internal.io.IOUtils; import org.elasticsearch.http.HttpInfo; import org.elasticsearch.plugins.Plugin; +import org.elasticsearch.search.SearchHit; import org.elasticsearch.test.ESSingleNodeTestCase; import org.elasticsearch.transport.Netty4Plugin; import org.elasticsearch.xpack.core.XPackPlugin; +import org.elasticsearch.xpack.stack.StackPlugin; import org.hamcrest.Matcher; import java.io.IOException; @@ -42,17 +49,25 @@ import java.util.Collection; import java.util.Collections; import java.util.List; +import java.util.Map; import java.util.stream.Collectors; import static org.elasticsearch.rest.RestStatus.OK; +import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertAcked; +import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertFirstHit; +import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertHitCount; import static org.elasticsearch.test.hamcrest.RegexMatcher.matches; import static org.elasticsearch.xpack.deprecation.TestDeprecationHeaderRestAction.TEST_DEPRECATED_SETTING_TRUE1; import static org.elasticsearch.xpack.deprecation.TestDeprecationHeaderRestAction.TEST_DEPRECATED_SETTING_TRUE2; import static org.elasticsearch.xpack.deprecation.TestDeprecationHeaderRestAction.TEST_NOT_DEPRECATED_SETTING; +import static org.elasticsearch.xpack.deprecation.logging.DeprecationIndexingAppender.DEPRECATION_MESSAGES_DATA_STREAM; +import static org.elasticsearch.xpack.deprecation.logging.DeprecationIndexingComponent.WRITE_DEPRECATION_LOGS_TO_INDEX; import static org.hamcrest.Matchers.containsString; import static org.hamcrest.Matchers.equalTo; +import static org.hamcrest.Matchers.hasEntry; import static org.hamcrest.Matchers.hasItem; import static org.hamcrest.Matchers.hasSize; +import static org.hamcrest.Matchers.sameInstance; /** * Tests {@code DeprecationLogger} uses the {@code ThreadContext} to add response headers. @@ -68,7 +83,7 @@ protected boolean addMockHttpTransport() { @Override protected Collection> getPlugins() { - return List.of(Netty4Plugin.class, XPackPlugin.class, Deprecation.class, TestDeprecationPlugin.class); + return List.of(Netty4Plugin.class, XPackPlugin.class, Deprecation.class, StackPlugin.class, TestDeprecationPlugin.class); } @Override @@ -205,6 +220,35 @@ private void doTestDeprecationWarningsAppearInHeaders() throws IOException { } } + /** + * Check that deprecation messages can be recorded to an index + */ + public void testDeprecationMessagesCanBeIndexed() throws Exception { + try { + configureWriteDeprecationLogsToIndex(true); + + doTestDeprecationWarningsAppearInHeaders(); + + assertBusy(() -> { + final SearchResponse searchResponse = client().search(new SearchRequest(DEPRECATION_MESSAGES_DATA_STREAM)).actionGet(); + assertHitCount(searchResponse, 1); + + final SearchHit hit = searchResponse.getHits().getHits()[0]; + final Map document = hit.getSourceAsMap(); + + assertThat(document, hasEntry("", "")); + }); + } finally { + configureWriteDeprecationLogsToIndex(false); + } + } + + private void configureWriteDeprecationLogsToIndex(boolean value) { + final ClusterUpdateSettingsRequest request = new ClusterUpdateSettingsRequest(); + request.transientSettings(Settings.builder().put(WRITE_DEPRECATION_LOGS_TO_INDEX.getKey(), value)); + assertAcked(client().admin().cluster().updateSettings(request).actionGet()); + } + private List getWarningHeaders(Header[] headers) { List warnings = new ArrayList<>(); diff --git a/x-pack/plugin/deprecation/src/main/java/org/elasticsearch/xpack/deprecation/logging/DeprecationIndexingAppender.java b/x-pack/plugin/deprecation/src/main/java/org/elasticsearch/xpack/deprecation/logging/DeprecationIndexingAppender.java index d3ae739055c47..e20a9ad8a23c7 100644 --- a/x-pack/plugin/deprecation/src/main/java/org/elasticsearch/xpack/deprecation/logging/DeprecationIndexingAppender.java +++ b/x-pack/plugin/deprecation/src/main/java/org/elasticsearch/xpack/deprecation/logging/DeprecationIndexingAppender.java @@ -27,7 +27,7 @@ */ @Plugin(name = "DeprecationIndexingAppender", category = Core.CATEGORY_NAME, elementType = Appender.ELEMENT_TYPE) public class DeprecationIndexingAppender extends AbstractAppender { - private static final String DATA_STREAM_NAME = "logs-deprecation-elasticsearch"; + public static final String DEPRECATION_MESSAGES_DATA_STREAM = "logs-deprecation-elasticsearch"; private final Consumer requestConsumer; @@ -61,7 +61,7 @@ public void append(LogEvent event) { final byte[] payload = this.getLayout().toByteArray(event); - final IndexRequest request = new IndexRequest(DATA_STREAM_NAME).source(payload, XContentType.JSON) + final IndexRequest request = new IndexRequest(DEPRECATION_MESSAGES_DATA_STREAM).source(payload, XContentType.JSON) .opType(DocWriteRequest.OpType.CREATE); this.requestConsumer.accept(request); From 345236768a0ac9399b4101c24144d53e7da4536c Mon Sep 17 00:00:00 2001 From: Rory Hunter Date: Tue, 4 Aug 2020 16:19:10 +0100 Subject: [PATCH 36/48] Implement indexing test --- x-pack/plugin/deprecation/build.gradle | 4 + .../xpack/deprecation/DeprecationHttpIT.java | 75 ++++++++++++------- .../logging/DeprecationIndexingComponent.java | 40 ++++++---- 3 files changed, 78 insertions(+), 41 deletions(-) diff --git a/x-pack/plugin/deprecation/build.gradle b/x-pack/plugin/deprecation/build.gradle index 742cfa5b4b07e..1e92d41172b3b 100644 --- a/x-pack/plugin/deprecation/build.gradle +++ b/x-pack/plugin/deprecation/build.gradle @@ -11,6 +11,10 @@ archivesBaseName = 'x-pack-deprecation' dependencies { compileOnly project(":x-pack:plugin:core") + // The following are only required for testing + compileOnly project(':x-pack:plugin:data-streams') + compileOnly project(':x-pack:plugin:mapper-constant-keyword') + compileOnly project(':x-pack:plugin:ilm') compileOnly project(':x-pack:plugin:stack') } diff --git a/x-pack/plugin/deprecation/src/internalClusterTest/java/org/elasticsearch/xpack/deprecation/DeprecationHttpIT.java b/x-pack/plugin/deprecation/src/internalClusterTest/java/org/elasticsearch/xpack/deprecation/DeprecationHttpIT.java index f0e7f88f593e9..99375fb81bbae 100644 --- a/x-pack/plugin/deprecation/src/internalClusterTest/java/org/elasticsearch/xpack/deprecation/DeprecationHttpIT.java +++ b/x-pack/plugin/deprecation/src/internalClusterTest/java/org/elasticsearch/xpack/deprecation/DeprecationHttpIT.java @@ -13,11 +13,9 @@ import org.elasticsearch.action.admin.cluster.node.info.NodeInfo; import org.elasticsearch.action.admin.cluster.node.info.NodesInfoResponse; import org.elasticsearch.action.admin.cluster.settings.ClusterUpdateSettingsRequest; -import org.elasticsearch.action.admin.cluster.settings.ClusterUpdateSettingsRequestBuilder; import org.elasticsearch.action.admin.indices.refresh.RefreshRequest; import org.elasticsearch.action.search.SearchRequest; import org.elasticsearch.action.search.SearchResponse; -import org.elasticsearch.action.search.SearchType; import org.elasticsearch.client.Client; import org.elasticsearch.client.Request; import org.elasticsearch.client.Response; @@ -35,11 +33,15 @@ import org.elasticsearch.common.xcontent.json.JsonXContent; import org.elasticsearch.core.internal.io.IOUtils; import org.elasticsearch.http.HttpInfo; +import org.elasticsearch.index.IndexNotFoundException; import org.elasticsearch.plugins.Plugin; import org.elasticsearch.search.SearchHit; import org.elasticsearch.test.ESSingleNodeTestCase; import org.elasticsearch.transport.Netty4Plugin; +import org.elasticsearch.xpack.constantkeyword.ConstantKeywordMapperPlugin; import org.elasticsearch.xpack.core.XPackPlugin; +import org.elasticsearch.xpack.datastreams.DataStreamsPlugin; +import org.elasticsearch.xpack.ilm.IndexLifecycle; import org.elasticsearch.xpack.stack.StackPlugin; import org.hamcrest.Matcher; @@ -50,12 +52,11 @@ import java.util.Collections; import java.util.List; import java.util.Map; +import java.util.concurrent.TimeUnit; import java.util.stream.Collectors; import static org.elasticsearch.rest.RestStatus.OK; import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertAcked; -import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertFirstHit; -import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertHitCount; import static org.elasticsearch.test.hamcrest.RegexMatcher.matches; import static org.elasticsearch.xpack.deprecation.TestDeprecationHeaderRestAction.TEST_DEPRECATED_SETTING_TRUE1; import static org.elasticsearch.xpack.deprecation.TestDeprecationHeaderRestAction.TEST_DEPRECATED_SETTING_TRUE2; @@ -64,10 +65,10 @@ import static org.elasticsearch.xpack.deprecation.logging.DeprecationIndexingComponent.WRITE_DEPRECATION_LOGS_TO_INDEX; import static org.hamcrest.Matchers.containsString; import static org.hamcrest.Matchers.equalTo; +import static org.hamcrest.Matchers.greaterThan; import static org.hamcrest.Matchers.hasEntry; import static org.hamcrest.Matchers.hasItem; import static org.hamcrest.Matchers.hasSize; -import static org.hamcrest.Matchers.sameInstance; /** * Tests {@code DeprecationLogger} uses the {@code ThreadContext} to add response headers. @@ -83,17 +84,26 @@ protected boolean addMockHttpTransport() { @Override protected Collection> getPlugins() { - return List.of(Netty4Plugin.class, XPackPlugin.class, Deprecation.class, StackPlugin.class, TestDeprecationPlugin.class); + return List.of( + ConstantKeywordMapperPlugin.class, + DataStreamsPlugin.class, + Deprecation.class, + IndexLifecycle.class, + Netty4Plugin.class, + StackPlugin.class, + TestDeprecationPlugin.class, + XPackPlugin.class + ); } @Override protected Settings nodeSettings() { return Settings.builder() // change values of deprecated settings so that accessing them is logged - .put(TEST_DEPRECATED_SETTING_TRUE1.getKey(), ! TEST_DEPRECATED_SETTING_TRUE1.getDefault(Settings.EMPTY)) - .put(TEST_DEPRECATED_SETTING_TRUE2.getKey(), ! TEST_DEPRECATED_SETTING_TRUE2.getDefault(Settings.EMPTY)) + .put(TEST_DEPRECATED_SETTING_TRUE1.getKey(), !TEST_DEPRECATED_SETTING_TRUE1.getDefault(Settings.EMPTY)) + .put(TEST_DEPRECATED_SETTING_TRUE2.getKey(), !TEST_DEPRECATED_SETTING_TRUE2.getDefault(Settings.EMPTY)) // non-deprecated setting to ensure not everything is logged - .put(TEST_NOT_DEPRECATED_SETTING.getKey(), ! TEST_NOT_DEPRECATED_SETTING.getDefault(Settings.EMPTY)) + .put(TEST_NOT_DEPRECATED_SETTING.getKey(), !TEST_NOT_DEPRECATED_SETTING.getDefault(Settings.EMPTY)) .build(); } @@ -135,7 +145,7 @@ public void testUniqueDeprecationResponsesMergedTogether() throws IOException { // trigger all index deprecations Request request = new Request("GET", "/" + commaSeparatedIndices + "/_search"); - request.setJsonEntity("{\"query\":{\"bool\":{\"filter\":[{\"" + TestDeprecatedQueryBuilder.NAME + "\":{}}]}}}"); + request.setJsonEntity("{\"query\":{\"bool\":{\"filter\":[{\"" + TestDeprecatedQueryBuilder.NAME + "\":{}}]}}}"); Response response = getRestClient().performRequest(request); assertThat(response.getStatusLine().getStatusCode(), equalTo(OK.getStatus())); @@ -143,7 +153,7 @@ public void testUniqueDeprecationResponsesMergedTogether() throws IOException { final List> headerMatchers = new ArrayList<>(indices.length); for (String index : indices) { - headerMatchers.add(containsString(LoggerMessageFormat.format("[{}] index", (Object)index))); + headerMatchers.add(containsString(LoggerMessageFormat.format("[{}] index", (Object) index))); } assertThat(deprecatedWarnings, hasSize(headerMatchers.size())); @@ -202,9 +212,14 @@ private void doTestDeprecationWarningsAppearInHeaders() throws IOException { } for (Setting setting : settings) { if (setting.isDeprecated()) { - headerMatchers.add(equalTo( - "[" + setting.getKey() + "] setting was deprecated in Elasticsearch and will be removed in a future release! " + - "See the breaking changes documentation for the next major version.")); + headerMatchers.add( + equalTo( + "[" + + setting.getKey() + + "] setting was deprecated in Elasticsearch and will be removed in a future release! " + + "See the breaking changes documentation for the next major version." + ) + ); } } @@ -212,9 +227,9 @@ private void doTestDeprecationWarningsAppearInHeaders() throws IOException { for (final String deprecatedWarning : deprecatedWarnings) { assertThat(deprecatedWarning, matches(HeaderWarning.WARNING_HEADER_PATTERN.pattern())); } - final List actualWarningValues = - deprecatedWarnings.stream().map(s -> HeaderWarning.extractWarningValueFromWarningHeader(s, true)) - .collect(Collectors.toList()); + final List actualWarningValues = deprecatedWarnings.stream() + .map(s -> HeaderWarning.extractWarningValueFromWarningHeader(s, true)) + .collect(Collectors.toList()); for (Matcher headerMatcher : headerMatchers) { assertThat(actualWarningValues, hasItem(headerMatcher)); } @@ -230,22 +245,32 @@ public void testDeprecationMessagesCanBeIndexed() throws Exception { doTestDeprecationWarningsAppearInHeaders(); assertBusy(() -> { - final SearchResponse searchResponse = client().search(new SearchRequest(DEPRECATION_MESSAGES_DATA_STREAM)).actionGet(); - assertHitCount(searchResponse, 1); + final SearchResponse searchResponse; + try { + searchResponse = client().search(new SearchRequest(DEPRECATION_MESSAGES_DATA_STREAM)).actionGet(); + } catch (IndexNotFoundException e) { + throw new AssertionError("Index does not exist"); + } + + assertThat(searchResponse.getHits().getHits().length, greaterThan(0)); final SearchHit hit = searchResponse.getHits().getHits()[0]; final Map document = hit.getSourceAsMap(); - assertThat(document, hasEntry("", "")); - }); + assertThat(document, hasEntry("message", "[/_test_cluster/deprecated_settings] exists for deprecated tests")); + }, 120, TimeUnit.SECONDS); } finally { - configureWriteDeprecationLogsToIndex(false); + configureWriteDeprecationLogsToIndex(null); + restClient.performRequest(new Request("DELETE", "_data_stream/" + DEPRECATION_MESSAGES_DATA_STREAM)); } } - private void configureWriteDeprecationLogsToIndex(boolean value) { + private void configureWriteDeprecationLogsToIndex(Boolean value) { final ClusterUpdateSettingsRequest request = new ClusterUpdateSettingsRequest(); - request.transientSettings(Settings.builder().put(WRITE_DEPRECATION_LOGS_TO_INDEX.getKey(), value)); + Settings settings = value == null + ? Settings.builder().putNull(WRITE_DEPRECATION_LOGS_TO_INDEX.getKey()).build() + : Settings.builder().put(WRITE_DEPRECATION_LOGS_TO_INDEX.getKey(), value).build(); + request.transientSettings(settings); assertAcked(client().admin().cluster().updateSettings(request).actionGet()); } @@ -286,7 +311,7 @@ private static synchronized RestClient getRestClient(Client client) { return restClient; } - private static RestClient buildRestClient(Client client ) { + private static RestClient buildRestClient(Client client) { NodesInfoResponse nodesInfoResponse = client.admin().cluster().prepareNodesInfo().get(); assertFalse(nodesInfoResponse.hasFailures()); assertThat(nodesInfoResponse.getNodes(), hasSize(1)); diff --git a/x-pack/plugin/deprecation/src/main/java/org/elasticsearch/xpack/deprecation/logging/DeprecationIndexingComponent.java b/x-pack/plugin/deprecation/src/main/java/org/elasticsearch/xpack/deprecation/logging/DeprecationIndexingComponent.java index d572bf2760162..2d75480dad74d 100644 --- a/x-pack/plugin/deprecation/src/main/java/org/elasticsearch/xpack/deprecation/logging/DeprecationIndexingComponent.java +++ b/x-pack/plugin/deprecation/src/main/java/org/elasticsearch/xpack/deprecation/logging/DeprecationIndexingComponent.java @@ -46,9 +46,11 @@ public class DeprecationIndexingComponent extends AbstractLifecycleComponent imp ); private final DeprecationIndexingAppender appender; + private final BulkProcessor processor; public DeprecationIndexingComponent(ThreadPool threadPool, Client client) { - final Consumer consumer = buildIndexRequestConsumer(threadPool, client); + this.processor = getBulkProcessor(new OriginSettingClient(client, ClientHelper.DEPRECATION_ORIGIN)); + final Consumer consumer = buildIndexRequestConsumer(threadPool); final LoggerContext context = (LoggerContext) LogManager.getContext(false); final Configuration configuration = context.getConfiguration(); @@ -72,7 +74,7 @@ protected void doStop() { @Override protected void doClose() { - // Nothing to do at present + this.processor.close(); } /** @@ -97,11 +99,28 @@ public void clusterChanged(ClusterChangedEvent event) { * * @param threadPool due to #50440, * extra care must be taken to avoid blocking the thread that writes a deprecation message. - * @param client the client to pass to {@link BulkProcessor} * @return a consumer that accepts an index request and handles all the details of writing it * into the cluster */ - private Consumer buildIndexRequestConsumer(ThreadPool threadPool, Client client) { + private Consumer buildIndexRequestConsumer(ThreadPool threadPool) { + return indexRequest -> { + try { + // TODO: remove the threadpool wrapping when the .add call is non-blocking + // (it can currently execute the bulk request occasionally) + // see: https://github.com/elastic/elasticsearch/issues/50440 + threadPool.executor(ThreadPool.Names.GENERIC).execute(() -> this.processor.add(indexRequest)); + } catch (Exception e) { + logger.error("Failed to queue deprecation message index request: " + e.getMessage(), e); + } + }; + } + + /** + * Constructs a bulk processor for writing documents + * @param client the client to use + * @return an initialised bulk processor + */ + private BulkProcessor getBulkProcessor(Client client) { final OriginSettingClient originSettingClient = new OriginSettingClient(client, ClientHelper.DEPRECATION_ORIGIN); final BulkProcessor.Listener listener = new BulkProcessor.Listener() { @@ -117,20 +136,9 @@ public void afterBulk(long executionId, BulkRequest request, Throwable failure) } }; - BulkProcessor processor = BulkProcessor.builder(originSettingClient::bulk, listener) + return BulkProcessor.builder(originSettingClient::bulk, listener) .setBulkActions(100) .setFlushInterval(TimeValue.timeValueSeconds(5)) .build(); - - return indexRequest -> { - try { - // TODO: remove the threadpool wrapping when the .add call is non-blocking - // (it can currently execute the bulk request occasionally) - // see: https://github.com/elastic/elasticsearch/issues/50440 - threadPool.executor(ThreadPool.Names.GENERIC).execute(() -> processor.add(indexRequest)); - } catch (Exception e) { - logger.error("Failed to queue deprecation message index request: " + e.getMessage(), e); - } - }; } } From d17a5e814cbfd2c814d8f6388dc77a253a578923 Mon Sep 17 00:00:00 2001 From: Rory Hunter Date: Wed, 5 Aug 2020 20:34:08 +0100 Subject: [PATCH 37/48] WIP - trying to rewrite DeprecationHttpIT as a rest test --- x-pack/plugin/deprecation/build.gradle | 15 +- x-pack/plugin/deprecation/qa/build.gradle | 8 + .../plugin/deprecation/qa/rest/build.gradle | 32 ++++ .../TestDeprecatedQueryBuilder.java | 0 .../TestDeprecationHeaderRestAction.java | 0 .../deprecation/TestDeprecationPlugin.java | 0 .../xpack/deprecation/DeprecationHttpIT.java | 171 ++++-------------- .../resources/plugin-security.policy | 9 - 8 files changed, 82 insertions(+), 153 deletions(-) create mode 100644 x-pack/plugin/deprecation/qa/build.gradle create mode 100644 x-pack/plugin/deprecation/qa/rest/build.gradle rename x-pack/plugin/deprecation/{src/internalClusterTest => qa/rest/src/main}/java/org/elasticsearch/xpack/deprecation/TestDeprecatedQueryBuilder.java (100%) rename x-pack/plugin/deprecation/{src/internalClusterTest => qa/rest/src/main}/java/org/elasticsearch/xpack/deprecation/TestDeprecationHeaderRestAction.java (100%) rename x-pack/plugin/deprecation/{src/internalClusterTest => qa/rest/src/main}/java/org/elasticsearch/xpack/deprecation/TestDeprecationPlugin.java (100%) rename x-pack/plugin/deprecation/{src/internalClusterTest => qa/rest/src/test}/java/org/elasticsearch/xpack/deprecation/DeprecationHttpIT.java (54%) delete mode 100644 x-pack/plugin/deprecation/src/internalClusterTest/resources/plugin-security.policy diff --git a/x-pack/plugin/deprecation/build.gradle b/x-pack/plugin/deprecation/build.gradle index 1e92d41172b3b..8e5ceabd3db4f 100644 --- a/x-pack/plugin/deprecation/build.gradle +++ b/x-pack/plugin/deprecation/build.gradle @@ -1,5 +1,4 @@ apply plugin: 'elasticsearch.esplugin' -apply plugin: 'elasticsearch.internal-cluster-test' esplugin { name 'x-pack-deprecation' @@ -9,13 +8,17 @@ esplugin { } archivesBaseName = 'x-pack-deprecation' +// add all sub-projects of the qa sub-project +gradle.projectsEvaluated { + project.subprojects + .find { it.path == project.path + ":qa" } + .subprojects + .findAll { it.path.startsWith(project.path + ":qa") } + .each { check.dependsOn it.check } +} + dependencies { compileOnly project(":x-pack:plugin:core") - // The following are only required for testing - compileOnly project(':x-pack:plugin:data-streams') - compileOnly project(':x-pack:plugin:mapper-constant-keyword') - compileOnly project(':x-pack:plugin:ilm') - compileOnly project(':x-pack:plugin:stack') } integTest.enabled = false diff --git a/x-pack/plugin/deprecation/qa/build.gradle b/x-pack/plugin/deprecation/qa/build.gradle new file mode 100644 index 0000000000000..75d524cc11500 --- /dev/null +++ b/x-pack/plugin/deprecation/qa/build.gradle @@ -0,0 +1,8 @@ +import org.elasticsearch.gradle.test.RestIntegTestTask + +apply plugin: 'elasticsearch.build' +test.enabled = false + +dependencies { + api project(':test:framework') +} diff --git a/x-pack/plugin/deprecation/qa/rest/build.gradle b/x-pack/plugin/deprecation/qa/rest/build.gradle new file mode 100644 index 0000000000000..6e2af4d765cf8 --- /dev/null +++ b/x-pack/plugin/deprecation/qa/rest/build.gradle @@ -0,0 +1,32 @@ +import org.elasticsearch.gradle.info.BuildParams + +apply plugin: 'elasticsearch.testclusters' +apply plugin: 'elasticsearch.esplugin' +apply plugin: 'elasticsearch.rest-resources' + +esplugin { + description 'Deprecated query plugin' + classname 'org.elasticsearch.query.DeprecatedQueryPlugin' +} + +dependencies { + api("com.fasterxml.jackson.core:jackson-annotations:${versions.jackson}") + api("com.fasterxml.jackson.core:jackson-databind:${versions.jackson}") +} + +restResources { + restApi { + includeCore '_common', 'indices', 'index' + } +} + +testClusters.integTest { + testDistribution = 'DEFAULT' + // add the deprecated query plugin + plugin file(project(':x-pack:plugin:deprecation:qa:rest').tasks.bundlePlugin.archiveFile) + setting 'xpack.security.enabled', 'false' +} + +test.enabled = false + +check.dependsOn integTest diff --git a/x-pack/plugin/deprecation/src/internalClusterTest/java/org/elasticsearch/xpack/deprecation/TestDeprecatedQueryBuilder.java b/x-pack/plugin/deprecation/qa/rest/src/main/java/org/elasticsearch/xpack/deprecation/TestDeprecatedQueryBuilder.java similarity index 100% rename from x-pack/plugin/deprecation/src/internalClusterTest/java/org/elasticsearch/xpack/deprecation/TestDeprecatedQueryBuilder.java rename to x-pack/plugin/deprecation/qa/rest/src/main/java/org/elasticsearch/xpack/deprecation/TestDeprecatedQueryBuilder.java diff --git a/x-pack/plugin/deprecation/src/internalClusterTest/java/org/elasticsearch/xpack/deprecation/TestDeprecationHeaderRestAction.java b/x-pack/plugin/deprecation/qa/rest/src/main/java/org/elasticsearch/xpack/deprecation/TestDeprecationHeaderRestAction.java similarity index 100% rename from x-pack/plugin/deprecation/src/internalClusterTest/java/org/elasticsearch/xpack/deprecation/TestDeprecationHeaderRestAction.java rename to x-pack/plugin/deprecation/qa/rest/src/main/java/org/elasticsearch/xpack/deprecation/TestDeprecationHeaderRestAction.java diff --git a/x-pack/plugin/deprecation/src/internalClusterTest/java/org/elasticsearch/xpack/deprecation/TestDeprecationPlugin.java b/x-pack/plugin/deprecation/qa/rest/src/main/java/org/elasticsearch/xpack/deprecation/TestDeprecationPlugin.java similarity index 100% rename from x-pack/plugin/deprecation/src/internalClusterTest/java/org/elasticsearch/xpack/deprecation/TestDeprecationPlugin.java rename to x-pack/plugin/deprecation/qa/rest/src/main/java/org/elasticsearch/xpack/deprecation/TestDeprecationPlugin.java diff --git a/x-pack/plugin/deprecation/src/internalClusterTest/java/org/elasticsearch/xpack/deprecation/DeprecationHttpIT.java b/x-pack/plugin/deprecation/qa/rest/src/test/java/org/elasticsearch/xpack/deprecation/DeprecationHttpIT.java similarity index 54% rename from x-pack/plugin/deprecation/src/internalClusterTest/java/org/elasticsearch/xpack/deprecation/DeprecationHttpIT.java rename to x-pack/plugin/deprecation/qa/rest/src/test/java/org/elasticsearch/xpack/deprecation/DeprecationHttpIT.java index 99375fb81bbae..2a7e8627833d0 100644 --- a/x-pack/plugin/deprecation/src/internalClusterTest/java/org/elasticsearch/xpack/deprecation/DeprecationHttpIT.java +++ b/x-pack/plugin/deprecation/qa/rest/src/test/java/org/elasticsearch/xpack/deprecation/DeprecationHttpIT.java @@ -5,64 +5,37 @@ */ package org.elasticsearch.xpack.deprecation; +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.ObjectMapper; import org.apache.http.Header; import org.apache.http.HttpEntity; -import org.apache.http.HttpHost; import org.apache.http.entity.ContentType; import org.apache.http.entity.StringEntity; -import org.elasticsearch.action.admin.cluster.node.info.NodeInfo; -import org.elasticsearch.action.admin.cluster.node.info.NodesInfoResponse; -import org.elasticsearch.action.admin.cluster.settings.ClusterUpdateSettingsRequest; -import org.elasticsearch.action.admin.indices.refresh.RefreshRequest; -import org.elasticsearch.action.search.SearchRequest; -import org.elasticsearch.action.search.SearchResponse; -import org.elasticsearch.client.Client; import org.elasticsearch.client.Request; import org.elasticsearch.client.Response; -import org.elasticsearch.client.RestClient; -import org.elasticsearch.client.RestClientBuilder; import org.elasticsearch.common.Strings; import org.elasticsearch.common.logging.HeaderWarning; import org.elasticsearch.common.logging.LoggerMessageFormat; -import org.elasticsearch.common.network.NetworkAddress; import org.elasticsearch.common.settings.Setting; import org.elasticsearch.common.settings.Settings; -import org.elasticsearch.common.transport.TransportAddress; import org.elasticsearch.common.xcontent.XContentBuilder; -import org.elasticsearch.common.xcontent.XContentType; import org.elasticsearch.common.xcontent.json.JsonXContent; -import org.elasticsearch.core.internal.io.IOUtils; -import org.elasticsearch.http.HttpInfo; -import org.elasticsearch.index.IndexNotFoundException; -import org.elasticsearch.plugins.Plugin; -import org.elasticsearch.search.SearchHit; -import org.elasticsearch.test.ESSingleNodeTestCase; -import org.elasticsearch.transport.Netty4Plugin; -import org.elasticsearch.xpack.constantkeyword.ConstantKeywordMapperPlugin; -import org.elasticsearch.xpack.core.XPackPlugin; -import org.elasticsearch.xpack.datastreams.DataStreamsPlugin; -import org.elasticsearch.xpack.ilm.IndexLifecycle; -import org.elasticsearch.xpack.stack.StackPlugin; +import org.elasticsearch.test.rest.ESRestTestCase; import org.hamcrest.Matcher; import java.io.IOException; -import java.net.InetSocketAddress; import java.util.ArrayList; -import java.util.Collection; import java.util.Collections; +import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.concurrent.TimeUnit; import java.util.stream.Collectors; -import static org.elasticsearch.rest.RestStatus.OK; -import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertAcked; import static org.elasticsearch.test.hamcrest.RegexMatcher.matches; import static org.elasticsearch.xpack.deprecation.TestDeprecationHeaderRestAction.TEST_DEPRECATED_SETTING_TRUE1; import static org.elasticsearch.xpack.deprecation.TestDeprecationHeaderRestAction.TEST_DEPRECATED_SETTING_TRUE2; import static org.elasticsearch.xpack.deprecation.TestDeprecationHeaderRestAction.TEST_NOT_DEPRECATED_SETTING; -import static org.elasticsearch.xpack.deprecation.logging.DeprecationIndexingAppender.DEPRECATION_MESSAGES_DATA_STREAM; -import static org.elasticsearch.xpack.deprecation.logging.DeprecationIndexingComponent.WRITE_DEPRECATION_LOGS_TO_INDEX; import static org.hamcrest.Matchers.containsString; import static org.hamcrest.Matchers.equalTo; import static org.hamcrest.Matchers.greaterThan; @@ -73,39 +46,7 @@ /** * Tests {@code DeprecationLogger} uses the {@code ThreadContext} to add response headers. */ -public class DeprecationHttpIT extends ESSingleNodeTestCase { - - private static RestClient restClient; - - @Override - protected boolean addMockHttpTransport() { - return false; // enable http - } - - @Override - protected Collection> getPlugins() { - return List.of( - ConstantKeywordMapperPlugin.class, - DataStreamsPlugin.class, - Deprecation.class, - IndexLifecycle.class, - Netty4Plugin.class, - StackPlugin.class, - TestDeprecationPlugin.class, - XPackPlugin.class - ); - } - - @Override - protected Settings nodeSettings() { - return Settings.builder() - // change values of deprecated settings so that accessing them is logged - .put(TEST_DEPRECATED_SETTING_TRUE1.getKey(), !TEST_DEPRECATED_SETTING_TRUE1.getDefault(Settings.EMPTY)) - .put(TEST_DEPRECATED_SETTING_TRUE2.getKey(), !TEST_DEPRECATED_SETTING_TRUE2.getDefault(Settings.EMPTY)) - // non-deprecated setting to ensure not everything is logged - .put(TEST_NOT_DEPRECATED_SETTING.getKey(), !TEST_NOT_DEPRECATED_SETTING.getDefault(Settings.EMPTY)) - .build(); - } +public class DeprecationHttpIT extends ESRestTestCase { /** * Attempts to do a scatter/gather request that expects unique responses per sub-request. @@ -119,38 +60,29 @@ public void testUniqueDeprecationResponsesMergedTogether() throws IOException { indices[i] = "test" + i; // create indices with a single shard to reduce noise; the query only deprecates uniquely by index anyway - assertTrue( - client().admin() - .indices() - .prepareCreate(indices[i]) - .setSettings(Settings.builder().put("number_of_shards", 1)) - .get() - .isAcknowledged() - ); + createIndex(indices[i], Settings.builder().put("number_of_shards", 1).build()); int randomDocCount = randomIntBetween(1, 2); - for (int j = 0; j < randomDocCount; ++j) { - client().prepareIndex(indices[i]) - .setId(Integer.toString(j)) - .setSource("{\"field\":" + j + "}", XContentType.JSON) - .execute() - .actionGet(); + for (int j = 0; j < randomDocCount; j++) { + final Request request = new Request("PUT", indices[i] + "/" + j); + request.setJsonEntity("{ \"field\": " + j + " }"); + assertOK(client().performRequest(request)); } } - client().admin().indices().refresh(new RefreshRequest(indices)); - final String commaSeparatedIndices = String.join(",", indices); + client().performRequest(new Request("POST", commaSeparatedIndices + "/_refresh")); + // trigger all index deprecations Request request = new Request("GET", "/" + commaSeparatedIndices + "/_search"); - request.setJsonEntity("{\"query\":{\"bool\":{\"filter\":[{\"" + TestDeprecatedQueryBuilder.NAME + "\":{}}]}}}"); - Response response = getRestClient().performRequest(request); - assertThat(response.getStatusLine().getStatusCode(), equalTo(OK.getStatus())); + request.setJsonEntity("{ \"query\": { \"bool\": { \"filter\": [ { \"deprecated\": {} } ] } } }"); + Response response = client().performRequest(request); + assertOK(response); final List deprecatedWarnings = getWarningHeaders(response.getHeaders()); - final List> headerMatchers = new ArrayList<>(indices.length); + final List> headerMatchers = new ArrayList<>(); for (String index : indices) { headerMatchers.add(containsString(LoggerMessageFormat.format("[{}] index", (Object) index))); @@ -200,8 +132,8 @@ private void doTestDeprecationWarningsAppearInHeaders() throws IOException { // trigger all deprecations Request request = new Request("GET", "/_test_cluster/deprecated_settings"); request.setEntity(buildSettingsRequest(settings, useDeprecatedField)); - Response response = getRestClient().performRequest(request); - assertThat(response.getStatusLine().getStatusCode(), equalTo(OK.getStatus())); + Response response = client().performRequest(request); + assertOK(response); final List deprecatedWarnings = getWarningHeaders(response.getHeaders()); final List> headerMatchers = new ArrayList<>(4); @@ -245,33 +177,31 @@ public void testDeprecationMessagesCanBeIndexed() throws Exception { doTestDeprecationWarningsAppearInHeaders(); assertBusy(() -> { - final SearchResponse searchResponse; - try { - searchResponse = client().search(new SearchRequest(DEPRECATION_MESSAGES_DATA_STREAM)).actionGet(); - } catch (IndexNotFoundException e) { - throw new AssertionError("Index does not exist"); - } + final Response response = client().performRequest(new Request("GET", "logs-deprecation-elasticsearch/_search")); + assertOK(response); - assertThat(searchResponse.getHits().getHits().length, greaterThan(0)); + ObjectMapper mapper = new ObjectMapper(); + final JsonNode jsonNode = mapper.readTree(response.getEntity().getContent()); - final SearchHit hit = searchResponse.getHits().getHits()[0]; - final Map document = hit.getSourceAsMap(); + assertThat(jsonNode.at("hits/total/value").intValue(), greaterThan(0)); + + final JsonNode firstHit = jsonNode.at("hits/hits/0"); + + final Map document = new HashMap<>(); + firstHit.fields().forEachRemaining(entry -> document.put(entry.getKey(), entry.getValue())); assertThat(document, hasEntry("message", "[/_test_cluster/deprecated_settings] exists for deprecated tests")); }, 120, TimeUnit.SECONDS); } finally { configureWriteDeprecationLogsToIndex(null); - restClient.performRequest(new Request("DELETE", "_data_stream/" + DEPRECATION_MESSAGES_DATA_STREAM)); + client().performRequest(new Request("DELETE", "_data_stream/logs-deprecation-elasticsearch")); } } - private void configureWriteDeprecationLogsToIndex(Boolean value) { - final ClusterUpdateSettingsRequest request = new ClusterUpdateSettingsRequest(); - Settings settings = value == null - ? Settings.builder().putNull(WRITE_DEPRECATION_LOGS_TO_INDEX.getKey()).build() - : Settings.builder().put(WRITE_DEPRECATION_LOGS_TO_INDEX.getKey(), value).build(); - request.transientSettings(settings); - assertAcked(client().admin().cluster().updateSettings(request).actionGet()); + private void configureWriteDeprecationLogsToIndex(Boolean value) throws IOException { + final Request request = new Request("PUT", "_cluster/settings"); + request.setJsonEntity("{ \"transient\": { \"cluster.deprecation_indexing.enabled\": " + value + " } }"); + client().performRequest(request); } private List getWarningHeaders(Header[] headers) { @@ -299,39 +229,4 @@ private HttpEntity buildSettingsRequest(List> settings, boolean return new StringEntity(Strings.toString(builder), ContentType.APPLICATION_JSON); } - - protected RestClient getRestClient() { - return getRestClient(client()); - } - - private static synchronized RestClient getRestClient(Client client) { - if (restClient == null) { - restClient = buildRestClient(client); - } - return restClient; - } - - private static RestClient buildRestClient(Client client) { - NodesInfoResponse nodesInfoResponse = client.admin().cluster().prepareNodesInfo().get(); - assertFalse(nodesInfoResponse.hasFailures()); - assertThat(nodesInfoResponse.getNodes(), hasSize(1)); - - NodeInfo node = nodesInfoResponse.getNodes().get(0); - assertNotNull(node.getInfo(HttpInfo.class)); - - TransportAddress publishAddress = node.getInfo(HttpInfo.class).address().publishAddress(); - InetSocketAddress address = publishAddress.address(); - final HttpHost host = new HttpHost(NetworkAddress.format(address.getAddress()), address.getPort(), "http"); - RestClientBuilder builder = RestClient.builder(host); - return builder.build(); - } - - @Override - public void tearDown() throws Exception { - super.tearDown(); - if (restClient != null) { - IOUtils.closeWhileHandlingException(restClient); - restClient = null; - } - } } diff --git a/x-pack/plugin/deprecation/src/internalClusterTest/resources/plugin-security.policy b/x-pack/plugin/deprecation/src/internalClusterTest/resources/plugin-security.policy deleted file mode 100644 index a11e2427783af..0000000000000 --- a/x-pack/plugin/deprecation/src/internalClusterTest/resources/plugin-security.policy +++ /dev/null @@ -1,9 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License; - * you may not use this file except in compliance with the Elastic License. - */ -grant { - permission java.lang.RuntimePermission "*", "setContextClassLoader"; -}; - From 8f70584ae65b7893b983121c214196e78a3262ba Mon Sep 17 00:00:00 2001 From: Rory Hunter Date: Fri, 7 Aug 2020 13:33:23 +0100 Subject: [PATCH 38/48] Tests execute against custom plugin, but need fixed --- x-pack/plugin/deprecation/qa/rest/build.gradle | 2 +- .../xpack/deprecation/DeprecationHttpIT.java | 11 +++++++++++ 2 files changed, 12 insertions(+), 1 deletion(-) diff --git a/x-pack/plugin/deprecation/qa/rest/build.gradle b/x-pack/plugin/deprecation/qa/rest/build.gradle index 6e2af4d765cf8..987e03a6a5e3a 100644 --- a/x-pack/plugin/deprecation/qa/rest/build.gradle +++ b/x-pack/plugin/deprecation/qa/rest/build.gradle @@ -6,7 +6,7 @@ apply plugin: 'elasticsearch.rest-resources' esplugin { description 'Deprecated query plugin' - classname 'org.elasticsearch.query.DeprecatedQueryPlugin' + classname 'org.elasticsearch.xpack.deprecation.TestDeprecationPlugin' } dependencies { diff --git a/x-pack/plugin/deprecation/qa/rest/src/test/java/org/elasticsearch/xpack/deprecation/DeprecationHttpIT.java b/x-pack/plugin/deprecation/qa/rest/src/test/java/org/elasticsearch/xpack/deprecation/DeprecationHttpIT.java index 2a7e8627833d0..5f9b754dd11f5 100644 --- a/x-pack/plugin/deprecation/qa/rest/src/test/java/org/elasticsearch/xpack/deprecation/DeprecationHttpIT.java +++ b/x-pack/plugin/deprecation/qa/rest/src/test/java/org/elasticsearch/xpack/deprecation/DeprecationHttpIT.java @@ -9,10 +9,13 @@ import com.fasterxml.jackson.databind.ObjectMapper; import org.apache.http.Header; import org.apache.http.HttpEntity; +import org.apache.http.HttpHost; import org.apache.http.entity.ContentType; import org.apache.http.entity.StringEntity; import org.elasticsearch.client.Request; import org.elasticsearch.client.Response; +import org.elasticsearch.client.RestClient; +import org.elasticsearch.client.RestClientBuilder; import org.elasticsearch.common.Strings; import org.elasticsearch.common.logging.HeaderWarning; import org.elasticsearch.common.logging.LoggerMessageFormat; @@ -48,6 +51,14 @@ */ public class DeprecationHttpIT extends ESRestTestCase { + @Override + protected RestClient buildClient(Settings settings, HttpHost[] hosts) throws IOException { + RestClientBuilder builder = RestClient.builder(hosts); + configureClient(builder, settings); + builder.setStrictDeprecationMode(false); + return builder.build(); + } + /** * Attempts to do a scatter/gather request that expects unique responses per sub-request. */ From 4f6f5017b92e5d85bcf61c6d975156897583ee32 Mon Sep 17 00:00:00 2001 From: Rory Hunter Date: Wed, 12 Aug 2020 12:20:34 +0100 Subject: [PATCH 39/48] Rework deprecation HTTP tests --- .../xpack/deprecation/DeprecationHttpIT.java | 129 +++++++++++------- 1 file changed, 79 insertions(+), 50 deletions(-) diff --git a/x-pack/plugin/deprecation/qa/rest/src/test/java/org/elasticsearch/xpack/deprecation/DeprecationHttpIT.java b/x-pack/plugin/deprecation/qa/rest/src/test/java/org/elasticsearch/xpack/deprecation/DeprecationHttpIT.java index 5f9b754dd11f5..f6ee7bee16230 100644 --- a/x-pack/plugin/deprecation/qa/rest/src/test/java/org/elasticsearch/xpack/deprecation/DeprecationHttpIT.java +++ b/x-pack/plugin/deprecation/qa/rest/src/test/java/org/elasticsearch/xpack/deprecation/DeprecationHttpIT.java @@ -28,7 +28,6 @@ import java.io.IOException; import java.util.ArrayList; -import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; @@ -51,12 +50,51 @@ */ public class DeprecationHttpIT extends ESRestTestCase { - @Override - protected RestClient buildClient(Settings settings, HttpHost[] hosts) throws IOException { - RestClientBuilder builder = RestClient.builder(hosts); - configureClient(builder, settings); - builder.setStrictDeprecationMode(false); - return builder.build(); + /** + * Check that configuring deprecation settings causes a warning to be added to the + * response headers. + */ + public void testDeprecatedSettingsReturnWarnings() throws IOException { + XContentBuilder builder = JsonXContent.contentBuilder() + .startObject() + .startObject("transient") + .field(TEST_DEPRECATED_SETTING_TRUE1.getKey(), !TEST_DEPRECATED_SETTING_TRUE1.getDefault(Settings.EMPTY)) + .field(TEST_DEPRECATED_SETTING_TRUE2.getKey(), !TEST_DEPRECATED_SETTING_TRUE2.getDefault(Settings.EMPTY)) + // There should be no warning for this field + .field(TEST_NOT_DEPRECATED_SETTING.getKey(), !TEST_NOT_DEPRECATED_SETTING.getDefault(Settings.EMPTY)) + .endObject() + .endObject(); + + final Request request = new Request("PUT", "_cluster/settings"); + request.setJsonEntity(Strings.toString(builder)); + final Response response = client().performRequest(request); + + final List deprecatedWarnings = getWarningHeaders(response.getHeaders()); + final List> headerMatchers = new ArrayList<>(2); + + for (Setting setting : List.of(TEST_DEPRECATED_SETTING_TRUE1, TEST_DEPRECATED_SETTING_TRUE2)) { + headerMatchers.add( + equalTo( + "[" + + setting.getKey() + + "] setting was deprecated in Elasticsearch and will be removed in a future release! " + + "See the breaking changes documentation for the next major version." + ) + ); + } + + assertThat(deprecatedWarnings, hasSize(headerMatchers.size())); + for (final String deprecatedWarning : deprecatedWarnings) { + assertThat("Header does not conform to expected pattern", + deprecatedWarning, matches(HeaderWarning.WARNING_HEADER_PATTERN.pattern())); + } + + final List actualWarningValues = deprecatedWarnings.stream() + .map(s -> HeaderWarning.extractWarningValueFromWarningHeader(s, true)) + .collect(Collectors.toList()); + for (Matcher headerMatcher : headerMatchers) { + assertThat(actualWarningValues, hasItem(headerMatcher)); + } } /** @@ -124,25 +162,10 @@ public void testDeprecationHeadersDoNotGetStuck() throws Exception { */ private void doTestDeprecationWarningsAppearInHeaders() throws IOException { final boolean useDeprecatedField = randomBoolean(); - final boolean useNonDeprecatedSetting = randomBoolean(); - - // deprecated settings should also trigger a deprecation warning - final List> settings = new ArrayList<>(3); - settings.add(TEST_DEPRECATED_SETTING_TRUE1); - - if (randomBoolean()) { - settings.add(TEST_DEPRECATED_SETTING_TRUE2); - } - - if (useNonDeprecatedSetting) { - settings.add(TEST_NOT_DEPRECATED_SETTING); - } - - Collections.shuffle(settings, random()); // trigger all deprecations Request request = new Request("GET", "/_test_cluster/deprecated_settings"); - request.setEntity(buildSettingsRequest(settings, useDeprecatedField)); + request.setEntity(buildSettingsRequest(useDeprecatedField)); Response response = client().performRequest(request); assertOK(response); @@ -153,18 +176,6 @@ private void doTestDeprecationWarningsAppearInHeaders() throws IOException { if (useDeprecatedField) { headerMatchers.add(equalTo(TestDeprecationHeaderRestAction.DEPRECATED_USAGE)); } - for (Setting setting : settings) { - if (setting.isDeprecated()) { - headerMatchers.add( - equalTo( - "[" - + setting.getKey() - + "] setting was deprecated in Elasticsearch and will be removed in a future release! " - + "See the breaking changes documentation for the next major version." - ) - ); - } - } assertThat(deprecatedWarnings, hasSize(headerMatchers.size())); for (final String deprecatedWarning : deprecatedWarnings) { @@ -185,24 +196,35 @@ public void testDeprecationMessagesCanBeIndexed() throws Exception { try { configureWriteDeprecationLogsToIndex(true); - doTestDeprecationWarningsAppearInHeaders(); + Request request = new Request("GET", "/_test_cluster/deprecated_settings"); + request.setEntity(buildSettingsRequest(true)); + assertOK(client().performRequest(request)); assertBusy(() -> { - final Response response = client().performRequest(new Request("GET", "logs-deprecation-elasticsearch/_search")); + Response response; + try { + response = client().performRequest(new Request("GET", "logs-deprecation-elasticsearch/_search")); + } + catch (Exception e) { + // It can take a moment for the index to be created. If it doesn't exist then the client + // throws an exception. Translate it into an assertion error so that assertBusy() will + // continue trying. + throw new AssertionError(e); + } assertOK(response); ObjectMapper mapper = new ObjectMapper(); final JsonNode jsonNode = mapper.readTree(response.getEntity().getContent()); - assertThat(jsonNode.at("hits/total/value").intValue(), greaterThan(0)); + assertThat(jsonNode.at("/hits/total/value").intValue(), greaterThan(0)); - final JsonNode firstHit = jsonNode.at("hits/hits/0"); + final JsonNode firstHit = jsonNode.at("/hits/hits/0/_source"); final Map document = new HashMap<>(); - firstHit.fields().forEachRemaining(entry -> document.put(entry.getKey(), entry.getValue())); + firstHit.fields().forEachRemaining(entry -> document.put(entry.getKey(), entry.getValue().textValue())); assertThat(document, hasEntry("message", "[/_test_cluster/deprecated_settings] exists for deprecated tests")); - }, 120, TimeUnit.SECONDS); + }); } finally { configureWriteDeprecationLogsToIndex(null); client().performRequest(new Request("DELETE", "_data_stream/logs-deprecation-elasticsearch")); @@ -212,7 +234,7 @@ public void testDeprecationMessagesCanBeIndexed() throws Exception { private void configureWriteDeprecationLogsToIndex(Boolean value) throws IOException { final Request request = new Request("PUT", "_cluster/settings"); request.setJsonEntity("{ \"transient\": { \"cluster.deprecation_indexing.enabled\": " + value + " } }"); - client().performRequest(request); + assertOK(client().performRequest(request)); } private List getWarningHeaders(Header[] headers) { @@ -227,17 +249,24 @@ private List getWarningHeaders(Header[] headers) { return warnings; } - private HttpEntity buildSettingsRequest(List> settings, boolean useDeprecatedField) throws IOException { + private HttpEntity buildSettingsRequest(boolean useDeprecatedField) throws IOException { XContentBuilder builder = JsonXContent.contentBuilder(); - builder.startObject().startArray(useDeprecatedField ? "deprecated_settings" : "settings"); + builder.startObject().startArray(useDeprecatedField ? "deprecated_settings" : "settings").endArray().endObject(); - for (Setting setting : settings) { - builder.value(setting.getKey()); - } - - builder.endArray().endObject(); + final String string = Strings.toString(builder); + return new StringEntity(string, ContentType.APPLICATION_JSON); + } - return new StringEntity(Strings.toString(builder), ContentType.APPLICATION_JSON); + /** + * Builds a REST client that will tolerate warnings in the response headers. The default + * is to throw an exception. + */ + @Override + protected RestClient buildClient(Settings settings, HttpHost[] hosts) throws IOException { + RestClientBuilder builder = RestClient.builder(hosts); + configureClient(builder, settings); + builder.setStrictDeprecationMode(false); + return builder.build(); } } From 26a3495f6ba6de70e1e0eb6ffac1f8a1f22857b2 Mon Sep 17 00:00:00 2001 From: Rory Hunter Date: Wed, 12 Aug 2020 12:50:06 +0100 Subject: [PATCH 40/48] Tweaks --- .../logging/DeprecationIndexingComponent.java | 27 ++++++++++--------- 1 file changed, 14 insertions(+), 13 deletions(-) diff --git a/x-pack/plugin/deprecation/src/main/java/org/elasticsearch/xpack/deprecation/logging/DeprecationIndexingComponent.java b/x-pack/plugin/deprecation/src/main/java/org/elasticsearch/xpack/deprecation/logging/DeprecationIndexingComponent.java index d572bf2760162..b1f81df3fe09f 100644 --- a/x-pack/plugin/deprecation/src/main/java/org/elasticsearch/xpack/deprecation/logging/DeprecationIndexingComponent.java +++ b/x-pack/plugin/deprecation/src/main/java/org/elasticsearch/xpack/deprecation/logging/DeprecationIndexingComponent.java @@ -103,19 +103,7 @@ public void clusterChanged(ClusterChangedEvent event) { */ private Consumer buildIndexRequestConsumer(ThreadPool threadPool, Client client) { final OriginSettingClient originSettingClient = new OriginSettingClient(client, ClientHelper.DEPRECATION_ORIGIN); - - final BulkProcessor.Listener listener = new BulkProcessor.Listener() { - @Override - public void beforeBulk(long executionId, BulkRequest request) {} - - @Override - public void afterBulk(long executionId, BulkRequest request, BulkResponse response) {} - - @Override - public void afterBulk(long executionId, BulkRequest request, Throwable failure) { - logger.error("Bulk write of deprecation logs failed: " + failure.getMessage(), failure); - } - }; + final BulkProcessor.Listener listener = new DeprecationBulkListener(); BulkProcessor processor = BulkProcessor.builder(originSettingClient::bulk, listener) .setBulkActions(100) @@ -133,4 +121,17 @@ public void afterBulk(long executionId, BulkRequest request, Throwable failure) } }; } + + private static class DeprecationBulkListener implements BulkProcessor.Listener { + @Override + public void beforeBulk(long executionId, BulkRequest request) {} + + @Override + public void afterBulk(long executionId, BulkRequest request, BulkResponse response) {} + + @Override + public void afterBulk(long executionId, BulkRequest request, Throwable failure) { + logger.error("Bulk write of deprecation logs failed: " + failure.getMessage(), failure); + } + } } From 022793e56516519d5ea85a662d371d75f57d58f5 Mon Sep 17 00:00:00 2001 From: Rory Hunter Date: Thu, 13 Aug 2020 09:56:15 +0100 Subject: [PATCH 41/48] Fix tests finally, by resetting the rate limiting filter --- .../common/logging/RateLimitingFilter.java | 7 +++ .../TestDeprecationHeaderRestAction.java | 13 +++- .../xpack/deprecation/DeprecationHttpIT.java | 62 +++++++++++++++---- .../logging/DeprecationIndexingAppender.java | 10 ++- .../logging/DeprecationIndexingComponent.java | 14 ++++- 5 files changed, 88 insertions(+), 18 deletions(-) diff --git a/server/src/main/java/org/elasticsearch/common/logging/RateLimitingFilter.java b/server/src/main/java/org/elasticsearch/common/logging/RateLimitingFilter.java index 66dd3448f8003..b477741b97693 100644 --- a/server/src/main/java/org/elasticsearch/common/logging/RateLimitingFilter.java +++ b/server/src/main/java/org/elasticsearch/common/logging/RateLimitingFilter.java @@ -56,6 +56,13 @@ public RateLimitingFilter(Result onMatch, Result onMismatch) { super(onMatch, onMismatch); } + /** + * Clears the cache of previously-seen keys. + */ + public void reset() { + this.lruKeyCache.clear(); + } + public Result filter(Message message) { if (message instanceof ESLogMessage) { final ESLogMessage esLogMessage = (ESLogMessage) message; diff --git a/x-pack/plugin/deprecation/qa/rest/src/main/java/org/elasticsearch/xpack/deprecation/TestDeprecationHeaderRestAction.java b/x-pack/plugin/deprecation/qa/rest/src/main/java/org/elasticsearch/xpack/deprecation/TestDeprecationHeaderRestAction.java index fa3e3e205ceaa..d7999a4a62712 100644 --- a/x-pack/plugin/deprecation/qa/rest/src/main/java/org/elasticsearch/xpack/deprecation/TestDeprecationHeaderRestAction.java +++ b/x-pack/plugin/deprecation/qa/rest/src/main/java/org/elasticsearch/xpack/deprecation/TestDeprecationHeaderRestAction.java @@ -18,8 +18,10 @@ import java.io.IOException; import java.util.Collections; +import java.util.HashMap; import java.util.List; import java.util.Map; +import java.util.stream.Collectors; import static java.util.Collections.singletonList; import static org.elasticsearch.rest.RestRequest.Method.GET; @@ -90,12 +92,19 @@ public RestChannelConsumer prepareRequest(RestRequest request, NodeClient client } } + // Pull out the settings values here in order to guarantee that any deprecation messages are triggered + // in the same thread context. + final Map settingsMap = new HashMap<>(); + for (String setting : settings) { + settingsMap.put(setting, SETTINGS_MAP.get(setting).get(this.settings)); + } + return channel -> { final XContentBuilder builder = channel.newBuilder(); builder.startObject().startArray("settings"); - for (String setting : settings) { - builder.startObject().field(setting, SETTINGS_MAP.get(setting).get(this.settings)).endObject(); + for (Map.Entry entry : settingsMap.entrySet()) { + builder.startObject().field(entry.getKey(), entry.getValue()).endObject(); } builder.endArray().endObject(); channel.sendResponse(new BytesRestResponse(RestStatus.OK, builder)); diff --git a/x-pack/plugin/deprecation/qa/rest/src/test/java/org/elasticsearch/xpack/deprecation/DeprecationHttpIT.java b/x-pack/plugin/deprecation/qa/rest/src/test/java/org/elasticsearch/xpack/deprecation/DeprecationHttpIT.java index f6ee7bee16230..378684141cd62 100644 --- a/x-pack/plugin/deprecation/qa/rest/src/test/java/org/elasticsearch/xpack/deprecation/DeprecationHttpIT.java +++ b/x-pack/plugin/deprecation/qa/rest/src/test/java/org/elasticsearch/xpack/deprecation/DeprecationHttpIT.java @@ -23,15 +23,16 @@ import org.elasticsearch.common.settings.Settings; import org.elasticsearch.common.xcontent.XContentBuilder; import org.elasticsearch.common.xcontent.json.JsonXContent; +import org.elasticsearch.test.ESTestCase; import org.elasticsearch.test.rest.ESRestTestCase; import org.hamcrest.Matcher; import java.io.IOException; import java.util.ArrayList; +import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; -import java.util.concurrent.TimeUnit; import java.util.stream.Collectors; import static org.elasticsearch.test.hamcrest.RegexMatcher.matches; @@ -43,6 +44,7 @@ import static org.hamcrest.Matchers.greaterThan; import static org.hamcrest.Matchers.hasEntry; import static org.hamcrest.Matchers.hasItem; +import static org.hamcrest.Matchers.hasItems; import static org.hamcrest.Matchers.hasSize; /** @@ -162,10 +164,25 @@ public void testDeprecationHeadersDoNotGetStuck() throws Exception { */ private void doTestDeprecationWarningsAppearInHeaders() throws IOException { final boolean useDeprecatedField = randomBoolean(); + final boolean useNonDeprecatedSetting = randomBoolean(); + + // deprecated settings should also trigger a deprecation warning + final List> settings = new ArrayList<>(3); + settings.add(TEST_DEPRECATED_SETTING_TRUE1); + + if (randomBoolean()) { + settings.add(TEST_DEPRECATED_SETTING_TRUE2); + } + + if (useNonDeprecatedSetting) { + settings.add(TEST_NOT_DEPRECATED_SETTING); + } + + Collections.shuffle(settings, random()); // trigger all deprecations Request request = new Request("GET", "/_test_cluster/deprecated_settings"); - request.setEntity(buildSettingsRequest(useDeprecatedField)); + request.setEntity(buildSettingsRequest(settings, useDeprecatedField)); Response response = client().performRequest(request); assertOK(response); @@ -197,7 +214,7 @@ public void testDeprecationMessagesCanBeIndexed() throws Exception { configureWriteDeprecationLogsToIndex(true); Request request = new Request("GET", "/_test_cluster/deprecated_settings"); - request.setEntity(buildSettingsRequest(true)); + request.setEntity(buildSettingsRequest(List.of(TEST_DEPRECATED_SETTING_TRUE1), true)); assertOK(client().performRequest(request)); assertBusy(() -> { @@ -216,14 +233,27 @@ public void testDeprecationMessagesCanBeIndexed() throws Exception { ObjectMapper mapper = new ObjectMapper(); final JsonNode jsonNode = mapper.readTree(response.getEntity().getContent()); - assertThat(jsonNode.at("/hits/total/value").intValue(), greaterThan(0)); + final int hits = jsonNode.at("/hits/total/value").intValue(); + assertThat(hits, greaterThan(0)); - final JsonNode firstHit = jsonNode.at("/hits/hits/0/_source"); + List> documents = new ArrayList<>(); - final Map document = new HashMap<>(); - firstHit.fields().forEachRemaining(entry -> document.put(entry.getKey(), entry.getValue().textValue())); + for (int i = 0; i < hits; i++) { + final JsonNode hit = jsonNode.at("/hits/hits/" + i + "/_source"); - assertThat(document, hasEntry("message", "[/_test_cluster/deprecated_settings] exists for deprecated tests")); + final Map document = new HashMap<>(); + hit.fields().forEachRemaining(entry -> document.put(entry.getKey(), entry.getValue().textValue())); + + documents.add(document); + } + + logger.warn(documents); + assertThat(documents, hasSize(2)); + + assertThat(documents, hasItems( + hasEntry("message", "[deprecated_settings] usage is deprecated. use [settings] instead"), + hasEntry("message", "[/_test_cluster/deprecated_settings] exists for deprecated tests") + )); }); } finally { configureWriteDeprecationLogsToIndex(null); @@ -234,7 +264,8 @@ public void testDeprecationMessagesCanBeIndexed() throws Exception { private void configureWriteDeprecationLogsToIndex(Boolean value) throws IOException { final Request request = new Request("PUT", "_cluster/settings"); request.setJsonEntity("{ \"transient\": { \"cluster.deprecation_indexing.enabled\": " + value + " } }"); - assertOK(client().performRequest(request)); + final Response response = client().performRequest(request); + assertOK(response); } private List getWarningHeaders(Header[] headers) { @@ -249,13 +280,18 @@ private List getWarningHeaders(Header[] headers) { return warnings; } - private HttpEntity buildSettingsRequest(boolean useDeprecatedField) throws IOException { + private HttpEntity buildSettingsRequest(List> settings, boolean useDeprecatedField) throws IOException { XContentBuilder builder = JsonXContent.contentBuilder(); - builder.startObject().startArray(useDeprecatedField ? "deprecated_settings" : "settings").endArray().endObject(); + builder.startObject().startArray(useDeprecatedField ? "deprecated_settings" : "settings"); + + for (Setting setting : settings) { + builder.value(setting.getKey()); + } + + builder.endArray().endObject(); - final String string = Strings.toString(builder); - return new StringEntity(string, ContentType.APPLICATION_JSON); + return new StringEntity(Strings.toString(builder), ContentType.APPLICATION_JSON); } /** diff --git a/x-pack/plugin/deprecation/src/main/java/org/elasticsearch/xpack/deprecation/logging/DeprecationIndexingAppender.java b/x-pack/plugin/deprecation/src/main/java/org/elasticsearch/xpack/deprecation/logging/DeprecationIndexingAppender.java index e20a9ad8a23c7..33ee4e29318fc 100644 --- a/x-pack/plugin/deprecation/src/main/java/org/elasticsearch/xpack/deprecation/logging/DeprecationIndexingAppender.java +++ b/x-pack/plugin/deprecation/src/main/java/org/elasticsearch/xpack/deprecation/logging/DeprecationIndexingAppender.java @@ -68,10 +68,18 @@ public void append(LogEvent event) { } /** - * Sets whether this appender is enabled or disabled. + * Sets whether this appender is enabled or disabled. When disabled, the appender will + * not perform indexing operations. * @param isEnabled the enabled status of the appender. */ public void setEnabled(boolean isEnabled) { this.isEnabled = isEnabled; } + + /** + * Returns whether the appender is enabled i.e. performing indexing operations. + */ + public boolean isEnabled() { + return isEnabled; + } } diff --git a/x-pack/plugin/deprecation/src/main/java/org/elasticsearch/xpack/deprecation/logging/DeprecationIndexingComponent.java b/x-pack/plugin/deprecation/src/main/java/org/elasticsearch/xpack/deprecation/logging/DeprecationIndexingComponent.java index 47eedf511917e..376f3f3fb86ba 100644 --- a/x-pack/plugin/deprecation/src/main/java/org/elasticsearch/xpack/deprecation/logging/DeprecationIndexingComponent.java +++ b/x-pack/plugin/deprecation/src/main/java/org/elasticsearch/xpack/deprecation/logging/DeprecationIndexingComponent.java @@ -47,6 +47,7 @@ public class DeprecationIndexingComponent extends AbstractLifecycleComponent imp private final DeprecationIndexingAppender appender; private final BulkProcessor processor; + private final RateLimitingFilter filter; public DeprecationIndexingComponent(ThreadPool threadPool, Client client) { this.processor = getBulkProcessor(new OriginSettingClient(client, ClientHelper.DEPRECATION_ORIGIN)); @@ -57,7 +58,8 @@ public DeprecationIndexingComponent(ThreadPool threadPool, Client client) { final EcsLayout ecsLayout = ECSJsonLayout.newBuilder().setType("deprecation").setConfiguration(configuration).build(); - this.appender = new DeprecationIndexingAppender("deprecation_indexing_appender", new RateLimitingFilter(), ecsLayout, consumer); + this.filter = new RateLimitingFilter(); + this.appender = new DeprecationIndexingAppender("deprecation_indexing_appender", filter, ecsLayout, consumer); } @Override @@ -87,7 +89,15 @@ protected void doClose() { @Override public void clusterChanged(ClusterChangedEvent event) { final ClusterState state = event.state(); - appender.setEnabled(WRITE_DEPRECATION_LOGS_TO_INDEX.get(state.getMetadata().settings())); + final boolean newEnabled = WRITE_DEPRECATION_LOGS_TO_INDEX.get(state.getMetadata().settings()); + if (appender.isEnabled() != newEnabled) { + // We've flipped from disabled to enabled. Make sure we start with a clean cache of + // previously-seen keys, otherwise we won't index anything. + if (newEnabled) { + this.filter.reset(); + } + appender.setEnabled(newEnabled); + } } /** From afc4dcf1f41a91c5e7e3c4f1c6f9ee09534137c0 Mon Sep 17 00:00:00 2001 From: Rory Hunter Date: Thu, 13 Aug 2020 10:05:38 +0100 Subject: [PATCH 42/48] Add test --- .../logging/RateLimitingFilterTests.java | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/server/src/test/java/org/elasticsearch/common/logging/RateLimitingFilterTests.java b/server/src/test/java/org/elasticsearch/common/logging/RateLimitingFilterTests.java index ded057b9f4162..3f62ea7a715c0 100644 --- a/server/src/test/java/org/elasticsearch/common/logging/RateLimitingFilterTests.java +++ b/server/src/test/java/org/elasticsearch/common/logging/RateLimitingFilterTests.java @@ -140,4 +140,22 @@ public void testOnlyEsMessagesAreFiltered() { Message message = new SimpleMessage("a message"); assertThat(filter.filter(message), equalTo(Result.NEUTRAL)); } + + /** + * Check that the filter can be reset, so that previously-seen keys are treated as new keys. + */ + public void testFilterCanBeReset() { + final Message message = DeprecatedMessage.of("key", "", "msg"); + + // First time, the message is a allowed + assertThat(filter.filter(message), equalTo(Result.ACCEPT)); + + // Second time, it is filtered out + assertThat(filter.filter(message), equalTo(Result.DENY)); + + filter.reset(); + + // Third time, it is allowed again + assertThat(filter.filter(message), equalTo(Result.ACCEPT)); + } } From 3c8069be83c749b1aa5319be16725cbf11ca2c97 Mon Sep 17 00:00:00 2001 From: Rory Hunter Date: Thu, 13 Aug 2020 14:18:44 +0100 Subject: [PATCH 43/48] Fix license checks --- .../plugin/deprecation/qa/rest/build.gradle | 5 +++++ .../qa/rest/licenses/jackson-LICENSE | 8 ++++++++ .../qa/rest/licenses/jackson-NOTICE | 20 +++++++++++++++++++ .../jackson-annotations-2.10.4.jar.sha1 | 1 + .../licenses/jackson-databind-2.10.4.jar.sha1 | 1 + 5 files changed, 35 insertions(+) create mode 100644 x-pack/plugin/deprecation/qa/rest/licenses/jackson-LICENSE create mode 100644 x-pack/plugin/deprecation/qa/rest/licenses/jackson-NOTICE create mode 100644 x-pack/plugin/deprecation/qa/rest/licenses/jackson-annotations-2.10.4.jar.sha1 create mode 100644 x-pack/plugin/deprecation/qa/rest/licenses/jackson-databind-2.10.4.jar.sha1 diff --git a/x-pack/plugin/deprecation/qa/rest/build.gradle b/x-pack/plugin/deprecation/qa/rest/build.gradle index 987e03a6a5e3a..81581e2c9601e 100644 --- a/x-pack/plugin/deprecation/qa/rest/build.gradle +++ b/x-pack/plugin/deprecation/qa/rest/build.gradle @@ -30,3 +30,8 @@ testClusters.integTest { test.enabled = false check.dependsOn integTest + +tasks.named("dependencyLicenses").configure { + mapping from: /jackson-.*/, to: 'jackson' +} + diff --git a/x-pack/plugin/deprecation/qa/rest/licenses/jackson-LICENSE b/x-pack/plugin/deprecation/qa/rest/licenses/jackson-LICENSE new file mode 100644 index 0000000000000..f5f45d26a49d6 --- /dev/null +++ b/x-pack/plugin/deprecation/qa/rest/licenses/jackson-LICENSE @@ -0,0 +1,8 @@ +This copy of Jackson JSON processor streaming parser/generator is licensed under the +Apache (Software) License, version 2.0 ("the License"). +See the License for details about distribution rights, and the +specific rights regarding derivate works. + +You may obtain a copy of the License at: + +http://www.apache.org/licenses/LICENSE-2.0 diff --git a/x-pack/plugin/deprecation/qa/rest/licenses/jackson-NOTICE b/x-pack/plugin/deprecation/qa/rest/licenses/jackson-NOTICE new file mode 100644 index 0000000000000..4c976b7b4cc58 --- /dev/null +++ b/x-pack/plugin/deprecation/qa/rest/licenses/jackson-NOTICE @@ -0,0 +1,20 @@ +# Jackson JSON processor + +Jackson is a high-performance, Free/Open Source JSON processing library. +It was originally written by Tatu Saloranta (tatu.saloranta@iki.fi), and has +been in development since 2007. +It is currently developed by a community of developers, as well as supported +commercially by FasterXML.com. + +## Licensing + +Jackson core and extension components may licensed under different licenses. +To find the details that apply to this artifact see the accompanying LICENSE file. +For more information, including possible other licensing options, contact +FasterXML.com (http://fasterxml.com). + +## Credits + +A list of contributors may be found from CREDITS file, which is included +in some artifacts (usually source distributions); but is always available +from the source code management (SCM) system project uses. diff --git a/x-pack/plugin/deprecation/qa/rest/licenses/jackson-annotations-2.10.4.jar.sha1 b/x-pack/plugin/deprecation/qa/rest/licenses/jackson-annotations-2.10.4.jar.sha1 new file mode 100644 index 0000000000000..0c548bb0e7711 --- /dev/null +++ b/x-pack/plugin/deprecation/qa/rest/licenses/jackson-annotations-2.10.4.jar.sha1 @@ -0,0 +1 @@ +6ae6028aff033f194c9710ad87c224ccaadeed6c \ No newline at end of file diff --git a/x-pack/plugin/deprecation/qa/rest/licenses/jackson-databind-2.10.4.jar.sha1 b/x-pack/plugin/deprecation/qa/rest/licenses/jackson-databind-2.10.4.jar.sha1 new file mode 100644 index 0000000000000..27d5a72cd27af --- /dev/null +++ b/x-pack/plugin/deprecation/qa/rest/licenses/jackson-databind-2.10.4.jar.sha1 @@ -0,0 +1 @@ +76e9152e93d4cf052f93a64596f633ba5b1c8ed9 \ No newline at end of file From 2dac9a3834fca0229193da868b7bdbd9534f5024 Mon Sep 17 00:00:00 2001 From: Rory Hunter Date: Thu, 13 Aug 2020 14:41:32 +0100 Subject: [PATCH 44/48] Formatting --- .../TestDeprecationHeaderRestAction.java | 47 ++++++++++++------- .../deprecation/TestDeprecationPlugin.java | 20 +++++--- .../xpack/deprecation/DeprecationHttpIT.java | 22 +++++---- 3 files changed, 57 insertions(+), 32 deletions(-) diff --git a/x-pack/plugin/deprecation/qa/rest/src/main/java/org/elasticsearch/xpack/deprecation/TestDeprecationHeaderRestAction.java b/x-pack/plugin/deprecation/qa/rest/src/main/java/org/elasticsearch/xpack/deprecation/TestDeprecationHeaderRestAction.java index d7999a4a62712..f53c835252bcb 100644 --- a/x-pack/plugin/deprecation/qa/rest/src/main/java/org/elasticsearch/xpack/deprecation/TestDeprecationHeaderRestAction.java +++ b/x-pack/plugin/deprecation/qa/rest/src/main/java/org/elasticsearch/xpack/deprecation/TestDeprecationHeaderRestAction.java @@ -21,7 +21,6 @@ import java.util.HashMap; import java.util.List; import java.util.Map; -import java.util.stream.Collectors; import static java.util.Collections.singletonList; import static org.elasticsearch.rest.RestRequest.Method.GET; @@ -35,20 +34,35 @@ public class TestDeprecationHeaderRestAction extends BaseRestHandler { private static final DeprecationLogger deprecationLogger = DeprecationLogger.getLogger(TestDeprecationHeaderRestAction.class); - public static final Setting TEST_DEPRECATED_SETTING_TRUE1 = - Setting.boolSetting("test.setting.deprecated.true1", true, - Setting.Property.NodeScope, Setting.Property.Deprecated, Setting.Property.Dynamic); - public static final Setting TEST_DEPRECATED_SETTING_TRUE2 = - Setting.boolSetting("test.setting.deprecated.true2", true, - Setting.Property.NodeScope, Setting.Property.Deprecated, Setting.Property.Dynamic); - public static final Setting TEST_NOT_DEPRECATED_SETTING = - Setting.boolSetting("test.setting.not_deprecated", false, - Setting.Property.NodeScope, Setting.Property.Dynamic); + public static final Setting TEST_DEPRECATED_SETTING_TRUE1 = Setting.boolSetting( + "test.setting.deprecated.true1", + true, + Setting.Property.NodeScope, + Setting.Property.Deprecated, + Setting.Property.Dynamic + ); + public static final Setting TEST_DEPRECATED_SETTING_TRUE2 = Setting.boolSetting( + "test.setting.deprecated.true2", + true, + Setting.Property.NodeScope, + Setting.Property.Deprecated, + Setting.Property.Dynamic + ); + public static final Setting TEST_NOT_DEPRECATED_SETTING = Setting.boolSetting( + "test.setting.not_deprecated", + false, + Setting.Property.NodeScope, + Setting.Property.Dynamic + ); private static final Map> SETTINGS_MAP = Map.of( - TEST_DEPRECATED_SETTING_TRUE1.getKey(), TEST_DEPRECATED_SETTING_TRUE1, - TEST_DEPRECATED_SETTING_TRUE2.getKey(), TEST_DEPRECATED_SETTING_TRUE2, - TEST_NOT_DEPRECATED_SETTING.getKey(), TEST_NOT_DEPRECATED_SETTING); + TEST_DEPRECATED_SETTING_TRUE1.getKey(), + TEST_DEPRECATED_SETTING_TRUE1, + TEST_DEPRECATED_SETTING_TRUE2.getKey(), + TEST_DEPRECATED_SETTING_TRUE2, + TEST_NOT_DEPRECATED_SETTING.getKey(), + TEST_NOT_DEPRECATED_SETTING + ); public static final String DEPRECATED_ENDPOINT = "[/_test_cluster/deprecated_settings] exists for deprecated tests"; public static final String DEPRECATED_USAGE = "[deprecated_settings] usage is deprecated. use [settings] instead"; @@ -61,8 +75,7 @@ public TestDeprecationHeaderRestAction(Settings settings) { @Override public List deprecatedRoutes() { - return singletonList( - new DeprecatedRoute(GET, "/_test_cluster/deprecated_settings", DEPRECATED_ENDPOINT)); + return singletonList(new DeprecatedRoute(GET, "/_test_cluster/deprecated_settings", DEPRECATED_ENDPOINT)); } @Override @@ -86,9 +99,9 @@ public RestChannelConsumer prepareRequest(RestRequest request, NodeClient client if (source.containsKey("deprecated_settings")) { deprecationLogger.deprecate("deprecated_settings", DEPRECATED_USAGE); - settings = (List)source.get("deprecated_settings"); + settings = (List) source.get("deprecated_settings"); } else { - settings = (List)source.get("settings"); + settings = (List) source.get("settings"); } } diff --git a/x-pack/plugin/deprecation/qa/rest/src/main/java/org/elasticsearch/xpack/deprecation/TestDeprecationPlugin.java b/x-pack/plugin/deprecation/qa/rest/src/main/java/org/elasticsearch/xpack/deprecation/TestDeprecationPlugin.java index e14f32f35680c..8297e9a5627b5 100644 --- a/x-pack/plugin/deprecation/qa/rest/src/main/java/org/elasticsearch/xpack/deprecation/TestDeprecationPlugin.java +++ b/x-pack/plugin/deprecation/qa/rest/src/main/java/org/elasticsearch/xpack/deprecation/TestDeprecationPlugin.java @@ -31,9 +31,15 @@ public class TestDeprecationPlugin extends Plugin implements ActionPlugin, SearchPlugin { @Override - public List getRestHandlers(Settings settings, RestController restController, ClusterSettings clusterSettings, - IndexScopedSettings indexScopedSettings, SettingsFilter settingsFilter, IndexNameExpressionResolver indexNameExpressionResolver, - Supplier nodesInCluster) { + public List getRestHandlers( + Settings settings, + RestController restController, + ClusterSettings clusterSettings, + IndexScopedSettings indexScopedSettings, + SettingsFilter settingsFilter, + IndexNameExpressionResolver indexNameExpressionResolver, + Supplier nodesInCluster + ) { return Collections.singletonList(new TestDeprecationHeaderRestAction(settings)); } @@ -42,13 +48,15 @@ public List> getSettings() { return Arrays.asList( TestDeprecationHeaderRestAction.TEST_DEPRECATED_SETTING_TRUE1, TestDeprecationHeaderRestAction.TEST_DEPRECATED_SETTING_TRUE2, - TestDeprecationHeaderRestAction.TEST_NOT_DEPRECATED_SETTING); + TestDeprecationHeaderRestAction.TEST_NOT_DEPRECATED_SETTING + ); } @Override public List> getQueries() { - return singletonList(new QuerySpec<>(TestDeprecatedQueryBuilder.NAME, TestDeprecatedQueryBuilder::new, - TestDeprecatedQueryBuilder::fromXContent)); + return singletonList( + new QuerySpec<>(TestDeprecatedQueryBuilder.NAME, TestDeprecatedQueryBuilder::new, TestDeprecatedQueryBuilder::fromXContent) + ); } } diff --git a/x-pack/plugin/deprecation/qa/rest/src/test/java/org/elasticsearch/xpack/deprecation/DeprecationHttpIT.java b/x-pack/plugin/deprecation/qa/rest/src/test/java/org/elasticsearch/xpack/deprecation/DeprecationHttpIT.java index 378684141cd62..36e21eb1554d5 100644 --- a/x-pack/plugin/deprecation/qa/rest/src/test/java/org/elasticsearch/xpack/deprecation/DeprecationHttpIT.java +++ b/x-pack/plugin/deprecation/qa/rest/src/test/java/org/elasticsearch/xpack/deprecation/DeprecationHttpIT.java @@ -23,7 +23,6 @@ import org.elasticsearch.common.settings.Settings; import org.elasticsearch.common.xcontent.XContentBuilder; import org.elasticsearch.common.xcontent.json.JsonXContent; -import org.elasticsearch.test.ESTestCase; import org.elasticsearch.test.rest.ESRestTestCase; import org.hamcrest.Matcher; @@ -87,8 +86,11 @@ public void testDeprecatedSettingsReturnWarnings() throws IOException { assertThat(deprecatedWarnings, hasSize(headerMatchers.size())); for (final String deprecatedWarning : deprecatedWarnings) { - assertThat("Header does not conform to expected pattern", - deprecatedWarning, matches(HeaderWarning.WARNING_HEADER_PATTERN.pattern())); + assertThat( + "Header does not conform to expected pattern", + deprecatedWarning, + matches(HeaderWarning.WARNING_HEADER_PATTERN.pattern()) + ); } final List actualWarningValues = deprecatedWarnings.stream() @@ -221,8 +223,7 @@ public void testDeprecationMessagesCanBeIndexed() throws Exception { Response response; try { response = client().performRequest(new Request("GET", "logs-deprecation-elasticsearch/_search")); - } - catch (Exception e) { + } catch (Exception e) { // It can take a moment for the index to be created. If it doesn't exist then the client // throws an exception. Translate it into an assertion error so that assertBusy() will // continue trying. @@ -250,10 +251,13 @@ public void testDeprecationMessagesCanBeIndexed() throws Exception { logger.warn(documents); assertThat(documents, hasSize(2)); - assertThat(documents, hasItems( - hasEntry("message", "[deprecated_settings] usage is deprecated. use [settings] instead"), - hasEntry("message", "[/_test_cluster/deprecated_settings] exists for deprecated tests") - )); + assertThat( + documents, + hasItems( + hasEntry("message", "[deprecated_settings] usage is deprecated. use [settings] instead"), + hasEntry("message", "[/_test_cluster/deprecated_settings] exists for deprecated tests") + ) + ); }); } finally { configureWriteDeprecationLogsToIndex(null); From f0f3edb4152c3e89b9b3eb4a93c779950accce8c Mon Sep 17 00:00:00 2001 From: Rory Hunter Date: Thu, 13 Aug 2020 16:13:37 +0100 Subject: [PATCH 45/48] Remove redundant line --- .../main/java/org/elasticsearch/test/ESSingleNodeTestCase.java | 1 - 1 file changed, 1 deletion(-) diff --git a/test/framework/src/main/java/org/elasticsearch/test/ESSingleNodeTestCase.java b/test/framework/src/main/java/org/elasticsearch/test/ESSingleNodeTestCase.java index 26d860aadbcaa..ee7be7e687c4a 100644 --- a/test/framework/src/main/java/org/elasticsearch/test/ESSingleNodeTestCase.java +++ b/test/framework/src/main/java/org/elasticsearch/test/ESSingleNodeTestCase.java @@ -230,7 +230,6 @@ private Node newNode() { plugins.add(MockHttpTransport.TestPlugin.class); } plugins.add(MockScriptService.TestPlugin.class); - LogConfigurator.setNodeName(Node.NODE_NAME_SETTING.get(settings)); Node node = new MockNode(settings, plugins, forbidPrivateIndexSettings()); try { node.start(); From 1309355584cacc19ddb9da736ee8ace9ee2f4552 Mon Sep 17 00:00:00 2001 From: Rory Hunter Date: Mon, 17 Aug 2020 12:53:08 +0100 Subject: [PATCH 46/48] Remove unused import --- .../main/java/org/elasticsearch/test/ESSingleNodeTestCase.java | 1 - 1 file changed, 1 deletion(-) diff --git a/test/framework/src/main/java/org/elasticsearch/test/ESSingleNodeTestCase.java b/test/framework/src/main/java/org/elasticsearch/test/ESSingleNodeTestCase.java index ee7be7e687c4a..4d00f33000990 100644 --- a/test/framework/src/main/java/org/elasticsearch/test/ESSingleNodeTestCase.java +++ b/test/framework/src/main/java/org/elasticsearch/test/ESSingleNodeTestCase.java @@ -32,7 +32,6 @@ import org.elasticsearch.cluster.routing.allocation.DiskThresholdSettings; import org.elasticsearch.common.Priority; import org.elasticsearch.common.Strings; -import org.elasticsearch.common.logging.LogConfigurator; import org.elasticsearch.common.settings.Settings; import org.elasticsearch.common.unit.TimeValue; import org.elasticsearch.common.util.BigArrays; From 76993c95f9696058fad4df7ce3d58cd21c9a1e06 Mon Sep 17 00:00:00 2001 From: Rory Hunter Date: Tue, 18 Aug 2020 13:00:06 +0100 Subject: [PATCH 47/48] Add warning headers solely through the log4j appender --- .../docker/src/docker/config/log4j2.properties | 6 +++++- .../docker/src/docker/config/oss/log4j2.properties | 12 ++++++++---- distribution/src/config/log4j2.properties | 6 +++++- libs/x-content/src/main/resources/log4j2.properties | 6 +++++- .../common/logging/config/log4j2.properties | 6 +++++- .../common/logging/deprecation/log4j2.properties | 6 +++++- .../common/logging/no_node_name/log4j2.properties | 6 +++++- .../common/logging/settings/log4j2.properties | 6 +++++- qa/logging-config/custom-log4j2.properties | 5 ++++- .../common/logging/json_layout/log4j2.properties | 6 +++++- .../metadata/MetadataIndexTemplateService.java | 6 +++--- .../common/logging/DeprecationLogger.java | 5 ++++- .../elasticsearch/common/logging/HeaderWarning.java | 9 +++------ .../common/logging/HeaderWarningTests.java | 2 -- .../src/main/resources/log4j2-test.properties | 5 ++++- 15 files changed, 66 insertions(+), 26 deletions(-) diff --git a/distribution/docker/src/docker/config/log4j2.properties b/distribution/docker/src/docker/config/log4j2.properties index d731dc0074543..d2616d6060a2f 100644 --- a/distribution/docker/src/docker/config/log4j2.properties +++ b/distribution/docker/src/docker/config/log4j2.properties @@ -17,10 +17,14 @@ appender.deprecation_rolling.filter.rate_limit.type = RateLimitingFilter appender.header_warning.type = HeaderWarningAppender appender.header_warning.name = header_warning +logger.header_logger.name = header_logger +logger.header_logger.level = warn +logger.header_logger.appenderRef.header_warning.ref = header_warning +logger.header_logger.additivity = false + logger.deprecation.name = org.elasticsearch.deprecation logger.deprecation.level = deprecation logger.deprecation.appenderRef.deprecation_rolling.ref = deprecation_rolling -logger.deprecation.appenderRef.header_warning.ref = header_warning logger.deprecation.additivity = false appender.index_search_slowlog_rolling.type = Console diff --git a/distribution/docker/src/docker/config/oss/log4j2.properties b/distribution/docker/src/docker/config/oss/log4j2.properties index 8ab751ec5e470..7286142a6350e 100644 --- a/distribution/docker/src/docker/config/oss/log4j2.properties +++ b/distribution/docker/src/docker/config/oss/log4j2.properties @@ -8,19 +8,23 @@ appender.rolling.layout.type_name = server rootLogger.level = info rootLogger.appenderRef.rolling.ref = rolling +appender.header_warning.type = HeaderWarningAppender +appender.header_warning.name = header_warning + +logger.header_logger.name = header_logger +logger.header_logger.level = warn +logger.header_logger.appenderRef.header_warning.ref = header_warning +logger.header_logger.additivity = false + appender.deprecation_rolling.type = Console appender.deprecation_rolling.name = deprecation_rolling appender.deprecation_rolling.layout.type = ECSJsonLayout appender.deprecation_rolling.layout.type_name = deprecation appender.deprecation_rolling.filter.rate_limit.type = RateLimitingFilter -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.header_warning.ref = header_warning logger.deprecation.additivity = false appender.index_search_slowlog_rolling.type = Console diff --git a/distribution/src/config/log4j2.properties b/distribution/src/config/log4j2.properties index c3867747d69e2..28bd21030d01e 100644 --- a/distribution/src/config/log4j2.properties +++ b/distribution/src/config/log4j2.properties @@ -74,12 +74,16 @@ appender.deprecation_rolling.strategy.max = 4 appender.header_warning.type = HeaderWarningAppender appender.header_warning.name = header_warning + +logger.header_logger.name = header_logger +logger.header_logger.level = warn +logger.header_logger.appenderRef.header_warning.ref = header_warning +logger.header_logger.additivity = false ################################################# logger.deprecation.name = org.elasticsearch.deprecation logger.deprecation.level = deprecation logger.deprecation.appenderRef.deprecation_rolling.ref = deprecation_rolling -logger.deprecation.appenderRef.header_warning.ref = header_warning logger.deprecation.additivity = false ######## Search slowlog JSON #################### diff --git a/libs/x-content/src/main/resources/log4j2.properties b/libs/x-content/src/main/resources/log4j2.properties index eb969b2b8c20b..f21ca54eccc6d 100644 --- a/libs/x-content/src/main/resources/log4j2.properties +++ b/libs/x-content/src/main/resources/log4j2.properties @@ -1,8 +1,12 @@ appender.header_warning.type = HeaderWarningAppender appender.header_warning.name = header_warning +logger.header_logger.name = header_logger +logger.header_logger.level = warn +logger.header_logger.appenderRef.header_warning.ref = header_warning +logger.header_logger.additivity = false + logger.deprecation.name = deprecation logger.deprecation.level = deprecation logger.deprecation.appenderRef.deprecation_file.ref = deprecation_file -logger.deprecation.appenderRef.header_warning.ref = header_warning logger.deprecation.additivity = false diff --git a/qa/evil-tests/src/test/resources/org/elasticsearch/common/logging/config/log4j2.properties b/qa/evil-tests/src/test/resources/org/elasticsearch/common/logging/config/log4j2.properties index 310f991a05797..1bd5013729d46 100644 --- a/qa/evil-tests/src/test/resources/org/elasticsearch/common/logging/config/log4j2.properties +++ b/qa/evil-tests/src/test/resources/org/elasticsearch/common/logging/config/log4j2.properties @@ -28,8 +28,12 @@ appender.deprecation_file.layout.pattern = [%p][%l] [%test_thread_info]%marker % appender.header_warning.type = HeaderWarningAppender appender.header_warning.name = header_warning +logger.header_logger.name = header_logger +logger.header_logger.level = warn +logger.header_logger.appenderRef.header_warning.ref = header_warning +logger.header_logger.additivity = false + logger.deprecation.name = deprecation logger.deprecation.level = warn logger.deprecation.appenderRef.deprecation_file.ref = deprecation_file -logger.deprecation.appenderRef.header_warning.ref = header_warning logger.deprecation.additivity = false diff --git a/qa/evil-tests/src/test/resources/org/elasticsearch/common/logging/deprecation/log4j2.properties b/qa/evil-tests/src/test/resources/org/elasticsearch/common/logging/deprecation/log4j2.properties index e488da2f95f58..e19735eaf92f3 100644 --- a/qa/evil-tests/src/test/resources/org/elasticsearch/common/logging/deprecation/log4j2.properties +++ b/qa/evil-tests/src/test/resources/org/elasticsearch/common/logging/deprecation/log4j2.properties @@ -23,8 +23,12 @@ appender.deprecation_file.filter.rate_limit.type = RateLimitingFilter appender.header_warning.type = HeaderWarningAppender appender.header_warning.name = header_warning +logger.header_logger.name = header_logger +logger.header_logger.level = warn +logger.header_logger.appenderRef.header_warning.ref = header_warning +logger.header_logger.additivity = false + logger.deprecation.name = deprecation logger.deprecation.level = deprecation logger.deprecation.appenderRef.deprecation_file.ref = deprecation_file -logger.deprecation.appenderRef.header_warning.ref = header_warning logger.deprecation.additivity = false diff --git a/qa/evil-tests/src/test/resources/org/elasticsearch/common/logging/no_node_name/log4j2.properties b/qa/evil-tests/src/test/resources/org/elasticsearch/common/logging/no_node_name/log4j2.properties index cdd33e965d20e..cd723924eaddc 100644 --- a/qa/evil-tests/src/test/resources/org/elasticsearch/common/logging/no_node_name/log4j2.properties +++ b/qa/evil-tests/src/test/resources/org/elasticsearch/common/logging/no_node_name/log4j2.properties @@ -22,8 +22,12 @@ appender.deprecation_file.layout.pattern = [%p][%l] %marker%m%n appender.header_warning.type = HeaderWarningAppender appender.header_warning.name = header_warning +logger.header_logger.name = header_logger +logger.header_logger.level = warn +logger.header_logger.appenderRef.header_warning.ref = header_warning +logger.header_logger.additivity = false + logger.deprecation.name = deprecation logger.deprecation.level = warn logger.deprecation.appenderRef.deprecation_file.ref = deprecation_file -logger.deprecation.appenderRef.header_warning.ref = header_warning logger.deprecation.additivity = false diff --git a/qa/evil-tests/src/test/resources/org/elasticsearch/common/logging/settings/log4j2.properties b/qa/evil-tests/src/test/resources/org/elasticsearch/common/logging/settings/log4j2.properties index 284b7bda1f7b0..c9977acacb383 100644 --- a/qa/evil-tests/src/test/resources/org/elasticsearch/common/logging/settings/log4j2.properties +++ b/qa/evil-tests/src/test/resources/org/elasticsearch/common/logging/settings/log4j2.properties @@ -23,9 +23,13 @@ appender.deprecation_file.filter.rate_limit.type = RateLimitingFilter appender.header_warning.type = HeaderWarningAppender appender.header_warning.name = header_warning +logger.header_logger.name = header_logger +logger.header_logger.level = warn +logger.header_logger.appenderRef.header_warning.ref = header_warning +logger.header_logger.additivity = false + logger.deprecation.name = org.elasticsearch.deprecation.common.settings logger.deprecation.level = deprecation logger.deprecation.appenderRef.deprecation_console.ref = console logger.deprecation.appenderRef.deprecation_file.ref = deprecation_file -logger.deprecation.appenderRef.header_warning.ref = header_warning logger.deprecation.additivity = false diff --git a/qa/logging-config/custom-log4j2.properties b/qa/logging-config/custom-log4j2.properties index c084928b7fe0e..eb21fb2ee048b 100644 --- a/qa/logging-config/custom-log4j2.properties +++ b/qa/logging-config/custom-log4j2.properties @@ -89,12 +89,15 @@ appender.deprecation_rolling_old.strategy.max = 4 ################################################# appender.header_warning.type = HeaderWarningAppender appender.header_warning.name = header_warning +logger.header_logger.name = header_logger +logger.header_logger.level = warn +logger.header_logger.appenderRef.header_warning.ref = header_warning +logger.header_logger.additivity = false ################################################# logger.deprecation.name = org.elasticsearch.deprecation logger.deprecation.level = warn 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 #################### diff --git a/qa/logging-config/src/test/resources/org/elasticsearch/common/logging/json_layout/log4j2.properties b/qa/logging-config/src/test/resources/org/elasticsearch/common/logging/json_layout/log4j2.properties index af9a0428d4f10..7bc87d663efe7 100644 --- a/qa/logging-config/src/test/resources/org/elasticsearch/common/logging/json_layout/log4j2.properties +++ b/qa/logging-config/src/test/resources/org/elasticsearch/common/logging/json_layout/log4j2.properties @@ -37,11 +37,15 @@ appender.plaintext.filter.rate_limit.type = RateLimitingFilter appender.header_warning.type = HeaderWarningAppender appender.header_warning.name = header_warning +logger.header_logger.name = header_logger +logger.header_logger.level = warn +logger.header_logger.appenderRef.header_warning.ref = header_warning +logger.header_logger.additivity = false + logger.deprecation.name = deprecation.test logger.deprecation.level = deprecation logger.deprecation.appenderRef.deprecation_rolling.ref = deprecated logger.deprecation.appenderRef.deprecatedconsole.ref = deprecatedconsole -logger.deprecation.appenderRef.header_warning.ref = header_warning logger.deprecation.additivity = false diff --git a/server/src/main/java/org/elasticsearch/cluster/metadata/MetadataIndexTemplateService.java b/server/src/main/java/org/elasticsearch/cluster/metadata/MetadataIndexTemplateService.java index 8131b0e53e5c9..e1690264beb8f 100644 --- a/server/src/main/java/org/elasticsearch/cluster/metadata/MetadataIndexTemplateService.java +++ b/server/src/main/java/org/elasticsearch/cluster/metadata/MetadataIndexTemplateService.java @@ -41,7 +41,6 @@ import org.elasticsearch.common.bytes.BytesReference; import org.elasticsearch.common.compress.CompressedXContent; import org.elasticsearch.common.inject.Inject; -import org.elasticsearch.common.logging.HeaderWarning; import org.elasticsearch.common.regex.Regex; import org.elasticsearch.common.settings.IndexScopedSettings; import org.elasticsearch.common.settings.Settings; @@ -97,6 +96,7 @@ public class MetadataIndexTemplateService { " }\n" + " }"; private static final Logger logger = LogManager.getLogger(MetadataIndexTemplateService.class); + private static final Logger headerLogger = LogManager.getLogger("header_logger"); private final ClusterService clusterService; private final AliasValidator aliasValidator; private final IndicesService indicesService; @@ -480,7 +480,7 @@ public ClusterState addIndexTemplateV2(final ClusterState currentState, final bo .collect(Collectors.joining(",")), name); logger.warn(warning); - HeaderWarning.addWarning(warning); + headerLogger.warn(warning); } ComposableIndexTemplate finalIndexTemplate = template; @@ -792,7 +792,7 @@ static ClusterState innerPutTemplate(final ClusterState currentState, PutRequest .collect(Collectors.joining(",")), request.name); logger.warn(warning); - HeaderWarning.addWarning(warning); + headerLogger.warn(warning); } else { // Otherwise, this is a hard error, the user should use V2 index templates instead String error = String.format(Locale.ROOT, "legacy template [%s] has index patterns %s matching patterns" + diff --git a/server/src/main/java/org/elasticsearch/common/logging/DeprecationLogger.java b/server/src/main/java/org/elasticsearch/common/logging/DeprecationLogger.java index 07ee55a3c97d4..6b94cca01a98e 100644 --- a/server/src/main/java/org/elasticsearch/common/logging/DeprecationLogger.java +++ b/server/src/main/java/org/elasticsearch/common/logging/DeprecationLogger.java @@ -45,6 +45,7 @@ public class DeprecationLogger { public static Level DEPRECATION = Level.forName("DEPRECATION", Level.WARN.intLevel() + 1); private final Logger logger; + private final Logger headerLogger = LogManager.getLogger("header_logger"); private DeprecationLogger(Logger parentLogger) { this.logger = parentLogger; @@ -83,7 +84,8 @@ private static String toLoggerName(final Class cls) { } /** - * Logs a message at the {@link #DEPRECATION} level. + * Logs a message at the {@link #DEPRECATION} level. The message is also sent to the header warning logger, + * so that it can be returned to the client. */ public DeprecationLoggerBuilder deprecate(final String key, final String msg, final Object... params) { return new DeprecationLoggerBuilder().withDeprecation(key, msg, params); @@ -95,6 +97,7 @@ public DeprecationLoggerBuilder withDeprecation(String key, String msg, Object[] ESLogMessage deprecationMessage = DeprecatedMessage.of(key, HeaderWarning.getXOpaqueId(), msg, params); logger.log(DEPRECATION, deprecationMessage); + headerLogger.warn(deprecationMessage); return this; } diff --git a/server/src/main/java/org/elasticsearch/common/logging/HeaderWarning.java b/server/src/main/java/org/elasticsearch/common/logging/HeaderWarning.java index 714c738a50e57..9b79930095d5b 100644 --- a/server/src/main/java/org/elasticsearch/common/logging/HeaderWarning.java +++ b/server/src/main/java/org/elasticsearch/common/logging/HeaderWarning.java @@ -77,7 +77,7 @@ public class HeaderWarning { Build.CURRENT.isSnapshot() ? "-SNAPSHOT" : "", Build.CURRENT.hash()); - private static BitSet doesNotNeedEncoding; + private static final BitSet doesNotNeedEncoding; static { doesNotNeedEncoding = new BitSet(1 + 0xFF); @@ -205,10 +205,7 @@ private static boolean assertWarningValue(final String s, final String warningVa */ public static String formatWarning(final String s) { // Assume that the common scenario won't have a string to escape and encode. - int length = WARNING_PREFIX.length() + s.length() + 3; - final StringBuilder sb = new StringBuilder(length); - sb.append(WARNING_PREFIX).append(" \"").append(escapeAndEncode(s)).append("\""); - return sb.toString(); + return WARNING_PREFIX + " \"" + escapeAndEncode(s) + "\""; } /** @@ -321,7 +318,7 @@ public static String getXOpaqueId() { .orElse(""); } - public static void addWarning(String message, Object... params) { + static void addWarning(String message, Object... params) { addWarning(THREAD_CONTEXT, message, params); } diff --git a/server/src/test/java/org/elasticsearch/common/logging/HeaderWarningTests.java b/server/src/test/java/org/elasticsearch/common/logging/HeaderWarningTests.java index 56cf86d2e7642..4dba05f4e4250 100644 --- a/server/src/test/java/org/elasticsearch/common/logging/HeaderWarningTests.java +++ b/server/src/test/java/org/elasticsearch/common/logging/HeaderWarningTests.java @@ -48,8 +48,6 @@ public class HeaderWarningTests extends ESTestCase { private static final RegexMatcher warningValueMatcher = matches(WARNING_HEADER_PATTERN.pattern()); - private final HeaderWarning logger = new HeaderWarning(); - @Override protected boolean enableWarningsCheck() { //this is a low level test for the deprecation logger, setup and checks are done manually diff --git a/test/framework/src/main/resources/log4j2-test.properties b/test/framework/src/main/resources/log4j2-test.properties index dae00805cb35f..88612e20dc7e2 100644 --- a/test/framework/src/main/resources/log4j2-test.properties +++ b/test/framework/src/main/resources/log4j2-test.properties @@ -9,6 +9,9 @@ rootLogger.appenderRef.console.ref = console appender.header_warning.type = HeaderWarningAppender appender.header_warning.name = header_warning +logger.header_logger.name = header_logger +logger.header_logger.level = warn +logger.header_logger.appenderRef.header_warning.ref = header_warning + logger.deprecation.name = org.elasticsearch.deprecation logger.deprecation.level = deprecation -logger.deprecation.appenderRef.header_warning.ref = header_warning From 3ff1015fde0cab4324fb0d7960133b588c7130f7 Mon Sep 17 00:00:00 2001 From: Rory Hunter Date: Wed, 19 Aug 2020 14:23:33 +0100 Subject: [PATCH 48/48] Fixes to JsonLoggerTest --- qa/logging-config/custom-log4j2.properties | 2 +- .../common/logging/JsonLoggerTests.java | 35 ++++++++----------- .../org/elasticsearch/test/ESTestCase.java | 4 +++ 3 files changed, 20 insertions(+), 21 deletions(-) diff --git a/qa/logging-config/custom-log4j2.properties b/qa/logging-config/custom-log4j2.properties index eb21fb2ee048b..7164264da7654 100644 --- a/qa/logging-config/custom-log4j2.properties +++ b/qa/logging-config/custom-log4j2.properties @@ -95,7 +95,7 @@ logger.header_logger.appenderRef.header_warning.ref = header_warning logger.header_logger.additivity = false ################################################# logger.deprecation.name = org.elasticsearch.deprecation -logger.deprecation.level = warn +logger.deprecation.level = deprecation logger.deprecation.appenderRef.deprecation_rolling.ref = deprecation_rolling logger.deprecation.appenderRef.deprecation_rolling_old.ref = deprecation_rolling_old logger.deprecation.additivity = false diff --git a/qa/logging-config/src/test/java/org/elasticsearch/common/logging/JsonLoggerTests.java b/qa/logging-config/src/test/java/org/elasticsearch/common/logging/JsonLoggerTests.java index 2cd1b98d3bbaa..93ca01f446878 100644 --- a/qa/logging-config/src/test/java/org/elasticsearch/common/logging/JsonLoggerTests.java +++ b/qa/logging-config/src/test/java/org/elasticsearch/common/logging/JsonLoggerTests.java @@ -87,8 +87,9 @@ public void tearDown() throws Exception { } public void testDeprecatedMessage() throws IOException { - final Logger testLogger = LogManager.getLogger("deprecation.test"); - testLogger.log(DEPRECATION, DeprecatedMessage.of("someKey", "someId","deprecated message1")); + setXOpaqueId("someId"); + final DeprecationLogger testLogger = DeprecationLogger.getLogger("test"); + testLogger.deprecate("someKey", "deprecated message1"); final Path path = PathUtils.get(System.getProperty("es.logs.base_path"), System.getProperty("es.logs.cluster_name") + "_deprecated.json"); @@ -172,15 +173,17 @@ public void testCustomMessageWithMultipleFields() throws IOException { public void testDeprecatedMessageWithoutXOpaqueId() throws IOException { - final Logger testLogger = LogManager.getLogger("deprecation.test"); - testLogger.log(DEPRECATION, DeprecatedMessage.of("a key", "someOtherId","deprecated message1")); - testLogger.log(DEPRECATION, DeprecatedMessage.of("a key", "", "deprecated message2")); - // This next message will be filtered out, because a null opaque ID has the same effect as an empty ID. - testLogger.log(DEPRECATION, DeprecatedMessage.of("a key", null,"deprecated message3")); - testLogger.log(DEPRECATION,"deprecated message4"); + final DeprecationLogger testLogger = DeprecationLogger.getLogger("test"); + + testLogger.deprecate("a key", "deprecated message1"); + + // Also test that a message sent directly to the logger comes through + final Logger rawLogger = LogManager.getLogger("deprecation.test"); + rawLogger.log(DEPRECATION, "deprecated message2"); final Path path = PathUtils.get(System.getProperty("es.logs.base_path"), System.getProperty("es.logs.cluster_name") + "_deprecated.json"); + try (Stream> stream = JsonLogsStream.mapStreamFrom(path)) { List> jsonLogs = stream .collect(Collectors.toList()); @@ -193,14 +196,6 @@ public void testDeprecatedMessageWithoutXOpaqueId() throws IOException { hasEntry("cluster.name", "elasticsearch"), hasEntry("node.name", "sample-name"), hasEntry("message", "deprecated message1"), - hasEntry("x-opaque-id", "someOtherId")), - allOf( - hasEntry("type", "deprecation"), - hasEntry("log.level", "DEPRECATION"), - hasEntry("log.logger", "deprecation.test"), - hasEntry("cluster.name", "elasticsearch"), - hasEntry("node.name", "sample-name"), - hasEntry("message", "deprecated message2"), not(hasKey("x-opaque-id")) ), allOf( @@ -209,16 +204,16 @@ public void testDeprecatedMessageWithoutXOpaqueId() throws IOException { hasEntry("log.logger", "deprecation.test"), hasEntry("cluster.name", "elasticsearch"), hasEntry("node.name", "sample-name"), - hasEntry("message", "deprecated message4"), + hasEntry("message", "deprecated message2"), not(hasKey("x-opaque-id")) ) ) ); - - assertThat(jsonLogs, not(contains(hasEntry("message", "deprecated message3")))); } - assertWarnings("deprecated message1", "deprecated message2", "deprecated message3", "deprecated message4"); + // The message sent directly to the logger does not appear in the header warnings, because + // it is DeprecationLogger that also writes it to the `header_logger` logger instance. + assertWarnings("deprecated message1"); } public void testJsonLayout() throws IOException { diff --git a/test/framework/src/main/java/org/elasticsearch/test/ESTestCase.java b/test/framework/src/main/java/org/elasticsearch/test/ESTestCase.java index 28b3286460dbd..b6afb999abc4f 100644 --- a/test/framework/src/main/java/org/elasticsearch/test/ESTestCase.java +++ b/test/framework/src/main/java/org/elasticsearch/test/ESTestCase.java @@ -149,6 +149,7 @@ import static java.util.Collections.emptyMap; import static org.elasticsearch.common.util.CollectionUtils.arrayAsArrayList; +import static org.elasticsearch.tasks.Task.X_OPAQUE_ID; import static org.hamcrest.Matchers.empty; import static org.hamcrest.Matchers.equalTo; import static org.hamcrest.Matchers.hasItem; @@ -1471,4 +1472,7 @@ protected static InetAddress randomIp(boolean v4) { } } + public void setXOpaqueId(String value) { + threadContext.putHeader(X_OPAQUE_ID, value); + } }