You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@lucene.apache.org by ab...@apache.org on 2020/10/15 11:45:06 UTC

[lucene-solr] branch branch_8x updated: SOLR-14914: Add option to disable metrics collection.

This is an automated email from the ASF dual-hosted git repository.

ab pushed a commit to branch branch_8x
in repository https://gitbox.apache.org/repos/asf/lucene-solr.git


The following commit(s) were added to refs/heads/branch_8x by this push:
     new 766aaf1  SOLR-14914: Add option to disable metrics collection.
766aaf1 is described below

commit 766aaf1b2772a2bd96c89a546509a531f4dc08d5
Author: Andrzej Bialecki <ab...@apache.org>
AuthorDate: Thu Oct 15 13:44:30 2020 +0200

    SOLR-14914: Add option to disable metrics collection.
---
 solr/CHANGES.txt                                   |   2 +
 .../prometheus/PrometheusExporterTestBase.java     |   1 +
 .../solr/cloud/api/collections/SplitShardCmd.java  |   9 +-
 .../java/org/apache/solr/core/CoreContainer.java   |  12 +-
 .../java/org/apache/solr/core/MetricsConfig.java   |  71 +++++-
 .../java/org/apache/solr/core/SolrXmlConfig.java   |  13 +-
 .../apache/solr/handler/admin/MetricsHandler.java  |  18 +-
 .../solr/handler/admin/MetricsHistoryHandler.java  |  15 +-
 .../org/apache/solr/metrics/MetricSuppliers.java   | 267 ++++++++++++++++++---
 .../org/apache/solr/metrics/SolrMetricManager.java |  14 +-
 .../src/test-files/solr/solr-metricsconfig.xml     |   2 +-
 solr/core/src/test-files/solr/solr.xml             |   2 +
 .../apache/solr/cloud/CollectionsAPISolrJTest.java |   1 +
 .../solr/cloud/MetricsHistoryIntegrationTest.java  |   1 +
 .../MetricsHistoryWithAuthIntegrationTest.java     |   2 +-
 .../cloud/RoutingToNodesWithPropertiesTest.java    |   1 +
 .../test/org/apache/solr/cloud/SplitShardTest.java |   1 +
 .../org/apache/solr/cloud/TestCloudRecovery.java   |   1 +
 .../org/apache/solr/cloud/TestTlogReplica.java     |   1 +
 .../org/apache/solr/cloud/TestUtilizeNode.java     |   1 +
 .../org/apache/solr/cloud/TestWithCollection.java  |   1 +
 .../ConcurrentCreateCollectionTest.java            |   1 +
 .../AutoAddReplicasIntegrationTest.java            |   1 +
 .../autoscaling/AutoAddReplicasPlanActionTest.java |   1 +
 .../cloud/autoscaling/AutoScalingHandlerTest.java  |   1 +
 .../cloud/autoscaling/ComputePlanActionTest.java   |   1 +
 .../cloud/autoscaling/ExecutePlanActionTest.java   |   2 +-
 .../HdfsAutoAddReplicasIntegrationTest.java        |   1 +
 .../cloud/autoscaling/HttpTriggerListenerTest.java |   1 +
 .../IndexSizeTriggerMixedBoundsTest.java           |   1 +
 .../IndexSizeTriggerSizeEstimationTest.java        |   1 +
 .../cloud/autoscaling/IndexSizeTriggerTest.java    |   1 +
 .../autoscaling/MetricTriggerIntegrationTest.java  |   1 +
 .../solr/cloud/autoscaling/MetricTriggerTest.java  |   1 +
 .../NodeAddedTriggerIntegrationTest.java           |   1 +
 .../cloud/autoscaling/NodeAddedTriggerTest.java    |   1 +
 .../NodeLostTriggerIntegrationTest.java            |   3 +-
 .../cloud/autoscaling/NodeLostTriggerTest.java     |   3 +-
 .../autoscaling/NodeMarkersRegistrationTest.java   |   1 +
 .../cloud/autoscaling/RestoreTriggerStateTest.java |   1 +
 .../ScheduledMaintenanceTriggerTest.java           |   1 +
 .../ScheduledTriggerIntegrationTest.java           |   1 +
 .../cloud/autoscaling/ScheduledTriggerTest.java    |   1 +
 .../SearchRateTriggerIntegrationTest.java          |   1 +
 .../cloud/autoscaling/SearchRateTriggerTest.java   |   1 +
 .../cloud/autoscaling/SystemLogListenerTest.java   |   1 +
 .../solr/cloud/autoscaling/TestPolicyCloud.java    |   1 +
 .../TriggerCooldownIntegrationTest.java            |   1 +
 .../cloud/autoscaling/TriggerIntegrationTest.java  |   1 +
 .../TriggerSetPropertiesIntegrationTest.java       |   1 +
 .../sim/TestSimClusterStateProvider.java           |   1 +
 .../autoscaling/sim/TestSnapshotCloudManager.java  |   1 +
 .../test/org/apache/solr/cloud/rule/RulesTest.java |   1 +
 .../solr/handler/admin/AdminHandlersProxyTest.java |   1 +
 .../admin/AutoscalingHistoryHandlerTest.java       |   1 +
 .../handler/admin/MetricsHistoryHandlerTest.java   |   1 +
 .../org/apache/solr/metrics/MetricsConfigTest.java |  12 +
 .../solr/metrics/MetricsDisabledCloudTest.java     |  72 ++++++
 .../reporters/SolrJmxReporterCloudTest.java        |   1 +
 .../reporters/solr/SolrCloudReportersTest.java     |   1 +
 .../solr/search/stats/TestDefaultStatsCache.java   |   1 +
 solr/server/solr/solr.xml                          |   2 +
 solr/solr-ref-guide/src/metrics-history.adoc       |   3 +
 solr/solr-ref-guide/src/metrics-reporting.adoc     |  15 ++
 .../client/solrj/cloud/autoscaling/Policy.java     |   2 +-
 .../solrj/impl/SolrClientNodeStateProvider.java    |  48 ++--
 .../solrj/impl/CloudHttp2SolrClientRetryTest.java  |   1 +
 .../solrj/impl/CloudHttp2SolrClientTest.java       |   1 +
 .../solrj/impl/CloudSolrClientRetryTest.java       |   1 +
 .../client/solrj/impl/CloudSolrClientTest.java     |   1 +
 .../apache/solr/cloud/MiniSolrCloudCluster.java    |   3 +-
 .../apache/solr/cloud/SolrCloudAuthTestCase.java   |  12 +
 72 files changed, 555 insertions(+), 97 deletions(-)

diff --git a/solr/CHANGES.txt b/solr/CHANGES.txt
index 235e12e..d510e70 100644
--- a/solr/CHANGES.txt
+++ b/solr/CHANGES.txt
@@ -40,6 +40,8 @@ New Features
 
 * SOLR-14907: Support single file upload/overwrite in configSet API. (Houston Putman)
 
+* SOLR-14914: Add option to disable metrics collection. (ab)
+
 Improvements
 ---------------------
 
