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/05 20:15:54 UTC

lucene-solr:jira/solr-9959: SOLR-9959 Simplify metrics snapshot from SolrInfoBean.getMetricsSnapshot().

Repository: lucene-solr
Updated Branches:
  refs/heads/jira/solr-9959 b9b707cce -> 404fccf86


SOLR-9959 Simplify metrics snapshot from SolrInfoBean.getMetricsSnapshot().


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

Branch: refs/heads/jira/solr-9959
Commit: 404fccf8605232aaf637f6e8d9d58a881f2dc62f
Parents: b9b707c
Author: Andrzej Bialecki <ab...@apache.org>
Authored: Wed Apr 5 22:14:47 2017 +0200
Committer: Andrzej Bialecki <ab...@apache.org>
Committed: Wed Apr 5 22:14:47 2017 +0200

----------------------------------------------------------------------
 .../plugin/AnalyticsStatisticsCollector.java    |   4 +-
 .../java/org/apache/solr/core/SolrInfoBean.java |  27 ++-
 .../handler/admin/MetricsCollectorHandler.java  |   2 +-
 .../solr/handler/admin/PluginInfoHandler.java   |   2 +-
 .../handler/admin/SolrInfoMBeanHandler.java     |   2 +-
 .../solr/handler/admin/SystemInfoHandler.java   |  66 +------
 .../solr/metrics/reporters/SolrJmxReporter.java |  31 +--
 .../org/apache/solr/search/FastLRUCache.java    |   1 +
 .../java/org/apache/solr/search/LFUCache.java   |   2 +
 .../java/org/apache/solr/search/LRUCache.java   |   3 +-
 .../apache/solr/search/SolrFieldCacheBean.java  |   2 +
 .../org/apache/solr/util/stats/MetricUtils.java | 194 ++++++++++++++-----
 .../apache/solr/core/TestJmxIntegration.java    |  14 +-
 .../handler/admin/SystemInfoHandlerTest.java    |   9 +-
 .../apache/solr/util/stats/MetricUtilsTest.java |  11 +-
 15 files changed, 228 insertions(+), 142 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/404fccf8/solr/contrib/analytics/src/java/org/apache/solr/analytics/plugin/AnalyticsStatisticsCollector.java
