mirror of https://github.com/apache/lucene.git
SOLR-9960 MetricsHandler should support multiple prefixes.
This commit is contained in:
parent
64b86331c2
commit
53d5af17da
|
@ -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)
|
||||||
|
|
||||||
|
|
|
@ -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);
|
MetricRegistry registry = metricManager.registry(registryName);
|
||||||
if (group == Group.core) {
|
response.add(registryName, MetricUtils.toNamedList(registry, metricFilters, mustMatchFilter));
|
||||||
// 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);
|
|
||||||
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());
|
}
|
||||||
|
for (String r : registryStr) {
|
||||||
|
List<String> split = StrUtils.splitSmart(r, ',');
|
||||||
|
for (String s : split) {
|
||||||
|
if (s.trim().equals("all")) {
|
||||||
|
allRegistries = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
initialPrefixes.add(SolrMetricManager.overridableRegistryName(s.trim()));
|
||||||
|
}
|
||||||
|
if (allRegistries) {
|
||||||
|
return container.getMetricManager().registryNames();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
} catch (IllegalArgumentException e) {
|
|
||||||
throw new SolrException(SolrException.ErrorCode.BAD_REQUEST, "Invalid group in: " + groups + " specified. Must be one of (all, jvm, jetty, http, node, core)", e);
|
|
||||||
}
|
}
|
||||||
return requestedGroups;
|
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) {
|
||||||
|
|
|
@ -93,31 +93,37 @@ 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;
|
||||||
}
|
}
|
||||||
if (name.startsWith(prefix)) {
|
for (String prefix : prefixes) {
|
||||||
matched.add(name);
|
if (name.startsWith(prefix)) {
|
||||||
return true;
|
matched.add(name);
|
||||||
} else {
|
return true;
|
||||||
return false;
|
}
|
||||||
}
|
}
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -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);
|
||||||
|
|
Loading…
Reference in New Issue