HBASE-12455 Add 'description' to bean and attribute output when you do /jmx?description=true

This commit is contained in:
stack 2014-11-11 12:59:30 -08:00
parent 6c2a299657
commit 804444f892
3 changed files with 72 additions and 38 deletions

View File

@ -42,14 +42,12 @@ public class BaseSourceImpl implements BaseSource, MetricsSource {
private static enum DefaultMetricsSystemInitializer { private static enum DefaultMetricsSystemInitializer {
INSTANCE; INSTANCE;
private boolean inited = false; private boolean inited = false;
private JvmMetrics jvmMetricsSource;
synchronized void init(String name) { synchronized void init(String name) {
if (inited) return; if (inited) return;
inited = true; inited = true;
DefaultMetricsSystem.initialize(HBASE_METRICS_SYSTEM_NAME); DefaultMetricsSystem.initialize(HBASE_METRICS_SYSTEM_NAME);
jvmMetricsSource = JvmMetrics.initSingleton(name, ""); JvmMetrics.initSingleton(name, "");
} }
} }

View File

@ -36,7 +36,6 @@ import javax.management.ObjectName;
import javax.management.ReflectionException; import javax.management.ReflectionException;
import javax.management.RuntimeMBeanException; import javax.management.RuntimeMBeanException;
import javax.management.RuntimeErrorException; import javax.management.RuntimeErrorException;
import javax.management.RuntimeMBeanException;
import javax.management.openmbean.CompositeData; import javax.management.openmbean.CompositeData;
import javax.management.openmbean.CompositeType; import javax.management.openmbean.CompositeType;
import javax.management.openmbean.TabularData; import javax.management.openmbean.TabularData;
@ -124,6 +123,12 @@ public class JMXJsonServlet extends HttpServlet {
private static final long serialVersionUID = 1L; private static final long serialVersionUID = 1L;
private static final String CALLBACK_PARAM = "callback"; private static final String CALLBACK_PARAM = "callback";
/**
* If query string includes 'description', then we will emit bean and attribute descriptions to
* output IFF they are not null and IFF the description is not the same as the attribute name:
* i.e. specify an URL like so: /jmx?description=true
*/
private static final String INCLUDE_DESCRIPTION = "description";
/** /**
* MBean server. * MBean server.
@ -155,8 +160,7 @@ public class JMXJsonServlet extends HttpServlet {
@Override @Override
public void doGet(HttpServletRequest request, HttpServletResponse response) { public void doGet(HttpServletRequest request, HttpServletResponse response) {
try { try {
if (!HttpServer.isInstrumentationAccessAllowed(getServletContext(), if (!HttpServer.isInstrumentationAccessAllowed(getServletContext(), request, response)) {
request, response)) {
return; return;
} }
JsonGenerator jg = null; JsonGenerator jg = null;
@ -173,6 +177,9 @@ public class JMXJsonServlet extends HttpServlet {
} else { } else {
response.setContentType("application/json; charset=utf8"); response.setContentType("application/json; charset=utf8");
} }
// Should we output description on each attribute and bean?
String tmpStr = request.getParameter(INCLUDE_DESCRIPTION);
boolean description = tmpStr != null && tmpStr.length() > 0;
jg = jsonFactory.createJsonGenerator(writer); jg = jsonFactory.createJsonGenerator(writer);
jg.disable(JsonGenerator.Feature.AUTO_CLOSE_TARGET); jg.disable(JsonGenerator.Feature.AUTO_CLOSE_TARGET);
@ -190,8 +197,7 @@ public class JMXJsonServlet extends HttpServlet {
response.setStatus(HttpServletResponse.SC_BAD_REQUEST); response.setStatus(HttpServletResponse.SC_BAD_REQUEST);
return; return;
} }
listBeans(jg, new ObjectName(splitStrings[0]), splitStrings[1], listBeans(jg, new ObjectName(splitStrings[0]), splitStrings[1], description, response);
response);
return; return;
} }
@ -200,7 +206,7 @@ public class JMXJsonServlet extends HttpServlet {
if (qry == null) { if (qry == null) {
qry = "*:*"; qry = "*:*";
} }
listBeans(jg, new ObjectName(qry), null, response); listBeans(jg, new ObjectName(qry), null, description, response);
} finally { } finally {
if (jg != null) { if (jg != null) {
jg.close(); jg.close();
@ -222,23 +228,24 @@ public class JMXJsonServlet extends HttpServlet {
} }
// --------------------------------------------------------- Private Methods // --------------------------------------------------------- Private Methods
private void listBeans(JsonGenerator jg, ObjectName qry, String attribute, private void listBeans(JsonGenerator jg, ObjectName qry, String attribute,
HttpServletResponse response) final boolean description, final HttpServletResponse response)
throws IOException { throws IOException {
LOG.trace("Listing beans for "+qry); LOG.trace("Listing beans for "+qry);
Set<ObjectName> names = null; Set<ObjectName> names = null;
names = mBeanServer.queryNames(qry, null); names = mBeanServer.queryNames(qry, null);
jg.writeArrayFieldStart("beans"); jg.writeArrayFieldStart("beans");
Iterator<ObjectName> it = names.iterator(); Iterator<ObjectName> it = names.iterator();
while (it.hasNext()) { while (it.hasNext()) {
ObjectName oname = it.next(); ObjectName oname = it.next();
MBeanInfo minfo; MBeanInfo minfo;
String code = ""; String code = "";
String descriptionStr = null;
Object attributeinfo = null; Object attributeinfo = null;
try { try {
minfo = mBeanServer.getMBeanInfo(oname); minfo = mBeanServer.getMBeanInfo(oname);
code = minfo.getClassName(); code = minfo.getClassName();
if (description) descriptionStr = minfo.getDescription();
String prs = ""; String prs = "";
try { try {
if ("org.apache.commons.modeler.BaseModelMBean".equals(code)) { if ("org.apache.commons.modeler.BaseModelMBean".equals(code)) {
@ -302,12 +309,13 @@ public class JMXJsonServlet extends HttpServlet {
jg.writeStartObject(); jg.writeStartObject();
jg.writeStringField("name", oname.toString()); jg.writeStringField("name", oname.toString());
if (description && descriptionStr != null && descriptionStr.length() > 0) {
jg.writeStringField("description", descriptionStr);
}
jg.writeStringField("modelerType", code); jg.writeStringField("modelerType", code);
if ((attribute != null) && (attributeinfo == null)) { if (attribute != null && attributeinfo == null) {
jg.writeStringField("result", "ERROR"); jg.writeStringField("result", "ERROR");
jg.writeStringField("message", "No attribute with name " + attribute jg.writeStringField("message", "No attribute with name " + attribute + " was found.");
+ " was found.");
jg.writeEndObject(); jg.writeEndObject();
jg.writeEndArray(); jg.writeEndArray();
jg.close(); jg.close();
@ -316,11 +324,11 @@ public class JMXJsonServlet extends HttpServlet {
} }
if (attribute != null) { if (attribute != null) {
writeAttribute(jg, attribute, attributeinfo); writeAttribute(jg, attribute, descriptionStr, attributeinfo);
} else { } else {
MBeanAttributeInfo attrs[] = minfo.getAttributes(); MBeanAttributeInfo attrs[] = minfo.getAttributes();
for (int i = 0; i < attrs.length; i++) { for (int i = 0; i < attrs.length; i++) {
writeAttribute(jg, oname, attrs[i]); writeAttribute(jg, oname, description, attrs[i]);
} }
} }
jg.writeEndObject(); jg.writeEndObject();
@ -328,7 +336,8 @@ public class JMXJsonServlet extends HttpServlet {
jg.writeEndArray(); jg.writeEndArray();
} }
private void writeAttribute(JsonGenerator jg, ObjectName oname, MBeanAttributeInfo attr) private void writeAttribute(JsonGenerator jg, ObjectName oname, final boolean description,
MBeanAttributeInfo attr)
throws IOException { throws IOException {
if (!attr.isReadable()) { if (!attr.isReadable()) {
return; return;
@ -337,10 +346,10 @@ public class JMXJsonServlet extends HttpServlet {
if ("modelerType".equals(attName)) { if ("modelerType".equals(attName)) {
return; return;
} }
if (attName.indexOf("=") >= 0 || attName.indexOf(":") >= 0 if (attName.indexOf("=") >= 0 || attName.indexOf(":") >= 0 || attName.indexOf(" ") >= 0) {
|| attName.indexOf(" ") >= 0) {
return; return;
} }
String descriptionStr = description? attr.getDescription(): null;
Object value = null; Object value = null;
try { try {
value = mBeanServer.getAttribute(oname, attName); value = mBeanServer.getAttribute(oname, attName);
@ -387,15 +396,30 @@ public class JMXJsonServlet extends HttpServlet {
return; return;
} }
writeAttribute(jg, attName, value); writeAttribute(jg, attName, descriptionStr, value);
} }
private void writeAttribute(JsonGenerator jg, String attName, Object value) throws IOException { private void writeAttribute(JsonGenerator jg, String attName, final String descriptionStr,
jg.writeFieldName(attName); Object value)
writeObject(jg, value); throws IOException {
boolean description = false;
if (descriptionStr != null && descriptionStr.length() > 0 && !attName.equals(descriptionStr)) {
description = true;
jg.writeFieldName(attName);
jg.writeStartObject();
jg.writeFieldName("description");
jg.writeString(descriptionStr);
jg.writeFieldName("value");
writeObject(jg, description, value);
jg.writeEndObject();
} else {
jg.writeFieldName(attName);
writeObject(jg, description, value);
}
} }
private void writeObject(JsonGenerator jg, Object value) throws IOException { private void writeObject(JsonGenerator jg, final boolean description, Object value)
throws IOException {
if(value == null) { if(value == null) {
jg.writeNull(); jg.writeNull();
} else { } else {
@ -405,7 +429,7 @@ public class JMXJsonServlet extends HttpServlet {
int len = Array.getLength(value); int len = Array.getLength(value);
for (int j = 0; j < len; j++) { for (int j = 0; j < len; j++) {
Object item = Array.get(value, j); Object item = Array.get(value, j);
writeObject(jg, item); writeObject(jg, description, item);
} }
jg.writeEndArray(); jg.writeEndArray();
} else if(value instanceof Number) { } else if(value instanceof Number) {
@ -419,15 +443,15 @@ public class JMXJsonServlet extends HttpServlet {
CompositeType comp = cds.getCompositeType(); CompositeType comp = cds.getCompositeType();
Set<String> keys = comp.keySet(); Set<String> keys = comp.keySet();
jg.writeStartObject(); jg.writeStartObject();
for(String key: keys) { for (String key: keys) {
writeAttribute(jg, key, cds.get(key)); writeAttribute(jg, key, null, cds.get(key));
} }
jg.writeEndObject(); jg.writeEndObject();
} else if(value instanceof TabularData) { } else if(value instanceof TabularData) {
TabularData tds = (TabularData)value; TabularData tds = (TabularData)value;
jg.writeStartArray(); jg.writeStartArray();
for(Object entry : tds.values()) { for(Object entry : tds.values()) {
writeObject(jg, entry); writeObject(jg, description, entry);
} }
jg.writeEndArray(); jg.writeEndArray();
} else { } else {
@ -435,4 +459,4 @@ public class JMXJsonServlet extends HttpServlet {
} }
} }
} }
} }

View File

@ -1015,26 +1015,38 @@ $ for i in `cat conf/regionservers|sort`; do ./bin/graceful_stop.sh --restart --
uncommented lines. Restart the region server for the changes to take effect.</para> uncommented lines. Restart the region server for the changes to take effect.</para>
</section> </section>
<section> <section xml:id="discovering.available.metrics">
<title>Discovering Available Metrics</title> <title>Discovering Available Metrics</title>
<para>Rather than listing each metric which HBase emits by default, you can browse through the <para>Rather than listing each metric which HBase emits by default, you can browse through the
available metrics, either as a JSON output or via JMX. At this time, the JSON output does available metrics, either as a JSON output or via JMX. Different metrics are
not include the description field which is included in the JMX view. Different metrics are
exposed for the Master process and each region server process.</para> exposed for the Master process and each region server process.</para>
<procedure> <procedure>
<title>Access a JSON Output of Available Metrics</title> <title>Access a JSON Output of Available Metrics</title>
<step> <step>
<para>After starting HBase, access the region server's web UI, at <para>After starting HBase, access the region server's web UI, at
<literal>http://localhost:60030</literal> by default.</para> <literal>http://REGIONSERVER_HOSTNAME:60030</literal> by default (or port 16030 in HBase 1.0+).</para>
</step> </step>
<step> <step>
<para>Click the <guilabel>Metrics Dump</guilabel> link near the top. The metrics for the region server are <para>Click the <guilabel>Metrics Dump</guilabel> link near the top. The metrics for the region server are
presented as a dump of the JMX bean in JSON format.</para> presented as a dump of the JMX bean in JSON format. This will dump out all metrics names and their
values.
To include metrics descriptions in the listing &mdash; this can be useful when you are exploring
what is available &mdash; add a query string of
<literal>?description=true</literal> so your URL becomes
<literal>http://REGIONSERVER_HOSTNAME:60030/jmx?description=true</literal>.
Not all beans and attributes have descriptions.
</para>
</step> </step>
<step> <step>
<para>To view metrics for the Master, connect to the Master's web UI instead (defaults to <para>To view metrics for the Master, connect to the Master's web UI instead (defaults to
<literal>http://localhost:60010</literal>) and click its <guilabel>Metrics <literal>http://localhost:60010</literal> or port 16010 in HBase 1.0+) and click its <guilabel>Metrics
Dump</guilabel> link.</para> Dump</guilabel> link.
To include metrics descriptions in the listing &mdash; this can be useful when you are exploring
what is available &mdash; add a query string of
<literal>?description=true</literal> so your URL becomes
<literal>http://REGIONSERVER_HOSTNAME:60010/jmx?description=true</literal>.
Not all beans and attributes have descriptions.
</para>
</step> </step>
</procedure> </procedure>