diff --git a/docs/reference/setup/logging-config.asciidoc b/docs/reference/setup/logging-config.asciidoc index f4eb69d83c00b..50b32bc4f9b81 100644 --- a/docs/reference/setup/logging-config.asciidoc +++ b/docs/reference/setup/logging-config.asciidoc @@ -234,6 +234,17 @@ The user ID is included in the `X-Opaque-ID` field in deprecation JSON logs. --------------------------- // NOTCONSOLE +Deprecation logs can be indexed into `.logs-deprecation.elasticsearch-default` data stream +`cluster.deprecation_indexing.enabled` setting is set to true. + +==== Deprecation logs throttling +Deprecation logs are deduplicated based on a deprecated feature key +and x-opaque-id so that if a feature is repeatedly used, it will not overload the deprecation logs. +This applies to both indexed deprecation logs and logs emitted to log files. +You can disable the use of `x-opaque-id` in throttling by changing +`cluster.deprecation_indexing.x_opaque_id_used.enabled` to false +See link:./server/src/main/java/org/elasticsearch/common/logging/RateLimitingFilter.java[RateLimitingFilter] + [discrete] [[json-logging]] === JSON log format 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 1f3ad69375f3d..167094f258e71 100644 --- a/server/src/main/java/org/elasticsearch/common/logging/RateLimitingFilter.java +++ b/server/src/main/java/org/elasticsearch/common/logging/RateLimitingFilter.java @@ -28,9 +28,21 @@ import static org.elasticsearch.common.logging.DeprecatedMessage.KEY_FIELD_NAME; import static org.elasticsearch.common.logging.DeprecatedMessage.X_OPAQUE_ID_FIELD_NAME; +/** + * A filter used for throttling deprecation logs. + * A throttling is based on a combined key which consists of `key` from the logged ESMessage and `x-opaque-id` + * passed by a user on a HTTP header. + * This filter works by using a lruKeyCache - a set of keys which prevents a second message with the same key to be logged. + * The lruKeyCache has a size limited to 128, which when breached will remove the oldest entries. + * + * It is possible to disable use of `x-opaque-id` as a key with {@link RateLimitingFilter#setUseXOpaqueId(boolean) } + * @see Log4j2 Filters + */ @Plugin(name = "RateLimitingFilter", category = Node.CATEGORY, elementType = Filter.ELEMENT_TYPE) public class RateLimitingFilter extends AbstractFilter { + private volatile boolean useXOpaqueId = true; + private final Set lruKeyCache = Collections.newSetFromMap(Collections.synchronizedMap(new LinkedHashMap<>() { @Override protected boolean removeEldestEntry(final Map.Entry eldest) { @@ -57,16 +69,23 @@ 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_FIELD_NAME); - - return lruKeyCache.add(xOpaqueId + key) ? Result.ACCEPT : Result.DENY; + final String key = getKey(esLogMessage); + return lruKeyCache.add(key) ? Result.ACCEPT : Result.DENY; } else { return Result.NEUTRAL; } } + private String getKey(ESLogMessage esLogMessage) { + final String key = esLogMessage.get(KEY_FIELD_NAME); + if (useXOpaqueId) { + String xOpaqueId = esLogMessage.get(X_OPAQUE_ID_FIELD_NAME); + return xOpaqueId + key; + } + return key; + } + @Override public Result filter(LogEvent event) { return filter(event.getMessage()); @@ -84,4 +103,8 @@ public static RateLimitingFilter createFilter( ) { return new RateLimitingFilter(match, mismatch); } + + public void setUseXOpaqueId(boolean useXOpaqueId) { + this.useXOpaqueId = useXOpaqueId; + } } 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 d9cbdc9b44dcd..c14eb47d55d8d 100644 --- a/server/src/test/java/org/elasticsearch/common/logging/RateLimitingFilterTests.java +++ b/server/src/test/java/org/elasticsearch/common/logging/RateLimitingFilterTests.java @@ -147,4 +147,26 @@ public void testFilterCanBeReset() { // Third time, it is allowed again assertThat(filter.filter(message), equalTo(Result.ACCEPT)); } + + public void testMessagesXOpaqueIsIgnoredWhenDisabled() { + RateLimitingFilter filter = new RateLimitingFilter(); + filter.setUseXOpaqueId(false); + filter.start(); + + // Should NOT be rate-limited because it's not in the cache + Message message = DeprecatedMessage.of(DeprecationCategory.OTHER, "key 0", "opaque-id 0", "msg 0"); + assertThat(filter.filter(message), equalTo(Result.ACCEPT)); + + // Should be rate-limited because it was just added to the cache + message = DeprecatedMessage.of(DeprecationCategory.OTHER, "key 0", "opaque-id 0", "msg 0"); + assertThat(filter.filter(message), equalTo(Result.DENY)); + + // Should be rate-limited because X-Opaque-Id is not used + message = DeprecatedMessage.of(DeprecationCategory.OTHER, "key 0", "opaque-id 1", "msg 0"); + assertThat(filter.filter(message), equalTo(Result.DENY)); + + // Should NOT be rate-limited because "key 1" it not in the cache + message = DeprecatedMessage.of(DeprecationCategory.OTHER, "key 1", "opaque-id 1", "msg 0"); + assertThat(filter.filter(message), equalTo(Result.ACCEPT)); + } } diff --git a/x-pack/plugin/core/src/main/resources/org/elasticsearch/xpack/deprecation/deprecation-indexing-settings.json b/x-pack/plugin/core/src/main/resources/org/elasticsearch/xpack/deprecation/deprecation-indexing-settings.json index 4c594c2c97520..716ec31e781c3 100644 --- a/x-pack/plugin/core/src/main/resources/org/elasticsearch/xpack/deprecation/deprecation-indexing-settings.json +++ b/x-pack/plugin/core/src/main/resources/org/elasticsearch/xpack/deprecation/deprecation-indexing-settings.json @@ -2,6 +2,7 @@ "template": { "settings": { "index": { + "hidden" : true, "lifecycle": { "name": ".deprecation-indexing-ilm-policy" }, diff --git a/x-pack/plugin/deprecation/qa/rest/src/javaRestTest/java/org/elasticsearch/xpack/deprecation/DeprecationHttpIT.java b/x-pack/plugin/deprecation/qa/rest/src/javaRestTest/java/org/elasticsearch/xpack/deprecation/DeprecationHttpIT.java index 17bab498632c4..2249f087b2bac 100644 --- a/x-pack/plugin/deprecation/qa/rest/src/javaRestTest/java/org/elasticsearch/xpack/deprecation/DeprecationHttpIT.java +++ b/x-pack/plugin/deprecation/qa/rest/src/javaRestTest/java/org/elasticsearch/xpack/deprecation/DeprecationHttpIT.java @@ -17,6 +17,7 @@ import org.elasticsearch.client.Request; import org.elasticsearch.client.RequestOptions; import org.elasticsearch.client.Response; +import org.elasticsearch.client.ResponseException; import org.elasticsearch.client.RestClient; import org.elasticsearch.client.RestClientBuilder; import org.elasticsearch.common.Strings; @@ -29,6 +30,8 @@ import org.elasticsearch.xcontent.XContentBuilder; import org.elasticsearch.xcontent.json.JsonXContent; import org.hamcrest.Matcher; +import org.junit.After; +import org.junit.Before; import java.io.IOException; import java.util.ArrayList; @@ -62,66 +65,139 @@ public class DeprecationHttpIT extends ESRestTestCase { */ private static final String DATA_STREAM_NAME = ".logs-deprecation.elasticsearch-default"; + @Before + public void assertIndexingIsEnabled() throws Exception { + configureWriteDeprecationLogsToIndex(true); + + // make sure the deprecation logs indexing is enabled + Response response = client().performRequest(new Request("GET", "/_cluster/settings?include_defaults=true&flat_settings=true")); + assertOK(response); + ObjectMapper mapper = new ObjectMapper(); + final JsonNode jsonNode = mapper.readTree(response.getEntity().getContent()); + + final boolean transientValue = jsonNode.at("/transient/cluster.deprecation_indexing.enabled").asBoolean(); + assertTrue(transientValue); + + // assert index does not exist, which will prevent previous tests to interfere + assertBusy(() -> { + try { + client().performRequest(new Request("GET", "/_data_stream/" + DATA_STREAM_NAME)); + } catch (ResponseException e) { + if (e.getResponse().getStatusLine().getStatusCode() == 404) { + return; + } + } + List> documents = getIndexedDeprecations(); + logger.warn(documents); + fail("Index should be removed on startup"); + }, 30, TimeUnit.SECONDS); + } + + @After + public void cleanUp() throws Exception { + // making sure the deprecation indexing cache is reset and index is deleted + assertBusy(() -> { + try { + client().performRequest(new Request("DELETE", "_logging/deprecation_cache")); + client().performRequest(new Request("DELETE", "/_data_stream/" + DATA_STREAM_NAME)); + } catch (Exception e) { + throw new AssertionError(e); + } + + }, 30, TimeUnit.SECONDS); + + // switch logging setting to default + configureWriteDeprecationLogsToIndex(null); + } + /** * Check that configuring deprecation settings causes a warning to be added to the * response headers. */ - public void testDeprecatedSettingsReturnWarnings() throws IOException { + public void testDeprecatedSettingsReturnWarnings() throws Exception { + try { + XContentBuilder builder = JsonXContent.contentBuilder() + .startObject() + .startObject("persistent") + .field( + TestDeprecationHeaderRestAction.TEST_DEPRECATED_SETTING_TRUE1.getKey(), + TestDeprecationHeaderRestAction.TEST_DEPRECATED_SETTING_TRUE1.getDefault(Settings.EMPTY) == false + ) + .field( + TestDeprecationHeaderRestAction.TEST_DEPRECATED_SETTING_TRUE2.getKey(), + TestDeprecationHeaderRestAction.TEST_DEPRECATED_SETTING_TRUE2.getDefault(Settings.EMPTY) == false + ) + // There should be no warning for this field + .field( + TestDeprecationHeaderRestAction.TEST_NOT_DEPRECATED_SETTING.getKey(), + TestDeprecationHeaderRestAction.TEST_NOT_DEPRECATED_SETTING.getDefault(Settings.EMPTY) == false + ) + .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( + TestDeprecationHeaderRestAction.TEST_DEPRECATED_SETTING_TRUE1, + TestDeprecationHeaderRestAction.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)); + } + + assertBusy(() -> { + List> documents = getIndexedDeprecations(); + logger.warn(documents); + assertThat(documents, hasSize(2)); + }); + + } finally { + cleanupSettings(); + } + } + + private void cleanupSettings() throws IOException { XContentBuilder builder = JsonXContent.contentBuilder() .startObject() - .startObject("transient") - .field( - TestDeprecationHeaderRestAction.TEST_DEPRECATED_SETTING_TRUE1.getKey(), - TestDeprecationHeaderRestAction.TEST_DEPRECATED_SETTING_TRUE1.getDefault(Settings.EMPTY) == false - ) - .field( - TestDeprecationHeaderRestAction.TEST_DEPRECATED_SETTING_TRUE2.getKey(), - TestDeprecationHeaderRestAction.TEST_DEPRECATED_SETTING_TRUE2.getDefault(Settings.EMPTY) == false - ) + .startObject("persistent") + .field(TestDeprecationHeaderRestAction.TEST_DEPRECATED_SETTING_TRUE1.getKey(), (Boolean) null) + .field(TestDeprecationHeaderRestAction.TEST_DEPRECATED_SETTING_TRUE2.getKey(), (Boolean) null) // There should be no warning for this field - .field( - TestDeprecationHeaderRestAction.TEST_NOT_DEPRECATED_SETTING.getKey(), - TestDeprecationHeaderRestAction.TEST_NOT_DEPRECATED_SETTING.getDefault(Settings.EMPTY) == false - ) + .field(TestDeprecationHeaderRestAction.TEST_NOT_DEPRECATED_SETTING.getKey(), (Boolean) null) .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( - TestDeprecationHeaderRestAction.TEST_DEPRECATED_SETTING_TRUE1, - TestDeprecationHeaderRestAction.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)); - } + client().performRequest(request); } /** @@ -208,6 +284,11 @@ private void doTestDeprecationWarningsAppearInHeaders() throws IOException { // trigger all deprecations Request request = new Request("GET", "/_test_cluster/deprecated_settings"); request.setEntity(buildSettingsRequest(settings, useDeprecatedField ? "deprecated_settings" : "settings")); + final RequestOptions options = request.getOptions() + .toBuilder() + .addHeader("X-Opaque-Id", "XOpaqueId-doTestDeprecationWarningsAppearInHeaders") + .build(); + request.setOptions(options); Response response = client().performRequest(request); assertOK(response); @@ -232,77 +313,79 @@ private void doTestDeprecationWarningsAppearInHeaders() throws IOException { } public void testDeprecationRouteThrottling() throws Exception { - try { - configureWriteDeprecationLogsToIndex(true); - final Request getRequest = createTestRequest("GET"); - assertOK(client().performRequest(getRequest)); + final Request deprecatedRequest = deprecatedRequest("GET", "xOpaqueId-testDeprecationRouteThrottling"); + assertOK(client().performRequest(deprecatedRequest)); - assertOK(client().performRequest(getRequest)); + assertOK(client().performRequest(deprecatedRequest)); - final Request postRequest = createTestRequest("POST"); - assertOK(client().performRequest(postRequest)); + final Request postRequest = deprecatedRequest("POST", "xOpaqueId-testDeprecationRouteThrottling"); + assertOK(client().performRequest(postRequest)); - assertBusy(() -> { - Response response; - try { - client().performRequest(new Request("POST", "/" + DATA_STREAM_NAME + "/_refresh?ignore_unavailable=true")); - response = client().performRequest(new Request("GET", "/" + DATA_STREAM_NAME + "/_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); + assertBusy(() -> { + List> documents = getIndexedDeprecations(); - ObjectMapper mapper = new ObjectMapper(); - final JsonNode jsonNode = mapper.readTree(response.getEntity().getContent()); + logger.warn(documents); + assertThat(documents, hasSize(3)); - final int hits = jsonNode.at("/hits/total/value").intValue(); - assertThat(hits, greaterThan(0)); + assertThat( + documents, + containsInAnyOrder( + allOf( + hasEntry(KEY_FIELD_NAME, "deprecated_route_POST_/_test_cluster/deprecated_settings"), + hasEntry("message", "[/_test_cluster/deprecated_settings] exists for deprecated tests") + ), + allOf( + hasEntry(KEY_FIELD_NAME, "deprecated_route_GET_/_test_cluster/deprecated_settings"), + hasEntry("message", "[/_test_cluster/deprecated_settings] exists for deprecated tests") + ), + allOf( + hasEntry(KEY_FIELD_NAME, "deprecated_settings"), + hasEntry("message", "[deprecated_settings] usage is deprecated. use [settings] instead") + ) + ) + ); + }, 30, TimeUnit.SECONDS); - List> documents = new ArrayList<>(); + } - for (int i = 0; i < hits; i++) { - final JsonNode hit = jsonNode.at("/hits/hits/" + i + "/_source"); + public void testDisableDeprecationLogIndexing() throws Exception { - final Map document = new HashMap<>(); - hit.fields().forEachRemaining(entry -> document.put(entry.getKey(), entry.getValue().textValue())); + configureWriteDeprecationLogsToIndex(true); + final Request deprecatedRequest = deprecatedRequest("GET", "xOpaqueId-testDisableDeprecationLogIndexing"); + assertOK(client().performRequest(deprecatedRequest)); + configureWriteDeprecationLogsToIndex(false); - documents.add(document); - } + final Request postRequest = deprecatedRequest("POST", "xOpaqueId-testDisableDeprecationLogIndexing"); + assertOK(client().performRequest(postRequest)); - logger.warn(documents); - assertThat(documents, hasSize(3)); + assertBusy(() -> { + List> documents = getIndexedDeprecations(); - assertThat( - documents, - containsInAnyOrder( - allOf( - hasEntry(KEY_FIELD_NAME, "deprecated_route_POST_/_test_cluster/deprecated_settings"), - hasEntry("message", "[/_test_cluster/deprecated_settings] exists for deprecated tests") - ), - allOf( - hasEntry(KEY_FIELD_NAME, "deprecated_route_GET_/_test_cluster/deprecated_settings"), - hasEntry("message", "[/_test_cluster/deprecated_settings] exists for deprecated tests") - ), - allOf( - hasEntry(KEY_FIELD_NAME, "deprecated_settings"), - hasEntry("message", "[deprecated_settings] usage is deprecated. use [settings] instead") - ) + logger.warn(documents); + assertThat(documents, hasSize(2)); + + assertThat( + documents, + containsInAnyOrder( + allOf( + hasEntry(KEY_FIELD_NAME, "deprecated_route_GET_/_test_cluster/deprecated_settings"), + hasEntry("message", "[/_test_cluster/deprecated_settings] exists for deprecated tests") + ), + allOf( + hasEntry(KEY_FIELD_NAME, "deprecated_settings"), + hasEntry("message", "[deprecated_settings] usage is deprecated. use [settings] instead") ) - ); - }, 30, TimeUnit.SECONDS); - } finally { - configureWriteDeprecationLogsToIndex(null); - client().performRequest(new Request("DELETE", "_data_stream/" + DATA_STREAM_NAME)); - } + ) + ); + }, 30, TimeUnit.SECONDS); + } - private Request createTestRequest(String method) throws IOException { + // triggers two deprecations - endpoint and setting + private Request deprecatedRequest(String method, String xOpaqueId) throws IOException { final Request getRequest = new Request(method, "/_test_cluster/deprecated_settings"); - final RequestOptions options = getRequest.getOptions().toBuilder().addHeader("X-Opaque-Id", "some xid").build(); + final RequestOptions options = getRequest.getOptions().toBuilder().addHeader("X-Opaque-Id", xOpaqueId).build(); getRequest.setOptions(options); getRequest.setEntity( buildSettingsRequest( @@ -317,390 +400,254 @@ private Request createTestRequest(String method) throws IOException { * Check that deprecation messages can be recorded to an index */ public void testDeprecationMessagesCanBeIndexed() throws Exception { - try { - configureWriteDeprecationLogsToIndex(true); - - final Request request = new Request("GET", "/_test_cluster/deprecated_settings"); - final RequestOptions options = request.getOptions().toBuilder().addHeader("X-Opaque-Id", "some xid").build(); - request.setOptions(options); - request.setEntity( - buildSettingsRequest( - Collections.singletonList(TestDeprecationHeaderRestAction.TEST_DEPRECATED_SETTING_TRUE1), - "deprecated_settings" - ) - ); - assertOK(client().performRequest(request)); - - assertBusy(() -> { - Response response; - try { - client().performRequest(new Request("POST", "/" + DATA_STREAM_NAME + "/_refresh?ignore_unavailable=true")); - response = client().performRequest(new Request("GET", "/" + DATA_STREAM_NAME + "/_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()); - - final int hits = jsonNode.at("/hits/total/value").intValue(); - assertThat(hits, greaterThan(0)); - List> documents = new ArrayList<>(); + final Request request = deprecatedRequest("GET", "xOpaqueId-testDeprecationMessagesCanBeIndexed"); + assertOK(client().performRequest(request)); - for (int i = 0; i < hits; i++) { - final JsonNode hit = jsonNode.at("/hits/hits/" + i + "/_source"); + assertBusy(() -> { + List> documents = getIndexedDeprecations(); - final Map document = new HashMap<>(); - hit.fields().forEachRemaining(entry -> document.put(entry.getKey(), entry.getValue().textValue())); + logger.warn(documents); + assertThat(documents, hasSize(2)); - documents.add(document); - } - - logger.warn(documents); - assertThat(documents, hasSize(2)); - - assertThat( - documents, - containsInAnyOrder( - allOf( - hasKey("@timestamp"), - hasKey("elasticsearch.cluster.name"), - hasKey("elasticsearch.cluster.uuid"), - hasEntry(X_OPAQUE_ID_FIELD_NAME, "some xid"), - hasEntry("elasticsearch.event.category", "settings"), - hasKey("elasticsearch.node.id"), - hasKey("elasticsearch.node.name"), - hasEntry("data_stream.dataset", "deprecation.elasticsearch"), - hasEntry("data_stream.namespace", "default"), - hasEntry("data_stream.type", "logs"), - hasEntry("ecs.version", "1.7"), - hasEntry(KEY_FIELD_NAME, "deprecated_settings"), - hasEntry("event.dataset", "deprecation.elasticsearch"), - hasEntry("log.level", "CRITICAL"), - hasKey("log.logger"), - hasEntry("message", "[deprecated_settings] usage is deprecated. use [settings] instead") - ), - allOf( - hasKey("@timestamp"), - hasKey("elasticsearch.cluster.name"), - hasKey("elasticsearch.cluster.uuid"), - hasEntry(X_OPAQUE_ID_FIELD_NAME, "some xid"), - hasEntry("elasticsearch.event.category", "api"), - hasKey("elasticsearch.node.id"), - hasKey("elasticsearch.node.name"), - hasEntry("data_stream.dataset", "deprecation.elasticsearch"), - hasEntry("data_stream.namespace", "default"), - hasEntry("data_stream.type", "logs"), - hasEntry("ecs.version", "1.7"), - hasEntry(KEY_FIELD_NAME, "deprecated_route_GET_/_test_cluster/deprecated_settings"), - hasEntry("event.dataset", "deprecation.elasticsearch"), - hasEntry("log.level", "CRITICAL"), - hasKey("log.logger"), - hasEntry("message", "[/_test_cluster/deprecated_settings] exists for deprecated tests") - ) + assertThat( + documents, + containsInAnyOrder( + allOf( + hasKey("@timestamp"), + hasKey("elasticsearch.cluster.name"), + hasKey("elasticsearch.cluster.uuid"), + hasEntry(X_OPAQUE_ID_FIELD_NAME, "xOpaqueId-testDeprecationMessagesCanBeIndexed"), + hasEntry("elasticsearch.event.category", "settings"), + hasKey("elasticsearch.node.id"), + hasKey("elasticsearch.node.name"), + hasEntry("data_stream.dataset", "deprecation.elasticsearch"), + hasEntry("data_stream.namespace", "default"), + hasEntry("data_stream.type", "logs"), + hasEntry("ecs.version", "1.7"), + hasEntry(KEY_FIELD_NAME, "deprecated_settings"), + hasEntry("event.dataset", "deprecation.elasticsearch"), + hasEntry("log.level", "CRITICAL"), + hasKey("log.logger"), + hasEntry("message", "[deprecated_settings] usage is deprecated. use [settings] instead") + ), + allOf( + hasKey("@timestamp"), + hasKey("elasticsearch.cluster.name"), + hasKey("elasticsearch.cluster.uuid"), + hasEntry(X_OPAQUE_ID_FIELD_NAME, "xOpaqueId-testDeprecationMessagesCanBeIndexed"), + hasEntry("elasticsearch.event.category", "api"), + hasKey("elasticsearch.node.id"), + hasKey("elasticsearch.node.name"), + hasEntry("data_stream.dataset", "deprecation.elasticsearch"), + hasEntry("data_stream.namespace", "default"), + hasEntry("data_stream.type", "logs"), + hasEntry("ecs.version", "1.7"), + hasEntry(KEY_FIELD_NAME, "deprecated_route_GET_/_test_cluster/deprecated_settings"), + hasEntry("event.dataset", "deprecation.elasticsearch"), + hasEntry("log.level", "CRITICAL"), + hasKey("log.logger"), + hasEntry("message", "[/_test_cluster/deprecated_settings] exists for deprecated tests") ) - ); - }, 30, TimeUnit.SECONDS); - } finally { - configureWriteDeprecationLogsToIndex(null); - client().performRequest(new Request("DELETE", "_data_stream/" + DATA_STREAM_NAME)); - } + ) + ); + }, 30, TimeUnit.SECONDS); + } /** * Check that deprecation messages with WARN level can be recorded to an index */ public void testDeprecationWarnMessagesCanBeIndexed() throws Exception { - try { - configureWriteDeprecationLogsToIndex(true); - - final Request request = new Request("GET", "/_test_cluster/deprecated_settings"); - final RequestOptions options = request.getOptions().toBuilder().addHeader("X-Opaque-Id", "some xid").build(); - request.setOptions(options); - request.setEntity( - buildSettingsRequest( - Collections.singletonList(TestDeprecationHeaderRestAction.TEST_DEPRECATED_SETTING_TRUE1), - "deprecation_warning" - ) - ); - assertOK(client().performRequest(request)); - assertBusy(() -> { - Response response; - try { - client().performRequest(new Request("POST", "/" + DATA_STREAM_NAME + "/_refresh?ignore_unavailable=true")); - response = client().performRequest(new Request("GET", "/" + DATA_STREAM_NAME + "/_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()); - - final int hits = jsonNode.at("/hits/total/value").intValue(); - assertThat(hits, greaterThan(0)); - - List> documents = new ArrayList<>(); - - for (int i = 0; i < hits; i++) { - final JsonNode hit = jsonNode.at("/hits/hits/" + i + "/_source"); - - final Map document = new HashMap<>(); - hit.fields().forEachRemaining(entry -> document.put(entry.getKey(), entry.getValue().textValue())); + final Request request = new Request("GET", "/_test_cluster/deprecated_settings"); + final RequestOptions options = request.getOptions() + .toBuilder() + .addHeader("X-Opaque-Id", "xOpaqueId-testDeprecationWarnMessagesCanBeIndexed") + .build(); + request.setOptions(options); + request.setEntity( + buildSettingsRequest( + Collections.singletonList(TestDeprecationHeaderRestAction.TEST_DEPRECATED_SETTING_TRUE1), + "deprecation_warning" + ) + ); + assertOK(client().performRequest(request)); - documents.add(document); - } + assertBusy(() -> { + List> documents = getIndexedDeprecations(); - logger.warn(documents); - assertThat(documents, hasSize(2)); + logger.warn(documents); + assertThat(documents, hasSize(2)); - assertThat( - documents, - containsInAnyOrder( - allOf( - hasKey("@timestamp"), - hasKey("elasticsearch.cluster.name"), - hasKey("elasticsearch.cluster.uuid"), - hasEntry(X_OPAQUE_ID_FIELD_NAME, "some xid"), - hasEntry("elasticsearch.event.category", "settings"), - hasKey("elasticsearch.node.id"), - hasKey("elasticsearch.node.name"), - hasEntry("data_stream.dataset", "deprecation.elasticsearch"), - hasEntry("data_stream.namespace", "default"), - hasEntry("data_stream.type", "logs"), - hasEntry("ecs.version", "1.7"), - hasEntry(KEY_FIELD_NAME, "deprecated_warn_settings"), - hasEntry("event.dataset", "deprecation.elasticsearch"), - hasEntry("log.level", "WARN"), - hasKey("log.logger"), - hasEntry("message", "[deprecated_warn_settings] usage is deprecated but won't be breaking in next version") - ), - allOf( - hasKey("@timestamp"), - hasKey("elasticsearch.cluster.name"), - hasKey("elasticsearch.cluster.uuid"), - hasEntry(X_OPAQUE_ID_FIELD_NAME, "some xid"), - hasEntry("elasticsearch.event.category", "api"), - hasKey("elasticsearch.node.id"), - hasKey("elasticsearch.node.name"), - hasEntry("data_stream.dataset", "deprecation.elasticsearch"), - hasEntry("data_stream.namespace", "default"), - hasEntry("data_stream.type", "logs"), - hasEntry("ecs.version", "1.7"), - hasEntry(KEY_FIELD_NAME, "deprecated_route_GET_/_test_cluster/deprecated_settings"), - hasEntry("event.dataset", "deprecation.elasticsearch"), - hasEntry("log.level", "CRITICAL"), - hasKey("log.logger"), - hasEntry("message", "[/_test_cluster/deprecated_settings] exists for deprecated tests") - ) + assertThat( + documents, + containsInAnyOrder( + allOf( + hasKey("@timestamp"), + hasKey("elasticsearch.cluster.name"), + hasKey("elasticsearch.cluster.uuid"), + hasEntry(X_OPAQUE_ID_FIELD_NAME, "xOpaqueId-testDeprecationWarnMessagesCanBeIndexed"), + hasEntry("elasticsearch.event.category", "settings"), + hasKey("elasticsearch.node.id"), + hasKey("elasticsearch.node.name"), + hasEntry("data_stream.dataset", "deprecation.elasticsearch"), + hasEntry("data_stream.namespace", "default"), + hasEntry("data_stream.type", "logs"), + hasEntry("ecs.version", "1.7"), + hasEntry(KEY_FIELD_NAME, "deprecated_warn_settings"), + hasEntry("event.dataset", "deprecation.elasticsearch"), + hasEntry("log.level", "WARN"), + hasKey("log.logger"), + hasEntry("message", "[deprecated_warn_settings] usage is deprecated but won't be breaking in next version") + ), + allOf( + hasKey("@timestamp"), + hasKey("elasticsearch.cluster.name"), + hasKey("elasticsearch.cluster.uuid"), + hasEntry(X_OPAQUE_ID_FIELD_NAME, "xOpaqueId-testDeprecationWarnMessagesCanBeIndexed"), + hasEntry("elasticsearch.event.category", "api"), + hasKey("elasticsearch.node.id"), + hasKey("elasticsearch.node.name"), + hasEntry("data_stream.dataset", "deprecation.elasticsearch"), + hasEntry("data_stream.namespace", "default"), + hasEntry("data_stream.type", "logs"), + hasEntry("ecs.version", "1.7"), + hasEntry(KEY_FIELD_NAME, "deprecated_route_GET_/_test_cluster/deprecated_settings"), + hasEntry("event.dataset", "deprecation.elasticsearch"), + hasEntry("log.level", "CRITICAL"), + hasKey("log.logger"), + hasEntry("message", "[/_test_cluster/deprecated_settings] exists for deprecated tests") ) - ); - }, 30, TimeUnit.SECONDS); - } finally { - configureWriteDeprecationLogsToIndex(null); - client().performRequest(new Request("DELETE", "_data_stream/" + DATA_STREAM_NAME)); - } + ) + ); + }, 30, TimeUnit.SECONDS); + } /** * Check that log messages about REST API compatibility are recorded to an index */ public void testCompatibleMessagesCanBeIndexed() throws Exception { - try { - configureWriteDeprecationLogsToIndex(true); - - final Request compatibleRequest = new Request("GET", "/_test_cluster/deprecated_settings"); - final RequestOptions compatibleOptions = compatibleRequest.getOptions() - .toBuilder() - .addHeader("X-Opaque-Id", "some xid") - .addHeader("Accept", "application/vnd.elasticsearch+json;compatible-with=" + RestApiVersion.minimumSupported().major) - .addHeader("Content-Type", "application/vnd.elasticsearch+json;compatible-with=" + RestApiVersion.minimumSupported().major) - .build(); - compatibleRequest.setOptions(compatibleOptions); - compatibleRequest.setEntity( - buildSettingsRequest( - Collections.singletonList(TestDeprecationHeaderRestAction.TEST_DEPRECATED_SETTING_TRUE1), - "deprecated_settings" - ) - ); - Response deprecatedApiResponse = client().performRequest(compatibleRequest); - assertOK(deprecatedApiResponse); - - final List deprecatedWarnings = getWarningHeaders(deprecatedApiResponse.getHeaders()); - final List actualWarningValues = deprecatedWarnings.stream() - .map(s -> HeaderWarning.extractWarningValueFromWarningHeader(s, true)) - .collect(Collectors.toList()); - assertThat( - actualWarningValues, - containsInAnyOrder( - TestDeprecationHeaderRestAction.DEPRECATED_ENDPOINT, - TestDeprecationHeaderRestAction.COMPATIBLE_API_USAGE - ) - ); - - assertBusy(() -> { - Response response; - try { - client().performRequest(new Request("POST", "/" + DATA_STREAM_NAME + "/_refresh?ignore_unavailable=true")); - response = client().performRequest(new Request("GET", "/" + DATA_STREAM_NAME + "/_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()); - final int hits = jsonNode.at("/hits/total/value").intValue(); - assertThat(hits, greaterThan(0)); - - List> documents = new ArrayList<>(); - - for (int i = 0; i < hits; i++) { - final JsonNode hit = jsonNode.at("/hits/hits/" + i + "/_source"); + final Request compatibleRequest = new Request("GET", "/_test_cluster/deprecated_settings"); + final RequestOptions compatibleOptions = compatibleRequest.getOptions() + .toBuilder() + .addHeader("X-Opaque-Id", "xOpaqueId-testCompatibleMessagesCanBeIndexed") + .addHeader("Accept", "application/vnd.elasticsearch+json;compatible-with=" + RestApiVersion.minimumSupported().major) + .addHeader("Content-Type", "application/vnd.elasticsearch+json;compatible-with=" + RestApiVersion.minimumSupported().major) + .build(); + compatibleRequest.setOptions(compatibleOptions); + compatibleRequest.setEntity( + buildSettingsRequest( + Collections.singletonList(TestDeprecationHeaderRestAction.TEST_DEPRECATED_SETTING_TRUE1), + "deprecated_settings" + ) + ); + Response deprecatedApiResponse = client().performRequest(compatibleRequest); + assertOK(deprecatedApiResponse); - final Map document = new HashMap<>(); - hit.fields().forEachRemaining(entry -> document.put(entry.getKey(), entry.getValue().textValue())); + final List deprecatedWarnings = getWarningHeaders(deprecatedApiResponse.getHeaders()); + final List actualWarningValues = deprecatedWarnings.stream() + .map(s -> HeaderWarning.extractWarningValueFromWarningHeader(s, true)) + .collect(Collectors.toList()); + assertThat( + actualWarningValues, + containsInAnyOrder(TestDeprecationHeaderRestAction.DEPRECATED_ENDPOINT, TestDeprecationHeaderRestAction.COMPATIBLE_API_USAGE) + ); - documents.add(document); - } + assertBusy(() -> { + List> documents = getIndexedDeprecations(); - logger.warn(documents); - assertThat(documents, hasSize(2)); + logger.warn(documents); + assertThat(documents, hasSize(2)); - assertThat( - documents, - containsInAnyOrder( - allOf( - hasKey("@timestamp"), - hasKey("elasticsearch.cluster.name"), - hasKey("elasticsearch.cluster.uuid"), - hasEntry(X_OPAQUE_ID_FIELD_NAME, "some xid"), - hasEntry("elasticsearch.event.category", "compatible_api"), - hasKey("elasticsearch.node.id"), - hasKey("elasticsearch.node.name"), - hasEntry("data_stream.dataset", "deprecation.elasticsearch"), - hasEntry("data_stream.namespace", "default"), - hasEntry("data_stream.type", "logs"), - hasEntry("ecs.version", "1.7"), - hasEntry(KEY_FIELD_NAME, "compatible_key"), - hasEntry("event.dataset", "deprecation.elasticsearch"), - hasEntry("log.level", "CRITICAL"), - hasKey("log.logger"), - hasEntry("message", "You are using a compatible API for this request") - ), - allOf( - hasKey("@timestamp"), - hasKey("elasticsearch.cluster.name"), - hasKey("elasticsearch.cluster.uuid"), - hasEntry(X_OPAQUE_ID_FIELD_NAME, "some xid"), - hasEntry("elasticsearch.event.category", "api"), - hasKey("elasticsearch.node.id"), - hasKey("elasticsearch.node.name"), - hasEntry("data_stream.dataset", "deprecation.elasticsearch"), - hasEntry("data_stream.namespace", "default"), - hasEntry("data_stream.type", "logs"), - hasEntry("ecs.version", "1.7"), - hasEntry(KEY_FIELD_NAME, "deprecated_route_GET_/_test_cluster/deprecated_settings"), - hasEntry("event.dataset", "deprecation.elasticsearch"), - hasEntry("log.level", "CRITICAL"), - hasKey("log.logger"), - hasEntry("message", "[/_test_cluster/deprecated_settings] exists for deprecated tests") - ) + assertThat( + documents, + containsInAnyOrder( + allOf( + hasKey("@timestamp"), + hasKey("elasticsearch.cluster.name"), + hasKey("elasticsearch.cluster.uuid"), + hasEntry(X_OPAQUE_ID_FIELD_NAME, "xOpaqueId-testCompatibleMessagesCanBeIndexed"), + hasEntry("elasticsearch.event.category", "compatible_api"), + hasKey("elasticsearch.node.id"), + hasKey("elasticsearch.node.name"), + hasEntry("data_stream.dataset", "deprecation.elasticsearch"), + hasEntry("data_stream.namespace", "default"), + hasEntry("data_stream.type", "logs"), + hasEntry("ecs.version", "1.7"), + hasEntry(KEY_FIELD_NAME, "compatible_key"), + hasEntry("event.dataset", "deprecation.elasticsearch"), + hasEntry("log.level", "CRITICAL"), + hasKey("log.logger"), + hasEntry("message", "You are using a compatible API for this request") + ), + allOf( + hasKey("@timestamp"), + hasKey("elasticsearch.cluster.name"), + hasKey("elasticsearch.cluster.uuid"), + hasEntry(X_OPAQUE_ID_FIELD_NAME, "xOpaqueId-testCompatibleMessagesCanBeIndexed"), + hasEntry("elasticsearch.event.category", "api"), + hasKey("elasticsearch.node.id"), + hasKey("elasticsearch.node.name"), + hasEntry("data_stream.dataset", "deprecation.elasticsearch"), + hasEntry("data_stream.namespace", "default"), + hasEntry("data_stream.type", "logs"), + hasEntry("ecs.version", "1.7"), + hasEntry(KEY_FIELD_NAME, "deprecated_route_GET_/_test_cluster/deprecated_settings"), + hasEntry("event.dataset", "deprecation.elasticsearch"), + hasEntry("log.level", "CRITICAL"), + hasKey("log.logger"), + hasEntry("message", "[/_test_cluster/deprecated_settings] exists for deprecated tests") ) - ); - }, 30, TimeUnit.SECONDS); - } finally { - configureWriteDeprecationLogsToIndex(null); - client().performRequest(new Request("DELETE", "_data_stream/" + DATA_STREAM_NAME)); - } + ) + ); + }, 30, TimeUnit.SECONDS); + } /** * Check that deprecation messages can be recorded to an index */ public void testDeprecationIndexingCacheReset() throws Exception { - try { - configureWriteDeprecationLogsToIndex(true); - - final Request getRequest = createTestRequest("GET"); - assertOK(client().performRequest(getRequest)); - client().performRequest(new Request("DELETE", "/_logging/deprecation_cache")); + final Request deprecatedRequest = deprecatedRequest("GET", "xOpaqueId-testDeprecationIndexingCacheReset"); + assertOK(client().performRequest(deprecatedRequest)); - assertOK(client().performRequest(getRequest)); + client().performRequest(new Request("DELETE", "/_logging/deprecation_cache")); - assertBusy(() -> { - Response response; - try { - client().performRequest(new Request("POST", "/" + DATA_STREAM_NAME + "/_refresh?ignore_unavailable=true")); - response = client().performRequest(new Request("GET", "/" + DATA_STREAM_NAME + "/_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()); + assertOK(client().performRequest(deprecatedRequest)); - final int hits = jsonNode.at("/hits/total/value").intValue(); - assertThat(hits, greaterThan(0)); + assertBusy(() -> { + List> documents = getIndexedDeprecations(); - List> documents = new ArrayList<>(); + logger.warn(documents); + assertThat(documents, hasSize(4)); - for (int i = 0; i < hits; i++) { - final JsonNode hit = jsonNode.at("/hits/hits/" + i + "/_source"); - - 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(4)); - - assertThat( - documents, - containsInAnyOrder( - allOf( - hasEntry(KEY_FIELD_NAME, "deprecated_route_GET_/_test_cluster/deprecated_settings"), - hasEntry("message", "[/_test_cluster/deprecated_settings] exists for deprecated tests") - ), - allOf( - hasEntry(KEY_FIELD_NAME, "deprecated_route_GET_/_test_cluster/deprecated_settings"), - hasEntry("message", "[/_test_cluster/deprecated_settings] exists for deprecated tests") - ), - allOf( - hasEntry(KEY_FIELD_NAME, "deprecated_settings"), - hasEntry("message", "[deprecated_settings] usage is deprecated. use [settings] instead") - ), - allOf( - hasEntry(KEY_FIELD_NAME, "deprecated_settings"), - hasEntry("message", "[deprecated_settings] usage is deprecated. use [settings] instead") - ) + assertThat( + documents, + containsInAnyOrder( + allOf( + hasEntry(KEY_FIELD_NAME, "deprecated_route_GET_/_test_cluster/deprecated_settings"), + hasEntry("message", "[/_test_cluster/deprecated_settings] exists for deprecated tests") + ), + allOf( + hasEntry(KEY_FIELD_NAME, "deprecated_route_GET_/_test_cluster/deprecated_settings"), + hasEntry("message", "[/_test_cluster/deprecated_settings] exists for deprecated tests") + ), + allOf( + hasEntry(KEY_FIELD_NAME, "deprecated_settings"), + hasEntry("message", "[deprecated_settings] usage is deprecated. use [settings] instead") + ), + allOf( + hasEntry(KEY_FIELD_NAME, "deprecated_settings"), + hasEntry("message", "[deprecated_settings] usage is deprecated. use [settings] instead") ) - ); - }, 30, TimeUnit.SECONDS); - } finally { - configureWriteDeprecationLogsToIndex(null); - client().performRequest(new Request("DELETE", "_data_stream/" + DATA_STREAM_NAME)); - } + ) + ); + }, 30, TimeUnit.SECONDS); + } private void configureWriteDeprecationLogsToIndex(Boolean value) throws IOException { @@ -710,6 +657,38 @@ private void configureWriteDeprecationLogsToIndex(Boolean value) throws IOExcept assertOK(response); } + private List> getIndexedDeprecations() throws IOException { + Response response; + try { + client().performRequest(new Request("POST", "/" + DATA_STREAM_NAME + "/_refresh?ignore_unavailable=true")); + response = client().performRequest(new Request("GET", "/" + DATA_STREAM_NAME + "/_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()); + + final int hits = jsonNode.at("/hits/total/value").intValue(); + assertThat(hits, greaterThan(0)); + + List> documents = new ArrayList<>(); + + for (int i = 0; i < hits; i++) { + final JsonNode hit = jsonNode.at("/hits/hits/" + i + "/_source"); + + final Map document = new HashMap<>(); + hit.fields().forEachRemaining(entry -> document.put(entry.getKey(), entry.getValue().textValue())); + + documents.add(document); + } + return documents; + } + private List getWarningHeaders(Header[] headers) { List warnings = new ArrayList<>(); 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 53ae8eb4b2252..b89ebd71c9475 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 @@ -40,13 +40,26 @@ import java.util.List; import java.util.function.Supplier; -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 { + public static final Setting WRITE_DEPRECATION_LOGS_TO_INDEX = Setting.boolSetting( + "cluster.deprecation_indexing.enabled", + false, + Setting.Property.NodeScope, + Setting.Property.Dynamic + ); + + public static final Setting USE_X_OPAQUE_ID_IN_FILTERING = Setting.boolSetting( + "cluster.deprecation_indexing.x_opaque_id_used.enabled", + true, + Setting.Property.NodeScope, + Setting.Property.Dynamic + ); + @Override public List> getActions() { return List.of( @@ -84,16 +97,25 @@ public Collection createComponents( templateRegistry.initialize(); final RateLimitingFilter rateLimitingFilterForIndexing = new RateLimitingFilter(); + // enable on start. + rateLimitingFilterForIndexing.setUseXOpaqueId(USE_X_OPAQUE_ID_IN_FILTERING.getDefault(environment.settings())); + + final DeprecationIndexingComponent component = new DeprecationIndexingComponent(client, + environment.settings(), + rateLimitingFilterForIndexing, + WRITE_DEPRECATION_LOGS_TO_INDEX.getDefault(environment.settings()) //pass the default on startup + ); - final DeprecationIndexingComponent component = new DeprecationIndexingComponent(client, environment.settings(), - rateLimitingFilterForIndexing); - clusterService.addListener(component); + clusterService.getClusterSettings().addSettingsUpdateConsumer(USE_X_OPAQUE_ID_IN_FILTERING, + rateLimitingFilterForIndexing::setUseXOpaqueId); + clusterService.getClusterSettings().addSettingsUpdateConsumer(WRITE_DEPRECATION_LOGS_TO_INDEX, + component::enableDeprecationLogIndexing); return List.of(component, rateLimitingFilterForIndexing); } @Override public List> getSettings() { - return List.of(WRITE_DEPRECATION_LOGS_TO_INDEX); + return List.of(USE_X_OPAQUE_ID_IN_FILTERING, WRITE_DEPRECATION_LOGS_TO_INDEX); } } 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 96aac9115fc08..3b1ce5359d1fb 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,6 +8,7 @@ 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; @@ -20,19 +21,15 @@ 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.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; import org.elasticsearch.common.settings.Settings; import org.elasticsearch.common.unit.ByteSizeUnit; import org.elasticsearch.common.unit.ByteSizeValue; -import org.elasticsearch.core.TimeValue; import org.elasticsearch.common.util.concurrent.EsExecutors; +import org.elasticsearch.core.TimeValue; import org.elasticsearch.xpack.core.ClientHelper; import java.util.Arrays; @@ -44,21 +41,15 @@ * 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 { +public class DeprecationIndexingComponent extends AbstractLifecycleComponent { 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; private final BulkProcessor processor; private final RateLimitingFilter rateLimitingFilterForIndexing; - public DeprecationIndexingComponent(Client client, Settings settings, RateLimitingFilter rateLimitingFilterForIndexing) { + public DeprecationIndexingComponent(Client client, Settings settings, RateLimitingFilter rateLimitingFilterForIndexing, + boolean enableDeprecationLogIndexingDefault) { this.rateLimitingFilterForIndexing = rateLimitingFilterForIndexing; this.processor = getBulkProcessor(new OriginSettingClient(client, ClientHelper.DEPRECATION_ORIGIN), settings); @@ -74,6 +65,7 @@ public DeprecationIndexingComponent(Client client, Settings settings, RateLimiti this.appender = new DeprecationIndexingAppender("deprecation_indexing_appender", rateLimitingFilterForIndexing, ecsLayout, consumer); + enableDeprecationLogIndexing(enableDeprecationLogIndexingDefault); } @Override @@ -93,24 +85,19 @@ protected void doClose() { this.processor.close(); } - /** - * 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) { - final ClusterState state = event.state(); - final boolean newEnabled = WRITE_DEPRECATION_LOGS_TO_INDEX.get(state.getMetadata().settings()); + + public void enableDeprecationLogIndexing(boolean newEnabled) { if (appender.isEnabled() != newEnabled) { + appender.setEnabled(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.rateLimitingFilterForIndexing.reset(); + } else { + // we have flipped from enabled to disabled. A processor could have accumulated some requests, so we have to flush it + this.processor.flush(); } - appender.setEnabled(newEnabled); } }