Rest HL client: Add watcher stats API (#35185)

Relates to #29827
This commit is contained in:
Igor Motov 2018-11-13 04:47:35 -10:00 committed by GitHub
parent 4b5fbada9f
commit a76ac5729d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
16 changed files with 1094 additions and 7 deletions

View File

@ -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 <a href="https://www.elastic.co/guide/en/elasticsearch/reference/current/watcher-api-stats.html">
* the docs</a> 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 <a href="https://www.elastic.co/guide/en/elasticsearch/reference/current/watcher-api-stats.html">
* the docs</a> 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<WatcherStatsResponse> listener) {
restHighLevelClient.performRequestAsyncAndParseEntity(request, WatcherRequestConverters::watcherStats, options,
WatcherStatsResponse::fromXContent, listener, emptySet());
}
}

View File

@ -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;
}
}

View File

@ -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;
}
}

View File

@ -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<QueuedWatch, Void> 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);
}
}

View File

@ -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<WatchExecutionSnapshot, Void> 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<String>) args[5]).toArray(new String[0]),
args[6] == null ? null : ((List<String>) 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;
}
}

View File

@ -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);
}
}

View File

@ -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;
}
}

View File

@ -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 + "]";
}
}

View File

@ -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<Node> nodes;
private final NodesResponseHeader header;
private final String clusterName;
private final WatcherMetaData watcherMetaData;
public WatcherStatsResponse(NodesResponseHeader header, String clusterName, WatcherMetaData watcherMetaData, List<Node> 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<Node> 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<WatcherStatsResponse, Void> PARSER =
new ConstructingObjectParser<>("watcher_stats_response", true,
a -> new WatcherStatsResponse((NodesResponseHeader) a[0], (String) a[1], new WatcherMetaData((boolean) a[2]),
(List<Node>) 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<Node, Void> 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<Long, Long>) args[3]).v1(),
((Tuple<Long, Long>) args[3]).v2(),
(List<WatchExecutionSnapshot>) args[4],
(List<QueuedWatch>) args[5],
(Map<String, Object>) args[6]
));
private static final ConstructingObjectParser<Tuple<Long, Long>, 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<WatchExecutionSnapshot> snapshots;
private List<QueuedWatch> queuedWatches;
private Map<String, Object> stats;
public Node(String nodeId, WatcherState watcherState, long watchesCount, long threadPoolQueueSize, long threadPoolMaxSize,
List<WatchExecutionSnapshot> snapshots, List<QueuedWatch> queuedWatches, Map<String, Object> 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<WatchExecutionSnapshot> getSnapshots() {
return snapshots;
}
public List<QueuedWatch> getQueuedWatches() {
return queuedWatches;
}
public Map<String, Object> 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);
}
}
}

View File

@ -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);
}
}

View File

@ -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);
}
}

View File

@ -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<String> metric = Strings.tokenizeByCommaToSet(request.getParameters().get("metric"));
assertThat(metric, hasSize((includeCurrent?1:0) + (includeQueued?1:0)));
Set<String> 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());
}
}

View File

@ -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<WatcherStatsResponse.Node> nodes = response.getNodes(); // <1>
//end::watcher-stats-response
}
{
WatcherStatsRequest request = new WatcherStatsRequest();
// tag::watcher-stats-execute-listener
ActionListener<WatcherStatsResponse> listener = new ActionListener<WatcherStatsResponse>() {
@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));
}
}
}

View File

@ -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<WatcherStatsResponse.Node> nodes = new ArrayList<>(nodeCount);
for (int i = 0; i < nodeCount; i++) {
List<WatchExecutionSnapshot> 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<QueuedWatch> queuedWatches = null;
if(randomBoolean()) {
int queuedWatchCount = randomInt(10);
queuedWatches = new ArrayList<>(queuedWatchCount);
for (int j=0; j<queuedWatchCount; j++) {
queuedWatches.add(new QueuedWatch(randomAlphaOfLength(10), randomAlphaOfLength(10),
new DateTime(randomInt(), DateTimeZone.UTC), new DateTime(randomInt(), DateTimeZone.UTC)));
}
}
Map<String, Object> stats = null;
if (randomBoolean()) {
int statsCount = randomInt(10);
stats = new HashMap<>(statsCount);
for (int j=0; j<statsCount; j++) {
stats.put(randomAlphaOfLength(10), randomNonNegativeLong());
}
}
nodes.add(new WatcherStatsResponse.Node(randomAlphaOfLength(10), randomFrom(WatcherState.values()), randomNonNegativeLong(),
randomNonNegativeLong(), randomNonNegativeLong(), snapshots, queuedWatches, stats));
}
NodesResponseHeader nodesResponseHeader = new NodesResponseHeader(randomInt(10), randomInt(10),
randomInt(10), Collections.emptyList());
WatcherMetaData watcherMetaData = new WatcherMetaData(randomBoolean());
return new WatcherStatsResponse(nodesResponseHeader, randomAlphaOfLength(10), watcherMetaData, nodes);
}
}

View File

@ -373,6 +373,7 @@ The Java High Level REST Client supports the following Watcher APIs:
* <<java-rest-high-watcher-deactivate-watch>>
* <<{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

View File

@ -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[]