HBASE-15943 Add page displaying JVM process metrics

Signed-off-by: Michael Stack <stack@apache.org>
This commit is contained in:
samirMop 2017-07-03 13:00:45 +02:00 committed by Michael Stack
parent 84a5d29391
commit dcfd319b5b
13 changed files with 1016 additions and 0 deletions

View File

@ -128,6 +128,7 @@ AssignmentManager assignmentManager = master.getAssignmentManager();
<%if master.isActiveMaster() %>
<li><a href="/procedures.jsp">Procedures &amp; Locks</a></li>
</%if>
<li><a href="/processMaster.jsp">Process Metrics</a></li>
<li><a href="/logs/">Local Logs</a></li>
<li><a href="/logLevel">Log Level</a></li>
<li><a href="/dump">Debug Dump</a></li>

View File

@ -81,6 +81,7 @@ org.apache.hadoop.hbase.zookeeper.MasterAddressTracker;
<div class="collapse navbar-collapse">
<ul class="nav navbar-nav">
<li class="active"><a href="/">Home</a></li>
<li><a href="/processRS.jsp">Process Metrics</a></li>
<li><a href="/logs/">Local Logs</a></li>
<li><a href="/logLevel">Log Level</a></li>
<li><a href="/dump">Debug Dump</a></li>

View File

@ -0,0 +1,214 @@
/**
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
* */
package org.apache.hadoop.hbase.util;
import java.beans.IntrospectionException;
import java.io.IOException;
import java.io.PrintWriter;
import java.io.StringWriter;
import java.lang.management.GarbageCollectorMXBean;
import java.lang.management.ManagementFactory;
import java.lang.management.MemoryPoolMXBean;
import java.lang.management.RuntimeMXBean;
import java.util.Hashtable;
import java.util.List;
import java.util.Set;
import javax.management.InstanceNotFoundException;
import javax.management.MBeanAttributeInfo;
import javax.management.MBeanInfo;
import javax.management.MBeanServer;
import javax.management.MalformedObjectNameException;
import javax.management.ObjectName;
import javax.management.ReflectionException;
import javax.management.openmbean.CompositeData;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.codehaus.jackson.JsonNode;
import org.codehaus.jackson.JsonProcessingException;
import org.codehaus.jackson.map.ObjectMapper;
import org.codehaus.jettison.json.JSONException;
public final class JSONMetricUtil {
private static final Log LOG = LogFactory.getLog(JSONMetricUtil.class);
private static MBeanServer mbServer = ManagementFactory.getPlatformMBeanServer();
//MBeans ObjectName domain names
public static final String JAVA_LANG_DOMAIN = "java.lang";
public static final String JAVA_NIO_DOMAIN = "java.nio";
public static final String SUN_MGMT_DOMAIN = "com.sun.management";
public static final String HADOOP_DOMAIN = "Hadoop";
//MBeans ObjectName properties key names
public static final String TYPE_KEY = "type";
public static final String NAME_KEY = "name";
public static final String SERVICE_KEY = "service";
public static final String SUBSYSTEM_KEY = "sub";
/**
* Utility for getting metric values. Collection of static methods intended for
* easier access to metric values.
*/
private JSONMetricUtil() {
// Not to be called
}
public static MBeanAttributeInfo[] getMBeanAttributeInfo(ObjectName bean)
throws IntrospectionException, InstanceNotFoundException, ReflectionException,
IntrospectionException, javax.management.IntrospectionException {
MBeanInfo mbinfo = mbServer.getMBeanInfo(bean);
return mbinfo.getAttributes();
}
public static Object getValueFromMBean(ObjectName bean, String attribute) {
Object value = null;
try {
value = mbServer.getAttribute(bean, attribute);
}
catch(Exception e) {
LOG.error("Unable to get value from MBean= "+ bean.toString() +
"for attribute=" + attribute + " " + e.getMessage());
}
return value;
}
/**
* Returns a subset of mbeans defined by qry.
* Modeled after {@link JSONBean#dumpRegionServerMetrics()}
* Example: String qry= "java.lang:type=Memory"
* @throws MalformedObjectNameException if json have bad format
* @throws IOException /
* @return String representation of json array.
*/
public static String dumpBeanToString(String qry) throws MalformedObjectNameException,
IOException {
StringWriter sw = new StringWriter(1024 * 100); // Guess this size
try (PrintWriter writer = new PrintWriter(sw)) {
JSONBean dumper = new JSONBean();
try (JSONBean.Writer jsonBeanWriter = dumper.open(writer)) {
MBeanServer mbeanServer = ManagementFactory.getPlatformMBeanServer();
jsonBeanWriter.write(mbeanServer,
new ObjectName(qry), null, false);
}
}
sw.close();
return sw.toString();
}
public static JsonNode mappStringToJsonNode(String jsonString) throws
JsonProcessingException, IOException {
ObjectMapper mapper = new ObjectMapper();
JsonNode node = mapper.readTree(jsonString);
return node;
}
public static JsonNode searchJson(JsonNode tree, String searchKey)
throws JsonProcessingException, IOException {
if (tree == null) {
return null;
}
if(tree.has(searchKey)) {
return tree.get(searchKey);
}
if(tree.isContainerNode()) {
for(JsonNode branch: tree) {
JsonNode branchResult = searchJson(branch, searchKey);
if (branchResult != null && !branchResult.isMissingNode()) {
return branchResult;
}
}
}
return null;
}
/**
* Method for building hashtable used for constructing ObjectName.
* Mapping is done with arrays indices
* @param keys Hashtable keys
* @param values Hashtable values
* @return Hashtable or null if arrays are empty * or have different number of elements
*/
public static Hashtable<String, String> buldKeyValueTable(String[] keys, String[] values) {
if (keys.length != values.length) {
LOG.error("keys and values arrays must be same size");
return null;
}
if (keys.length == 0 || values.length == 0) {
LOG.error("keys and values arrays can not be empty;");
return null;
}
Hashtable<String, String> table = new Hashtable<String, String>();
for(int i = 0; i < keys.length; i++) {
table.put(keys[i], values[i]);
}
return table;
}
public static ObjectName buildObjectName(String pattern) throws MalformedObjectNameException {
return new ObjectName(pattern);
}
public static ObjectName buildObjectName(String domain, Hashtable<String, String> keyValueTable)
throws MalformedObjectNameException {
return new ObjectName(domain, keyValueTable);
}
public static Set<ObjectName> getRegistredMBeans(ObjectName name, MBeanServer mbs) {
return mbs.queryNames(name, null);
}
public static String getProcessPID() {
return ManagementFactory.getRuntimeMXBean().getName().split("@")[0];
}
public static String getCommmand() throws MalformedObjectNameException,
IOException, JSONException {
RuntimeMXBean runtimeBean = ManagementFactory.getRuntimeMXBean();
return runtimeBean.getSystemProperties().get("sun.java.command");
}
public static List<GarbageCollectorMXBean> getGcCollectorBeans() {
List<GarbageCollectorMXBean> gcBeans = ManagementFactory.getGarbageCollectorMXBeans();
return gcBeans;
}
public static long getLastGcDuration(ObjectName gcCollector) {
long lastGcDuration = 0;
Object lastGcInfo = getValueFromMBean(gcCollector, "LastGcInfo");
if (lastGcInfo != null && lastGcInfo instanceof CompositeData) {
CompositeData cds = (CompositeData)lastGcInfo;
lastGcDuration = (long) cds.get("duration");
}
return lastGcDuration;
}
public static List<MemoryPoolMXBean> getMemoryPools() {
List<MemoryPoolMXBean> mPools = ManagementFactory.getMemoryPoolMXBeans();
return mPools;
}
public static float calcPercentage(long a, long b) {
if (a == 0 || b == 0) {
return 0;
}
return ((float)a / (float)b) *100;
}
}