----------------------------------------------------------------------
diff --git a/solr/contrib/analytics/src/java/org/apache/solr/analytics/plugin/AnalyticsStatisticsCollector.java b/solr/contrib/analytics/src/java/org/apache/solr/analytics/plugin/AnalyticsStatisticsCollector.java
index a796803..657768f 100644
--- a/solr/contrib/analytics/src/java/org/apache/solr/analytics/plugin/AnalyticsStatisticsCollector.java
+++ b/solr/contrib/analytics/src/java/org/apache/solr/analytics/plugin/AnalyticsStatisticsCollector.java
@@ -88,7 +88,9 @@ public class AnalyticsStatisticsCollector {
   public Map<String, Object> getStatistics() {
 
     Map<String, Object> map = new HashMap<>();
-    map.putAll(MetricUtils.convertTimer(requestTimes, false));
+    MetricUtils.convertTimer("", requestTimes, false, false, (k, v) -> {
+      map.putAll((Map<String, Object>)v);
+    });
     map.put("requests", numRequests.longValue());
     map.put("analyticsRequests", numAnalyticsRequests.longValue());
     map.put("statsRequests", numStatsRequests.longValue());

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/404fccf8/solr/core/src/java/org/apache/solr/core/SolrInfoBean.java
----------------------------------------------------------------------
diff --git a/solr/core/src/java/org/apache/solr/core/SolrInfoBean.java b/solr/core/src/java/org/apache/solr/core/SolrInfoBean.java
index a2e213f..472b15e 100644
--- a/solr/core/src/java/org/apache/solr/core/SolrInfoBean.java
+++ b/solr/core/src/java/org/apache/solr/core/SolrInfoBean.java
@@ -20,6 +20,7 @@ import java.util.Map;
 import java.util.Set;
 
 import com.codahale.metrics.MetricRegistry;
+import org.apache.solr.metrics.SolrMetricManager;
 import org.apache.solr.util.stats.MetricUtils;
 
 /**
@@ -50,8 +51,11 @@ public interface SolrInfoBean {
   /** Category of this component */
   Category getCategory();
 
-  /** Optionally return metrics that this component reports, or null. */
-  default Map<String, Object> getMetrics() {
+  /** Optionally return a snapshot of metrics that this component reports, or null.
+   * Default implementation requires that both {@link #getMetricNames()} and
+   * {@link #getMetricRegistry()} return non-null values.
+   */
+  default Map<String, Object> getMetricsSnapshot() {
     if (getMetricRegistry() == null || getMetricNames() == null) {
       return null;
     }
@@ -59,18 +63,29 @@ public interface SolrInfoBean {
   }
 
   /**
-   * Modifiable set of metric names that this component reports, or null if none.
+   * Modifiable set of metric names that this component reports (default is null,
+   * which means none). If not null then this set is used by {@link #registerMetricName(String)}
+   * to capture what metrics names are reported from this component.
    */
-  Set<String> getMetricNames();
+  default Set<String> getMetricNames() {
+    return null;
+  }
 
   /**
-   * An instance of {@link MetricRegistry} that this component uses for keeping metrics, or null.
+   * An instance of {@link MetricRegistry} that this component uses for metrics reporting
+   * (default is null, which means no registry).
    */
   default MetricRegistry getMetricRegistry() {
     return null;
   }
 
-  /** Register a metric name that this component reports. */
+  /** Register a metric name that this component reports. This method is called by various
+   * metric registration methods in {@link org.apache.solr.metrics.SolrMetricManager} in order
+   * to capture what metric names are reported from this component (which in turn is called
+   * from {@link org.apache.solr.metrics.SolrMetricProducer#initializeMetrics(SolrMetricManager, String, String)}).
+   * <p>Default implementation registers all metrics added by a component. Implementations may
+   * override this to avoid reporting some or all metrics returned by {@link #getMetricsSnapshot()}</p>
+   */
   default void registerMetricName(String name) {
     Set<String> names = getMetricNames();
     if (names != null) {

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/404fccf8/solr/core/src/java/org/apache/solr/handler/admin/MetricsCollectorHandler.java
----------------------------------------------------------------------
diff --git a/solr/core/src/java/org/apache/solr/handler/admin/MetricsCollectorHandler.java b/solr/core/src/java/org/apache/solr/handler/admin/MetricsCollectorHandler.java
index de39a61..8474f55 100644
--- a/solr/core/src/java/org/apache/solr/handler/admin/MetricsCollectorHandler.java
+++ b/solr/core/src/java/org/apache/solr/handler/admin/MetricsCollectorHandler.java
@@ -134,7 +134,7 @@ public class MetricsCollectorHandler extends RequestHandlerBase {
 
   @Override
   public String getDescription() {
-    return "Handler for collecting and aggregating metric reports.";
+    return "Handler for collecting and aggregating SolrCloud metric reports.";
   }
 
   private static class MetricUpdateProcessor extends UpdateRequestProcessor {

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/404fccf8/solr/core/src/java/org/apache/solr/handler/admin/PluginInfoHandler.java
----------------------------------------------------------------------
diff --git a/solr/core/src/java/org/apache/solr/handler/admin/PluginInfoHandler.java b/solr/core/src/java/org/apache/solr/handler/admin/PluginInfoHandler.java
index e4457bf..8bdc478 100644
--- a/solr/core/src/java/org/apache/solr/handler/admin/PluginInfoHandler.java
+++ b/solr/core/src/java/org/apache/solr/handler/admin/PluginInfoHandler.java
@@ -63,7 +63,7 @@ public class PluginInfoHandler extends RequestHandlerBase
         info.add( "description", (m.getDescription()!=null ? m.getDescription() : na) );
 
         if (stats) {
-          info.add( "stats", m.getMetrics());
+          info.add( "stats", m.getMetricsSnapshot());
         }
       }
     }

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/404fccf8/solr/core/src/java/org/apache/solr/handler/admin/SolrInfoMBeanHandler.java
----------------------------------------------------------------------
diff --git a/solr/core/src/java/org/apache/solr/handler/admin/SolrInfoMBeanHandler.java b/solr/core/src/java/org/apache/solr/handler/admin/SolrInfoMBeanHandler.java
index 9efcaf7..3c04322 100644
--- a/solr/core/src/java/org/apache/solr/handler/admin/SolrInfoMBeanHandler.java
+++ b/solr/core/src/java/org/apache/solr/handler/admin/SolrInfoMBeanHandler.java
@@ -145,7 +145,7 @@ public class SolrInfoMBeanHandler extends RequestHandlerBase {
     mBeanInfo.add("description", m.getDescription());
 
     if (req.getParams().getFieldBool(key, "stats", false))
-      mBeanInfo.add("stats", m.getMetrics());
+      mBeanInfo.add("stats", m.getMetricsSnapshot());
 
     catInfo.add(key, mBeanInfo);
   }

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/404fccf8/solr/core/src/java/org/apache/solr/handler/admin/SystemInfoHandler.java
----------------------------------------------------------------------
diff --git a/solr/core/src/java/org/apache/solr/handler/admin/SystemInfoHandler.java b/solr/core/src/java/org/apache/solr/handler/admin/SystemInfoHandler.java
index fc1679f..ab2d449 100644
--- a/solr/core/src/java/org/apache/solr/handler/admin/SystemInfoHandler.java
+++ b/solr/core/src/java/org/apache/solr/handler/admin/SystemInfoHandler.java
@@ -16,10 +16,6 @@
  */
 package org.apache.solr.handler.admin;
 
-import java.beans.BeanInfo;
-import java.beans.IntrospectionException;
-import java.beans.Introspector;
-import java.beans.PropertyDescriptor;
 import java.io.File;
 import java.io.IOException;
 import java.io.InputStream;
@@ -27,23 +23,20 @@ import java.io.InputStreamReader;
 import java.lang.invoke.MethodHandles;
 import java.lang.management.ManagementFactory;
 import java.lang.management.OperatingSystemMXBean;
-import java.lang.management.PlatformManagedObject;
 import java.lang.management.RuntimeMXBean;
-import java.lang.reflect.InvocationTargetException;
 import java.net.InetAddress;
 import java.nio.charset.Charset;
 import java.text.DecimalFormat;
 import java.text.DecimalFormatSymbols;
-import java.util.Arrays;
 import java.util.Date;
 import java.util.LinkedList;
 import java.util.List;
 import java.util.Locale;
 
+import com.codahale.metrics.Gauge;
 import org.apache.commons.io.IOUtils;
 import org.apache.lucene.LucenePackage;
 import org.apache.lucene.util.Constants;
-import org.apache.solr.common.util.NamedList;
 import org.apache.solr.common.util.SimpleOrderedMap;
 import org.apache.solr.core.CoreContainer;
 import org.apache.solr.core.SolrCore;
@@ -53,6 +46,7 @@ import org.apache.solr.response.SolrQueryResponse;
 import org.apache.solr.schema.IndexSchema;
 import org.apache.solr.util.RTimer;
 import org.apache.solr.util.RedactionUtils;
+import org.apache.solr.util.stats.MetricUtils;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
@@ -207,29 +201,13 @@ public class SystemInfoHandler extends RequestHandlerBase
     
     OperatingSystemMXBean os = ManagementFactory.getOperatingSystemMXBean();
     info.add(NAME, os.getName()); // add at least this one
-    try {
-      // add remaining ones dynamically using Java Beans API
-      addMXBeanProperties(os, OperatingSystemMXBean.class, info);
-    } catch (IntrospectionException | ReflectiveOperationException e) {
-      log.warn("Unable to fetch properties of OperatingSystemMXBean.", e);
-    }
-
-    // There are some additional beans we want to add (not available on all JVMs):
-    for (String clazz : Arrays.asList(
-        "com.sun.management.OperatingSystemMXBean",
-        "com.sun.management.UnixOperatingSystemMXBean", 
-        "com.ibm.lang.management.OperatingSystemMXBean"
-    )) {
-      try {
-        final Class<? extends PlatformManagedObject> intf = Class.forName(clazz)
-            .asSubclass(PlatformManagedObject.class);
-        addMXBeanProperties(os, intf, info);
-      } catch (ClassNotFoundException e) {
-        // ignore
-      } catch (IntrospectionException | ReflectiveOperationException e) {
-        log.warn("Unable to fetch properties of JVM-specific OperatingSystemMXBean.", e);
+    // add remaining ones dynamically using Java Beans API
+    // also those from JVM implementation-specific classes
+    MetricUtils.addMXBeanMetrics(os, MetricUtils.OS_MXBEAN_CLASSES, null, (name, metric) -> {
+      if (info.get(name) == null) {
+        info.add(name, ((Gauge) metric).getValue());
       }
-    }
+    });
 
     // Try some command line things:
     try { 
@@ -244,34 +222,6 @@ public class SystemInfoHandler extends RequestHandlerBase
   }
   
   /**
-   * Add all bean properties of a {@link PlatformManagedObject} to the given {@link NamedList}.
-   * <p>
-   * If you are running a OpenJDK/Oracle JVM, there are nice properties in:
-   * {@code com.sun.management.UnixOperatingSystemMXBean} and
-   * {@code com.sun.management.OperatingSystemMXBean}
-   */
-  static <T extends PlatformManagedObject> void addMXBeanProperties(T obj, Class<? extends T> intf, NamedList<Object> info)
-      throws IntrospectionException, ReflectiveOperationException {
-    if (intf.isInstance(obj)) {
-      final BeanInfo beanInfo = Introspector.getBeanInfo(intf, intf.getSuperclass(), Introspector.IGNORE_ALL_BEANINFO);
-      for (final PropertyDescriptor desc : beanInfo.getPropertyDescriptors()) {
-        final String name = desc.getName();
-        if (info.get(name) == null) {
-          try {
-            final Object v = desc.getReadMethod().invoke(obj);
-            if(v != null) {
-              info.add(name, v);
-            }
-          } catch (InvocationTargetException ite) {
-            // ignore (some properties throw UOE)
-          }
-        }
-      }
-    }
-  }
-  
-  
-  /**
    * Utility function to execute a function
    */
   private static String execute( String cmd )

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/404fccf8/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 c500e7f..db3b81f 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
@@ -17,14 +17,15 @@
 package org.apache.solr.metrics.reporters;
 
 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.HashMap;
 import java.util.HashSet;
-import java.util.List;
 import java.util.Locale;
+import java.util.Map;
 import java.util.Set;
 import java.util.concurrent.locks.ReentrantLock;
 
@@ -60,6 +61,8 @@ public class SolrJmxReporter extends SolrMetricReporter {
   private MBeanServer mBeanServer;
   private MetricsMapListener listener;
 
+  private static Map<String,PluginInfo> reporters = new HashMap<>();
+
   /**
    * Creates a new instance of {@link SolrJmxReporter}.
    *
@@ -79,7 +82,12 @@ public class SolrJmxReporter extends SolrMetricReporter {
   @Override
   public synchronized void init(PluginInfo pluginInfo) {
     super.init(pluginInfo);
-
+    log.info("Initializing for registry " + registryName + ": " + pluginInfo);
+    String key = pluginInfo.name + "/" + registryName;
+    if (reporters.containsKey(key)) {
+      throw new RuntimeException("already reporting: " + reporters.get(key));
+    }
+    reporters.put(key, pluginInfo);
     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.",
@@ -95,7 +103,7 @@ public class SolrJmxReporter extends SolrMetricReporter {
       mBeanServer = JmxUtil.findMBeanServerForAgentId(agentId);
     } else {
       mBeanServer = JmxUtil.findFirstMBeanServer();
-      log.warn("No serviceUrl or agentId was configured, using first MBeanServer.", mBeanServer);
+      log.debug("No serviceUrl or agentId was configured, using first MBeanServer: " + mBeanServer);
     }
 
     if (mBeanServer == null) {
@@ -103,8 +111,7 @@ public class SolrJmxReporter extends SolrMetricReporter {
       return;
     }
 
-    JmxObjectNameFactory jmxObjectNameFactory = new JmxObjectNameFactory(pluginInfo.name, domain,
-        "instance", Integer.toHexString(this.hashCode()));
+    JmxObjectNameFactory jmxObjectNameFactory = new JmxObjectNameFactory(pluginInfo.name, domain);
     registry = metricManager.registry(registryName);
     // filter out MetricsMap gauges - we have a better way of handling them
     MetricFilter filter = (name, metric) -> !(metric instanceof MetricsMap);
@@ -120,7 +127,7 @@ public class SolrJmxReporter extends SolrMetricReporter {
     listener = new MetricsMapListener(mBeanServer, jmxObjectNameFactory);
     registry.addListener(listener);
 
-    log.info("JMX monitoring enabled at server: " + mBeanServer);
+    log.info("JMX monitoring for registry '" + registryName + "' enabled at server: " + mBeanServer);
   }
 
   /**
@@ -206,7 +213,7 @@ public class SolrJmxReporter extends SolrMetricReporter {
   }
 
   /**
-   * Retrieves the reporter's MBeanServer.
+   * Return the reporter's MBeanServer.
    *
    * @return the reporter's MBeanServer
    */
@@ -249,15 +256,17 @@ public class SolrJmxReporter extends SolrMetricReporter {
       if (!lock.tryLock()) {
         return;
       }
-      log.info("#### registering " + name + " in domain " + nameFactory.getDomain());
       try {
         ObjectName objectName = nameFactory.createName("gauges", nameFactory.getDomain(), name);
         if (server.isRegistered(objectName)) {
           // silently unregister - may have been left over from a previous reporter
           server.unregisterMBean(objectName);
         }
-        server.registerMBean(gauge, objectName);
-        registered.add(objectName);
+        // 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);
       } finally {

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/404fccf8/solr/core/src/java/org/apache/solr/search/FastLRUCache.java
----------------------------------------------------------------------
diff --git a/solr/core/src/java/org/apache/solr/search/FastLRUCache.java b/solr/core/src/java/org/apache/solr/search/FastLRUCache.java
index cb699b2..4d3f2a9 100644
--- a/solr/core/src/java/org/apache/solr/search/FastLRUCache.java
+++ b/solr/core/src/java/org/apache/solr/search/FastLRUCache.java
@@ -21,6 +21,7 @@ import org.apache.solr.common.SolrException;
 import org.apache.solr.metrics.MetricsMap;
 import org.apache.solr.metrics.SolrMetricManager;
 import org.apache.solr.util.ConcurrentLRUCache;
+import org.apache.solr.util.stats.MetricUtils;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/404fccf8/solr/core/src/java/org/apache/solr/search/LFUCache.java
----------------------------------------------------------------------
diff --git a/solr/core/src/java/org/apache/solr/search/LFUCache.java b/solr/core/src/java/org/apache/solr/search/LFUCache.java
index 82ba6d2..2a53b95 100644
--- a/solr/core/src/java/org/apache/solr/search/LFUCache.java
+++ b/solr/core/src/java/org/apache/solr/search/LFUCache.java
@@ -26,9 +26,11 @@ import java.util.concurrent.TimeUnit;
 
 import com.codahale.metrics.MetricRegistry;
 import org.apache.solr.common.SolrException;
+import org.apache.solr.core.SolrInfoBean;
 import org.apache.solr.metrics.MetricsMap;
 import org.apache.solr.metrics.SolrMetricManager;
 import org.apache.solr.util.ConcurrentLFUCache;
+import org.apache.solr.util.stats.MetricUtils;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/404fccf8/solr/core/src/java/org/apache/solr/search/LRUCache.java
----------------------------------------------------------------------
diff --git a/solr/core/src/java/org/apache/solr/search/LRUCache.java b/solr/core/src/java/org/apache/solr/search/LRUCache.java
index 6c12451..530a0a6 100644
--- a/solr/core/src/java/org/apache/solr/search/LRUCache.java
+++ b/solr/core/src/java/org/apache/solr/search/LRUCache.java
@@ -34,6 +34,7 @@ import org.apache.lucene.util.RamUsageEstimator;
 import org.apache.solr.common.SolrException;
 import org.apache.solr.metrics.MetricsMap;
 import org.apache.solr.metrics.SolrMetricManager;
+import org.apache.solr.util.stats.MetricUtils;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
@@ -372,7 +373,7 @@ public class LRUCache<K,V> extends SolrCacheBase implements SolrCache<K,V>, Acco
   public MetricRegistry getMetricRegistry() {
     return registry;
   }
-  
+
   @Override
   public String toString() {
     return name() + cacheMap != null ? cacheMap.getValue().toString() : "";

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/404fccf8/solr/core/src/java/org/apache/solr/search/SolrFieldCacheBean.java
----------------------------------------------------------------------
diff --git a/solr/core/src/java/org/apache/solr/search/SolrFieldCacheBean.java b/solr/core/src/java/org/apache/solr/search/SolrFieldCacheBean.java
index ffcc37d..2303b6f 100644
--- a/solr/core/src/java/org/apache/solr/search/SolrFieldCacheBean.java
+++ b/solr/core/src/java/org/apache/solr/search/SolrFieldCacheBean.java
@@ -17,6 +17,7 @@
 package org.apache.solr.search;
 
 import java.util.HashSet;
+import java.util.Map;
 import java.util.Set;
 
 import com.codahale.metrics.MetricRegistry;
@@ -25,6 +26,7 @@ import org.apache.solr.metrics.MetricsMap;
 import org.apache.solr.metrics.SolrMetricManager;
 import org.apache.solr.metrics.SolrMetricProducer;
 import org.apache.solr.uninverting.UninvertingReader;
+import org.apache.solr.util.stats.MetricUtils;
 
 /**
  * A SolrInfoBean that provides introspection of the Solr FieldCache

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/404fccf8/solr/core/src/java/org/apache/solr/util/stats/MetricUtils.java
----------------------------------------------------------------------
diff --git a/solr/core/src/java/org/apache/solr/util/stats/MetricUtils.java b/solr/core/src/java/org/apache/solr/util/stats/MetricUtils.java
index d54c76b..a2cab8b 100644
--- a/solr/core/src/java/org/apache/solr/util/stats/MetricUtils.java
+++ b/solr/core/src/java/org/apache/solr/util/stats/MetricUtils.java
@@ -21,6 +21,7 @@ import java.beans.IntrospectionException;
 import java.beans.Introspector;
 import java.beans.PropertyDescriptor;
 import java.lang.invoke.MethodHandles;
+import java.lang.management.OperatingSystemMXBean;
 import java.lang.management.PlatformManagedObject;
 import java.lang.reflect.InvocationTargetException;
 import java.util.Collection;
@@ -133,7 +134,7 @@ public class MetricUtils {
                                       boolean skipAggregateValues, boolean compact,
                                       Map<String, Object> metadata) {
     NamedList result = new SimpleOrderedMap();
-    toMaps(registry, shouldMatchFilters, mustMatchFilter, skipHistograms, skipAggregateValues, compact, (k, v) -> {
+    toMaps(registry, shouldMatchFilters, mustMatchFilter, skipHistograms, skipAggregateValues, compact, false, (k, v) -> {
       result.add(k, v);
     });
     if (metadata != null && !metadata.isEmpty()) {
@@ -165,7 +166,7 @@ public class MetricUtils {
                                           boolean skipAggregateValues, boolean compact,
                                           Map<String, Object> metadata, Consumer<SolrInputDocument> consumer) {
     boolean addMetadata = metadata != null && !metadata.isEmpty();
-    toMaps(registry, shouldMatchFilters, mustMatchFilter, skipHistograms, skipAggregateValues, compact, (k, v) -> {
+    toMaps(registry, shouldMatchFilters, mustMatchFilter, skipHistograms, skipAggregateValues, compact, false, (k, v) -> {
       SolrInputDocument doc = new SolrInputDocument();
       doc.setField(METRIC_NAME, k);
       toSolrInputDocument(null, doc, v);
@@ -176,6 +177,12 @@ public class MetricUtils {
     });
   }
 
+  /**
+   * Fill in a SolrInputDocument with values from a converted metric, recursively.
+   * @param prefix prefix to add to generated field names, or null if none.
+   * @param doc document to fill
+   * @param o an instance of converted metric, either a Map or a flat Object
+   */
   static void toSolrInputDocument(String prefix, SolrInputDocument doc, Object o) {
     if (!(o instanceof Map)) {
       String key = prefix != null ? prefix : VALUE;
@@ -193,9 +200,21 @@ public class MetricUtils {
     }
   }
 
+  /**
+   * Convert selected metrics to maps or to flattened objects.
+   * @param registry source of metrics
+   * @param shouldMatchFilters metrics must match any of these filters
+   * @param mustMatchFilter metrics must match this filter
+   * @param skipHistograms discard any {@link Histogram}-s and histogram parts of {@link Timer}-s.
+   * @param skipAggregateValues discard internal values of {@link AggregateMetric}-s.
+   * @param compact use compact representation for counters and gauges.
+   * @param simple use simplified representation for complex metrics - instead of a (name, map)
+   *             only the selected (name "." key, value) pairs will be produced.
+   * @param consumer consumer that accepts produced objects
+   */
   static void toMaps(MetricRegistry registry, List<MetricFilter> shouldMatchFilters,
                             MetricFilter mustMatchFilter, boolean skipHistograms, boolean skipAggregateValues,
-                            boolean compact,
+                            boolean compact, boolean simple,
                             BiConsumer<String, Object> consumer) {
     final Map<String, Metric> metrics = registry.getMetrics();
     final SortedSet<String> names = registry.getNames();
@@ -204,20 +223,20 @@ public class MetricUtils {
         .filter(s -> mustMatchFilter.matches(s, metrics.get(s)))
         .forEach(n -> {
           Metric metric = metrics.get(n);
-          convertMetric(n, metric, skipHistograms, skipAggregateValues, compact, consumer);
+          convertMetric(n, metric, skipHistograms, skipAggregateValues, compact, simple, consumer);
         });
   }
 
   /**
-   * Convert selected metrics from a registry into a map, with metrics in a compact format.
+   * Convert selected metrics from a registry into a map, with metrics in a compact AND simple format.
    * @param registry registry
    * @param names metric names
    * @return map where keys are metric names (if they were present in the registry) and values are
-   * converted metrics.
+   * converted metrics in simplified format.
    */
   public static Map<String, Object> convertMetrics(MetricRegistry registry, Collection<String> names) {
     final Map<String, Object> metrics = new HashMap<>();
-    convertMetrics(registry, names, false, true, true, (k, v) -> metrics.put(k, v));
+    convertMetrics(registry, names, false, true, true, true, (k, v) -> metrics.put(k, v));
     return metrics;
   }
 
@@ -229,28 +248,42 @@ public class MetricUtils {
    * @param skipHistograms discard any {@link Histogram}-s and histogram parts of {@link Timer}-s.
    * @param skipAggregateValues discard internal values of {@link AggregateMetric}-s.
    * @param compact use compact representation for counters and gauges.
+   * @param simple use simplified representation for complex metrics - instead of a (name, map)
+   *             only the selected (name "." key, value) pairs will be produced.
    * @param consumer consumer that accepts produced objects
    */
   public static void convertMetrics(MetricRegistry registry, Collection<String> names,
-                                    boolean skipHistograms, boolean skipAggregateValues, boolean compact,
+                                    boolean skipHistograms, boolean skipAggregateValues,
+                                    boolean compact, boolean simple,
                                     BiConsumer<String, Object> consumer) {
     final Map<String, Metric> metrics = registry.getMetrics();
     names.stream()
         .forEach(n -> {
           Metric metric = metrics.get(n);
-          convertMetric(n, metric, skipHistograms, skipAggregateValues, compact, consumer);
+          convertMetric(n, metric, skipHistograms, skipAggregateValues, compact, simple, consumer);
         });
   }
 
+  /**
+   * Convert a single instance of metric into a map or flattened object.
+   * @param n metric name
+   * @param metric metric instance
+   * @param skipHistograms discard any {@link Histogram}-s and histogram parts of {@link Timer}-s.
+   * @param skipAggregateValues discard internal values of {@link AggregateMetric}-s.
+   * @param compact use compact representation for counters and gauges.
+   * @param simple use simplified representation for complex metrics - instead of a (name, map)
+   *             only the selected (name "." key, value) pairs will be produced.
+   * @param consumer consumer that accepts produced objects
+   */
   static void convertMetric(String n, Metric metric, boolean skipHistograms, boolean skipAggregateValues,
-                              boolean compact, BiConsumer<String, Object> consumer) {
+                              boolean compact, boolean simple, BiConsumer<String, Object> consumer) {
     if (metric instanceof Counter) {
       Counter counter = (Counter) metric;
       consumer.accept(n, convertCounter(counter, compact));
     } else if (metric instanceof Gauge) {
       Gauge gauge = (Gauge) metric;
       try {
-        consumer.accept(n, convertGauge(gauge, compact));
+        convertGauge(n, gauge, simple, compact, consumer);
       } catch (InternalError ie) {
         if (n.startsWith("memory.") && ie.getMessage().contains("Memory Pool not found")) {
           LOG.warn("Error converting gauge '" + n + "', possible JDK bug: SOLR-10362", ie);
@@ -261,20 +294,26 @@ public class MetricUtils {
       }
     } else if (metric instanceof Meter) {
       Meter meter = (Meter) metric;
-      consumer.accept(n, convertMeter(meter));
+      convertMeter(n, meter, simple, consumer);
     } else if (metric instanceof Timer) {
       Timer timer = (Timer) metric;
-      consumer.accept(n, convertTimer(timer, skipHistograms));
+      convertTimer(n, timer, skipHistograms, simple, consumer);
     } else if (metric instanceof Histogram) {
       if (!skipHistograms) {
         Histogram histogram = (Histogram) metric;
-        consumer.accept(n, convertHistogram(histogram));
+        convertHistogram(n, histogram, simple, consumer);
       }
     } else if (metric instanceof AggregateMetric) {
       consumer.accept(n, convertAggregateMetric((AggregateMetric)metric, skipAggregateValues));
     }
   }
 
+  /**
+   * Convert an instance of {@link AggregateMetric}.
+   * @param metric an instance of {@link AggregateMetric}
+   * @param skipAggregateValues discard internal values of {@link AggregateMetric}-s.
+   * @return a map containing metric properties
+   */
   static Map<String, Object> convertAggregateMetric(AggregateMetric metric, boolean skipAggregateValues) {
     Map<String, Object> response = new LinkedHashMap<>();
     response.put("count", metric.size());
@@ -296,13 +335,23 @@ public class MetricUtils {
     return response;
   }
 
-  static Map<String, Object> convertHistogram(Histogram histogram) {
-    Map<String, Object> response = new LinkedHashMap<>();
+  /**
+   * Convert an instance of {@link Histogram}. NOTE: it's assumed that histogram contains non-time
+   * based values that don't require unit conversion.
+   * @param histogram an instance of {@link Histogram}
+   */
+  static void convertHistogram(String name, Histogram histogram,
+                                              boolean simple, BiConsumer<String, Object> consumer) {
     Snapshot snapshot = histogram.getSnapshot();
-    response.put("count", histogram.getCount());
-    // non-time based values
-    addSnapshot(response, snapshot, false);
-    return response;
+    if (simple) {
+      consumer.accept(name + "." + MEAN, snapshot.getMean());
+    } else {
+      Map<String, Object> response = new LinkedHashMap<>();
+      consumer.accept(name, response);
+      response.put("count", histogram.getCount());
+      // non-time based values
+      addSnapshot(response, snapshot, false);
+    }
   }
 
   // optionally convert ns to ms
@@ -331,42 +380,75 @@ public class MetricUtils {
    * Convert a {@link Timer} to a map.
    * @param timer timer instance
    * @param skipHistograms if true then discard the histogram part of the timer.
-   * @return a map containing timer properties.
    */
-  public static Map<String,Object> convertTimer(Timer timer, boolean skipHistograms) {
-    Map<String, Object> response = new LinkedHashMap<>();
-    response.put("count", timer.getCount());
-    response.put("meanRate", timer.getMeanRate());
-    response.put("1minRate", timer.getOneMinuteRate());
-    response.put("5minRate", timer.getFiveMinuteRate());
-    response.put("15minRate", timer.getFifteenMinuteRate());
-    if (!skipHistograms) {
-      // time-based values in nanoseconds
-      addSnapshot(response, timer.getSnapshot(), true);
+  public static void convertTimer(String name, Timer timer, boolean skipHistograms,
+                                                boolean simple, BiConsumer<String, Object> consumer) {
+    if (simple) {
+      consumer.accept(name + ".meanRate", timer.getMeanRate());
+    } else {
+      Map<String, Object> response = new LinkedHashMap<>();
+      consumer.accept(name, response);
+      response.put("count", timer.getCount());
+      response.put("meanRate", timer.getMeanRate());
+      response.put("1minRate", timer.getOneMinuteRate());
+      response.put("5minRate", timer.getFiveMinuteRate());
+      response.put("15minRate", timer.getFifteenMinuteRate());
+      if (!skipHistograms) {
+        // time-based values in nanoseconds
+        addSnapshot(response, timer.getSnapshot(), true);
+      }
     }
-    return response;
   }
 
-  static Map<String, Object> convertMeter(Meter meter) {
-    Map<String, Object> response = new LinkedHashMap<>();
-    response.put("count", meter.getCount());
-    response.put("meanRate", meter.getMeanRate());
-    response.put("1minRate", meter.getOneMinuteRate());
-    response.put("5minRate", meter.getFiveMinuteRate());
-    response.put("15minRate", meter.getFifteenMinuteRate());
-    return response;
+  /**
+   * Convert a {@link Meter} to a map.
+   * @param meter meter instance
+   */
+  static void convertMeter(String name, Meter meter, boolean simple, BiConsumer<String, Object> consumer) {
+    if (simple) {
+      consumer.accept(name + ".count", meter.getCount());
+    } else {
+      Map<String, Object> response = new LinkedHashMap<>();
+      response.put("count", meter.getCount());
+      response.put("meanRate", meter.getMeanRate());
+      response.put("1minRate", meter.getOneMinuteRate());
+      response.put("5minRate", meter.getFiveMinuteRate());
+      response.put("15minRate", meter.getFifteenMinuteRate());
+      consumer.accept(name, response);
+    }
   }
 
-  static Object convertGauge(Gauge gauge, boolean compact) {
-    if (compact) {
-      return gauge.getValue();
+  /**
+   * Convert a {@link Gauge}.
+   * @param gauge gauge instance
+   * @param compact if true then only return {@link Gauge#getValue()}. If false
+   *                then return a map with a "value" field.
+   */
+  static void convertGauge(String name, Gauge gauge, boolean simple, boolean compact,
+                             BiConsumer<String, Object> consumer) {
+    if (compact || simple) {
+      Object o = gauge.getValue();
+      if (simple && (o instanceof Map)) {
+        for (Map.Entry<?, ?> entry : ((Map<?, ?>)o).entrySet()) {
+          consumer.accept(name + "." + entry.getKey().toString(), entry.getValue());
+        }
+      } else {
+        consumer.accept(name, o);
+      }
     } else {
       Map<String, Object> response = new LinkedHashMap<>();
       response.put("value", gauge.getValue());
-      return response;
+      consumer.accept(name, response);
     }
   }
 
+  /**
+   * Convert a {@link Counter}
+   * @param counter counter instance
+   * @param compact if true then only return {@link Counter#getCount()}. If false
+   *                then return a map with a "count" field.
+   * @return map or object
+   */
   static Object convertCounter(Counter counter, boolean compact) {
     if (compact) {
       return counter.getCount();
@@ -430,4 +512,28 @@ public class MetricUtils {
       }
     }
   }
+
+  /**
+   * These are well-known implementations of {@link java.lang.management.OperatingSystemMXBean}.
+   * Some of them provide additional useful properties beyond those declared by the interface.
+   */
+  public static String[] OS_MXBEAN_CLASSES = new String[] {
+      OperatingSystemMXBean.class.getName(),
+      "com.sun.management.OperatingSystemMXBean",
+      "com.sun.management.UnixOperatingSystemMXBean",
+      "com.ibm.lang.management.OperatingSystemMXBean"
+  };
+
+  public static <T extends PlatformManagedObject> void addMXBeanMetrics(T obj, String[] interfaces,
+      String prefix, BiConsumer<String, Metric> consumer) {
+    for (String clazz : interfaces) {
+      try {
+        final Class<? extends PlatformManagedObject> intf = Class.forName(clazz)
+            .asSubclass(PlatformManagedObject.class);
+        MetricUtils.addMXBeanMetrics(obj, intf, null, consumer);
+      } catch (ClassNotFoundException e) {
+        // ignore
+      }
+    }
+  }
 }

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/404fccf8/solr/core/src/test/org/apache/solr/core/TestJmxIntegration.java
----------------------------------------------------------------------
diff --git a/solr/core/src/test/org/apache/solr/core/TestJmxIntegration.java b/solr/core/src/test/org/apache/solr/core/TestJmxIntegration.java
index d374be5..db941f7 100644
--- a/solr/core/src/test/org/apache/solr/core/TestJmxIntegration.java
+++ b/solr/core/src/test/org/apache/solr/core/TestJmxIntegration.java
@@ -32,12 +32,10 @@ import javax.management.AttributeNotFoundException;
 import javax.management.MBeanAttributeInfo;
 import javax.management.MBeanInfo;
 import javax.management.MBeanServer;
-import javax.management.MalformedObjectNameException;
 import javax.management.ObjectInstance;
 import javax.management.ObjectName;
 import java.lang.invoke.MethodHandles;
 import java.lang.management.ManagementFactory;
-import java.util.Hashtable;
 import java.util.Map;
 import java.util.Set;
 
@@ -89,8 +87,7 @@ public class TestJmxIntegration extends AbstractSolrTestCase {
     // agetnId or serviceUrl is that it will use whatever the "first" MBean server
     // returned by the JVM is.
 
-    nameFactory = new JmxObjectNameFactory("default", registryName,
-        "instance", Integer.toHexString(jmx.hashCode()));
+    nameFactory = new JmxObjectNameFactory("default", registryName);
   }
 
   @AfterClass
@@ -200,13 +197,4 @@ public class TestJmxIntegration extends AbstractSolrTestCase {
     log.info("After Reload: Size of infoRegistry: " + registrySize + " MBeans: " + newNumberOfObjects);
     assertEquals("Number of registered MBeans is not the same as info registry size", registrySize, newNumberOfObjects);
   }
-
-  private ObjectName getObjectName(String key, SolrInfoBean infoBean)
-      throws MalformedObjectNameException {
-    Hashtable<String, String> map = new Hashtable<>();
-    map.put("type", key);
-    map.put("id", infoBean.getName());
-    String coreName = h.getCore().getName();
-    return ObjectName.getInstance(("solr" + (null != coreName ? "/" + coreName : "")), map);
-  }
 }
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/404fccf8/solr/core/src/test/org/apache/solr/handler/admin/SystemInfoHandlerTest.java
----------------------------------------------------------------------
diff --git a/solr/core/src/test/org/apache/solr/handler/admin/SystemInfoHandlerTest.java b/solr/core/src/test/org/apache/solr/handler/admin/SystemInfoHandlerTest.java
index c961a55..92779a1 100644
--- a/solr/core/src/test/org/apache/solr/handler/admin/SystemInfoHandlerTest.java
+++ b/solr/core/src/test/org/apache/solr/handler/admin/SystemInfoHandlerTest.java
@@ -20,8 +20,11 @@ import java.lang.management.ManagementFactory;
 import java.lang.management.OperatingSystemMXBean;
 import java.util.Arrays;
 
+import com.codahale.metrics.Gauge;
+import org.apache.hadoop.metrics.MetricsUtil;
 import org.apache.lucene.util.LuceneTestCase;
 import org.apache.solr.common.util.SimpleOrderedMap;
+import org.apache.solr.util.stats.MetricUtils;
 
 
 public class SystemInfoHandlerTest extends LuceneTestCase {
@@ -36,9 +39,11 @@ public class SystemInfoHandlerTest extends LuceneTestCase {
     info.add( "version", os.getVersion() );
     info.add( "arch", os.getArch() );
 
-    // make another using addMXBeanProperties() 
+    // make another using MetricUtils.addMXBeanMetrics()
     SimpleOrderedMap<Object> info2 = new SimpleOrderedMap<>();
-    SystemInfoHandler.addMXBeanProperties( os, OperatingSystemMXBean.class, info2 );
+    MetricUtils.addMXBeanMetrics( os, OperatingSystemMXBean.class, null, (k, v) -> {
+      info2.add(k, ((Gauge)v).getValue());
+    } );
 
     // make sure they got the same thing
     for (String p : Arrays.asList("name", "version", "arch")) {

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/404fccf8/solr/core/src/test/org/apache/solr/util/stats/MetricUtilsTest.java
----------------------------------------------------------------------
diff --git a/solr/core/src/test/org/apache/solr/util/stats/MetricUtilsTest.java b/solr/core/src/test/org/apache/solr/util/stats/MetricUtilsTest.java
index aa02de5..b852a28 100644
--- a/solr/core/src/test/org/apache/solr/util/stats/MetricUtilsTest.java
+++ b/solr/core/src/test/org/apache/solr/util/stats/MetricUtilsTest.java
@@ -18,6 +18,7 @@
 package org.apache.solr.util.stats;
 
 import java.util.Collections;
+import java.util.HashMap;
 import java.util.Map;
 import java.util.concurrent.TimeUnit;
 
@@ -45,7 +46,11 @@ public class MetricUtilsTest extends SolrTestCaseJ4 {
       timer.update(Math.abs(random().nextInt()) + 1, TimeUnit.NANOSECONDS);
     }
     // obtain timer metrics
-    NamedList lst = new NamedList(MetricUtils.convertTimer(timer, false));
+    Map<String,Object> map = new HashMap<>();
+    MetricUtils.convertTimer("", timer, false, false, (k, v) -> {
+      map.putAll((Map<String,Object>)v);
+    });
+    NamedList lst = new NamedList(map);
     // check that expected metrics were obtained
     assertEquals(14, lst.size());
     final Snapshot snapshot = timer.getSnapshot();
@@ -84,7 +89,7 @@ public class MetricUtilsTest extends SolrTestCaseJ4 {
     Gauge<Long> error = () -> {throw new InternalError("Memory Pool not found error");};
     registry.register("memory.expected.error", error);
     MetricUtils.toMaps(registry, Collections.singletonList(MetricFilter.ALL), MetricFilter.ALL,
-        false, false, false, (k, o) -> {
+        false, false, false, false, (k, o) -> {
       Map v = (Map)o;
       if (k.startsWith("counter")) {
         assertEquals(1L, v.get("count"));
@@ -114,7 +119,7 @@ public class MetricUtilsTest extends SolrTestCaseJ4 {
     });
     // test compact format
     MetricUtils.toMaps(registry, Collections.singletonList(MetricFilter.ALL), MetricFilter.ALL,
-        false, false, true, (k, o) -> {
+        false, false, true, false, (k, o) -> {
           if (k.startsWith("counter")) {
             assertTrue(o instanceof Long);
             assertEquals(1L, o);