SOLR-9960 MetricsHandler should support multiple prefixes.

This commit is contained in:
Andrzej Bialecki 2017-01-13 19:24:40 +01:00
parent 64b86331c2
commit 53d5af17da
4 changed files with 109 additions and 53 deletions

View File

@ -196,14 +196,16 @@ New Features
* SOLR-9805: Use metrics-jvm library to instrument jvm internals such as GC, memory usage and others. (shalin) * SOLR-9805: Use metrics-jvm library to instrument jvm internals such as GC, memory usage and others. (shalin)
* SOLR-9812: SOLR-9911: Added a new /admin/metrics API to return all metrics collected by Solr via API. * SOLR-9812: SOLR-9911, SOLR-9960: Added a new /admin/metrics API to return all metrics collected by Solr via API.
API supports three optional parameters: API supports four optional multi-valued parameters:
* 'group' (all,jvm,jetty,node,core), * 'group' (all,jvm,jetty,node,core),
* 'type' (all,counter,timer,gauge,histogram) both of which are multi-valued * 'type' (all,counter,timer,gauge,histogram),
* 'prefix' that filters the returned metrics * 'prefix' that filters the returned metrics,
* 'registry' that selects one or more registries by prefix (eg. solr.jvm,solr.core.collection1)
Example: http://localhost:8983/solr/admin/metrics?group=jvm,jetty&type=counter Example: http://localhost:8983/solr/admin/metrics?group=jvm,jetty&type=counter
Example: http://localhost:8983/solr/admin/metrics?group=jvm&prefix=buffers Example: http://localhost:8983/solr/admin/metrics?group=jvm&prefix=buffers,os
(shalin) Example: http://localhost:8983/solr/admin/metrics?registry=solr.node,solr.core&prefix=ADMIN
(shalin, ab)
* SOLR-9884: Add version to segments handler output (Steven Bower via Erick Erickson) * SOLR-9884: Add version to segments handler output (Steven Bower via Erick Erickson)

View File