View File

@ -91,6 +91,7 @@
<li><a href="/master-status">Home</a></li>
<li><a href="/tablesDetailed.jsp">Table Details</a></li>
<li><a href="/procedures.jsp">Procedures &amp; Locks</a></li>
<li><a href="/processMaster.jsp">Process Metrics</a></li>
<li><a href="/logs/">Local Logs</a></li>
<li><a href="/logLevel">Log Level</a></li>
<li><a href="/dump">Debug Dump</a></li>

View File

@ -0,0 +1,228 @@
<%--
/**
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
--%>
<%@ page contentType="text/html;charset=UTF-8"
import="java.util.Date"
import="java.util.List"
import="org.apache.hadoop.hbase.HBaseConfiguration"
import="static org.apache.commons.lang.StringEscapeUtils.escapeXml"
import="javax.management.ObjectName"
import="java.lang.management.ManagementFactory"
import="java.lang.management.MemoryPoolMXBean"
import="java.lang.management.RuntimeMXBean"
import="java.lang.management.GarbageCollectorMXBean"
import="org.apache.hadoop.hbase.util.JSONMetricUtil"
import="org.apache.hadoop.hbase.procedure2.util.StringUtils"
import="org.codehaus.jackson.JsonNode"
%>
<%
RuntimeMXBean runtimeBean = ManagementFactory.getRuntimeMXBean();
ObjectName jvmMetrics = new ObjectName("Hadoop:service=HBase,name=JvmMetrics");
// There is always two of GC collectors
List<GarbageCollectorMXBean> gcBeans = JSONMetricUtil.getGcCollectorBeans();
GarbageCollectorMXBean collector1 = null;
GarbageCollectorMXBean collector2 = null;
try {
collector1 = gcBeans.get(0);
collector2 = gcBeans.get(1);
} catch(IndexOutOfBoundsException e) {}
List<MemoryPoolMXBean> mPools = JSONMetricUtil.getMemoryPools();
%>
<!--[if IE]>
<!DOCTYPE html>
<![endif]-->
<?xml version="1.0" encoding="UTF-8" ?>
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta charset="utf-8">
<title>Process info for PID: <%= JSONMetricUtil.getProcessPID() %></title>
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta name="description" content="">
<meta name="author" content="">
<link href="/static/css/bootstrap.min.css" rel="stylesheet">
<link href="/static/css/bootstrap-theme.min.css" rel="stylesheet">
<link href="/static/css/hbase.css" rel="stylesheet">
</head>
<body>
<div class="navbar navbar-fixed-top navbar-default">
<div class="container-fluid">
<div class="navbar-header">
<button type="button" class="navbar-toggle" data-toggle="collapse" data-target=".navbar-collapse">
<span class="icon-bar"></span>
<span class="icon-bar"></span>
<span class="icon-bar"></span>
</button>
<a class="navbar-brand" href="/rs-status"><img src="/static/hbase_logo_small.png" alt="HBase Logo"/></a>
</div>
<div class="collapse navbar-collapse">
<ul class="nav navbar-nav">
<li><a href="/master-status">Home</a></li>
<li><a href="/tablesDetailed.jsp">Table Details</a></li>
<li><a href="/procedures.jsp">Procedures</a></li>
<li><a href="/processMaster.jsp">Process Metrics</a></li>
<li><a href="/logs/">Local Logs</a></li>
<li><a href="/logLevel">Log Level</a></li>
<li><a href="/dump">Debug Dump</a></li>
<li><a href="/jmx">Metrics Dump</a></li>
<% if (HBaseConfiguration.isShowConfInServlet()) { %>
<li><a href="/conf">HBase Configuration</a></li>
<% } %>
</ul>
</div><!--/.nav-collapse -->
</div>
</div>
<div class="container-fluid content">
<div class="row">
<div class="page-header">
<h1><%= JSONMetricUtil.getCommmand().split(" ")[0] %></h1>
</div>
</div>
<table class="table table-striped" width="90%" >
<tr>
<th>Started</th>
<th>Uptime</th>
<th>PID</th>
<th>Owner</th>
</tr>
<tr>
<tr>
<td><%= new Date(runtimeBean.getStartTime()) %></a></td>
<td><%= StringUtils.humanTimeDiff(runtimeBean.getUptime()) %></a></td>
<td><%= JSONMetricUtil.getProcessPID() %></a></td>
<td><%= runtimeBean.getSystemProperties().get("user.name") %></a></td>
</tr>
</table>
</div>
<div class="container-fluid content">
<div class="row">
<div class="page-header">
<h2>Threads</h2>
</div>
</div>
<table class="table table-striped" width="90%" >
<tr>
<th>ThreadsNew</th>
<th>ThreadsRunable</th>
<th>ThreadsBlocked</th>
<th>ThreadsWaiting</th>
<th>ThreadsTimeWaiting</th>
<th>ThreadsTerminated</th>
</tr>
<tr>
<td><%= JSONMetricUtil.getValueFromMBean(jvmMetrics, "ThreadsNew") %></a></td>
<td><%= JSONMetricUtil.getValueFromMBean(jvmMetrics, "ThreadsRunnable")%></a></td>
<td><%= JSONMetricUtil.getValueFromMBean(jvmMetrics, "ThreadsBlocked")%></a></td>
<td><%= JSONMetricUtil.getValueFromMBean(jvmMetrics, "ThreadsWaiting")%></a></td>
<td><%= JSONMetricUtil.getValueFromMBean(jvmMetrics, "ThreadsTimedWaiting")%></a></td>
<td><%= JSONMetricUtil.getValueFromMBean(jvmMetrics, "ThreadsTerminated")%></a></td>
</tr>
</table>
</div>
<div class="container-fluid content">
<div class="row">
<div class="page-header">
<h2>GC Collectors</h2>
</div>
</div>
<% if (gcBeans.size() == 2) { %>
<div class="tabbable">
<ul class="nav nav-pills">
<li class="active">
<a href="#tab_gc1" data-toggle="tab"><%=collector1.getName() %></a>
</li>
<li class="">
<a href="#tab_gc2" data-toggle="tab"><%=collector2.getName() %></a>
</li>
</ul>
<div class="tab-content" style="padding-bottom: 9px; border-bottom: 1px solid #ddd;">
<div class="tab-pane active" id="tab_gc1">
<table class="table table-striped">
<tr>
<th>Collection Count</th>
<th>Collection Time</th>
<th>Last duration</th>
</tr>
<tr>
<td> <%= collector1.getCollectionCount() %></td>
<td> <%= StringUtils.humanTimeDiff(collector1.getCollectionTime()) %> </td>
<td> <%= StringUtils.humanTimeDiff(JSONMetricUtil.getLastGcDuration(
collector1.getObjectName())) %></td>
</tr>
</table>
</div>
<div class="tab-pane" id="tab_gc2">
<table class="table table-striped">
<tr>
<th>Collection Count</th>
<th>Collection Time</th>
<th>Last duration</th>
</tr>
<tr>
<td> <%= collector2.getCollectionCount() %></td>
<td> <%= StringUtils.humanTimeDiff(collector2.getCollectionTime()) %> </td>
<td> <%= StringUtils.humanTimeDiff(JSONMetricUtil.getLastGcDuration(
collector2.getObjectName())) %></td>
</tr>
</table>
</div>
</div>
</div>
<%} else { %>
<p> Can not display GC Collector stats.</p>
<%} %>
Total GC Collection time: <%= StringUtils.humanTimeDiff(collector1.getCollectionTime() +
collector2.getCollectionTime())%>
</div>
<% for(MemoryPoolMXBean mp:mPools) {
if(mp.getName().contains("Cache")) continue;%>
<div class="container-fluid content">
<div class="row">
<div class="page-header">
<h2><%= mp.getName() %></h2>
</div>
</div>
<table class="table table-striped" width="90%" >
<tr>
<th>Commited</th>
<th>Init</th>
<th>Max</th>
<th>Used</th>
<th>Utilization [%]</th>
</tr>
<tr>
<tr>
<td><%= StringUtils.humanSize(mp.getUsage().getCommitted()) %></a></td>
<td><%= StringUtils.humanSize(mp.getUsage().getInit())%></a></td>
<td><%= StringUtils.humanSize(mp.getUsage().getMax())%></a></td>
<td><%= StringUtils.humanSize(mp.getUsage().getUsed())%></a></td>
<td><%= JSONMetricUtil.calcPercentage(mp.getUsage().getUsed(),
mp.getUsage().getCommitted()) %></a></td>
</tr>
</table>
</div>
<% } %>
<script src="/static/js/jquery.min.js" type="text/javascript"></script>
<script src="/static/js/bootstrap.min.js" type="text/javascript"></script>
<script src="/static/js/tab.js" type="text/javascript"></script>
</body>
</html>

View File

@ -0,0 +1,230 @@
<%--
/**
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
--%>
<%@ page contentType="text/html;charset=UTF-8"
import="java.util.Date"
import="java.util.List"
import="org.apache.hadoop.hbase.HBaseConfiguration"
import="static org.apache.commons.lang.StringEscapeUtils.escapeXml"
import="javax.management.ObjectName"
import="java.lang.management.ManagementFactory"
import="java.lang.management.MemoryPoolMXBean"
import="java.lang.management.RuntimeMXBean"
import="java.lang.management.GarbageCollectorMXBean"
import="org.apache.hadoop.hbase.util.JSONMetricUtil"
import="org.apache.hadoop.hbase.procedure2.util.StringUtils"
import="org.codehaus.jackson.JsonNode"
%>
<%
RuntimeMXBean runtimeBean = ManagementFactory.getRuntimeMXBean();
ObjectName jvmMetrics = new ObjectName("Hadoop:service=HBase,name=JvmMetrics");
ObjectName rsMetrics = new ObjectName("Hadoop:service=HBase,name=RegionServer,sub=Server");
// There is always two of GC collectors
List<GarbageCollectorMXBean> gcBeans = JSONMetricUtil.getGcCollectorBeans();
GarbageCollectorMXBean collector1 = null;
GarbageCollectorMXBean collector2 = null;
try {
collector1 = gcBeans.get(0);
collector2 = gcBeans.get(1);
} catch(IndexOutOfBoundsException e) {}
List<MemoryPoolMXBean> mPools = JSONMetricUtil.getMemoryPools();
%>
<!--[if IE]>
<!DOCTYPE html>
<![endif]-->
<?xml version="1.0" encoding="UTF-8" ?>
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta charset="utf-8">
<title>Process info for PID: <%= JSONMetricUtil.getProcessPID() %></title>
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta name="description" content="">
<meta name="author" content="">
<link href="/static/css/bootstrap.min.css" rel="stylesheet">
<link href="/static/css/bootstrap-theme.min.css" rel="stylesheet">
<link href="/static/css/hbase.css" rel="stylesheet">
</head>
<body>
<div class="navbar navbar-fixed-top navbar-default">
<div class="container-fluid">
<div class="navbar-header">
<button type="button" class="navbar-toggle" data-toggle="collapse" data-target=".navbar-collapse">
<span class="icon-bar"></span>
<span class="icon-bar"></span>
<span class="icon-bar"></span>
</button>
<a class="navbar-brand" href="/rs-status"><img src="/static/hbase_logo_small.png" alt="HBase Logo"/></a>
</div>
<div class="collapse navbar-collapse">
<ul class="nav navbar-nav">
<li><a href="/rs-status">Home</a></li>
<li><a href="/processRS.jsp">Process Metrics</a></li>
<li><a href="/logs/">Local Logs</a></li>
<li><a href="/dump">Debug Dump</a></li>
<li><a href="/jmx">Metrics Dump</a></li>
<% if (HBaseConfiguration.isShowConfInServlet()) { %>
<li><a href="/conf">HBase Configuration</a></li>
<% } %>
</ul>
</div><!--/.nav-collapse -->
</div>
</div>
<div class="container-fluid content">
<div class="row">
<div class="page-header">
<h1><%= JSONMetricUtil.getCommmand().split(" ")[0] %></h1>
</div>
</div>
<table class="table table-striped" width="90%" >
<tr>
<th>Started</th>
<th>Uptime</th>
<th>PID</th>
<th>JvmPauseMonitor Count </th>
<th>Owner</th>
</tr>
<tr>
<tr>
<td><%= new Date(runtimeBean.getStartTime()) %></a></td>
<td><%= StringUtils.humanTimeDiff(runtimeBean.getUptime()) %></a></td>
<td><%= JSONMetricUtil.getProcessPID() %></a></td>
<td><%= (long)JSONMetricUtil.getValueFromMBean(rsMetrics, "pauseWarnThresholdExceeded")
+ (long)JSONMetricUtil.getValueFromMBean(rsMetrics, "pauseInfoThresholdExceeded") %></a></td>
<td><%= runtimeBean.getSystemProperties().get("user.name") %></a></td>
</tr>
</table>
</div>
<div class="container-fluid content">
<div class="row">
<div class="page-header">
<h2>Threads</h2>
</div>
</div>
<table class="table table-striped" width="90%" >
<tr>
<th>ThreadsNew</th>
<th>ThreadsRunable</th>
<th>ThreadsBlocked</th>
<th>ThreadsWaiting</th>
<th>ThreadsTimeWaiting</th>
<th>ThreadsTerminated</th>
</tr>
<tr>
<tr>
<td><%= JSONMetricUtil.getValueFromMBean(jvmMetrics, "ThreadsNew") %></a></td>
<td><%= JSONMetricUtil.getValueFromMBean(jvmMetrics, "ThreadsRunnable")%></a></td>
<td><%= JSONMetricUtil.getValueFromMBean(jvmMetrics, "ThreadsBlocked")%></a></td>
<td><%= JSONMetricUtil.getValueFromMBean(jvmMetrics, "ThreadsWaiting")%></a></td>
<td><%= JSONMetricUtil.getValueFromMBean(jvmMetrics, "ThreadsTimedWaiting")%></a></td>
<td><%= JSONMetricUtil.getValueFromMBean(jvmMetrics, "ThreadsTerminated")%></a></td>
</tr>
</table>
</div>
<div class="container-fluid content">
<div class="row">
<div class="page-header">
<h2>GC Collectors</h2>
</div>
</div>
<% if (gcBeans.size() == 2) { %>
<div class="tabbable">
<ul class="nav nav-pills">
<li class="active">
<a href="#tab_gc1" data-toggle="tab"><%=collector1.getName() %></a>
</li>
<li class="">
<a href="#tab_gc2" data-toggle="tab"><%=collector2.getName() %></a>
</li>
</ul>
<div class="tab-content" style="padding-bottom: 9px; border-bottom: 1px solid #ddd;">
<div class="tab-pane active" id="tab_gc1">
<table class="table table-striped">
<tr>
<th>Collection Count</th>
<th>Collection Time</th>
<th>Last duration</th>
</tr>
<tr>
<td> <%= collector1.getCollectionCount() %></td>
<td> <%= StringUtils.humanTimeDiff(collector1.getCollectionTime()) %> </td>
<td> <%= StringUtils.humanTimeDiff(JSONMetricUtil.getLastGcDuration(
collector1.getObjectName())) %></td>
</tr>
</table>
</div>
<div class="tab-pane" id="tab_gc2">
<table class="table table-striped">
<tr>
<th>Collection Count</th>
<th>Collection Time</th>
<th>Last duration</th>
</tr>
<tr>
<td> <%= collector2.getCollectionCount() %></td>
<td> <%= StringUtils.humanTimeDiff(collector2.getCollectionTime()) %> </td>
<td> <%= StringUtils.humanTimeDiff(JSONMetricUtil.getLastGcDuration(
collector2.getObjectName())) %></td>
</tr>
</table>
</div>
</div>
</div>
<%} else { %>
<p> Can not display GC Collector stats.</p>
<%} %>
Total GC Collection time: <%= StringUtils.humanTimeDiff(collector1.getCollectionTime() +
collector2.getCollectionTime())%>
</div>
<% for(MemoryPoolMXBean mp:mPools) {
if(mp.getName().contains("Cache")) continue;%>
<div class="container-fluid content">
<div class="row">
<div class="page-header">
<h2><%= mp.getName() %></h2>
</div>
</div>
<table class="table table-striped" width="90%" >
<tr>
<th>Commited</th>
<th>Init</th>
<th>Max</th>
<th>Used</th>
<th>Utilization [%]</th>
</tr>
<tr>
<tr>
<td><%= StringUtils.humanSize(mp.getUsage().getCommitted()) %></a></td>
<td><%= StringUtils.humanSize(mp.getUsage().getInit())%></a></td>
<td><%= StringUtils.humanSize(mp.getUsage().getMax())%></a></td>
<td><%= StringUtils.humanSize(mp.getUsage().getUsed())%></a></td>
<td><%= JSONMetricUtil.calcPercentage(mp.getUsage().getUsed(),
mp.getUsage().getCommitted()) %></a></td>
</tr>
</table>
</div>
<% } %>
<script src="/static/js/jquery.min.js" type="text/javascript"></script>
<script src="/static/js/bootstrap.min.js" type="text/javascript"></script>
<script src="/static/js/tab.js" type="text/javascript"></script>
</body>
</html>

View File

@ -97,6 +97,7 @@
<li><a href="/master-status">Home</a></li>
<li><a href="/tablesDetailed.jsp">Table Details</a></li>
<li><a href="/procedures.jsp">Procedures &amp; Locks</a></li>
<li><a href="/processMaster.jsp">Process Metrics</a></li>
<li><a href="/logs/">Local Logs</a></li>
<li><a href="/logLevel">Log Level</a></li>
<li><a href="/dump">Debug Dump</a></li>

View File

@ -164,6 +164,7 @@
<li><a href="/master-status">Home</a></li>
<li><a href="/tablesDetailed.jsp">Table Details</a></li>
<li><a href="/procedures.jsp">Procedures &amp; Locks</a></li>
<li><a href="/processMaster.jsp">Process Metrics</a></li>
<li><a href="/logs/">Local Logs</a></li>
<li><a href="/logLevel">Log Level</a></li>
<li><a href="/dump">Debug Dump</a></li>

View File

@ -67,6 +67,7 @@
<%if (master.isActiveMaster()) { %>
<li><a href="/procedures.jsp">Procedures &amp; Locks</a></li>
<% } %>
<li><a href="/processMaster.jsp">Process Metrics</a></li>
<li><a href="/logs/">Local Logs</a></li>
<li><a href="/logLevel">Log Level</a></li>
<li><a href="/dump">Debug Dump</a></li>

View File

@ -61,6 +61,7 @@
<li><a href="/master-status">Home</a></li>
<li><a href="/tablesDetailed.jsp">Table Details</a></li>
<li><a href="/procedures.jsp">Procedures &amp; Locks</a></li>
<li><a href="/processMaster.jsp">Process Metrics</a></li>
<li><a href="/logs/">Local Logs</a></li>
<li><a href="/logLevel">Log Level</a></li>
<li><a href="/dump">Debug Dump</a></li>

View File

@ -0,0 +1,230 @@
<%--
/**
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
--%>
<%@ page contentType="text/html;charset=UTF-8"
import="java.util.Date"
import="java.util.List"
import="org.apache.hadoop.hbase.HBaseConfiguration"
import="static org.apache.commons.lang.StringEscapeUtils.escapeXml"
import="javax.management.ObjectName"
import="java.lang.management.ManagementFactory"
import="java.lang.management.MemoryPoolMXBean"
import="java.lang.management.RuntimeMXBean"
import="java.lang.management.GarbageCollectorMXBean"
import="org.apache.hadoop.hbase.util.JSONMetricUtil"
import="org.apache.hadoop.hbase.procedure2.util.StringUtils"
import="org.codehaus.jackson.JsonNode"
%>
<%
RuntimeMXBean runtimeBean = ManagementFactory.getRuntimeMXBean();
ObjectName jvmMetrics = new ObjectName("Hadoop:service=HBase,name=JvmMetrics");
ObjectName rsMetrics = new ObjectName("Hadoop:service=HBase,name=RegionServer,sub=Server");
// There is always two of GC collectors
List<GarbageCollectorMXBean> gcBeans = JSONMetricUtil.getGcCollectorBeans();
GarbageCollectorMXBean collector1 = null;
GarbageCollectorMXBean collector2 = null;
try {
collector1 = gcBeans.get(0);
collector2 = gcBeans.get(1);
} catch(IndexOutOfBoundsException e) {}
List<MemoryPoolMXBean> mPools = JSONMetricUtil.getMemoryPools();
%>
<!--[if IE]>
<!DOCTYPE html>
<![endif]-->
<?xml version="1.0" encoding="UTF-8" ?>
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta charset="utf-8">
<title>Process info for PID: <%= JSONMetricUtil.getProcessPID() %></title>
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta name="description" content="">
<meta name="author" content="">
<link href="/static/css/bootstrap.min.css" rel="stylesheet">
<link href="/static/css/bootstrap-theme.min.css" rel="stylesheet">
<link href="/static/css/hbase.css" rel="stylesheet">
</head>
<body>
<div class="navbar navbar-fixed-top navbar-default">
<div class="container-fluid">
<div class="navbar-header">
<button type="button" class="navbar-toggle" data-toggle="collapse" data-target=".navbar-collapse">
<span class="icon-bar"></span>
<span class="icon-bar"></span>
<span class="icon-bar"></span>
</button>
<a class="navbar-brand" href="/rs-status"><img src="/static/hbase_logo_small.png" alt="HBase Logo"/></a>
</div>
<div class="collapse navbar-collapse">
<ul class="nav navbar-nav">
<li><a href="/rs-status">Home</a></li>
<li><a href="/processRS.jsp">Process Metrics</a></li>
<li><a href="/logs/">Local Logs</a></li>
<li><a href="/dump">Debug Dump</a></li>
<li><a href="/jmx">Metrics Dump</a></li>
<% if (HBaseConfiguration.isShowConfInServlet()) { %>
<li><a href="/conf">HBase Configuration</a></li>
<% } %>
</ul>
</div><!--/.nav-collapse -->
</div>
</div>
<div class="container-fluid content">
<div class="row">
<div class="page-header">
<h1><%= JSONMetricUtil.getCommmand().split(" ")[0] %></h1>
</div>
</div>
<table class="table table-striped" width="90%" >
<tr>
<th>Started</th>
<th>Uptime</th>
<th>PID</th>
<th>JvmPauseMonitor Count </th>
<th>Owner</th>
</tr>
<tr>
<tr>
<td><%= new Date(runtimeBean.getStartTime()) %></a></td>
<td><%= StringUtils.humanTimeDiff(runtimeBean.getUptime()) %></a></td>
<td><%= JSONMetricUtil.getProcessPID() %></a></td>
<td><%= (long)JSONMetricUtil.getValueFromMBean(rsMetrics, "pauseWarnThresholdExceeded")
+ (long)JSONMetricUtil.getValueFromMBean(rsMetrics, "pauseInfoThresholdExceeded") %></a></td>
<td><%= runtimeBean.getSystemProperties().get("user.name") %></a></td>
</tr>
</table>
</div>
<div class="container-fluid content">
<div class="row">
<div class="page-header">
<h2>Threads</h2>
</div>
</div>
<table class="table table-striped" width="90%" >
<tr>
<th>ThreadsNew</th>
<th>ThreadsRunable</th>
<th>ThreadsBlocked</th>
<th>ThreadsWaiting</th>
<th>ThreadsTimeWaiting</th>
<th>ThreadsTerminated</th>
</tr>
<tr>
<tr>
<td><%= JSONMetricUtil.getValueFromMBean(jvmMetrics, "ThreadsNew") %></a></td>
<td><%= JSONMetricUtil.getValueFromMBean(jvmMetrics, "ThreadsRunnable")%></a></td>
<td><%= JSONMetricUtil.getValueFromMBean(jvmMetrics, "ThreadsBlocked")%></a></td>
<td><%= JSONMetricUtil.getValueFromMBean(jvmMetrics, "ThreadsWaiting")%></a></td>
<td><%= JSONMetricUtil.getValueFromMBean(jvmMetrics, "ThreadsTimedWaiting")%></a></td>
<td><%= JSONMetricUtil.getValueFromMBean(jvmMetrics, "ThreadsTerminated")%></a></td>
</tr>
</table>
</div>
<div class="container-fluid content">
<div class="row">
<div class="page-header">
<h2>GC Collectors</h2>
</div>
</div>
<% if (gcBeans.size() == 2) { %>
<div class="tabbable">
<ul class="nav nav-pills">
<li class="active">
<a href="#tab_gc1" data-toggle="tab"><%=collector1.getName() %></a>
</li>
<li class="">
<a href="#tab_gc2" data-toggle="tab"><%=collector2.getName() %></a>
</li>
</ul>
<div class="tab-content" style="padding-bottom: 9px; border-bottom: 1px solid #ddd;">
<div class="tab-pane active" id="tab_gc1">
<table class="table table-striped">
<tr>
<th>Collection Count</th>
<th>Collection Time</th>
<th>Last duration</th>
</tr>
<tr>
<td> <%= collector1.getCollectionCount() %></td>
<td> <%= StringUtils.humanTimeDiff(collector1.getCollectionTime()) %> </td>
<td> <%= StringUtils.humanTimeDiff(JSONMetricUtil.getLastGcDuration(
collector1.getObjectName())) %></td>
</tr>
</table>
</div>
<div class="tab-pane" id="tab_gc2">
<table class="table table-striped">
<tr>
<th>Collection Count</th>
<th>Collection Time</th>
<th>Last duration</th>
</tr>
<tr>
<td> <%= collector2.getCollectionCount() %></td>
<td> <%= StringUtils.humanTimeDiff(collector2.getCollectionTime()) %> </td>
<td> <%= StringUtils.humanTimeDiff(JSONMetricUtil.getLastGcDuration(
collector2.getObjectName())) %></td>
</tr>
</table>
</div>
</div>
</div>
<%} else { %>
<p> Can not display GC Collector stats.</p>
<%} %>
Total GC Collection time: <%= StringUtils.humanTimeDiff(collector1.getCollectionTime() +
collector2.getCollectionTime())%>
</div>
<% for(MemoryPoolMXBean mp:mPools) {
if(mp.getName().contains("Cache")) continue;%>
<div class="container-fluid content">
<div class="row">
<div class="page-header">
<h2><%= mp.getName() %></h2>
</div>
</div>
<table class="table table-striped" width="90%" >
<tr>
<th>Commited</th>
<th>Init</th>
<th>Max</th>
<th>Used</th>
<th>Utilization [%]</th>
</tr>
<tr>
<tr>
<td><%= StringUtils.humanSize(mp.getUsage().getCommitted()) %></a></td>
<td><%= StringUtils.humanSize(mp.getUsage().getInit())%></a></td>
<td><%= StringUtils.humanSize(mp.getUsage().getMax())%></a></td>
<td><%= StringUtils.humanSize(mp.getUsage().getUsed())%></a></td>
<td><%= JSONMetricUtil.calcPercentage(mp.getUsage().getUsed(),
mp.getUsage().getCommitted()) %></a></td>
</tr>
</table>
</div>
<% } %>
<script src="/static/js/jquery.min.js" type="text/javascript"></script>
<script src="/static/js/bootstrap.min.js" type="text/javascript"></script>
<script src="/static/js/tab.js" type="text/javascript"></script>
</body>
</html>

View File

@ -73,6 +73,7 @@
<div class="collapse navbar-collapse">
<ul class="nav navbar-nav">
<li class="active"><a href="/rs-status">Home</a></li>
<li><a href="/processRS.jsp">Process Metrics</a></li>
<li><a href="/logs/">Local Logs</a></li>
<li><a href="/logLevel">Log Level</a></li>
<li><a href="/dump">Debug Dump</a></li>

View File

@ -0,0 +1,106 @@
/**
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.hadoop.hbase.util;
import java.io.IOException;
import java.lang.management.GarbageCollectorMXBean;
import java.lang.management.ManagementFactory;
import java.util.Hashtable;
import java.util.List;
import javax.management.MalformedObjectNameException;
import javax.management.ObjectName;
import javax.management.openmbean.CompositeData;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.hadoop.hbase.testclassification.MiscTests;
import org.apache.hadoop.hbase.testclassification.SmallTests;
import org.codehaus.jackson.JsonNode;
import org.codehaus.jackson.JsonProcessingException;
import org.junit.Test;
import org.junit.experimental.categories.Category;
@Category({MiscTests.class, SmallTests.class})
public class TestJSONMetricUtil {
private static final Log LOG = LogFactory.getLog(TestJSONMetricUtil.class);
@Test
public void testBuildHashtable() {
String[] keys = {"type", "name"};
String[] emptyKey = {};
String[] values = {"MemoryPool", "Par Eden Space"};
String[] values2 = {"MemoryPool", "Par Eden Space", "Test"};
String[] emptyValue = {};
Hashtable<String, String> properties = JSONMetricUtil.buldKeyValueTable(keys, values);
Hashtable<String, String> nullObject = JSONMetricUtil.buldKeyValueTable(keys, values2);
Hashtable<String, String> nullObject1 = JSONMetricUtil.buldKeyValueTable(keys, emptyValue);
Hashtable<String, String> nullObject2 = JSONMetricUtil.buldKeyValueTable(emptyKey, values2);
Hashtable<String, String> nullObject3 = JSONMetricUtil.buldKeyValueTable(emptyKey, emptyValue);
assertEquals(properties.get("type"), values[0]);
assertEquals(properties.get("name"), values[1]);
assertEquals(nullObject, null);
assertEquals(nullObject1, null);
assertEquals(nullObject2, null);
assertEquals(nullObject3, null);
}
@Test
public void testSearchJson() throws JsonProcessingException, IOException {
String jsonString = "{\"test\":[{\"data1\":100,\"data2\":\"hello\",\"data3\": [1 , 2 , 3]}, "
+ "{\"data4\":0}]}";
JsonNode node = JSONMetricUtil.mappStringToJsonNode(jsonString);
JsonNode r1 = JSONMetricUtil.searchJson(node, "data1");
JsonNode r2 = JSONMetricUtil.searchJson(node, "data2");
JsonNode r3 = JSONMetricUtil.searchJson(node, "data3");
JsonNode r4 = JSONMetricUtil.searchJson(node, "data4");
assertEquals(r1.getIntValue(), 100);
assertEquals(r2.getTextValue(), "hello");
assertEquals(r3.get(0).getIntValue(), 1);
assertEquals(r4.getIntValue(), 0);
}
@Test
public void testBuildObjectName() throws MalformedObjectNameException {
String[] keys = {"type", "name"};
String[] values = {"MemoryPool", "Par Eden Space"};
Hashtable<String, String> properties = JSONMetricUtil.buldKeyValueTable(keys, values);
ObjectName testObject = JSONMetricUtil.buildObjectName(JSONMetricUtil.JAVA_LANG_DOMAIN,
properties);
assertEquals(testObject.getDomain(), JSONMetricUtil.JAVA_LANG_DOMAIN);
assertEquals(testObject.getKeyPropertyList(), properties);
}
@Test
public void testGetLastGCInfo() {
List<GarbageCollectorMXBean> gcBeans = ManagementFactory.getGarbageCollectorMXBeans();
for(GarbageCollectorMXBean bean:gcBeans) {
ObjectName on = bean.getObjectName();
Object value = JSONMetricUtil.getValueFromMBean(on, "LastGcInfo");
LOG.info("Collector Info: "+ value);
if (value != null && value instanceof CompositeData) {
CompositeData cds = (CompositeData)value;
assertNotNull(cds.get("duration"));
}
}
}
}