diff --git a/solr/contrib/prometheus-exporter/src/test/org/apache/solr/prometheus/PrometheusExporterTestBase.java b/solr/contrib/prometheus-exporter/src/test/org/apache/solr/prometheus/PrometheusExporterTestBase.java
index f0b9d2c..c63cb42 100644
--- a/solr/contrib/prometheus-exporter/src/test/org/apache/solr/prometheus/PrometheusExporterTestBase.java
+++ b/solr/contrib/prometheus-exporter/src/test/org/apache/solr/prometheus/PrometheusExporterTestBase.java
@@ -60,6 +60,7 @@ public class PrometheusExporterTestBase extends SolrCloudTestCase {
 
   @BeforeClass
   public static void setupCluster() throws Exception {
+    System.setProperty("metricsEnabled", "true");
     configureCluster(NUM_NODES)
         .addConfig(CONF_NAME, getFile(CONF_DIR).toPath())
         .configure();
diff --git a/solr/core/src/java/org/apache/solr/cloud/api/collections/SplitShardCmd.java b/solr/core/src/java/org/apache/solr/cloud/api/collections/SplitShardCmd.java
index 4dd2d70..ecf9fed 100644
--- a/solr/core/src/java/org/apache/solr/cloud/api/collections/SplitShardCmd.java
+++ b/solr/core/src/java/org/apache/solr/cloud/api/collections/SplitShardCmd.java
@@ -153,9 +153,12 @@ public class SplitShardCmd implements OverseerCollectionMessageHandler.Cmd {
       throw new SolrException(SolrException.ErrorCode.SERVER_ERROR, "Interrupted.");
     }
 
-    RTimerTree t = timings.sub("checkDiskSpace");
-    checkDiskSpace(collectionName, slice.get(), parentShardLeader, splitMethod, ocmh.cloudManager);
-    t.stop();
+    RTimerTree t;
+    if (ocmh.overseer.getCoreContainer().getNodeConfig().getMetricsConfig().isEnabled()) {
+      t = timings.sub("checkDiskSpace");
+      checkDiskSpace(collectionName, slice.get(), parentShardLeader, splitMethod, ocmh.cloudManager);
+      t.stop();
+    }
 
     // let's record the ephemeralOwner of the parent leader node
     Stat leaderZnodeStat = zkStateReader.getZkClient().exists(ZkStateReader.LIVE_NODES_ZKNODE + "/" + parentShardLeader.getNodeName(), null, true);
diff --git a/solr/core/src/java/org/apache/solr/core/CoreContainer.java b/solr/core/src/java/org/apache/solr/core/CoreContainer.java
index a5e705b..0c08b71 100644
--- a/solr/core/src/java/org/apache/solr/core/CoreContainer.java
+++ b/solr/core/src/java/org/apache/solr/core/CoreContainer.java
@@ -748,9 +748,11 @@ public class CoreContainer {
     createMetricsHistoryHandler();
 
     autoscalingHistoryHandler = createHandler(AUTOSCALING_HISTORY_PATH, AutoscalingHistoryHandler.class.getName(), AutoscalingHistoryHandler.class);
-    metricsCollectorHandler = createHandler(MetricsCollectorHandler.HANDLER_PATH, MetricsCollectorHandler.class.getName(), MetricsCollectorHandler.class);
-    // may want to add some configuration here in the future
-    metricsCollectorHandler.init(null);
+    if (cfg.getMetricsConfig().isEnabled()) {
+      metricsCollectorHandler = createHandler(MetricsCollectorHandler.HANDLER_PATH, MetricsCollectorHandler.class.getName(), MetricsCollectorHandler.class);
+      // may want to add some configuration here in the future
+      metricsCollectorHandler.init(null);
+    }
 
     containerHandlers.put(AUTHZ_PATH, securityConfHandler);
     securityConfHandler.initializeMetrics(solrMetricsContext, AUTHZ_PATH);
@@ -912,6 +914,10 @@ public class CoreContainer {
   @SuppressWarnings({"unchecked"})
   private void createMetricsHistoryHandler() {
     PluginInfo plugin = cfg.getMetricsConfig().getHistoryHandler();
+    if (plugin != null && MetricsConfig.NOOP_IMPL_CLASS.equals(plugin.className)) {
+      // still create the handler but it will be disabled
+      plugin = null;
+    }
     Map<String, Object> initArgs;
     if (plugin != null && plugin.initArgs != null) {
       initArgs = plugin.initArgs.asMap(5);
diff --git a/solr/core/src/java/org/apache/solr/core/MetricsConfig.java b/solr/core/src/java/org/apache/solr/core/MetricsConfig.java
index fab2553..cdaa56d 100644
--- a/solr/core/src/java/org/apache/solr/core/MetricsConfig.java
+++ b/solr/core/src/java/org/apache/solr/core/MetricsConfig.java
@@ -16,6 +16,7 @@
  */
 package org.apache.solr.core;
 
+import java.util.Collections;
 import java.util.HashSet;
 import java.util.Set;
 
@@ -31,11 +32,14 @@ public class MetricsConfig {
   private final PluginInfo timerSupplier;
   private final PluginInfo histogramSupplier;
   private final PluginInfo historyHandler;
+  private final boolean enabled;
 
-  private MetricsConfig(PluginInfo[] metricReporters, Set<String> hiddenSysProps,
+  private MetricsConfig(boolean enabled,
+                        PluginInfo[] metricReporters, Set<String> hiddenSysProps,
                         PluginInfo counterSupplier, PluginInfo meterSupplier,
                         PluginInfo timerSupplier, PluginInfo histogramSupplier,
                         PluginInfo historyHandler) {
+    this.enabled = enabled;
     this.metricReporters = metricReporters;
     this.hiddenSysProps = hiddenSysProps;
     this.counterSupplier = counterSupplier;
@@ -45,32 +49,74 @@ public class MetricsConfig {
     this.historyHandler = historyHandler;
   }
 
+  public boolean isEnabled() {
+    return enabled;
+  }
+
+  private static final PluginInfo[] NO_OP_REPORTERS = new PluginInfo[0];
+
   public PluginInfo[] getMetricReporters() {
-    return metricReporters;
+    if (enabled) {
+      return metricReporters;
+    } else {
+      return NO_OP_REPORTERS;
+    }
   }
 
   public Set<String> getHiddenSysProps() {
-    return hiddenSysProps;
+    if (enabled) {
+      return hiddenSysProps;
+    } else {
+      return Collections.emptySet();
+    }
   }
 
+  /** Symbolic name to use as plugin class name for no-op implementations. */
+  public static final String NOOP_IMPL_CLASS = "__noop__";
+
+  private static final PluginInfo NO_OP_PLUGIN =
+      new PluginInfo("typeUnused",
+            Collections.singletonMap("class", NOOP_IMPL_CLASS),
+            null, null);
+
   public PluginInfo getCounterSupplier() {
-    return counterSupplier;
+    if (enabled) {
+      return counterSupplier;
+    } else {
+      return NO_OP_PLUGIN;
+    }
   }
 
   public PluginInfo getMeterSupplier() {
-    return meterSupplier;
+    if (enabled) {
+      return meterSupplier;
+    } else {
+      return NO_OP_PLUGIN;
+    }
   }
 
   public PluginInfo getTimerSupplier() {
-    return timerSupplier;
+    if (enabled) {
+      return timerSupplier;
+    } else {
+      return NO_OP_PLUGIN;
+    }
   }
 
   public PluginInfo getHistogramSupplier() {
-    return histogramSupplier;
+    if (enabled) {
+      return histogramSupplier;
+    } else {
+      return NO_OP_PLUGIN;
+    }
   }
 
   public PluginInfo getHistoryHandler() {
-    return historyHandler;
+    if (enabled) {
+      return historyHandler;
+    } else {
+      return NO_OP_PLUGIN;
+    }
   }
 
   public static class MetricsConfigBuilder {
@@ -81,11 +127,18 @@ public class MetricsConfig {
     private PluginInfo timerSupplier;
     private PluginInfo histogramSupplier;
     private PluginInfo historyHandler;
+    // default to metrics enabled
+    private boolean enabled = true;
 
     public MetricsConfigBuilder() {
 
     }
 
+    public MetricsConfigBuilder setEnabled(boolean enabled) {
+      this.enabled = enabled;
+      return this;
+    }
+
     public MetricsConfigBuilder setHiddenSysProps(Set<String> hiddenSysProps) {
       if (hiddenSysProps != null && !hiddenSysProps.isEmpty()) {
         this.hiddenSysProps.clear();
@@ -125,7 +178,7 @@ public class MetricsConfig {
     }
 
     public MetricsConfig build() {
-      return new MetricsConfig(metricReporterPlugins, hiddenSysProps, counterSupplier, meterSupplier,
+      return new MetricsConfig(enabled, metricReporterPlugins, hiddenSysProps, counterSupplier, meterSupplier,
           timerSupplier, histogramSupplier, historyHandler);
     }
 
diff --git a/solr/core/src/java/org/apache/solr/core/SolrXmlConfig.java b/solr/core/src/java/org/apache/solr/core/SolrXmlConfig.java
index 18694f6..eafc9d5 100644
--- a/solr/core/src/java/org/apache/solr/core/SolrXmlConfig.java
+++ b/solr/core/src/java/org/apache/solr/core/SolrXmlConfig.java
@@ -496,7 +496,18 @@ public class SolrXmlConfig {
 
   private static MetricsConfig getMetricsConfig(XmlConfigFile config) {
     MetricsConfig.MetricsConfigBuilder builder = new MetricsConfig.MetricsConfigBuilder();
-    Node node = config.getNode("solr/metrics/suppliers/counter", false);
+    Node node = config.getNode("solr/metrics", false);
+    // enabled by default
+    boolean enabled = true;
+    if (node != null) {
+      enabled = Boolean.parseBoolean(DOMUtil.getAttrOrDefault(node, "enabled", "true"));
+    }
+    builder.setEnabled(enabled);
+    if (!enabled) {
+      log.info("Metrics collection is disabled.");
+      return builder.build();
+    }
+    node = config.getNode("solr/metrics/suppliers/counter", false);
     if (node != null) {
       builder = builder.setCounterSupplier(new PluginInfo(node, "counterSupplier", false, false));
     }
diff --git a/solr/core/src/java/org/apache/solr/handler/admin/MetricsHandler.java b/solr/core/src/java/org/apache/solr/handler/admin/MetricsHandler.java
index 76bb04d..d3b5cb9 100644
--- a/solr/core/src/java/org/apache/solr/handler/admin/MetricsHandler.java
+++ b/solr/core/src/java/org/apache/solr/handler/admin/MetricsHandler.java
@@ -71,20 +71,24 @@ public class MetricsHandler extends RequestHandlerBase implements PermissionName
   public static final String ALL = "all";
 
   private static final Pattern KEY_REGEX = Pattern.compile("(?<!" + Pattern.quote("\\") + ")" + Pattern.quote(":"));
-  private CoreContainer cc;
+  private final CoreContainer cc;
   private final Map<String, String> injectedSysProps = CommonTestInjection.injectAdditionalProps();
-
-  public MetricsHandler() {
-    this.metricManager = null;
-  }
+  private final boolean enabled;
 
   public MetricsHandler(CoreContainer coreContainer) {
     this.metricManager = coreContainer.getMetricManager();
     this.cc = coreContainer;
+    this.enabled = coreContainer.getConfig().getMetricsConfig().isEnabled();
   }
 
   public MetricsHandler(SolrMetricManager metricManager) {
     this.metricManager = metricManager;
+    this.cc = null;
+    this.enabled = true;
+  }
+
+  public boolean isEnabled() {
+    return enabled;
   }
 
   @Override
@@ -107,6 +111,10 @@ public class MetricsHandler extends RequestHandlerBase implements PermissionName
   
   @SuppressWarnings({"unchecked"})
   public void handleRequest(SolrParams params, BiConsumer<String, Object> consumer) throws Exception {
+    if (!enabled) {
+      consumer.accept("error", "metrics collection is disabled");
+      return;
+    }
     boolean compact = params.getBool(COMPACT_PARAM, true);
     String[] keys = params.getParams(KEY_PARAM);
     if (keys != null && keys.length > 0) {
diff --git a/solr/core/src/java/org/apache/solr/handler/admin/MetricsHistoryHandler.java b/solr/core/src/java/org/apache/solr/handler/admin/MetricsHistoryHandler.java
index 43b7f38..610071a 100644
--- a/solr/core/src/java/org/apache/solr/handler/admin/MetricsHistoryHandler.java
+++ b/solr/core/src/java/org/apache/solr/handler/admin/MetricsHistoryHandler.java
@@ -197,7 +197,9 @@ public class MetricsHistoryHandler extends RequestHandlerBase implements Permiss
     }
 
     this.nodeName = nodeName;
-    this.enable = Boolean.parseBoolean(String.valueOf(args.getOrDefault(ENABLE_PROP, "true")));
+    // disable when metrics reporting is disabled
+    this.enable = Boolean.parseBoolean(String.valueOf(args.getOrDefault(ENABLE_PROP, "true")))
+        && metricsHandler.isEnabled();
     // default to false - don't collect local per-replica metrics
     this.enableReplicas = Boolean.parseBoolean(String.valueOf(args.getOrDefault(ENABLE_REPLICAS_PROP, "false")));
     this.enableNodes = Boolean.parseBoolean(String.valueOf(args.getOrDefault(ENABLE_NODES_PROP, "false")));
@@ -246,6 +248,9 @@ public class MetricsHistoryHandler extends RequestHandlerBase implements Permiss
 
   // check that .system exists
   public void checkSystemCollection() {
+    if (!enable) {
+      return;
+    }
     if (cloudManager != null) {
       try {
         if (cloudManager.isClosed() || Thread.interrupted()) {
@@ -307,7 +312,9 @@ public class MetricsHistoryHandler extends RequestHandlerBase implements Permiss
   public void removeHistory(String registry) throws IOException {
     registry = SolrMetricManager.enforcePrefix(registry);
     knownDbs.remove(registry);
-    factory.remove(registry);
+    if (factory != null) {
+      factory.remove(registry);
+    }
   }
 
   @VisibleForTesting
@@ -700,6 +707,10 @@ public class MetricsHistoryHandler extends RequestHandlerBase implements Permiss
 
   @Override
   public void handleRequestBody(SolrQueryRequest req, SolrQueryResponse rsp) throws Exception {
+    if (!enable) {
+      rsp.add("error", "metrics history collection is disabled");
+      return;
+    }
     String actionStr = req.getParams().get(CommonParams.ACTION);
     if (actionStr == null) {
       throw new SolrException(SolrException.ErrorCode.BAD_REQUEST, "'action' is a required param");
diff --git a/solr/core/src/java/org/apache/solr/metrics/MetricSuppliers.java b/solr/core/src/java/org/apache/solr/metrics/MetricSuppliers.java
index 7ce07e8..a53fa4c 100644
--- a/solr/core/src/java/org/apache/solr/metrics/MetricSuppliers.java
+++ b/solr/core/src/java/org/apache/solr/metrics/MetricSuppliers.java
@@ -19,19 +19,13 @@ package org.apache.solr.metrics;
 import java.lang.invoke.MethodHandles;
 import java.lang.management.ManagementFactory;
 import java.lang.management.ThreadMXBean;
+import java.time.Duration;
+import java.util.concurrent.Callable;
 import java.util.concurrent.TimeUnit;
+import java.util.function.Supplier;
 
-import com.codahale.metrics.Clock;
-import com.codahale.metrics.Counter;
-import com.codahale.metrics.ExponentiallyDecayingReservoir;
-import com.codahale.metrics.Histogram;
-import com.codahale.metrics.Meter;
-import com.codahale.metrics.MetricRegistry;
-import com.codahale.metrics.Reservoir;
-import com.codahale.metrics.SlidingTimeWindowReservoir;
-import com.codahale.metrics.SlidingWindowReservoir;
-import com.codahale.metrics.Timer;
-import com.codahale.metrics.UniformReservoir;
+import com.codahale.metrics.*;
+import org.apache.solr.core.MetricsConfig;
 import org.apache.solr.core.PluginInfo;
 import org.apache.solr.core.SolrResourceLoader;
 import org.apache.solr.util.SolrPluginUtils;
@@ -197,17 +191,21 @@ public class MetricSuppliers {
       return new SlidingWindowReservoir(size);
     } else { // custom reservoir
       Reservoir reservoir;
-      try {
-        reservoir = loader.newInstance(clazz, Reservoir.class);
-        if (reservoir instanceof PluginInfoInitialized) {
-          ((PluginInfoInitialized)reservoir).init(info);
-        } else {
-          SolrPluginUtils.invokeSetters(reservoir, info.initArgs, true);
-        }
-        return reservoir;
-      } catch (Exception e) {
-        log.warn("Error initializing custom Reservoir implementation (will use default): {}", info, e);
+      if (loader == null) {
         return new ExponentiallyDecayingReservoir(size, alpha, clk);
+      } else {
+        try {
+          reservoir = loader.newInstance(clazz, Reservoir.class);
+          if (reservoir instanceof PluginInfoInitialized) {
+            ((PluginInfoInitialized)reservoir).init(info);
+          } else {
+            SolrPluginUtils.invokeSetters(reservoir, info.initArgs, true);
+          }
+          return reservoir;
+        } catch (Exception e) {
+          log.warn("Error initializing custom Reservoir implementation (will use default): {}", info, e);
+          return new ExponentiallyDecayingReservoir(size, alpha, clk);
+        }
       }
     }
   }
@@ -282,13 +280,19 @@ public class MetricSuppliers {
     if (info == null || info.className == null || info.className.trim().isEmpty()) {
       return new DefaultCounterSupplier();
     }
-
+    if (MetricsConfig.NOOP_IMPL_CLASS.equals(info.className)) {
+      return NoOpCounterSupplier.INSTANCE;
+    }
     MetricRegistry.MetricSupplier<Counter> supplier;
-    try {
-      supplier = loader.newInstance(info.className, MetricRegistry.MetricSupplier.class);
-    } catch (Exception e) {
-      log.warn("Error creating custom Counter supplier (will use default): {}", info, e);
+    if (loader == null) {
       supplier = new DefaultCounterSupplier();
+    } else {
+      try {
+        supplier = loader.newInstance(info.className, MetricRegistry.MetricSupplier.class);
+      } catch (Exception e) {
+        log.warn("Error creating custom Counter supplier (will use default): {}", info, e);
+        supplier = new DefaultCounterSupplier();
+      }
     }
     if (supplier instanceof PluginInfoInitialized) {
       ((PluginInfoInitialized)supplier).init(info);
@@ -310,11 +314,18 @@ public class MetricSuppliers {
     if (info == null || info.className == null || info.className.isEmpty()) {
       supplier = new DefaultMeterSupplier();
     } else {
-      try {
-        supplier = loader.newInstance(info.className, MetricRegistry.MetricSupplier.class);
-      } catch (Exception e) {
-        log.warn("Error creating custom Meter supplier (will use default): {}",info, e);
+      if (MetricsConfig.NOOP_IMPL_CLASS.equals(info.className)) {
+        return NoOpMeterSupplier.INSTANCE;
+      }
+      if (loader == null) {
         supplier = new DefaultMeterSupplier();
+      } else {
+        try {
+          supplier = loader.newInstance(info.className, MetricRegistry.MetricSupplier.class);
+        } catch (Exception e) {
+          log.warn("Error creating custom Meter supplier (will use default): {}",info, e);
+          supplier = new DefaultMeterSupplier();
+        }
       }
     }
     if (supplier instanceof PluginInfoInitialized) {
@@ -337,11 +348,18 @@ public class MetricSuppliers {
     if (info == null || info.className == null || info.className.isEmpty()) {
       supplier = new DefaultTimerSupplier(loader);
     } else {
-      try {
-        supplier = loader.newInstance(info.className, MetricRegistry.MetricSupplier.class);
-      } catch (Exception e) {
-        log.warn("Error creating custom Timer supplier (will use default): {}", info, e);
-        supplier = new DefaultTimerSupplier(loader);
+      if (MetricsConfig.NOOP_IMPL_CLASS.equals(info.className)) {
+        return NoOpTimerSupplier.INSTANCE;
+      }
+      if (loader == null) {
+        supplier = new DefaultTimerSupplier(null);
+      } else {
+        try {
+          supplier = loader.newInstance(info.className, MetricRegistry.MetricSupplier.class);
+        } catch (Exception e) {
+          log.warn("Error creating custom Timer supplier (will use default): {}", info, e);
+          supplier = new DefaultTimerSupplier(loader);
+        }
       }
     }
     if (supplier instanceof PluginInfoInitialized) {
@@ -363,11 +381,18 @@ public class MetricSuppliers {
     if (info == null || info.className == null || info.className.isEmpty()) {
       supplier = new DefaultHistogramSupplier(loader);
     } else {
-      try {
-        supplier = loader.newInstance(info.className, MetricRegistry.MetricSupplier.class);
-      } catch (Exception e) {
-        log.warn("Error creating custom Histogram supplier (will use default): {}", info, e);
-        supplier = new DefaultHistogramSupplier(loader);
+      if (MetricsConfig.NOOP_IMPL_CLASS.equals(info.className)) {
+        return NoOpHistogramSupplier.INSTANCE;
+      }
+      if (loader == null) {
+        supplier = new DefaultHistogramSupplier(null);
+      } else {
+        try {
+          supplier = loader.newInstance(info.className, MetricRegistry.MetricSupplier.class);
+        } catch (Exception e) {
+          log.warn("Error creating custom Histogram supplier (will use default): {}", info, e);
+          supplier = new DefaultHistogramSupplier(loader);
+        }
       }
     }
     if (supplier instanceof PluginInfoInitialized) {
@@ -377,4 +402,166 @@ public class MetricSuppliers {
     }
     return supplier;
   }
+
+  // NO-OP implementations. These can be used to effectively "turn off" the metrics
+  // collection, or rather eliminate the overhead of the metrics accounting and their
+  // memory footprint.
+
+  /**
+   * Marker interface for all no-op metrics.
+   */
+  public interface NoOpMetric {
+
+  }
+
+  /**
+   * No-op implementation of {@link Counter} supplier.
+   */
+  public static final class NoOpCounterSupplier implements MetricRegistry.MetricSupplier<Counter> {
+    public static final NoOpCounterSupplier INSTANCE = new NoOpCounterSupplier();
+
+    private static final class NoOpCounter extends Counter implements NoOpMetric {
+      @Override
+      public void inc() {
+        // no-op
+      }
+
+      @Override
+      public void inc(long n) {
+        // no-op
+      }
+
+      @Override
+      public void dec() {
+        // no-op
+      }
+
+      @Override
+      public void dec(long n) {
+        // no-op
+      }
+    }
+
+    private static final Counter METRIC = new NoOpCounter();
+
+    @Override
+    public Counter newMetric() {
+      return METRIC;
+    }
+  }
+
+  /**
+   * No-op implementation of {@link Histogram} supplier.
+   */
+  public static final class NoOpHistogramSupplier implements MetricRegistry.MetricSupplier<Histogram> {
+    public static final NoOpHistogramSupplier INSTANCE = new NoOpHistogramSupplier();
+
+    private static final class NoOpHistogram extends Histogram implements NoOpMetric {
+
+      public NoOpHistogram() {
+        super(new UniformReservoir(1));
+      }
+
+      @Override
+      public void update(int value) {
+        // no-op
+      }
+
+      @Override
+      public void update(long value) {
+        // no-op
+      }
+    }
+
+    private static final NoOpHistogram METRIC = new NoOpHistogram();
+
+    @Override
+    public Histogram newMetric() {
+      return METRIC;
+    }
+  }
+
+  /**
+   * No-op implementation of {@link Meter} supplier.
+   */
+  public static final class NoOpMeterSupplier implements MetricRegistry.MetricSupplier<Meter> {
+    public static final NoOpMeterSupplier INSTANCE = new NoOpMeterSupplier();
+
+    private static final class NoOpMeter extends Meter implements NoOpMetric {
+      @Override
+      public void mark() {
+        // no-op
+      }
+
+      @Override
+      public void mark(long n) {
+        // no-op
+      }
+    }
+
+    private static final NoOpMeter METRIC = new NoOpMeter();
+
+    @Override
+    public Meter newMetric() {
+      return METRIC;
+    }
+  }
+
+  /**
+   * No-op implementation of {@link Timer} supplier.
+   */
+  public static final class NoOpTimerSupplier implements MetricRegistry.MetricSupplier<Timer> {
+    public static final NoOpTimerSupplier INSTANCE = new NoOpTimerSupplier();
+
+    private static final class NoOpTimer extends Timer implements NoOpMetric {
+      @Override
+      public void update(long duration, TimeUnit unit) {
+        // no-op
+      }
+
+      @Override
+      public void update(Duration duration) {
+        // no-op
+      }
+
+      @Override
+      public <T> T time(Callable<T> event) throws Exception {
+        return event.call();
+      }
+
+      @Override
+      public <T> T timeSupplier(Supplier<T> event) {
+        return event.get();
+      }
+
+      @Override
+      public void time(Runnable event) {
+        event.run();
+      }
+    }
+
+    private static final NoOpTimer METRIC = new NoOpTimer();
+
+    @Override
+    public Timer newMetric() {
+      return METRIC;
+    }
+  }
+
+  /**
+   * No-op implementation of {@link Gauge}.
+   */
+  public static final class NoOpGauge implements Gauge<Object>, NoOpMetric {
+    public static final NoOpGauge INSTANCE = new NoOpGauge();
+
+    @Override
+    public Object getValue() {
+      return null;
+    }
+  }
+
+  @SuppressWarnings("unchecked")
+  public static <T> Gauge<T> getNoOpGauge(Gauge<T> actual) {
+    return (Gauge<T>) NoOpGauge.INSTANCE;
+  }
 }
diff --git a/solr/core/src/java/org/apache/solr/metrics/SolrMetricManager.java b/solr/core/src/java/org/apache/solr/metrics/SolrMetricManager.java
index 0fab8a9..dcb6849 100644
--- a/solr/core/src/java/org/apache/solr/metrics/SolrMetricManager.java
+++ b/solr/core/src/java/org/apache/solr/metrics/SolrMetricManager.java
@@ -110,12 +110,14 @@ public class SolrMetricManager {
 
   public static final int DEFAULT_CLOUD_REPORTER_PERIOD = 60;
 
-  private MetricRegistry.MetricSupplier<Counter> counterSupplier;
-  private MetricRegistry.MetricSupplier<Meter> meterSupplier;
-  private MetricRegistry.MetricSupplier<Timer> timerSupplier;
-  private MetricRegistry.MetricSupplier<Histogram> histogramSupplier;
+  private final MetricsConfig metricsConfig;
+  private final MetricRegistry.MetricSupplier<Counter> counterSupplier;
+  private final MetricRegistry.MetricSupplier<Meter> meterSupplier;
+  private final MetricRegistry.MetricSupplier<Timer> timerSupplier;
+  private final MetricRegistry.MetricSupplier<Histogram> histogramSupplier;
 
   public SolrMetricManager() {
+    metricsConfig = new MetricsConfig.MetricsConfigBuilder().build();
     counterSupplier = MetricSuppliers.counterSupplier(null, null);
     meterSupplier = MetricSuppliers.meterSupplier(null, null);
     timerSupplier = MetricSuppliers.timerSupplier(null, null);
@@ -123,6 +125,7 @@ public class SolrMetricManager {
   }
 
   public SolrMetricManager(SolrResourceLoader loader, MetricsConfig metricsConfig) {
+    this.metricsConfig = metricsConfig;
     counterSupplier = MetricSuppliers.counterSupplier(loader, metricsConfig.getCounterSupplier());
     meterSupplier = MetricSuppliers.meterSupplier(loader, metricsConfig.getMeterSupplier());
     timerSupplier = MetricSuppliers.timerSupplier(loader, metricsConfig.getTimerSupplier());
@@ -741,6 +744,9 @@ public class SolrMetricManager {
   }
   @SuppressWarnings({"unchecked", "rawtypes"})
   public void registerGauge(SolrInfoBean info, String registry, Gauge<?> gauge, String tag, boolean force, String metricName, String... metricPath) {
+    if (!metricsConfig.isEnabled()) {
+      gauge = MetricSuppliers.getNoOpGauge(gauge);
+    }
     registerMetric(info, registry, new GaugeWrapper(gauge, tag), force, metricName, metricPath);
   }
 
diff --git a/solr/core/src/test-files/solr/solr-metricsconfig.xml b/solr/core/src/test-files/solr/solr-metricsconfig.xml
index 6edb2fa..3e65682 100644
--- a/solr/core/src/test-files/solr/solr-metricsconfig.xml
+++ b/solr/core/src/test-files/solr/solr-metricsconfig.xml
@@ -17,7 +17,7 @@
 -->
 
 <solr>
- <metrics>
+ <metrics enabled="${metricsEnabled:true}">
   <suppliers>
     <counter class="${counter.class:}">
       <str name="foo">bar</str>
diff --git a/solr/core/src/test-files/solr/solr.xml b/solr/core/src/test-files/solr/solr.xml
index 6e032da..79db206 100644
--- a/solr/core/src/test-files/solr/solr.xml
+++ b/solr/core/src/test-files/solr/solr.xml
@@ -21,6 +21,8 @@
 -->
 <solr>
 
+  <metrics enabled="${metricsEnabled:true}"/>
+
   <str name="shareSchema">${shareSchema:false}</str>
   <str name="configSetBaseDir">${configSetBaseDir:configsets}</str>
   <str name="coreRootDirectory">${coreRootDirectory:.}</str>
diff --git a/solr/core/src/test/org/apache/solr/cloud/CollectionsAPISolrJTest.java b/solr/core/src/test/org/apache/solr/cloud/CollectionsAPISolrJTest.java
index f6ade9e..e1f502e 100644
--- a/solr/core/src/test/org/apache/solr/cloud/CollectionsAPISolrJTest.java
+++ b/solr/core/src/test/org/apache/solr/cloud/CollectionsAPISolrJTest.java
@@ -85,6 +85,7 @@ public class CollectionsAPISolrJTest extends SolrCloudTestCase {
 
   @Before
   public void beforeTest() throws Exception {
+    //System.setProperty("metricsEnabled", "true");
     configureCluster(4)
     .addConfig("conf", configset("cloud-minimal"))
     .addConfig("conf2", configset("cloud-dynamic"))
diff --git a/solr/core/src/test/org/apache/solr/cloud/MetricsHistoryIntegrationTest.java b/solr/core/src/test/org/apache/solr/cloud/MetricsHistoryIntegrationTest.java
index a720677..73c01a2 100644
--- a/solr/core/src/test/org/apache/solr/cloud/MetricsHistoryIntegrationTest.java
+++ b/solr/core/src/test/org/apache/solr/cloud/MetricsHistoryIntegrationTest.java
@@ -59,6 +59,7 @@ public class MetricsHistoryIntegrationTest extends SolrCloudTestCase {
 
   @BeforeClass
   public static void setupCluster() throws Exception {
+    System.setProperty("metricsEnabled", "true");
     boolean simulated = TEST_NIGHTLY ? random().nextBoolean() : true;
     if (simulated) {
       cloudManager = SimCloudManager.createCluster(1, TimeSource.get("simTime:50"));
diff --git a/solr/core/src/test/org/apache/solr/cloud/MetricsHistoryWithAuthIntegrationTest.java b/solr/core/src/test/org/apache/solr/cloud/MetricsHistoryWithAuthIntegrationTest.java
index 5a96ac3..6090361 100644
--- a/solr/core/src/test/org/apache/solr/cloud/MetricsHistoryWithAuthIntegrationTest.java
+++ b/solr/core/src/test/org/apache/solr/cloud/MetricsHistoryWithAuthIntegrationTest.java
@@ -55,7 +55,7 @@ public class MetricsHistoryWithAuthIntegrationTest extends SolrCloudTestCase {
 
   @BeforeClass
   public static void setupCluster() throws Exception {
-    String solrXml = MiniSolrCloudCluster.DEFAULT_CLOUD_SOLR_XML.replace("<metrics>\n",
+    String solrXml = MiniSolrCloudCluster.DEFAULT_CLOUD_SOLR_XML.replace("<metrics enabled=\"${metricsEnabled:false}\">\n",
         "<metrics>\n" + SOLR_XML_HISTORY_CONFIG);
     // Spin up a cluster with a protected /admin/metrics handler, and a 2 seconds metrics collectPeriod
     configureCluster(1)
diff --git a/solr/core/src/test/org/apache/solr/cloud/RoutingToNodesWithPropertiesTest.java b/solr/core/src/test/org/apache/solr/cloud/RoutingToNodesWithPropertiesTest.java
index 4f9cbfc..d133489 100644
--- a/solr/core/src/test/org/apache/solr/cloud/RoutingToNodesWithPropertiesTest.java
+++ b/solr/core/src/test/org/apache/solr/cloud/RoutingToNodesWithPropertiesTest.java
@@ -67,6 +67,7 @@ public class RoutingToNodesWithPropertiesTest extends SolrCloudTestCase {
 
   @Before
   public void setupCluster() throws Exception {
+    System.setProperty("metricsEnabled", "true");
     CommonTestInjection.setAdditionalProps(ImmutableMap.of("zone", "us-west1"));
     configureCluster(2)
         .withSolrXml(TEST_PATH().resolve("solr-trackingshardhandler.xml"))
diff --git a/solr/core/src/test/org/apache/solr/cloud/SplitShardTest.java b/solr/core/src/test/org/apache/solr/cloud/SplitShardTest.java
index 926110f..0e36b57 100644
--- a/solr/core/src/test/org/apache/solr/cloud/SplitShardTest.java
+++ b/solr/core/src/test/org/apache/solr/cloud/SplitShardTest.java
@@ -54,6 +54,7 @@ public class SplitShardTest extends SolrCloudTestCase {
 
   @BeforeClass
   public static void setupCluster() throws Exception {
+    System.setProperty("metricsEnabled", "true");
     configureCluster(1)
         .addConfig("conf", configset("cloud-minimal"))
         .configure();
diff --git a/solr/core/src/test/org/apache/solr/cloud/TestCloudRecovery.java b/solr/core/src/test/org/apache/solr/cloud/TestCloudRecovery.java
index 2afeea9..60382c9 100644
--- a/solr/core/src/test/org/apache/solr/cloud/TestCloudRecovery.java
+++ b/solr/core/src/test/org/apache/solr/cloud/TestCloudRecovery.java
@@ -58,6 +58,7 @@ public class TestCloudRecovery extends SolrCloudTestCase {
 
   @BeforeClass
   public static void setupCluster() throws Exception {
+    System.setProperty("metricsEnabled", "true");
     System.setProperty("solr.directoryFactory", "solr.StandardDirectoryFactory");
     System.setProperty("solr.ulog.numRecordsToKeep", "1000");
   }
diff --git a/solr/core/src/test/org/apache/solr/cloud/TestTlogReplica.java b/solr/core/src/test/org/apache/solr/cloud/TestTlogReplica.java
index 4e531ac..89825be 100644
--- a/solr/core/src/test/org/apache/solr/cloud/TestTlogReplica.java
+++ b/solr/core/src/test/org/apache/solr/cloud/TestTlogReplica.java
@@ -91,6 +91,7 @@ public class TestTlogReplica extends SolrCloudTestCase {
 
   @BeforeClass
   public static void setupCluster() throws Exception {
+    System.setProperty("metricsEnabled", "true");
     System.setProperty("solr.waitToSeeReplicasInStateTimeoutSeconds", "30");
     configureCluster(2) // 2 + random().nextInt(3)
         .addConfig("conf", configset("cloud-minimal-inplace-updates"))
diff --git a/solr/core/src/test/org/apache/solr/cloud/TestUtilizeNode.java b/solr/core/src/test/org/apache/solr/cloud/TestUtilizeNode.java
index ab9585b..e72c233 100644
--- a/solr/core/src/test/org/apache/solr/cloud/TestUtilizeNode.java
+++ b/solr/core/src/test/org/apache/solr/cloud/TestUtilizeNode.java
@@ -43,6 +43,7 @@ public class TestUtilizeNode extends SolrCloudTestCase {
 
   @BeforeClass
   public static void setupCluster() throws Exception {
+    System.setProperty("metricsEnabled", "true");
     configureCluster(3)
         .addConfig("conf1", TEST_PATH().resolve("configsets").resolve("cloud-dynamic").resolve("conf"))
         .configure();
diff --git a/solr/core/src/test/org/apache/solr/cloud/TestWithCollection.java b/solr/core/src/test/org/apache/solr/cloud/TestWithCollection.java
index c7c5224..1e4934a 100644
--- a/solr/core/src/test/org/apache/solr/cloud/TestWithCollection.java
+++ b/solr/core/src/test/org/apache/solr/cloud/TestWithCollection.java
@@ -70,6 +70,7 @@ public class TestWithCollection extends SolrCloudTestCase {
 
   @Before
   public void setupCluster() throws Exception {
+    System.setProperty("metricsEnabled", "true");
     configureCluster(NUM_JETTIES)
         .addConfig("conf", configset("cloud-minimal"))
         .configure();
diff --git a/solr/core/src/test/org/apache/solr/cloud/api/collections/ConcurrentCreateCollectionTest.java b/solr/core/src/test/org/apache/solr/cloud/api/collections/ConcurrentCreateCollectionTest.java
index d9fb8b1..c000f51 100644
--- a/solr/core/src/test/org/apache/solr/cloud/api/collections/ConcurrentCreateCollectionTest.java
+++ b/solr/core/src/test/org/apache/solr/cloud/api/collections/ConcurrentCreateCollectionTest.java
@@ -48,6 +48,7 @@ public class ConcurrentCreateCollectionTest extends SolrCloudTestCase {
 
   @BeforeClass
   public static void setupCluster() throws Exception {
+    System.setProperty("metricsEnabled", "true");
     configureCluster(NODES)
          .addConfig("conf", configset("cloud-minimal"))
         //.addConfig("conf", configset("_default"))
diff --git a/solr/core/src/test/org/apache/solr/cloud/autoscaling/AutoAddReplicasIntegrationTest.java b/solr/core/src/test/org/apache/solr/cloud/autoscaling/AutoAddReplicasIntegrationTest.java
index 1613eaf..4e12ac3 100644
--- a/solr/core/src/test/org/apache/solr/cloud/autoscaling/AutoAddReplicasIntegrationTest.java
+++ b/solr/core/src/test/org/apache/solr/cloud/autoscaling/AutoAddReplicasIntegrationTest.java
@@ -63,6 +63,7 @@ public class AutoAddReplicasIntegrationTest extends SolrCloudTestCase {
   
   @Before
   public void setupCluster() throws Exception {
+    System.setProperty("metricsEnabled", "true");
     configureCluster(3)
         .addConfig("conf", configset(getConfigSet()))
         .withSolrXml(TEST_PATH().resolve("solr.xml"))
diff --git a/solr/core/src/test/org/apache/solr/cloud/autoscaling/AutoAddReplicasPlanActionTest.java b/solr/core/src/test/org/apache/solr/cloud/autoscaling/AutoAddReplicasPlanActionTest.java
index 8fca98d..1264996 100644
--- a/solr/core/src/test/org/apache/solr/cloud/autoscaling/AutoAddReplicasPlanActionTest.java
+++ b/solr/core/src/test/org/apache/solr/cloud/autoscaling/AutoAddReplicasPlanActionTest.java
@@ -51,6 +51,7 @@ public class AutoAddReplicasPlanActionTest extends SolrCloudTestCase{
   
   @BeforeClass
   public static void setupCluster() throws Exception {
+    System.setProperty("metricsEnabled", "true");
     System.setProperty("solr.httpclient.retries", "4");
     System.setProperty("solr.retries.on.forward", "1");
     System.setProperty("solr.retries.to.followers", "1"); 
diff --git a/solr/core/src/test/org/apache/solr/cloud/autoscaling/AutoScalingHandlerTest.java b/solr/core/src/test/org/apache/solr/cloud/autoscaling/AutoScalingHandlerTest.java
index a103020..51189d6 100644
--- a/solr/core/src/test/org/apache/solr/cloud/autoscaling/AutoScalingHandlerTest.java
+++ b/solr/core/src/test/org/apache/solr/cloud/autoscaling/AutoScalingHandlerTest.java
@@ -66,6 +66,7 @@ public class AutoScalingHandlerTest extends SolrCloudTestCase {
 
   @BeforeClass
   public static void setupCluster() throws Exception {
+    System.setProperty("metricsEnabled", "true");
     configureCluster(2)
         .addConfig(CONFIGSET_NAME, configset("cloud-minimal"))
         .configure();
diff --git a/solr/core/src/test/org/apache/solr/cloud/autoscaling/ComputePlanActionTest.java b/solr/core/src/test/org/apache/solr/cloud/autoscaling/ComputePlanActionTest.java
index eab52d5..2526292 100644
--- a/solr/core/src/test/org/apache/solr/cloud/autoscaling/ComputePlanActionTest.java
+++ b/solr/core/src/test/org/apache/solr/cloud/autoscaling/ComputePlanActionTest.java
@@ -74,6 +74,7 @@ public class ComputePlanActionTest extends SolrCloudTestCase {
 
   @BeforeClass
   public static void setupCluster() throws Exception {
+    System.setProperty("metricsEnabled", "true");
     configureCluster(NODE_COUNT)
         .addConfig("conf", configset("cloud-minimal"))
         .configure();
diff --git a/solr/core/src/test/org/apache/solr/cloud/autoscaling/ExecutePlanActionTest.java b/solr/core/src/test/org/apache/solr/cloud/autoscaling/ExecutePlanActionTest.java
index 25a7616..30aae77 100644
--- a/solr/core/src/test/org/apache/solr/cloud/autoscaling/ExecutePlanActionTest.java
+++ b/solr/core/src/test/org/apache/solr/cloud/autoscaling/ExecutePlanActionTest.java
@@ -99,7 +99,7 @@ public class ExecutePlanActionTest extends SolrCloudTestCase {
   @Before
   public void setUp() throws Exception  {
     super.setUp();
-    
+    System.setProperty("metricsEnabled", "true");
     configureCluster(NODE_COUNT)
     .addConfig("conf", configset("cloud-minimal"))
     .configure();
diff --git a/solr/core/src/test/org/apache/solr/cloud/autoscaling/HdfsAutoAddReplicasIntegrationTest.java b/solr/core/src/test/org/apache/solr/cloud/autoscaling/HdfsAutoAddReplicasIntegrationTest.java
index 7600bf4..5e1ad22 100644
--- a/solr/core/src/test/org/apache/solr/cloud/autoscaling/HdfsAutoAddReplicasIntegrationTest.java
+++ b/solr/core/src/test/org/apache/solr/cloud/autoscaling/HdfsAutoAddReplicasIntegrationTest.java
@@ -42,6 +42,7 @@ public class HdfsAutoAddReplicasIntegrationTest extends AutoAddReplicasIntegrati
 
   @BeforeClass
   public static void setupClass() throws Exception {
+    System.setProperty("metricsEnabled", "true");
     dfsCluster = HdfsTestUtil.setupClass(createTempDir().toFile().getAbsolutePath());
   }
 
diff --git a/solr/core/src/test/org/apache/solr/cloud/autoscaling/HttpTriggerListenerTest.java b/solr/core/src/test/org/apache/solr/cloud/autoscaling/HttpTriggerListenerTest.java
index a712aee..d3fed73 100644
--- a/solr/core/src/test/org/apache/solr/cloud/autoscaling/HttpTriggerListenerTest.java
+++ b/solr/core/src/test/org/apache/solr/cloud/autoscaling/HttpTriggerListenerTest.java
@@ -57,6 +57,7 @@ public class HttpTriggerListenerTest extends SolrCloudTestCase {
 
   @BeforeClass
   public static void setupCluster() throws Exception {
+    System.setProperty("metricsEnabled", "true");
     configureCluster(2)
         .addConfig("conf", configset("cloud-minimal"))
         .configure();
diff --git a/solr/core/src/test/org/apache/solr/cloud/autoscaling/IndexSizeTriggerMixedBoundsTest.java b/solr/core/src/test/org/apache/solr/cloud/autoscaling/IndexSizeTriggerMixedBoundsTest.java
index 2ebce0a..11f258f 100644
--- a/solr/core/src/test/org/apache/solr/cloud/autoscaling/IndexSizeTriggerMixedBoundsTest.java
+++ b/solr/core/src/test/org/apache/solr/cloud/autoscaling/IndexSizeTriggerMixedBoundsTest.java
@@ -81,6 +81,7 @@ public class IndexSizeTriggerMixedBoundsTest extends SolrCloudTestCase {
 
   @BeforeClass
   public static void setupCluster() throws Exception {
+    System.setProperty("metricsEnabled", "true");
     configureCluster(2)
     .addConfig("conf", configset("cloud-minimal"))
     .configure();
diff --git a/solr/core/src/test/org/apache/solr/cloud/autoscaling/IndexSizeTriggerSizeEstimationTest.java b/solr/core/src/test/org/apache/solr/cloud/autoscaling/IndexSizeTriggerSizeEstimationTest.java
index 150a67f..628f0cc 100644
--- a/solr/core/src/test/org/apache/solr/cloud/autoscaling/IndexSizeTriggerSizeEstimationTest.java
+++ b/solr/core/src/test/org/apache/solr/cloud/autoscaling/IndexSizeTriggerSizeEstimationTest.java
@@ -83,6 +83,7 @@ public class IndexSizeTriggerSizeEstimationTest extends SolrCloudTestCase {
 
   @BeforeClass
   public static void setupCluster() throws Exception {
+    System.setProperty("metricsEnabled", "true");
     configureCluster(2)
     .addConfig("conf", configset("cloud-minimal"))
     .configure();
diff --git a/solr/core/src/test/org/apache/solr/cloud/autoscaling/IndexSizeTriggerTest.java b/solr/core/src/test/org/apache/solr/cloud/autoscaling/IndexSizeTriggerTest.java
index b937668..0711970 100644
--- a/solr/core/src/test/org/apache/solr/cloud/autoscaling/IndexSizeTriggerTest.java
+++ b/solr/core/src/test/org/apache/solr/cloud/autoscaling/IndexSizeTriggerTest.java
@@ -91,6 +91,7 @@ public class IndexSizeTriggerTest extends SolrCloudTestCase {
 
   @BeforeClass
   public static void setupCluster() throws Exception {
+    System.setProperty("metricsEnabled", "true");
     configureCluster(2)
         .addConfig("conf", configset("cloud-minimal"))
         .configure();
diff --git a/solr/core/src/test/org/apache/solr/cloud/autoscaling/MetricTriggerIntegrationTest.java b/solr/core/src/test/org/apache/solr/cloud/autoscaling/MetricTriggerIntegrationTest.java
index bba3096..c189a9f 100644
--- a/solr/core/src/test/org/apache/solr/cloud/autoscaling/MetricTriggerIntegrationTest.java
+++ b/solr/core/src/test/org/apache/solr/cloud/autoscaling/MetricTriggerIntegrationTest.java
@@ -65,6 +65,7 @@ public class MetricTriggerIntegrationTest extends SolrCloudTestCase {
 
   @BeforeClass
   public static void setupCluster() throws Exception {
+    System.setProperty("metricsEnabled", "true");
     configureCluster(2)
         .addConfig("conf", configset("cloud-minimal"))
         .configure();
diff --git a/solr/core/src/test/org/apache/solr/cloud/autoscaling/MetricTriggerTest.java b/solr/core/src/test/org/apache/solr/cloud/autoscaling/MetricTriggerTest.java
index 74ebca5..fc97fbe 100644
--- a/solr/core/src/test/org/apache/solr/cloud/autoscaling/MetricTriggerTest.java
+++ b/solr/core/src/test/org/apache/solr/cloud/autoscaling/MetricTriggerTest.java
@@ -45,6 +45,7 @@ public class MetricTriggerTest extends SolrCloudTestCase {
 
   @BeforeClass
   public static void setupCluster() throws Exception {
+    System.setProperty("metricsEnabled", "true");
     configureCluster(1)
         .addConfig("conf", configset("cloud-minimal"))
         .configure();
diff --git a/solr/core/src/test/org/apache/solr/cloud/autoscaling/NodeAddedTriggerIntegrationTest.java b/solr/core/src/test/org/apache/solr/cloud/autoscaling/NodeAddedTriggerIntegrationTest.java
index bbd2482..0219449 100644
--- a/solr/core/src/test/org/apache/solr/cloud/autoscaling/NodeAddedTriggerIntegrationTest.java
+++ b/solr/core/src/test/org/apache/solr/cloud/autoscaling/NodeAddedTriggerIntegrationTest.java
@@ -80,6 +80,7 @@ public class NodeAddedTriggerIntegrationTest extends SolrCloudTestCase {
 
   @Before
   public void setupTest() throws Exception {
+    System.setProperty("metricsEnabled", "true");
     configureCluster(2)
         .addConfig("conf", configset("cloud-minimal"))
         .configure();
diff --git a/solr/core/src/test/org/apache/solr/cloud/autoscaling/NodeAddedTriggerTest.java b/solr/core/src/test/org/apache/solr/cloud/autoscaling/NodeAddedTriggerTest.java
index 88cdcc3..9f8d0ba 100644
--- a/solr/core/src/test/org/apache/solr/cloud/autoscaling/NodeAddedTriggerTest.java
+++ b/solr/core/src/test/org/apache/solr/cloud/autoscaling/NodeAddedTriggerTest.java
@@ -55,6 +55,7 @@ public class NodeAddedTriggerTest extends SolrCloudTestCase {
 
   @BeforeClass
   public static void setupCluster() throws Exception {
+    System.setProperty("metricsEnabled", "true");
 
   }
 
diff --git a/solr/core/src/test/org/apache/solr/cloud/autoscaling/NodeLostTriggerIntegrationTest.java b/solr/core/src/test/org/apache/solr/cloud/autoscaling/NodeLostTriggerIntegrationTest.java
index ef52267..bce299f 100644
--- a/solr/core/src/test/org/apache/solr/cloud/autoscaling/NodeLostTriggerIntegrationTest.java
+++ b/solr/core/src/test/org/apache/solr/cloud/autoscaling/NodeLostTriggerIntegrationTest.java
@@ -72,7 +72,8 @@ public class NodeLostTriggerIntegrationTest extends SolrCloudTestCase {
 
   @Before
   public void setupTest() throws Exception {
-    
+    System.setProperty("metricsEnabled", "true");
+
     configureCluster(4)
     .addConfig("conf", configset("cloud-minimal"))
     .configure();
diff --git a/solr/core/src/test/org/apache/solr/cloud/autoscaling/NodeLostTriggerTest.java b/solr/core/src/test/org/apache/solr/cloud/autoscaling/NodeLostTriggerTest.java
index 5d417f9..edb3e0c 100644
--- a/solr/core/src/test/org/apache/solr/cloud/autoscaling/NodeLostTriggerTest.java
+++ b/solr/core/src/test/org/apache/solr/cloud/autoscaling/NodeLostTriggerTest.java
@@ -64,7 +64,8 @@ public class NodeLostTriggerTest extends SolrCloudTestCase {
     actionConstructorCalled = new AtomicBoolean(false);
     actionInitCalled = new AtomicBoolean(false);
     actionCloseCalled = new AtomicBoolean(false);
-    
+    System.setProperty("metricsEnabled", "true");
+
     configureCluster(3)
     .addConfig("conf", configset("cloud-minimal"))
     .configure();
diff --git a/solr/core/src/test/org/apache/solr/cloud/autoscaling/NodeMarkersRegistrationTest.java b/solr/core/src/test/org/apache/solr/cloud/autoscaling/NodeMarkersRegistrationTest.java
index 5ed30c0..4a05bb0 100644
--- a/solr/core/src/test/org/apache/solr/cloud/autoscaling/NodeMarkersRegistrationTest.java
+++ b/solr/core/src/test/org/apache/solr/cloud/autoscaling/NodeMarkersRegistrationTest.java
@@ -69,6 +69,7 @@ public class NodeMarkersRegistrationTest extends SolrCloudTestCase {
 
   @Before
   public void setupCluster() throws Exception {
+    System.setProperty("metricsEnabled", "true");
     configureCluster(2)
         .addConfig("conf", configset("cloud-minimal"))
         .configure();
diff --git a/solr/core/src/test/org/apache/solr/cloud/autoscaling/RestoreTriggerStateTest.java b/solr/core/src/test/org/apache/solr/cloud/autoscaling/RestoreTriggerStateTest.java
index 7fa4dc7..0cb6c2b 100644
--- a/solr/core/src/test/org/apache/solr/cloud/autoscaling/RestoreTriggerStateTest.java
+++ b/solr/core/src/test/org/apache/solr/cloud/autoscaling/RestoreTriggerStateTest.java
@@ -60,6 +60,7 @@ public class RestoreTriggerStateTest extends SolrCloudTestCase {
 
   @BeforeClass
   public static void setupCluster() throws Exception {
+    System.setProperty("metricsEnabled", "true");
     configureCluster(2)
         .addConfig("conf", configset("cloud-minimal"))
         .configure();
diff --git a/solr/core/src/test/org/apache/solr/cloud/autoscaling/ScheduledMaintenanceTriggerTest.java b/solr/core/src/test/org/apache/solr/cloud/autoscaling/ScheduledMaintenanceTriggerTest.java
index 68808e1..0f1f261 100644
--- a/solr/core/src/test/org/apache/solr/cloud/autoscaling/ScheduledMaintenanceTriggerTest.java
+++ b/solr/core/src/test/org/apache/solr/cloud/autoscaling/ScheduledMaintenanceTriggerTest.java
@@ -68,6 +68,7 @@ public class ScheduledMaintenanceTriggerTest extends SolrCloudTestCase {
 
   @BeforeClass
   public static void setupCluster() throws Exception {
+    System.setProperty("metricsEnabled", "true");
     configureCluster(1)
         .addConfig("conf", configset("cloud-minimal"))
         .configure();
diff --git a/solr/core/src/test/org/apache/solr/cloud/autoscaling/ScheduledTriggerIntegrationTest.java b/solr/core/src/test/org/apache/solr/cloud/autoscaling/ScheduledTriggerIntegrationTest.java
index 63c0e70..247a079 100644
--- a/solr/core/src/test/org/apache/solr/cloud/autoscaling/ScheduledTriggerIntegrationTest.java
+++ b/solr/core/src/test/org/apache/solr/cloud/autoscaling/ScheduledTriggerIntegrationTest.java
@@ -57,6 +57,7 @@ public class ScheduledTriggerIntegrationTest extends SolrCloudTestCase {
 
   @Before
   public void setupCluster() throws Exception {
+    System.setProperty("metricsEnabled", "true");
     configureCluster(2)
         .addConfig("conf", configset("cloud-minimal"))
         .configure();
diff --git a/solr/core/src/test/org/apache/solr/cloud/autoscaling/ScheduledTriggerTest.java b/solr/core/src/test/org/apache/solr/cloud/autoscaling/ScheduledTriggerTest.java
index 84c6df9..ea6ed19 100644
--- a/solr/core/src/test/org/apache/solr/cloud/autoscaling/ScheduledTriggerTest.java
+++ b/solr/core/src/test/org/apache/solr/cloud/autoscaling/ScheduledTriggerTest.java
@@ -51,6 +51,7 @@ public class ScheduledTriggerTest extends SolrCloudTestCase {
 
   @BeforeClass
   public static void setupCluster() throws Exception {
+    System.setProperty("metricsEnabled", "true");
     configureCluster(1)
         .addConfig("conf", configset("cloud-minimal"))
         .configure();
diff --git a/solr/core/src/test/org/apache/solr/cloud/autoscaling/SearchRateTriggerIntegrationTest.java b/solr/core/src/test/org/apache/solr/cloud/autoscaling/SearchRateTriggerIntegrationTest.java
index 15ab701..8273751 100644
--- a/solr/core/src/test/org/apache/solr/cloud/autoscaling/SearchRateTriggerIntegrationTest.java
+++ b/solr/core/src/test/org/apache/solr/cloud/autoscaling/SearchRateTriggerIntegrationTest.java
@@ -82,6 +82,7 @@ public class SearchRateTriggerIntegrationTest extends SolrCloudTestCase {
 
   @BeforeClass
   public static void setupCluster() throws Exception {
+    System.setProperty("metricsEnabled", "true");
     configureCluster(5)
         .addConfig("conf", configset("cloud-minimal"))
         .configure();
diff --git a/solr/core/src/test/org/apache/solr/cloud/autoscaling/SearchRateTriggerTest.java b/solr/core/src/test/org/apache/solr/cloud/autoscaling/SearchRateTriggerTest.java
index 41bbd8b..3a280e4 100644
--- a/solr/core/src/test/org/apache/solr/cloud/autoscaling/SearchRateTriggerTest.java
+++ b/solr/core/src/test/org/apache/solr/cloud/autoscaling/SearchRateTriggerTest.java
@@ -72,6 +72,7 @@ public class SearchRateTriggerTest extends SolrCloudTestCase {
 
   @BeforeClass
   public static void setupCluster() throws Exception {
+    System.setProperty("metricsEnabled", "true");
 
   }
 
diff --git a/solr/core/src/test/org/apache/solr/cloud/autoscaling/SystemLogListenerTest.java b/solr/core/src/test/org/apache/solr/cloud/autoscaling/SystemLogListenerTest.java
index d2f7c23..6641c15 100644
--- a/solr/core/src/test/org/apache/solr/cloud/autoscaling/SystemLogListenerTest.java
+++ b/solr/core/src/test/org/apache/solr/cloud/autoscaling/SystemLogListenerTest.java
@@ -85,6 +85,7 @@ public class SystemLogListenerTest extends SolrCloudTestCase {
 
   @Before
   public void setupCluster() throws Exception {
+    System.setProperty("metricsEnabled", "true");
     configureCluster(NODE_COUNT)
         .addConfig("conf", configset("cloud-minimal"))
         .configure();
diff --git a/solr/core/src/test/org/apache/solr/cloud/autoscaling/TestPolicyCloud.java b/solr/core/src/test/org/apache/solr/cloud/autoscaling/TestPolicyCloud.java
index 404e986..67ffff1 100644
--- a/solr/core/src/test/org/apache/solr/cloud/autoscaling/TestPolicyCloud.java
+++ b/solr/core/src/test/org/apache/solr/cloud/autoscaling/TestPolicyCloud.java
@@ -76,6 +76,7 @@ public class TestPolicyCloud extends SolrCloudTestCase {
 
   @BeforeClass
   public static void setupCluster() throws Exception {
+    System.setProperty("metricsEnabled", "true");
     configureCluster(5)
         .addConfig("conf", configset("cloud-minimal"))
         .configure();
diff --git a/solr/core/src/test/org/apache/solr/cloud/autoscaling/TriggerCooldownIntegrationTest.java b/solr/core/src/test/org/apache/solr/cloud/autoscaling/TriggerCooldownIntegrationTest.java
index b7f40cf..97bb7d5 100644
--- a/solr/core/src/test/org/apache/solr/cloud/autoscaling/TriggerCooldownIntegrationTest.java
+++ b/solr/core/src/test/org/apache/solr/cloud/autoscaling/TriggerCooldownIntegrationTest.java
@@ -65,6 +65,7 @@ public class TriggerCooldownIntegrationTest extends SolrCloudTestCase {
   
   @BeforeClass
   public static void setupCluster() throws Exception {
+    System.setProperty("metricsEnabled", "true");
     resetTriggerAndListenerState();
     configureCluster(2)
         .addConfig("conf", configset("cloud-minimal"))
diff --git a/solr/core/src/test/org/apache/solr/cloud/autoscaling/TriggerIntegrationTest.java b/solr/core/src/test/org/apache/solr/cloud/autoscaling/TriggerIntegrationTest.java
index 73cfcfa..a9938cd 100644
--- a/solr/core/src/test/org/apache/solr/cloud/autoscaling/TriggerIntegrationTest.java
+++ b/solr/core/src/test/org/apache/solr/cloud/autoscaling/TriggerIntegrationTest.java
@@ -82,6 +82,7 @@ public class TriggerIntegrationTest extends SolrCloudTestCase {
 
   @BeforeClass
   public static void setupCluster() throws Exception {
+    System.setProperty("metricsEnabled", "true");
     configureCluster(NODE_COUNT)
         .addConfig("conf", configset("cloud-minimal"))
         .configure();
diff --git a/solr/core/src/test/org/apache/solr/cloud/autoscaling/TriggerSetPropertiesIntegrationTest.java b/solr/core/src/test/org/apache/solr/cloud/autoscaling/TriggerSetPropertiesIntegrationTest.java
index dbdff51..68935f2 100644
--- a/solr/core/src/test/org/apache/solr/cloud/autoscaling/TriggerSetPropertiesIntegrationTest.java
+++ b/solr/core/src/test/org/apache/solr/cloud/autoscaling/TriggerSetPropertiesIntegrationTest.java
@@ -51,6 +51,7 @@ public class TriggerSetPropertiesIntegrationTest extends SolrCloudTestCase {
 
   @BeforeClass
   public static void setupCluster() throws Exception {
+    System.setProperty("metricsEnabled", "true");
     configureCluster(2)
         .addConfig("conf", configset("cloud-minimal"))
         .configure();
diff --git a/solr/core/src/test/org/apache/solr/cloud/autoscaling/sim/TestSimClusterStateProvider.java b/solr/core/src/test/org/apache/solr/cloud/autoscaling/sim/TestSimClusterStateProvider.java
index abb75cc..e341e42 100644
--- a/solr/core/src/test/org/apache/solr/cloud/autoscaling/sim/TestSimClusterStateProvider.java
+++ b/solr/core/src/test/org/apache/solr/cloud/autoscaling/sim/TestSimClusterStateProvider.java
@@ -71,6 +71,7 @@ public class TestSimClusterStateProvider extends SolrCloudTestCase {
   // set up a real cluster as the source of test data
   @BeforeClass
   public static void setupCluster() throws Exception {
+    System.setProperty("metricsEnabled", "true");
     simulated = random().nextBoolean();
     simulated = true;
     log.info("####### Using simulated components? {}", simulated);
diff --git a/solr/core/src/test/org/apache/solr/cloud/autoscaling/sim/TestSnapshotCloudManager.java b/solr/core/src/test/org/apache/solr/cloud/autoscaling/sim/TestSnapshotCloudManager.java
index 7b2d769..b4dfd08 100644
--- a/solr/core/src/test/org/apache/solr/cloud/autoscaling/sim/TestSnapshotCloudManager.java
+++ b/solr/core/src/test/org/apache/solr/cloud/autoscaling/sim/TestSnapshotCloudManager.java
@@ -71,6 +71,7 @@ public class TestSnapshotCloudManager extends SolrCloudTestCase {
   // set up a real cluster as the source of test data
   @BeforeClass
   public static void setupCluster() throws Exception {
+    System.setProperty("metricsEnabled", "true");
     configureCluster(NODE_COUNT)
         .addConfig("conf", configset("cloud-minimal"))
         .configure();
diff --git a/solr/core/src/test/org/apache/solr/cloud/rule/RulesTest.java b/solr/core/src/test/org/apache/solr/cloud/rule/RulesTest.java
index bc9bfcd..9a2cb55 100644
--- a/solr/core/src/test/org/apache/solr/cloud/rule/RulesTest.java
+++ b/solr/core/src/test/org/apache/solr/cloud/rule/RulesTest.java
@@ -60,6 +60,7 @@ public class RulesTest extends SolrCloudTestCase {
 
   @BeforeClass
   public static void setupCluster() throws Exception {
+    System.setProperty("metricsEnabled", "true");
     configureCluster(5)
         .addConfig("conf", configset("cloud-minimal"))
         .configure();
diff --git a/solr/core/src/test/org/apache/solr/handler/admin/AdminHandlersProxyTest.java b/solr/core/src/test/org/apache/solr/handler/admin/AdminHandlersProxyTest.java
index 93cbe3e..ee6183b 100644
--- a/solr/core/src/test/org/apache/solr/handler/admin/AdminHandlersProxyTest.java
+++ b/solr/core/src/test/org/apache/solr/handler/admin/AdminHandlersProxyTest.java
@@ -44,6 +44,7 @@ public class AdminHandlersProxyTest extends SolrCloudTestCase {
 
   @BeforeClass
   public static void setupCluster() throws Exception {
+    System.setProperty("metricsEnabled", "true");
     configureCluster(2)
         .addConfig("conf", configset("cloud-minimal"))
         .configure();
diff --git a/solr/core/src/test/org/apache/solr/handler/admin/AutoscalingHistoryHandlerTest.java b/solr/core/src/test/org/apache/solr/handler/admin/AutoscalingHistoryHandlerTest.java
index 962bd93..a9eea7f 100644
--- a/solr/core/src/test/org/apache/solr/handler/admin/AutoscalingHistoryHandlerTest.java
+++ b/solr/core/src/test/org/apache/solr/handler/admin/AutoscalingHistoryHandlerTest.java
@@ -77,6 +77,7 @@ public class AutoscalingHistoryHandlerTest extends SolrCloudTestCase {
 
   @BeforeClass
   public static void setupCluster() throws Exception {
+    System.setProperty("metricsEnabled", "true");
     configureCluster(2)
         .addConfig("conf", configset("cloud-minimal"))
         .configure();
diff --git a/solr/core/src/test/org/apache/solr/handler/admin/MetricsHistoryHandlerTest.java b/solr/core/src/test/org/apache/solr/handler/admin/MetricsHistoryHandlerTest.java
index a796f6f..27987bd 100644
--- a/solr/core/src/test/org/apache/solr/handler/admin/MetricsHistoryHandlerTest.java
+++ b/solr/core/src/test/org/apache/solr/handler/admin/MetricsHistoryHandlerTest.java
@@ -59,6 +59,7 @@ public class MetricsHistoryHandlerTest extends SolrCloudTestCase {
   @BeforeClass
   public static void beforeClass() throws Exception {
     simulated = random().nextBoolean();
+    System.setProperty("metricsEnabled", "true");
     Map<String, Object> args = new HashMap<>();
     args.put(MetricsHistoryHandler.SYNC_PERIOD_PROP, 1);
     args.put(MetricsHistoryHandler.COLLECT_PERIOD_PROP, 1);
diff --git a/solr/core/src/test/org/apache/solr/metrics/MetricsConfigTest.java b/solr/core/src/test/org/apache/solr/metrics/MetricsConfigTest.java
index 2a3c658..7ef5895 100644
--- a/solr/core/src/test/org/apache/solr/metrics/MetricsConfigTest.java
+++ b/solr/core/src/test/org/apache/solr/metrics/MetricsConfigTest.java
@@ -116,6 +116,18 @@ public class MetricsConfigTest extends SolrTestCaseJ4 {
     assertNotNull(mockHistogramSupplier.info);
   }
 
+  @Test
+  public void testDisabledMetrics() throws Exception {
+    System.setProperty("metricsEnabled", "false");
+    NodeConfig cfg = loadNodeConfig();
+    SolrMetricManager mgr = new SolrMetricManager(cfg.getSolrResourceLoader(), cfg.getMetricsConfig());
+    assertTrue(mgr.getCounterSupplier() instanceof MetricSuppliers.NoOpCounterSupplier);
+    assertTrue(mgr.getMeterSupplier() instanceof MetricSuppliers.NoOpMeterSupplier);
+    assertTrue(mgr.getTimerSupplier() instanceof MetricSuppliers.NoOpTimerSupplier);
+    assertTrue(mgr.getHistogramSupplier() instanceof MetricSuppliers.NoOpHistogramSupplier);
+
+  }
+
   private NodeConfig loadNodeConfig() throws Exception {
     InputStream is = MetricsConfigTest.class.getResourceAsStream("/solr/solr-metricsconfig.xml");
     return SolrXmlConfig.fromInputStream(TEST_PATH(), is, new Properties()); //TODO pass in props
diff --git a/solr/core/src/test/org/apache/solr/metrics/MetricsDisabledCloudTest.java b/solr/core/src/test/org/apache/solr/metrics/MetricsDisabledCloudTest.java
new file mode 100644
index 0000000..fcd1be1
--- /dev/null
+++ b/solr/core/src/test/org/apache/solr/metrics/MetricsDisabledCloudTest.java
@@ -0,0 +1,72 @@
+/*
+ * 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.client.solrj.request.CollectionAdminRequest;
+import org.apache.solr.cloud.SolrCloudTestCase;
+import org.apache.solr.core.MetricsConfig;
+import org.apache.solr.core.NodeConfig;
+import org.junit.AfterClass;
+import org.junit.BeforeClass;
+import org.junit.Test;
+
+/**
+ *
+ */
+public class MetricsDisabledCloudTest extends SolrCloudTestCase {
+
+  @BeforeClass
+  public static void startCluster() throws Exception {
+    System.setProperty("metricsEnabled", "false");
+    configureCluster(2).configure();
+    CollectionAdminRequest.Create create = CollectionAdminRequest.createCollection("test",
+        "config", 1, 2);
+  }
+
+  @Test
+  public void testBasic() throws Exception {
+    NodeConfig cfg = cluster.getRandomJetty(random()).getCoreContainer().getNodeConfig();
+    MetricsConfig metricsConfig = cfg.getMetricsConfig();
+    assertFalse("metrics should be disabled", metricsConfig.isEnabled());
+    SolrMetricManager metricManager = cluster.getRandomJetty(random()).getCoreContainer().getMetricManager();
+    assertTrue("wrong type of supplier: " + metricManager.getCounterSupplier(),
+        metricManager.getCounterSupplier() instanceof MetricSuppliers.NoOpCounterSupplier);
+    assertTrue("wrong type of supplier: " + metricManager.getHistogramSupplier(),
+        metricManager.getHistogramSupplier() instanceof MetricSuppliers.NoOpHistogramSupplier);
+    assertTrue("wrong type of supplier: " + metricManager.getTimerSupplier(),
+        metricManager.getTimerSupplier() instanceof MetricSuppliers.NoOpTimerSupplier);
+    assertTrue("wrong type of supplier: " + metricManager.getMeterSupplier(),
+        metricManager.getMeterSupplier() instanceof MetricSuppliers.NoOpMeterSupplier);
+    for (String registryName : metricManager.registryNames()) {
+      if (!registryName.startsWith("solr.core.")) {
+        continue;
+      }
+      MetricRegistry registry = metricManager.registry(registryName);
+      registry.getMetrics().forEach((name, metric) -> {
+        assertTrue("should be NoOpMetric but was: " + name + "=" +
+            metric + "(" + metric.getClass() + ")",
+            metric instanceof MetricSuppliers.NoOpMetric);
+      });
+    }
+  }
+
+  @AfterClass
+  public static void stopCluster() throws Exception {
+    shutdownCluster();
+  }
+}
diff --git a/solr/core/src/test/org/apache/solr/metrics/reporters/SolrJmxReporterCloudTest.java b/solr/core/src/test/org/apache/solr/metrics/reporters/SolrJmxReporterCloudTest.java
index 94205b2..42e7159 100644
--- a/solr/core/src/test/org/apache/solr/metrics/reporters/SolrJmxReporterCloudTest.java
+++ b/solr/core/src/test/org/apache/solr/metrics/reporters/SolrJmxReporterCloudTest.java
@@ -52,6 +52,7 @@ public class SolrJmxReporterCloudTest extends SolrCloudTestCase {
 
   @BeforeClass
   public static void setupCluster() throws Exception {
+    System.setProperty("metricsEnabled", "true");
     // make sure there's an MBeanServer
     mBeanServer = ManagementFactory.getPlatformMBeanServer();
     configureCluster(1)
diff --git a/solr/core/src/test/org/apache/solr/metrics/reporters/solr/SolrCloudReportersTest.java b/solr/core/src/test/org/apache/solr/metrics/reporters/solr/SolrCloudReportersTest.java
index 132c91e..cbd6092 100644
--- a/solr/core/src/test/org/apache/solr/metrics/reporters/solr/SolrCloudReportersTest.java
+++ b/solr/core/src/test/org/apache/solr/metrics/reporters/solr/SolrCloudReportersTest.java
@@ -48,6 +48,7 @@ public class SolrCloudReportersTest extends SolrCloudTestCase {
 
   @BeforeClass
   public static void configureDummyCluster() throws Exception {
+    System.setProperty("metricsEnabled", "true");
     configureCluster(0).configure();
   }
 
diff --git a/solr/core/src/test/org/apache/solr/search/stats/TestDefaultStatsCache.java b/solr/core/src/test/org/apache/solr/search/stats/TestDefaultStatsCache.java
index 9b848d1..0aefb9b 100644
--- a/solr/core/src/test/org/apache/solr/search/stats/TestDefaultStatsCache.java
+++ b/solr/core/src/test/org/apache/solr/search/stats/TestDefaultStatsCache.java
@@ -29,6 +29,7 @@ public class TestDefaultStatsCache extends BaseDistributedSearchTestCase {
   
   @Override
   public void distribSetUp() throws Exception {
+    System.setProperty("metricsEnabled", "true");
     super.distribSetUp();
     System.setProperty("solr.statsCache", LocalStatsCache.class.getName());
   }
diff --git a/solr/server/solr/solr.xml b/solr/server/solr/solr.xml
index 1bb9b28..6cb5c40 100644
--- a/solr/server/solr/solr.xml
+++ b/solr/server/solr/solr.xml
@@ -55,4 +55,6 @@
     <str name="shardsWhitelist">${solr.shardsWhitelist:}</str>
   </shardHandlerFactory>
 
+  <metrics enabled="${metricsEnabled:true}"/>
+
 </solr>
diff --git a/solr/solr-ref-guide/src/metrics-history.adoc b/solr/solr-ref-guide/src/metrics-history.adoc
index 5238642..47fa48d 100644
--- a/solr/solr-ref-guide/src/metrics-history.adoc
+++ b/solr/solr-ref-guide/src/metrics-history.adoc
@@ -121,6 +121,9 @@ The handler assumes that a simple aggregation (sum of partial metric values from
 sufficient. This happens to make sense for the default built-in sets of metrics. Future extensions will
 provide other aggregation strategies (such as, average, max, min, etc.).
 
+This handler is automatically disabled when metrics collection is disabled using the
+`<metrics enabled="false">` element in `solr.xml`.
+
 == Metrics History Configuration
 There are two ways to configure this subsystem:
 
diff --git a/solr/solr-ref-guide/src/metrics-reporting.adoc b/solr/solr-ref-guide/src/metrics-reporting.adoc
index c66017c..7000456 100644
--- a/solr/solr-ref-guide/src/metrics-reporting.adoc
+++ b/solr/solr-ref-guide/src/metrics-reporting.adoc
@@ -87,6 +87,21 @@ The metrics available in your system can be customized by modifying the `<metric
 
 TIP: See also the section <<format-of-solr-xml.adoc#format-of-solr-xml,Format of Solr.xml>> for more information about the `solr.xml` file, where to find it, and how to edit it.
 
+=== Disabling the metrics collection ===
+The `<metrics>` element in `solr.xml` supports one attribute `enabled`, which takes a boolean value,
+for example `<metrics enabled="true">`.
+
+The default value of this attribute is `true`, meaning that metrics are being collected, processed and
+reported by Solr according to the configured metric reporters. They are also available from the
+metrics and metrics history APIs.
+
+The `false` value of this attribute (`<metrics enabled="false">`) turns off metrics collection, processing,
+and the collection of metrics history. Internally, all metrics suppliers are replaced by singleton no-op
+implementations, which effectively removes nearly all overheads related to metrics collection.
+All reporter configurations are skipped, and the metrics
+and metrics history APIs stop reporting any metrics and only return an `<error>`
+element in their responses.
+
 === The <metrics> <hiddenSysProps> Element
 
 This section of `solr.xml` allows you to define the system properties which are considered system-sensitive and should not be exposed via the Metrics API.
diff --git a/solr/solrj/src/java/org/apache/solr/client/solrj/cloud/autoscaling/Policy.java b/solr/solrj/src/java/org/apache/solr/client/solrj/cloud/autoscaling/Policy.java
index 89355e5..91b34bb 100644
--- a/solr/solrj/src/java/org/apache/solr/client/solrj/cloud/autoscaling/Policy.java
+++ b/solr/solrj/src/java/org/apache/solr/client/solrj/cloud/autoscaling/Policy.java
@@ -322,7 +322,7 @@ public class Policy implements MapWriter {
           } catch (IOException e1) {
             //
           }
-          throw new RuntimeException(e.getMessage());
+          throw new RuntimeException(e.getMessage(), e);
         }
         p.setApproxVal(tmpMatrix);
       }
diff --git a/solr/solrj/src/java/org/apache/solr/client/solrj/impl/SolrClientNodeStateProvider.java b/solr/solrj/src/java/org/apache/solr/client/solrj/impl/SolrClientNodeStateProvider.java
index e164a93..b905d7a 100644
--- a/solr/solrj/src/java/org/apache/solr/client/solrj/impl/SolrClientNodeStateProvider.java
+++ b/solr/solrj/src/java/org/apache/solr/client/solrj/impl/SolrClientNodeStateProvider.java
@@ -342,30 +342,32 @@ public class SolrClientNodeStateProvider implements NodeStateProvider, MapWriter
         }
 
         NamedList<?> metrics = (NamedList<?>) rsp.nl.get("metrics");
-        if (requestedTags.contains(FREEDISK.tagName)) {
-          Object n = Utils.getObjectByPath(metrics, true, "solr.node/CONTAINER.fs.usableSpace");
-          if (n != null) ctx.getTags().put(FREEDISK.tagName, FREEDISK.convertVal(n));
-        }
-        if (requestedTags.contains(TOTALDISK.tagName)) {
-          Object n = Utils.getObjectByPath(metrics, true, "solr.node/CONTAINER.fs.totalSpace");
-          if (n != null) ctx.getTags().put(TOTALDISK.tagName, TOTALDISK.convertVal(n));
-        }
-        if (requestedTags.contains(CORES)) {
-          NamedList<?> node = (NamedList<?>) metrics.get("solr.node");
-          int count = 0;
-          for (String leafCoreMetricName : new String[]{"lazy", "loaded", "unloaded"}) {
-            Number n = (Number) node.get("CONTAINER.cores." + leafCoreMetricName);
-            if (n != null) count += n.intValue();
+        if (metrics != null) { // metrics enabled
+          if (requestedTags.contains(FREEDISK.tagName)) {
+            Object n = Utils.getObjectByPath(metrics, true, "solr.node/CONTAINER.fs.usableSpace");
+            if (n != null) ctx.getTags().put(FREEDISK.tagName, FREEDISK.convertVal(n));
+          }
+          if (requestedTags.contains(TOTALDISK.tagName)) {
+            Object n = Utils.getObjectByPath(metrics, true, "solr.node/CONTAINER.fs.totalSpace");
+            if (n != null) ctx.getTags().put(TOTALDISK.tagName, TOTALDISK.convertVal(n));
+          }
+          if (requestedTags.contains(CORES)) {
+            NamedList<?> node = (NamedList<?>) metrics.get("solr.node");
+            int count = 0;
+            for (String leafCoreMetricName : new String[]{"lazy", "loaded", "unloaded"}) {
+              Number n = (Number) node.get("CONTAINER.cores." + leafCoreMetricName);
+              if (n != null) count += n.intValue();
+            }
+            ctx.getTags().put(CORES, count);
+          }
+          if (requestedTags.contains(SYSLOADAVG)) {
+            Number n = (Number) Utils.getObjectByPath(metrics, true, "solr.jvm/os.systemLoadAverage");
+            if (n != null) ctx.getTags().put(SYSLOADAVG, n.doubleValue() * 100.0d);
+          }
+          if (requestedTags.contains(HEAPUSAGE)) {
+            Number n = (Number) Utils.getObjectByPath(metrics, true, "solr.jvm/memory.heap.usage");
+            if (n != null) ctx.getTags().put(HEAPUSAGE, n.doubleValue() * 100.0d);
           }
-          ctx.getTags().put(CORES, count);
-        }
-        if (requestedTags.contains(SYSLOADAVG)) {
-          Number n = (Number) Utils.getObjectByPath(metrics, true, "solr.jvm/os.systemLoadAverage");
-          if (n != null) ctx.getTags().put(SYSLOADAVG, n.doubleValue() * 100.0d);
-        }
-        if (requestedTags.contains(HEAPUSAGE)) {
-          Number n = (Number) Utils.getObjectByPath(metrics, true, "solr.jvm/memory.heap.usage");
-          if (n != null) ctx.getTags().put(HEAPUSAGE, n.doubleValue() * 100.0d);
         }
       } catch (Exception e) {
         throw new SolrException(SolrException.ErrorCode.SERVER_ERROR, "Error getting remote info", e);
diff --git a/solr/solrj/src/test/org/apache/solr/client/solrj/impl/CloudHttp2SolrClientRetryTest.java b/solr/solrj/src/test/org/apache/solr/client/solrj/impl/CloudHttp2SolrClientRetryTest.java
index d0266e8..8eb2fb5 100644
--- a/solr/solrj/src/test/org/apache/solr/client/solrj/impl/CloudHttp2SolrClientRetryTest.java
+++ b/solr/solrj/src/test/org/apache/solr/client/solrj/impl/CloudHttp2SolrClientRetryTest.java
@@ -37,6 +37,7 @@ public class CloudHttp2SolrClientRetryTest extends SolrCloudTestCase {
 
   @BeforeClass
   public static void setupCluster() throws Exception {
+    System.setProperty("metricsEnabled", "true");
     configureCluster(NODE_COUNT)
         .addConfig("conf", getFile("solrj").toPath().resolve("solr").resolve("configsets").resolve("streaming").resolve("conf"))
         .configure();
diff --git a/solr/solrj/src/test/org/apache/solr/client/solrj/impl/CloudHttp2SolrClientTest.java b/solr/solrj/src/test/org/apache/solr/client/solrj/impl/CloudHttp2SolrClientTest.java
index bd2c289..2db6dc7 100644
--- a/solr/solrj/src/test/org/apache/solr/client/solrj/impl/CloudHttp2SolrClientTest.java
+++ b/solr/solrj/src/test/org/apache/solr/client/solrj/impl/CloudHttp2SolrClientTest.java
@@ -104,6 +104,7 @@ public class CloudHttp2SolrClientTest extends SolrCloudTestCase {
 
   @Before
   public void setupCluster() throws Exception {
+    System.setProperty("metricsEnabled", "true");
     configureCluster(NODE_COUNT)
         .addConfig("conf", getFile("solrj").toPath().resolve("solr").resolve("configsets").resolve("streaming").resolve("conf"))
         .configure();
diff --git a/solr/solrj/src/test/org/apache/solr/client/solrj/impl/CloudSolrClientRetryTest.java b/solr/solrj/src/test/org/apache/solr/client/solrj/impl/CloudSolrClientRetryTest.java
index 9dc1524..af1d021 100644
--- a/solr/solrj/src/test/org/apache/solr/client/solrj/impl/CloudSolrClientRetryTest.java
+++ b/solr/solrj/src/test/org/apache/solr/client/solrj/impl/CloudSolrClientRetryTest.java
@@ -34,6 +34,7 @@ public class CloudSolrClientRetryTest extends SolrCloudTestCase {
 
   @BeforeClass
   public static void setupCluster() throws Exception {
+    System.setProperty("metricsEnabled", "true");
     configureCluster(NODE_COUNT)
         .addConfig("conf", getFile("solrj").toPath().resolve("solr").resolve("configsets").resolve("streaming").resolve("conf"))
         .configure();
diff --git a/solr/solrj/src/test/org/apache/solr/client/solrj/impl/CloudSolrClientTest.java b/solr/solrj/src/test/org/apache/solr/client/solrj/impl/CloudSolrClientTest.java
index 1212821..1820d90 100644
--- a/solr/solrj/src/test/org/apache/solr/client/solrj/impl/CloudSolrClientTest.java
+++ b/solr/solrj/src/test/org/apache/solr/client/solrj/impl/CloudSolrClientTest.java
@@ -102,6 +102,7 @@ public class CloudSolrClientTest extends SolrCloudTestCase {
 
   @Before
   public void setupCluster() throws Exception {
+    System.setProperty("metricsEnabled", "true");
     configureCluster(NODE_COUNT)
         .addConfig("conf", getFile("solrj").toPath().resolve("solr").resolve("configsets").resolve("streaming").resolve("conf"))
         .configure();
diff --git a/solr/test-framework/src/java/org/apache/solr/cloud/MiniSolrCloudCluster.java b/solr/test-framework/src/java/org/apache/solr/cloud/MiniSolrCloudCluster.java
index 42f12a6..458afa8 100644
--- a/solr/test-framework/src/java/org/apache/solr/cloud/MiniSolrCloudCluster.java
+++ b/solr/test-framework/src/java/org/apache/solr/cloud/MiniSolrCloudCluster.java
@@ -116,7 +116,8 @@ public class MiniSolrCloudCluster {
       "    <str name=\"zkCredentialsProvider\">${zkCredentialsProvider:org.apache.solr.common.cloud.DefaultZkCredentialsProvider}</str> \n" +
       "    <str name=\"zkACLProvider\">${zkACLProvider:org.apache.solr.common.cloud.DefaultZkACLProvider}</str> \n" +
       "  </solrcloud>\n" +
-      "  <metrics>\n" +
+      // NOTE: this turns off the metrics collection unless overriden by a sysprop
+      "  <metrics enabled=\"${metricsEnabled:false}\">\n" +
       "    <reporter name=\"default\" class=\"org.apache.solr.metrics.reporters.SolrJmxReporter\">\n" +
       "      <str name=\"rootName\">solr_${hostPort:8983}</str>\n" +
       "    </reporter>\n" +
diff --git a/solr/test-framework/src/java/org/apache/solr/cloud/SolrCloudAuthTestCase.java b/solr/test-framework/src/java/org/apache/solr/cloud/SolrCloudAuthTestCase.java
index 2d23857..462e2ab 100644
--- a/solr/test-framework/src/java/org/apache/solr/cloud/SolrCloudAuthTestCase.java
+++ b/solr/test-framework/src/java/org/apache/solr/cloud/SolrCloudAuthTestCase.java
@@ -46,6 +46,8 @@ import org.apache.solr.common.util.Utils;
 import org.apache.solr.util.TimeOut;
 import org.jose4j.jws.JsonWebSignature;
 import org.jose4j.lang.JoseException;
+import org.junit.AfterClass;
+import org.junit.BeforeClass;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
@@ -70,6 +72,16 @@ public class SolrCloudAuthTestCase extends SolrCloudTestCase {
   private static final List<String> AUTH_METRICS_TO_COMPARE = Arrays.asList("requests", "authenticated", "passThrough", "failWrongCredentials", "failMissingCredentials", "errors");
   private static final List<String> AUDIT_METRICS_TO_COMPARE = Arrays.asList("count");
 
+  @BeforeClass
+  public static void enableMetrics() {
+    System.setProperty("metricsEnabled", "true");
+  }
+
+  @AfterClass
+  public static void disableMetrics() {
+    System.clearProperty("metricsEnabled");
+
+  }
   /**
    * Used to check metric counts for PKI auth
    */