@ -18,9 +18,10 @@
package org.apache.solr.handler.admin; package org.apache.solr.handler.admin;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections; import java.util.Collections;
import java.util.HashSet;
import java.util.List; import java.util.List;
import java.util.Set;
import java.util.stream.Collectors; import java.util.stream.Collectors;
import com.codahale.metrics.Counter; import com.codahale.metrics.Counter;
@ -34,7 +35,6 @@ import org.apache.solr.common.SolrException;
import org.apache.solr.common.util.NamedList; import org.apache.solr.common.util.NamedList;
import org.apache.solr.common.util.StrUtils; import org.apache.solr.common.util.StrUtils;
import org.apache.solr.core.CoreContainer; import org.apache.solr.core.CoreContainer;
import org.apache.solr.core.SolrCore;
import org.apache.solr.handler.RequestHandlerBase; import org.apache.solr.handler.RequestHandlerBase;
import org.apache.solr.metrics.SolrMetricManager; import org.apache.solr.metrics.SolrMetricManager;
import org.apache.solr.request.SolrQueryRequest; import org.apache.solr.request.SolrQueryRequest;
@ -74,59 +74,85 @@ public class MetricsHandler extends RequestHandlerBase implements PermissionName
MetricFilter mustMatchFilter = parseMustMatchFilter(req); MetricFilter mustMatchFilter = parseMustMatchFilter(req);
List<MetricType> metricTypes = parseMetricTypes(req); List<MetricType> metricTypes = parseMetricTypes(req);
List<MetricFilter> metricFilters = metricTypes.stream().map(MetricType::asMetricFilter).collect(Collectors.toList()); List<MetricFilter> metricFilters = metricTypes.stream().map(MetricType::asMetricFilter).collect(Collectors.toList());
List<Group> requestedGroups = parseGroups(req); Set<String> requestedRegistries = parseRegistries(req);
NamedList response = new NamedList(); NamedList response = new NamedList();
for (Group group : requestedGroups) { for (String registryName : requestedRegistries) {
String registryName = SolrMetricManager.getRegistryName(group);
if (group == Group.core) {
// this requires special handling because of the way we create registry name for a core (deeply nested)
container.getAllCoreNames().forEach(s -> {
String coreRegistryName;
try (SolrCore core = container.getCore(s)) {
coreRegistryName = core.getCoreMetricManager().getRegistryName();
}
MetricRegistry registry = metricManager.registry(coreRegistryName);
response.add(coreRegistryName, MetricUtils.toNamedList(registry, metricFilters, mustMatchFilter));
});
} else {
MetricRegistry registry = metricManager.registry(registryName); MetricRegistry registry = metricManager.registry(registryName);
response.add(registryName, MetricUtils.toNamedList(registry, metricFilters, mustMatchFilter)); response.add(registryName, MetricUtils.toNamedList(registry, metricFilters, mustMatchFilter));
} }
}
rsp.getValues().add("metrics", response); rsp.getValues().add("metrics", response);
} }
private MetricFilter parseMustMatchFilter(SolrQueryRequest req) { private MetricFilter parseMustMatchFilter(SolrQueryRequest req) {
String prefix = req.getParams().get("prefix"); String[] prefixes = req.getParams().getParams("prefix");
MetricFilter mustMatchFilter; MetricFilter mustMatchFilter;
if (prefix != null) { if (prefixes != null && prefixes.length > 0) {
mustMatchFilter = new SolrMetricManager.PrefixFilter(prefix.trim()); Set<String> prefixSet = new HashSet<>();
for (String prefix : prefixes) {
prefixSet.addAll(StrUtils.splitSmart(prefix, ','));
}
mustMatchFilter = new SolrMetricManager.PrefixFilter((String[])prefixSet.toArray(new String[prefixSet.size()]));
} else { } else {
mustMatchFilter = MetricFilter.ALL; mustMatchFilter = MetricFilter.ALL;
} }
return mustMatchFilter; return mustMatchFilter;
} }
private List<Group> parseGroups(SolrQueryRequest req) { private Set<String> parseRegistries(SolrQueryRequest req) {
String[] groupStr = req.getParams().getParams("group"); String[] groupStr = req.getParams().getParams("group");
List<String> groups = Collections.emptyList(); String[] registryStr = req.getParams().getParams("registry");
if ((groupStr == null || groupStr.length == 0) && (registryStr == null || registryStr.length == 0)) {
// return all registries
return container.getMetricManager().registryNames();
}
boolean allRegistries = false;
Set<String> initialPrefixes = Collections.emptySet();
if (groupStr != null && groupStr.length > 0) { if (groupStr != null && groupStr.length > 0) {
groups = new ArrayList<>(); initialPrefixes = new HashSet<>();
for (String g : groupStr) { for (String g : groupStr) {
groups.addAll(StrUtils.splitSmart(g, ',')); List<String> split = StrUtils.splitSmart(g, ',');
for (String s : split) {
if (s.trim().equals("all")) {
allRegistries = true;
break;
}
initialPrefixes.add(SolrMetricManager.overridableRegistryName(s.trim()));
}
if (allRegistries) {
return container.getMetricManager().registryNames();
}
} }
} }
List<Group> requestedGroups = Arrays.asList(Group.values()); // by default we return all groups if (registryStr != null && registryStr.length > 0) {
try { if (initialPrefixes.isEmpty()) {
if (groups.size() > 0 && !groups.contains("all")) { initialPrefixes = new HashSet<>();
requestedGroups = groups.stream().map(String::trim).map(Group::valueOf).collect(Collectors.toList());
} }
} catch (IllegalArgumentException e) { for (String r : registryStr) {
throw new SolrException(SolrException.ErrorCode.BAD_REQUEST, "Invalid group in: " + groups + " specified. Must be one of (all, jvm, jetty, http, node, core)", e); List<String> split = StrUtils.splitSmart(r, ',');
for (String s : split) {
if (s.trim().equals("all")) {
allRegistries = true;
break;
} }
return requestedGroups; initialPrefixes.add(SolrMetricManager.overridableRegistryName(s.trim()));
}
if (allRegistries) {
return container.getMetricManager().registryNames();
}
}
}
Set<String> validRegistries = new HashSet<>();
for (String r : container.getMetricManager().registryNames()) {
for (String prefix : initialPrefixes) {
if (r.startsWith(prefix)) {
validRegistries.add(r);
break;
}
}
}
return validRegistries;
} }
private List<MetricType> parseMetricTypes(SolrQueryRequest req) { private List<MetricType> parseMetricTypes(SolrQueryRequest req) {

View File

@ -93,32 +93,38 @@ public class SolrMetricManager {
* with names that start with a prefix. * with names that start with a prefix.
*/ */
public static class PrefixFilter implements MetricFilter { public static class PrefixFilter implements MetricFilter {
private final String prefix; private final String[] prefixes;
private final Set<String> matched = new HashSet<>(); private final Set<String> matched = new HashSet<>();
private boolean allMatch = false;
/** /**
* Create a filter that uses the provided prefix. * Create a filter that uses the provided prefix.
* @param prefix prefix to use, must not be null. If empty then any * @param prefixes prefixes to use, must not be null. If empty then any
* name will match. * name will match, if not empty then match on any prefix will
* succeed (logical OR).
*/ */
public PrefixFilter(String prefix) { public PrefixFilter(String... prefixes) {
Objects.requireNonNull(prefix); Objects.requireNonNull(prefixes);
this.prefix = prefix; this.prefixes = prefixes;
if (prefixes.length == 0) {
allMatch = true;
}
} }
@Override @Override
public boolean matches(String name, Metric metric) { public boolean matches(String name, Metric metric) {
if (prefix.isEmpty()) { if (allMatch) {
matched.add(name); matched.add(name);
return true; return true;
} }
for (String prefix : prefixes) {
if (name.startsWith(prefix)) { if (name.startsWith(prefix)) {
matched.add(name); matched.add(name);
return true; return true;
} else {
return false;
} }
} }
return false;
}
/** /**
* Return the set of names that matched this filter. * Return the set of names that matched this filter.

View File

@ -65,6 +65,26 @@ public class MetricsHandlerTest extends SolrTestCaseJ4 {
assertNotNull(values.get("solr.jetty")); assertNotNull(values.get("solr.jetty"));
assertNotNull(values.get("solr.jvm")); assertNotNull(values.get("solr.jvm"));
resp = new SolrQueryResponse();
// "collection" works too, because it's a prefix for "collection1"
handler.handleRequestBody(req(CommonParams.QT, "/admin/metrics", CommonParams.WT, "json", "registry", "solr.core.collection,solr.jvm"), resp);
values = resp.getValues();
assertNotNull(values.get("metrics"));
values = (NamedList) values.get("metrics");
assertEquals(2, values.size());
assertNotNull(values.get("solr.core.collection1"));
assertNotNull(values.get("solr.jvm"));
resp = new SolrQueryResponse();
// "collection" works too, because it's a prefix for "collection1"
handler.handleRequestBody(req(CommonParams.QT, "/admin/metrics", CommonParams.WT, "json", "registry", "solr.core.collection", "registry", "solr.jvm"), resp);
values = resp.getValues();
assertNotNull(values.get("metrics"));
values = (NamedList) values.get("metrics");
assertEquals(2, values.size());
assertNotNull(values.get("solr.core.collection1"));
assertNotNull(values.get("solr.jvm"));
resp = new SolrQueryResponse(); resp = new SolrQueryResponse();
handler.handleRequestBody(req(CommonParams.QT, "/admin/metrics", CommonParams.WT, "json", "group", "jvm,jetty"), resp); handler.handleRequestBody(req(CommonParams.QT, "/admin/metrics", CommonParams.WT, "json", "group", "jvm,jetty"), resp);
values = resp.getValues(); values = resp.getValues();
@ -94,7 +114,7 @@ public class MetricsHandlerTest extends SolrTestCaseJ4 {
assertNull(values.get("ADMIN./admin/authorization.errors")); // this is a timer node assertNull(values.get("ADMIN./admin/authorization.errors")); // this is a timer node
resp = new SolrQueryResponse(); resp = new SolrQueryResponse();
handler.handleRequestBody(req(CommonParams.QT, "/admin/metrics", CommonParams.WT, "json", "prefix", "CONTAINER.cores"), resp); handler.handleRequestBody(req(CommonParams.QT, "/admin/metrics", CommonParams.WT, "json", "prefix", "CONTAINER.cores,CONTAINER.threadPool"), resp);
values = resp.getValues(); values = resp.getValues();
assertNotNull(values.get("metrics")); assertNotNull(values.get("metrics"));
values = (NamedList) values.get("metrics"); values = (NamedList) values.get("metrics");
@ -102,10 +122,12 @@ public class MetricsHandlerTest extends SolrTestCaseJ4 {
assertEquals(0, ((NamedList)values.get("solr.jvm")).size()); assertEquals(0, ((NamedList)values.get("solr.jvm")).size());
assertEquals(0, ((NamedList)values.get("solr.jetty")).size()); assertEquals(0, ((NamedList)values.get("solr.jetty")).size());
assertEquals(0, ((NamedList)values.get("solr.core.collection1")).size()); assertEquals(0, ((NamedList)values.get("solr.core.collection1")).size());
assertEquals(3, ((NamedList)values.get("solr.node")).size()); assertEquals(11, ((NamedList)values.get("solr.node")).size());
assertNotNull(values.get("solr.node")); assertNotNull(values.get("solr.node"));
values = (NamedList) values.get("solr.node"); values = (NamedList) values.get("solr.node");
assertNotNull(values.get("CONTAINER.cores.lazy")); // this is a gauge node assertNotNull(values.get("CONTAINER.cores.lazy")); // this is a gauge node
assertNotNull(values.get("CONTAINER.threadPool.coreContainerWorkExecutor.completed"));
assertNotNull(values.get("CONTAINER.threadPool.coreLoadExecutor.completed"));
resp = new SolrQueryResponse(); resp = new SolrQueryResponse();
handler.handleRequestBody(req(CommonParams.QT, "/admin/metrics", CommonParams.WT, "json", "group", "jvm", "prefix", "CONTAINER.cores"), resp); handler.handleRequestBody(req(CommonParams.QT, "/admin/metrics", CommonParams.WT, "json", "group", "jvm", "prefix", "CONTAINER.cores"), resp);