From a76ac5729d81208c9a35db1dcfb5c84eddf1ba21 Mon Sep 17 00:00:00 2001 From: Igor Motov Date: Tue, 13 Nov 2018 04:47:35 -1000 Subject: [PATCH] Rest HL client: Add watcher stats API (#35185) Relates to #29827 --- .../elasticsearch/client/WatcherClient.java | 29 +++ .../client/WatcherRequestConverters.java | 23 ++ .../client/watcher/ExecutionPhase.java | 50 ++++ .../client/watcher/QueuedWatch.java | 91 +++++++ .../watcher/WatchExecutionSnapshot.java | 123 ++++++++++ .../client/watcher/WatcherMetaData.java | 54 +++++ .../client/watcher/WatcherState.java | 54 +++++ .../client/watcher/WatcherStatsRequest.java | 53 ++++ .../client/watcher/WatcherStatsResponse.java | 229 ++++++++++++++++++ .../client/NodesResponseHeaderTestUtils.java | 49 ++++ .../org/elasticsearch/client/WatcherIT.java | 26 ++ .../client/WatcherRequestConvertersTests.java | 48 +++- .../documentation/WatcherDocumentationIT.java | 50 +++- .../watcher/WatcherStatsResponseTests.java | 188 ++++++++++++++ .../high-level/supported-apis.asciidoc | 2 + .../high-level/watcher/watcher-stats.asciidoc | 32 +++ 16 files changed, 1094 insertions(+), 7 deletions(-) create mode 100644 client/rest-high-level/src/main/java/org/elasticsearch/client/watcher/ExecutionPhase.java create mode 100644 client/rest-high-level/src/main/java/org/elasticsearch/client/watcher/QueuedWatch.java create mode 100644 client/rest-high-level/src/main/java/org/elasticsearch/client/watcher/WatchExecutionSnapshot.java create mode 100644 client/rest-high-level/src/main/java/org/elasticsearch/client/watcher/WatcherMetaData.java create mode 100644 client/rest-high-level/src/main/java/org/elasticsearch/client/watcher/WatcherState.java create mode 100644 client/rest-high-level/src/main/java/org/elasticsearch/client/watcher/WatcherStatsRequest.java create mode 100644 client/rest-high-level/src/main/java/org/elasticsearch/client/watcher/WatcherStatsResponse.java create mode 100644 client/rest-high-level/src/test/java/org/elasticsearch/client/NodesResponseHeaderTestUtils.java create mode 100644 client/rest-high-level/src/test/java/org/elasticsearch/client/watcher/WatcherStatsResponseTests.java create mode 100644 docs/java-rest/high-level/watcher/watcher-stats.asciidoc diff --git a/client/rest-high-level/src/main/java/org/elasticsearch/client/WatcherClient.java b/client/rest-high-level/src/main/java/org/elasticsearch/client/WatcherClient.java index 71247764566..c06493aea73 100644 --- a/client/rest-high-level/src/main/java/org/elasticsearch/client/WatcherClient.java +++ b/client/rest-high-level/src/main/java/org/elasticsearch/client/WatcherClient.java @@ -32,6 +32,8 @@ import org.elasticsearch.client.watcher.DeleteWatchRequest; import org.elasticsearch.client.watcher.DeleteWatchResponse; import org.elasticsearch.client.watcher.PutWatchRequest; import org.elasticsearch.client.watcher.PutWatchResponse; +import org.elasticsearch.client.watcher.WatcherStatsRequest; +import org.elasticsearch.client.watcher.WatcherStatsResponse; import java.io.IOException; @@ -237,4 +239,31 @@ public final class WatcherClient { ActivateWatchResponse::fromXContent, listener, singleton(404)); } + /** + * Get the watcher stats + * See + * the docs for more. + * @param request the request + * @param options the request options (e.g. headers), use {@link RequestOptions#DEFAULT} if nothing needs to be customized + * @return the response + * @throws IOException in case there is a problem sending the request or parsing back the response + */ + public WatcherStatsResponse watcherStats(WatcherStatsRequest request, RequestOptions options) throws IOException { + return restHighLevelClient.performRequestAndParseEntity(request, WatcherRequestConverters::watcherStats, options, + WatcherStatsResponse::fromXContent, emptySet()); + } + + /** + * Asynchronously get the watcher stats + * See + * the docs for more. + * @param request the request + * @param options the request options (e.g. headers), use {@link RequestOptions#DEFAULT} if nothing needs to be customized + * @param listener the listener to be notified upon request completion + */ + public void watcherStatsAsync(WatcherStatsRequest request, RequestOptions options, ActionListener listener) { + restHighLevelClient.performRequestAsyncAndParseEntity(request, WatcherRequestConverters::watcherStats, options, + WatcherStatsResponse::fromXContent, listener, emptySet()); + } + } diff --git a/client/rest-high-level/src/main/java/org/elasticsearch/client/WatcherRequestConverters.java b/client/rest-high-level/src/main/java/org/elasticsearch/client/WatcherRequestConverters.java index 57e817e083a..a017779495b 100644 --- a/client/rest-high-level/src/main/java/org/elasticsearch/client/WatcherRequestConverters.java +++ b/client/rest-high-level/src/main/java/org/elasticsearch/client/WatcherRequestConverters.java @@ -20,6 +20,7 @@ package org.elasticsearch.client; import org.apache.http.client.methods.HttpDelete; +import org.apache.http.client.methods.HttpGet; import org.apache.http.client.methods.HttpPost; import org.apache.http.client.methods.HttpPut; import org.apache.http.entity.ByteArrayEntity; @@ -29,6 +30,7 @@ import org.elasticsearch.client.watcher.ActivateWatchRequest; import org.elasticsearch.client.watcher.AckWatchRequest; import org.elasticsearch.client.watcher.StartWatchServiceRequest; import org.elasticsearch.client.watcher.StopWatchServiceRequest; +import org.elasticsearch.client.watcher.WatcherStatsRequest; import org.elasticsearch.common.bytes.BytesReference; import org.elasticsearch.client.watcher.DeleteWatchRequest; import org.elasticsearch.client.watcher.PutWatchRequest; @@ -115,4 +117,25 @@ final class WatcherRequestConverters { Request request = new Request(HttpPut.METHOD_NAME, endpoint); return request; } + + static Request watcherStats(WatcherStatsRequest watcherStatsRequest) { + RequestConverters.EndpointBuilder builder = new RequestConverters.EndpointBuilder().addPathPartAsIs("_xpack", "watcher", "stats"); + String endpoint = builder.build(); + Request request = new Request(HttpGet.METHOD_NAME, endpoint); + RequestConverters.Params parameters = new RequestConverters.Params(request); + StringBuilder metric = new StringBuilder(); + if (watcherStatsRequest.includeCurrentWatches()) { + metric.append("current_watches"); + } + if (watcherStatsRequest.includeQueuedWatches()) { + if (metric.length() > 0) { + metric.append(","); + } + metric.append("queued_watches"); + } + if (metric.length() > 0) { + parameters.putParam("metric", metric.toString()); + } + return request; + } } diff --git a/client/rest-high-level/src/main/java/org/elasticsearch/client/watcher/ExecutionPhase.java b/client/rest-high-level/src/main/java/org/elasticsearch/client/watcher/ExecutionPhase.java new file mode 100644 index 00000000000..9cdfcfba153 --- /dev/null +++ b/client/rest-high-level/src/main/java/org/elasticsearch/client/watcher/ExecutionPhase.java @@ -0,0 +1,50 @@ +/* + * 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.client.watcher; + +public enum ExecutionPhase { + + // awaiting execution of the watch + AWAITS_EXECUTION(false), + // initial phase, watch execution has started, but the input is not yet processed + STARTED(false), + // input is being executed + INPUT(false), + // condition phase is being executed + CONDITION(false), + // transform phase (optional, depends if a global transform was configured in the watch) + WATCH_TRANSFORM(false), + // actions phase, all actions, including specific action transforms + ACTIONS(false), + // missing watch, failed execution of input/condition/transform, + ABORTED(true), + // successful run + FINISHED(true); + + private final boolean sealed; + + ExecutionPhase(boolean sealed) { + this.sealed = sealed; + } + + public boolean sealed() { + return sealed; + } +} diff --git a/client/rest-high-level/src/main/java/org/elasticsearch/client/watcher/QueuedWatch.java b/client/rest-high-level/src/main/java/org/elasticsearch/client/watcher/QueuedWatch.java new file mode 100644 index 00000000000..e091c15e812 --- /dev/null +++ b/client/rest-high-level/src/main/java/org/elasticsearch/client/watcher/QueuedWatch.java @@ -0,0 +1,91 @@ +/* + * 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.client.watcher; + +import org.elasticsearch.common.ParseField; +import org.elasticsearch.common.xcontent.ConstructingObjectParser; +import org.joda.time.DateTime; + +import java.util.Objects; + +public class QueuedWatch { + + @SuppressWarnings("unchecked") + public static final ConstructingObjectParser PARSER = + new ConstructingObjectParser<>("watcher_stats_node", true, (args, c) -> new QueuedWatch( + (String) args[0], + (String) args[1], + DateTime.parse((String) args[2]), + DateTime.parse((String) args[3]) + )); + + static { + PARSER.declareString(ConstructingObjectParser.constructorArg(), new ParseField("watch_id")); + PARSER.declareString(ConstructingObjectParser.constructorArg(), new ParseField("watch_record_id")); + PARSER.declareString(ConstructingObjectParser.constructorArg(), new ParseField("triggered_time")); + PARSER.declareString(ConstructingObjectParser.constructorArg(), new ParseField("execution_time")); + } + + + private final String watchId; + private final String watchRecordId; + private final DateTime triggeredTime; + private final DateTime executionTime; + + public QueuedWatch(String watchId, String watchRecordId, DateTime triggeredTime, DateTime executionTime) { + this.watchId = watchId; + this.watchRecordId = watchRecordId; + this.triggeredTime = triggeredTime; + this.executionTime = executionTime; + } + + public String getWatchId() { + return watchId; + } + + public String getWatchRecordId() { + return watchRecordId; + } + + public DateTime getTriggeredTime() { + return triggeredTime; + } + + public DateTime getExecutionTime() { + return executionTime; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + QueuedWatch that = (QueuedWatch) o; + return Objects.equals(watchId, that.watchId) && + Objects.equals(watchRecordId, that.watchRecordId) && + Objects.equals(triggeredTime, that.triggeredTime) && + Objects.equals(executionTime, that.executionTime); + } + + @Override + public int hashCode() { + + return Objects.hash(watchId, watchRecordId, triggeredTime, executionTime); + } +} diff --git a/client/rest-high-level/src/main/java/org/elasticsearch/client/watcher/WatchExecutionSnapshot.java b/client/rest-high-level/src/main/java/org/elasticsearch/client/watcher/WatchExecutionSnapshot.java new file mode 100644 index 00000000000..d1dfcd1b0b2 --- /dev/null +++ b/client/rest-high-level/src/main/java/org/elasticsearch/client/watcher/WatchExecutionSnapshot.java @@ -0,0 +1,123 @@ +/* + * 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.client.watcher; + +import org.elasticsearch.common.ParseField; +import org.elasticsearch.common.xcontent.ConstructingObjectParser; +import org.joda.time.DateTime; + +import java.util.Arrays; +import java.util.List; +import java.util.Locale; +import java.util.Objects; + +public class WatchExecutionSnapshot { + @SuppressWarnings("unchecked") + public static final ConstructingObjectParser PARSER = + new ConstructingObjectParser<>("watcher_stats_node", true, (args, c) -> new WatchExecutionSnapshot( + (String) args[0], + (String) args[1], + DateTime.parse((String) args[2]), + DateTime.parse((String) args[3]), + ExecutionPhase.valueOf(((String) args[4]).toUpperCase(Locale.ROOT)), + args[5] == null ? null : ((List) args[5]).toArray(new String[0]), + args[6] == null ? null : ((List) args[6]).toArray(new String[0]) + )); + + static { + PARSER.declareString(ConstructingObjectParser.constructorArg(), new ParseField("watch_id")); + PARSER.declareString(ConstructingObjectParser.constructorArg(), new ParseField("watch_record_id")); + PARSER.declareString(ConstructingObjectParser.constructorArg(), new ParseField("triggered_time")); + PARSER.declareString(ConstructingObjectParser.constructorArg(), new ParseField("execution_time")); + PARSER.declareString(ConstructingObjectParser.constructorArg(), new ParseField("execution_phase")); + PARSER.declareStringArray(ConstructingObjectParser.optionalConstructorArg(), new ParseField("executed_actions")); + PARSER.declareStringArray(ConstructingObjectParser.optionalConstructorArg(), new ParseField("stack_trace")); + } + + private final String watchId; + private final String watchRecordId; + private final DateTime triggeredTime; + private final DateTime executionTime; + private final ExecutionPhase phase; + private final String[] executedActions; + private final String[] executionStackTrace; + + public WatchExecutionSnapshot(String watchId, String watchRecordId, DateTime triggeredTime, DateTime executionTime, + ExecutionPhase phase, String[] executedActions, String[] executionStackTrace) { + this.watchId = watchId; + this.watchRecordId = watchRecordId; + this.triggeredTime = triggeredTime; + this.executionTime = executionTime; + this.phase = phase; + this.executedActions = executedActions; + this.executionStackTrace = executionStackTrace; + } + + public String getWatchId() { + return watchId; + } + + public String getWatchRecordId() { + return watchRecordId; + } + + public DateTime getTriggeredTime() { + return triggeredTime; + } + + public DateTime getExecutionTime() { + return executionTime; + } + + public ExecutionPhase getPhase() { + return phase; + } + + public String[] getExecutedActions() { + return executedActions; + } + + public String[] getExecutionStackTrace() { + return executionStackTrace; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + WatchExecutionSnapshot that = (WatchExecutionSnapshot) o; + return Objects.equals(watchId, that.watchId) && + Objects.equals(watchRecordId, that.watchRecordId) && + Objects.equals(triggeredTime, that.triggeredTime) && + Objects.equals(executionTime, that.executionTime) && + phase == that.phase && + Arrays.equals(executedActions, that.executedActions) && + Arrays.equals(executionStackTrace, that.executionStackTrace); + } + + @Override + public int hashCode() { + + int result = Objects.hash(watchId, watchRecordId, triggeredTime, executionTime, phase); + result = 31 * result + Arrays.hashCode(executedActions); + result = 31 * result + Arrays.hashCode(executionStackTrace); + return result; + } +} diff --git a/client/rest-high-level/src/main/java/org/elasticsearch/client/watcher/WatcherMetaData.java b/client/rest-high-level/src/main/java/org/elasticsearch/client/watcher/WatcherMetaData.java new file mode 100644 index 00000000000..1d84fb943f0 --- /dev/null +++ b/client/rest-high-level/src/main/java/org/elasticsearch/client/watcher/WatcherMetaData.java @@ -0,0 +1,54 @@ +/* + * 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.client.watcher; + +import java.util.Objects; + +public class WatcherMetaData { + + private final boolean manuallyStopped; + + public WatcherMetaData(boolean manuallyStopped) { + this.manuallyStopped = manuallyStopped; + } + + public boolean manuallyStopped() { + return manuallyStopped; + } + @Override + public String toString() { + return "manuallyStopped["+ manuallyStopped +"]"; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + + WatcherMetaData action = (WatcherMetaData) o; + + return manuallyStopped == action.manuallyStopped; + } + + @Override + public int hashCode() { + return Objects.hash(manuallyStopped); + } +} diff --git a/client/rest-high-level/src/main/java/org/elasticsearch/client/watcher/WatcherState.java b/client/rest-high-level/src/main/java/org/elasticsearch/client/watcher/WatcherState.java new file mode 100644 index 00000000000..710f9e437c5 --- /dev/null +++ b/client/rest-high-level/src/main/java/org/elasticsearch/client/watcher/WatcherState.java @@ -0,0 +1,54 @@ +/* + * 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.client.watcher; + +public enum WatcherState { + + /** + * The watcher plugin is not running and not functional. + */ + STOPPED(0), + + /** + * The watcher plugin is performing the necessary operations to get into a started state. + */ + STARTING(1), + + /** + * The watcher plugin is running and completely functional. + */ + STARTED(2), + + /** + * The watcher plugin is shutting down and not functional. + */ + STOPPING(3); + + private final byte id; + + WatcherState(int id) { + this.id = (byte) id; + } + + public byte getId() { + return id; + } + +} + diff --git a/client/rest-high-level/src/main/java/org/elasticsearch/client/watcher/WatcherStatsRequest.java b/client/rest-high-level/src/main/java/org/elasticsearch/client/watcher/WatcherStatsRequest.java new file mode 100644 index 00000000000..5b48978c88a --- /dev/null +++ b/client/rest-high-level/src/main/java/org/elasticsearch/client/watcher/WatcherStatsRequest.java @@ -0,0 +1,53 @@ +/* + * 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.client.watcher; + +import org.elasticsearch.client.Validatable; + +/** + * A request to explicitly acknowledge a watch. + */ +public class WatcherStatsRequest implements Validatable { + + private final boolean includeCurrentWatches; + private final boolean includeQueuedWatches; + + public WatcherStatsRequest( ) { + this(true, true); + } + + public WatcherStatsRequest(boolean includeCurrentWatches, boolean includeQueuedWatches) { + this.includeCurrentWatches = includeCurrentWatches; + this.includeQueuedWatches = includeQueuedWatches; + } + + public boolean includeCurrentWatches() { + return includeCurrentWatches; + } + + public boolean includeQueuedWatches() { + return includeQueuedWatches; + } + + @Override + public String toString() { + return "stats [current=" + includeCurrentWatches + ", " + "queued=" + includeQueuedWatches + "]"; + } +} diff --git a/client/rest-high-level/src/main/java/org/elasticsearch/client/watcher/WatcherStatsResponse.java b/client/rest-high-level/src/main/java/org/elasticsearch/client/watcher/WatcherStatsResponse.java new file mode 100644 index 00000000000..708954e666b --- /dev/null +++ b/client/rest-high-level/src/main/java/org/elasticsearch/client/watcher/WatcherStatsResponse.java @@ -0,0 +1,229 @@ +/* + * 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.client.watcher; + +import org.elasticsearch.client.NodesResponseHeader; +import org.elasticsearch.common.ParseField; +import org.elasticsearch.common.collect.Tuple; +import org.elasticsearch.common.xcontent.ConstructingObjectParser; +import org.elasticsearch.common.xcontent.XContentParser; + +import java.io.IOException; +import java.util.List; +import java.util.Locale; +import java.util.Map; +import java.util.Objects; + +/** + * The response from an 'ack watch' request. + */ +public class WatcherStatsResponse { + + private final List nodes; + private final NodesResponseHeader header; + private final String clusterName; + + private final WatcherMetaData watcherMetaData; + + public WatcherStatsResponse(NodesResponseHeader header, String clusterName, WatcherMetaData watcherMetaData, List nodes) { + this.nodes = nodes; + this.header = header; + this.clusterName = clusterName; + this.watcherMetaData = watcherMetaData; + } + + /** + * @return the status of the requested watch. If an action was + * successfully acknowledged, this will be reflected in its status. + */ + public WatcherMetaData getWatcherMetaData() { + return watcherMetaData; + } + + /** + * returns a list of nodes that returned stats + */ + public List getNodes() { + return nodes; + } + + /** + * Gets information about the number of total, successful and failed nodes the request was run on. + * Also includes exceptions if relevant. + */ + public NodesResponseHeader getHeader() { + return header; + } + + /** + * Get the cluster name associated with all of the nodes. + * + * @return Never {@code null}. + */ + public String getClusterName() { + return clusterName; + } + + @SuppressWarnings("unchecked") + private static ConstructingObjectParser PARSER = + new ConstructingObjectParser<>("watcher_stats_response", true, + a -> new WatcherStatsResponse((NodesResponseHeader) a[0], (String) a[1], new WatcherMetaData((boolean) a[2]), + (List) a[3])); + + static { + PARSER.declareObject(ConstructingObjectParser.constructorArg(), NodesResponseHeader::fromXContent, new ParseField("_nodes")); + PARSER.declareString(ConstructingObjectParser.constructorArg(), new ParseField("cluster_name")); + PARSER.declareBoolean(ConstructingObjectParser.constructorArg(), new ParseField("manually_stopped")); + PARSER.declareObjectArray(ConstructingObjectParser.constructorArg(), (p, c) -> Node.PARSER.apply(p, null), + new ParseField("stats")); + } + + public static WatcherStatsResponse fromXContent(XContentParser parser) throws IOException { + return PARSER.parse(parser, null); + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + WatcherStatsResponse that = (WatcherStatsResponse) o; + return Objects.equals(nodes, that.nodes) && + Objects.equals(header, that.header) && + Objects.equals(clusterName, that.clusterName) && + Objects.equals(watcherMetaData, that.watcherMetaData); + } + + @Override + public int hashCode() { + + return Objects.hash(nodes, header, clusterName, watcherMetaData); + } + + public static class Node { + @SuppressWarnings("unchecked") + public static final ConstructingObjectParser PARSER = + new ConstructingObjectParser<>("watcher_stats_node", true, (args, c) -> new Node( + (String) args[0], + WatcherState.valueOf(((String) args[1]).toUpperCase(Locale.ROOT)), + (long) args[2], + ((Tuple) args[3]).v1(), + ((Tuple) args[3]).v2(), + (List) args[4], + (List) args[5], + (Map) args[6] + + )); + + private static final ConstructingObjectParser, Void> THREAD_POOL_PARSER = + new ConstructingObjectParser<>("execution_thread_pool", true, (args, id) -> new Tuple<>((Long) args[0], (Long) args[1])); + + static { + PARSER.declareString(ConstructingObjectParser.constructorArg(), new ParseField("node_id")); + PARSER.declareString(ConstructingObjectParser.constructorArg(), new ParseField("watcher_state")); + PARSER.declareLong(ConstructingObjectParser.constructorArg(), new ParseField("watch_count")); + PARSER.declareObject(ConstructingObjectParser.constructorArg(), THREAD_POOL_PARSER::apply, + new ParseField("execution_thread_pool")); + PARSER.declareObjectArray(ConstructingObjectParser.optionalConstructorArg(), WatchExecutionSnapshot.PARSER, + new ParseField("current_watches")); + PARSER.declareObjectArray(ConstructingObjectParser.optionalConstructorArg(), QueuedWatch.PARSER, + new ParseField("queued_watches")); + PARSER.declareObject(ConstructingObjectParser.optionalConstructorArg(), (p, c) -> p.map(), new ParseField("stats")); + + THREAD_POOL_PARSER.declareLong(ConstructingObjectParser.constructorArg(), new ParseField("queue_size")); + THREAD_POOL_PARSER.declareLong(ConstructingObjectParser.constructorArg(), new ParseField("max_size")); + } + + private final String nodeId; + + private WatcherState watcherState; + private long watchesCount; + private long threadPoolQueueSize; + private long threadPoolMaxSize; + private List snapshots; + private List queuedWatches; + private Map stats; + + + public Node(String nodeId, WatcherState watcherState, long watchesCount, long threadPoolQueueSize, long threadPoolMaxSize, + List snapshots, List queuedWatches, Map stats) { + this.nodeId = nodeId; + this.watcherState = watcherState; + this.watchesCount = watchesCount; + this.threadPoolQueueSize = threadPoolQueueSize; + this.threadPoolMaxSize = threadPoolMaxSize; + this.snapshots = snapshots; + this.queuedWatches = queuedWatches; + this.stats = stats; + } + + public String getNodeId() { + return nodeId; + } + + public long getWatchesCount() { + return watchesCount; + } + + public WatcherState getWatcherState() { + return watcherState; + } + + public long getThreadPoolQueueSize() { + return threadPoolQueueSize; + } + + public long getThreadPoolMaxSize() { + return threadPoolMaxSize; + } + + public List getSnapshots() { + return snapshots; + } + + public List getQueuedWatches() { + return queuedWatches; + } + + public Map getStats() { + return stats; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + Node node = (Node) o; + return watchesCount == node.watchesCount && + threadPoolQueueSize == node.threadPoolQueueSize && + threadPoolMaxSize == node.threadPoolMaxSize && + Objects.equals(nodeId, node.nodeId) && + watcherState == node.watcherState && + Objects.equals(snapshots, node.snapshots) && + Objects.equals(queuedWatches, node.queuedWatches) && + Objects.equals(stats, node.stats); + } + + @Override + public int hashCode() { + return Objects.hash(nodeId, watcherState, watchesCount, threadPoolQueueSize, threadPoolMaxSize, snapshots, queuedWatches, + stats); + } + } +} diff --git a/client/rest-high-level/src/test/java/org/elasticsearch/client/NodesResponseHeaderTestUtils.java b/client/rest-high-level/src/test/java/org/elasticsearch/client/NodesResponseHeaderTestUtils.java new file mode 100644 index 00000000000..09ec3f38bfd --- /dev/null +++ b/client/rest-high-level/src/test/java/org/elasticsearch/client/NodesResponseHeaderTestUtils.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.client; + +import org.elasticsearch.ElasticsearchException; +import org.elasticsearch.common.xcontent.ToXContent; +import org.elasticsearch.common.xcontent.XContentBuilder; + +import java.io.IOException; + +public class NodesResponseHeaderTestUtils { + + public static void toXContent(NodesResponseHeader header, String clusterName, XContentBuilder builder) throws IOException { + builder.startObject("_nodes"); + builder.field("total", header.getTotal()); + builder.field("successful", header.getSuccessful()); + builder.field("failed", header.getFailed()); + + if (header.getFailures().isEmpty() == false) { + builder.startArray("failures"); + for (ElasticsearchException failure : header.getFailures()) { + builder.startObject(); + failure.toXContent(builder, ToXContent.EMPTY_PARAMS); + builder.endObject(); + } + builder.endArray(); + } + + builder.endObject(); + builder.field("cluster_name", clusterName); + } + +} diff --git a/client/rest-high-level/src/test/java/org/elasticsearch/client/WatcherIT.java b/client/rest-high-level/src/test/java/org/elasticsearch/client/WatcherIT.java index c4a2242f901..8d1429d319b 100644 --- a/client/rest-high-level/src/test/java/org/elasticsearch/client/WatcherIT.java +++ b/client/rest-high-level/src/test/java/org/elasticsearch/client/WatcherIT.java @@ -32,6 +32,9 @@ import org.elasticsearch.client.watcher.ActivateWatchRequest; import org.elasticsearch.client.watcher.ActivateWatchResponse; import org.elasticsearch.client.watcher.StartWatchServiceRequest; import org.elasticsearch.client.watcher.StopWatchServiceRequest; +import org.elasticsearch.client.watcher.WatcherState; +import org.elasticsearch.client.watcher.WatcherStatsRequest; +import org.elasticsearch.client.watcher.WatcherStatsResponse; import org.elasticsearch.common.bytes.BytesArray; import org.elasticsearch.common.bytes.BytesReference; import org.elasticsearch.common.xcontent.XContentType; @@ -41,8 +44,10 @@ import org.elasticsearch.client.watcher.PutWatchRequest; import org.elasticsearch.client.watcher.PutWatchResponse; import org.elasticsearch.rest.RestStatus; +import static org.hamcrest.Matchers.empty; import static org.hamcrest.Matchers.is; import static org.hamcrest.Matchers.lessThan; +import static org.hamcrest.Matchers.not; public class WatcherIT extends ESRestHighLevelClientTestCase { @@ -50,12 +55,22 @@ public class WatcherIT extends ESRestHighLevelClientTestCase { AcknowledgedResponse response = highLevelClient().watcher().startWatchService(new StartWatchServiceRequest(), RequestOptions.DEFAULT); assertTrue(response.isAcknowledged()); + + WatcherStatsResponse stats = highLevelClient().watcher().watcherStats(new WatcherStatsRequest(), RequestOptions.DEFAULT); + assertFalse(stats.getWatcherMetaData().manuallyStopped()); + assertThat(stats.getNodes(), not(empty())); + for(WatcherStatsResponse.Node node : stats.getNodes()) { + assertEquals(WatcherState.STARTED, node.getWatcherState()); + } } public void testStopWatchService() throws Exception { AcknowledgedResponse response = highLevelClient().watcher().stopWatchService(new StopWatchServiceRequest(), RequestOptions.DEFAULT); assertTrue(response.isAcknowledged()); + + WatcherStatsResponse stats = highLevelClient().watcher().watcherStats(new WatcherStatsRequest(), RequestOptions.DEFAULT); + assertTrue(stats.getWatcherMetaData().manuallyStopped()); } public void testPutWatch() throws Exception { @@ -169,4 +184,15 @@ public class WatcherIT extends ESRestHighLevelClientTestCase { highLevelClient().watcher().activateWatch(new ActivateWatchRequest(watchId), RequestOptions.DEFAULT)); assertEquals(RestStatus.NOT_FOUND, exception.status()); } + + public void testWatcherStatsMetrics() throws Exception { + boolean includeCurrent = randomBoolean(); + boolean includeQueued = randomBoolean(); + WatcherStatsRequest request = new WatcherStatsRequest(includeCurrent, includeQueued); + + WatcherStatsResponse stats = highLevelClient().watcher().watcherStats(request, RequestOptions.DEFAULT); + assertThat(stats.getNodes(), not(empty())); + assertEquals(includeCurrent, stats.getNodes().get(0).getSnapshots() != null); + assertEquals(includeQueued, stats.getNodes().get(0).getQueuedWatches() != null); + } } diff --git a/client/rest-high-level/src/test/java/org/elasticsearch/client/WatcherRequestConvertersTests.java b/client/rest-high-level/src/test/java/org/elasticsearch/client/WatcherRequestConvertersTests.java index b0b04fd0e5b..2712dbc0438 100644 --- a/client/rest-high-level/src/test/java/org/elasticsearch/client/WatcherRequestConvertersTests.java +++ b/client/rest-high-level/src/test/java/org/elasticsearch/client/WatcherRequestConvertersTests.java @@ -20,25 +20,34 @@ package org.elasticsearch.client; import org.apache.http.client.methods.HttpDelete; +import org.apache.http.client.methods.HttpGet; import org.apache.http.client.methods.HttpPost; import org.apache.http.client.methods.HttpPut; -import org.elasticsearch.client.watcher.DeactivateWatchRequest; -import org.elasticsearch.client.watcher.ActivateWatchRequest; import org.elasticsearch.client.watcher.AckWatchRequest; -import org.elasticsearch.client.watcher.StartWatchServiceRequest; -import org.elasticsearch.client.watcher.StopWatchServiceRequest; -import org.elasticsearch.common.bytes.BytesArray; -import org.elasticsearch.common.xcontent.XContentType; +import org.elasticsearch.client.watcher.ActivateWatchRequest; +import org.elasticsearch.client.watcher.DeactivateWatchRequest; import org.elasticsearch.client.watcher.DeleteWatchRequest; import org.elasticsearch.client.watcher.PutWatchRequest; +import org.elasticsearch.client.watcher.StartWatchServiceRequest; +import org.elasticsearch.client.watcher.StopWatchServiceRequest; +import org.elasticsearch.client.watcher.WatcherStatsRequest; +import org.elasticsearch.common.Strings; +import org.elasticsearch.common.bytes.BytesArray; +import org.elasticsearch.common.xcontent.XContentType; import org.elasticsearch.test.ESTestCase; import java.io.ByteArrayOutputStream; import java.util.HashMap; +import java.util.HashSet; import java.util.Map; +import java.util.Set; import java.util.StringJoiner; +import static org.hamcrest.CoreMatchers.equalTo; +import static org.hamcrest.Matchers.hasKey; +import static org.hamcrest.Matchers.hasSize; import static org.hamcrest.Matchers.is; +import static org.hamcrest.Matchers.not; import static org.hamcrest.Matchers.nullValue; public class WatcherRequestConvertersTests extends ESTestCase { @@ -130,4 +139,31 @@ public class WatcherRequestConvertersTests extends ESTestCase { assertEquals("/_xpack/watcher/watch/" + watchId + "/_activate", request.getEndpoint()); assertThat(request.getEntity(), nullValue()); } + + public void testWatcherStatsRequest() { + boolean includeCurrent = randomBoolean(); + boolean includeQueued = randomBoolean(); + + WatcherStatsRequest watcherStatsRequest = new WatcherStatsRequest(includeCurrent, includeQueued); + + Request request = WatcherRequestConverters.watcherStats(watcherStatsRequest); + assertThat(request.getEndpoint(), equalTo("/_xpack/watcher/stats")); + assertThat(request.getMethod(), equalTo(HttpGet.METHOD_NAME)); + if (includeCurrent || includeQueued) { + assertThat(request.getParameters(), hasKey("metric")); + Set metric = Strings.tokenizeByCommaToSet(request.getParameters().get("metric")); + assertThat(metric, hasSize((includeCurrent?1:0) + (includeQueued?1:0))); + Set expectedMetric = new HashSet<>(); + if (includeCurrent) { + expectedMetric.add("current_watches"); + } + if (includeQueued) { + expectedMetric.add("queued_watches"); + } + assertThat(metric, equalTo(expectedMetric)); + } else { + assertThat(request.getParameters(), not(hasKey("metric"))); + } + assertThat(request.getEntity(), nullValue()); + } } diff --git a/client/rest-high-level/src/test/java/org/elasticsearch/client/documentation/WatcherDocumentationIT.java b/client/rest-high-level/src/test/java/org/elasticsearch/client/documentation/WatcherDocumentationIT.java index ac4fca82b2e..74562a1d17f 100644 --- a/client/rest-high-level/src/test/java/org/elasticsearch/client/documentation/WatcherDocumentationIT.java +++ b/client/rest-high-level/src/test/java/org/elasticsearch/client/documentation/WatcherDocumentationIT.java @@ -37,6 +37,8 @@ import org.elasticsearch.client.watcher.DeactivateWatchResponse; import org.elasticsearch.client.watcher.StartWatchServiceRequest; import org.elasticsearch.client.watcher.StopWatchServiceRequest; import org.elasticsearch.client.watcher.WatchStatus; +import org.elasticsearch.client.watcher.WatcherStatsRequest; +import org.elasticsearch.client.watcher.WatcherStatsResponse; import org.elasticsearch.common.bytes.BytesArray; import org.elasticsearch.common.bytes.BytesReference; import org.elasticsearch.common.xcontent.XContentType; @@ -46,6 +48,7 @@ import org.elasticsearch.client.watcher.PutWatchRequest; import org.elasticsearch.client.watcher.PutWatchResponse; import org.elasticsearch.rest.RestStatus; +import java.util.List; import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeUnit; @@ -100,7 +103,7 @@ public class WatcherDocumentationIT extends ESRestHighLevelClientTestCase { } }; // end::start-watch-service-execute-listener - + CountDownLatch latch = new CountDownLatch(1); listener = new LatchedActionListener<>(listener, latch); @@ -408,4 +411,49 @@ public class WatcherDocumentationIT extends ESRestHighLevelClientTestCase { } } + public void testWatcherStats() throws Exception { + RestHighLevelClient client = highLevelClient(); + + { + //tag::watcher-stats-request + WatcherStatsRequest request = new WatcherStatsRequest(true, true); + //end::watcher-stats-request + + //tag::watcher-stats-execute + WatcherStatsResponse response = client.watcher().watcherStats(request, RequestOptions.DEFAULT); + //end::watcher-stats-execute + + //tag::watcher-stats-response + List nodes = response.getNodes(); // <1> + //end::watcher-stats-response + } + + { + WatcherStatsRequest request = new WatcherStatsRequest(); + + // tag::watcher-stats-execute-listener + ActionListener listener = new ActionListener() { + @Override + public void onResponse(WatcherStatsResponse response) { + // <1> + } + + @Override + public void onFailure(Exception e) { + // <2> + } + }; + // end::watcher-stats-execute-listener + + CountDownLatch latch = new CountDownLatch(1); + listener = new LatchedActionListener<>(listener, latch); + + // tag::watcher-stats-execute-async + client.watcher().watcherStatsAsync(request, RequestOptions.DEFAULT, listener); // <1> + // end::watcher-stats-execute-async + + assertTrue(latch.await(30L, TimeUnit.SECONDS)); + } + } + } diff --git a/client/rest-high-level/src/test/java/org/elasticsearch/client/watcher/WatcherStatsResponseTests.java b/client/rest-high-level/src/test/java/org/elasticsearch/client/watcher/WatcherStatsResponseTests.java new file mode 100644 index 00000000000..f1efe13c761 --- /dev/null +++ b/client/rest-high-level/src/test/java/org/elasticsearch/client/watcher/WatcherStatsResponseTests.java @@ -0,0 +1,188 @@ +/* + * 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.client.watcher; + +import org.elasticsearch.client.NodesResponseHeader; +import org.elasticsearch.client.NodesResponseHeaderTestUtils; +import org.elasticsearch.common.xcontent.XContentBuilder; +import org.elasticsearch.test.ESTestCase; +import org.joda.time.DateTime; +import org.joda.time.DateTimeZone; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Locale; +import java.util.Map; + +import static org.elasticsearch.test.AbstractXContentTestCase.xContentTester; + +public class WatcherStatsResponseTests extends ESTestCase { + + public void testFromXContent() throws IOException { + xContentTester( + this::createParser, + this::createTestInstance, + this::toXContent, + WatcherStatsResponse::fromXContent) + .supportsUnknownFields(true) + .randomFieldsExcludeFilter(field -> field.endsWith("stats")) + .test(); + } + + private void toXContent(WatcherStatsResponse response, XContentBuilder builder) throws IOException { + builder.startObject(); + NodesResponseHeaderTestUtils.toXContent(response.getHeader(), response.getClusterName(), builder); + toXContent(response.getWatcherMetaData(), builder); + builder.startArray("stats"); + for (WatcherStatsResponse.Node node : response.getNodes()) { + toXContent(node, builder); + } + builder.endArray(); + builder.endObject(); + } + + private void toXContent(WatcherMetaData metaData, XContentBuilder builder) throws IOException { + builder.field("manually_stopped", metaData.manuallyStopped()); + } + + private void toXContent(WatcherStatsResponse.Node node, XContentBuilder builder) throws IOException { + builder.startObject(); + builder.field("node_id", node.getNodeId()); + builder.field("watcher_state", node.getWatcherState().toString().toLowerCase(Locale.ROOT)); + builder.field("watch_count", node.getWatchesCount()); + builder.startObject("execution_thread_pool"); + builder.field("queue_size", node.getThreadPoolQueueSize()); + builder.field("max_size", node.getThreadPoolMaxSize()); + builder.endObject(); + + if (node.getSnapshots() != null) { + builder.startArray("current_watches"); + for (WatchExecutionSnapshot snapshot : node.getSnapshots()) { + toXContent(snapshot, builder); + } + builder.endArray(); + } + if (node.getQueuedWatches() != null) { + builder.startArray("queued_watches"); + for (QueuedWatch queuedWatch : node.getQueuedWatches()) { + toXContent(queuedWatch, builder); + } + builder.endArray(); + } + if (node.getStats() != null) { + builder.field("stats", node.getStats()); + } + builder.endObject(); + } + + private void toXContent(WatchExecutionSnapshot snapshot, XContentBuilder builder) throws IOException { + builder.startObject(); + builder.field("watch_id", snapshot.getWatchId()); + builder.field("watch_record_id", snapshot.getWatchRecordId()); + builder.timeField("triggered_time", snapshot.getTriggeredTime()); + builder.timeField("execution_time", snapshot.getExecutionTime()); + builder.field("execution_phase", snapshot.getPhase()); + if (snapshot.getExecutedActions() != null) { + builder.startArray("executed_actions"); + for (String executedAction : snapshot.getExecutedActions()) { + builder.value(executedAction); + } + builder.endArray(); + } + if (snapshot.getExecutionStackTrace() != null) { + builder.startArray("stack_trace"); + for (String element : snapshot.getExecutionStackTrace()) { + builder.value(element); + } + builder.endArray(); + } + builder.endObject(); + } + + private void toXContent(QueuedWatch queuedWatch, XContentBuilder builder) throws IOException { + builder.startObject(); + builder.field("watch_id", queuedWatch.getWatchId()); + builder.field("watch_record_id", queuedWatch.getWatchRecordId()); + builder.timeField("triggered_time", queuedWatch.getTriggeredTime()); + builder.timeField("execution_time", queuedWatch.getExecutionTime()); + builder.endObject(); + } + + protected WatcherStatsResponse createTestInstance() { + int nodeCount = randomInt(10); + List nodes = new ArrayList<>(nodeCount); + for (int i = 0; i < nodeCount; i++) { + List snapshots = null; + if (randomBoolean()) { + int snapshotCount = randomInt(10); + snapshots = new ArrayList<>(snapshotCount); + + for (int j = 0; j < snapshotCount; j++) { + String[] actions = null; + if (randomBoolean()) { + actions = new String[randomInt(10)]; + for (int k = 0; k < actions.length; k++) { + actions[k] = randomAlphaOfLength(10); + } + } + String[] stackTrace = null; + if (randomBoolean()) { + stackTrace = new String[randomInt(10)]; + for (int k = 0; k < stackTrace.length; k++) { + stackTrace[k] = randomAlphaOfLength(10); + } + } + snapshots.add(new WatchExecutionSnapshot(randomAlphaOfLength(10), randomAlphaOfLength(10), + new DateTime(randomInt(), DateTimeZone.UTC), new DateTime(randomInt(), DateTimeZone.UTC), + randomFrom(ExecutionPhase.values()), actions, stackTrace)); + } + } + + List queuedWatches = null; + if(randomBoolean()) { + int queuedWatchCount = randomInt(10); + queuedWatches = new ArrayList<>(queuedWatchCount); + for (int j=0; j stats = null; + if (randomBoolean()) { + int statsCount = randomInt(10); + stats = new HashMap<>(statsCount); + for (int j=0; j> * <<{upid}-ack-watch>> * <<{upid}-activate-watch>> +* <<{upid}-watcher-stats>> include::watcher/start-watch-service.asciidoc[] include::watcher/stop-watch-service.asciidoc[] @@ -381,6 +382,7 @@ include::watcher/delete-watch.asciidoc[] include::watcher/ack-watch.asciidoc[] include::watcher/deactivate-watch.asciidoc[] include::watcher/activate-watch.asciidoc[] +include::watcher/watcher-stats.asciidoc[] == Graph APIs diff --git a/docs/java-rest/high-level/watcher/watcher-stats.asciidoc b/docs/java-rest/high-level/watcher/watcher-stats.asciidoc new file mode 100644 index 00000000000..7fd27053fcb --- /dev/null +++ b/docs/java-rest/high-level/watcher/watcher-stats.asciidoc @@ -0,0 +1,32 @@ +-- +:api: watcher-stats +:request: WatcherStatsRequest +:response: WatcherStatsResponse +-- +[id="{upid}-{api}"] +=== Watcher Stats API + +[id="{upid}-{api}-request"] +==== Execution + +{ref}/watcher-api-stats.html[Watcher Stats] returns the current {watcher} metrics. +Submit the following request to get the stats: + +["source","java",subs="attributes,callouts,macros"] +-------------------------------------------------- +include-tagged::{doc-tests-file}[{api}-request] +-------------------------------------------------- + +[id="{upid}-{api}-response"] +==== Response + +The returned `AcknowledgeResponse` contains a value on whether or not the request +was received: + +["source","java",subs="attributes,callouts,macros"] +-------------------------------------------------- +include-tagged::{doc-tests-file}[{api}-response] +-------------------------------------------------- +<1> A boolean value of `true` if successfully received, `false` otherwise. + +include::../execution.asciidoc[]