SOLR-10262: Add support for configurable metrics implementations.

This commit is contained in:
Andrzej Bialecki 2017-05-09 12:53:18 +02:00
parent d59faafd89
commit 3217fd7c3c
19 changed files with 942 additions and 43 deletions

View File

@ -88,6 +88,8 @@ New Features
* SOLR-10547: JSON Facet API: Implement support for single-valued string fields for min/max aggregations.
(yonik)
* SOLR-10262: Add support for configurable metrics implementations. (ab)
Bug Fixes
----------------------
* SOLR-9262: Connection and read timeouts are being ignored by UpdateShardHandler after SOLR-4509.

View File

@ -479,7 +479,7 @@ public class CoreContainer {
}
}
metricManager = new SolrMetricManager();
metricManager = new SolrMetricManager(loader, cfg.getMetricsConfig());
coreContainerWorkExecutor = MetricUtils.instrumentedExecutorService(
coreContainerWorkExecutor, null,
@ -525,9 +525,10 @@ public class CoreContainer {
if(pkiAuthenticationPlugin != null)
containerHandlers.put(PKIAuthenticationPlugin.PATH, pkiAuthenticationPlugin.getRequestHandler());
metricManager.loadReporters(cfg.getMetricReporterPlugins(), loader, null, SolrInfoBean.Group.node);
metricManager.loadReporters(cfg.getMetricReporterPlugins(), loader, null, SolrInfoBean.Group.jvm);
metricManager.loadReporters(cfg.getMetricReporterPlugins(), loader, null, SolrInfoBean.Group.jetty);
PluginInfo[] metricReporters = cfg.getMetricsConfig().getMetricReporters();
metricManager.loadReporters(metricReporters, loader, null, SolrInfoBean.Group.node);
metricManager.loadReporters(metricReporters, loader, null, SolrInfoBean.Group.jvm);
metricManager.loadReporters(metricReporters, loader, null, SolrInfoBean.Group.jetty);
coreConfigService = ConfigSetService.createConfigSetService(cfg, loader, zkSys.zkController);
@ -556,7 +557,7 @@ public class CoreContainer {
fieldCacheBean.initializeMetrics(metricManager, registryName, null);
if (isZooKeeperAware()) {
metricManager.loadClusterReporters(cfg.getMetricReporterPlugins(), this);
metricManager.loadClusterReporters(metricReporters, this);
}
// setup executor to load cores in parallel

View File

@ -0,0 +1,121 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF 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.apache.solr.core;
import java.util.HashSet;
import java.util.Set;
/**
*
*/
public class MetricsConfig {
private final PluginInfo[] metricReporters;
private final Set<String> hiddenSysProps;
private final PluginInfo counterSupplier;
private final PluginInfo meterSupplier;
private final PluginInfo timerSupplier;
private final PluginInfo histogramSupplier;
private MetricsConfig(PluginInfo[] metricReporters, Set<String> hiddenSysProps,
PluginInfo counterSupplier, PluginInfo meterSupplier,
PluginInfo timerSupplier, PluginInfo histogramSupplier) {
this.metricReporters = metricReporters;
this.hiddenSysProps = hiddenSysProps;
this.counterSupplier = counterSupplier;
this.meterSupplier = meterSupplier;
this.timerSupplier = timerSupplier;
this.histogramSupplier = histogramSupplier;
}
public PluginInfo[] getMetricReporters() {
return metricReporters;
}
public Set<String> getHiddenSysProps() {
return hiddenSysProps;
}
public PluginInfo getCounterSupplier() {
return counterSupplier;
}
public PluginInfo getMeterSupplier() {
return meterSupplier;
}
public PluginInfo getTimerSupplier() {
return timerSupplier;
}
public PluginInfo getHistogramSupplier() {
return histogramSupplier;
}
public static class MetricsConfigBuilder {
private PluginInfo[] metricReporterPlugins = new PluginInfo[0];
private Set<String> hiddenSysProps = new HashSet<>();
private PluginInfo counterSupplier;
private PluginInfo meterSupplier;
private PluginInfo timerSupplier;
private PluginInfo histogramSupplier;
public MetricsConfigBuilder() {
}
public MetricsConfigBuilder setHiddenSysProps(Set<String> hiddenSysProps) {
if (hiddenSysProps != null && !hiddenSysProps.isEmpty()) {
this.hiddenSysProps.clear();
this.hiddenSysProps.addAll(hiddenSysProps);
}
return this;
}
public MetricsConfigBuilder setMetricReporterPlugins(PluginInfo[] metricReporterPlugins) {
this.metricReporterPlugins = metricReporterPlugins != null ? metricReporterPlugins : new PluginInfo[0];
return this;
}
public MetricsConfigBuilder setCounterSupplier(PluginInfo info) {
this.counterSupplier = info;
return this;
}
public MetricsConfigBuilder setMeterSupplier(PluginInfo info) {
this.meterSupplier = info;
return this;
}
public MetricsConfigBuilder setTimerSupplier(PluginInfo info) {
this.timerSupplier = info;
return this;
}
public MetricsConfigBuilder setHistogramSupplier(PluginInfo info) {
this.histogramSupplier = info;
return this;
}
public MetricsConfig build() {
return new MetricsConfig(metricReporterPlugins, hiddenSysProps, counterSupplier, meterSupplier,
timerSupplier, histogramSupplier);
}
}
}

View File

@ -65,9 +65,7 @@ public class NodeConfig {
private final PluginInfo[] backupRepositoryPlugins;
private final PluginInfo[] metricReporterPlugins;
private final Set<String> hiddenSysProps;
private final MetricsConfig metricsConfig;
private final PluginInfo transientCacheConfig;
@ -78,7 +76,7 @@ public class NodeConfig {
LogWatcherConfig logWatcherConfig, CloudConfig cloudConfig, Integer coreLoadThreads,
int transientCacheSize, boolean useSchemaCache, String managementPath, SolrResourceLoader loader,
Properties solrProperties, PluginInfo[] backupRepositoryPlugins,
PluginInfo[] metricReporterPlugins, Set<String> hiddenSysProps, PluginInfo transientCacheConfig) {
MetricsConfig metricsConfig, PluginInfo transientCacheConfig) {
this.nodeName = nodeName;
this.coreRootDirectory = coreRootDirectory;
this.configSetBaseDirectory = configSetBaseDirectory;
@ -98,8 +96,7 @@ public class NodeConfig {
this.loader = loader;
this.solrProperties = solrProperties;
this.backupRepositoryPlugins = backupRepositoryPlugins;
this.metricReporterPlugins = metricReporterPlugins;
this.hiddenSysProps = hiddenSysProps;
this.metricsConfig = metricsConfig;
this.transientCacheConfig = transientCacheConfig;
if (this.cloudConfig != null && this.getCoreLoadThreadCount(false) < 2) {
@ -189,12 +186,8 @@ public class NodeConfig {
return backupRepositoryPlugins;
}
public PluginInfo[] getMetricReporterPlugins() {
return metricReporterPlugins;
}
public Set<String> getHiddenSysProps() {
return hiddenSysProps;
public MetricsConfig getMetricsConfig() {
return metricsConfig;
}
public PluginInfo getTransientCachePluginInfo() { return transientCacheConfig; }
@ -220,8 +213,7 @@ public class NodeConfig {
private String managementPath;
private Properties solrProperties = new Properties();
private PluginInfo[] backupRepositoryPlugins;
private PluginInfo[] metricReporterPlugins;
private Set<String> hiddenSysProps = new HashSet<>(DEFAULT_HIDDEN_SYS_PROPS);
private MetricsConfig metricsConfig;
private PluginInfo transientCacheConfig;
private final SolrResourceLoader loader;
@ -251,6 +243,7 @@ public class NodeConfig {
this.loader = loader;
this.coreRootDirectory = loader.getInstancePath();
this.configSetBaseDirectory = loader.getInstancePath().resolve("configsets");
this.metricsConfig = new MetricsConfig.MetricsConfigBuilder().build();
}
public NodeConfigBuilder setCoreRootDirectory(String coreRootDirectory) {
@ -340,8 +333,8 @@ public class NodeConfig {
return this;
}
public NodeConfigBuilder setMetricReporterPlugins(PluginInfo[] metricReporterPlugins) {
this.metricReporterPlugins = metricReporterPlugins;
public NodeConfigBuilder setMetricsConfig(MetricsConfig metricsConfig) {
this.metricsConfig = metricsConfig;
return this;
}
@ -350,16 +343,11 @@ public class NodeConfig {
return this;
}
public NodeConfigBuilder setHiddenSysProps(Set<String> hiddenSysProps) {
this.hiddenSysProps = hiddenSysProps;
return this;
}
public NodeConfig build() {
return new NodeConfig(nodeName, coreRootDirectory, configSetBaseDirectory, sharedLibDirectory, shardHandlerFactoryConfig,
updateShardHandlerConfig, coreAdminHandlerClass, collectionsAdminHandlerClass, infoHandlerClass, configSetsHandlerClass,
logWatcherConfig, cloudConfig, coreLoadThreads, transientCacheSize, useSchemaCache, managementPath, loader, solrProperties,
backupRepositoryPlugins, metricReporterPlugins, hiddenSysProps, transientCacheConfig);
backupRepositoryPlugins, metricsConfig, transientCacheConfig);
}
}
}

View File

@ -103,8 +103,7 @@ public class SolrXmlConfig {
if (cloudConfig != null)
configBuilder.setCloudConfig(cloudConfig);
configBuilder.setBackupRepositoryPlugins(getBackupRepositoryPluginInfos(config));
configBuilder.setMetricReporterPlugins(getMetricReporterPluginInfos(config));
configBuilder.setHiddenSysProps(getHiddenSysProps(config));
configBuilder.setMetricsConfig(getMetricsConfig(config));
return fillSolrSection(configBuilder, entries);
}
@ -461,6 +460,32 @@ public class SolrXmlConfig {
return configs;
}
private static MetricsConfig getMetricsConfig(Config config) {
MetricsConfig.MetricsConfigBuilder builder = new MetricsConfig.MetricsConfigBuilder();
Node node = config.getNode("solr/metrics/suppliers/counter", false);
if (node != null) {
builder = builder.setCounterSupplier(new PluginInfo(node, "counterSupplier", false, false));
}
node = config.getNode("solr/metrics/suppliers/meter", false);
if (node != null) {
builder = builder.setMeterSupplier(new PluginInfo(node, "meterSupplier", false, false));
}
node = config.getNode("solr/metrics/suppliers/timer", false);
if (node != null) {
builder = builder.setTimerSupplier(new PluginInfo(node, "timerSupplier", false, false));
}
node = config.getNode("solr/metrics/suppliers/histogram", false);
if (node != null) {
builder = builder.setHistogramSupplier(new PluginInfo(node, "histogramSupplier", false, false));
}
PluginInfo[] reporterPlugins = getMetricReporterPluginInfos(config);
Set<String> hiddenSysProps = getHiddenSysProps(config);
return builder
.setMetricReporterPlugins(reporterPlugins)
.setHiddenSysProps(hiddenSysProps)
.build();
}
private static PluginInfo[] getMetricReporterPluginInfos(Config config) {
NodeList nodes = (NodeList) config.evaluate("solr/metrics/reporter", XPathConstants.NODESET);
List<PluginInfo> configs = new ArrayList<>();

View File

@ -0,0 +1,363 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF 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.apache.solr.metrics;
import java.lang.invoke.MethodHandles;
import java.util.concurrent.TimeUnit;
import com.codahale.metrics.Clock;
import com.codahale.metrics.Counter;
import com.codahale.metrics.ExponentiallyDecayingReservoir;
import com.codahale.metrics.Histogram;
import com.codahale.metrics.Meter;
import com.codahale.metrics.MetricRegistry;
import com.codahale.metrics.Reservoir;
import com.codahale.metrics.SlidingTimeWindowReservoir;
import com.codahale.metrics.SlidingWindowReservoir;
import com.codahale.metrics.Timer;
import com.codahale.metrics.UniformReservoir;
import org.apache.solr.core.PluginInfo;
import org.apache.solr.core.SolrResourceLoader;
import org.apache.solr.util.SolrPluginUtils;
import org.apache.solr.util.plugin.PluginInfoInitialized;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* Helper class for constructing instances of {@link com.codahale.metrics.MetricRegistry.MetricSupplier}
* based on plugin configuration. This allows us to customize eg. {@link com.codahale.metrics.Reservoir}
* implementations and parameters for timers and histograms.
* <p>Custom supplier implementations must provide a zero-args constructor, and may optionally implement
* {@link org.apache.solr.util.plugin.PluginInfoInitialized} interface for configuration - if they don't then
* {@link org.apache.solr.util.SolrPluginUtils#invokeSetters(Object, Iterable, boolean)} will be used for initialization.</p>
*/
public class MetricSuppliers {
private static final Logger log = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass());
/**
* Default {@link Counter} supplier. No configuration available.
*/
public static final class DefaultCounterSupplier implements MetricRegistry.MetricSupplier<Counter> {
@Override
public Counter newMetric() {
return new Counter();
}
}
private static final Clock CPU_CLOCK = new Clock.CpuTimeClock();
private static final Clock USER_CLOCK = new Clock.UserTimeClock();
/** Clock type parameter. */
public static final String CLOCK = "clock";
/** User-time clock. */
public static final String CLOCK_USER = "user";
/** CPU-time clock. */
public static final String CLOCK_CPU = "cpu";
/**
* Default {@link Meter} supplier. The following configuration is available, either as attribute
* or initArgs:
* <ul>
* <li>clock - (string) can be set to {@link #CLOCK_USER} for {@link com.codahale.metrics.Clock.UserTimeClock} or
* {@link #CLOCK_CPU} for {@link com.codahale.metrics.Clock.CpuTimeClock}. If not set then the value of
* {@link Clock#defaultClock()} will be used.</li>
* </ul>
*/
public static final class DefaultMeterSupplier implements MetricRegistry.MetricSupplier<Meter>, PluginInfoInitialized {
public Clock clk = Clock.defaultClock();
@Override
public void init(PluginInfo info) {
clk = getClock(info, CLOCK);
}
@Override
public Meter newMetric() {
return new Meter(clk);
}
}
private static Clock getClock(PluginInfo info, String param) {
if (info == null) {
return Clock.defaultClock();
}
String clock = null;
if (info.attributes != null) {
clock = info.attributes.get(param);
}
if (clock == null && info.initArgs != null) {
clock = (String)info.initArgs.get(param);
}
Clock clk = Clock.defaultClock();
if (clock != null) {
if (clock.equalsIgnoreCase(CLOCK_USER)) {
clk = USER_CLOCK;
} else if (clock.equalsIgnoreCase(CLOCK_CPU)) {
clk = CPU_CLOCK;
}
}
return clk;
}
/** Implementation class, must implement {@link Reservoir}. Supports non-standard configuration
* of the implementations available in metrics-core.
*/
public static final String RESERVOIR = "reservoir";
/** Size of reservoir. */
public static final String RESERVOIR_SIZE = "size";
/** Alpha parameter of {@link ExponentiallyDecayingReservoir}. */
public static final String RESERVOIR_EDR_ALPHA = "alpha";
/** Time window in seconds of {@link SlidingTimeWindowReservoir}. */
public static final String RESERVOIR_WINDOW = "window";
private static final String EDR_CLAZZ = ExponentiallyDecayingReservoir.class.getName();
private static final String UNI_CLAZZ = UniformReservoir.class.getName();
private static final String STW_CLAZZ = SlidingTimeWindowReservoir.class.getName();
private static final String SW_CLAZZ = SlidingWindowReservoir.class.getName();
private static final int DEFAULT_SIZE = 1028;
private static final double DEFAULT_ALPHA = 0.015;
private static final long DEFAULT_WINDOW = 300;
private static final Reservoir getReservoir(SolrResourceLoader loader, PluginInfo info) {
if (info == null) {
return new ExponentiallyDecayingReservoir();
}
Clock clk = getClock(info, CLOCK);
String clazz = ExponentiallyDecayingReservoir.class.getName();
int size = -1;
double alpha = -1;
long window = -1;
if (info.initArgs != null) {
if (info.initArgs.get(RESERVOIR) != null) {
String val = String.valueOf(info.initArgs.get(RESERVOIR)).trim();
if (!val.isEmpty()) {
clazz = val;
}
}
Number n = (Number)info.initArgs.get(RESERVOIR_SIZE);
if (n != null) {
size = n.intValue();
}
n = (Number)info.initArgs.get(RESERVOIR_EDR_ALPHA);
if (n != null) {
alpha = n.doubleValue();
}
n = (Number)info.initArgs.get(RESERVOIR_WINDOW);
if (n != null) {
window = n.longValue();
}
}
if (size <= 0) {
size = DEFAULT_SIZE;
}
if (alpha <= 0) {
alpha = DEFAULT_ALPHA;
}
// special case for core implementations
if (clazz.equals(EDR_CLAZZ)) {
return new ExponentiallyDecayingReservoir(size, alpha, clk);
} else if (clazz.equals(UNI_CLAZZ)) {
return new UniformReservoir(size);
} else if (clazz.equals(STW_CLAZZ)) {
if (window <= 0) {
window = DEFAULT_WINDOW; // 5 minutes, comparable to EDR
}
return new SlidingTimeWindowReservoir(window, TimeUnit.SECONDS);
} else if (clazz.equals(SW_CLAZZ)) {
return new SlidingWindowReservoir(size);
} else { // custom reservoir
Reservoir reservoir;
try {
reservoir = loader.newInstance(clazz, Reservoir.class);
if (reservoir instanceof PluginInfoInitialized) {
((PluginInfoInitialized)reservoir).init(info);
} else {
SolrPluginUtils.invokeSetters(reservoir, info.initArgs, true);
}
return reservoir;
} catch (Exception e) {
log.warn("Error initializing custom Reservoir implementation (will use default): " + info, e);
return new ExponentiallyDecayingReservoir(size, alpha, clk);
}
}
}
/**
* Default supplier of {@link Timer} instances, with configurable clock and reservoir.
* See {@link DefaultMeterSupplier} for clock configuration. Reservoir configuration uses
* {@link #RESERVOIR}, {@link #RESERVOIR_EDR_ALPHA}, {@link #RESERVOIR_SIZE} and
* {@link #RESERVOIR_WINDOW}.
*/
public static final class DefaultTimerSupplier implements MetricRegistry.MetricSupplier<Timer>, PluginInfoInitialized {
public Clock clk = Clock.defaultClock();
private PluginInfo info;
private SolrResourceLoader loader;
public DefaultTimerSupplier(SolrResourceLoader loader) {
this.loader = loader;
}
@Override
public void init(PluginInfo info) {
clk = getClock(info, CLOCK);
this.info = info;
}
public Reservoir getReservoir() {
return MetricSuppliers.getReservoir(loader, info);
}
@Override
public Timer newMetric() {
return new Timer(getReservoir(), clk);
}
}
/**
* Default supplier of {@link Histogram} instances, with configurable reservoir.
*/
public static final class DefaultHistogramSupplier implements MetricRegistry.MetricSupplier<Histogram>, PluginInfoInitialized {
private PluginInfo info;
private SolrResourceLoader loader;
public DefaultHistogramSupplier(SolrResourceLoader loader) {
this.loader = loader;
}
@Override
public void init(PluginInfo info) {
this.info = info;
}
public Reservoir getReservoir() {
return MetricSuppliers.getReservoir(loader, info);
}
@Override
public Histogram newMetric() {
return new Histogram(getReservoir());
}
}
/**
* Create a {@link Counter} supplier.
* @param loader resource loader
* @param info plugin configuration, or null for default
* @return configured supplier instance, or default instance if configuration was invalid
*/
public static MetricRegistry.MetricSupplier<Counter> counterSupplier(SolrResourceLoader loader, PluginInfo info) {
if (info == null || info.className == null || info.className.trim().isEmpty()) {
return new DefaultCounterSupplier();
}
MetricRegistry.MetricSupplier<Counter> supplier;
try {
supplier = loader.newInstance(info.className, MetricRegistry.MetricSupplier.class);
} catch (Exception e) {
log.warn("Error creating custom Counter supplier (will use default): " + info, e);
supplier = new DefaultCounterSupplier();
}
if (supplier instanceof PluginInfoInitialized) {
((PluginInfoInitialized)supplier).init(info);
} else {
SolrPluginUtils.invokeSetters(supplier, info.initArgs, true);
}
return supplier;
}
/**
* Create a {@link Meter} supplier.
* @param loader resource loader
* @param info plugin configuration, or null for default
* @return configured supplier instance, or default instance if configuration was invalid
*/
public static MetricRegistry.MetricSupplier<Meter> meterSupplier(SolrResourceLoader loader, PluginInfo info) {
MetricRegistry.MetricSupplier<Meter> supplier;
if (info == null || info.className == null || info.className.isEmpty()) {
supplier = new DefaultMeterSupplier();
} else {
try {
supplier = loader.newInstance(info.className, MetricRegistry.MetricSupplier.class);
} catch (Exception e) {
log.warn("Error creating custom Meter supplier (will use default): " + info, e);
supplier = new DefaultMeterSupplier();
}
}
if (supplier instanceof PluginInfoInitialized) {
((PluginInfoInitialized)supplier).init(info);
} else {
SolrPluginUtils.invokeSetters(supplier, info.initArgs, true);
}
return supplier;
}
/**
* Create a {@link Timer} supplier.
* @param loader resource loader
* @param info plugin configuration, or null for default
* @return configured supplier instance, or default instance if configuration was invalid
*/
public static MetricRegistry.MetricSupplier<Timer> timerSupplier(SolrResourceLoader loader, PluginInfo info) {
MetricRegistry.MetricSupplier<Timer> supplier;
if (info == null || info.className == null || info.className.isEmpty()) {
supplier = new DefaultTimerSupplier(loader);
} else {
try {
supplier = loader.newInstance(info.className, MetricRegistry.MetricSupplier.class);
} catch (Exception e) {
log.warn("Error creating custom Timer supplier (will use default): " + info, e);
supplier = new DefaultTimerSupplier(loader);
}
}
if (supplier instanceof PluginInfoInitialized) {
((PluginInfoInitialized)supplier).init(info);
} else {
SolrPluginUtils.invokeSetters(supplier, info.initArgs, true);
}
return supplier;
}
/**
* Create a {@link Histogram} supplier.
* @param info plugin configuration, or null for default
* @return configured supplier instance, or default instance if configuration was invalid
*/
public static MetricRegistry.MetricSupplier<Histogram> histogramSupplier(SolrResourceLoader loader, PluginInfo info) {
MetricRegistry.MetricSupplier<Histogram> supplier;
if (info == null || info.className == null || info.className.isEmpty()) {
supplier = new DefaultHistogramSupplier(loader);
} else {
try {
supplier = loader.newInstance(info.className, MetricRegistry.MetricSupplier.class);
} catch (Exception e) {
log.warn("Error creating custom Histogram supplier (will use default): " + info, e);
supplier = new DefaultHistogramSupplier(loader);
}
}
if (supplier instanceof PluginInfoInitialized) {
((PluginInfoInitialized)supplier).init(info);
} else {
SolrPluginUtils.invokeSetters(supplier, info.initArgs, true);
}
return supplier;
}
}

View File

@ -82,7 +82,7 @@ public class SolrCoreMetricManager implements Closeable {
*/
public void loadReporters() {
NodeConfig nodeConfig = core.getCoreContainer().getConfig();
PluginInfo[] pluginInfos = nodeConfig.getMetricReporterPlugins();
PluginInfo[] pluginInfos = nodeConfig.getMetricsConfig().getMetricReporters();
metricManager.loadReporters(pluginInfos, core.getResourceLoader(), tag,
SolrInfoBean.Group.core, registryName);
if (cloudMode) {

View File

@ -49,6 +49,7 @@ import com.codahale.metrics.SharedMetricRegistries;
import com.codahale.metrics.Timer;
import org.apache.solr.common.util.NamedList;
import org.apache.solr.core.CoreContainer;
import org.apache.solr.core.MetricsConfig;
import org.apache.solr.core.PluginInfo;
import org.apache.solr.core.SolrCore;
import org.apache.solr.core.SolrInfoBean;
@ -102,7 +103,41 @@ public class SolrMetricManager {
public static final int DEFAULT_CLOUD_REPORTER_PERIOD = 60;
public SolrMetricManager() { }
private MetricRegistry.MetricSupplier<Counter> counterSupplier;
private MetricRegistry.MetricSupplier<Meter> meterSupplier;
private MetricRegistry.MetricSupplier<Timer> timerSupplier;
private MetricRegistry.MetricSupplier<Histogram> histogramSupplier;
public SolrMetricManager() {
counterSupplier = MetricSuppliers.counterSupplier(null, null);
meterSupplier = MetricSuppliers.meterSupplier(null, null);
timerSupplier = MetricSuppliers.timerSupplier(null, null);
histogramSupplier = MetricSuppliers.histogramSupplier(null, null);
}
public SolrMetricManager(SolrResourceLoader loader, MetricsConfig metricsConfig) {
counterSupplier = MetricSuppliers.counterSupplier(loader, metricsConfig.getCounterSupplier());
meterSupplier = MetricSuppliers.meterSupplier(loader, metricsConfig.getMeterSupplier());
timerSupplier = MetricSuppliers.timerSupplier(loader, metricsConfig.getTimerSupplier());
histogramSupplier = MetricSuppliers.histogramSupplier(loader, metricsConfig.getHistogramSupplier());
}
// for unit tests
public MetricRegistry.MetricSupplier<Counter> getCounterSupplier() {
return counterSupplier;
}
public MetricRegistry.MetricSupplier<Meter> getMeterSupplier() {
return meterSupplier;
}
public MetricRegistry.MetricSupplier<Timer> getTimerSupplier() {
return timerSupplier;
}
public MetricRegistry.MetricSupplier<Histogram> getHistogramSupplier() {
return histogramSupplier;
}
/**
* An implementation of {@link MetricFilter} that selects metrics
@ -539,7 +574,7 @@ public class SolrMetricManager {
if (info != null) {
info.registerMetricName(name);
}
return registry(registry).meter(name);
return registry(registry).meter(name, meterSupplier);
}
/**
@ -555,7 +590,7 @@ public class SolrMetricManager {
if (info != null) {
info.registerMetricName(name);
}
return registry(registry).timer(name);
return registry(registry).timer(name, timerSupplier);
}
/**
@ -571,7 +606,7 @@ public class SolrMetricManager {
if (info != null) {
info.registerMetricName(name);
}
return registry(registry).counter(name);
return registry(registry).counter(name, counterSupplier);
}
/**
@ -587,7 +622,7 @@ public class SolrMetricManager {
if (info != null) {
info.registerMetricName(name);
}
return registry(registry).histogram(name);
return registry(registry).histogram(name, histogramSupplier);
}
/**

View File

@ -196,7 +196,7 @@ public class SolrDispatchFilter extends BaseSolrFilter {
private void setupJvmMetrics(CoreContainer coresInit) {
SolrMetricManager metricManager = coresInit.getMetricManager();
final Set<String> hiddenSysProps = coresInit.getConfig().getHiddenSysProps();
final Set<String> hiddenSysProps = coresInit.getConfig().getMetricsConfig().getHiddenSysProps();
try {
String registry = SolrMetricManager.getRegistryName(SolrInfoBean.Group.jvm);
metricManager.registerAll(registry, new AltBufferPoolMetricSet(), true, "buffers");

View File

@ -1070,6 +1070,10 @@ public class SolrPluginUtils {
public static void invokeSetters(Object bean, Iterable<Map.Entry<String,Object>> initArgs) {
invokeSetters(bean, initArgs, false);
}
public static void invokeSetters(Object bean, Iterable<Map.Entry<String,Object>> initArgs, boolean lenient) {
if (initArgs == null) return;
final Class<?> clazz = bean.getClass();
for (Map.Entry<String,Object> entry : initArgs) {
@ -1077,19 +1081,27 @@ public class SolrPluginUtils {
String setterName = "set" + String.valueOf(Character.toUpperCase(key.charAt(0))) + key.substring(1);
try {
final Object val = entry.getValue();
final Method method = findSetter(clazz, setterName, key, val.getClass());
method.invoke(bean, val);
final Method method = findSetter(clazz, setterName, key, val.getClass(), lenient);
if (method != null) {
method.invoke(bean, val);
}
} catch (InvocationTargetException | IllegalAccessException e1) {
if (lenient) {
continue;
}
throw new RuntimeException("Error invoking setter " + setterName + " on class : " + clazz.getName(), e1);
}
}
}
private static Method findSetter(Class<?> clazz, String setterName, String key, Class<?> paramClazz) {
private static Method findSetter(Class<?> clazz, String setterName, String key, Class<?> paramClazz, boolean lenient) {
BeanInfo beanInfo;
try {
beanInfo = Introspector.getBeanInfo(clazz);
} catch (IntrospectionException ie) {
if (lenient) {
return null;
}
throw new RuntimeException("Error getting bean info for class : " + clazz.getName(), ie);
}
for (final boolean matchParamClazz: new boolean[]{true, false}) {
@ -1102,6 +1114,9 @@ public class SolrPluginUtils {
}
}
}
if (lenient) {
return null;
}
throw new RuntimeException("No setter corrresponding to '" + key + "' in " + clazz.getName());
}

View File

@ -0,0 +1,61 @@
<?xml version="1.0" encoding="UTF-8" ?>
<!--
Licensed to the Apache Software Foundation (ASF) under one or more
contributor license agreements. See the NOTICE file distributed with
this work for additional information regarding copyright ownership.
The ASF 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.
-->
<solr>
<metrics>
<suppliers>
<counter class="${counter.class:}">
<str name="foo">bar</str>
<int name="wxt">100</int>
<bool name="flag">true</bool>
</counter>
<meter class="${meter.class:}">
<str name="foo">bar</str>
<str name="clock">${clock:user}</str>
</meter>
<timer class="${timer.class:}">
<str name="foo">bar</str>
<str name="clock">${clock:user}</str>
<str name="reservoir">${timer.reservoir:}</str>
<int name="size">${histogram.size:-1}</int>
<double name="alpha">${histogram.alpha:-1}</double>
<long name="window">${histogram.window:-1}</long>
<str name="strParam">strParam</str>
<int name="intParam">-100</int>
<bool name="boolParam">true</bool>
</timer>
<histogram class="${histogram.class:}">
<str name="foo">bar</str>
<str name="clock">${clock:user}</str>
<str name="reservoir">${histogram.reservoir:}</str>
<int name="size">${histogram.size:-1}</int>
<double name="alpha">${histogram.alpha:-1}</double>
<long name="window">${histogram.window:-1}</long>
</histogram>
</suppliers>
<hiddenSysProps>
<str>foo</str>
<str>bar</str>
<str>baz</str>
</hiddenSysProps>
<!-- this reporter doesn't specify 'group' or 'registry', it will be instantiated for any group. -->
<reporter name="universal" class="org.apache.solr.metrics.reporters.MockMetricReporter">
<str name="configurable">configured</str>
</reporter>
</metrics>
</solr>

View File

@ -124,14 +124,14 @@ public class JvmMetricsTest extends SolrJettyTestBase {
String solrXml = FileUtils.readFileToString(Paths.get(home.toString(), "solr.xml").toFile(), "UTF-8");
NodeConfig config = SolrXmlConfig.fromString(loader, solrXml);
NodeConfig.NodeConfigBuilder.DEFAULT_HIDDEN_SYS_PROPS.forEach(s -> {
assertTrue(s, config.getHiddenSysProps().contains(s));
assertTrue(s, config.getMetricsConfig().getHiddenSysProps().contains(s));
});
// custom config
solrXml = FileUtils.readFileToString(Paths.get(home.toString(), "solr-hiddensysprops.xml").toFile(), "UTF-8");
NodeConfig config2 = SolrXmlConfig.fromString(loader, solrXml);
Arrays.asList("foo", "bar", "baz").forEach(s -> {
assertTrue(s, config2.getHiddenSysProps().contains(s));
assertTrue(s, config2.getMetricsConfig().getHiddenSysProps().contains(s));
});
}

View File

@ -0,0 +1,126 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF 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.apache.solr.metrics;
import java.io.File;
import java.io.InputStream;
import com.carrotsearch.randomizedtesting.rules.SystemPropertiesRestoreRule;
import com.codahale.metrics.Clock;
import com.codahale.metrics.ExponentiallyDecayingReservoir;
import com.codahale.metrics.Reservoir;
import com.codahale.metrics.SlidingTimeWindowReservoir;
import com.codahale.metrics.UniformReservoir;
import org.apache.solr.SolrTestCaseJ4;
import org.apache.solr.core.NodeConfig;
import org.apache.solr.core.SolrResourceLoader;
import org.apache.solr.core.SolrXmlConfig;
import org.junit.AfterClass;
import org.junit.BeforeClass;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.RuleChain;
import org.junit.rules.TestRule;
/**
*
*/
public class MetricsConfigTest extends SolrTestCaseJ4 {
@Rule
public TestRule solrTestRules = RuleChain.outerRule(new SystemPropertiesRestoreRule());
// tmp dir, cleaned up automatically.
private static File solrHome = null;
private static SolrResourceLoader loader = null;
@BeforeClass
public static void setupLoader() throws Exception {
solrHome = createTempDir().toFile();
loader = new SolrResourceLoader(solrHome.toPath());
}
@AfterClass
public static void cleanupLoader() throws Exception {
solrHome = null;
loader = null;
}
@Test
public void testDefaults() throws Exception {
NodeConfig cfg = loadNodeConfig();
SolrMetricManager mgr = new SolrMetricManager(loader, cfg.getMetricsConfig());
assertTrue(mgr.getCounterSupplier() instanceof MetricSuppliers.DefaultCounterSupplier);
assertTrue(mgr.getMeterSupplier() instanceof MetricSuppliers.DefaultMeterSupplier);
assertTrue(mgr.getTimerSupplier() instanceof MetricSuppliers.DefaultTimerSupplier);
assertTrue(mgr.getHistogramSupplier() instanceof MetricSuppliers.DefaultHistogramSupplier);
Clock clk = ((MetricSuppliers.DefaultTimerSupplier)mgr.getTimerSupplier()).clk;
assertTrue(clk instanceof Clock.UserTimeClock);
Reservoir rsv = ((MetricSuppliers.DefaultTimerSupplier)mgr.getTimerSupplier()).getReservoir();
assertTrue(rsv instanceof ExponentiallyDecayingReservoir);
}
@Test
public void testCustomReservoir() throws Exception {
System.setProperty("timer.reservoir", UniformReservoir.class.getName());
System.setProperty("histogram.size", "2048");
System.setProperty("histogram.window", "600");
System.setProperty("histogram.reservoir", SlidingTimeWindowReservoir.class.getName());
NodeConfig cfg = loadNodeConfig();
SolrMetricManager mgr = new SolrMetricManager(loader, cfg.getMetricsConfig());
assertTrue(mgr.getCounterSupplier() instanceof MetricSuppliers.DefaultCounterSupplier);
assertTrue(mgr.getMeterSupplier() instanceof MetricSuppliers.DefaultMeterSupplier);
assertTrue(mgr.getTimerSupplier() instanceof MetricSuppliers.DefaultTimerSupplier);
assertTrue(mgr.getHistogramSupplier() instanceof MetricSuppliers.DefaultHistogramSupplier);
Reservoir rsv = ((MetricSuppliers.DefaultTimerSupplier)mgr.getTimerSupplier()).getReservoir();
assertTrue(rsv instanceof UniformReservoir);
rsv = ((MetricSuppliers.DefaultHistogramSupplier)mgr.getHistogramSupplier()).getReservoir();
assertTrue(rsv instanceof SlidingTimeWindowReservoir);
}
@Test
public void testCustomSupplier() throws Exception {
System.setProperty("counter.class", MockCounterSupplier.class.getName());
System.setProperty("meter.class", MockMeterSupplier.class.getName());
System.setProperty("timer.class", MockTimerSupplier.class.getName());
System.setProperty("histogram.class", MockHistogramSupplier.class.getName());
NodeConfig cfg = loadNodeConfig();
SolrMetricManager mgr = new SolrMetricManager(loader, cfg.getMetricsConfig());
assertTrue(mgr.getCounterSupplier() instanceof MockCounterSupplier);
assertTrue(mgr.getMeterSupplier() instanceof MockMeterSupplier);
assertTrue(mgr.getTimerSupplier() instanceof MockTimerSupplier);
assertTrue(mgr.getHistogramSupplier() instanceof MockHistogramSupplier);
// assert setter-based configuration
MockCounterSupplier mockCounterSupplier = ((MockCounterSupplier)mgr.getCounterSupplier());
assertEquals("bar", mockCounterSupplier.foo);
MockMeterSupplier mockMeterSupplier = ((MockMeterSupplier)mgr.getMeterSupplier());
assertEquals("bar", mockMeterSupplier.foo);
MockTimerSupplier mockTimerSupplier = ((MockTimerSupplier)mgr.getTimerSupplier());
assertEquals(true, mockTimerSupplier.boolParam);
assertEquals("strParam", mockTimerSupplier.strParam);
assertEquals(-100, mockTimerSupplier.intParam);
// assert PluginInfoInitialized-based configuration
MockHistogramSupplier mockHistogramSupplier = ((MockHistogramSupplier)mgr.getHistogramSupplier());
assertNotNull(mockHistogramSupplier.info);
}
private NodeConfig loadNodeConfig() throws Exception {
InputStream is = MetricsConfigTest.class.getResourceAsStream("/solr/solr-metricsconfig.xml");
return SolrXmlConfig.fromInputStream(loader, is);
}
}

View File

@ -0,0 +1,36 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF 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.apache.solr.metrics;
import com.codahale.metrics.Counter;
import com.codahale.metrics.MetricRegistry;
/**
*
*/
public class MockCounterSupplier implements MetricRegistry.MetricSupplier<Counter> {
public String foo;
public void setFoo(String foo) {
this.foo = foo;
}
@Override
public Counter newMetric() {
return new Counter();
}
}

View File

@ -0,0 +1,40 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF 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.apache.solr.metrics;
import com.codahale.metrics.ExponentiallyDecayingReservoir;
import com.codahale.metrics.Histogram;
import com.codahale.metrics.MetricRegistry;
import org.apache.solr.core.PluginInfo;
import org.apache.solr.util.plugin.PluginInfoInitialized;
/**
*
*/
public class MockHistogramSupplier implements MetricRegistry.MetricSupplier<Histogram>, PluginInfoInitialized {
public PluginInfo info;
@Override
public Histogram newMetric() {
return new Histogram(new ExponentiallyDecayingReservoir());
}
@Override
public void init(PluginInfo info) {
this.info = info;
}
}

View File

@ -0,0 +1,36 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF 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.apache.solr.metrics;
import com.codahale.metrics.Meter;
import com.codahale.metrics.MetricRegistry;
/**
*
*/
public class MockMeterSupplier implements MetricRegistry.MetricSupplier<Meter> {
public String foo;
public void setFoo(String foo) {
this.foo = foo;
}
@Override
public Meter newMetric() {
return new Meter();
}
}

View File

@ -0,0 +1,46 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF 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.apache.solr.metrics;
import com.codahale.metrics.MetricRegistry;
import com.codahale.metrics.Timer;
/**
*
*/
public class MockTimerSupplier implements MetricRegistry.MetricSupplier<Timer> {
public boolean boolParam;
public String strParam;
public int intParam;
public void setBoolParam(boolean boolParam) {
this.boolParam = boolParam;
}
public void setStrParam(String strParam) {
this.strParam = strParam;
}
public void setIntParam(int intParam) {
this.intParam = intParam;
}
@Override
public Timer newMetric() {
return new Timer();
}
}

View File

@ -86,7 +86,7 @@ public class SolrMetricsIntegrationTest extends SolrTestCaseJ4 {
cc.rename(DEFAULT_TEST_CORENAME, CORE_NAME);
h.coreName = CORE_NAME;
cfg = cc.getConfig();
PluginInfo[] plugins = cfg.getMetricReporterPlugins();
PluginInfo[] plugins = cfg.getMetricsConfig().getMetricReporters();
assertNotNull(plugins);
assertEquals(10 + jmxReporter, plugins.length);
reporters = metricManager.getReporters("solr.node");

View File

@ -36,6 +36,7 @@ import org.apache.solr.core.CoreContainer;
import org.apache.solr.core.CoreDescriptor;
import org.apache.solr.core.CorePropertiesLocator;
import org.apache.solr.core.CoresLocator;
import org.apache.solr.core.MetricsConfig;
import org.apache.solr.core.NodeConfig;
import org.apache.solr.core.PluginInfo;
import org.apache.solr.core.SolrConfig;
@ -197,12 +198,15 @@ public class TestHarness extends BaseTestHarness {
attributes.put("name", "default");
attributes.put("class", SolrJmxReporter.class.getName());
PluginInfo defaultPlugin = new PluginInfo("reporter", attributes);
MetricsConfig metricsConfig = new MetricsConfig.MetricsConfigBuilder()
.setMetricReporterPlugins(new PluginInfo[] {defaultPlugin})
.build();
return new NodeConfig.NodeConfigBuilder("testNode", loader)
.setUseSchemaCache(Boolean.getBoolean("shareSchema"))
.setCloudConfig(cloudConfig)
.setUpdateShardHandlerConfig(updateShardHandlerConfig)
.setMetricReporterPlugins(new PluginInfo[] {defaultPlugin})
.setMetricsConfig(metricsConfig)
.build();
}