From b3b7ca34a4592543e4ae766b6966f68560a7738f Mon Sep 17 00:00:00 2001 From: Tanguy Leroux Date: Tue, 11 Aug 2015 19:09:57 +0200 Subject: [PATCH] Marvel: Add additional stats to index/node stats Closes elastic/elasticsearch#397 Original commit: elastic/x-pack-elasticsearch@0ddaeb8bb1b9b1b2d6db66ec6c65bb4a6016cbfa --- .../indices/IndexStatsCollector.java | 3 - .../cluster/ClusterStateRenderer.java | 2 +- .../cluster/ClusterStatsRenderer.java | 2 +- .../renderer/indices/IndexStatsRenderer.java | 19 ++- .../renderer/node/NodeStatsRenderer.java | 3 +- .../main/resources/marvel_index_template.json | 48 ++++++++ .../indices/IndexStatsRendererIT.java | 114 ++++++++++++++++++ .../indices/IndexStatsRendererTests.java | 14 ++- .../resources/samples/marvel_index_stats.json | 22 +++- .../resources/samples/marvel_node_stats.json | 7 +- 10 files changed, 221 insertions(+), 13 deletions(-) create mode 100644 marvel/src/test/java/org/elasticsearch/marvel/agent/renderer/indices/IndexStatsRendererIT.java diff --git a/marvel/src/main/java/org/elasticsearch/marvel/agent/collector/indices/IndexStatsCollector.java b/marvel/src/main/java/org/elasticsearch/marvel/agent/collector/indices/IndexStatsCollector.java index 2de0b7521da..44a80935876 100644 --- a/marvel/src/main/java/org/elasticsearch/marvel/agent/collector/indices/IndexStatsCollector.java +++ b/marvel/src/main/java/org/elasticsearch/marvel/agent/collector/indices/IndexStatsCollector.java @@ -49,9 +49,6 @@ public class IndexStatsCollector extends AbstractCollector ImmutableList.Builder results = ImmutableList.builder(); IndicesStatsResponse indicesStats = client.admin().indices().prepareStats() - .setStore(true) - .setIndexing(true) - .setDocs(true) .setIndices(marvelSettings.indices()) .get(marvelSettings.indexStatsTimeout()); diff --git a/marvel/src/main/java/org/elasticsearch/marvel/agent/renderer/cluster/ClusterStateRenderer.java b/marvel/src/main/java/org/elasticsearch/marvel/agent/renderer/cluster/ClusterStateRenderer.java index 32edfae3163..25f213cd2fa 100644 --- a/marvel/src/main/java/org/elasticsearch/marvel/agent/renderer/cluster/ClusterStateRenderer.java +++ b/marvel/src/main/java/org/elasticsearch/marvel/agent/renderer/cluster/ClusterStateRenderer.java @@ -20,7 +20,7 @@ import java.util.Locale; public class ClusterStateRenderer extends AbstractRenderer { - private static final String[] FILTERS = { + public static final String[] FILTERS = { "cluster_state.version", "cluster_state.master_node", "cluster_state.status", diff --git a/marvel/src/main/java/org/elasticsearch/marvel/agent/renderer/cluster/ClusterStatsRenderer.java b/marvel/src/main/java/org/elasticsearch/marvel/agent/renderer/cluster/ClusterStatsRenderer.java index 026506312e7..f5553608abe 100644 --- a/marvel/src/main/java/org/elasticsearch/marvel/agent/renderer/cluster/ClusterStatsRenderer.java +++ b/marvel/src/main/java/org/elasticsearch/marvel/agent/renderer/cluster/ClusterStatsRenderer.java @@ -16,7 +16,7 @@ import java.io.IOException; public class ClusterStatsRenderer extends AbstractRenderer { - private static final String[] FILTERS = { + public static final String[] FILTERS = { "cluster_stats.nodes.count.total", "cluster_stats.indices.shards.total", "cluster_stats.indices.shards.index.replication.min", diff --git a/marvel/src/main/java/org/elasticsearch/marvel/agent/renderer/indices/IndexStatsRenderer.java b/marvel/src/main/java/org/elasticsearch/marvel/agent/renderer/indices/IndexStatsRenderer.java index 56d1c59907e..5c7936df949 100644 --- a/marvel/src/main/java/org/elasticsearch/marvel/agent/renderer/indices/IndexStatsRenderer.java +++ b/marvel/src/main/java/org/elasticsearch/marvel/agent/renderer/indices/IndexStatsRenderer.java @@ -16,12 +16,20 @@ import java.io.IOException; public class IndexStatsRenderer extends AbstractRenderer { - private static final String[] FILTERS = { + public static final String[] FILTERS = { "index_stats.index", + "index_stats.primaries.docs.count", "index_stats.total.docs.count", + "index_stats.total.indexing.index_total", + "index_stats.total.indexing.index_time_in_millis", + "index_stats.total.indexing.throttle_time_in_millis", + "index_stats.total.merges.total_size_in_bytes", + "index_stats.total.search.query_total", + "index_stats.total.search.query_time_in_millis", + "index_stats.total.segments.memory_in_bytes", "index_stats.total.store.size_in_bytes", "index_stats.total.store.throttle_time_in_millis", - "index_stats.total.indexing.throttle_time_in_millis", + "index_stats.total.refresh.total_time_in_millis", }; public IndexStatsRenderer() { @@ -43,6 +51,12 @@ public class IndexStatsRenderer extends AbstractRenderer { indexStats.getTotal().toXContent(builder, params); } builder.endObject(); + + builder.startObject(Fields.PRIMARIES); + if (indexStats.getPrimaries() != null) { + indexStats.getPrimaries().toXContent(builder, params); + } + builder.endObject(); } } builder.endObject(); @@ -52,5 +66,6 @@ public class IndexStatsRenderer extends AbstractRenderer { static final XContentBuilderString INDEX_STATS = new XContentBuilderString("index_stats"); static final XContentBuilderString INDEX = new XContentBuilderString("index"); static final XContentBuilderString TOTAL = new XContentBuilderString("total"); + static final XContentBuilderString PRIMARIES = new XContentBuilderString("primaries"); } } diff --git a/marvel/src/main/java/org/elasticsearch/marvel/agent/renderer/node/NodeStatsRenderer.java b/marvel/src/main/java/org/elasticsearch/marvel/agent/renderer/node/NodeStatsRenderer.java index 89e007d0039..283cad08cb2 100644 --- a/marvel/src/main/java/org/elasticsearch/marvel/agent/renderer/node/NodeStatsRenderer.java +++ b/marvel/src/main/java/org/elasticsearch/marvel/agent/renderer/node/NodeStatsRenderer.java @@ -16,7 +16,7 @@ import java.io.IOException; public class NodeStatsRenderer extends AbstractRenderer { - private static final String[] FILTERS = { + public static final String[] FILTERS = { // Extra information "node_stats.node_id", "node_stats.node_master", @@ -32,6 +32,7 @@ public class NodeStatsRenderer extends AbstractRenderer { "node_stats.fs.total.total_in_bytes", "node_stats.fs.total.free_in_bytes", "node_stats.fs.total.available_in_bytes", + "node_stats.os.load_average", "node_stats.process.max_file_descriptors", "node_stats.process.open_file_descriptors", "node_stats.jvm.mem.heap_used_in_bytes", diff --git a/marvel/src/main/resources/marvel_index_template.json b/marvel/src/main/resources/marvel_index_template.json index 2d129f78af1..051b8c093b2 100644 --- a/marvel/src/main/resources/marvel_index_template.json +++ b/marvel/src/main/resources/marvel_index_template.json @@ -33,6 +33,17 @@ "type": "string", "index": "not_analyzed" }, + "primaries": { + "properties": { + "docs": { + "properties": { + "count": { + "type": "long" + } + } + } + } + }, "total": { "properties": { "docs": { @@ -54,10 +65,47 @@ }, "indexing": { "properties": { + "index_total": { + "type": "long" + }, + "index_time_in_millis": { + "type": "long" + }, "throttle_time_in_millis": { "type": "long" } } + }, + "merges": { + "properties": { + "total_size_in_bytes": { + "type": "long" + } + } + }, + "search": { + "properties": { + "query_total": { + "type": "long" + }, + "query_time_in_millis": { + "type": "long" + } + } + }, + "segments": { + "properties": { + "memory_in_bytes": { + "type": "long" + } + } + }, + "refresh": { + "properties": { + "total_time_in_millis": { + "type": "long" + } + } } } } diff --git a/marvel/src/test/java/org/elasticsearch/marvel/agent/renderer/indices/IndexStatsRendererIT.java b/marvel/src/test/java/org/elasticsearch/marvel/agent/renderer/indices/IndexStatsRendererIT.java new file mode 100644 index 00000000000..47c5ec22d52 --- /dev/null +++ b/marvel/src/test/java/org/elasticsearch/marvel/agent/renderer/indices/IndexStatsRendererIT.java @@ -0,0 +1,114 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ +package org.elasticsearch.marvel.agent.renderer.indices; + +import org.elasticsearch.action.count.CountResponse; +import org.elasticsearch.action.search.SearchResponse; +import org.elasticsearch.common.Strings; +import org.elasticsearch.common.settings.Settings; +import org.elasticsearch.index.query.QueryBuilders; +import org.elasticsearch.license.plugin.LicensePlugin; +import org.elasticsearch.marvel.MarvelPlugin; +import org.elasticsearch.marvel.agent.AgentService; +import org.elasticsearch.marvel.agent.collector.indices.IndexStatsCollector; +import org.elasticsearch.node.Node; +import org.elasticsearch.search.SearchHit; +import org.elasticsearch.test.ESIntegTestCase; +import org.elasticsearch.test.ESIntegTestCase.ClusterScope; +import org.junit.Test; + +import java.util.Map; + +import static org.hamcrest.Matchers.*; + +@ClusterScope(scope = ESIntegTestCase.Scope.SUITE, numDataNodes = 1) +public class IndexStatsRendererIT extends ESIntegTestCase { + + @Override + protected Settings nodeSettings(int nodeOrdinal) { + return Settings.builder() + .put(super.nodeSettings(nodeOrdinal)) + .put("plugin.types", MarvelPlugin.class.getName() + "," + LicensePlugin.class.getName()) + .put(Node.HTTP_ENABLED, true) + .put(AgentService.SETTINGS_INTERVAL, "200ms") + .build(); + } + + @Test + public void testIndexStats() throws Exception { + final int nbIndices = randomIntBetween(1, 5); + for (int i = 0; i < nbIndices; i++) { + createIndex("test" + i); + } + + final long[] nbDocsPerIndex = new long[nbIndices]; + for (int i = 0; i < nbIndices; i++) { + nbDocsPerIndex[i] = randomIntBetween(1, 50); + for (int j = 0; j < nbDocsPerIndex[i]; j++) { + client().prepareIndex("test" + i, "type1").setSource("num", i).get(); + } + } + refresh(); + + logger.debug("--> wait for index stats collector to collect stat for each index"); + assertBusy(new Runnable() { + @Override + public void run() { + for (int i = 0; i < nbIndices; i++) { + CountResponse count = client().prepareCount() + .setTypes(IndexStatsCollector.TYPE) + .setQuery(QueryBuilders.termQuery("index_stats.index", "test" + i)) + .get(); + assertThat(count.getCount(), greaterThan(0L)); + } + } + }); + + logger.debug("--> get the list of filters used by the renderer"); + String[] filters = IndexStatsRenderer.FILTERS; + + logger.debug("--> checking that every document contains the expected fields"); + SearchResponse response = client().prepareSearch().setTypes(IndexStatsCollector.TYPE).get(); + for (SearchHit searchHit : response.getHits().getHits()) { + Map fields = searchHit.sourceAsMap(); + + for (String filter : filters) { + assertContains(filter, fields); + } + } + } + + /** + * Checks if a field exist in a map of values. If the field contains a dot like 'foo.bar' + * it checks that 'foo' exists in the map of values and that it points to a sub-map. Then + * it recurses to check if 'bar' exists in the sub-map. + */ + private void assertContains(String field, Map values) { + assertNotNull(field); + assertNotNull(values); + + int point = field.indexOf('.'); + if (point > -1) { + assertThat(point, allOf(greaterThan(0), lessThan(field.length()))); + + String segment = field.substring(0, point); + assertTrue(Strings.hasText(segment)); + + Object value = values.get(segment); + assertNotNull(value); + + String next = field.substring(point + 1); + if (next.length() > 0) { + assertTrue(value instanceof Map); + assertContains(next, (Map) value); + } else { + assertFalse(value instanceof Map); + } + } else { + assertNotNull(values.get(field)); + } + } +} diff --git a/marvel/src/test/java/org/elasticsearch/marvel/agent/renderer/indices/IndexStatsRendererTests.java b/marvel/src/test/java/org/elasticsearch/marvel/agent/renderer/indices/IndexStatsRendererTests.java index 511df3e0bcd..2de2dd55ab3 100644 --- a/marvel/src/test/java/org/elasticsearch/marvel/agent/renderer/indices/IndexStatsRendererTests.java +++ b/marvel/src/test/java/org/elasticsearch/marvel/agent/renderer/indices/IndexStatsRendererTests.java @@ -8,7 +8,11 @@ package org.elasticsearch.marvel.agent.renderer.indices; import org.elasticsearch.action.admin.indices.stats.CommonStats; import org.elasticsearch.action.admin.indices.stats.IndexStats; import org.elasticsearch.action.admin.indices.stats.ShardStats; +import org.elasticsearch.index.engine.SegmentsStats; import org.elasticsearch.index.indexing.IndexingStats; +import org.elasticsearch.index.merge.MergeStats; +import org.elasticsearch.index.refresh.RefreshStats; +import org.elasticsearch.index.search.stats.SearchStats; import org.elasticsearch.index.shard.DocsStats; import org.elasticsearch.index.store.StoreStats; import org.elasticsearch.marvel.agent.collector.indices.IndexStatsMarvelDoc; @@ -32,7 +36,13 @@ public class IndexStatsRendererTests extends ESTestCase { CommonStats stats = new CommonStats(); stats.docs = new DocsStats(345678L, 123L); stats.store = new StoreStats(5761573L, 0L); - stats.indexing = new IndexingStats(new IndexingStats.Stats(0L, 0L, 0L, 0L, 0L, 0L, 0L, true, 302L), null); + stats.indexing = new IndexingStats(new IndexingStats.Stats(3L, 71L, 0L, 0L, 0L, 0L, 0L, true, 302L), null); + stats.search = new SearchStats(new SearchStats.Stats(1L, 7L, 0L, 0L, 0L, 0L, 0L, 0L, 0L), 0L, null); + stats.merge = new MergeStats(); + stats.merge.add(0L, 0L, 0L, 42L, 0L, 0L, 0L, 0L, 0L, 0L); + stats.refresh = new RefreshStats(0L, 978L); + stats.segments = new SegmentsStats(); + stats.segments.add(0, 87965412L); return stats; } @@ -40,7 +50,7 @@ public class IndexStatsRendererTests extends ESTestCase { public CommonStats getPrimaries() { // Primaries will be filtered out by the renderer CommonStats stats = new CommonStats(); - stats.docs = new DocsStats(randomLong(), randomLong()); + stats.docs = new DocsStats(345678L, randomLong()); stats.store = new StoreStats(randomLong(), randomLong()); stats.indexing = new IndexingStats(new IndexingStats.Stats(0L, 0L, 0L, 0L, 0L, 0L, 0L, true, randomLong()), null); return stats; diff --git a/marvel/src/test/resources/samples/marvel_index_stats.json b/marvel/src/test/resources/samples/marvel_index_stats.json index c9751a9de18..66e2f2f9c31 100644 --- a/marvel/src/test/resources/samples/marvel_index_stats.json +++ b/marvel/src/test/resources/samples/marvel_index_stats.json @@ -12,8 +12,28 @@ "throttle_time_in_millis": 0 }, "indexing": { + "index_total" : 3, + "index_time_in_millis" : 71, "throttle_time_in_millis": 302 + }, + "search" : { + "query_total" : 1, + "query_time_in_millis" : 7 + }, + "merges" : { + "total_size_in_bytes" : 42 + }, + "refresh" : { + "total_time_in_millis" : 978 + }, + "segments" : { + "memory_in_bytes" : 87965412 + } + }, + "primaries" : { + "docs" : { + "count" : 345678 } } } -} \ No newline at end of file +} diff --git a/marvel/src/test/resources/samples/marvel_node_stats.json b/marvel/src/test/resources/samples/marvel_node_stats.json index dcc2fb3c5d4..9650bae7e1f 100644 --- a/marvel/src/test/resources/samples/marvel_node_stats.json +++ b/marvel/src/test/resources/samples/marvel_node_stats.json @@ -22,6 +22,9 @@ "count": 0 } }, + "os": { + "load_average" : 0.66 + }, "process": { "open_file_descriptors": 235, "max_file_descriptors": 4096 @@ -48,10 +51,10 @@ "index": { "rejected": 0 }, - "bulk": { + "search": { "rejected": 0 }, - "search": { + "bulk": { "rejected": 0 } },