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 2017/04/10 17:18:07 UTC

lucene-solr:jira/solr-9959: SOLR-9959 Several improvements and fixes: * Add support for multiple "prefix" filters to most reporters. * Add support for "enabled" flag to all reporters. * Refactor JMX reporter to reuse created MBean servers for a specific

Repository: lucene-solr
Updated Branches:
  refs/heads/jira/solr-9959 75dcf4f4e -> 4c774f1dc


SOLR-9959 Several improvements and fixes:
* Add support for multiple "prefix" filters to most reporters.
* Add support for "enabled" flag to all reporters.
* Refactor JMX reporter to reuse created MBean servers for a specific serviceUrl. Use
  the same pattern for Graphite and Ganglia.
* Don't use reporter name in JMX object names.


Project: http://git-wip-us.apache.org/repos/asf/lucene-solr/repo
Commit: http://git-wip-us.apache.org/repos/asf/lucene-solr/commit/4c774f1d
Tree: http://git-wip-us.apache.org/repos/asf/lucene-solr/tree/4c774f1d
Diff: http://git-wip-us.apache.org/repos/asf/lucene-solr/diff/4c774f1d

Branch: refs/heads/jira/solr-9959
Commit: 4c774f1dc4b7added526a656a4ae2f42b744b014
Parents: 75dcf4f
Author: Andrzej Bialecki <ab...@apache.org>
Authored: Mon Apr 10 19:15:06 2017 +0200
Committer: Andrzej Bialecki <ab...@apache.org>
Committed: Mon Apr 10 19:15:06 2017 +0200

----------------------------------------------------------------------
 .../src/java/org/apache/solr/core/SolrCore.java |   2 +-
 .../org/apache/solr/core/SolrXmlConfig.java     |   4 +-
 .../solr/metrics/SolrCoreMetricManager.java     |   3 +
 .../apache/solr/metrics/SolrMetricManager.java  |  30 ++++++
 .../apache/solr/metrics/SolrMetricReporter.java |  12 +++
 .../metrics/reporters/JmxObjectNameFactory.java |  11 +-
 .../reporters/MetricServiceRegistry.java        |  23 ++++
 .../metrics/reporters/SolrGangliaReporter.java  |  55 ++++++++--
 .../metrics/reporters/SolrGraphiteReporter.java |  49 +++++++--
 .../solr/metrics/reporters/SolrJmxReporter.java | 106 ++++++++++++-------
 .../metrics/reporters/SolrSlf4jReporter.java    |  29 ++++-
 .../reporters/solr/SolrClusterReporter.java     |  14 +++
 .../reporters/solr/SolrShardReporter.java       |  14 ++-
 .../src/test-files/solr/solr-solrreporter.xml   |   4 +
 .../solr/cloud/ReplicationFactorTest.java       |   8 --
 .../solr/metrics/SolrMetricReporterTest.java    |   1 +
 .../metrics/reporters/SolrJmxReporterTest.java  |  27 ++++-
 .../java/org/apache/solr/SolrTestCaseJ4.java    |  14 +++
 .../cloud/AbstractFullDistribZkTestBase.java    |   8 --
 .../java/org/apache/solr/util/TestHarness.java  |   4 +-
 20 files changed, 327 insertions(+), 91 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/4c774f1d/solr/core/src/java/org/apache/solr/core/SolrCore.java
----------------------------------------------------------------------
diff --git a/solr/core/src/java/org/apache/solr/core/SolrCore.java b/solr/core/src/java/org/apache/solr/core/SolrCore.java
index 419cafc..f099452 100644
--- a/solr/core/src/java/org/apache/solr/core/SolrCore.java
+++ b/solr/core/src/java/org/apache/solr/core/SolrCore.java
@@ -914,7 +914,7 @@ public final class SolrCore implements SolrInfoBean, SolrMetricProducer, Closeab
     SolrFieldCacheBean solrFieldCacheBean = new SolrFieldCacheBean();
     // this is registered at the CONTAINER level because it's not core-specific - for now we
     // also register it here for back-compat
