mirror of https://github.com/apache/lucene.git
Squashed commit of branch 'feature/metrics', containing:
SOLR-4735: Improve Solr metrics reporting SOLR-9812: Implement /admin/metrics API SOLR-9805: Use metrics-jvm library to instrument jvm internals SOLR-9788: Use instrumented jetty classes
This commit is contained in:
parent
84bbb8f797
commit
8bbdb6248c
|
@ -14,6 +14,7 @@
|
|||
<orderEntry type="library" scope="TEST" name="JUnit" level="project" />
|
||||
<orderEntry type="library" name="Solr core library" level="project" />
|
||||
<orderEntry type="library" name="Solrj library" level="project" />
|
||||
<orderEntry type="library" name="Solr example library" level="project" />
|
||||
<orderEntry type="module" scope="TEST" module-name="lucene-test-framework" />
|
||||
<orderEntry type="module" scope="TEST" module-name="solr-test-framework" />
|
||||
<orderEntry type="module" module-name="lucene-core" />
|
||||
|
|
|
@ -76,6 +76,8 @@ com.sun.jersey.version = 1.9
|
|||
io.dropwizard.metrics.version = 3.1.2
|
||||
/io.dropwizard.metrics/metrics-core = ${io.dropwizard.metrics.version}
|
||||
/io.dropwizard.metrics/metrics-healthchecks = ${io.dropwizard.metrics.version}
|
||||
/io.dropwizard.metrics/metrics-jetty9 = ${io.dropwizard.metrics.version}
|
||||
/io.dropwizard.metrics/metrics-jvm = ${io.dropwizard.metrics.version}
|
||||
|
||||
io.netty.netty-all.version = 4.0.36.Final
|
||||
/io.netty/netty-all = ${io.netty.netty-all.version}
|
||||
|
|
|
@ -173,11 +173,24 @@ New Features
|
|||
* SOLR-9844: FieldCache information fetched via the mbeans handler or seen via the UI now displays the total size used.
|
||||
The individual cache entries in the response are now formatted better as well. (Varun Thacker)
|
||||
|
||||
<<<<<<< HEAD
|
||||
* SOLR-9513: Generic authentication plugins (GenericHadoopAuthPlugin and ConfigurableInternodeAuthHadoopPlugin) that delegate
|
||||
all functionality to Hadoop authentication framework. (Hrishikesh Gadre via Ishan Chattopadhyaya)
|
||||
|
||||
* SOLR-9860: Enable configuring invariantParams via HttpSolrClient.Builder (Hrishikesh Gadre, Ishan Chattopadhyaya)
|
||||
|
||||
* SOLR-4735: Improve metrics reporting. This uses the dropwizard metric library, adding an internal API
|
||||
for registering and reporting metrics from Solr components. Several new metrics and an improved JMX
|
||||
reporter have been added (Alan Woodward, Jeff Wartes, Christine Poerschke, Kelvin Wong, shalin, ab)
|
||||
|
||||
* SOLR-9788: Use instrumented jetty classes provided by the dropwizard metric library. (shalin)
|
||||
|
||||
* SOLR-9805: Use metrics-jvm library to instrument jvm internals such as GC, memory usage and others. (shalin)
|
||||
|
||||
* SOLR-9812: Added a new /admin/metrics API to return all metrics collected by Solr via API. API supports two
|
||||
optional parameters 'group' (all,jvm,jetty,http,node,core) and 'type' (all,counter,timer,gauge,histogram) both
|
||||
of which are multi-valued. Example: http://localhost:8983/solr/admin/metrics?group=jvm,jetty&type=counter
|
||||
(shalin)
|
||||
|
||||
Optimizations
|
||||
----------------------
|
||||
|
|
|
@ -21,7 +21,7 @@ import java.util.concurrent.atomic.AtomicLong;
|
|||
import com.codahale.metrics.Timer;
|
||||
import org.apache.solr.common.util.NamedList;
|
||||
import org.apache.solr.common.util.SimpleOrderedMap;
|
||||
import org.apache.solr.util.stats.TimerUtils;
|
||||
import org.apache.solr.util.stats.MetricUtils;
|
||||
|
||||
public class AnalyticsStatisticsCollector {
|
||||
private final AtomicLong numRequests;
|
||||
|
@ -95,7 +95,7 @@ public class AnalyticsStatisticsCollector {
|
|||
lst.add("rangeFacets", numRangeFacets.longValue());
|
||||
lst.add("queryFacets", numQueryFacets.longValue());
|
||||
lst.add("queriesInQueryFacets", numQueries.longValue());
|
||||
TimerUtils.addMetrics(lst, requestTimes);
|
||||
MetricUtils.addMetrics(lst, requestTimes);
|
||||
return lst;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -50,7 +50,6 @@
|
|||
<dependency org="log4j" name="log4j" rev="${/log4j/log4j}" conf="compile"/>
|
||||
<dependency org="org.slf4j" name="slf4j-log4j12" rev="${/org.slf4j/slf4j-log4j12}" conf="compile"/>
|
||||
<dependency org="org.slf4j" name="jcl-over-slf4j" rev="${/org.slf4j/jcl-over-slf4j}" conf="compile"/>
|
||||
<dependency org="io.dropwizard.metrics" name="metrics-core" rev="${/io.dropwizard.metrics/metrics-core}" conf="compile" />
|
||||
|
||||
<dependency org="org.easymock" name="easymock" rev="${/org.easymock/easymock}" conf="test"/>
|
||||
<dependency org="cglib" name="cglib-nodep" rev="${/cglib/cglib-nodep}" conf="test"/>
|
||||
|
|
|
@ -30,7 +30,7 @@ import org.apache.solr.common.cloud.ZkNodeProps;
|
|||
import org.apache.solr.common.cloud.ZkStateReader;
|
||||
import org.apache.solr.common.util.NamedList;
|
||||
import org.apache.solr.common.util.SimpleOrderedMap;
|
||||
import org.apache.solr.util.stats.TimerUtils;
|
||||
import org.apache.solr.util.stats.MetricUtils;
|
||||
import org.apache.zookeeper.data.Stat;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
@ -100,7 +100,7 @@ public class OverseerStatusCmd implements Cmd {
|
|||
lst.add("errors", errors);
|
||||
}
|
||||
Timer timer = entry.getValue().requestTime;
|
||||
TimerUtils.addMetrics(lst, timer);
|
||||
MetricUtils.addMetrics(lst, timer);
|
||||
}
|
||||
results.add("overseer_operations", overseerStats);
|
||||
results.add("collection_operations", collectionStats);
|
||||
|
|
|
@ -33,6 +33,7 @@ import java.util.concurrent.ExecutionException;
|
|||
import java.util.concurrent.ExecutorService;
|
||||
import java.util.concurrent.Future;
|
||||
|
||||
import com.codahale.metrics.Gauge;
|
||||
import com.google.common.collect.ImmutableMap;
|
||||
import com.google.common.collect.Maps;
|
||||
import org.apache.http.auth.AuthSchemeProvider;
|
||||
|
@ -58,6 +59,7 @@ import org.apache.solr.handler.admin.CollectionsHandler;
|
|||
import org.apache.solr.handler.admin.ConfigSetsHandler;
|
||||
import org.apache.solr.handler.admin.CoreAdminHandler;
|
||||
import org.apache.solr.handler.admin.InfoHandler;
|
||||
import org.apache.solr.handler.admin.MetricsHandler;
|
||||
import org.apache.solr.handler.admin.SecurityConfHandler;
|
||||
import org.apache.solr.handler.admin.SecurityConfHandlerLocal;
|
||||
import org.apache.solr.handler.admin.SecurityConfHandlerZk;
|
||||
|
@ -65,6 +67,8 @@ import org.apache.solr.handler.admin.ZookeeperInfoHandler;
|
|||
import org.apache.solr.handler.component.ShardHandlerFactory;
|
||||
import org.apache.solr.logging.LogWatcher;
|
||||
import org.apache.solr.logging.MDCLoggingContext;
|
||||
import org.apache.solr.metrics.SolrMetricManager;
|
||||
import org.apache.solr.metrics.SolrMetricProducer;
|
||||
import org.apache.solr.request.SolrRequestHandler;
|
||||
import org.apache.solr.security.AuthenticationPlugin;
|
||||
import org.apache.solr.security.AuthorizationPlugin;
|
||||
|
@ -85,6 +89,7 @@ import static org.apache.solr.common.params.CommonParams.COLLECTIONS_HANDLER_PAT
|
|||
import static org.apache.solr.common.params.CommonParams.CONFIGSETS_HANDLER_PATH;
|
||||
import static org.apache.solr.common.params.CommonParams.CORES_HANDLER_PATH;
|
||||
import static org.apache.solr.common.params.CommonParams.INFO_HANDLER_PATH;
|
||||
import static org.apache.solr.common.params.CommonParams.METRICS_PATH;
|
||||
import static org.apache.solr.common.params.CommonParams.ZK_PATH;
|
||||
import static org.apache.solr.security.AuthenticationPlugin.AUTHENTICATION_PLUGIN_PROP;
|
||||
|
||||
|
@ -156,6 +161,10 @@ public class CoreContainer {
|
|||
|
||||
private BackupRepositoryFactory backupRepoFactory;
|
||||
|
||||
protected SolrMetricManager metricManager;
|
||||
|
||||
protected MetricsHandler metricsHandler;
|
||||
|
||||
/**
|
||||
* This method instantiates a new instance of {@linkplain BackupRepository}.
|
||||
*
|
||||
|
@ -423,6 +432,10 @@ public class CoreContainer {
|
|||
return pkiAuthenticationPlugin;
|
||||
}
|
||||
|
||||
public SolrMetricManager getMetricManager() {
|
||||
return metricManager;
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------
|
||||
// Initialization / Cleanup
|
||||
//-------------------------------------------------------------------
|
||||
|
@ -463,28 +476,45 @@ public class CoreContainer {
|
|||
|
||||
MDCLoggingContext.setNode(this);
|
||||
|
||||
metricManager = new SolrMetricManager();
|
||||
|
||||
securityConfHandler = isZooKeeperAware() ? new SecurityConfHandlerZk(this) : new SecurityConfHandlerLocal(this);
|
||||
reloadSecurityProperties();
|
||||
this.backupRepoFactory = new BackupRepositoryFactory(cfg.getBackupRepositoryPlugins());
|
||||
|
||||
containerHandlers.put(ZK_PATH, new ZookeeperInfoHandler(this));
|
||||
collectionsHandler = createHandler(cfg.getCollectionsHandlerClass(), CollectionsHandler.class);
|
||||
containerHandlers.put(COLLECTIONS_HANDLER_PATH, collectionsHandler);
|
||||
infoHandler = createHandler(cfg.getInfoHandlerClass(), InfoHandler.class);
|
||||
containerHandlers.put(INFO_HANDLER_PATH, infoHandler);
|
||||
coreAdminHandler = createHandler(cfg.getCoreAdminHandlerClass(), CoreAdminHandler.class);
|
||||
containerHandlers.put(CORES_HANDLER_PATH, coreAdminHandler);
|
||||
configSetsHandler = createHandler(cfg.getConfigSetsHandlerClass(), ConfigSetsHandler.class);
|
||||
containerHandlers.put(CONFIGSETS_HANDLER_PATH, configSetsHandler);
|
||||
createHandler(ZK_PATH, ZookeeperInfoHandler.class.getName(), ZookeeperInfoHandler.class);
|
||||
collectionsHandler = createHandler(COLLECTIONS_HANDLER_PATH, cfg.getCollectionsHandlerClass(), CollectionsHandler.class);
|
||||
infoHandler = createHandler(INFO_HANDLER_PATH, cfg.getInfoHandlerClass(), InfoHandler.class);
|
||||
coreAdminHandler = createHandler(CORES_HANDLER_PATH, cfg.getCoreAdminHandlerClass(), CoreAdminHandler.class);
|
||||
configSetsHandler = createHandler(CONFIGSETS_HANDLER_PATH, cfg.getConfigSetsHandlerClass(), ConfigSetsHandler.class);
|
||||
metricsHandler = createHandler(METRICS_PATH, MetricsHandler.class.getName(), MetricsHandler.class);
|
||||
containerHandlers.put(AUTHZ_PATH, securityConfHandler);
|
||||
securityConfHandler.initializeMetrics(metricManager, SolrInfoMBean.Group.node.toString(), AUTHZ_PATH);
|
||||
containerHandlers.put(AUTHC_PATH, securityConfHandler);
|
||||
if(pkiAuthenticationPlugin != null)
|
||||
containerHandlers.put(PKIAuthenticationPlugin.PATH, pkiAuthenticationPlugin.getRequestHandler());
|
||||
|
||||
metricManager.loadReporters(cfg.getMetricReporterPlugins(), loader, SolrInfoMBean.Group.node);
|
||||
metricManager.loadReporters(cfg.getMetricReporterPlugins(), loader, SolrInfoMBean.Group.jvm);
|
||||
metricManager.loadReporters(cfg.getMetricReporterPlugins(), loader, SolrInfoMBean.Group.jetty);
|
||||
metricManager.loadReporters(cfg.getMetricReporterPlugins(), loader, SolrInfoMBean.Group.http);
|
||||
|
||||
coreConfigService = ConfigSetService.createConfigSetService(cfg, loader, zkSys.zkController);
|
||||
|
||||
containerProperties.putAll(cfg.getSolrProperties());
|
||||
|
||||
// initialize gauges for reporting the number of cores
|
||||
Gauge<Integer> loadedCores = () -> solrCores.getCores().size();
|
||||
Gauge<Integer> lazyCores = () -> solrCores.getCoreNames().size() - solrCores.getCores().size();
|
||||
Gauge<Integer> unloadedCores = () -> solrCores.getAllCoreNames().size() - solrCores.getCoreNames().size();
|
||||
|
||||
metricManager.register(SolrMetricManager.getRegistryName(SolrInfoMBean.Group.node),
|
||||
loadedCores, true, "loaded", "cores");
|
||||
metricManager.register(SolrMetricManager.getRegistryName(SolrInfoMBean.Group.node),
|
||||
lazyCores, true, "lazy", "cores");
|
||||
metricManager.register(SolrMetricManager.getRegistryName(SolrInfoMBean.Group.node),
|
||||
unloadedCores, true, "unloaded", "cores");
|
||||
|
||||
// setup executor to load cores in parallel
|
||||
ExecutorService coreLoadExecutor = ExecutorUtil.newMDCAwareFixedThreadPool(
|
||||
cfg.getCoreLoadThreadCount(isZooKeeperAware()),
|
||||
|
@ -658,6 +688,10 @@ public class CoreContainer {
|
|||
}
|
||||
}
|
||||
|
||||
if (metricManager != null) {
|
||||
metricManager.closeReporters(SolrMetricManager.getRegistryName(SolrInfoMBean.Group.node));
|
||||
}
|
||||
|
||||
// It should be safe to close the authorization plugin at this point.
|
||||
try {
|
||||
if(authorizationPlugin != null) {
|
||||
|
@ -1034,6 +1068,9 @@ public class CoreContainer {
|
|||
SolrCore core = solrCores.remove(name);
|
||||
coresLocator.delete(this, cd);
|
||||
|
||||
// delete metrics specific to this core
|
||||
metricManager.removeRegistry(SolrMetricManager.getRegistryName(SolrInfoMBean.Group.core, name));
|
||||
|
||||
if (core == null) {
|
||||
// transient core
|
||||
SolrCore.deleteUnloadedCore(cd, deleteDataDir, deleteInstanceDir);
|
||||
|
@ -1167,8 +1204,15 @@ public class CoreContainer {
|
|||
|
||||
// ---------------- CoreContainer request handlers --------------
|
||||
|
||||
protected <T> T createHandler(String handlerClass, Class<T> clazz) {
|
||||
return loader.newInstance(handlerClass, clazz, null, new Class[] { CoreContainer.class }, new Object[] { this });
|
||||
protected <T> T createHandler(String path, String handlerClass, Class<T> clazz) {
|
||||
T handler = loader.newInstance(handlerClass, clazz, null, new Class[] { CoreContainer.class }, new Object[] { this });
|
||||
if (handler instanceof SolrRequestHandler) {
|
||||
containerHandlers.put(path, (SolrRequestHandler)handler);
|
||||
}
|
||||
if (handler instanceof SolrMetricProducer) {
|
||||
((SolrMetricProducer)handler).initializeMetrics(metricManager, SolrInfoMBean.Group.node.toString(), path);
|
||||
}
|
||||
return handler;
|
||||
}
|
||||
|
||||
public CoreAdminHandler getMultiCoreHandler() {
|
||||
|
|
|
@ -60,13 +60,16 @@ public class NodeConfig {
|
|||
|
||||
private final PluginInfo[] backupRepositoryPlugins;
|
||||
|
||||
private final PluginInfo[] metricReporterPlugins;
|
||||
|
||||
private NodeConfig(String nodeName, Path coreRootDirectory, Path configSetBaseDirectory, String sharedLibDirectory,
|
||||
PluginInfo shardHandlerFactoryConfig, UpdateShardHandlerConfig updateShardHandlerConfig,
|
||||
String coreAdminHandlerClass, String collectionsAdminHandlerClass,
|
||||
String infoHandlerClass, String configSetsHandlerClass,
|
||||
LogWatcherConfig logWatcherConfig, CloudConfig cloudConfig, Integer coreLoadThreads,
|
||||
int transientCacheSize, boolean useSchemaCache, String managementPath, SolrResourceLoader loader,
|
||||
Properties solrProperties, PluginInfo[] backupRepositoryPlugins) {
|
||||
Properties solrProperties, PluginInfo[] backupRepositoryPlugins,
|
||||
PluginInfo[] metricReporterPlugins) {
|
||||
this.nodeName = nodeName;
|
||||
this.coreRootDirectory = coreRootDirectory;
|
||||
this.configSetBaseDirectory = configSetBaseDirectory;
|
||||
|
@ -86,6 +89,7 @@ public class NodeConfig {
|
|||
this.loader = loader;
|
||||
this.solrProperties = solrProperties;
|
||||
this.backupRepositoryPlugins = backupRepositoryPlugins;
|
||||
this.metricReporterPlugins = metricReporterPlugins;
|
||||
|
||||
if (this.cloudConfig != null && this.getCoreLoadThreadCount(false) < 2) {
|
||||
throw new SolrException(SolrException.ErrorCode.SERVER_ERROR,
|
||||
|
@ -174,6 +178,10 @@ public class NodeConfig {
|
|||
return backupRepositoryPlugins;
|
||||
}
|
||||
|
||||
public PluginInfo[] getMetricReporterPlugins() {
|
||||
return metricReporterPlugins;
|
||||
}
|
||||
|
||||
public static class NodeConfigBuilder {
|
||||
|
||||
private Path coreRootDirectory;
|
||||
|
@ -193,6 +201,7 @@ public class NodeConfig {
|
|||
private String managementPath;
|
||||
private Properties solrProperties = new Properties();
|
||||
private PluginInfo[] backupRepositoryPlugins;
|
||||
private PluginInfo[] metricReporterPlugins;
|
||||
|
||||
private final SolrResourceLoader loader;
|
||||
private final String nodeName;
|
||||
|
@ -300,11 +309,16 @@ public class NodeConfig {
|
|||
return this;
|
||||
}
|
||||
|
||||
public NodeConfigBuilder setMetricReporterPlugins(PluginInfo[] metricReporterPlugins) {
|
||||
this.metricReporterPlugins = metricReporterPlugins;
|
||||
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);
|
||||
backupRepositoryPlugins, metricReporterPlugins);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -111,6 +111,7 @@ public class PluginInfo implements MapSerializable {
|
|||
if (type != null) sb.append("type = " + type + ",");
|
||||
if (name != null) sb.append("name = " + name + ",");
|
||||
if (className != null) sb.append("class = " + className + ",");
|
||||
if (attributes != null && attributes.size() > 0) sb.append("attributes = " + attributes + ",");
|
||||
if (initArgs != null && initArgs.size() > 0) sb.append("args = " + initArgs);
|
||||
sb.append("}");
|
||||
return sb.toString();
|
||||
|
@ -181,7 +182,8 @@ public class PluginInfo implements MapSerializable {
|
|||
|
||||
}
|
||||
public PluginInfo copy() {
|
||||
PluginInfo result = new PluginInfo(type, attributes, initArgs.clone(), children);
|
||||
PluginInfo result = new PluginInfo(type, attributes,
|
||||
initArgs != null ? initArgs.clone() : null, children);
|
||||
result.isFromSolrConfig = isFromSolrConfig;
|
||||
return result;
|
||||
}
|
||||
|
|
|
@ -53,6 +53,8 @@ import java.util.concurrent.TimeUnit;
|
|||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
import java.util.concurrent.locks.ReentrantLock;
|
||||
|
||||
import com.codahale.metrics.Counter;
|
||||
import com.codahale.metrics.Timer;
|
||||
import com.google.common.collect.MapMaker;
|
||||
import org.apache.commons.io.FileUtils;
|
||||
import org.apache.lucene.analysis.util.ResourceLoader;
|
||||
|
@ -94,6 +96,9 @@ import org.apache.solr.handler.RequestHandlerBase;
|
|||
import org.apache.solr.handler.component.HighlightComponent;
|
||||
import org.apache.solr.handler.component.SearchComponent;
|
||||
import org.apache.solr.logging.MDCLoggingContext;
|
||||
import org.apache.solr.metrics.SolrCoreMetricManager;
|
||||
import org.apache.solr.metrics.SolrMetricManager;
|
||||
import org.apache.solr.metrics.SolrMetricProducer;
|
||||
import org.apache.solr.request.SolrQueryRequest;
|
||||
import org.apache.solr.request.SolrRequestHandler;
|
||||
import org.apache.solr.response.BinaryResponseWriter;
|
||||
|
@ -187,6 +192,7 @@ public final class SolrCore implements SolrInfoMBean, Closeable {
|
|||
private final PluginBag<SearchComponent> searchComponents = new PluginBag<>(SearchComponent.class, this);
|
||||
private final PluginBag<UpdateRequestProcessorFactory> updateProcessors = new PluginBag<>(UpdateRequestProcessorFactory.class, this, true);
|
||||
private final Map<String,UpdateRequestProcessorChain> updateProcessorChains;
|
||||
private final SolrCoreMetricManager coreMetricManager;
|
||||
private final Map<String, SolrInfoMBean> infoRegistry;
|
||||
private final IndexDeletionPolicyWrapper solrDelPolicy;
|
||||
private final SolrSnapshotMetaDataManager snapshotMgr;
|
||||
|
@ -200,6 +206,12 @@ public final class SolrCore implements SolrInfoMBean, Closeable {
|
|||
private final ReentrantLock ruleExpiryLock;
|
||||
private final ReentrantLock snapshotDelLock; // A lock instance to guard against concurrent deletions.
|
||||
|
||||
private final Timer newSearcherTimer;
|
||||
private final Timer newSearcherWarmupTimer;
|
||||
private final Counter newSearcherCounter;
|
||||
private final Counter newSearcherMaxReachedCounter;
|
||||
private final Counter newSearcherOtherErrorsCounter;
|
||||
|
||||
public Date getStartTimeStamp() { return startTime; }
|
||||
|
||||
private final Map<Object, IndexFingerprint> perSegmentFingerprintCache = new MapMaker().weakKeys().makeMap();
|
||||
|
@ -386,9 +398,13 @@ public final class SolrCore implements SolrInfoMBean, Closeable {
|
|||
}
|
||||
|
||||
public void setName(String v) {
|
||||
String oldName = this.name;
|
||||
this.name = v;
|
||||
this.logid = (v==null)?"":("["+v+"] ");
|
||||
this.coreDescriptor = new CoreDescriptor(v, this.coreDescriptor);
|
||||
if (coreMetricManager != null) {
|
||||
coreMetricManager.afterCoreSetName();
|
||||
}
|
||||
}
|
||||
|
||||
public String getLogId()
|
||||
|
@ -396,6 +412,15 @@ public final class SolrCore implements SolrInfoMBean, Closeable {
|
|||
return this.logid;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the {@link SolrCoreMetricManager} for this core.
|
||||
*
|
||||
* @return the {@link SolrCoreMetricManager} for this core
|
||||
*/
|
||||
public SolrCoreMetricManager getCoreMetricManager() {
|
||||
return coreMetricManager;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a Map of name vs SolrInfoMBean objects. The returned map is an instance of
|
||||
* a ConcurrentHashMap and therefore no synchronization is needed for putting, removing
|
||||
|
@ -838,6 +863,18 @@ public final class SolrCore implements SolrInfoMBean, Closeable {
|
|||
|
||||
checkVersionFieldExistsInSchema(schema, coreDescriptor);
|
||||
|
||||
// Initialize the metrics manager
|
||||
this.coreMetricManager = initCoreMetricManager(config);
|
||||
|
||||
SolrMetricManager metricManager = this.coreDescriptor.getCoreContainer().getMetricManager();
|
||||
|
||||
// initialize searcher-related metrics
|
||||
newSearcherCounter = metricManager.counter(coreMetricManager.getRegistryName(), "newSearcher");
|
||||
newSearcherTimer = metricManager.timer(coreMetricManager.getRegistryName(), "newSearcherTime");
|
||||
newSearcherWarmupTimer = metricManager.timer(coreMetricManager.getRegistryName(), "newSearcherWarmup");
|
||||
newSearcherMaxReachedCounter = metricManager.counter(coreMetricManager.getRegistryName(), "newSearcherMaxReached");
|
||||
newSearcherOtherErrorsCounter = metricManager.counter(coreMetricManager.getRegistryName(), "newSearcherErrors");
|
||||
|
||||
// Initialize JMX
|
||||
this.infoRegistry = initInfoRegistry(name, config);
|
||||
infoRegistry.put("fieldCache", new SolrFieldCacheMBean());
|
||||
|
@ -1041,6 +1078,19 @@ public final class SolrCore implements SolrInfoMBean, Closeable {
|
|||
setLatestSchema(schema);
|
||||
}
|
||||
|
||||
/**
|
||||
* Initializes the core's {@link SolrCoreMetricManager} with a given configuration.
|
||||
* If metric reporters are configured, they are also initialized for this core.
|
||||
*
|
||||
* @param config the given configuration
|
||||
* @return an instance of {@link SolrCoreMetricManager}
|
||||
*/
|
||||
private SolrCoreMetricManager initCoreMetricManager(SolrConfig config) {
|
||||
SolrCoreMetricManager coreMetricManager = new SolrCoreMetricManager(this);
|
||||
coreMetricManager.loadReporters();
|
||||
return coreMetricManager;
|
||||
}
|
||||
|
||||
private Map<String,SolrInfoMBean> initInfoRegistry(String name, SolrConfig config) {
|
||||
if (config.jmxConfig.enabled) {
|
||||
return new JmxMonitoredMap<String, SolrInfoMBean>(name, String.valueOf(this.hashCode()), config.jmxConfig);
|
||||
|
@ -1361,6 +1411,15 @@ public final class SolrCore implements SolrInfoMBean, Closeable {
|
|||
}
|
||||
}
|
||||
|
||||
try {
|
||||
coreMetricManager.close();
|
||||
} catch (Throwable e) {
|
||||
SolrException.log(log, e);
|
||||
if (e instanceof Error) {
|
||||
throw (Error) e;
|
||||
}
|
||||
}
|
||||
|
||||
// Close the snapshots meta-data directory.
|
||||
Directory snapshotsDir = snapshotMgr.getSnapshotsDir();
|
||||
try {
|
||||
|
@ -1920,12 +1979,14 @@ public final class SolrCore implements SolrInfoMBean, Closeable {
|
|||
// first: increment count to signal other threads that we are
|
||||
// opening a new searcher.
|
||||
onDeckSearchers++;
|
||||
newSearcherCounter.inc();
|
||||
if (onDeckSearchers < 1) {
|
||||
// should never happen... just a sanity check
|
||||
log.error(logid + "ERROR!!! onDeckSearchers is " + onDeckSearchers);
|
||||
onDeckSearchers = 1; // reset
|
||||
} else if (onDeckSearchers > maxWarmingSearchers) {
|
||||
onDeckSearchers--;
|
||||
newSearcherMaxReachedCounter.inc();
|
||||
try {
|
||||
searcherLock.wait();
|
||||
} catch (InterruptedException e) {
|
||||
|
@ -1947,6 +2008,7 @@ public final class SolrCore implements SolrInfoMBean, Closeable {
|
|||
boolean success = false;
|
||||
|
||||
openSearcherLock.lock();
|
||||
Timer.Context timerContext = newSearcherTimer.time();
|
||||
try {
|
||||
searchHolder = openNewSearcher(updateHandlerReopens, false);
|
||||
// the searchHolder will be incremented once already (and it will eventually be assigned to _searcher when registered)
|
||||
|
@ -1989,6 +2051,7 @@ public final class SolrCore implements SolrInfoMBean, Closeable {
|
|||
// should this go before the other event handlers or after?
|
||||
if (currSearcher != null) {
|
||||
future = searcherExecutor.submit(() -> {
|
||||
Timer.Context warmupContext = newSearcherWarmupTimer.time();
|
||||
try {
|
||||
newSearcher.warm(currSearcher);
|
||||
} catch (Throwable e) {
|
||||
|
@ -1996,6 +2059,8 @@ public final class SolrCore implements SolrInfoMBean, Closeable {
|
|||
if (e instanceof Error) {
|
||||
throw (Error) e;
|
||||
}
|
||||
} finally {
|
||||
warmupContext.close();
|
||||
}
|
||||
return null;
|
||||
});
|
||||
|
@ -2076,7 +2141,10 @@ public final class SolrCore implements SolrInfoMBean, Closeable {
|
|||
throw new SolrException(SolrException.ErrorCode.SERVER_ERROR, e);
|
||||
} finally {
|
||||
|
||||
timerContext.close();
|
||||
|
||||
if (!success) {
|
||||
newSearcherOtherErrorsCounter.inc();;
|
||||
synchronized (searcherLock) {
|
||||
onDeckSearchers--;
|
||||
|
||||
|
@ -2750,6 +2818,11 @@ public final class SolrCore implements SolrInfoMBean, Closeable {
|
|||
|
||||
public void registerInfoBean(String name, SolrInfoMBean solrInfoMBean) {
|
||||
infoRegistry.put(name, solrInfoMBean);
|
||||
|
||||
if (solrInfoMBean instanceof SolrMetricProducer) {
|
||||
SolrMetricProducer producer = (SolrMetricProducer) solrInfoMBean;
|
||||
coreMetricManager.registerMetricProducer(name, producer);
|
||||
}
|
||||
}
|
||||
|
||||
private static boolean checkStale(SolrZkClient zkClient, String zkPath, int currentVersion) {
|
||||
|
|
|
@ -29,7 +29,15 @@ import org.apache.solr.common.util.NamedList;
|
|||
*/
|
||||
public interface SolrInfoMBean {
|
||||
|
||||
public enum Category { CORE, QUERYHANDLER, UPDATEHANDLER, CACHE, HIGHLIGHTING, QUERYPARSER, OTHER };
|
||||
/**
|
||||
* Category of {@link SolrCore} component.
|
||||
*/
|
||||
enum Category { CORE, QUERYHANDLER, UPDATEHANDLER, CACHE, HIGHLIGHTING, QUERYPARSER, OTHER }
|
||||
|
||||
/**
|
||||
* Top-level group of beans for a subsystem.
|
||||
*/
|
||||
enum Group { jvm, jetty, http, node, core }
|
||||
|
||||
/**
|
||||
* Simple common usage name, e.g. BasicQueryHandler,
|
||||
|
|
|
@ -95,7 +95,8 @@ public class SolrXmlConfig {
|
|||
configBuilder.setSolrProperties(loadProperties(config));
|
||||
if (cloudConfig != null)
|
||||
configBuilder.setCloudConfig(cloudConfig);
|
||||
configBuilder.setBackupRepositoryPlugins((getBackupRepositoryPluginInfos(config)));
|
||||
configBuilder.setBackupRepositoryPlugins(getBackupRepositoryPluginInfos(config));
|
||||
configBuilder.setMetricReporterPlugins(getMetricReporterPluginInfos(config));
|
||||
return fillSolrSection(configBuilder, entries);
|
||||
}
|
||||
|
||||
|
@ -436,5 +437,16 @@ public class SolrXmlConfig {
|
|||
}
|
||||
return configs;
|
||||
}
|
||||
|
||||
private static PluginInfo[] getMetricReporterPluginInfos(Config config) {
|
||||
NodeList nodes = (NodeList) config.evaluate("solr/metrics/reporter", XPathConstants.NODESET);
|
||||
if (nodes == null || nodes.getLength() == 0)
|
||||
return new PluginInfo[0];
|
||||
PluginInfo[] configs = new PluginInfo[nodes.getLength()];
|
||||
for (int i = 0; i < nodes.getLength(); i++) {
|
||||
configs[i] = new PluginInfo(nodes.item(i), "SolrMetricReporter", true, true);
|
||||
}
|
||||
return configs;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -18,8 +18,11 @@ package org.apache.solr.handler;
|
|||
|
||||
import java.lang.invoke.MethodHandles;
|
||||
import java.net.URL;
|
||||
import java.util.concurrent.atomic.LongAdder;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
|
||||
import com.codahale.metrics.Counter;
|
||||
import com.codahale.metrics.Meter;
|
||||
import com.codahale.metrics.Timer;
|
||||
import org.apache.solr.common.SolrException;
|
||||
import org.apache.solr.common.params.SolrParams;
|
||||
|
@ -29,12 +32,14 @@ import org.apache.solr.common.util.SuppressForbidden;
|
|||
import org.apache.solr.core.PluginBag;
|
||||
import org.apache.solr.core.PluginInfo;
|
||||
import org.apache.solr.core.SolrInfoMBean;
|
||||
import org.apache.solr.metrics.SolrMetricManager;
|
||||
import org.apache.solr.metrics.SolrMetricProducer;
|
||||
import org.apache.solr.request.SolrQueryRequest;
|
||||
import org.apache.solr.request.SolrRequestHandler;
|
||||
import org.apache.solr.response.SolrQueryResponse;
|
||||
import org.apache.solr.search.SyntaxError;
|
||||
import org.apache.solr.util.SolrPluginUtils;
|
||||
import org.apache.solr.util.stats.TimerUtils;
|
||||
import org.apache.solr.util.stats.MetricUtils;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
|
@ -43,7 +48,7 @@ import static org.apache.solr.core.RequestParams.USEPARAM;
|
|||
/**
|
||||
*
|
||||
*/
|
||||
public abstract class RequestHandlerBase implements SolrRequestHandler, SolrInfoMBean, NestedRequestHandler {
|
||||
public abstract class RequestHandlerBase implements SolrRequestHandler, SolrInfoMBean, SolrMetricProducer, NestedRequestHandler {
|
||||
|
||||
protected NamedList initArgs = null;
|
||||
protected SolrParams defaults;
|
||||
|
@ -52,11 +57,12 @@ public abstract class RequestHandlerBase implements SolrRequestHandler, SolrInfo
|
|||
protected boolean httpCaching = true;
|
||||
|
||||
// Statistics
|
||||
private final LongAdder numRequests = new LongAdder();
|
||||
private final LongAdder numServerErrors = new LongAdder();
|
||||
private final LongAdder numClientErrors = new LongAdder();
|
||||
private final LongAdder numTimeouts = new LongAdder();
|
||||
private final Timer requestTimes = new Timer();
|
||||
private Meter numErrors = new Meter();
|
||||
private Meter numServerErrors = new Meter();
|
||||
private Meter numClientErrors = new Meter();
|
||||
private Meter numTimeouts = new Meter();
|
||||
private Counter requests = new Counter();
|
||||
private Timer requestTimes = new Timer();
|
||||
|
||||
private final long handlerStart;
|
||||
|
||||
|
@ -126,6 +132,17 @@ public abstract class RequestHandlerBase implements SolrRequestHandler, SolrInfo
|
|||
|
||||
}
|
||||
|
||||
@Override
|
||||
public Collection<String> initializeMetrics(SolrMetricManager manager, String registryName, String scope) {
|
||||
numErrors = manager.meter(registryName, "errors", getCategory().toString(), scope);
|
||||
numServerErrors = manager.meter(registryName, "serverErrors", getCategory().toString(), scope);
|
||||
numClientErrors = manager.meter(registryName, "clientErrors", getCategory().toString(), scope);
|
||||
numTimeouts = manager.meter(registryName, "timeouts", getCategory().toString(), scope);
|
||||
requests = manager.counter(registryName, "requests", getCategory().toString(), scope);
|
||||
requestTimes = manager.timer(registryName, "requestTimes", getCategory().toString(), scope);
|
||||
return Arrays.asList("errors", "serverErrors", "clientErrors", "timeouts", "requestTimes", "requests");
|
||||
}
|
||||
|
||||
public static SolrParams getSolrParamsFromNamedList(NamedList args, String key) {
|
||||
Object o = args.get(key);
|
||||
if (o != null && o instanceof NamedList) {
|
||||
|
@ -142,7 +159,7 @@ public abstract class RequestHandlerBase implements SolrRequestHandler, SolrInfo
|
|||
|
||||
@Override
|
||||
public void handleRequest(SolrQueryRequest req, SolrQueryResponse rsp) {
|
||||
numRequests.increment();
|
||||
requests.inc();
|
||||
Timer.Context timer = requestTimes.time();
|
||||
try {
|
||||
if(pluginInfo != null && pluginInfo.attributes.containsKey(USEPARAM)) req.getContext().put(USEPARAM,pluginInfo.attributes.get(USEPARAM));
|
||||
|
@ -156,7 +173,7 @@ public abstract class RequestHandlerBase implements SolrRequestHandler, SolrInfo
|
|||
Object partialResults = header.get(SolrQueryResponse.RESPONSE_HEADER_PARTIAL_RESULTS_KEY);
|
||||
boolean timedOut = partialResults == null ? false : (Boolean)partialResults;
|
||||
if( timedOut ) {
|
||||
numTimeouts.increment();
|
||||
numTimeouts.mark();
|
||||
rsp.setHttpCaching(false);
|
||||
}
|
||||
}
|
||||
|
@ -182,14 +199,14 @@ public abstract class RequestHandlerBase implements SolrRequestHandler, SolrInfo
|
|||
if (incrementErrors) {
|
||||
SolrException.log(log, e);
|
||||
|
||||
numErrors.mark();
|
||||
if (isServerError) {
|
||||
numServerErrors.increment();
|
||||
numServerErrors.mark();
|
||||
} else {
|
||||
numClientErrors.increment();
|
||||
numClientErrors.mark();
|
||||
}
|
||||
}
|
||||
}
|
||||
finally {
|
||||
} finally {
|
||||
timer.stop();
|
||||
}
|
||||
}
|
||||
|
@ -268,15 +285,14 @@ public abstract class RequestHandlerBase implements SolrRequestHandler, SolrInfo
|
|||
public NamedList<Object> getStatistics() {
|
||||
NamedList<Object> lst = new SimpleOrderedMap<>();
|
||||
lst.add("handlerStart",handlerStart);
|
||||
lst.add("requests", numRequests.longValue());
|
||||
lst.add("errors", numServerErrors.longValue() + numClientErrors.longValue());
|
||||
lst.add("serverErrors", numServerErrors.longValue());
|
||||
lst.add("clientErrors", numClientErrors.longValue());
|
||||
lst.add("timeouts", numTimeouts.longValue());
|
||||
TimerUtils.addMetrics(lst, requestTimes);
|
||||
lst.add("requests", requests.getCount());
|
||||
lst.add("errors", numErrors.getCount());
|
||||
lst.add("serverErrors", numServerErrors.getCount());
|
||||
lst.add("clientErrors", numClientErrors.getCount());
|
||||
lst.add("timeouts", numTimeouts.getCount());
|
||||
MetricUtils.addMetrics(lst, requestTimes);
|
||||
return lst;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -0,0 +1,164 @@
|
|||
/*
|
||||
* 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.handler.admin;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import com.codahale.metrics.Counter;
|
||||
import com.codahale.metrics.Gauge;
|
||||
import com.codahale.metrics.Histogram;
|
||||
import com.codahale.metrics.Meter;
|
||||
import com.codahale.metrics.MetricFilter;
|
||||
import com.codahale.metrics.MetricRegistry;
|
||||
import com.codahale.metrics.Timer;
|
||||
import org.apache.solr.common.SolrException;
|
||||
import org.apache.solr.common.util.NamedList;
|
||||
import org.apache.solr.common.util.StrUtils;
|
||||
import org.apache.solr.core.CoreContainer;
|
||||
import org.apache.solr.core.SolrCore;
|
||||
import org.apache.solr.handler.RequestHandlerBase;
|
||||
import org.apache.solr.metrics.SolrMetricManager;
|
||||
import org.apache.solr.request.SolrQueryRequest;
|
||||
import org.apache.solr.response.SolrQueryResponse;
|
||||
import org.apache.solr.security.AuthorizationContext;
|
||||
import org.apache.solr.security.PermissionNameProvider;
|
||||
import org.apache.solr.util.stats.MetricUtils;
|
||||
|
||||
/**
|
||||
* Request handler to return metrics
|
||||
*/
|
||||
public class MetricsHandler extends RequestHandlerBase implements PermissionNameProvider {
|
||||
final CoreContainer container;
|
||||
final SolrMetricManager metricManager;
|
||||
|
||||
public MetricsHandler() {
|
||||
this.container = null;
|
||||
this.metricManager = null;
|
||||
}
|
||||
|
||||
public MetricsHandler(CoreContainer container) {
|
||||
this.container = container;
|
||||
this.metricManager = this.container.getMetricManager();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Name getPermissionName(AuthorizationContext request) {
|
||||
return Name.METRICS_READ_PERM;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handleRequestBody(SolrQueryRequest req, SolrQueryResponse rsp) throws Exception {
|
||||
if (container == null) {
|
||||
throw new SolrException(SolrException.ErrorCode.INVALID_STATE, "Core container instance not initialized");
|
||||
}
|
||||
|
||||
List<MetricType> metricTypes = parseMetricTypes(req);
|
||||
List<MetricFilter> metricFilters = metricTypes.stream().map(MetricType::asMetricFilter).collect(Collectors.toList());
|
||||
List<Group> requestedGroups = parseGroups(req);
|
||||
|
||||
NamedList response = new NamedList();
|
||||
for (Group group : requestedGroups) {
|
||||
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));
|
||||
});
|
||||
} else {
|
||||
MetricRegistry registry = metricManager.registry(registryName);
|
||||
response.add(registryName, MetricUtils.toNamedList(registry, metricFilters));
|
||||
}
|
||||
}
|
||||
rsp.getValues().add("metrics", response);
|
||||
}
|
||||
|
||||
private List<Group> parseGroups(SolrQueryRequest req) {
|
||||
String[] groupStr = req.getParams().getParams("group");
|
||||
List<String> groups = Collections.emptyList();
|
||||
if (groupStr != null && groupStr.length > 0) {
|
||||
groups = new ArrayList<>();
|
||||
for (String g : groupStr) {
|
||||
groups.addAll(StrUtils.splitSmart(g, ','));
|
||||
}
|
||||
}
|
||||
|
||||
List<Group> requestedGroups = Arrays.asList(Group.values()); // by default we return all groups
|
||||
try {
|
||||
if (groups.size() > 0 && !groups.contains("all")) {
|
||||
requestedGroups = groups.stream().map(String::trim).map(Group::valueOf).collect(Collectors.toList());
|
||||
}
|
||||
} 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;
|
||||
}
|
||||
|
||||
private List<MetricType> parseMetricTypes(SolrQueryRequest req) {
|
||||
String[] typeStr = req.getParams().getParams("type");
|
||||
List<String> types = Collections.emptyList();
|
||||
if (typeStr != null && typeStr.length > 0) {
|
||||
types = new ArrayList<>();
|
||||
for (String type : typeStr) {
|
||||
types.addAll(StrUtils.splitSmart(type, ','));
|
||||
}
|
||||
}
|
||||
|
||||
List<MetricType> metricTypes = Collections.singletonList(MetricType.all); // include all metrics by default
|
||||
try {
|
||||
if (types.size() > 0) {
|
||||
metricTypes = types.stream().map(String::trim).map(MetricType::valueOf).collect(Collectors.toList());
|
||||
}
|
||||
} catch (IllegalArgumentException e) {
|
||||
throw new SolrException(SolrException.ErrorCode.BAD_REQUEST, "Invalid metric type in: " + types + " specified. Must be one of (all, meter, timer, histogram, counter, gauge)", e);
|
||||
}
|
||||
return metricTypes;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getDescription() {
|
||||
return "A handler to return all the metrics gathered by Solr";
|
||||
}
|
||||
|
||||
enum MetricType {
|
||||
histogram(Histogram.class),
|
||||
meter(Meter.class),
|
||||
timer(Timer.class),
|
||||
counter(Counter.class),
|
||||
gauge(Gauge.class),
|
||||
all(null);
|
||||
|
||||
private final Class klass;
|
||||
|
||||
MetricType(Class klass) {
|
||||
this.klass = klass;
|
||||
}
|
||||
|
||||
public MetricFilter asMetricFilter() {
|
||||
return (name, metric) -> klass == null || klass.isInstance(metric);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,154 @@
|
|||
/*
|
||||
* 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.Closeable;
|
||||
import java.io.IOException;
|
||||
import java.util.Collection;
|
||||
import java.lang.invoke.MethodHandles;
|
||||
|
||||
import org.apache.solr.core.NodeConfig;
|
||||
import org.apache.solr.core.PluginInfo;
|
||||
import org.apache.solr.core.SolrCore;
|
||||
import org.apache.solr.core.SolrInfoMBean;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
/**
|
||||
* Responsible for collecting metrics from {@link SolrMetricProducer}'s
|
||||
* and exposing metrics to {@link SolrMetricReporter}'s.
|
||||
*/
|
||||
public class SolrCoreMetricManager implements Closeable {
|
||||
|
||||
private static final Logger log = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass());
|
||||
|
||||
private final SolrCore core;
|
||||
private final SolrMetricManager metricManager;
|
||||
private String registryName;
|
||||
|
||||
/**
|
||||
* Constructs a metric manager.
|
||||
*
|
||||
* @param core the metric manager's core
|
||||
*/
|
||||
public SolrCoreMetricManager(SolrCore core) {
|
||||
this.core = core;
|
||||
this.metricManager = core.getCoreDescriptor().getCoreContainer().getMetricManager();
|
||||
registryName = createRegistryName(core.getCoreDescriptor().getCollectionName(), core.getName());
|
||||
}
|
||||
|
||||
/**
|
||||
* Load reporters configured globally and specific to {@link org.apache.solr.core.SolrInfoMBean.Group#core}
|
||||
* group or with a registry name specific to this core.
|
||||
*/
|
||||
public void loadReporters() {
|
||||
NodeConfig nodeConfig = core.getCoreDescriptor().getCoreContainer().getConfig();
|
||||
PluginInfo[] pluginInfos = nodeConfig.getMetricReporterPlugins();
|
||||
metricManager.loadReporters(pluginInfos, core.getResourceLoader(), SolrInfoMBean.Group.core, registryName);
|
||||
}
|
||||
|
||||
/**
|
||||
* Make sure that metrics already collected that correspond to the old core name
|
||||
* are carried over and will be used under the new core name.
|
||||
* This method also reloads reporters so that they use the new core name.
|
||||
*/
|
||||
public void afterCoreSetName() {
|
||||
String oldRegistryName = registryName;
|
||||
registryName = createRegistryName(core.getCoreDescriptor().getCollectionName(), core.getName());
|
||||
if (oldRegistryName.equals(registryName)) {
|
||||
return;
|
||||
}
|
||||
// close old reporters
|
||||
metricManager.closeReporters(oldRegistryName);
|
||||
metricManager.moveMetrics(oldRegistryName, registryName, null);
|
||||
// old registry is no longer used - we have moved the metrics
|
||||
metricManager.removeRegistry(oldRegistryName);
|
||||
// load reporters again, using the new core name
|
||||
loadReporters();
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers a mapping of name/metric's with the manager's metric registry.
|
||||
*
|
||||
* @param scope the scope of the metrics to be registered (e.g. `/admin/ping`)
|
||||
* @param producer producer of metrics to be registered
|
||||
*/
|
||||
public void registerMetricProducer(String scope, SolrMetricProducer producer) {
|
||||
if (scope == null || producer == null || producer.getCategory() == null) {
|
||||
throw new IllegalArgumentException("registerMetricProducer() called with illegal arguments: " +
|
||||
"scope = " + scope + ", producer = " + producer);
|
||||
}
|
||||
Collection<String> registered = producer.initializeMetrics(metricManager, getRegistryName(), scope);
|
||||
if (registered == null || registered.isEmpty()) {
|
||||
throw new IllegalArgumentException("registerMetricProducer() did not register any metrics " +
|
||||
"for scope = " + scope + ", producer = " + producer);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Closes reporters specific to this core.
|
||||
*/
|
||||
@Override
|
||||
public void close() throws IOException {
|
||||
metricManager.closeReporters(getRegistryName());
|
||||
}
|
||||
|
||||
public SolrCore getCore() {
|
||||
return core;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the metric registry name of the manager.
|
||||
*
|
||||
* In order to make it easier for reporting tools to aggregate metrics from
|
||||
* different cores that logically belong to a single collection we convert the
|
||||
* core name into a dot-separated hierarchy of: collection name, shard name (with optional split)
|
||||
* and replica name.
|
||||
*
|
||||
* <p>For example, when the core name looks like this but it's NOT a SolrCloud collection:
|
||||
* <code>my_collection_shard1_1_replica1</code> then this will be used as the registry name (plus
|
||||
* the required <code>solr.core</code> prefix). However,
|
||||
* if this is a SolrCloud collection <code>my_collection</code> then the registry name will become
|
||||
* <code>solr.core.my_collection.shard1_1.replica1</code>.</p>
|
||||
*
|
||||
*
|
||||
* @return the metric registry name of the manager.
|
||||
*/
|
||||
public String getRegistryName() {
|
||||
return registryName;
|
||||
}
|
||||
|
||||
/* package visibility for tests. */
|
||||
String createRegistryName(String collectionName, String coreName) {
|
||||
if (collectionName == null || (collectionName != null && !coreName.startsWith(collectionName + "_"))) {
|
||||
// single core, or unknown naming scheme
|
||||
return SolrMetricManager.getRegistryName(SolrInfoMBean.Group.core, coreName);
|
||||
}
|
||||
// split "collection1_shard1_1_replica1" into parts
|
||||
String str = coreName.substring(collectionName.length() + 1);
|
||||
String shard;
|
||||
String replica = null;
|
||||
int pos = str.lastIndexOf("_replica");
|
||||
if (pos == -1) { // ?? no _replicaN part ??
|
||||
shard = str;
|
||||
} else {
|
||||
shard = str.substring(0, pos);
|
||||
replica = str.substring(pos + 1);
|
||||
}
|
||||
return SolrMetricManager.getRegistryName(SolrInfoMBean.Group.core, collectionName, shard, replica);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,104 @@
|
|||
/*
|
||||
* 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 org.apache.solr.core.SolrInfoMBean;
|
||||
|
||||
/**
|
||||
* Wraps meta-data for a metric.
|
||||
*/
|
||||
public final class SolrMetricInfo {
|
||||
public final String name;
|
||||
public final String scope;
|
||||
public final SolrInfoMBean.Category category;
|
||||
|
||||
/**
|
||||
* Creates a new instance of {@link SolrMetricInfo}.
|
||||
*
|
||||
* @param category the category of the metric (e.g. `QUERYHANDLERS`)
|
||||
* @param scope the scope of the metric (e.g. `/admin/ping`)
|
||||
* @param name the name of the metric (e.g. `Requests`)
|
||||
*/
|
||||
public SolrMetricInfo(SolrInfoMBean.Category category, String scope, String name) {
|
||||
this.name = name;
|
||||
this.scope = scope;
|
||||
this.category = category;
|
||||
}
|
||||
|
||||
public static SolrMetricInfo of(String fullName) {
|
||||
if (fullName == null || fullName.isEmpty()) {
|
||||
return null;
|
||||
}
|
||||
String[] names = fullName.split("\\.");
|
||||
if (names.length < 3) { // not a valid info
|
||||
return null;
|
||||
}
|
||||
// check top-level name for valid category
|
||||
SolrInfoMBean.Category category;
|
||||
try {
|
||||
category = SolrInfoMBean.Category.valueOf(names[0]);
|
||||
} catch (IllegalArgumentException e) { // not a valid category
|
||||
return null;
|
||||
}
|
||||
String scope = names[1];
|
||||
String name = fullName.substring(names[0].length() + names[1].length() + 2);
|
||||
return new SolrMetricInfo(category, scope, name);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the metric name defined by this object.
|
||||
* For example, if the name is `Requests`, scope is `/admin/ping`,
|
||||
* and category is `QUERYHANDLERS`, then the metric name is
|
||||
* `QUERYHANDLERS./admin/ping.Requests`.
|
||||
*
|
||||
* @return the metric name defined by this object
|
||||
*/
|
||||
public String getMetricName() {
|
||||
return MetricRegistry.name(category.toString(), scope, name);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "SolrMetricInfo{" +
|
||||
"name='" + name + '\'' +
|
||||
", scope='" + scope + '\'' +
|
||||
", category=" + category +
|
||||
'}';
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) return true;
|
||||
if (o == null || getClass() != o.getClass()) return false;
|
||||
|
||||
SolrMetricInfo that = (SolrMetricInfo) o;
|
||||
|
||||
if (name != null ? !name.equals(that.name) : that.name != null) return false;
|
||||
if (scope != null ? !scope.equals(that.scope) : that.scope != null) return false;
|
||||
return category == that.category;
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
int result = name != null ? name.hashCode() : 0;
|
||||
result = 31 * result + (scope != null ? scope.hashCode() : 0);
|
||||
result = 31 * result + (category != null ? category.hashCode() : 0);
|
||||
return result;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,652 @@
|
|||
/*
|
||||
* 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.IOException;
|
||||
import java.lang.invoke.MethodHandles;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.ConcurrentMap;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.concurrent.locks.Lock;
|
||||
import java.util.concurrent.locks.ReentrantLock;
|
||||
|
||||
import com.codahale.metrics.Counter;
|
||||
import com.codahale.metrics.Histogram;
|
||||
import com.codahale.metrics.Meter;
|
||||
import com.codahale.metrics.Metric;
|
||||
import com.codahale.metrics.MetricFilter;
|
||||
import com.codahale.metrics.MetricRegistry;
|
||||
import com.codahale.metrics.MetricSet;
|
||||
import com.codahale.metrics.SharedMetricRegistries;
|
||||
import com.codahale.metrics.Timer;
|
||||
import org.apache.solr.core.PluginInfo;
|
||||
import org.apache.solr.core.SolrInfoMBean;
|
||||
import org.apache.solr.core.SolrResourceLoader;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
/**
|
||||
* This class maintains a repository of named {@link MetricRegistry} instances, and provides several
|
||||
* helper methods for managing various aspects of metrics reporting:
|
||||
* <ul>
|
||||
* <li>registry creation, clearing and removal,</li>
|
||||
* <li>creation of most common metric implementations,</li>
|
||||
* <li>management of {@link SolrMetricReporter}-s specific to a named registry.</li>
|
||||
* </ul>
|
||||
* {@link MetricRegistry} instances are automatically created when first referenced by name. Similarly,
|
||||
* instances of {@link Metric} implementations, such as {@link Meter}, {@link Counter}, {@link Timer} and
|
||||
* {@link Histogram} are automatically created and registered under hierarchical names, in a specified
|
||||
* registry, when {@link #meter(String, String, String...)} and other similar methods are called.
|
||||
* <p>This class enforces a common prefix ({@link #REGISTRY_NAME_PREFIX}) in all registry
|
||||
* names.</p>
|
||||
* <p>Solr uses several different registries for collecting metrics belonging to different groups, using
|
||||
* {@link org.apache.solr.core.SolrInfoMBean.Group} as the main name of the registry (plus the
|
||||
* above-mentioned prefix). Instances of {@link SolrMetricManager} are created for each {@link org.apache.solr.core.CoreContainer},
|
||||
* and most registries are local to each instance, with the exception of two global registries:
|
||||
* <code>solr.jetty</code> and <code>solr.jvm</code>, which are shared between all {@link org.apache.solr.core.CoreContainer}-s</p>
|
||||
*/
|
||||
public class SolrMetricManager {
|
||||
|
||||
private static final Logger log = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass());
|
||||
|
||||
public static final String REGISTRY_NAME_PREFIX = "solr.";
|
||||
|
||||
public static final String JETTY_REGISTRY = REGISTRY_NAME_PREFIX + SolrInfoMBean.Group.jetty.toString();
|
||||
|
||||
public static final String JVM_REGISTRY = REGISTRY_NAME_PREFIX + SolrInfoMBean.Group.jvm.toString();
|
||||
|
||||
private final ConcurrentMap<String, MetricRegistry> registries = new ConcurrentHashMap<>();
|
||||
|
||||
// these reporters are per CoreContainer
|
||||
private final Map<String, Map<String, SolrMetricReporter>> reporters = new HashMap<>();
|
||||
|
||||
// these reporters are per JVM
|
||||
private static final Map<String, Map<String, SolrMetricReporter>> sharedReporters = new HashMap<>();
|
||||
|
||||
private final Lock reportersLock = new ReentrantLock();
|
||||
|
||||
public SolrMetricManager() { }
|
||||
|
||||
/**
|
||||
* An implementation of {@link MetricFilter} that selects metrics
|
||||
* with names that start with a prefix.
|
||||
*/
|
||||
public static class PrefixFilter implements MetricFilter {
|
||||
private final String prefix;
|
||||
private final Set<String> matched = new HashSet<>();
|
||||
|
||||
/**
|
||||
* Create a filter that uses the provided prefix.
|
||||
* @param prefix prefix to use, must not be null. If empty then any
|
||||
* name will match.
|
||||
*/
|
||||
public PrefixFilter(String prefix) {
|
||||
Objects.requireNonNull(prefix);
|
||||
this.prefix = prefix;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean matches(String name, Metric metric) {
|
||||
if (prefix.isEmpty()) {
|
||||
matched.add(name);
|
||||
return true;
|
||||
}
|
||||
if (name.startsWith(prefix)) {
|
||||
matched.add(name);
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the set of names that matched this filter.
|
||||
* @return matching names
|
||||
*/
|
||||
public Set<String> getMatched() {
|
||||
return Collections.unmodifiableSet(matched);
|
||||
}
|
||||
|
||||
/**
|
||||
* Clear the set of names that matched.
|
||||
*/
|
||||
public void reset() {
|
||||
matched.clear();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a set of existing registry names.
|
||||
*/
|
||||
public Set<String> registryNames() {
|
||||
Set<String> set = new HashSet<>();
|
||||
set.addAll(registries.keySet());
|
||||
set.addAll(SharedMetricRegistries.names());
|
||||
return Collections.unmodifiableSet(set);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get (or create if not present) a named registry
|
||||
* @param registry name of the registry
|
||||
* @return existing or newly created registry
|
||||
*/
|
||||
public MetricRegistry registry(String registry) {
|
||||
registry = overridableRegistryName(registry);
|
||||
if (JETTY_REGISTRY.equals(registry) || JVM_REGISTRY.equals(registry)) {
|
||||
return SharedMetricRegistries.getOrCreate(registry);
|
||||
} else {
|
||||
final MetricRegistry existing = registries.get(registry);
|
||||
if (existing == null) {
|
||||
final MetricRegistry created = new MetricRegistry();
|
||||
final MetricRegistry raced = registries.putIfAbsent(registry, created);
|
||||
if (raced == null) {
|
||||
return created;
|
||||
} else {
|
||||
return raced;
|
||||
}
|
||||
} else {
|
||||
return existing;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove a named registry.
|
||||
* @param registry name of the registry to remove
|
||||
*/
|
||||
public void removeRegistry(String registry) {
|
||||
// close any reporters for this registry first
|
||||
closeReporters(registry);
|
||||
// make sure we use a name with prefix, with overrides
|
||||
registry = overridableRegistryName(registry);
|
||||
if (JETTY_REGISTRY.equals(registry) || JVM_REGISTRY.equals(registry)) {
|
||||
SharedMetricRegistries.remove(registry);
|
||||
} else {
|
||||
registries.remove(registry);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Move all matching metrics from one registry to another. This is useful eg. during
|
||||
* {@link org.apache.solr.core.SolrCore} rename or swap operations.
|
||||
* @param fromRegistry source registry
|
||||
* @param toRegistry target registry
|
||||
* @param filter optional {@link MetricFilter} to select what metrics to move. If null
|
||||
* then all metrics will be moved.
|
||||
*/
|
||||
public void moveMetrics(String fromRegistry, String toRegistry, MetricFilter filter) {
|
||||
MetricRegistry from = registry(fromRegistry);
|
||||
MetricRegistry to = registry(toRegistry);
|
||||
if (from == to) {
|
||||
return;
|
||||
}
|
||||
if (filter == null) {
|
||||
to.registerAll(from);
|
||||
from.removeMatching(MetricFilter.ALL);
|
||||
} else {
|
||||
for (Map.Entry<String, Metric> entry : from.getMetrics().entrySet()) {
|
||||
if (filter.matches(entry.getKey(), entry.getValue())) {
|
||||
to.register(entry.getKey(), entry.getValue());
|
||||
}
|
||||
}
|
||||
from.removeMatching(filter);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Register all metrics in the provided {@link MetricSet}, optionally skipping those that
|
||||
* already exist.
|
||||
* @param registry registry name
|
||||
* @param metrics metric set to register
|
||||
* @param force if true then already existing metrics with the same name will be replaced.
|
||||
* When false and a metric with the same name already exists an exception
|
||||
* will be thrown.
|
||||
* @param metricPath (optional) additional top-most metric name path elements
|
||||
* @throws Exception if a metric with this name already exists.
|
||||
*/
|
||||
public void registerAll(String registry, MetricSet metrics, boolean force, String... metricPath) throws Exception {
|
||||
MetricRegistry metricRegistry = registry(registry);
|
||||
synchronized (metricRegistry) {
|
||||
Map<String, Metric> existingMetrics = metricRegistry.getMetrics();
|
||||
for (Map.Entry<String, Metric> entry : metrics.getMetrics().entrySet()) {
|
||||
String fullName = mkName(entry.getKey(), metricPath);
|
||||
if (force && existingMetrics.containsKey(fullName)) {
|
||||
metricRegistry.remove(fullName);
|
||||
}
|
||||
metricRegistry.register(fullName, entry.getValue());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove all metrics from a specified registry.
|
||||
* @param registry registry name
|
||||
*/
|
||||
public void clearRegistry(String registry) {
|
||||
registry(registry).removeMatching(MetricFilter.ALL);
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove some metrics from a named registry
|
||||
* @param registry registry name
|
||||
* @param metricPath (optional) top-most metric name path elements. If empty then
|
||||
* this is equivalent to calling {@link #clearRegistry(String)},
|
||||
* otherwise non-empty elements will be joined using dotted notation
|
||||
* to form a fully-qualified prefix. Metrics with names that start
|
||||
* with the prefix will be removed.
|
||||
* @return set of metrics names that have been removed.
|
||||
*/
|
||||
public Set<String> clearMetrics(String registry, String... metricPath) {
|
||||
PrefixFilter filter;
|
||||
if (metricPath == null || metricPath.length == 0) {
|
||||
filter = new PrefixFilter("");
|
||||
} else {
|
||||
String prefix = MetricRegistry.name("", metricPath);
|
||||
filter = new PrefixFilter(prefix);
|
||||
}
|
||||
registry(registry).removeMatching(filter);
|
||||
return filter.getMatched();
|
||||
}
|
||||
|
||||
/**
|
||||
* Create or get an existing named {@link Meter}
|
||||
* @param registry registry name
|
||||
* @param metricName metric name, either final name or a fully-qualified name
|
||||
* using dotted notation
|
||||
* @param metricPath (optional) additional top-most metric name path elements
|
||||
* @return existing or a newly created {@link Meter}
|
||||
*/
|
||||
public Meter meter(String registry, String metricName, String... metricPath) {
|
||||
return registry(registry).meter(mkName(metricName, metricPath));
|
||||
}
|
||||
|
||||
/**
|
||||
* Create or get an existing named {@link Timer}
|
||||
* @param registry registry name
|
||||
* @param metricName metric name, either final name or a fully-qualified name
|
||||
* using dotted notation
|
||||
* @param metricPath (optional) additional top-most metric name path elements
|
||||
* @return existing or a newly created {@link Timer}
|
||||
*/
|
||||
public Timer timer(String registry, String metricName, String... metricPath) {
|
||||
return registry(registry).timer(mkName(metricName, metricPath));
|
||||
}
|
||||
|
||||
/**
|
||||
* Create or get an existing named {@link Counter}
|
||||
* @param registry registry name
|
||||
* @param metricName metric name, either final name or a fully-qualified name
|
||||
* using dotted notation
|
||||
* @param metricPath (optional) additional top-most metric name path elements
|
||||
* @return existing or a newly created {@link Counter}
|
||||
*/
|
||||
public Counter counter(String registry, String metricName, String... metricPath) {
|
||||
return registry(registry).counter(mkName(metricName, metricPath));
|
||||
}
|
||||
|
||||
/**
|
||||
* Create or get an existing named {@link Histogram}
|
||||
* @param registry registry name
|
||||
* @param metricName metric name, either final name or a fully-qualified name
|
||||
* using dotted notation
|
||||
* @param metricPath (optional) additional top-most metric name path elements
|
||||
* @return existing or a newly created {@link Histogram}
|
||||
*/
|
||||
public Histogram histogram(String registry, String metricName, String... metricPath) {
|
||||
return registry(registry).histogram(mkName(metricName, metricPath));
|
||||
}
|
||||
|
||||
/**
|
||||
* Register an instance of {@link Metric}.
|
||||
* @param registry registry name
|
||||
* @param metric metric instance
|
||||
* @param force if true then an already existing metric with the same name will be replaced.
|
||||
* When false and a metric with the same name already exists an exception
|
||||
* will be thrown.
|
||||
* @param metricName metric name, either final name or a fully-qualified name
|
||||
* using dotted notation
|
||||
* @param metricPath (optional) additional top-most metric name path elements
|
||||
*/
|
||||
public void register(String registry, Metric metric, boolean force, String metricName, String... metricPath) {
|
||||
MetricRegistry metricRegistry = registry(registry);
|
||||
String fullName = mkName(metricName, metricPath);
|
||||
synchronized (metricRegistry) {
|
||||
if (force && metricRegistry.getMetrics().containsKey(fullName)) {
|
||||
metricRegistry.remove(fullName);
|
||||
}
|
||||
metricRegistry.register(fullName, metric);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* This method creates a hierarchical name with arbitrary levels of hierarchy
|
||||
* @param name the final segment of the name, must not be null or empty.
|
||||
* @param path optional path segments, starting from the top level. Empty or null
|
||||
* segments will be skipped.
|
||||
* @return fully-qualified name using dotted notation, with all valid hierarchy
|
||||
* segments prepended to the name.
|
||||
*/
|
||||
public static String mkName(String name, String... path) {
|
||||
if (name == null || name.isEmpty()) {
|
||||
throw new IllegalArgumentException("name must not be empty");
|
||||
}
|
||||
if (path == null || path.length == 0) {
|
||||
return name;
|
||||
} else {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
for (String s : path) {
|
||||
if (s == null || s.isEmpty()) {
|
||||
continue;
|
||||
}
|
||||
if (sb.length() > 0) {
|
||||
sb.append('.');
|
||||
}
|
||||
sb.append(s);
|
||||
}
|
||||
if (sb.length() > 0) {
|
||||
sb.append('.');
|
||||
}
|
||||
sb.append(name);
|
||||
return sb.toString();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Allows named registries to be renamed using System properties.
|
||||
* This would be mostly be useful if you want to combine the metrics from a few registries for a single
|
||||
* reporter.
|
||||
* <p>For example, in order to collect metrics from related cores in a single registry you could specify
|
||||
* the following system properties:</p>
|
||||
* <pre>
|
||||
* ... -Dsolr.core.collection1=solr.core.allCollections -Dsolr.core.collection2=solr.core.allCollections
|
||||
* </pre>
|
||||
* <b>NOTE:</b> Once a registry is renamed in a way that its metrics are combined with another repository
|
||||
* it is no longer possible to retrieve the original metrics until this renaming is removed and the Solr
|
||||
* {@link org.apache.solr.core.SolrInfoMBean.Group} of components that reported to that name is restarted.
|
||||
* @param registry The name of the registry
|
||||
* @return A potentially overridden (via System properties) registry name
|
||||
*/
|
||||
public static String overridableRegistryName(String registry) {
|
||||
String fqRegistry = enforcePrefix(registry);
|
||||
return enforcePrefix(System.getProperty(fqRegistry,fqRegistry));
|
||||
}
|
||||
|
||||
/**
|
||||
* Enforces the leading {@link #REGISTRY_NAME_PREFIX} in a name.
|
||||
* @param name input name, possibly without the prefix
|
||||
* @return original name if it contained the prefix, or the
|
||||
* input name with the prefix prepended.
|
||||
*/
|
||||
public static String enforcePrefix(String name) {
|
||||
if (name.startsWith(REGISTRY_NAME_PREFIX)) {
|
||||
return name;
|
||||
} else {
|
||||
return new StringBuilder(REGISTRY_NAME_PREFIX).append(name).toString();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper method to construct a properly prefixed registry name based on the group.
|
||||
* @param group reporting group
|
||||
* @param names optional child elements of the registry name. If exactly one element is provided
|
||||
* and it already contains the required prefix and group name then this value will be used,
|
||||
* and the group parameter will be ignored.
|
||||
* @return fully-qualified and prefixed registry name, with overrides applied.
|
||||
*/
|
||||
public static String getRegistryName(SolrInfoMBean.Group group, String... names) {
|
||||
String fullName;
|
||||
String prefix = REGISTRY_NAME_PREFIX + group.toString() + ".";
|
||||
// check for existing prefix and group
|
||||
if (names != null && names.length > 0 && names[0] != null && names[0].startsWith(prefix)) {
|
||||
// assume the first segment already was expanded
|
||||
if (names.length > 1) {
|
||||
String[] newNames = new String[names.length - 1];
|
||||
System.arraycopy(names, 1, newNames, 0, newNames.length);
|
||||
fullName = MetricRegistry.name(names[0], newNames);
|
||||
} else {
|
||||
fullName = MetricRegistry.name(names[0]);
|
||||
}
|
||||
} else {
|
||||
fullName = MetricRegistry.name(group.toString(), names);
|
||||
}
|
||||
return overridableRegistryName(fullName);
|
||||
}
|
||||
|
||||
// reporter management
|
||||
|
||||
/**
|
||||
* Create and register {@link SolrMetricReporter}-s specific to a {@link org.apache.solr.core.SolrInfoMBean.Group}.
|
||||
* Note: reporters that specify neither "group" nor "registry" attributes are treated as universal -
|
||||
* they will always be loaded for any group. These two attributes may also contain multiple comma- or
|
||||
* whitespace-separated values, in which case the reporter will be loaded for any matching value from
|
||||
* the list. If both attributes are present then only "group" attribute will be processed.
|
||||
* @param pluginInfos plugin configurations
|
||||
* @param loader resource loader
|
||||
* @param group selected group, not null
|
||||
* @param registryNames optional child registry name elements
|
||||
*/
|
||||
public void loadReporters(PluginInfo[] pluginInfos, SolrResourceLoader loader, SolrInfoMBean.Group group, String... registryNames) {
|
||||
if (pluginInfos == null || pluginInfos.length == 0) {
|
||||
return;
|
||||
}
|
||||
String registryName = getRegistryName(group, registryNames);
|
||||
for (PluginInfo info : pluginInfos) {
|
||||
String target = info.attributes.get("group");
|
||||
if (target == null) { // no "group"
|
||||
target = info.attributes.get("registry");
|
||||
if (target != null) {
|
||||
String[] targets = target.split("[\\s,]+");
|
||||
boolean found = false;
|
||||
for (String t : targets) {
|
||||
t = overridableRegistryName(t);
|
||||
if (registryName.equals(t)) {
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!found) {
|
||||
continue;
|
||||
}
|
||||
} else {
|
||||
// neither group nor registry specified.
|
||||
// always register this plugin for all groups and registries
|
||||
}
|
||||
} else { // check groups
|
||||
String[] targets = target.split("[\\s,]+");
|
||||
boolean found = false;
|
||||
for (String t : targets) {
|
||||
if (group.toString().equals(t)) {
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!found) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
try {
|
||||
loadReporter(registryName, loader, info);
|
||||
} catch (Exception e) {
|
||||
log.warn("Error loading metrics reporter, plugin info: " + info, e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Create and register an instance of {@link SolrMetricReporter}.
|
||||
* @param registry reporter is associated with this registry
|
||||
* @param loader loader to use when creating an instance of the reporter
|
||||
* @param pluginInfo plugin configuration. Plugin "name" and "class" attributes are required.
|
||||
* @throws Exception if any argument is missing or invalid
|
||||
*/
|
||||
public void loadReporter(String registry, SolrResourceLoader loader, PluginInfo pluginInfo) throws Exception {
|
||||
if (registry == null || pluginInfo == null || pluginInfo.name == null || pluginInfo.className == null) {
|
||||
throw new IllegalArgumentException("loadReporter called with missing arguments: " +
|
||||
"registry=" + registry + ", loader=" + loader + ", pluginInfo=" + pluginInfo);
|
||||
}
|
||||
// make sure we use a name with prefix, with overrides
|
||||
registry = overridableRegistryName(registry);
|
||||
SolrMetricReporter reporter = loader.newInstance(
|
||||
pluginInfo.className,
|
||||
SolrMetricReporter.class,
|
||||
new String[0],
|
||||
new Class[] { SolrMetricManager.class, String.class },
|
||||
new Object[] { this, registry }
|
||||
);
|
||||
try {
|
||||
reporter.init(pluginInfo);
|
||||
} catch (IllegalStateException e) {
|
||||
throw new IllegalArgumentException("reporter init failed: " + pluginInfo, e);
|
||||
}
|
||||
try {
|
||||
if (!reportersLock.tryLock(10, TimeUnit.SECONDS)) {
|
||||
throw new Exception("Could not obtain lock to modify reporters registry: " + registry);
|
||||
}
|
||||
} catch (InterruptedException e) {
|
||||
throw new Exception("Interrupted while trying to obtain lock to modify reporters registry: " + registry);
|
||||
}
|
||||
try {
|
||||
Map<String, SolrMetricReporter> perRegistry = reporters.get(registry);
|
||||
if (perRegistry == null) {
|
||||
perRegistry = new HashMap<>();
|
||||
reporters.put(registry, perRegistry);
|
||||
}
|
||||
SolrMetricReporter oldReporter = perRegistry.get(pluginInfo.name);
|
||||
if (oldReporter != null) { // close it
|
||||
log.info("Replacing existing reporter '" + pluginInfo.name + "' in registry '" + registry + "': " + oldReporter.toString());
|
||||
oldReporter.close();
|
||||
}
|
||||
perRegistry.put(pluginInfo.name, reporter);
|
||||
|
||||
} finally {
|
||||
reportersLock.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Close and unregister a named {@link SolrMetricReporter} for a registry.
|
||||
* @param registry registry name
|
||||
* @param name reporter name
|
||||
* @return true if a named reporter existed and was closed.
|
||||
*/
|
||||
public boolean closeReporter(String registry, String name) {
|
||||
// make sure we use a name with prefix, with overrides
|
||||
registry = overridableRegistryName(registry);
|
||||
try {
|
||||
if (!reportersLock.tryLock(10, TimeUnit.SECONDS)) {
|
||||
log.warn("Could not obtain lock to modify reporters registry: " + registry);
|
||||
return false;
|
||||
}
|
||||
} catch (InterruptedException e) {
|
||||
log.warn("Interrupted while trying to obtain lock to modify reporters registry: " + registry);
|
||||
return false;
|
||||
}
|
||||
try {
|
||||
Map<String, SolrMetricReporter> perRegistry = reporters.get(registry);
|
||||
if (perRegistry == null) {
|
||||
return false;
|
||||
}
|
||||
SolrMetricReporter reporter = perRegistry.remove(name);
|
||||
if (reporter == null) {
|
||||
return false;
|
||||
}
|
||||
try {
|
||||
reporter.close();
|
||||
} catch (Exception e) {
|
||||
log.warn("Error closing metric reporter, registry=" + registry + ", name=" + name, e);
|
||||
}
|
||||
return true;
|
||||
} finally {
|
||||
reportersLock.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Close and unregister all {@link SolrMetricReporter}-s for a registry.
|
||||
* @param registry registry name
|
||||
* @return names of closed reporters
|
||||
*/
|
||||
public Set<String> closeReporters(String registry) {
|
||||
// make sure we use a name with prefix, with overrides
|
||||
registry = overridableRegistryName(registry);
|
||||
try {
|
||||
if (!reportersLock.tryLock(10, TimeUnit.SECONDS)) {
|
||||
log.warn("Could not obtain lock to modify reporters registry: " + registry);
|
||||
return Collections.emptySet();
|
||||
}
|
||||
} catch (InterruptedException e) {
|
||||
log.warn("Interrupted while trying to obtain lock to modify reporters registry: " + registry);
|
||||
return Collections.emptySet();
|
||||
}
|
||||
log.info("Closing metric reporters for: " + registry);
|
||||
try {
|
||||
Map<String, SolrMetricReporter> perRegistry = reporters.remove(registry);
|
||||
if (perRegistry != null) {
|
||||
for (SolrMetricReporter reporter : perRegistry.values()) {
|
||||
try {
|
||||
reporter.close();
|
||||
} catch (IOException ioe) {
|
||||
log.warn("Exception closing reporter " + reporter, ioe);
|
||||
}
|
||||
}
|
||||
return perRegistry.keySet();
|
||||
} else {
|
||||
return Collections.emptySet();
|
||||
}
|
||||
} finally {
|
||||
reportersLock.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a map of reporters for a registry. Keys are reporter names, values are reporter instances.
|
||||
* @param registry registry name
|
||||
* @return map of reporters and their names, may be empty but never null
|
||||
*/
|
||||
public Map<String, SolrMetricReporter> getReporters(String registry) {
|
||||
// make sure we use a name with prefix, with overrides
|
||||
registry = overridableRegistryName(registry);
|
||||
try {
|
||||
if (!reportersLock.tryLock(10, TimeUnit.SECONDS)) {
|
||||
log.warn("Could not obtain lock to modify reporters registry: " + registry);
|
||||
return Collections.emptyMap();
|
||||
}
|
||||
} catch (InterruptedException e) {
|
||||
log.warn("Interrupted while trying to obtain lock to modify reporters registry: " + registry);
|
||||
return Collections.emptyMap();
|
||||
}
|
||||
try {
|
||||
Map<String, SolrMetricReporter> perRegistry = reporters.get(registry);
|
||||
if (perRegistry == null) {
|
||||
return Collections.emptyMap();
|
||||
} else {
|
||||
// defensive copy - the original map may change after we release the lock
|
||||
return Collections.unmodifiableMap(new HashMap<>(perRegistry));
|
||||
}
|
||||
} finally {
|
||||
reportersLock.unlock();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,38 @@
|
|||
/*
|
||||
* 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.util.Collection;
|
||||
|
||||
import org.apache.solr.core.SolrInfoMBean;
|
||||
|
||||
/**
|
||||
* Extension of {@link SolrInfoMBean} for use by objects that
|
||||
* expose metrics through {@link SolrCoreMetricManager}.
|
||||
*/
|
||||
public interface SolrMetricProducer extends SolrInfoMBean {
|
||||
|
||||
/**
|
||||
* Initializes metrics specific to this producer
|
||||
* @param manager an instance of {@link SolrMetricManager}
|
||||
* @param registry registry name where metrics are registered
|
||||
* @param scope scope of the metrics (eg. handler name) to separate metrics of
|
||||
* instances of the same component executing in different contexts
|
||||
* @return registered (or existing) unqualified names of metrics specific to this producer.
|
||||
*/
|
||||
Collection<String> initializeMetrics(SolrMetricManager manager, String registry, String scope);
|
||||
}
|
|
@ -0,0 +1,83 @@
|
|||
/*
|
||||
* 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.Closeable;
|
||||
|
||||
import org.apache.solr.core.PluginInfo;
|
||||
import org.apache.solr.util.SolrPluginUtils;
|
||||
import org.apache.solr.util.plugin.PluginInfoInitialized;
|
||||
|
||||
/**
|
||||
* Interface for 'pluggable' metric reporters.
|
||||
*/
|
||||
public abstract class SolrMetricReporter implements Closeable, PluginInfoInitialized {
|
||||
|
||||
protected final String registryName;
|
||||
protected final SolrMetricManager metricManager;
|
||||
protected PluginInfo pluginInfo;
|
||||
|
||||
/**
|
||||
* Create a reporter for metrics managed in a named registry.
|
||||
* @param registryName registry to use, one of registries managed by
|
||||
* {@link SolrMetricManager}
|
||||
*/
|
||||
protected SolrMetricReporter(SolrMetricManager metricManager, String registryName) {
|
||||
this.registryName = registryName;
|
||||
this.metricManager = metricManager;
|
||||
}
|
||||
|
||||
/**
|
||||
* Initializes a {@link SolrMetricReporter} with the plugin's configuration.
|
||||
*
|
||||
* @param pluginInfo the plugin's configuration
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public void init(PluginInfo pluginInfo) {
|
||||
if (pluginInfo != null) {
|
||||
this.pluginInfo = pluginInfo.copy();
|
||||
if (this.pluginInfo.initArgs != null) {
|
||||
SolrPluginUtils.invokeSetters(this, this.pluginInfo.initArgs);
|
||||
}
|
||||
}
|
||||
validate();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the effective {@link PluginInfo} instance that was used for
|
||||
* initialization of this plugin.
|
||||
* @return plugin info, or null if not yet initialized.
|
||||
*/
|
||||
public PluginInfo getPluginInfo() {
|
||||
return pluginInfo;
|
||||
}
|
||||
|
||||
/**
|
||||
* Validates that the reporter has been correctly configured.
|
||||
*
|
||||
* @throws IllegalStateException if the reporter is not properly configured
|
||||
*/
|
||||
protected abstract void validate() throws IllegalStateException;
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return getClass().getName() + "{" +
|
||||
"registryName='" + registryName + '\'' +
|
||||
", pluginInfo=" + pluginInfo +
|
||||
'}';
|
||||
}
|
||||
}
|
|
@ -0,0 +1,23 @@
|
|||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
/**
|
||||
* The {@link org.apache.solr.metrics.SolrCoreMetricManager} is responsible for
|
||||
* collecting metrics from {@link org.apache.solr.metrics.SolrMetricProducer}'s
|
||||
* and exposing metrics to {@link org.apache.solr.metrics.SolrMetricReporter}'s.
|
||||
*/
|
||||
package org.apache.solr.metrics;
|
|
@ -0,0 +1,284 @@
|
|||
/*
|
||||
* 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.reporters;
|
||||
|
||||
import javax.management.MBeanServer;
|
||||
import javax.management.MalformedObjectNameException;
|
||||
import javax.management.ObjectName;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.lang.invoke.MethodHandles;
|
||||
import java.lang.management.ManagementFactory;
|
||||
import java.util.Locale;
|
||||
|
||||
import com.codahale.metrics.JmxReporter;
|
||||
import com.codahale.metrics.ObjectNameFactory;
|
||||
import org.apache.solr.core.PluginInfo;
|
||||
import org.apache.solr.metrics.SolrMetricInfo;
|
||||
import org.apache.solr.metrics.SolrMetricManager;
|
||||
import org.apache.solr.metrics.SolrMetricReporter;
|
||||
import org.apache.solr.util.JmxUtil;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
/**
|
||||
* A {@link SolrMetricReporter} that finds (or creates) a MBeanServer from
|
||||
* the given configuration and registers metrics to it with JMX.
|
||||
*/
|
||||
public class SolrJmxReporter extends SolrMetricReporter {
|
||||
|
||||
private static final Logger log = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass());
|
||||
|
||||
private String domain;
|
||||
private String agentId;
|
||||
private String serviceUrl;
|
||||
|
||||
private JmxReporter reporter;
|
||||
private MBeanServer mBeanServer;
|
||||
|
||||
/**
|
||||
* Creates a new instance of {@link SolrJmxReporter}.
|
||||
*
|
||||
* @param registryName name of the registry to report
|
||||
*/
|
||||
public SolrJmxReporter(SolrMetricManager metricManager, String registryName) {
|
||||
super(metricManager, registryName);
|
||||
setDomain(registryName);
|
||||
}
|
||||
|
||||
/**
|
||||
* Initializes the reporter by finding (or creating) a MBeanServer
|
||||
* and registering the metricManager's metric registry.
|
||||
*
|
||||
* @param pluginInfo the configuration for the reporter
|
||||
*/
|
||||
@Override
|
||||
public synchronized void init(PluginInfo pluginInfo) {
|
||||
super.init(pluginInfo);
|
||||
|
||||
if (serviceUrl != null && agentId != null) {
|
||||
ManagementFactory.getPlatformMBeanServer(); // Ensure at least one MBeanServer is available.
|
||||
mBeanServer = JmxUtil.findFirstMBeanServer();
|
||||
log.warn("No more than one of serviceUrl(%s) and agentId(%s) should be configured, using first MBeanServer instead of configuration.",
|
||||
serviceUrl, agentId, mBeanServer);
|
||||
}
|
||||
else if (serviceUrl != null) {
|
||||
try {
|
||||
mBeanServer = JmxUtil.findMBeanServerForServiceUrl(serviceUrl);
|
||||
} catch (IOException e) {
|
||||
log.warn("findMBeanServerForServiceUrl(%s) exception: %s", serviceUrl, e);
|
||||
mBeanServer = null;
|
||||
}
|
||||
}
|
||||
else if (agentId != null) {
|
||||
mBeanServer = JmxUtil.findMBeanServerForAgentId(agentId);
|
||||
} else {
|
||||
ManagementFactory.getPlatformMBeanServer(); // Ensure at least one MBeanServer is available.
|
||||
mBeanServer = JmxUtil.findFirstMBeanServer();
|
||||
log.warn("No serviceUrl or agentId was configured, using first MBeanServer.", mBeanServer);
|
||||
}
|
||||
|
||||
if (mBeanServer == null) {
|
||||
log.warn("No JMX server found. Not exposing Solr metrics.");
|
||||
return;
|
||||
}
|
||||
|
||||
JmxObjectNameFactory jmxObjectNameFactory = new JmxObjectNameFactory(pluginInfo.name, domain);
|
||||
|
||||
reporter = JmxReporter.forRegistry(metricManager.registry(registryName))
|
||||
.registerWith(mBeanServer)
|
||||
.inDomain(domain)
|
||||
.createsObjectNamesWith(jmxObjectNameFactory)
|
||||
.build();
|
||||
reporter.start();
|
||||
|
||||
log.info("JMX monitoring enabled at server: " + mBeanServer);
|
||||
}
|
||||
|
||||
/**
|
||||
* Stops the reporter from publishing metrics.
|
||||
*/
|
||||
@Override
|
||||
public synchronized void close() {
|
||||
if (reporter != null) {
|
||||
reporter.close();
|
||||
reporter = null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Validates that the reporter has been correctly configured.
|
||||
* Note that all configurable arguments are currently optional.
|
||||
*
|
||||
* @throws IllegalStateException if the reporter is not properly configured
|
||||
*/
|
||||
@Override
|
||||
protected void validate() throws IllegalStateException {
|
||||
// Nothing to validate
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the domain with which MBeans are published. If none is set,
|
||||
* the domain defaults to the name of the core.
|
||||
*
|
||||
* @param domain the domain
|
||||
*/
|
||||
public void setDomain(String domain) {
|
||||
if (domain != null) {
|
||||
this.domain = domain;
|
||||
} else {
|
||||
this.domain = registryName;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the service url for a JMX server.
|
||||
* Note that this configuration is optional.
|
||||
*
|
||||
* @param serviceUrl the service url
|
||||
*/
|
||||
public void setServiceUrl(String serviceUrl) {
|
||||
this.serviceUrl = serviceUrl;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the agent id for a JMX server.
|
||||
* Note that this configuration is optional.
|
||||
*
|
||||
* @param agentId the agent id
|
||||
*/
|
||||
public void setAgentId(String agentId) {
|
||||
this.agentId = agentId;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the reporter's MBeanServer.
|
||||
*
|
||||
* @return the reporter's MBeanServer
|
||||
*/
|
||||
public MBeanServer getMBeanServer() {
|
||||
return mBeanServer;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return String.format(Locale.ENGLISH, "[%s@%s: domain = %s, service url = %s, agent id = %s]",
|
||||
getClass().getName(), Integer.toHexString(hashCode()), domain, serviceUrl, agentId);
|
||||
}
|
||||
|
||||
/**
|
||||
* Factory to create MBean names for a given metric.
|
||||
*/
|
||||
private static class JmxObjectNameFactory implements ObjectNameFactory {
|
||||
|
||||
private final String domain;
|
||||
private final String[] subdomains;
|
||||
private final String reporterName;
|
||||
|
||||
JmxObjectNameFactory(String reporterName, String domain) {
|
||||
this.reporterName = reporterName;
|
||||
this.domain = domain;
|
||||
this.subdomains = domain.split("\\.");
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a hierarchical name of a metric.
|
||||
*
|
||||
* @param type metric class, eg. "counters"
|
||||
* @param currentDomain JMX domain
|
||||
* @param name metric name
|
||||
*/
|
||||
@Override
|
||||
public ObjectName createName(String type, String currentDomain, String name) {
|
||||
SolrMetricInfo metricInfo = SolrMetricInfo.of(name);
|
||||
|
||||
// It turns out that ObjectName(String) mostly preserves key ordering
|
||||
// as specified in the constructor (except for the 'type' key that ends
|
||||
// up at top level) - unlike ObjectName(String, Map) constructor
|
||||
// that seems to have a mind of its own...
|
||||
StringBuilder sb = new StringBuilder();
|
||||
if (domain.equals(currentDomain)) {
|
||||
if (subdomains != null && subdomains.length > 1) {
|
||||
// use only first segment as domain
|
||||
sb.append(subdomains[0]);
|
||||
sb.append(':');
|
||||
// use remaining segments as properties
|
||||
for (int i = 1; i < subdomains.length; i++) {
|
||||
if (i > 1) {
|
||||
sb.append(',');
|
||||
}
|
||||
sb.append("dom");
|
||||
sb.append(String.valueOf(i));
|
||||
sb.append('=');
|
||||
sb.append(subdomains[i]);
|
||||
}
|
||||
sb.append(','); // separate from other properties
|
||||
} else {
|
||||
sb.append(currentDomain);
|
||||
sb.append(':');
|
||||
}
|
||||
} else {
|
||||
sb.append(currentDomain);
|
||||
sb.append(':');
|
||||
}
|
||||
sb.append("reporter=");
|
||||
sb.append(reporterName);
|
||||
sb.append(',');
|
||||
if (metricInfo != null) {
|
||||
sb.append("category=");
|
||||
sb.append(metricInfo.category.toString());
|
||||
sb.append(",scope=");
|
||||
sb.append(metricInfo.scope);
|
||||
// we could also split by type, but don't call it 'type' :)
|
||||
// sb.append(",class=");
|
||||
//sb.append(type);
|
||||
sb.append(",name=");
|
||||
sb.append(metricInfo.name);
|
||||
} else {
|
||||
// make dotted names into hierarchies
|
||||
String[] path = name.split("\\.");
|
||||
for (int i = 0; i < path.length - 1; i++) {
|
||||
if (i > 0) {
|
||||
sb.append(',');
|
||||
}
|
||||
sb.append("name"); sb.append(String.valueOf(i));
|
||||
sb.append('=');
|
||||
sb.append(path[i]);
|
||||
}
|
||||
if (path.length > 1) {
|
||||
sb.append(',');
|
||||
}
|
||||
// split by type
|
||||
// sb.append("class=");
|
||||
// sb.append(type);
|
||||
sb.append("name=");
|
||||
sb.append(path[path.length - 1]);
|
||||
}
|
||||
|
||||
ObjectName objectName;
|
||||
|
||||
try {
|
||||
objectName = new ObjectName(sb.toString());
|
||||
} catch (MalformedObjectNameException e) {
|
||||
throw new RuntimeException(sb.toString(), e);
|
||||
}
|
||||
|
||||
return objectName;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,22 @@
|
|||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
/**
|
||||
* This package houses 'pluggable' metric reporters that
|
||||
* inherit from the {@link org.apache.solr.metrics.SolrMetricReporter} class.
|
||||
*/
|
||||
package org.apache.solr.metrics.reporters;
|
|
@ -46,6 +46,7 @@ public interface PermissionNameProvider {
|
|||
SCHEMA_EDIT_PERM("schema-edit", "*"),
|
||||
SECURITY_EDIT_PERM("security-edit", null),
|
||||
SECURITY_READ_PERM("security-read", null),
|
||||
METRICS_READ_PERM("metrics-read", null),
|
||||
ALL("all", unmodifiableSet(new HashSet<>(asList("*", null))))
|
||||
;
|
||||
final String name;
|
||||
|
|
|
@ -16,6 +16,7 @@
|
|||
*/
|
||||
package org.apache.solr.servlet;
|
||||
|
||||
import javax.management.MBeanServer;
|
||||
import javax.servlet.FilterChain;
|
||||
import javax.servlet.FilterConfig;
|
||||
import javax.servlet.ServletException;
|
||||
|
@ -33,6 +34,7 @@ import java.io.IOException;
|
|||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
import java.lang.invoke.MethodHandles;
|
||||
import java.lang.management.ManagementFactory;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.Paths;
|
||||
import java.time.Instant;
|
||||
|
@ -45,6 +47,12 @@ import java.util.concurrent.atomic.AtomicReference;
|
|||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
import com.codahale.metrics.jvm.BufferPoolMetricSet;
|
||||
import com.codahale.metrics.jvm.ClassLoadingGaugeSet;
|
||||
import com.codahale.metrics.jvm.FileDescriptorRatioGauge;
|
||||
import com.codahale.metrics.jvm.GarbageCollectorMetricSet;
|
||||
import com.codahale.metrics.jvm.MemoryUsageGaugeSet;
|
||||
import com.codahale.metrics.jvm.ThreadStatesGaugeSet;
|
||||
import org.apache.commons.io.FileCleaningTracker;
|
||||
import org.apache.commons.io.input.CloseShieldInputStream;
|
||||
import org.apache.commons.io.output.CloseShieldOutputStream;
|
||||
|
@ -58,8 +66,10 @@ import org.apache.solr.common.util.ExecutorUtil;
|
|||
import org.apache.solr.core.CoreContainer;
|
||||
import org.apache.solr.core.NodeConfig;
|
||||
import org.apache.solr.core.SolrCore;
|
||||
import org.apache.solr.core.SolrInfoMBean;
|
||||
import org.apache.solr.core.SolrResourceLoader;
|
||||
import org.apache.solr.core.SolrXmlConfig;
|
||||
import org.apache.solr.metrics.SolrMetricManager;
|
||||
import org.apache.solr.request.SolrRequestInfo;
|
||||
import org.apache.solr.security.AuthenticationPlugin;
|
||||
import org.apache.solr.security.PKIAuthenticationPlugin;
|
||||
|
@ -157,6 +167,7 @@ public class SolrDispatchFilter extends BaseSolrFilter {
|
|||
this.cores = createCoreContainer(solrHome == null ? SolrResourceLoader.locateSolrHome() : Paths.get(solrHome),
|
||||
extraProperties);
|
||||
this.httpClient = cores.getUpdateShardHandler().getHttpClient();
|
||||
setupJvmMetrics();
|
||||
log.debug("user.dir=" + System.getProperty("user.dir"));
|
||||
}
|
||||
catch( Throwable t ) {
|
||||
|
@ -171,6 +182,22 @@ public class SolrDispatchFilter extends BaseSolrFilter {
|
|||
log.trace("SolrDispatchFilter.init() done");
|
||||
}
|
||||
|
||||
private void setupJvmMetrics() {
|
||||
MBeanServer platformMBeanServer = ManagementFactory.getPlatformMBeanServer();
|
||||
SolrMetricManager metricManager = cores.getMetricManager();
|
||||
try {
|
||||
String registry = SolrMetricManager.getRegistryName(SolrInfoMBean.Group.jvm);
|
||||
metricManager.registerAll(registry, new BufferPoolMetricSet(platformMBeanServer), true, "bufferPools");
|
||||
metricManager.registerAll(registry, new ClassLoadingGaugeSet(), true, "classLoading");
|
||||
metricManager.register(registry, new FileDescriptorRatioGauge(), true, "fileDescriptorRatio");
|
||||
metricManager.registerAll(registry, new GarbageCollectorMetricSet(), true, "gc");
|
||||
metricManager.registerAll(registry, new MemoryUsageGaugeSet(), true, "memory");
|
||||
metricManager.registerAll(registry, new ThreadStatesGaugeSet(), true, "threads"); // todo should we use CachedThreadStatesGaugeSet instead?
|
||||
} catch (Exception e) {
|
||||
log.warn("Error registering JVM metrics", e);
|
||||
}
|
||||
}
|
||||
|
||||
private void logWelcomeBanner() {
|
||||
log.info(" ___ _ Welcome to Apache Solr™ version {}", solrVersion());
|
||||
log.info("/ __| ___| |_ _ Starting in {} mode on port {}", isCloudMode() ? "cloud" : "standalone", getSolrPort());
|
||||
|
|
|
@ -0,0 +1,78 @@
|
|||
/*
|
||||
* 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.util;
|
||||
|
||||
import javax.management.MBeanServer;
|
||||
import javax.management.MBeanServerFactory;
|
||||
import javax.management.remote.JMXConnectorServer;
|
||||
import javax.management.remote.JMXConnectorServerFactory;
|
||||
import javax.management.remote.JMXServiceURL;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Utility methods to find a MBeanServer.
|
||||
*
|
||||
* This was factored out from {@link org.apache.solr.core.JmxMonitoredMap}
|
||||
* and can eventually replace the logic used there.
|
||||
*/
|
||||
public final class JmxUtil {
|
||||
|
||||
/**
|
||||
* Retrieve the first MBeanServer found.
|
||||
*
|
||||
* @return the first MBeanServer found
|
||||
*/
|
||||
public static MBeanServer findFirstMBeanServer() {
|
||||
return findMBeanServerForAgentId(null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Find a MBeanServer given a service url.
|
||||
*
|
||||
* @param serviceUrl the service url
|
||||
* @return a MBeanServer
|
||||
*/
|
||||
public static MBeanServer findMBeanServerForServiceUrl(String serviceUrl) throws IOException {
|
||||
if (serviceUrl == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
MBeanServer server = MBeanServerFactory.newMBeanServer();
|
||||
JMXConnectorServer connector = JMXConnectorServerFactory
|
||||
.newJMXConnectorServer(new JMXServiceURL(serviceUrl), null, server);
|
||||
connector.start();
|
||||
|
||||
return server;
|
||||
}
|
||||
|
||||
/**
|
||||
* Find a MBeanServer given an agent id.
|
||||
*
|
||||
* @param agentId the agent id
|
||||
* @return a MBeanServer
|
||||
*/
|
||||
public static MBeanServer findMBeanServerForAgentId(String agentId) {
|
||||
List<MBeanServer> servers = MBeanServerFactory.findMBeanServer(agentId);
|
||||
if (servers == null || servers.isEmpty()) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return servers.get(0);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,144 @@
|
|||
/*
|
||||
* 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.util.stats;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.SortedSet;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import com.codahale.metrics.Counter;
|
||||
import com.codahale.metrics.Gauge;
|
||||
import com.codahale.metrics.Histogram;
|
||||
import com.codahale.metrics.Meter;
|
||||
import com.codahale.metrics.Metric;
|
||||
import com.codahale.metrics.MetricFilter;
|
||||
import com.codahale.metrics.MetricRegistry;
|
||||
import com.codahale.metrics.Snapshot;
|
||||
import com.codahale.metrics.Timer;
|
||||
import org.apache.solr.common.util.NamedList;
|
||||
|
||||
/**
|
||||
* Metrics specific utility functions.
|
||||
*/
|
||||
public class MetricUtils {
|
||||
|
||||
/**
|
||||
* Adds metrics from a Timer to a NamedList, using well-known names.
|
||||
* @param lst The NamedList to add the metrics data to
|
||||
* @param timer The Timer to extract the metrics from
|
||||
*/
|
||||
public static void addMetrics(NamedList<Object> lst, Timer timer) {
|
||||
Snapshot snapshot = timer.getSnapshot();
|
||||
lst.add("avgRequestsPerSecond", timer.getMeanRate());
|
||||
lst.add("5minRateRequestsPerSecond", timer.getFiveMinuteRate());
|
||||
lst.add("15minRateRequestsPerSecond", timer.getFifteenMinuteRate());
|
||||
lst.add("avgTimePerRequest", nsToMs(snapshot.getMean()));
|
||||
lst.add("medianRequestTime", nsToMs(snapshot.getMedian()));
|
||||
lst.add("75thPcRequestTime", nsToMs(snapshot.get75thPercentile()));
|
||||
lst.add("95thPcRequestTime", nsToMs(snapshot.get95thPercentile()));
|
||||
lst.add("99thPcRequestTime", nsToMs(snapshot.get99thPercentile()));
|
||||
lst.add("999thPcRequestTime", nsToMs(snapshot.get999thPercentile()));
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts a double representing nanoseconds to a double representing milliseconds.
|
||||
*
|
||||
* @param ns the amount of time in nanoseconds
|
||||
* @return the amount of time in milliseconds
|
||||
*/
|
||||
static double nsToMs(double ns) {
|
||||
return ns / TimeUnit.MILLISECONDS.toNanos(1);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a NamedList respresentation of the given metric registry. Only those metrics
|
||||
* are converted to NamedList which match at least one of the given MetricFilter instances.
|
||||
*
|
||||
* @param registry the {@link MetricRegistry} to be converted to NamedList
|
||||
* @param metricFilters a list of {@link MetricFilter} instances
|
||||
* @return a {@link NamedList}
|
||||
*/
|
||||
public static NamedList toNamedList(MetricRegistry registry, List<MetricFilter> metricFilters) {
|
||||
NamedList response = new NamedList();
|
||||
Map<String, Metric> metrics = registry.getMetrics();
|
||||
SortedSet<String> names = registry.getNames();
|
||||
names.stream().filter(s -> metricFilters.stream().anyMatch(metricFilter -> metricFilter.matches(s, metrics.get(s)))).forEach(n -> {
|
||||
Metric metric = metrics.get(n);
|
||||
if (metric instanceof Counter) {
|
||||
Counter counter = (Counter) metric;
|
||||
response.add(n, counterToNamedList(counter));
|
||||
} else if (metric instanceof Gauge) {
|
||||
Gauge gauge = (Gauge) metric;
|
||||
response.add(n, gaugeToNamedList(gauge));
|
||||
} else if (metric instanceof Meter) {
|
||||
Meter meter = (Meter) metric;
|
||||
response.add(n, meterToNamedList(meter));
|
||||
} else if (metric instanceof Timer) {
|
||||
Timer timer = (Timer) metric;
|
||||
response.add(n, timerToNamedList(timer));
|
||||
} else if (metric instanceof Histogram) {
|
||||
Histogram histogram = (Histogram) metric;
|
||||
response.add(n, histogramToNamedList(histogram));
|
||||
}
|
||||
});
|
||||
return response;
|
||||
}
|
||||
|
||||
static NamedList histogramToNamedList(Histogram histogram) {
|
||||
NamedList response = new NamedList();
|
||||
Snapshot snapshot = histogram.getSnapshot();
|
||||
response.add("requests", histogram.getCount());
|
||||
response.add("minTime", nsToMs(snapshot.getMin()));
|
||||
response.add("maxTime", nsToMs(snapshot.getMax()));
|
||||
response.add("avgTimePerRequest", nsToMs(snapshot.getMean()));
|
||||
response.add("medianRequestTime", nsToMs(snapshot.getMedian()));
|
||||
response.add("75thPcRequestTime", nsToMs(snapshot.get75thPercentile()));
|
||||
response.add("95thPcRequestTime", nsToMs(snapshot.get95thPercentile()));
|
||||
response.add("99thPcRequestTime", nsToMs(snapshot.get99thPercentile()));
|
||||
response.add("999thPcRequestTime", nsToMs(snapshot.get999thPercentile()));
|
||||
return response;
|
||||
}
|
||||
|
||||
static NamedList timerToNamedList(Timer timer) {
|
||||
NamedList response = new NamedList();
|
||||
addMetrics(response, timer);
|
||||
return response;
|
||||
}
|
||||
|
||||
static NamedList meterToNamedList(Meter meter) {
|
||||
NamedList response = new NamedList();
|
||||
response.add("requests", meter.getCount());
|
||||
response.add("avgRequestsPerSecond", meter.getMeanRate());
|
||||
response.add("1minRateRequestsPerSecond", meter.getOneMinuteRate());
|
||||
response.add("5minRateRequestsPerSecond", meter.getFiveMinuteRate());
|
||||
response.add("15minRateRequestsPerSecond", meter.getFifteenMinuteRate());
|
||||
return response;
|
||||
}
|
||||
|
||||
static NamedList gaugeToNamedList(Gauge gauge) {
|
||||
NamedList response = new NamedList();
|
||||
response.add("value", gauge.getValue());
|
||||
return response;
|
||||
}
|
||||
|
||||
static NamedList counterToNamedList(Counter counter) {
|
||||
NamedList response = new NamedList();
|
||||
response.add("requests", counter.getCount());
|
||||
return response;
|
||||
}
|
||||
}
|
|
@ -1,58 +0,0 @@
|
|||
/*
|
||||
* 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.util.stats;
|
||||
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import com.codahale.metrics.Snapshot;
|
||||
import com.codahale.metrics.Timer;
|
||||
import org.apache.solr.common.util.NamedList;
|
||||
|
||||
/**
|
||||
* Solr specific {@link Timer} utility functions.
|
||||
*/
|
||||
public class TimerUtils {
|
||||
|
||||
/**
|
||||
* Adds metrics from a Timer to a NamedList, using well-known names.
|
||||
* @param lst The NamedList to add the metrics data to
|
||||
* @param timer The Timer to extract the metrics from
|
||||
*/
|
||||
public static void addMetrics(NamedList<Object> lst, Timer timer) {
|
||||
Snapshot snapshot = timer.getSnapshot();
|
||||
lst.add("avgRequestsPerSecond", timer.getMeanRate());
|
||||
lst.add("5minRateRequestsPerSecond", timer.getFiveMinuteRate());
|
||||
lst.add("15minRateRequestsPerSecond", timer.getFifteenMinuteRate());
|
||||
lst.add("avgTimePerRequest", nsToMs(snapshot.getMean()));
|
||||
lst.add("medianRequestTime", nsToMs(snapshot.getMedian()));
|
||||
lst.add("75thPcRequestTime", nsToMs(snapshot.get75thPercentile()));
|
||||
lst.add("95thPcRequestTime", nsToMs(snapshot.get95thPercentile()));
|
||||
lst.add("99thPcRequestTime", nsToMs(snapshot.get99thPercentile()));
|
||||
lst.add("999thPcRequestTime", nsToMs(snapshot.get999thPercentile()));
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts a double representing nanoseconds to a double representing milliseconds.
|
||||
*
|
||||
* @param ns the amount of time in nanoseconds
|
||||
* @return the amount of time in milliseconds
|
||||
*/
|
||||
static double nsToMs(double ns) {
|
||||
return ns / TimeUnit.MILLISECONDS.toNanos(1);
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,57 @@
|
|||
<?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>
|
||||
<!-- 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>
|
||||
<!-- this reporter will be created for both "node" and "core" groups -->
|
||||
<reporter name="multigroup" group="node, core" class="org.apache.solr.metrics.reporters.MockMetricReporter">
|
||||
<str name="configurable">configured</str>
|
||||
</reporter>
|
||||
<!-- this reporter will be created for both "node" and "core.collection1" registries -->
|
||||
<reporter name="multiregistry" registry="node, core.collection1" class="org.apache.solr.metrics.reporters.MockMetricReporter">
|
||||
<str name="configurable">configured</str>
|
||||
</reporter>
|
||||
<reporter name="reporter1" group="jvm" class="org.apache.solr.metrics.reporters.MockMetricReporter">
|
||||
<str name="configurable">configured</str>
|
||||
</reporter>
|
||||
<reporter name="reporter1" group="jetty" class="org.apache.solr.metrics.reporters.MockMetricReporter">
|
||||
<str name="configurable">configured</str>
|
||||
</reporter>
|
||||
<reporter name="reporter1" group="http" class="org.apache.solr.metrics.reporters.MockMetricReporter">
|
||||
<str name="configurable">configured</str>
|
||||
</reporter>
|
||||
<reporter name="reporter1" group="node" class="org.apache.solr.metrics.reporters.MockMetricReporter">
|
||||
<str name="configurable">configured</str>
|
||||
</reporter>
|
||||
<!-- core reporters. -->
|
||||
<reporter name="reporter1" group="core" class="org.apache.solr.metrics.reporters.MockMetricReporter">
|
||||
<str name="configurable">configured</str>
|
||||
</reporter>
|
||||
<reporter name="reporter2" group="core" class="org.apache.solr.metrics.reporters.MockMetricReporter">
|
||||
<str name="configurable">configured</str>
|
||||
</reporter>
|
||||
<!-- reporter for a specific registry -->
|
||||
<reporter name="specific" registry="core.collection1" class="org.apache.solr.metrics.reporters.MockMetricReporter">
|
||||
<str name="configurable">configured</str>
|
||||
</reporter>
|
||||
</metrics>
|
||||
</solr>
|
|
@ -0,0 +1,97 @@
|
|||
/*
|
||||
* 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.handler.admin;
|
||||
|
||||
import org.apache.solr.SolrTestCaseJ4;
|
||||
import org.apache.solr.common.params.CommonParams;
|
||||
import org.apache.solr.common.util.NamedList;
|
||||
import org.apache.solr.response.SolrQueryResponse;
|
||||
import org.junit.BeforeClass;
|
||||
import org.junit.Test;
|
||||
|
||||
/**
|
||||
* Test for {@link MetricsHandler}
|
||||
*/
|
||||
public class MetricsHandlerTest extends SolrTestCaseJ4 {
|
||||
@BeforeClass
|
||||
public static void beforeClass() throws Exception {
|
||||
initCore("solrconfig.xml", "schema.xml");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void test() throws Exception {
|
||||
MetricsHandler handler = new MetricsHandler(h.getCoreContainer());
|
||||
|
||||
SolrQueryResponse resp = new SolrQueryResponse();
|
||||
handler.handleRequestBody(req(CommonParams.QT, "/admin/metrics", CommonParams.WT, "json"), resp);
|
||||
NamedList values = resp.getValues();
|
||||
assertNotNull(values.get("metrics"));
|
||||
values = (NamedList) values.get("metrics");
|
||||
System.out.println(values);
|
||||
assertNotNull(values.get("solr.jetty"));
|
||||
assertNotNull(values.get("solr.jvm"));
|
||||
assertNotNull(values.get("solr.http"));
|
||||
assertNotNull(values.get("solr.node"));
|
||||
NamedList nl = (NamedList) values.get("solr.core.collection1");
|
||||
assertNotNull(nl);
|
||||
assertNotNull(nl.get("newSearcherErrors")); // counter type
|
||||
assertNotNull(((NamedList) nl.get("newSearcherErrors")).get("requests"));
|
||||
assertEquals(0L, ((NamedList) nl.get("newSearcherErrors")).get("requests"));
|
||||
nl = (NamedList) values.get("solr.node");
|
||||
assertNotNull(nl.get("cores.loaded")); // int gauge
|
||||
assertEquals(1, ((NamedList) nl.get("cores.loaded")).get("value"));
|
||||
assertNotNull(nl.get("QUERYHANDLER./admin/authorization.clientErrors")); // timer type
|
||||
assertEquals(5, ((NamedList) nl.get("QUERYHANDLER./admin/authorization.clientErrors")).size());
|
||||
|
||||
resp = new SolrQueryResponse();
|
||||
handler.handleRequestBody(req(CommonParams.QT, "/admin/metrics", CommonParams.WT, "json", "group", "jvm,jetty"), resp);
|
||||
values = resp.getValues();
|
||||
assertNotNull(values.get("metrics"));
|
||||
values = (NamedList) values.get("metrics");
|
||||
assertEquals(2, values.size());
|
||||
assertNotNull(values.get("solr.jetty"));
|
||||
assertNotNull(values.get("solr.jvm"));
|
||||
|
||||
resp = new SolrQueryResponse();
|
||||
handler.handleRequestBody(req(CommonParams.QT, "/admin/metrics", CommonParams.WT, "json", "group", "jvm,jetty"), resp);
|
||||
values = resp.getValues();
|
||||
assertNotNull(values.get("metrics"));
|
||||
values = (NamedList) values.get("metrics");
|
||||
assertEquals(2, values.size());
|
||||
assertNotNull(values.get("solr.jetty"));
|
||||
assertNotNull(values.get("solr.jvm"));
|
||||
|
||||
resp = new SolrQueryResponse();
|
||||
handler.handleRequestBody(req(CommonParams.QT, "/admin/metrics", CommonParams.WT, "json", "group", "jvm", "group", "jetty"), resp);
|
||||
values = resp.getValues();
|
||||
assertNotNull(values.get("metrics"));
|
||||
values = (NamedList) values.get("metrics");
|
||||
assertEquals(2, values.size());
|
||||
assertNotNull(values.get("solr.jetty"));
|
||||
assertNotNull(values.get("solr.jvm"));
|
||||
|
||||
resp = new SolrQueryResponse();
|
||||
handler.handleRequestBody(req(CommonParams.QT, "/admin/metrics", CommonParams.WT, "json", "group", "node", "type", "counter"), resp);
|
||||
values = resp.getValues();
|
||||
assertNotNull(values.get("metrics"));
|
||||
values = (NamedList) values.get("metrics");
|
||||
assertEquals(1, values.size());
|
||||
assertNotNull(values.get("solr.node"));
|
||||
assertNull(values.get("QUERYHANDLER./admin/authorization.errors")); // this is a timer node
|
||||
}
|
||||
}
|
|
@ -0,0 +1,172 @@
|
|||
/*
|
||||
* 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.IOException;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Random;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import com.codahale.metrics.Counter;
|
||||
import com.codahale.metrics.Metric;
|
||||
import com.codahale.metrics.MetricRegistry;
|
||||
import org.apache.lucene.util.TestUtil;
|
||||
import org.apache.solr.SolrTestCaseJ4;
|
||||
import org.apache.solr.common.params.CoreAdminParams;
|
||||
import org.apache.solr.core.PluginInfo;
|
||||
import org.apache.solr.core.SolrInfoMBean;
|
||||
import org.apache.solr.metrics.reporters.MockMetricReporter;
|
||||
import org.apache.solr.schema.FieldType;
|
||||
import org.junit.After;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
|
||||
public class SolrCoreMetricManagerTest extends SolrTestCaseJ4 {
|
||||
private static final int MAX_ITERATIONS = 100;
|
||||
|
||||
private SolrCoreMetricManager coreMetricManager;
|
||||
private SolrMetricManager metricManager;
|
||||
|
||||
@Before
|
||||
public void beforeTest() throws Exception {
|
||||
initCore("solrconfig-basic.xml", "schema.xml");
|
||||
coreMetricManager = h.getCore().getCoreMetricManager();
|
||||
metricManager = h.getCore().getCoreDescriptor().getCoreContainer().getMetricManager();
|
||||
}
|
||||
|
||||
@After
|
||||
public void afterTest() throws IOException {
|
||||
coreMetricManager.close();
|
||||
assertTrue(metricManager.getReporters(coreMetricManager.getRegistryName()).isEmpty());
|
||||
deleteCore();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRegisterMetrics() {
|
||||
Random random = random();
|
||||
|
||||
String scope = SolrMetricTestUtils.getRandomScope(random);
|
||||
SolrInfoMBean.Category category = SolrMetricTestUtils.getRandomCategory(random);
|
||||
Map<String, Counter> metrics = SolrMetricTestUtils.getRandomMetrics(random);
|
||||
SolrMetricProducer producer = SolrMetricTestUtils.getProducerOf(metricManager, category, scope, metrics);
|
||||
try {
|
||||
coreMetricManager.registerMetricProducer(scope, producer);
|
||||
assertNotNull(scope);
|
||||
assertNotNull(category);
|
||||
assertNotNull(metrics);
|
||||
assertRegistered(scope, metrics, coreMetricManager);
|
||||
} catch (final IllegalArgumentException e) {
|
||||
assertTrue("expected at least one null but got: scope="+scope+" category="+category+" metrics="+metrics,
|
||||
(scope == null || category == null || metrics == null));
|
||||
assertRegistered(scope, new HashMap<>(), coreMetricManager);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRegisterMetricsWithReplacements() {
|
||||
Random random = random();
|
||||
|
||||
Map<String, Counter> registered = new HashMap<>();
|
||||
String scope = SolrMetricTestUtils.getRandomScope(random, true);
|
||||
SolrInfoMBean.Category category = SolrMetricTestUtils.getRandomCategory(random, true);
|
||||
|
||||
int iterations = TestUtil.nextInt(random, 0, MAX_ITERATIONS);
|
||||
for (int i = 0; i < iterations; ++i) {
|
||||
Map<String, Counter> metrics = SolrMetricTestUtils.getRandomMetricsWithReplacements(random, registered);
|
||||
if (metrics.isEmpty()) {
|
||||
continue;
|
||||
}
|
||||
SolrMetricProducer producer = SolrMetricTestUtils.getProducerOf(metricManager, category, scope, metrics);
|
||||
coreMetricManager.registerMetricProducer(scope, producer);
|
||||
registered.putAll(metrics);
|
||||
assertRegistered(scope, registered, coreMetricManager);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testLoadReporter() throws Exception {
|
||||
Random random = random();
|
||||
|
||||
String className = MockMetricReporter.class.getName();
|
||||
String reporterName = TestUtil.randomUnicodeString(random);
|
||||
|
||||
Map<String, Object> attrs = new HashMap<>();
|
||||
attrs.put(FieldType.CLASS_NAME, className);
|
||||
attrs.put(CoreAdminParams.NAME, reporterName);
|
||||
|
||||
boolean shouldDefineConfigurable = random.nextBoolean();
|
||||
String configurable = TestUtil.randomUnicodeString(random);
|
||||
if (shouldDefineConfigurable) attrs.put("configurable", configurable);
|
||||
|
||||
boolean shouldDefinePlugin = random.nextBoolean();
|
||||
PluginInfo pluginInfo = shouldDefinePlugin ? new PluginInfo(TestUtil.randomUnicodeString(random), attrs) : null;
|
||||
|
||||
try {
|
||||
metricManager.loadReporter(coreMetricManager.getRegistryName(), coreMetricManager.getCore().getResourceLoader(), pluginInfo);
|
||||
assertNotNull(pluginInfo);
|
||||
Map<String, SolrMetricReporter> reporters = metricManager.getReporters(coreMetricManager.getRegistryName());
|
||||
assertTrue("reporters.size should be > 0, but was + " + reporters.size(), reporters.size() > 0);
|
||||
assertNotNull("reporter " + reporterName + " not present among " + reporters, reporters.get(reporterName));
|
||||
assertTrue("wrong reporter class: " + reporters.get(reporterName), reporters.get(reporterName) instanceof MockMetricReporter);
|
||||
} catch (IllegalArgumentException e) {
|
||||
assertTrue(pluginInfo == null || attrs.get("configurable") == null);
|
||||
assertNull(metricManager.getReporters(coreMetricManager.getRegistryName()).get(reporterName));
|
||||
}
|
||||
}
|
||||
|
||||
private void assertRegistered(String scope, Map<String, Counter> newMetrics, SolrCoreMetricManager coreMetricManager) {
|
||||
if (scope == null) {
|
||||
return;
|
||||
}
|
||||
String filter = "." + scope + ".";
|
||||
MetricRegistry registry = metricManager.registry(coreMetricManager.getRegistryName());
|
||||
assertEquals(newMetrics.size(), registry.getMetrics().
|
||||
keySet().stream().filter(s -> s.contains(filter)).count());
|
||||
|
||||
Map<String, Metric> registeredMetrics = registry.getMetrics().
|
||||
entrySet().stream().filter(e -> e.getKey() != null && e.getKey().contains(filter)).
|
||||
collect(Collectors.toMap(e -> e.getKey(), e -> e.getValue()));
|
||||
for (Map.Entry<String, Metric> entry : registeredMetrics.entrySet()) {
|
||||
String name = entry.getKey();
|
||||
Metric expectedMetric = entry.getValue();
|
||||
|
||||
Metric actualMetric = registry.getMetrics().get(name);
|
||||
|
||||
assertNotNull(actualMetric);
|
||||
assertEquals(expectedMetric, actualMetric);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRegistryName() throws Exception {
|
||||
String collectionName = "my_collection_";
|
||||
String cloudCoreName = "my_collection__shard1_0_replica0";
|
||||
String simpleCoreName = "collection_1_replica0";
|
||||
String simpleRegistryName = "solr.core." + simpleCoreName;
|
||||
String cloudRegistryName = "solr.core." + cloudCoreName;
|
||||
String nestedRegistryName = "solr.core.my_collection_.shard1_0.replica0";
|
||||
// pass through
|
||||
assertEquals(cloudRegistryName, coreMetricManager.createRegistryName(null, cloudCoreName));
|
||||
assertEquals(simpleRegistryName, coreMetricManager.createRegistryName(null, simpleCoreName));
|
||||
// unknown naming scheme -> pass through
|
||||
assertEquals(simpleRegistryName, coreMetricManager.createRegistryName(collectionName, simpleCoreName));
|
||||
// cloud collection
|
||||
assertEquals(nestedRegistryName, coreMetricManager.createRegistryName(collectionName, cloudCoreName));
|
||||
|
||||
}
|
||||
}
|
|
@ -0,0 +1,273 @@
|
|||
/*
|
||||
* 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.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Random;
|
||||
import java.util.Set;
|
||||
|
||||
import com.codahale.metrics.Counter;
|
||||
import com.codahale.metrics.Metric;
|
||||
import com.codahale.metrics.MetricRegistry;
|
||||
import org.apache.lucene.util.TestUtil;
|
||||
import org.apache.solr.SolrTestCaseJ4;
|
||||
import org.apache.solr.common.util.NamedList;
|
||||
import org.apache.solr.core.PluginInfo;
|
||||
import org.apache.solr.core.SolrInfoMBean;
|
||||
import org.apache.solr.core.SolrResourceLoader;
|
||||
import org.apache.solr.metrics.reporters.MockMetricReporter;
|
||||
import org.junit.Test;
|
||||
|
||||
public class SolrMetricManagerTest extends SolrTestCaseJ4 {
|
||||
|
||||
@Test
|
||||
public void testOverridableRegistryName() throws Exception {
|
||||
Random r = random();
|
||||
String originalName = TestUtil.randomSimpleString(r, 1, 10);
|
||||
String targetName = TestUtil.randomSimpleString(r, 1, 10);
|
||||
// no override
|
||||
String result = SolrMetricManager.overridableRegistryName(originalName);
|
||||
assertEquals(SolrMetricManager.REGISTRY_NAME_PREFIX + originalName, result);
|
||||
// with override
|
||||
System.setProperty(SolrMetricManager.REGISTRY_NAME_PREFIX + originalName, targetName);
|
||||
result = SolrMetricManager.overridableRegistryName(originalName);
|
||||
assertEquals(SolrMetricManager.REGISTRY_NAME_PREFIX + targetName, result);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testMoveMetrics() throws Exception {
|
||||
Random r = random();
|
||||
|
||||
SolrMetricManager metricManager = new SolrMetricManager();
|
||||
|
||||
Map<String, Counter> metrics1 = SolrMetricTestUtils.getRandomMetrics(r, true);
|
||||
Map<String, Counter> metrics2 = SolrMetricTestUtils.getRandomMetrics(r, true);
|
||||
String fromName = TestUtil.randomSimpleString(r, 1, 10);
|
||||
String toName = TestUtil.randomSimpleString(r, 1, 10);
|
||||
// register test metrics
|
||||
for (Map.Entry<String, Counter> entry : metrics1.entrySet()) {
|
||||
metricManager.register(fromName, entry.getValue(), false, entry.getKey(), "metrics1");
|
||||
}
|
||||
for (Map.Entry<String, Counter> entry : metrics2.entrySet()) {
|
||||
metricManager.register(fromName, entry.getValue(), false, entry.getKey(), "metrics2");
|
||||
}
|
||||
assertEquals(metrics1.size() + metrics2.size(), metricManager.registry(fromName).getMetrics().size());
|
||||
|
||||
// move metrics1
|
||||
metricManager.moveMetrics(fromName, toName, new SolrMetricManager.PrefixFilter("metrics1"));
|
||||
// check the remaining metrics
|
||||
Map<String, Metric> fromMetrics = metricManager.registry(fromName).getMetrics();
|
||||
assertEquals(metrics2.size(), fromMetrics.size());
|
||||
for (Map.Entry<String, Counter> entry : metrics2.entrySet()) {
|
||||
Object value = fromMetrics.get(SolrMetricManager.mkName(entry.getKey(), "metrics2"));
|
||||
assertNotNull(value);
|
||||
assertEquals(entry.getValue(), value);
|
||||
}
|
||||
// check the moved metrics
|
||||
Map<String, Metric> toMetrics = metricManager.registry(toName).getMetrics();
|
||||
assertEquals(metrics1.size(), toMetrics.size());
|
||||
for (Map.Entry<String, Counter> entry : metrics1.entrySet()) {
|
||||
Object value = toMetrics.get(SolrMetricManager.mkName(entry.getKey(), "metrics1"));
|
||||
assertNotNull(value);
|
||||
assertEquals(entry.getValue(), value);
|
||||
}
|
||||
|
||||
// move all remaining metrics
|
||||
metricManager.moveMetrics(fromName, toName, null);
|
||||
fromMetrics = metricManager.registry(fromName).getMetrics();
|
||||
assertEquals(0, fromMetrics.size());
|
||||
toMetrics = metricManager.registry(toName).getMetrics();
|
||||
assertEquals(metrics1.size() + metrics2.size(), toMetrics.size());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRegisterAll() throws Exception {
|
||||
Random r = random();
|
||||
|
||||
SolrMetricManager metricManager = new SolrMetricManager();
|
||||
|
||||
Map<String, Counter> metrics = SolrMetricTestUtils.getRandomMetrics(r, true);
|
||||
MetricRegistry mr = new MetricRegistry();
|
||||
for (Map.Entry<String, Counter> entry : metrics.entrySet()) {
|
||||
mr.register(entry.getKey(), entry.getValue());
|
||||
}
|
||||
|
||||
String registryName = TestUtil.randomSimpleString(r, 1, 10);
|
||||
assertEquals(0, metricManager.registry(registryName).getMetrics().size());
|
||||
metricManager.registerAll(registryName, mr, false);
|
||||
// this should simply skip existing names
|
||||
metricManager.registerAll(registryName, mr, true);
|
||||
// this should produce error
|
||||
try {
|
||||
metricManager.registerAll(registryName, mr, false);
|
||||
fail("registerAll with duplicate metric names should fail");
|
||||
} catch (IllegalArgumentException e) {
|
||||
// expected
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testClearMetrics() throws Exception {
|
||||
Random r = random();
|
||||
|
||||
SolrMetricManager metricManager = new SolrMetricManager();
|
||||
|
||||
Map<String, Counter> metrics = SolrMetricTestUtils.getRandomMetrics(r, true);
|
||||
String registryName = TestUtil.randomSimpleString(r, 1, 10);
|
||||
|
||||
for (Map.Entry<String, Counter> entry : metrics.entrySet()) {
|
||||
metricManager.register(registryName, entry.getValue(), false, entry.getKey(), "foo", "bar");
|
||||
}
|
||||
for (Map.Entry<String, Counter> entry : metrics.entrySet()) {
|
||||
metricManager.register(registryName, entry.getValue(), false, entry.getKey(), "foo", "baz");
|
||||
}
|
||||
for (Map.Entry<String, Counter> entry : metrics.entrySet()) {
|
||||
metricManager.register(registryName, entry.getValue(), false, entry.getKey(), "foo");
|
||||
}
|
||||
|
||||
assertEquals(metrics.size() * 3, metricManager.registry(registryName).getMetrics().size());
|
||||
|
||||
// clear "foo.bar"
|
||||
Set<String> removed = metricManager.clearMetrics(registryName, "foo", "bar");
|
||||
assertEquals(metrics.size(), removed.size());
|
||||
for (String s : removed) {
|
||||
assertTrue(s.startsWith("foo.bar."));
|
||||
}
|
||||
removed = metricManager.clearMetrics(registryName, "foo", "baz");
|
||||
assertEquals(metrics.size(), removed.size());
|
||||
for (String s : removed) {
|
||||
assertTrue(s.startsWith("foo.baz."));
|
||||
}
|
||||
// perhaps surprisingly, this works too - see PrefixFilter docs
|
||||
removed = metricManager.clearMetrics(registryName, "fo");
|
||||
assertEquals(metrics.size(), removed.size());
|
||||
for (String s : removed) {
|
||||
assertTrue(s.startsWith("foo."));
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSimpleMetrics() throws Exception {
|
||||
Random r = random();
|
||||
|
||||
SolrMetricManager metricManager = new SolrMetricManager();
|
||||
|
||||
String registryName = TestUtil.randomSimpleString(r, 1, 10);
|
||||
|
||||
metricManager.counter(registryName, "simple_counter", "foo", "bar");
|
||||
metricManager.timer(registryName, "simple_timer", "foo", "bar");
|
||||
metricManager.meter(registryName, "simple_meter", "foo", "bar");
|
||||
metricManager.histogram(registryName, "simple_histogram", "foo", "bar");
|
||||
Map<String, Metric> metrics = metricManager.registry(registryName).getMetrics();
|
||||
assertEquals(4, metrics.size());
|
||||
for (Map.Entry<String, Metric> entry : metrics.entrySet()) {
|
||||
assertTrue(entry.getKey().startsWith("foo.bar.simple_"));
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRegistryName() throws Exception {
|
||||
Random r = random();
|
||||
|
||||
String name = TestUtil.randomSimpleString(r, 1, 10);
|
||||
|
||||
String result = SolrMetricManager.getRegistryName(SolrInfoMBean.Group.core, name, "collection1");
|
||||
assertEquals("solr.core." + name + ".collection1", result);
|
||||
// try it with already prefixed name - group will be ignored
|
||||
result = SolrMetricManager.getRegistryName(SolrInfoMBean.Group.core, result);
|
||||
assertEquals("solr.core." + name + ".collection1", result);
|
||||
// try it with already prefixed name but with additional segments
|
||||
result = SolrMetricManager.getRegistryName(SolrInfoMBean.Group.core, result, "shard1", "replica1");
|
||||
assertEquals("solr.core." + name + ".collection1.shard1.replica1", result);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testReporters() throws Exception {
|
||||
Random r = random();
|
||||
|
||||
SolrResourceLoader loader = new SolrResourceLoader();
|
||||
SolrMetricManager metricManager = new SolrMetricManager();
|
||||
|
||||
PluginInfo[] plugins = new PluginInfo[] {
|
||||
createPluginInfo("universal_foo", null, null),
|
||||
createPluginInfo("multigroup_foo", "jvm, node, core", null),
|
||||
createPluginInfo("multiregistry_foo", null, "solr.node, solr.core.collection1"),
|
||||
createPluginInfo("specific_foo", null, "solr.core.collection1"),
|
||||
createPluginInfo("node_foo", "node", null),
|
||||
createPluginInfo("core_foo", "core", null)
|
||||
};
|
||||
|
||||
metricManager.loadReporters(plugins, loader, SolrInfoMBean.Group.node);
|
||||
Map<String, SolrMetricReporter> reporters = metricManager.getReporters(
|
||||
SolrMetricManager.getRegistryName(SolrInfoMBean.Group.node));
|
||||
assertEquals(4, reporters.size());
|
||||
assertTrue(reporters.containsKey("universal_foo"));
|
||||
assertTrue(reporters.containsKey("multigroup_foo"));
|
||||
assertTrue(reporters.containsKey("node_foo"));
|
||||
assertTrue(reporters.containsKey("multiregistry_foo"));
|
||||
|
||||
metricManager.loadReporters(plugins, loader, SolrInfoMBean.Group.core, "collection1");
|
||||
reporters = metricManager.getReporters(
|
||||
SolrMetricManager.getRegistryName(SolrInfoMBean.Group.core, "collection1"));
|
||||
assertEquals(5, reporters.size());
|
||||
assertTrue(reporters.containsKey("universal_foo"));
|
||||
assertTrue(reporters.containsKey("multigroup_foo"));
|
||||
assertTrue(reporters.containsKey("specific_foo"));
|
||||
assertTrue(reporters.containsKey("core_foo"));
|
||||
assertTrue(reporters.containsKey("multiregistry_foo"));
|
||||
|
||||
metricManager.loadReporters(plugins, loader, SolrInfoMBean.Group.jvm);
|
||||
reporters = metricManager.getReporters(
|
||||
SolrMetricManager.getRegistryName(SolrInfoMBean.Group.jvm));
|
||||
assertEquals(2, reporters.size());
|
||||
assertTrue(reporters.containsKey("universal_foo"));
|
||||
assertTrue(reporters.containsKey("multigroup_foo"));
|
||||
|
||||
metricManager.removeRegistry("solr.jvm");
|
||||
reporters = metricManager.getReporters(
|
||||
SolrMetricManager.getRegistryName(SolrInfoMBean.Group.jvm));
|
||||
assertEquals(0, reporters.size());
|
||||
|
||||
metricManager.removeRegistry("solr.node");
|
||||
reporters = metricManager.getReporters(
|
||||
SolrMetricManager.getRegistryName(SolrInfoMBean.Group.node));
|
||||
assertEquals(0, reporters.size());
|
||||
|
||||
metricManager.removeRegistry("solr.core.collection1");
|
||||
reporters = metricManager.getReporters(
|
||||
SolrMetricManager.getRegistryName(SolrInfoMBean.Group.core, "collection1"));
|
||||
assertEquals(0, reporters.size());
|
||||
|
||||
}
|
||||
|
||||
private PluginInfo createPluginInfo(String name, String group, String registry) {
|
||||
Map<String,String> attrs = new HashMap<>();
|
||||
attrs.put("name", name);
|
||||
attrs.put("class", MockMetricReporter.class.getName());
|
||||
if (group != null) {
|
||||
attrs.put("group", group);
|
||||
}
|
||||
if (registry != null) {
|
||||
attrs.put("registry", registry);
|
||||
}
|
||||
NamedList initArgs = new NamedList();
|
||||
initArgs.add("configurable", "true");
|
||||
return new PluginInfo("SolrMetricReporter", attrs, initArgs, null);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,69 @@
|
|||
/*
|
||||
* 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.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Random;
|
||||
|
||||
import org.apache.lucene.util.LuceneTestCase;
|
||||
import org.apache.lucene.util.TestUtil;
|
||||
import org.apache.solr.common.params.CoreAdminParams;
|
||||
import org.apache.solr.core.PluginInfo;
|
||||
import org.apache.solr.metrics.reporters.MockMetricReporter;
|
||||
import org.apache.solr.schema.FieldType;
|
||||
import org.junit.Test;
|
||||
|
||||
public class SolrMetricReporterTest extends LuceneTestCase {
|
||||
|
||||
@Test
|
||||
public void testInit() throws Exception {
|
||||
Random random = random();
|
||||
|
||||
SolrMetricManager metricManager = new SolrMetricManager();
|
||||
|
||||
final String registryName = TestUtil.randomSimpleString(random);
|
||||
final MockMetricReporter reporter = new MockMetricReporter(metricManager, registryName);
|
||||
|
||||
Map<String, Object> attrs = new HashMap<>();
|
||||
attrs.put(FieldType.CLASS_NAME, MockMetricReporter.class.getName());
|
||||
attrs.put(CoreAdminParams.NAME, TestUtil.randomUnicodeString(random));
|
||||
|
||||
boolean shouldDefineConfigurable = random.nextBoolean();
|
||||
String configurable = TestUtil.randomUnicodeString(random);
|
||||
if (shouldDefineConfigurable) attrs.put("configurable", configurable);
|
||||
|
||||
boolean shouldDefinePlugin = random.nextBoolean();
|
||||
String type = TestUtil.randomUnicodeString(random);
|
||||
PluginInfo pluginInfo = shouldDefinePlugin ? new PluginInfo(type, attrs) : null;
|
||||
|
||||
try {
|
||||
reporter.init(pluginInfo);
|
||||
assertNotNull(pluginInfo);
|
||||
assertEquals(configurable, attrs.get("configurable"));
|
||||
assertTrue(reporter.didValidate);
|
||||
assertNotNull(reporter.configurable);
|
||||
assertEquals(configurable, reporter.configurable);
|
||||
} catch (IllegalStateException e) {
|
||||
assertTrue(pluginInfo == null || attrs.get("configurable") == null);
|
||||
assertTrue(reporter.didValidate);
|
||||
assertNull(reporter.configurable);
|
||||
} finally {
|
||||
reporter.close();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,140 @@
|
|||
/*
|
||||
* 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.net.URL;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Random;
|
||||
|
||||
import com.codahale.metrics.Counter;
|
||||
import org.apache.lucene.util.TestUtil;
|
||||
import org.apache.solr.common.util.NamedList;
|
||||
import org.apache.solr.core.SolrInfoMBean;
|
||||
|
||||
public final class SolrMetricTestUtils {
|
||||
|
||||
private static final int MAX_ITERATIONS = 100;
|
||||
private static final SolrInfoMBean.Category CATEGORIES[] = SolrInfoMBean.Category.values();
|
||||
|
||||
public static String getRandomScope(Random random) {
|
||||
return getRandomScope(random, random.nextBoolean());
|
||||
}
|
||||
|
||||
public static String getRandomScope(Random random, boolean shouldDefineScope) {
|
||||
return shouldDefineScope ? TestUtil.randomSimpleString(random, 1, 10) : null; // must be simple string for JMX publishing
|
||||
}
|
||||
|
||||
public static SolrInfoMBean.Category getRandomCategory(Random random) {
|
||||
return getRandomCategory(random, random.nextBoolean());
|
||||
}
|
||||
|
||||
public static SolrInfoMBean.Category getRandomCategory(Random random, boolean shouldDefineCategory) {
|
||||
return shouldDefineCategory ? CATEGORIES[TestUtil.nextInt(random, 0, CATEGORIES.length - 1)] : null;
|
||||
}
|
||||
|
||||
public static Map<String, Counter> getRandomMetrics(Random random) {
|
||||
return getRandomMetrics(random, random.nextBoolean());
|
||||
}
|
||||
|
||||
public static Map<String, Counter> getRandomMetrics(Random random, boolean shouldDefineMetrics) {
|
||||
return shouldDefineMetrics ? getRandomMetricsWithReplacements(random, new HashMap<>()) : null;
|
||||
}
|
||||
|
||||
public static final String SUFFIX = "_testing";
|
||||
|
||||
public static Map<String, Counter> getRandomMetricsWithReplacements(Random random, Map<String, Counter> existing) {
|
||||
HashMap<String, Counter> metrics = new HashMap<>();
|
||||
ArrayList<String> existingKeys = new ArrayList<>(existing.keySet());
|
||||
|
||||
int numMetrics = TestUtil.nextInt(random, 1, MAX_ITERATIONS);
|
||||
for (int i = 0; i < numMetrics; ++i) {
|
||||
boolean shouldReplaceMetric = !existing.isEmpty() && random.nextBoolean();
|
||||
String name = shouldReplaceMetric
|
||||
? existingKeys.get(TestUtil.nextInt(random, 0, existingKeys.size() - 1))
|
||||
: TestUtil.randomSimpleString(random, 1, 10) + SUFFIX; // must be simple string for JMX publishing
|
||||
|
||||
Counter counter = new Counter();
|
||||
counter.inc(random.nextLong());
|
||||
metrics.put(name, counter);
|
||||
}
|
||||
|
||||
return metrics;
|
||||
}
|
||||
|
||||
public static SolrMetricProducer getProducerOf(SolrMetricManager metricManager, SolrInfoMBean.Category category, String scope, Map<String, Counter> metrics) {
|
||||
return new SolrMetricProducer() {
|
||||
@Override
|
||||
public Collection<String> initializeMetrics(SolrMetricManager manager, String registry, String scope) {
|
||||
if (metrics == null || metrics.isEmpty()) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
for (Map.Entry<String, Counter> entry : metrics.entrySet()) {
|
||||
manager.counter(registry, entry.getKey(), category.toString(), scope);
|
||||
}
|
||||
return metrics.keySet();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return scope;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getVersion() {
|
||||
return "0.0";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getDescription() {
|
||||
return "foo";
|
||||
}
|
||||
|
||||
@Override
|
||||
public Category getCategory() {
|
||||
return category;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getSource() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public URL[] getDocs() {
|
||||
return new URL[0];
|
||||
}
|
||||
|
||||
@Override
|
||||
public NamedList getStatistics() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "SolrMetricProducer.of{" +
|
||||
"\ncategory=" + category +
|
||||
"\nscope=" + scope +
|
||||
"\nmetrics=" + metrics +
|
||||
"\n}";
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
|
@ -0,0 +1,143 @@
|
|||
/*
|
||||
* 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.nio.file.Path;
|
||||
import java.nio.file.Paths;
|
||||
import java.util.Arrays;
|
||||
import java.util.Map;
|
||||
import java.util.Random;
|
||||
|
||||
import com.codahale.metrics.Timer;
|
||||
import org.apache.commons.io.FileUtils;
|
||||
import org.apache.lucene.util.TestUtil;
|
||||
import org.apache.solr.SolrTestCaseJ4;
|
||||
import org.apache.solr.core.CoreContainer;
|
||||
import org.apache.solr.core.NodeConfig;
|
||||
import org.apache.solr.core.PluginInfo;
|
||||
import org.apache.solr.core.SolrInfoMBean;
|
||||
import org.apache.solr.core.SolrResourceLoader;
|
||||
import org.apache.solr.core.SolrXmlConfig;
|
||||
import org.apache.solr.metrics.reporters.MockMetricReporter;
|
||||
import org.apache.solr.util.TestHarness;
|
||||
import org.junit.After;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
|
||||
public class SolrMetricsIntegrationTest extends SolrTestCaseJ4 {
|
||||
private static final int MAX_ITERATIONS = 20;
|
||||
private static final String CORE_NAME = "metrics_integration";
|
||||
private static final String METRIC_NAME = "requestTimes";
|
||||
private static final String HANDLER_NAME = "standard";
|
||||
private static final String[] REPORTER_NAMES = {"reporter1", "reporter2"};
|
||||
private static final String UNIVERSAL = "universal";
|
||||
private static final String SPECIFIC = "specific";
|
||||
private static final String MULTIGROUP = "multigroup";
|
||||
private static final String MULTIREGISTRY = "multiregistry";
|
||||
private static final String[] INITIAL_REPORTERS = {REPORTER_NAMES[0], REPORTER_NAMES[1], UNIVERSAL, SPECIFIC, MULTIGROUP, MULTIREGISTRY};
|
||||
private static final String[] RENAMED_REPORTERS = {REPORTER_NAMES[0], REPORTER_NAMES[1], UNIVERSAL, MULTIGROUP};
|
||||
private static final SolrInfoMBean.Category HANDLER_CATEGORY = SolrInfoMBean.Category.QUERYHANDLER;
|
||||
|
||||
private CoreContainer cc;
|
||||
private SolrMetricManager metricManager;
|
||||
|
||||
@Before
|
||||
public void beforeTest() throws Exception {
|
||||
Path home = Paths.get(TEST_HOME());
|
||||
// define these properties, they are used in solrconfig.xml
|
||||
System.setProperty("solr.test.sys.prop1", "propone");
|
||||
System.setProperty("solr.test.sys.prop2", "proptwo");
|
||||
String solrXml = FileUtils.readFileToString(Paths.get(home.toString(), "solr-metricreporter.xml").toFile(), "UTF-8");
|
||||
NodeConfig cfg = SolrXmlConfig.fromString(new SolrResourceLoader(home), solrXml);
|
||||
cc = createCoreContainer(cfg,
|
||||
new TestHarness.TestCoresLocator(DEFAULT_TEST_CORENAME, initCoreDataDir.getAbsolutePath(), "solrconfig.xml", "schema.xml"));
|
||||
h.coreName = DEFAULT_TEST_CORENAME;
|
||||
metricManager = cc.getMetricManager();
|
||||
// initially there are more reporters, because two of them are added via a matching collection name
|
||||
Map<String, SolrMetricReporter> reporters = metricManager.getReporters("solr.core." + DEFAULT_TEST_CORENAME);
|
||||
assertEquals(INITIAL_REPORTERS.length, reporters.size());
|
||||
assertTrue(reporters.keySet().containsAll(Arrays.asList(INITIAL_REPORTERS)));
|
||||
// test rename operation
|
||||
cc.rename(DEFAULT_TEST_CORENAME, CORE_NAME);
|
||||
h.coreName = CORE_NAME;
|
||||
cfg = cc.getConfig();
|
||||
PluginInfo[] plugins = cfg.getMetricReporterPlugins();
|
||||
assertNotNull(plugins);
|
||||
assertEquals(10, plugins.length);
|
||||
reporters = metricManager.getReporters("solr.node");
|
||||
assertEquals(4, reporters.size());
|
||||
assertTrue("Reporter '" + REPORTER_NAMES[0] + "' missing in solr.node", reporters.containsKey(REPORTER_NAMES[0]));
|
||||
assertTrue("Reporter '" + UNIVERSAL + "' missing in solr.node", reporters.containsKey(UNIVERSAL));
|
||||
assertTrue("Reporter '" + MULTIGROUP + "' missing in solr.node", reporters.containsKey(MULTIGROUP));
|
||||
assertTrue("Reporter '" + MULTIREGISTRY + "' missing in solr.node", reporters.containsKey(MULTIREGISTRY));
|
||||
SolrMetricReporter reporter = reporters.get(REPORTER_NAMES[0]);
|
||||
assertTrue("Reporter " + reporter + " is not an instance of " + MockMetricReporter.class.getName(),
|
||||
reporter instanceof MockMetricReporter);
|
||||
reporter = reporters.get(UNIVERSAL);
|
||||
assertTrue("Reporter " + reporter + " is not an instance of " + MockMetricReporter.class.getName(),
|
||||
reporter instanceof MockMetricReporter);
|
||||
}
|
||||
|
||||
@After
|
||||
public void afterTest() throws Exception {
|
||||
SolrCoreMetricManager coreMetricManager = h.getCore().getCoreMetricManager();
|
||||
Map<String, SolrMetricReporter> reporters = metricManager.getReporters(coreMetricManager.getRegistryName());
|
||||
|
||||
deleteCore();
|
||||
|
||||
for (String reporterName : RENAMED_REPORTERS) {
|
||||
SolrMetricReporter reporter = reporters.get(reporterName);
|
||||
MockMetricReporter mockReporter = (MockMetricReporter) reporter;
|
||||
assertTrue("Reporter " + reporterName + " was not closed: " + mockReporter, mockReporter.didClose);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testConfigureReporter() throws Exception {
|
||||
Random random = random();
|
||||
|
||||
String metricName = SolrMetricManager.mkName(METRIC_NAME, HANDLER_CATEGORY.toString(), HANDLER_NAME);
|
||||
SolrCoreMetricManager coreMetricManager = h.getCore().getCoreMetricManager();
|
||||
Timer timer = (Timer) metricManager.timer(coreMetricManager.getRegistryName(), metricName);
|
||||
|
||||
long initialCount = timer.getCount();
|
||||
|
||||
int iterations = TestUtil.nextInt(random, 0, MAX_ITERATIONS);
|
||||
for (int i = 0; i < iterations; ++i) {
|
||||
h.query(req("*"));
|
||||
}
|
||||
|
||||
long finalCount = timer.getCount();
|
||||
assertEquals("metric counter incorrect", iterations, finalCount - initialCount);
|
||||
Map<String, SolrMetricReporter> reporters = metricManager.getReporters(coreMetricManager.getRegistryName());
|
||||
assertEquals(RENAMED_REPORTERS.length, reporters.size());
|
||||
|
||||
// SPECIFIC and MULTIREGISTRY were skipped because they were
|
||||
// specific to collection1
|
||||
for (String reporterName : RENAMED_REPORTERS) {
|
||||
SolrMetricReporter reporter = reporters.get(reporterName);
|
||||
assertNotNull("Reporter " + reporterName + " was not found.", reporter);
|
||||
assertTrue(reporter instanceof MockMetricReporter);
|
||||
|
||||
MockMetricReporter mockReporter = (MockMetricReporter) reporter;
|
||||
assertTrue("Reporter " + reporterName + " was not initialized: " + mockReporter, mockReporter.didInit);
|
||||
assertTrue("Reporter " + reporterName + " was not validated: " + mockReporter, mockReporter.didValidate);
|
||||
assertFalse("Reporter " + reporterName + " was incorrectly closed: " + mockReporter, mockReporter.didClose);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,80 @@
|
|||
/*
|
||||
* 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.reporters;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Locale;
|
||||
import java.util.NoSuchElementException;
|
||||
|
||||
import com.codahale.metrics.Metric;
|
||||
import com.codahale.metrics.MetricRegistry;
|
||||
import org.apache.solr.core.PluginInfo;
|
||||
import org.apache.solr.metrics.SolrMetricManager;
|
||||
import org.apache.solr.metrics.SolrMetricReporter;
|
||||
|
||||
public class MockMetricReporter extends SolrMetricReporter {
|
||||
|
||||
public String configurable;
|
||||
|
||||
public boolean didInit = false;
|
||||
public boolean didClose = false;
|
||||
public boolean didValidate = false;
|
||||
|
||||
public MockMetricReporter(SolrMetricManager metricManager, String registryName) {
|
||||
super(metricManager, registryName);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void init(PluginInfo pluginInfo) {
|
||||
super.init(pluginInfo);
|
||||
didInit = true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() throws IOException {
|
||||
didClose = true;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void validate() throws IllegalStateException {
|
||||
didValidate = true;
|
||||
if (configurable == null) {
|
||||
throw new IllegalStateException("MockMetricReporter::configurable not defined.");
|
||||
}
|
||||
}
|
||||
|
||||
public void setConfigurable(String configurable) {
|
||||
this.configurable = configurable;
|
||||
}
|
||||
|
||||
public Metric reportMetric(String metricName) throws NoSuchElementException {
|
||||
MetricRegistry registry = metricManager.registry(registryName);
|
||||
Metric metric = registry.getMetrics().get(metricName);
|
||||
if (metric == null) {
|
||||
throw new NoSuchElementException("Metric was not found for metric name = " + metricName);
|
||||
}
|
||||
|
||||
return metric;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return String.format(Locale.ENGLISH, "[%s@%s: configurable = %s, didInit = %b, didValidate = %b, didClose = %b]",
|
||||
getClass().getName(), Integer.toHexString(hashCode()), configurable, didInit, didValidate, didClose);
|
||||
|
||||
}
|
||||
}
|
|
@ -0,0 +1,156 @@
|
|||
/*
|
||||
* 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.reporters;
|
||||
|
||||
import javax.management.MBeanServer;
|
||||
import javax.management.ObjectInstance;
|
||||
import javax.management.ObjectName;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Random;
|
||||
import java.util.Set;
|
||||
|
||||
import com.codahale.metrics.Counter;
|
||||
import org.apache.lucene.util.TestUtil;
|
||||
import org.apache.solr.SolrTestCaseJ4;
|
||||
import org.apache.solr.common.params.CoreAdminParams;
|
||||
import org.apache.solr.core.PluginInfo;
|
||||
import org.apache.solr.core.SolrCore;
|
||||
import org.apache.solr.core.SolrInfoMBean;
|
||||
import org.apache.solr.metrics.SolrCoreMetricManager;
|
||||
import org.apache.solr.metrics.SolrMetricManager;
|
||||
import org.apache.solr.metrics.SolrMetricProducer;
|
||||
import org.apache.solr.metrics.SolrMetricReporter;
|
||||
import org.apache.solr.metrics.SolrMetricTestUtils;
|
||||
import org.apache.solr.schema.FieldType;
|
||||
import org.junit.After;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
|
||||
public class SolrJmxReporterTest extends SolrTestCaseJ4 {
|
||||
|
||||
private static final int MAX_ITERATIONS = 20;
|
||||
|
||||
private String domain;
|
||||
|
||||
private SolrCoreMetricManager coreMetricManager;
|
||||
private SolrMetricManager metricManager;
|
||||
private SolrJmxReporter reporter;
|
||||
private MBeanServer mBeanServer;
|
||||
private String reporterName;
|
||||
|
||||
@Before
|
||||
public void beforeTest() throws Exception {
|
||||
initCore("solrconfig-basic.xml", "schema.xml");
|
||||
|
||||
final SolrCore core = h.getCore();
|
||||
domain = core.getName();
|
||||
|
||||
coreMetricManager = core.getCoreMetricManager();
|
||||
metricManager = core.getCoreDescriptor().getCoreContainer().getMetricManager();
|
||||
PluginInfo pluginInfo = createReporterPluginInfo();
|
||||
metricManager.loadReporter(coreMetricManager.getRegistryName(), coreMetricManager.getCore().getResourceLoader(), pluginInfo);
|
||||
|
||||
Map<String, SolrMetricReporter> reporters = metricManager.getReporters(coreMetricManager.getRegistryName());
|
||||
assertTrue("reporters.size should be > 0, but was + " + reporters.size(), reporters.size() > 0);
|
||||
reporterName = pluginInfo.name;
|
||||
assertNotNull("reporter " + reporterName + " not present among " + reporters, reporters.get(reporterName));
|
||||
assertTrue("wrong reporter class: " + reporters.get(reporterName), reporters.get(reporterName) instanceof SolrJmxReporter);
|
||||
|
||||
reporter = (SolrJmxReporter) reporters.get(reporterName);
|
||||
mBeanServer = reporter.getMBeanServer();
|
||||
assertNotNull("MBean server not found.", mBeanServer);
|
||||
}
|
||||
|
||||
private PluginInfo createReporterPluginInfo() {
|
||||
Random random = random();
|
||||
String className = SolrJmxReporter.class.getName();
|
||||
String reporterName = TestUtil.randomSimpleString(random, 1, 10);
|
||||
|
||||
Map<String, Object> attrs = new HashMap<>();
|
||||
attrs.put(FieldType.CLASS_NAME, className);
|
||||
attrs.put(CoreAdminParams.NAME, reporterName);
|
||||
|
||||
boolean shouldOverrideDomain = random.nextBoolean();
|
||||
if (shouldOverrideDomain) {
|
||||
domain = TestUtil.randomSimpleString(random);
|
||||
attrs.put("domain", domain);
|
||||
}
|
||||
|
||||
return new PluginInfo(TestUtil.randomUnicodeString(random), attrs);
|
||||
}
|
||||
|
||||
@After
|
||||
public void afterTest() throws Exception {
|
||||
metricManager.closeReporters(coreMetricManager.getRegistryName());
|
||||
Set<ObjectInstance> objects =
|
||||
mBeanServer.queryMBeans(ObjectName.getInstance(domain + ":*"), null);
|
||||
assertTrue(objects.isEmpty());
|
||||
|
||||
coreMetricManager.close();
|
||||
deleteCore();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testReportMetrics() throws Exception {
|
||||
Random random = random();
|
||||
|
||||
Map<String, Counter> registered = new HashMap<>();
|
||||
String scope = SolrMetricTestUtils.getRandomScope(random, true);
|
||||
SolrInfoMBean.Category category = SolrMetricTestUtils.getRandomCategory(random, true);
|
||||
|
||||
int iterations = TestUtil.nextInt(random, 0, MAX_ITERATIONS);
|
||||
for (int i = 0; i < iterations; ++i) {
|
||||
Map<String, Counter> metrics = SolrMetricTestUtils.getRandomMetricsWithReplacements(random, registered);
|
||||
SolrMetricProducer producer = SolrMetricTestUtils.getProducerOf(metricManager, category, scope, metrics);
|
||||
coreMetricManager.registerMetricProducer(scope, producer);
|
||||
registered.putAll(metrics);
|
||||
//waitForListener();
|
||||
Set<ObjectInstance> objects = mBeanServer.queryMBeans(null, null);
|
||||
assertEquals(registered.size(), objects.stream().
|
||||
filter(o -> scope.equals(o.getObjectName().getKeyProperty("scope")) &&
|
||||
reporterName.equals(o.getObjectName().getKeyProperty("reporter"))).count());
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testReloadCore() throws Exception {
|
||||
Random random = random();
|
||||
|
||||
String scope = SolrMetricTestUtils.getRandomScope(random, true);
|
||||
SolrInfoMBean.Category category = SolrMetricTestUtils.getRandomCategory(random, true);
|
||||
Map<String, Counter> metrics = SolrMetricTestUtils.getRandomMetrics(random, true);
|
||||
SolrMetricProducer producer = SolrMetricTestUtils.getProducerOf(metricManager, category, scope, metrics);
|
||||
coreMetricManager.registerMetricProducer(scope, producer);
|
||||
Set<ObjectInstance> objects = mBeanServer.queryMBeans(null, null);
|
||||
assertEquals(metrics.size(), objects.stream().
|
||||
filter(o -> scope.equals(o.getObjectName().getKeyProperty("scope")) &&
|
||||
reporterName.equals(o.getObjectName().getKeyProperty("reporter"))).count());
|
||||
|
||||
h.getCoreContainer().reload(h.getCore().getName());
|
||||
PluginInfo pluginInfo = createReporterPluginInfo();
|
||||
metricManager.loadReporter(coreMetricManager.getRegistryName(), coreMetricManager.getCore().getResourceLoader(), pluginInfo);
|
||||
coreMetricManager.registerMetricProducer(scope, producer);
|
||||
|
||||
objects = mBeanServer.queryMBeans(null, null);
|
||||
assertEquals(metrics.size(), objects.stream().
|
||||
filter(o -> scope.equals(o.getObjectName().getKeyProperty("scope")) &&
|
||||
pluginInfo.name.equals(o.getObjectName().getKeyProperty("reporter"))).count());
|
||||
}
|
||||
|
||||
}
|
|
@ -26,7 +26,7 @@ import org.apache.solr.common.util.NamedList;
|
|||
import org.apache.solr.common.util.SimpleOrderedMap;
|
||||
import org.junit.Test;
|
||||
|
||||
public class TimerUtilsTest extends SolrTestCaseJ4 {
|
||||
public class MetricUtilsTest extends SolrTestCaseJ4 {
|
||||
|
||||
@Test
|
||||
public void testSolrTimerGetSnapshot() {
|
||||
|
@ -38,7 +38,7 @@ public class TimerUtilsTest extends SolrTestCaseJ4 {
|
|||
}
|
||||
// obtain timer metrics
|
||||
final NamedList<Object> lst = new SimpleOrderedMap<>();
|
||||
TimerUtils.addMetrics(lst, timer);
|
||||
MetricUtils.addMetrics(lst, timer);
|
||||
// check that expected metrics were obtained
|
||||
assertEquals(lst.size(), 9);
|
||||
final Snapshot snapshot = timer.getSnapshot();
|
||||
|
@ -46,12 +46,12 @@ public class TimerUtilsTest extends SolrTestCaseJ4 {
|
|||
// assertEquals(lst.get("avgRequestsPerSecond"), timer.getMeanRate());
|
||||
assertEquals(lst.get("5minRateRequestsPerSecond"), timer.getFiveMinuteRate());
|
||||
assertEquals(lst.get("15minRateRequestsPerSecond"), timer.getFifteenMinuteRate());
|
||||
assertEquals(lst.get("avgTimePerRequest"), TimerUtils.nsToMs(snapshot.getMean()));
|
||||
assertEquals(lst.get("medianRequestTime"), TimerUtils.nsToMs(snapshot.getMedian()));
|
||||
assertEquals(lst.get("75thPcRequestTime"), TimerUtils.nsToMs(snapshot.get75thPercentile()));
|
||||
assertEquals(lst.get("95thPcRequestTime"), TimerUtils.nsToMs(snapshot.get95thPercentile()));
|
||||
assertEquals(lst.get("99thPcRequestTime"), TimerUtils.nsToMs(snapshot.get99thPercentile()));
|
||||
assertEquals(lst.get("999thPcRequestTime"), TimerUtils.nsToMs(snapshot.get999thPercentile()));
|
||||
assertEquals(lst.get("avgTimePerRequest"), MetricUtils.nsToMs(snapshot.getMean()));
|
||||
assertEquals(lst.get("medianRequestTime"), MetricUtils.nsToMs(snapshot.getMedian()));
|
||||
assertEquals(lst.get("75thPcRequestTime"), MetricUtils.nsToMs(snapshot.get75thPercentile()));
|
||||
assertEquals(lst.get("95thPcRequestTime"), MetricUtils.nsToMs(snapshot.get95thPercentile()));
|
||||
assertEquals(lst.get("99thPcRequestTime"), MetricUtils.nsToMs(snapshot.get99thPercentile()));
|
||||
assertEquals(lst.get("999thPcRequestTime"), MetricUtils.nsToMs(snapshot.get999thPercentile()));
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,203 @@
|
|||
|
||||
Apache License
|
||||
Version 2.0, January 2004
|
||||
http://www.apache.org/licenses/
|
||||
|
||||
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||
|
||||
1. Definitions.
|
||||
|
||||
"License" shall mean the terms and conditions for use, reproduction,
|
||||
and distribution as defined by Sections 1 through 9 of this document.
|
||||
|
||||
"Licensor" shall mean the copyright owner or entity authorized by
|
||||
the copyright owner that is granting the License.
|
||||
|
||||
"Legal Entity" shall mean the union of the acting entity and all
|
||||
other entities that control, are controlled by, or are under common
|
||||
control with that entity. For the purposes of this definition,
|
||||
"control" means (i) the power, direct or indirect, to cause the
|
||||
direction or management of such entity, whether by contract or
|
||||
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||
|
||||
"You" (or "Your") shall mean an individual or Legal Entity
|
||||
exercising permissions granted by this License.
|
||||
|
||||
"Source" form shall mean the preferred form for making modifications,
|
||||
including but not limited to software source code, documentation
|
||||
source, and configuration files.
|
||||
|
||||
"Object" form shall mean any form resulting from mechanical
|
||||
transformation or translation of a Source form, including but
|
||||
not limited to compiled object code, generated documentation,
|
||||
and conversions to other media types.
|
||||
|
||||
"Work" shall mean the work of authorship, whether in Source or
|
||||
Object form, made available under the License, as indicated by a
|
||||
copyright notice that is included in or attached to the work
|
||||
(an example is provided in the Appendix below).
|
||||
|
||||
"Derivative Works" shall mean any work, whether in Source or Object
|
||||
form, that is based on (or derived from) the Work and for which the
|
||||
editorial revisions, annotations, elaborations, or other modifications
|
||||
represent, as a whole, an original work of authorship. For the purposes
|
||||
of this License, Derivative Works shall not include works that remain
|
||||
separable from, or merely link (or bind by name) to the interfaces of,
|
||||
the Work and Derivative Works thereof.
|
||||
|
||||
"Contribution" shall mean any work of authorship, including
|
||||
the original version of the Work and any modifications or additions
|
||||
to that Work or Derivative Works thereof, that is intentionally
|
||||
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||
or by an individual or Legal Entity authorized to submit on behalf of
|
||||
the copyright owner. For the purposes of this definition, "submitted"
|
||||
means any form of electronic, verbal, or written communication sent
|
||||
to the Licensor or its representatives, including but not limited to
|
||||
communication on electronic mailing lists, source code control systems,
|
||||
and issue tracking systems that are managed by, or on behalf of, the
|
||||
Licensor for the purpose of discussing and improving the Work, but
|
||||
excluding communication that is conspicuously marked or otherwise
|
||||
designated in writing by the copyright owner as "Not a Contribution."
|
||||
|
||||
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||
on behalf of whom a Contribution has been received by Licensor and
|
||||
subsequently incorporated within the Work.
|
||||
|
||||
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
copyright license to reproduce, prepare Derivative Works of,
|
||||
publicly display, publicly perform, sublicense, and distribute the
|
||||
Work and such Derivative Works in Source or Object form.
|
||||
|
||||
3. Grant of Patent License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
(except as stated in this section) patent license to make, have made,
|
||||
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||
where such license applies only to those patent claims licensable
|
||||
by such Contributor that are necessarily infringed by their
|
||||
Contribution(s) alone or by combination of their Contribution(s)
|
||||
with the Work to which such Contribution(s) was submitted. If You
|
||||
institute patent litigation against any entity (including a
|
||||
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||
or a Contribution incorporated within the Work constitutes direct
|
||||
or contributory patent infringement, then any patent licenses
|
||||
granted to You under this License for that Work shall terminate
|
||||
as of the date such litigation is filed.
|
||||
|
||||
4. Redistribution. You may reproduce and distribute copies of the
|
||||
Work or Derivative Works thereof in any medium, with or without
|
||||
modifications, and in Source or Object form, provided that You
|
||||
meet the following conditions:
|
||||
|
||||
(a) You must give any other recipients of the Work or
|
||||
Derivative Works a copy of this License; and
|
||||
|
||||
(b) You must cause any modified files to carry prominent notices
|
||||
stating that You changed the files; and
|
||||
|
||||
(c) You must retain, in the Source form of any Derivative Works
|
||||
that You distribute, all copyright, patent, trademark, and
|
||||
attribution notices from the Source form of the Work,
|
||||
excluding those notices that do not pertain to any part of
|
||||
the Derivative Works; and
|
||||
|
||||
(d) If the Work includes a "NOTICE" text file as part of its
|
||||
distribution, then any Derivative Works that You distribute must
|
||||
include a readable copy of the attribution notices contained
|
||||
within such NOTICE file, excluding those notices that do not
|
||||
pertain to any part of the Derivative Works, in at least one
|
||||
of the following places: within a NOTICE text file distributed
|
||||
as part of the Derivative Works; within the Source form or
|
||||
documentation, if provided along with the Derivative Works; or,
|
||||
within a display generated by the Derivative Works, if and
|
||||
wherever such third-party notices normally appear. The contents
|
||||
of the NOTICE file are for informational purposes only and
|
||||
do not modify the License. You may add Your own attribution
|
||||
notices within Derivative Works that You distribute, alongside
|
||||
or as an addendum to the NOTICE text from the Work, provided
|
||||
that such additional attribution notices cannot be construed
|
||||
as modifying the License.
|
||||
|
||||
You may add Your own copyright statement to Your modifications and
|
||||
may provide additional or different license terms and conditions
|
||||
for use, reproduction, or distribution of Your modifications, or
|
||||
for any such Derivative Works as a whole, provided Your use,
|
||||
reproduction, and distribution of the Work otherwise complies with
|
||||
the conditions stated in this License.
|
||||
|
||||
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||
any Contribution intentionally submitted for inclusion in the Work
|
||||
by You to the Licensor shall be under the terms and conditions of
|
||||
this License, without any additional terms or conditions.
|
||||
Notwithstanding the above, nothing herein shall supersede or modify
|
||||
the terms of any separate license agreement you may have executed
|
||||
with Licensor regarding such Contributions.
|
||||
|
||||
6. Trademarks. This License does not grant permission to use the trade
|
||||
names, trademarks, service marks, or product names of the Licensor,
|
||||
except as required for reasonable and customary use in describing the
|
||||
origin of the Work and reproducing the content of the NOTICE file.
|
||||
|
||||
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||
agreed to in writing, Licensor provides the Work (and each
|
||||
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||
implied, including, without limitation, any warranties or conditions
|
||||
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||
appropriateness of using or redistributing the Work and assume any
|
||||
risks associated with Your exercise of permissions under this License.
|
||||
|
||||
8. Limitation of Liability. In no event and under no legal theory,
|
||||
whether in tort (including negligence), contract, or otherwise,
|
||||
unless required by applicable law (such as deliberate and grossly
|
||||
negligent acts) or agreed to in writing, shall any Contributor be
|
||||
liable to You for damages, including any direct, indirect, special,
|
||||
incidental, or consequential damages of any character arising as a
|
||||
result of this License or out of the use or inability to use the
|
||||
Work (including but not limited to damages for loss of goodwill,
|
||||
work stoppage, computer failure or malfunction, or any and all
|
||||
other commercial damages or losses), even if such Contributor
|
||||
has been advised of the possibility of such damages.
|
||||
|
||||
9. Accepting Warranty or Additional Liability. While redistributing
|
||||
the Work or Derivative Works thereof, You may choose to offer,
|
||||
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||
or other liability obligations and/or rights consistent with this
|
||||
License. However, in accepting such obligations, You may act only
|
||||
on Your own behalf and on Your sole responsibility, not on behalf
|
||||
of any other Contributor, and only if You agree to indemnify,
|
||||
defend, and hold each Contributor harmless for any liability
|
||||
incurred by, or claims asserted against, such Contributor by reason
|
||||
of your accepting any such warranty or additional liability.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
APPENDIX: How to apply the Apache License to your work.
|
||||
|
||||
To apply the Apache License to your work, attach the following
|
||||
boilerplate notice, with the fields enclosed by brackets "[]"
|
||||
replaced with your own identifying information. (Don't include
|
||||
the brackets!) The text should be enclosed in the appropriate
|
||||
comment syntax for the file format. We also recommend that a
|
||||
file or class name and description of purpose be included on the
|
||||
same "printed page" as the copyright notice for easier
|
||||
identification within third-party archives.
|
||||
|
||||
Copyright 2010-2012 Coda Hale and Yammer, Inc.
|
||||
|
||||
Licensed 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.
|
||||
|
|
@ -0,0 +1,12 @@
|
|||
Metrics
|
||||
Copyright 2010-2013 Coda Hale and Yammer, Inc.
|
||||
|
||||
This product includes software developed by Coda Hale and Yammer, Inc.
|
||||
|
||||
This product includes code derived from the JSR-166 project (ThreadLocalRandom, Striped64,
|
||||
LongAdder), which was released with the following comments:
|
||||
|
||||
Written by Doug Lea with assistance from members of JCP JSR-166
|
||||
Expert Group and released to the public domain, as explained at
|
||||
http://creativecommons.org/publicdomain/zero/1.0/
|
||||
|
|
@ -0,0 +1 @@
|
|||
7f2fe1039424ca687bea5d09ec0bfa372bf7d062
|
|
@ -0,0 +1,203 @@
|
|||
|
||||
Apache License
|
||||
Version 2.0, January 2004
|
||||
http://www.apache.org/licenses/
|
||||
|
||||
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||
|
||||
1. Definitions.
|
||||
|
||||
"License" shall mean the terms and conditions for use, reproduction,
|
||||
and distribution as defined by Sections 1 through 9 of this document.
|
||||
|
||||
"Licensor" shall mean the copyright owner or entity authorized by
|
||||
the copyright owner that is granting the License.
|
||||
|
||||
"Legal Entity" shall mean the union of the acting entity and all
|
||||
other entities that control, are controlled by, or are under common
|
||||
control with that entity. For the purposes of this definition,
|
||||
"control" means (i) the power, direct or indirect, to cause the
|
||||
direction or management of such entity, whether by contract or
|
||||
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||
|
||||
"You" (or "Your") shall mean an individual or Legal Entity
|
||||
exercising permissions granted by this License.
|
||||
|
||||
"Source" form shall mean the preferred form for making modifications,
|
||||
including but not limited to software source code, documentation
|
||||
source, and configuration files.
|
||||
|
||||
"Object" form shall mean any form resulting from mechanical
|
||||
transformation or translation of a Source form, including but
|
||||
not limited to compiled object code, generated documentation,
|
||||
and conversions to other media types.
|
||||
|
||||
"Work" shall mean the work of authorship, whether in Source or
|
||||
Object form, made available under the License, as indicated by a
|
||||
copyright notice that is included in or attached to the work
|
||||
(an example is provided in the Appendix below).
|
||||
|
||||
"Derivative Works" shall mean any work, whether in Source or Object
|
||||
form, that is based on (or derived from) the Work and for which the
|
||||
editorial revisions, annotations, elaborations, or other modifications
|
||||
represent, as a whole, an original work of authorship. For the purposes
|
||||
of this License, Derivative Works shall not include works that remain
|
||||
separable from, or merely link (or bind by name) to the interfaces of,
|
||||
the Work and Derivative Works thereof.
|
||||
|
||||
"Contribution" shall mean any work of authorship, including
|
||||
the original version of the Work and any modifications or additions
|
||||
to that Work or Derivative Works thereof, that is intentionally
|
||||
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||
or by an individual or Legal Entity authorized to submit on behalf of
|
||||
the copyright owner. For the purposes of this definition, "submitted"
|
||||
means any form of electronic, verbal, or written communication sent
|
||||
to the Licensor or its representatives, including but not limited to
|
||||
communication on electronic mailing lists, source code control systems,
|
||||
and issue tracking systems that are managed by, or on behalf of, the
|
||||
Licensor for the purpose of discussing and improving the Work, but
|
||||
excluding communication that is conspicuously marked or otherwise
|
||||
designated in writing by the copyright owner as "Not a Contribution."
|
||||
|
||||
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||
on behalf of whom a Contribution has been received by Licensor and
|
||||
subsequently incorporated within the Work.
|
||||
|
||||
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
copyright license to reproduce, prepare Derivative Works of,
|
||||
publicly display, publicly perform, sublicense, and distribute the
|
||||
Work and such Derivative Works in Source or Object form.
|
||||
|
||||
3. Grant of Patent License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
(except as stated in this section) patent license to make, have made,
|
||||
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||
where such license applies only to those patent claims licensable
|
||||
by such Contributor that are necessarily infringed by their
|
||||
Contribution(s) alone or by combination of their Contribution(s)
|
||||
with the Work to which such Contribution(s) was submitted. If You
|
||||
institute patent litigation against any entity (including a
|
||||
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||
or a Contribution incorporated within the Work constitutes direct
|
||||
or contributory patent infringement, then any patent licenses
|
||||
granted to You under this License for that Work shall terminate
|
||||
as of the date such litigation is filed.
|
||||
|
||||
4. Redistribution. You may reproduce and distribute copies of the
|
||||
Work or Derivative Works thereof in any medium, with or without
|
||||
modifications, and in Source or Object form, provided that You
|
||||
meet the following conditions:
|
||||
|
||||
(a) You must give any other recipients of the Work or
|
||||
Derivative Works a copy of this License; and
|
||||
|
||||
(b) You must cause any modified files to carry prominent notices
|
||||
stating that You changed the files; and
|
||||
|
||||
(c) You must retain, in the Source form of any Derivative Works
|
||||
that You distribute, all copyright, patent, trademark, and
|
||||
attribution notices from the Source form of the Work,
|
||||
excluding those notices that do not pertain to any part of
|
||||
the Derivative Works; and
|
||||
|
||||
(d) If the Work includes a "NOTICE" text file as part of its
|
||||
distribution, then any Derivative Works that You distribute must
|
||||
include a readable copy of the attribution notices contained
|
||||
within such NOTICE file, excluding those notices that do not
|
||||
pertain to any part of the Derivative Works, in at least one
|
||||
of the following places: within a NOTICE text file distributed
|
||||
as part of the Derivative Works; within the Source form or
|
||||
documentation, if provided along with the Derivative Works; or,
|
||||
within a display generated by the Derivative Works, if and
|
||||
wherever such third-party notices normally appear. The contents
|
||||
of the NOTICE file are for informational purposes only and
|
||||
do not modify the License. You may add Your own attribution
|
||||
notices within Derivative Works that You distribute, alongside
|
||||
or as an addendum to the NOTICE text from the Work, provided
|
||||
that such additional attribution notices cannot be construed
|
||||
as modifying the License.
|
||||
|
||||
You may add Your own copyright statement to Your modifications and
|
||||
may provide additional or different license terms and conditions
|
||||
for use, reproduction, or distribution of Your modifications, or
|
||||
for any such Derivative Works as a whole, provided Your use,
|
||||
reproduction, and distribution of the Work otherwise complies with
|
||||
the conditions stated in this License.
|
||||
|
||||
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||
any Contribution intentionally submitted for inclusion in the Work
|
||||
by You to the Licensor shall be under the terms and conditions of
|
||||
this License, without any additional terms or conditions.
|
||||
Notwithstanding the above, nothing herein shall supersede or modify
|
||||
the terms of any separate license agreement you may have executed
|
||||
with Licensor regarding such Contributions.
|
||||
|
||||
6. Trademarks. This License does not grant permission to use the trade
|
||||
names, trademarks, service marks, or product names of the Licensor,
|
||||
except as required for reasonable and customary use in describing the
|
||||
origin of the Work and reproducing the content of the NOTICE file.
|
||||
|
||||
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||
agreed to in writing, Licensor provides the Work (and each
|
||||
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||
implied, including, without limitation, any warranties or conditions
|
||||
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||
appropriateness of using or redistributing the Work and assume any
|
||||
risks associated with Your exercise of permissions under this License.
|
||||
|
||||
8. Limitation of Liability. In no event and under no legal theory,
|
||||
whether in tort (including negligence), contract, or otherwise,
|
||||
unless required by applicable law (such as deliberate and grossly
|
||||
negligent acts) or agreed to in writing, shall any Contributor be
|
||||
liable to You for damages, including any direct, indirect, special,
|
||||
incidental, or consequential damages of any character arising as a
|
||||
result of this License or out of the use or inability to use the
|
||||
Work (including but not limited to damages for loss of goodwill,
|
||||
work stoppage, computer failure or malfunction, or any and all
|
||||
other commercial damages or losses), even if such Contributor
|
||||
has been advised of the possibility of such damages.
|
||||
|
||||
9. Accepting Warranty or Additional Liability. While redistributing
|
||||
the Work or Derivative Works thereof, You may choose to offer,
|
||||
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||
or other liability obligations and/or rights consistent with this
|
||||
License. However, in accepting such obligations, You may act only
|
||||
on Your own behalf and on Your sole responsibility, not on behalf
|
||||
of any other Contributor, and only if You agree to indemnify,
|
||||
defend, and hold each Contributor harmless for any liability
|
||||
incurred by, or claims asserted against, such Contributor by reason
|
||||
of your accepting any such warranty or additional liability.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
APPENDIX: How to apply the Apache License to your work.
|
||||
|
||||
To apply the Apache License to your work, attach the following
|
||||
boilerplate notice, with the fields enclosed by brackets "[]"
|
||||
replaced with your own identifying information. (Don't include
|
||||
the brackets!) The text should be enclosed in the appropriate
|
||||
comment syntax for the file format. We also recommend that a
|
||||
file or class name and description of purpose be included on the
|
||||
same "printed page" as the copyright notice for easier
|
||||
identification within third-party archives.
|
||||
|
||||
Copyright 2010-2012 Coda Hale and Yammer, Inc.
|
||||
|
||||
Licensed 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.
|
||||
|
|
@ -0,0 +1,12 @@
|
|||
Metrics
|
||||
Copyright 2010-2013 Coda Hale and Yammer, Inc.
|
||||
|
||||
This product includes software developed by Coda Hale and Yammer, Inc.
|
||||
|
||||
This product includes code derived from the JSR-166 project (ThreadLocalRandom, Striped64,
|
||||
LongAdder), which was released with the following comments:
|
||||
|
||||
Written by Doug Lea with assistance from members of JCP JSR-166
|
||||
Expert Group and released to the public domain, as explained at
|
||||
http://creativecommons.org/publicdomain/zero/1.0/
|
||||
|
|
@ -0,0 +1 @@
|
|||
ed364e77218e50fdcdebce4d982cb4d1f4a8c187
|
|
@ -0,0 +1,203 @@
|
|||
|
||||
Apache License
|
||||
Version 2.0, January 2004
|
||||
http://www.apache.org/licenses/
|
||||
|
||||
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||
|
||||
1. Definitions.
|
||||
|
||||
"License" shall mean the terms and conditions for use, reproduction,
|
||||
and distribution as defined by Sections 1 through 9 of this document.
|
||||
|
||||
"Licensor" shall mean the copyright owner or entity authorized by
|
||||
the copyright owner that is granting the License.
|
||||
|
||||
"Legal Entity" shall mean the union of the acting entity and all
|
||||
other entities that control, are controlled by, or are under common
|
||||
control with that entity. For the purposes of this definition,
|
||||
"control" means (i) the power, direct or indirect, to cause the
|
||||
direction or management of such entity, whether by contract or
|
||||
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||
|
||||
"You" (or "Your") shall mean an individual or Legal Entity
|
||||
exercising permissions granted by this License.
|
||||
|
||||
"Source" form shall mean the preferred form for making modifications,
|
||||
including but not limited to software source code, documentation
|
||||
source, and configuration files.
|
||||
|
||||
"Object" form shall mean any form resulting from mechanical
|
||||
transformation or translation of a Source form, including but
|
||||
not limited to compiled object code, generated documentation,
|
||||
and conversions to other media types.
|
||||
|
||||
"Work" shall mean the work of authorship, whether in Source or
|
||||
Object form, made available under the License, as indicated by a
|
||||
copyright notice that is included in or attached to the work
|
||||
(an example is provided in the Appendix below).
|
||||
|
||||
"Derivative Works" shall mean any work, whether in Source or Object
|
||||
form, that is based on (or derived from) the Work and for which the
|
||||
editorial revisions, annotations, elaborations, or other modifications
|
||||
represent, as a whole, an original work of authorship. For the purposes
|
||||
of this License, Derivative Works shall not include works that remain
|
||||
separable from, or merely link (or bind by name) to the interfaces of,
|
||||
the Work and Derivative Works thereof.
|
||||
|
||||
"Contribution" shall mean any work of authorship, including
|
||||
the original version of the Work and any modifications or additions
|
||||
to that Work or Derivative Works thereof, that is intentionally
|
||||
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||
or by an individual or Legal Entity authorized to submit on behalf of
|
||||
the copyright owner. For the purposes of this definition, "submitted"
|
||||
means any form of electronic, verbal, or written communication sent
|
||||
to the Licensor or its representatives, including but not limited to
|
||||
communication on electronic mailing lists, source code control systems,
|
||||
and issue tracking systems that are managed by, or on behalf of, the
|
||||
Licensor for the purpose of discussing and improving the Work, but
|
||||
excluding communication that is conspicuously marked or otherwise
|
||||
designated in writing by the copyright owner as "Not a Contribution."
|
||||
|
||||
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||
on behalf of whom a Contribution has been received by Licensor and
|
||||
subsequently incorporated within the Work.
|
||||
|
||||
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
copyright license to reproduce, prepare Derivative Works of,
|
||||
publicly display, publicly perform, sublicense, and distribute the
|
||||
Work and such Derivative Works in Source or Object form.
|
||||
|
||||
3. Grant of Patent License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
(except as stated in this section) patent license to make, have made,
|
||||
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||
where such license applies only to those patent claims licensable
|
||||
by such Contributor that are necessarily infringed by their
|
||||
Contribution(s) alone or by combination of their Contribution(s)
|
||||
with the Work to which such Contribution(s) was submitted. If You
|
||||
institute patent litigation against any entity (including a
|
||||
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||
or a Contribution incorporated within the Work constitutes direct
|
||||
or contributory patent infringement, then any patent licenses
|
||||
granted to You under this License for that Work shall terminate
|
||||
as of the date such litigation is filed.
|
||||
|
||||
4. Redistribution. You may reproduce and distribute copies of the
|
||||
Work or Derivative Works thereof in any medium, with or without
|
||||
modifications, and in Source or Object form, provided that You
|
||||
meet the following conditions:
|
||||
|
||||
(a) You must give any other recipients of the Work or
|
||||
Derivative Works a copy of this License; and
|
||||
|
||||
(b) You must cause any modified files to carry prominent notices
|
||||
stating that You changed the files; and
|
||||
|
||||
(c) You must retain, in the Source form of any Derivative Works
|
||||
that You distribute, all copyright, patent, trademark, and
|
||||
attribution notices from the Source form of the Work,
|
||||
excluding those notices that do not pertain to any part of
|
||||
the Derivative Works; and
|
||||
|
||||
(d) If the Work includes a "NOTICE" text file as part of its
|
||||
distribution, then any Derivative Works that You distribute must
|
||||
include a readable copy of the attribution notices contained
|
||||
within such NOTICE file, excluding those notices that do not
|
||||
pertain to any part of the Derivative Works, in at least one
|
||||
of the following places: within a NOTICE text file distributed
|
||||
as part of the Derivative Works; within the Source form or
|
||||
documentation, if provided along with the Derivative Works; or,
|
||||
within a display generated by the Derivative Works, if and
|
||||
wherever such third-party notices normally appear. The contents
|
||||
of the NOTICE file are for informational purposes only and
|
||||
do not modify the License. You may add Your own attribution
|
||||
notices within Derivative Works that You distribute, alongside
|
||||
or as an addendum to the NOTICE text from the Work, provided
|
||||
that such additional attribution notices cannot be construed
|
||||
as modifying the License.
|
||||
|
||||
You may add Your own copyright statement to Your modifications and
|
||||
may provide additional or different license terms and conditions
|
||||
for use, reproduction, or distribution of Your modifications, or
|
||||
for any such Derivative Works as a whole, provided Your use,
|
||||
reproduction, and distribution of the Work otherwise complies with
|
||||
the conditions stated in this License.
|
||||
|
||||
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||
any Contribution intentionally submitted for inclusion in the Work
|
||||
by You to the Licensor shall be under the terms and conditions of
|
||||
this License, without any additional terms or conditions.
|
||||
Notwithstanding the above, nothing herein shall supersede or modify
|
||||
the terms of any separate license agreement you may have executed
|
||||
with Licensor regarding such Contributions.
|
||||
|
||||
6. Trademarks. This License does not grant permission to use the trade
|
||||
names, trademarks, service marks, or product names of the Licensor,
|
||||
except as required for reasonable and customary use in describing the
|
||||
origin of the Work and reproducing the content of the NOTICE file.
|
||||
|
||||
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||
agreed to in writing, Licensor provides the Work (and each
|
||||
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||
implied, including, without limitation, any warranties or conditions
|
||||
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||
appropriateness of using or redistributing the Work and assume any
|
||||
risks associated with Your exercise of permissions under this License.
|
||||
|
||||
8. Limitation of Liability. In no event and under no legal theory,
|
||||
whether in tort (including negligence), contract, or otherwise,
|
||||
unless required by applicable law (such as deliberate and grossly
|
||||
negligent acts) or agreed to in writing, shall any Contributor be
|
||||
liable to You for damages, including any direct, indirect, special,
|
||||
incidental, or consequential damages of any character arising as a
|
||||
result of this License or out of the use or inability to use the
|
||||
Work (including but not limited to damages for loss of goodwill,
|
||||
work stoppage, computer failure or malfunction, or any and all
|
||||
other commercial damages or losses), even if such Contributor
|
||||
has been advised of the possibility of such damages.
|
||||
|
||||
9. Accepting Warranty or Additional Liability. While redistributing
|
||||
the Work or Derivative Works thereof, You may choose to offer,
|
||||
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||
or other liability obligations and/or rights consistent with this
|
||||
License. However, in accepting such obligations, You may act only
|
||||
on Your own behalf and on Your sole responsibility, not on behalf
|
||||
of any other Contributor, and only if You agree to indemnify,
|
||||
defend, and hold each Contributor harmless for any liability
|
||||
incurred by, or claims asserted against, such Contributor by reason
|
||||
of your accepting any such warranty or additional liability.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
APPENDIX: How to apply the Apache License to your work.
|
||||
|
||||
To apply the Apache License to your work, attach the following
|
||||
boilerplate notice, with the fields enclosed by brackets "[]"
|
||||
replaced with your own identifying information. (Don't include
|
||||
the brackets!) The text should be enclosed in the appropriate
|
||||
comment syntax for the file format. We also recommend that a
|
||||
file or class name and description of purpose be included on the
|
||||
same "printed page" as the copyright notice for easier
|
||||
identification within third-party archives.
|
||||
|
||||
Copyright 2010-2012 Coda Hale and Yammer, Inc.
|
||||
|
||||
Licensed 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.
|
||||
|
|
@ -0,0 +1,12 @@
|
|||
Metrics
|
||||
Copyright 2010-2013 Coda Hale and Yammer, Inc.
|
||||
|
||||
This product includes software developed by Coda Hale and Yammer, Inc.
|
||||
|
||||
This product includes code derived from the JSR-166 project (ThreadLocalRandom, Striped64,
|
||||
LongAdder), which was released with the following comments:
|
||||
|
||||
Written by Doug Lea with assistance from members of JCP JSR-166
|
||||
Expert Group and released to the public domain, as explained at
|
||||
http://creativecommons.org/publicdomain/zero/1.0/
|
||||
|
|
@ -0,0 +1,203 @@
|
|||
|
||||
Apache License
|
||||
Version 2.0, January 2004
|
||||
http://www.apache.org/licenses/
|
||||
|
||||
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||
|
||||
1. Definitions.
|
||||
|
||||
"License" shall mean the terms and conditions for use, reproduction,
|
||||
and distribution as defined by Sections 1 through 9 of this document.
|
||||
|
||||
"Licensor" shall mean the copyright owner or entity authorized by
|
||||
the copyright owner that is granting the License.
|
||||
|
||||
"Legal Entity" shall mean the union of the acting entity and all
|
||||
other entities that control, are controlled by, or are under common
|
||||
control with that entity. For the purposes of this definition,
|
||||
"control" means (i) the power, direct or indirect, to cause the
|
||||
direction or management of such entity, whether by contract or
|
||||
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||
|
||||
"You" (or "Your") shall mean an individual or Legal Entity
|
||||
exercising permissions granted by this License.
|
||||
|
||||
"Source" form shall mean the preferred form for making modifications,
|
||||
including but not limited to software source code, documentation
|
||||
source, and configuration files.
|
||||
|
||||
"Object" form shall mean any form resulting from mechanical
|
||||
transformation or translation of a Source form, including but
|
||||
not limited to compiled object code, generated documentation,
|
||||
and conversions to other media types.
|
||||
|
||||
"Work" shall mean the work of authorship, whether in Source or
|
||||
Object form, made available under the License, as indicated by a
|
||||
copyright notice that is included in or attached to the work
|
||||
(an example is provided in the Appendix below).
|
||||
|
||||
"Derivative Works" shall mean any work, whether in Source or Object
|
||||
form, that is based on (or derived from) the Work and for which the
|
||||
editorial revisions, annotations, elaborations, or other modifications
|
||||
represent, as a whole, an original work of authorship. For the purposes
|
||||
of this License, Derivative Works shall not include works that remain
|
||||
separable from, or merely link (or bind by name) to the interfaces of,
|
||||
the Work and Derivative Works thereof.
|
||||
|
||||
"Contribution" shall mean any work of authorship, including
|
||||
the original version of the Work and any modifications or additions
|
||||
to that Work or Derivative Works thereof, that is intentionally
|
||||
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||
or by an individual or Legal Entity authorized to submit on behalf of
|
||||
the copyright owner. For the purposes of this definition, "submitted"
|
||||
means any form of electronic, verbal, or written communication sent
|
||||
to the Licensor or its representatives, including but not limited to
|
||||
communication on electronic mailing lists, source code control systems,
|
||||
and issue tracking systems that are managed by, or on behalf of, the
|
||||
Licensor for the purpose of discussing and improving the Work, but
|
||||
excluding communication that is conspicuously marked or otherwise
|
||||
designated in writing by the copyright owner as "Not a Contribution."
|
||||
|
||||
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||
on behalf of whom a Contribution has been received by Licensor and
|
||||
subsequently incorporated within the Work.
|
||||
|
||||
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
copyright license to reproduce, prepare Derivative Works of,
|
||||
publicly display, publicly perform, sublicense, and distribute the
|
||||
Work and such Derivative Works in Source or Object form.
|
||||
|
||||
3. Grant of Patent License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
(except as stated in this section) patent license to make, have made,
|
||||
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||
where such license applies only to those patent claims licensable
|
||||
by such Contributor that are necessarily infringed by their
|
||||
Contribution(s) alone or by combination of their Contribution(s)
|
||||
with the Work to which such Contribution(s) was submitted. If You
|
||||
institute patent litigation against any entity (including a
|
||||
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||
or a Contribution incorporated within the Work constitutes direct
|
||||
or contributory patent infringement, then any patent licenses
|
||||
granted to You under this License for that Work shall terminate
|
||||
as of the date such litigation is filed.
|
||||
|
||||
4. Redistribution. You may reproduce and distribute copies of the
|
||||
Work or Derivative Works thereof in any medium, with or without
|
||||
modifications, and in Source or Object form, provided that You
|
||||
meet the following conditions:
|
||||
|
||||
(a) You must give any other recipients of the Work or
|
||||
Derivative Works a copy of this License; and
|
||||
|
||||
(b) You must cause any modified files to carry prominent notices
|
||||
stating that You changed the files; and
|
||||
|
||||
(c) You must retain, in the Source form of any Derivative Works
|
||||
that You distribute, all copyright, patent, trademark, and
|
||||
attribution notices from the Source form of the Work,
|
||||
excluding those notices that do not pertain to any part of
|
||||
the Derivative Works; and
|
||||
|
||||
(d) If the Work includes a "NOTICE" text file as part of its
|
||||
distribution, then any Derivative Works that You distribute must
|
||||
include a readable copy of the attribution notices contained
|
||||
within such NOTICE file, excluding those notices that do not
|
||||
pertain to any part of the Derivative Works, in at least one
|
||||
of the following places: within a NOTICE text file distributed
|
||||
as part of the Derivative Works; within the Source form or
|
||||
documentation, if provided along with the Derivative Works; or,
|
||||
within a display generated by the Derivative Works, if and
|
||||
wherever such third-party notices normally appear. The contents
|
||||
of the NOTICE file are for informational purposes only and
|
||||
do not modify the License. You may add Your own attribution
|
||||
notices within Derivative Works that You distribute, alongside
|
||||
or as an addendum to the NOTICE text from the Work, provided
|
||||
that such additional attribution notices cannot be construed
|
||||
as modifying the License.
|
||||
|
||||
You may add Your own copyright statement to Your modifications and
|
||||
may provide additional or different license terms and conditions
|
||||
for use, reproduction, or distribution of Your modifications, or
|
||||
for any such Derivative Works as a whole, provided Your use,
|
||||
reproduction, and distribution of the Work otherwise complies with
|
||||
the conditions stated in this License.
|
||||
|
||||
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||
any Contribution intentionally submitted for inclusion in the Work
|
||||
by You to the Licensor shall be under the terms and conditions of
|
||||
this License, without any additional terms or conditions.
|
||||
Notwithstanding the above, nothing herein shall supersede or modify
|
||||
the terms of any separate license agreement you may have executed
|
||||
with Licensor regarding such Contributions.
|
||||
|
||||
6. Trademarks. This License does not grant permission to use the trade
|
||||
names, trademarks, service marks, or product names of the Licensor,
|
||||
except as required for reasonable and customary use in describing the
|
||||
origin of the Work and reproducing the content of the NOTICE file.
|
||||
|
||||
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||
agreed to in writing, Licensor provides the Work (and each
|
||||
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||
implied, including, without limitation, any warranties or conditions
|
||||
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||
appropriateness of using or redistributing the Work and assume any
|
||||
risks associated with Your exercise of permissions under this License.
|
||||
|
||||
8. Limitation of Liability. In no event and under no legal theory,
|
||||
whether in tort (including negligence), contract, or otherwise,
|
||||
unless required by applicable law (such as deliberate and grossly
|
||||
negligent acts) or agreed to in writing, shall any Contributor be
|
||||
liable to You for damages, including any direct, indirect, special,
|
||||
incidental, or consequential damages of any character arising as a
|
||||
result of this License or out of the use or inability to use the
|
||||
Work (including but not limited to damages for loss of goodwill,
|
||||
work stoppage, computer failure or malfunction, or any and all
|
||||
other commercial damages or losses), even if such Contributor
|
||||
has been advised of the possibility of such damages.
|
||||
|
||||
9. Accepting Warranty or Additional Liability. While redistributing
|
||||
the Work or Derivative Works thereof, You may choose to offer,
|
||||
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||
or other liability obligations and/or rights consistent with this
|
||||
License. However, in accepting such obligations, You may act only
|
||||
on Your own behalf and on Your sole responsibility, not on behalf
|
||||
of any other Contributor, and only if You agree to indemnify,
|
||||
defend, and hold each Contributor harmless for any liability
|
||||
incurred by, or claims asserted against, such Contributor by reason
|
||||
of your accepting any such warranty or additional liability.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
APPENDIX: How to apply the Apache License to your work.
|
||||
|
||||
To apply the Apache License to your work, attach the following
|
||||
boilerplate notice, with the fields enclosed by brackets "[]"
|
||||
replaced with your own identifying information. (Don't include
|
||||
the brackets!) The text should be enclosed in the appropriate
|
||||
comment syntax for the file format. We also recommend that a
|
||||
file or class name and description of purpose be included on the
|
||||
same "printed page" as the copyright notice for easier
|
||||
identification within third-party archives.
|
||||
|
||||
Copyright 2010-2012 Coda Hale and Yammer, Inc.
|
||||
|
||||
Licensed 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.
|
||||
|
|
@ -0,0 +1,12 @@
|
|||
Metrics
|
||||
Copyright 2010-2013 Coda Hale and Yammer, Inc.
|
||||
|
||||
This product includes software developed by Coda Hale and Yammer, Inc.
|
||||
|
||||
This product includes code derived from the JSR-166 project (ThreadLocalRandom, Striped64,
|
||||
LongAdder), which was released with the following comments:
|
||||
|
||||
Written by Doug Lea with assistance from members of JCP JSR-166
|
||||
Expert Group and released to the public domain, as explained at
|
||||
http://creativecommons.org/publicdomain/zero/1.0/
|
||||
|
|
@ -39,8 +39,8 @@
|
|||
<target name="resolve" depends="ivy-availability-check,ivy-fail,ivy-configure">
|
||||
<sequential>
|
||||
<!-- jetty libs in lib/ -->
|
||||
<ivy:retrieve conf="jetty,servlet" type="jar" log="download-only" symlink="${ivy.symlink}"
|
||||
pattern="lib/[artifact]-[revision].[ext]" sync="${ivy.sync}"/>
|
||||
<ivy:retrieve conf="jetty,servlet,metrics" type="jar,bundle" log="download-only" symlink="${ivy.symlink}"
|
||||
pattern="lib/[artifact]-[revision].[ext]" sync="${ivy.sync}"/>
|
||||
<ivy:retrieve conf="logging" type="jar,bundle" log="download-only" symlink="${ivy.symlink}"
|
||||
pattern="lib/ext/[artifact]-[revision].[ext]" sync="${ivy.sync}"/>
|
||||
<!-- start.jar - we don't use sync=true here, we don't own the dir, but
|
||||
|
|
|
@ -29,9 +29,16 @@
|
|||
<!-- Consult the javadoc of o.e.j.util.thread.QueuedThreadPool -->
|
||||
<!-- for all configuration that may be set here. -->
|
||||
<!-- =========================================================== -->
|
||||
<!-- uncomment to change type of threadpool
|
||||
<Arg name="threadpool"><New id="threadpool" class="org.eclipse.jetty.util.thread.QueuedThreadPool"/></Arg>
|
||||
-->
|
||||
<Arg name="threadpool">
|
||||
<New id="threadpool" class="com.codahale.metrics.jetty9.InstrumentedQueuedThreadPool">
|
||||
<Arg name="registry">
|
||||
<Call id="solrJettyMetricRegistry" name="getOrCreate" class="com.codahale.metrics.SharedMetricRegistries">
|
||||
<Arg>solr.jetty</Arg>
|
||||
</Call>
|
||||
</Arg>
|
||||
</New>
|
||||
</Arg>
|
||||
|
||||
<Get name="ThreadPool">
|
||||
<Set name="minThreads" type="int"><Property name="solr.jetty.threads.min" default="10"/></Set>
|
||||
<Set name="maxThreads" type="int"><Property name="solr.jetty.threads.max" default="10000"/></Set>
|
||||
|
@ -106,7 +113,12 @@
|
|||
<New id="Contexts" class="org.eclipse.jetty.server.handler.ContextHandlerCollection"/>
|
||||
</Item>
|
||||
<Item>
|
||||
<New id="DefaultHandler" class="org.eclipse.jetty.server.handler.DefaultHandler"/>
|
||||
<New id="InstrumentedHandler" class="com.codahale.metrics.jetty9.InstrumentedHandler">
|
||||
<Arg><Ref refid="solrJettyMetricRegistry"/></Arg>
|
||||
<Set name="handler">
|
||||
<New id="DefaultHandler" class="org.eclipse.jetty.server.handler.DefaultHandler"/>
|
||||
</Set>
|
||||
</New>
|
||||
</Item>
|
||||
<Item>
|
||||
<New id="RequestLog" class="org.eclipse.jetty.server.handler.RequestLogHandler"/>
|
||||
|
|
|
@ -18,7 +18,8 @@
|
|||
-->
|
||||
<ivy-module version="2.0">
|
||||
<info organisation="org.apache.solr" module="server"/>
|
||||
<configurations defaultconfmapping="jetty->master;start->master;servlet->master;logging->master">
|
||||
<configurations defaultconfmapping="metrics->master;jetty->master;start->master;servlet->master;logging->master">
|
||||
<conf name="metrics" description="metrics jars" transitive="true"/>
|
||||
<conf name="jetty" description="jetty jars" transitive="false"/>
|
||||
<conf name="start" description="jetty start jar" transitive="false"/>
|
||||
<conf name="servlet" description="servlet-api jar" transitive="false"/>
|
||||
|
@ -30,8 +31,12 @@
|
|||
<dependency org="org.slf4j" name="slf4j-api" rev="${/org.slf4j/slf4j-api}" conf="logging"/>
|
||||
<dependency org="org.slf4j" name="jcl-over-slf4j" rev="${/org.slf4j/jcl-over-slf4j}" conf="logging"/>
|
||||
<dependency org="org.slf4j" name="jul-to-slf4j" rev="${/org.slf4j/jul-to-slf4j}" conf="logging"/>
|
||||
<dependency org="org.slf4j" name="slf4j-log4j12" rev="${/org.slf4j/slf4j-log4j12}" conf="logging"/>
|
||||
|
||||
<dependency org="org.slf4j" name="slf4j-log4j12" rev="${/org.slf4j/slf4j-log4j12}" conf="logging"/>
|
||||
|
||||
<dependency org="io.dropwizard.metrics" name="metrics-core" rev="${/io.dropwizard.metrics/metrics-core}" conf="metrics" />
|
||||
<dependency org="io.dropwizard.metrics" name="metrics-jetty9" rev="${/io.dropwizard.metrics/metrics-jetty9}" conf="metrics" />
|
||||
<dependency org="io.dropwizard.metrics" name="metrics-jvm" rev="${/io.dropwizard.metrics/metrics-jvm}" conf="metrics" />
|
||||
|
||||
<dependency org="org.eclipse.jetty" name="jetty-continuation" rev="${/org.eclipse.jetty/jetty-continuation}" conf="jetty"/>
|
||||
<dependency org="org.eclipse.jetty" name="jetty-deploy" rev="${/org.eclipse.jetty/jetty-deploy}" conf="jetty"/>
|
||||
<dependency org="org.eclipse.jetty" name="jetty-http" rev="${/org.eclipse.jetty/jetty-http}" conf="jetty"/>
|
||||
|
|
|
@ -180,13 +180,15 @@ public interface CommonParams {
|
|||
String AUTHZ_PATH = "/admin/authorization";
|
||||
String AUTHC_PATH = "/admin/authentication";
|
||||
String ZK_PATH = "/admin/zookeeper";
|
||||
String METRICS_PATH = "/admin/metrics";
|
||||
|
||||
Set<String> ADMIN_PATHS = new HashSet<>(Arrays.asList(
|
||||
CORES_HANDLER_PATH,
|
||||
COLLECTIONS_HANDLER_PATH,
|
||||
CONFIGSETS_HANDLER_PATH,
|
||||
AUTHC_PATH,
|
||||
AUTHZ_PATH));
|
||||
AUTHZ_PATH,
|
||||
METRICS_PATH));
|
||||
|
||||
/** valid values for: <code>echoParams</code> */
|
||||
enum EchoParamStyle {
|
||||
|
|
|
@ -37,11 +37,13 @@ import org.apache.solr.core.CoreDescriptor;
|
|||
import org.apache.solr.core.CorePropertiesLocator;
|
||||
import org.apache.solr.core.CoresLocator;
|
||||
import org.apache.solr.core.NodeConfig;
|
||||
import org.apache.solr.core.PluginInfo;
|
||||
import org.apache.solr.core.SolrConfig;
|
||||
import org.apache.solr.core.SolrCore;
|
||||
import org.apache.solr.core.SolrResourceLoader;
|
||||
import org.apache.solr.core.SolrXmlConfig;
|
||||
import org.apache.solr.handler.UpdateRequestHandler;
|
||||
import org.apache.solr.metrics.reporters.SolrJmxReporter;
|
||||
import org.apache.solr.request.LocalSolrQueryRequest;
|
||||
import org.apache.solr.request.SolrQueryRequest;
|
||||
import org.apache.solr.request.SolrRequestHandler;
|
||||
|
@ -189,10 +191,17 @@ public class TestHarness extends BaseTestHarness {
|
|||
= new UpdateShardHandlerConfig(UpdateShardHandlerConfig.DEFAULT_MAXUPDATECONNECTIONS,
|
||||
UpdateShardHandlerConfig.DEFAULT_MAXUPDATECONNECTIONSPERHOST,
|
||||
30000, 30000);
|
||||
// universal default metric reporter
|
||||
Map<String,String> attributes = new HashMap<>();
|
||||
attributes.put("name", "default");
|
||||
attributes.put("class", SolrJmxReporter.class.getName());
|
||||
PluginInfo defaultPlugin = new PluginInfo("reporter", attributes, null, null);
|
||||
|
||||
return new NodeConfig.NodeConfigBuilder("testNode", loader)
|
||||
.setUseSchemaCache(Boolean.getBoolean("shareSchema"))
|
||||
.setCloudConfig(cloudConfig)
|
||||
.setUpdateShardHandlerConfig(updateShardHandlerConfig)
|
||||
.setMetricReporterPlugins(new PluginInfo[] {defaultPlugin})
|
||||
.build();
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue