HBASE-25002 Create simple pattern matching query for retrieving metri… (#2370)

* HBASE-25002 Create simple pattern matching query for retrieving metrics matching the pattern

* Address review comments

* Final set of comments addressed

* Address checkstyle comments
This commit is contained in:
ramkrish86 2020-09-14 19:07:40 +05:30 committed by GitHub
parent 724a0e5500
commit a3f40287ad
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 97 additions and 8 deletions

View File

@ -67,6 +67,21 @@ import org.slf4j.LoggerFactory;
* </code> will return the cluster id of the namenode mxbean.
* </p>
* <p>
* If we are not sure on the exact attribute and we want to get all the attributes that match one or
* more given pattern then the format is
* <code>http://.../jmx?get=MXBeanName::*[RegExp1],*[RegExp2]</code>
* </p>
* <p>
* For example
* <code>
* <p>
* http://../jmx?get=Hadoop:service=HBase,name=RegionServer,sub=Tables::[a-zA-z_0-9]*memStoreSize
* </p>
* <p>
* http://../jmx?get=Hadoop:service=HBase,name=RegionServer,sub=Tables::[a-zA-z_0-9]*memStoreSize,[a-zA-z_0-9]*storeFileSize
* </p>
* </code>
* </p>
* If the <code>qry</code> or the <code>get</code> parameter is not formatted
* correctly then a 400 BAD REQUEST http response code will be returned.
* </p>

View File

@ -25,6 +25,8 @@ import java.lang.reflect.Array;
import java.nio.charset.StandardCharsets;
import java.util.Iterator;
import java.util.Set;
import java.util.regex.Pattern;
import javax.management.AttributeNotFoundException;
import javax.management.InstanceNotFoundException;
import javax.management.IntrospectionException;
@ -40,18 +42,20 @@ import javax.management.RuntimeMBeanException;
import javax.management.openmbean.CompositeData;
import javax.management.openmbean.CompositeType;
import javax.management.openmbean.TabularData;
import org.apache.yetus.audience.InterfaceAudience;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.apache.hbase.thirdparty.com.google.gson.Gson;
import org.apache.hbase.thirdparty.com.google.gson.stream.JsonWriter;
import org.apache.yetus.audience.InterfaceAudience;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* Utility for doing JSON and MBeans.
*/
@InterfaceAudience.Private
public class JSONBean {
private static final String COMMA = ",";
private static final String ASTERICK = "*";
private static final Logger LOG = LoggerFactory.getLogger(JSONBean.class);
private static final Gson GSON = GsonUtil.createGson().create();
@ -125,11 +129,12 @@ public class JSONBean {
*/
private static int write(JsonWriter writer, MBeanServer mBeanServer, ObjectName qry,
String attribute, boolean description) throws IOException {
LOG.trace("Listing beans for " + qry);
LOG.debug("Listing beans for {}", qry);
Set<ObjectName> names = null;
names = mBeanServer.queryNames(qry, null);
writer.name("beans").beginArray();
Iterator<ObjectName> it = names.iterator();
Pattern[] matchingPattern = null;
while (it.hasNext()) {
ObjectName oname = it.next();
MBeanInfo minfo;
@ -149,8 +154,24 @@ public class JSONBean {
code = (String) mBeanServer.getAttribute(oname, prs);
}
if (attribute != null) {
prs = attribute;
attributeinfo = mBeanServer.getAttribute(oname, prs);
String[] patternAttr = null;
if (attribute.contains(ASTERICK)) {
if (attribute.contains(COMMA)) {
patternAttr = attribute.split(COMMA);
} else {
patternAttr = new String[1];
patternAttr[0] = attribute;
}
matchingPattern = new Pattern[patternAttr.length];
for (int i = 0; i < patternAttr.length; i++) {
matchingPattern[i] = Pattern.compile(patternAttr[i]);
}
// nullify the attribute
attribute = null;
} else {
prs = attribute;
attributeinfo = mBeanServer.getAttribute(oname, prs);
}
}
} catch (RuntimeMBeanException e) {
// UnsupportedOperationExceptions happen in the normal course of business,
@ -216,7 +237,7 @@ public class JSONBean {
} else {
MBeanAttributeInfo[] attrs = minfo.getAttributes();
for (int i = 0; i < attrs.length; i++) {
writeAttribute(writer, mBeanServer, oname, description, attrs[i]);
writeAttribute(writer, mBeanServer, oname, description, matchingPattern, attrs[i]);
}
}
writer.endObject();
@ -226,7 +247,7 @@ public class JSONBean {
}
private static void writeAttribute(JsonWriter writer, MBeanServer mBeanServer, ObjectName oname,
boolean description, MBeanAttributeInfo attr) throws IOException {
boolean description, Pattern pattern[], MBeanAttributeInfo attr) throws IOException {
if (!attr.isReadable()) {
return;
}
@ -237,6 +258,21 @@ public class JSONBean {
if (attName.indexOf("=") >= 0 || attName.indexOf(":") >= 0 || attName.indexOf(" ") >= 0) {
return;
}
if (pattern != null) {
boolean matchFound = false;
for (Pattern compile : pattern) {
// check if we have any match
if (compile.matcher(attName).find()) {
matchFound = true;
break;
}
}
if (!matchFound) {
return;
}
}
String descriptionStr = description ? attr.getDescription() : null;
Object value = null;
try {

View File

@ -66,6 +66,12 @@ public class TestJMXJsonServlet extends HttpServerFunctionalTest {
assertTrue("'"+p+"' does not match "+value, m.find());
}
public static void assertNotFind(String re, String value) {
Pattern p = Pattern.compile(re);
Matcher m = p.matcher(value);
assertFalse("'"+p+"' should not match "+value, m.find());
}
@Test public void testQuery() throws Exception {
String result = readOutput(new URL(baseUrl, "/jmx?qry=java.lang:type=Runtime"));
LOG.info("/jmx?qry=java.lang:type=Runtime RESULT: "+result);
@ -116,7 +122,39 @@ public class TestJMXJsonServlet extends HttpServerFunctionalTest {
assertReFind("\"name\"\\s*:\\s*\"java.lang:type=Memory\"", result);
assertReFind("\"committed\"\\s*:", result);
assertReFind("\\}\\);$", result);
}
@Test
public void testGetPattern() throws Exception {
// test to get an attribute of a mbean as JSONP
String result = readOutput(
new URL(baseUrl, "/jmx?get=java.lang:type=Memory::[a-zA-z_]*NonHeapMemoryUsage"));
LOG.info("/jmx RESULT: " + result);
assertReFind("\"name\"\\s*:\\s*\"java.lang:type=Memory\"", result);
assertReFind("\"committed\"\\s*:", result);
assertReFind("\"NonHeapMemoryUsage\"\\s*:", result);
assertNotFind("\"HeapMemoryUsage\"\\s*:", result);
result =
readOutput(new URL(baseUrl, "/jmx?get=java.lang:type=Memory::[^Non]*HeapMemoryUsage"));
LOG.info("/jmx RESULT: " + result);
assertReFind("\"name\"\\s*:\\s*\"java.lang:type=Memory\"", result);
assertReFind("\"committed\"\\s*:", result);
assertReFind("\"HeapMemoryUsage\"\\s*:", result);
assertNotFind("\"NonHeapHeapMemoryUsage\"\\s*:", result);
result = readOutput(new URL(baseUrl,
"/jmx?get=java.lang:type=Memory::[a-zA-z_]*HeapMemoryUsage,[a-zA-z_]*NonHeapMemoryUsage"));
LOG.info("/jmx RESULT: " + result);
assertReFind("\"name\"\\s*:\\s*\"java.lang:type=Memory\"", result);
assertReFind("\"committed\"\\s*:", result);
}
@Test
public void testPatternMatching() throws Exception {
assertReFind("[a-zA-z_]*Table1[a-zA-z_]*memStoreSize",
"Namespace_default_table_Table1_metric_memStoreSize");
assertReFind("[a-zA-z_]*memStoreSize", "Namespace_default_table_Table1_metric_memStoreSize");
}
@Test