-    solrFieldCacheBean.initializeMetrics(metricManager, coreMetricManager.getRegistryName(), getCategory().toString());
+    solrFieldCacheBean.initializeMetrics(metricManager, coreMetricManager.getRegistryName(), "core");
     infoRegistry.put("fieldCache", solrFieldCacheBean);
 
 

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/4c774f1d/solr/core/src/java/org/apache/solr/core/SolrXmlConfig.java
----------------------------------------------------------------------
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 c6fe2ae..5f68286 100644
--- a/solr/core/src/java/org/apache/solr/core/SolrXmlConfig.java
+++ b/solr/core/src/java/org/apache/solr/core/SolrXmlConfig.java
@@ -471,10 +471,10 @@ public class SolrXmlConfig {
     MBeanServer mBeanServer = JmxUtil.findFirstMBeanServer();
     if (mBeanServer != null && !hasJmxReporter) {
       log.info("MBean server found: " + mBeanServer + ", but no JMX reporters were configured - adding default JMX reporter.");
-      Map<String,String> attributes = new HashMap<>();
+      Map<String,Object> attributes = new HashMap<>();
       attributes.put("name", "default");
       attributes.put("class", SolrJmxReporter.class.getName());
-      PluginInfo defaultPlugin = new PluginInfo("reporter", attributes, null, null);
+      PluginInfo defaultPlugin = new PluginInfo("reporter", attributes);
       configs.add(defaultPlugin);
     }
     return configs.toArray(new PluginInfo[configs.size()]);

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/4c774f1d/solr/core/src/java/org/apache/solr/metrics/SolrCoreMetricManager.java
----------------------------------------------------------------------
diff --git a/solr/core/src/java/org/apache/solr/metrics/SolrCoreMetricManager.java b/solr/core/src/java/org/apache/solr/metrics/SolrCoreMetricManager.java
index 2a71047..193bf68 100644
--- a/solr/core/src/java/org/apache/solr/metrics/SolrCoreMetricManager.java
+++ b/solr/core/src/java/org/apache/solr/metrics/SolrCoreMetricManager.java
@@ -144,6 +144,9 @@ public class SolrCoreMetricManager implements Closeable {
   @Override
   public void close() throws IOException {
     metricManager.closeReporters(getRegistryName(), tag);
+    if (getLeaderRegistryName() != null) {
+      metricManager.closeReporters(getLeaderRegistryName(), tag);
+    }
   }
 
   public SolrCore getCore() {

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/4c774f1d/solr/core/src/java/org/apache/solr/metrics/SolrMetricManager.java
----------------------------------------------------------------------
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 a285b4c..d4eb06a 100644
--- a/solr/core/src/java/org/apache/solr/metrics/SolrMetricManager.java
+++ b/solr/core/src/java/org/apache/solr/metrics/SolrMetricManager.java
@@ -277,6 +277,36 @@ public class SolrMetricManager {
     }
   }
 
+  public static class AndFilter implements MetricFilter {
+    List<MetricFilter> filters = new ArrayList<>();
+
+    public AndFilter(Collection<MetricFilter> filters) {
+      if (filters != null) {
+        this.filters.addAll(filters);
+      }
+    }
+
+    public AndFilter(MetricFilter... filters) {
+      if (filters != null) {
+        for (MetricFilter filter : filters) {
+          if (filter != null) {
+            this.filters.add(filter);
+          }
+        }
+      }
+    }
+
+    @Override
+    public boolean matches(String s, Metric metric) {
+      for (MetricFilter filter : filters) {
+        if (!filter.matches(s, metric)) {
+          return false;
+        }
+      }
+      return true;
+    }
+  }
+
   /**
    * Return a set of existing registry names.
    */

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/4c774f1d/solr/core/src/java/org/apache/solr/metrics/SolrMetricReporter.java
----------------------------------------------------------------------
diff --git a/solr/core/src/java/org/apache/solr/metrics/SolrMetricReporter.java b/solr/core/src/java/org/apache/solr/metrics/SolrMetricReporter.java
index ff2d3fc..9ad15d0 100644
--- a/solr/core/src/java/org/apache/solr/metrics/SolrMetricReporter.java
+++ b/solr/core/src/java/org/apache/solr/metrics/SolrMetricReporter.java
@@ -30,6 +30,7 @@ public abstract class SolrMetricReporter implements Closeable, PluginInfoInitial
   protected final String registryName;
   protected final SolrMetricManager metricManager;
   protected PluginInfo pluginInfo;
+  protected boolean enabled = true;
 
   /**
    * Create a reporter for metrics managed in a named registry.
@@ -58,6 +59,17 @@ public abstract class SolrMetricReporter implements Closeable, PluginInfoInitial
   }
 
   /**
+   * Enable reporting, defaults to true. Implementations should check this flag in
+   * {@link #validate()} and accordingly enable or disable reporting.
+   * @param enabled enable, defaults to true when null or not set.
+   */
+  public void setEnabled(Boolean enabled) {
+    if (enabled != null) {
+      this.enabled = enabled;
+    }
+  }
+
+  /**
    * Get the effective {@link PluginInfo} instance that was used for
    * initialization of this plugin.
    * @return plugin info, or null if not yet initialized.

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/4c774f1d/solr/core/src/java/org/apache/solr/metrics/reporters/JmxObjectNameFactory.java
----------------------------------------------------------------------
diff --git a/solr/core/src/java/org/apache/solr/metrics/reporters/JmxObjectNameFactory.java b/solr/core/src/java/org/apache/solr/metrics/reporters/JmxObjectNameFactory.java
index 7405f6e..b337cdd 100644
--- a/solr/core/src/java/org/apache/solr/metrics/reporters/JmxObjectNameFactory.java
+++ b/solr/core/src/java/org/apache/solr/metrics/reporters/JmxObjectNameFactory.java
@@ -105,9 +105,11 @@ public class JmxObjectNameFactory implements ObjectNameFactory {
       sb.append(currentDomain);
       sb.append(':');
     }
-    sb.append("reporter=");
-    sb.append(reporterName);
+//    sb.append("reporter=");
+//    sb.append(reporterName);
+//    sb.append(',');
     if (props != null && props.length > 0) {
+      boolean added = false;
       for (int i = 0; i < props.length; i += 2) {
         if (props[i] == null || props[i].isEmpty()) {
           continue;
@@ -119,9 +121,12 @@ public class JmxObjectNameFactory implements ObjectNameFactory {
         sb.append(props[i]);
         sb.append('=');
         sb.append(props[i + 1]);
+        added = true;
+      }
+      if (added) {
+        sb.append(',');
       }
     }
-    sb.append(',');
     if (metricInfo != null) {
       sb.append("category=");
       sb.append(metricInfo.category.toString());

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/4c774f1d/solr/core/src/java/org/apache/solr/metrics/reporters/MetricServiceRegistry.java
----------------------------------------------------------------------
diff --git a/solr/core/src/java/org/apache/solr/metrics/reporters/MetricServiceRegistry.java b/solr/core/src/java/org/apache/solr/metrics/reporters/MetricServiceRegistry.java
new file mode 100644
index 0000000..bacb104
--- /dev/null
+++ b/solr/core/src/java/org/apache/solr/metrics/reporters/MetricServiceRegistry.java
@@ -0,0 +1,23 @@
+package org.apache.solr.metrics.reporters;
+
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+
+/**
+ *
+ */
+public class MetricServiceRegistry<T> {
+  protected Map<String, T> registry = new ConcurrentHashMap<>();
+
+  public void register(String id, T service) {
+    registry.put(id, service);
+  }
+
+  public T get(String id) {
+    return registry.get(id);
+  }
+
+  public T unregister(String id) {
+    return registry.remove(id);
+  }
+}

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/4c774f1d/solr/core/src/java/org/apache/solr/metrics/reporters/SolrGangliaReporter.java
----------------------------------------------------------------------
diff --git a/solr/core/src/java/org/apache/solr/metrics/reporters/SolrGangliaReporter.java b/solr/core/src/java/org/apache/solr/metrics/reporters/SolrGangliaReporter.java
index 45561e5..28f7e83 100644
--- a/solr/core/src/java/org/apache/solr/metrics/reporters/SolrGangliaReporter.java
+++ b/solr/core/src/java/org/apache/solr/metrics/reporters/SolrGangliaReporter.java
@@ -17,6 +17,9 @@
 package org.apache.solr.metrics.reporters;
 
 import java.io.IOException;
+import java.lang.invoke.MethodHandles;
+import java.util.ArrayList;
+import java.util.List;
 import java.util.concurrent.TimeUnit;
 
 import com.codahale.metrics.MetricFilter;
@@ -24,21 +27,26 @@ import com.codahale.metrics.ganglia.GangliaReporter;
 import info.ganglia.gmetric4j.gmetric.GMetric;
 import org.apache.solr.metrics.SolrMetricManager;
 import org.apache.solr.metrics.SolrMetricReporter;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
 
 /**
  *
  */
 public class SolrGangliaReporter extends SolrMetricReporter {
+  private static final Logger log = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass());
 
   private String host = null;
   private int port = -1;
   private boolean multicast;
   private int period = 60;
   private String instancePrefix = null;
-  private String filterPrefix = null;
+  private List<String> filters = new ArrayList<>();
   private boolean testing;
   private GangliaReporter reporter;
 
+  private static final MetricServiceRegistry<GMetric> serviceRegistry = new MetricServiceRegistry<>();
+
   // for unit tests
   GMetric ganglia = null;
 
@@ -65,10 +73,24 @@ public class SolrGangliaReporter extends SolrMetricReporter {
     this.instancePrefix = prefix;
   }
 
-  public void setFilter(String filter) {
-    this.filterPrefix = filter;
+  /**
+   * Report only metrics with names matching any of the prefix filters.
+   * @param filters list of 0 or more prefixes. If the list is empty then
+   *                all names will match.
+   */
+  public void setFilter(List<String> filters) {
+    if (filters == null || filters.isEmpty()) {
+      return;
+    }
+    this.filters.addAll(filters);
   }
 
+  // due to vagaries of SolrPluginUtils.invokeSetters we need this too
+  public void setFilter(String filter) {
+    if (filter != null && !filter.isEmpty()) {
+      this.filters.add(filter);
+    }
+  }
 
   public void setPeriod(int period) {
     this.period = period;
@@ -89,6 +111,10 @@ public class SolrGangliaReporter extends SolrMetricReporter {
 
   @Override
   protected void validate() throws IllegalStateException {
+    if (!enabled) {
+      log.info("Reporter disabled for registry " + registryName);
+      return;
+    }
     if (host == null) {
       throw new IllegalStateException("Init argument 'host' must be set to a valid Ganglia server name.");
     }
@@ -106,12 +132,19 @@ public class SolrGangliaReporter extends SolrMetricReporter {
   //this is a separate method for unit tests
   void start() {
     if (!testing) {
-      try {
-        ganglia = new GMetric(host, port,
-            multicast ? GMetric.UDPAddressingMode.MULTICAST : GMetric.UDPAddressingMode.UNICAST,
-            1);
-      } catch (IOException ioe) {
-        throw new IllegalStateException("Exception connecting to Ganglia", ioe);
+      synchronized (serviceRegistry) {
+        String id = host + ":" + port + ":" + multicast;
+        ganglia = serviceRegistry.get(id);
+        if (ganglia == null) {
+          try {
+            ganglia = new GMetric(host, port,
+                multicast ? GMetric.UDPAddressingMode.MULTICAST : GMetric.UDPAddressingMode.UNICAST,
+                1);
+            serviceRegistry.register(id, ganglia);
+          } catch (IOException ioe) {
+            throw new IllegalStateException("Exception connecting to Ganglia", ioe);
+          }
+        }
       }
     }
     if (instancePrefix == null) {
@@ -125,8 +158,8 @@ public class SolrGangliaReporter extends SolrMetricReporter {
         .convertDurationsTo(TimeUnit.MILLISECONDS)
         .prefixedWith(instancePrefix);
     MetricFilter filter;
-    if (filterPrefix != null) {
-      filter = new SolrMetricManager.PrefixFilter(filterPrefix);
+    if (!filters.isEmpty()) {
+      filter = new SolrMetricManager.PrefixFilter(filters);
     } else {
       filter = MetricFilter.ALL;
     }

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/4c774f1d/solr/core/src/java/org/apache/solr/metrics/reporters/SolrGraphiteReporter.java
----------------------------------------------------------------------
diff --git a/solr/core/src/java/org/apache/solr/metrics/reporters/SolrGraphiteReporter.java b/solr/core/src/java/org/apache/solr/metrics/reporters/SolrGraphiteReporter.java
index 8565ce8..48416ce 100644
--- a/solr/core/src/java/org/apache/solr/metrics/reporters/SolrGraphiteReporter.java
+++ b/solr/core/src/java/org/apache/solr/metrics/reporters/SolrGraphiteReporter.java
@@ -18,6 +18,8 @@ package org.apache.solr.metrics.reporters;
 
 import java.io.IOException;
 import java.lang.invoke.MethodHandles;
+import java.util.ArrayList;
+import java.util.List;
 import java.util.concurrent.TimeUnit;
 
 import com.codahale.metrics.MetricFilter;
@@ -41,8 +43,11 @@ public class SolrGraphiteReporter extends SolrMetricReporter {
   private int period = 60;
   private boolean pickled = false;
   private String instancePrefix = null;
-  private String filterPrefix = null;
+  private List<String> filters = new ArrayList<>();
   private GraphiteReporter reporter = null;
+  private boolean enabled = true;
+
+  private static final MetricServiceRegistry<GraphiteSender> serviceRegistry = new MetricServiceRegistry<>();
 
   /**
    * Create a Graphite reporter for metrics managed in a named registry.
@@ -67,10 +72,25 @@ public class SolrGraphiteReporter extends SolrMetricReporter {
     this.instancePrefix = prefix;
   }
 
+  /**
+   * Report only metrics with names matching any of the prefix filters.
+   * @param filters list of 0 or more prefixes. If the list is empty then
+   *                all names will match.
+   */
+  public void setFilter(List<String> filters) {
+    if (filters == null || filters.isEmpty()) {
+      return;
+    }
+    this.filters.addAll(filters);
+  }
+
   public void setFilter(String filter) {
-    this.filterPrefix = filter;
+    if (filter != null && !filter.isEmpty()) {
+      this.filters.add(filter);
+    }
   }
 
+
   public void setPickled(boolean pickled) {
     this.pickled = pickled;
   }
@@ -81,6 +101,10 @@ public class SolrGraphiteReporter extends SolrMetricReporter {
 
   @Override
   protected void validate() throws IllegalStateException {
+    if (!enabled) {
+      log.info("Reporter disabled for registry " + registryName);
+      return;
+    }
     if (host == null) {
       throw new IllegalStateException("Init argument 'host' must be set to a valid Graphite server name.");
     }
@@ -93,11 +117,18 @@ public class SolrGraphiteReporter extends SolrMetricReporter {
     if (period < 1) {
       throw new IllegalStateException("Init argument 'period' is in time unit 'seconds' and must be at least 1.");
     }
-    final GraphiteSender graphite;
-    if (pickled) {
-      graphite = new PickledGraphite(host, port);
-    } else {
-      graphite = new Graphite(host, port);
+    GraphiteSender graphite;
+    String id = host + ":" + port + ":" + pickled;
+    synchronized (serviceRegistry) {
+      graphite = serviceRegistry.get(id);
+      if (graphite == null) {
+        if (pickled) {
+          graphite = new PickledGraphite(host, port);
+        } else {
+          graphite = new Graphite(host, port);
+        }
+        serviceRegistry.register(id, graphite);
+      }
     }
     if (instancePrefix == null) {
       instancePrefix = registryName;
@@ -110,8 +141,8 @@ public class SolrGraphiteReporter extends SolrMetricReporter {
         .convertRatesTo(TimeUnit.SECONDS)
         .convertDurationsTo(TimeUnit.MILLISECONDS);
     MetricFilter filter;
-    if (filterPrefix != null) {
-      filter = new SolrMetricManager.PrefixFilter(filterPrefix);
+    if (!filters.isEmpty()) {
+      filter = new SolrMetricManager.PrefixFilter(filters);
     } else {
       filter = MetricFilter.ALL;
     }

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/4c774f1d/solr/core/src/java/org/apache/solr/metrics/reporters/SolrJmxReporter.java
----------------------------------------------------------------------
diff --git a/solr/core/src/java/org/apache/solr/metrics/reporters/SolrJmxReporter.java b/solr/core/src/java/org/apache/solr/metrics/reporters/SolrJmxReporter.java
index 6f13754..edbc17c 100644
--- a/solr/core/src/java/org/apache/solr/metrics/reporters/SolrJmxReporter.java
+++ b/solr/core/src/java/org/apache/solr/metrics/reporters/SolrJmxReporter.java
@@ -16,16 +16,18 @@
  */
 package org.apache.solr.metrics.reporters;
 
+import javax.management.InstanceNotFoundException;
 import javax.management.MBeanServer;
 import javax.management.ObjectInstance;
 import javax.management.ObjectName;
 
 import java.io.IOException;
 import java.lang.invoke.MethodHandles;
+import java.util.ArrayList;
 import java.util.HashSet;
+import java.util.List;
 import java.util.Locale;
 import java.util.Set;
-import java.util.concurrent.locks.ReentrantLock;
 
 import com.codahale.metrics.Gauge;
 import com.codahale.metrics.JmxReporter;
@@ -50,10 +52,13 @@ public class SolrJmxReporter extends SolrMetricReporter {
 
   private static final Logger log = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass());
 
+  private static final MetricServiceRegistry<MBeanServer> serviceRegistry = new MetricServiceRegistry<>();
+
   private String domain;
   private String agentId;
   private String serviceUrl;
   private String rootName;
+  private List<String> filters = new ArrayList<>();
 
   private JmxReporter reporter;
   private MetricRegistry registry;
@@ -81,20 +86,29 @@ public class SolrJmxReporter extends SolrMetricReporter {
   public synchronized void init(PluginInfo pluginInfo) {
     super.init(pluginInfo);
     if (!enabled) {
-      log.info("JMX monitoring disabled for registry " + registryName);
+      log.info("Reporter disabled for registry " + registryName);
       return;
     }
     log.debug("Initializing for registry " + registryName);
     if (serviceUrl != null && agentId != null) {
       mBeanServer = JmxUtil.findFirstMBeanServer();
-      log.warn("No more than one of serviceUrl(%s) and agentId(%s) should be configured, using first MBeanServer instead of configuration.",
+      log.warn("No more than one of serviceUrl({}) and agentId({}) 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;
+      // reuse existing services
+      synchronized (serviceRegistry) {
+        mBeanServer = serviceRegistry.get(serviceUrl);
+        if (mBeanServer == null) {
+          try {
+            mBeanServer = JmxUtil.findMBeanServerForServiceUrl(serviceUrl);
+            if (mBeanServer != null) {
+              serviceRegistry.register(serviceUrl, mBeanServer);
+            }
+          } catch (IOException e) {
+            log.warn("findMBeanServerForServiceUrl({}) exception: {}", serviceUrl, e);
+            mBeanServer = null;
+          }
+        }
       }
     } else if (agentId != null) {
       mBeanServer = JmxUtil.findMBeanServerForAgentId(agentId);
@@ -108,6 +122,9 @@ public class SolrJmxReporter extends SolrMetricReporter {
       return;
     }
 
+    if (domain == null || domain.isEmpty()) {
+      domain = registryName;
+    }
     String fullDomain = domain;
     if (rootName != null && !rootName.isEmpty()) {
       fullDomain = rootName + "." + domain;
@@ -115,7 +132,15 @@ public class SolrJmxReporter extends SolrMetricReporter {
     JmxObjectNameFactory jmxObjectNameFactory = new JmxObjectNameFactory(pluginInfo.name, fullDomain);
     registry = metricManager.registry(registryName);
     // filter out MetricsMap gauges - we have a better way of handling them
-    MetricFilter filter = (name, metric) -> !(metric instanceof MetricsMap);
+    MetricFilter mmFilter = (name, metric) -> !(metric instanceof MetricsMap);
+    MetricFilter filter;
+    if (filters.isEmpty()) {
+      filter = mmFilter;
+    } else {
+      // apply also prefix filters
+      SolrMetricManager.PrefixFilter prefixFilter = new SolrMetricManager.PrefixFilter(filters);
+      filter = new SolrMetricManager.AndFilter(prefixFilter, mmFilter);
+    }
 
     reporter = JmxReporter.forRegistry(registry)
                           .registerWith(mBeanServer)
@@ -224,14 +249,20 @@ public class SolrJmxReporter extends SolrMetricReporter {
   }
 
   /**
-   * Enable reporting, defaults to true.
-   * @param enabled enable, defaults to true when null or not set.
+   * Report only metrics with names matching any of the prefix filters.
+   * @param filters list of 0 or more prefixes. If the list is empty then
+   *                all names will match.
    */
-  public void setEnabled(Boolean enabled) {
-    if (enabled == null) {
-      this.enabled = true;
-    } else {
-      this.enabled = enabled;
+  public void setFilter(List<String> filters) {
+    if (filters == null || filters.isEmpty()) {
+      return;
+    }
+    this.filters.addAll(filters);
+  }
+
+  public void setFilter(String filter) {
+    if (filter != null && !filter.isEmpty()) {
+      this.filters.add(filter);
     }
   }
 
@@ -254,7 +285,7 @@ public class SolrJmxReporter extends SolrMetricReporter {
 
   @Override
   public String toString() {
-    return String.format(Locale.ENGLISH, "[%s@%s: rootName = %, domain = %s, service url = %s, agent id = %s]",
+    return String.format(Locale.ENGLISH, "[%s@%s: rootName = %s, domain = %s, service url = %s, agent id = %s]",
         getClass().getName(), Integer.toHexString(hashCode()), rootName, domain, serviceUrl, agentId);
   }
 
@@ -263,8 +294,6 @@ public class SolrJmxReporter extends SolrMetricReporter {
     JmxObjectNameFactory nameFactory;
     // keep the names so that we can unregister them on core close
     Set<ObjectName> registered = new HashSet<>();
-    // prevent ConcurrentModificationException when closing
-    ReentrantLock lock = new ReentrantLock();
 
     MetricsMapListener(MBeanServer server, JmxObjectNameFactory nameFactory) {
       this.server = server;
@@ -276,38 +305,41 @@ public class SolrJmxReporter extends SolrMetricReporter {
       if (!(gauge instanceof MetricsMap)) {
         return;
       }
-      if (!lock.tryLock()) {
-        return;
-      }
-      try {
-        ObjectName objectName = nameFactory.createName("gauges", nameFactory.getDomain(), name);
-        // some MBean servers re-write object name to include additional properties
-        ObjectInstance instance = server.registerMBean(gauge, objectName);
-        if (instance != null) {
-          registered.add(instance.getObjectName());
+      synchronized (server) {
+        try {
+          ObjectName objectName = nameFactory.createName("gauges", nameFactory.getDomain(), name);
+          log.debug("REGISTER " + objectName);
+          if (registered.contains(objectName) || server.isRegistered(objectName)) {
+            log.debug("-unregistering old instance of " + objectName);
+            try {
+              server.unregisterMBean(objectName);
+            } catch (InstanceNotFoundException e) {
+              // ignore
+            }
+          }
+          // some MBean servers re-write object name to include additional properties
+          ObjectInstance instance = server.registerMBean(gauge, objectName);
+          if (instance != null) {
+            registered.add(instance.getObjectName());
+          }
+        } catch (Exception e) {
+          log.warn("bean registration error", e);
         }
-      } catch (Exception e) {
-        log.warn("bean registration error", e);
-      } finally {
-        lock.unlock();
       }
     }
 
     public void close() {
-      lock.lock();
-      try {
+      synchronized (server) {
         for (ObjectName name : registered) {
           try {
             if (server.isRegistered(name)) {
               server.unregisterMBean(name);
             }
           } catch (Exception e) {
-            log.warn("bean unregistration error", e);
+            log.debug("bean unregistration error", e);
           }
         }
         registered.clear();
-      } finally {
-        lock.unlock();
       }
     }
   }

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/4c774f1d/solr/core/src/java/org/apache/solr/metrics/reporters/SolrSlf4jReporter.java
----------------------------------------------------------------------
diff --git a/solr/core/src/java/org/apache/solr/metrics/reporters/SolrSlf4jReporter.java b/solr/core/src/java/org/apache/solr/metrics/reporters/SolrSlf4jReporter.java
index 817dda1..8b7c35e 100644
--- a/solr/core/src/java/org/apache/solr/metrics/reporters/SolrSlf4jReporter.java
+++ b/solr/core/src/java/org/apache/solr/metrics/reporters/SolrSlf4jReporter.java
@@ -18,6 +18,8 @@ package org.apache.solr.metrics.reporters;
 
 import java.io.IOException;
 import java.lang.invoke.MethodHandles;
+import java.util.ArrayList;
+import java.util.List;
 import java.util.concurrent.TimeUnit;
 
 import com.codahale.metrics.MetricFilter;
@@ -47,7 +49,7 @@ public class SolrSlf4jReporter extends SolrMetricReporter {
   private int period = 60;
   private String instancePrefix = null;
   private String logger = null;
-  private String filterPrefix = null;
+  private List<String> filters = new ArrayList<>();
   private Slf4jReporter reporter;
 
   /**
@@ -65,10 +67,25 @@ public class SolrSlf4jReporter extends SolrMetricReporter {
     this.instancePrefix = prefix;
   }
 
+  /**
+   * Report only metrics with names matching any of the prefix filters.
+   * @param filters list of 0 or more prefixes. If the list is empty then
+   *                all names will match.
+   */
+  public void setFilter(List<String> filters) {
+    if (filters == null || filters.isEmpty()) {
+      return;
+    }
+    this.filters.addAll(filters);
+  }
+
   public void setFilter(String filter) {
-    this.filterPrefix = filter;
+    if (filter != null && !filter.isEmpty()) {
+      this.filters.add(filter);
+    }
   }
 
+
   public void setLogger(String logger) {
     this.logger = logger;
   }
@@ -79,6 +96,10 @@ public class SolrSlf4jReporter extends SolrMetricReporter {
 
   @Override
   protected void validate() throws IllegalStateException {
+    if (!enabled) {
+      log.info("Reporter disabled for registry " + registryName);
+      return;
+    }
     if (period < 1) {
       throw new IllegalStateException("Init argument 'period' is in time unit 'seconds' and must be at least 1.");
     }
@@ -93,8 +114,8 @@ public class SolrSlf4jReporter extends SolrMetricReporter {
         .convertDurationsTo(TimeUnit.MILLISECONDS);
 
     MetricFilter filter;
-    if (filterPrefix != null) {
-      filter = new SolrMetricManager.PrefixFilter(filterPrefix);
+    if (!filters.isEmpty()) {
+      filter = new SolrMetricManager.PrefixFilter(filters);
     } else {
       filter = MetricFilter.ALL;
     }

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/4c774f1d/solr/core/src/java/org/apache/solr/metrics/reporters/solr/SolrClusterReporter.java
----------------------------------------------------------------------
diff --git a/solr/core/src/java/org/apache/solr/metrics/reporters/solr/SolrClusterReporter.java b/solr/core/src/java/org/apache/solr/metrics/reporters/solr/SolrClusterReporter.java
index 56dc36f..30d31d9 100644
--- a/solr/core/src/java/org/apache/solr/metrics/reporters/solr/SolrClusterReporter.java
+++ b/solr/core/src/java/org/apache/solr/metrics/reporters/solr/SolrClusterReporter.java
@@ -159,6 +159,16 @@ public class SolrClusterReporter extends SolrMetricReporter {
     });
   }
 
+  public void setReport(Map map) {
+    if (map == null || map.isEmpty()) {
+      return;
+    }
+    SolrReporter.Report r = SolrReporter.Report.fromMap(map);
+    if (r != null) {
+      reports.add(r);
+    }
+  }
+
   // for unit tests
   int getPeriod() {
     return period;
@@ -189,6 +199,10 @@ public class SolrClusterReporter extends SolrMetricReporter {
     if (reporter != null) {
       reporter.close();;
     }
+    if (!enabled) {
+      log.info("Reporter disabled for registry " + registryName);
+      return;
+    }
     // start reporter only in cloud mode
     if (!cc.isZooKeeperAware()) {
       log.warn("Not ZK-aware, not starting...");

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/4c774f1d/solr/core/src/java/org/apache/solr/metrics/reporters/solr/SolrShardReporter.java
----------------------------------------------------------------------
diff --git a/solr/core/src/java/org/apache/solr/metrics/reporters/solr/SolrShardReporter.java b/solr/core/src/java/org/apache/solr/metrics/reporters/solr/SolrShardReporter.java
index 8b36d3e..6cc5a07 100644
--- a/solr/core/src/java/org/apache/solr/metrics/reporters/solr/SolrShardReporter.java
+++ b/solr/core/src/java/org/apache/solr/metrics/reporters/solr/SolrShardReporter.java
@@ -98,7 +98,13 @@ public class SolrShardReporter extends SolrMetricReporter {
     if (filterConfig == null || filterConfig.isEmpty()) {
       return;
     }
-    filters = filterConfig;
+    filters.addAll(filterConfig);
+  }
+
+  public void setFilter(String filter) {
+    if (filter != null && !filter.isEmpty()) {
+      this.filters.add(filter);
+    }
   }
 
   // for unit tests
@@ -128,13 +134,17 @@ public class SolrShardReporter extends SolrMetricReporter {
     if (reporter != null) {
       reporter.close();
     }
+    if (!enabled) {
+      log.info("Reporter disabled for registry " + registryName);
+      return;
+    }
     if (core.getCoreDescriptor().getCloudDescriptor() == null) {
       // not a cloud core
       log.warn("Not initializing shard reporter for non-cloud core " + core.getName());
       return;
     }
     if (period < 1) { // don't start it
-      log.warn("Not starting shard reporter ");
+      log.warn("period=" + period + ", not starting shard reporter ");
       return;
     }
     // our id is coreNodeName

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/4c774f1d/solr/core/src/test-files/solr/solr-solrreporter.xml
----------------------------------------------------------------------
diff --git a/solr/core/src/test-files/solr/solr-solrreporter.xml b/solr/core/src/test-files/solr/solr-solrreporter.xml
index db03e42..a66d9d0 100644
--- a/solr/core/src/test-files/solr/solr-solrreporter.xml
+++ b/solr/core/src/test-files/solr/solr-solrreporter.xml
@@ -38,6 +38,10 @@
   </solrcloud>
 
   <metrics>
+    <!-- disable default JMX reporter to avoid conflicts with multiple CoreContainers. -->
+    <reporter name="defaultJmx" class="org.apache.solr.metrics.reporters.SolrJmxReporter">
+      <bool name="enabled">false</bool>
+    </reporter>
     <reporter name="test" group="shard">
       <int name="period">5</int>
       <str name="filter">UPDATE\./update/.*requests</str>

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/4c774f1d/solr/core/src/test/org/apache/solr/cloud/ReplicationFactorTest.java
----------------------------------------------------------------------
diff --git a/solr/core/src/test/org/apache/solr/cloud/ReplicationFactorTest.java b/solr/core/src/test/org/apache/solr/cloud/ReplicationFactorTest.java
index 9441e3f..5921318 100644
--- a/solr/core/src/test/org/apache/solr/cloud/ReplicationFactorTest.java
+++ b/solr/core/src/test/org/apache/solr/cloud/ReplicationFactorTest.java
@@ -71,14 +71,6 @@ public class ReplicationFactorTest extends AbstractFullDistribZkTestBase {
     return createProxiedJetty(solrHome, dataDir, shardList, solrConfigOverride, schemaOverride);
   }
   
-  protected int getNextAvailablePort() throws Exception {    
-    int port = -1;
-    try (ServerSocket s = new ServerSocket(0)) {
-      port = s.getLocalPort();
-    }
-    return port;
-  }
-
   @Test
   public void test() throws Exception {
     log.info("replication factor test running");

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/4c774f1d/solr/core/src/test/org/apache/solr/metrics/SolrMetricReporterTest.java
----------------------------------------------------------------------
diff --git a/solr/core/src/test/org/apache/solr/metrics/SolrMetricReporterTest.java b/solr/core/src/test/org/apache/solr/metrics/SolrMetricReporterTest.java
index b275919..eb9e32b 100644
--- a/solr/core/src/test/org/apache/solr/metrics/SolrMetricReporterTest.java
+++ b/solr/core/src/test/org/apache/solr/metrics/SolrMetricReporterTest.java
@@ -42,6 +42,7 @@ public class SolrMetricReporterTest extends LuceneTestCase {
     Map<String, Object> attrs = new HashMap<>();
     attrs.put(FieldType.CLASS_NAME, MockMetricReporter.class.getName());
     attrs.put(CoreAdminParams.NAME, TestUtil.randomUnicodeString(random));
+    attrs.put("enabled", String.valueOf(random.nextBoolean()));
 
     boolean shouldDefineConfigurable = random.nextBoolean();
     String configurable = TestUtil.randomUnicodeString(random);

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/4c774f1d/solr/core/src/test/org/apache/solr/metrics/reporters/SolrJmxReporterTest.java
----------------------------------------------------------------------
diff --git a/solr/core/src/test/org/apache/solr/metrics/reporters/SolrJmxReporterTest.java b/solr/core/src/test/org/apache/solr/metrics/reporters/SolrJmxReporterTest.java
index ba46691..41c75e0 100644
--- a/solr/core/src/test/org/apache/solr/metrics/reporters/SolrJmxReporterTest.java
+++ b/solr/core/src/test/org/apache/solr/metrics/reporters/SolrJmxReporterTest.java
@@ -21,6 +21,8 @@ import javax.management.ObjectInstance;
 import javax.management.ObjectName;
 
 import java.lang.management.ManagementFactory;
+import java.rmi.registry.LocateRegistry;
+import java.rmi.registry.Registry;
 import java.util.HashMap;
 import java.util.Map;
 import java.util.Random;
@@ -41,12 +43,16 @@ import org.apache.solr.metrics.SolrMetricTestUtils;
 import org.apache.solr.schema.FieldType;
 import org.junit.After;
 import org.junit.Before;
+import org.junit.BeforeClass;
 import org.junit.Test;
 
 public class SolrJmxReporterTest extends SolrTestCaseJ4 {
 
   private static final int MAX_ITERATIONS = 20;
 
+  private static Registry jmxRegistry;
+  private static int jmxPort;
+
   private String domain;
 
   private SolrCoreMetricManager coreMetricManager;
@@ -54,15 +60,24 @@ public class SolrJmxReporterTest extends SolrTestCaseJ4 {
   private SolrJmxReporter reporter;
   private MBeanServer mBeanServer;
   private String reporterName;
+  private String rootName;
+
+  @BeforeClass
+  public static void init() throws Exception {
+    jmxPort = getNextAvailablePort();
+    assertFalse(jmxPort == -1);
+    jmxRegistry = LocateRegistry.createRegistry(jmxPort);
+  }
 
   @Before
   public void beforeTest() throws Exception {
     // make sure we're running an MBeanServer
-    ManagementFactory.getPlatformMBeanServer();
+    //ManagementFactory.getPlatformMBeanServer();
     initCore("solrconfig-basic.xml", "schema.xml");
 
     final SolrCore core = h.getCore();
     domain = core.getName();
+    rootName = TestUtil.randomSimpleString(random(), 1, 10);
 
     coreMetricManager = core.getCoreMetricManager();
     metricManager = core.getCoreDescriptor().getCoreContainer().getMetricManager();
@@ -80,6 +95,8 @@ public class SolrJmxReporterTest extends SolrTestCaseJ4 {
     reporter = (SolrJmxReporter) reporters.get(taggedName);
     mBeanServer = reporter.getMBeanServer();
     assertNotNull("MBean server not found.", mBeanServer);
+    System.err.println("jmxPort=" + jmxPort);
+//    Thread.sleep(1000000000);
   }
 
   private PluginInfo createReporterPluginInfo() {
@@ -90,6 +107,8 @@ public class SolrJmxReporterTest extends SolrTestCaseJ4 {
     Map<String, Object> attrs = new HashMap<>();
     attrs.put(FieldType.CLASS_NAME, className);
     attrs.put(CoreAdminParams.NAME, reporterName);
+    attrs.put("rootName", rootName);
+    attrs.put("serviceUrl", "service:jmx:rmi:///jndi/rmi://localhost:" + jmxPort + "/solrjmx");
 
     boolean shouldOverrideDomain = random.nextBoolean();
     if (shouldOverrideDomain) {
@@ -129,7 +148,7 @@ public class SolrJmxReporterTest extends SolrTestCaseJ4 {
       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());
+                      rootName.equals(o.getObjectName().getDomain())).count());
     }
   }
 
@@ -145,7 +164,7 @@ public class SolrJmxReporterTest extends SolrTestCaseJ4 {
     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());
+        o.getObjectName().getDomain().equals(rootName)).count());
 
     h.getCoreContainer().reload(h.getCore().getName());
     PluginInfo pluginInfo = createReporterPluginInfo();
@@ -156,7 +175,7 @@ public class SolrJmxReporterTest extends SolrTestCaseJ4 {
     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());
+            rootName.equals(o.getObjectName().getDomain())).count());
   }
 
 }

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/4c774f1d/solr/test-framework/src/java/org/apache/solr/SolrTestCaseJ4.java
----------------------------------------------------------------------
diff --git a/solr/test-framework/src/java/org/apache/solr/SolrTestCaseJ4.java b/solr/test-framework/src/java/org/apache/solr/SolrTestCaseJ4.java
index 0d4cedd..54ab06d 100644
--- a/solr/test-framework/src/java/org/apache/solr/SolrTestCaseJ4.java
+++ b/solr/test-framework/src/java/org/apache/solr/SolrTestCaseJ4.java
@@ -33,6 +33,7 @@ import java.lang.annotation.Target;
 import java.lang.invoke.MethodHandles;
 import java.lang.reflect.Method;
 import java.net.MalformedURLException;
+import java.net.ServerSocket;
 import java.net.URL;
 import java.nio.charset.Charset;
 import java.nio.charset.StandardCharsets;
@@ -802,6 +803,19 @@ public abstract class SolrTestCaseJ4 extends LuceneTestCase {
     configString = schemaString = null;
   }
 
+  /**
+   * Find next available local port.
+   * @return available port number or -1 if none could be found
+   * @throws Exception on IO errors
+   */
+  protected static int getNextAvailablePort() throws Exception {
+    int port = -1;
+    try (ServerSocket s = new ServerSocket(0)) {
+      port = s.getLocalPort();
+    }
+    return port;
+  }
+
 
   /** Validates an update XML String is successful
    */

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/4c774f1d/solr/test-framework/src/java/org/apache/solr/cloud/AbstractFullDistribZkTestBase.java
----------------------------------------------------------------------
diff --git a/solr/test-framework/src/java/org/apache/solr/cloud/AbstractFullDistribZkTestBase.java b/solr/test-framework/src/java/org/apache/solr/cloud/AbstractFullDistribZkTestBase.java
index ade1c69..0a06d78 100644
--- a/solr/test-framework/src/java/org/apache/solr/cloud/AbstractFullDistribZkTestBase.java
+++ b/solr/test-framework/src/java/org/apache/solr/cloud/AbstractFullDistribZkTestBase.java
@@ -598,14 +598,6 @@ public abstract class AbstractFullDistribZkTestBase extends AbstractDistribZkTes
     return proxy;
   }
 
-  protected int getNextAvailablePort() throws Exception {
-    int port = -1;
-    try (ServerSocket s = new ServerSocket(0)) {
-      port = s.getLocalPort();
-    }
-    return port;
-  }
-
   private File getRelativeSolrHomePath(File solrHome) {
     final Path solrHomePath = solrHome.toPath();
     final Path curDirPath = new File("").getAbsoluteFile().toPath();

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/4c774f1d/solr/test-framework/src/java/org/apache/solr/util/TestHarness.java
----------------------------------------------------------------------
diff --git a/solr/test-framework/src/java/org/apache/solr/util/TestHarness.java b/solr/test-framework/src/java/org/apache/solr/util/TestHarness.java
index 2386681..982f2b1 100644
--- a/solr/test-framework/src/java/org/apache/solr/util/TestHarness.java
+++ b/solr/test-framework/src/java/org/apache/solr/util/TestHarness.java
@@ -193,10 +193,10 @@ public class TestHarness extends BaseTestHarness {
                                        30000, 30000,
                                         UpdateShardHandlerConfig.DEFAULT_METRICNAMESTRATEGY);
     // universal default metric reporter
-    Map<String,String> attributes = new HashMap<>();
+    Map<String,Object> attributes = new HashMap<>();
     attributes.put("name", "default");
     attributes.put("class", SolrJmxReporter.class.getName());
-    PluginInfo defaultPlugin = new PluginInfo("reporter", attributes, null, null);
+    PluginInfo defaultPlugin = new PluginInfo("reporter", attributes);
 
     return new NodeConfig.NodeConfigBuilder("testNode", loader)
         .setUseSchemaCache(Boolean.getBoolean("shareSchema"))