Skip to content

Commit

Permalink
HBASE-27814 Add support for dump and process metrics servlet in REST …
Browse files Browse the repository at this point in the history
…InfoServer (apache#5215)

Other changes:
- Ensure info server stops during stop()
- Extract header and footer. This would fix the log level page layout for rest web UI (See HBASE-20693)
- Add hostname in the landing page instead of just port similar to other web UIs

Signed-off-by: Nick Dimiduk <[email protected]>
(cherry picked from commit a683fcf)
  • Loading branch information
NihalJain authored and PDavid committed Dec 12, 2024
1 parent 137c6dc commit 36bd555
Show file tree
Hide file tree
Showing 6 changed files with 422 additions and 67 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
/*
* 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.rest;

import java.io.IOException;
import java.io.OutputStream;
import java.io.PrintStream;
import java.io.PrintWriter;
import java.util.Date;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.hbase.http.HttpServer;
import org.apache.hadoop.hbase.monitoring.StateDumpServlet;
import org.apache.hadoop.hbase.util.LogMonitoring;
import org.apache.hadoop.hbase.util.Threads;
import org.apache.yetus.audience.InterfaceAudience;

@InterfaceAudience.Private
public class RESTDumpServlet extends StateDumpServlet {
private static final long serialVersionUID = 1L;
private static final String LINE = "===========================================================";

@Override
public void doGet(HttpServletRequest request, HttpServletResponse response) throws IOException {
if (!HttpServer.isInstrumentationAccessAllowed(getServletContext(), request, response)) {
return;
}

RESTServer restServer = (RESTServer) getServletContext().getAttribute(RESTServer.REST_SERVER);
assert restServer != null : "No REST Server in context!";

response.setContentType("text/plain");
OutputStream os = response.getOutputStream();
try (PrintWriter out = new PrintWriter(os)) {

out.println("REST Server status for " + restServer.getServerName() + " as of " + new Date());

out.println("\n\nVersion Info:");
out.println(LINE);
dumpVersionInfo(out);

out.println("\n\nStacks:");
out.println(LINE);
out.flush();
PrintStream ps = new PrintStream(response.getOutputStream(), false, "UTF-8");
Threads.printThreadInfo(ps, "");
ps.flush();

out.println("\n\nREST Server configuration:");
out.println(LINE);
Configuration conf = restServer.conf;
out.flush();
conf.writeXml(os);
os.flush();

out.println("\n\nLogs");
out.println(LINE);
long tailKb = getTailKbParam(request);
LogMonitoring.dumpTailOfLogs(out, tailKb);

out.flush();
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
import static org.apache.hadoop.hbase.http.HttpServerUtil.PATH_SPEC_ANY;

import java.lang.management.ManagementFactory;
import java.net.UnknownHostException;
import java.util.ArrayList;
import java.util.EnumSet;
import java.util.List;
Expand All @@ -31,6 +32,7 @@
import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.hbase.HBaseConfiguration;
import org.apache.hadoop.hbase.HBaseInterfaceAudience;
import org.apache.hadoop.hbase.ServerName;
import org.apache.hadoop.hbase.http.HttpServerUtil;
import org.apache.hadoop.hbase.http.InfoServer;
import org.apache.hadoop.hbase.log.HBaseMarkers;
Expand Down Expand Up @@ -83,6 +85,7 @@
@InterfaceAudience.LimitedPrivate(HBaseInterfaceAudience.TOOLS)
public class RESTServer implements Constants {
static Logger LOG = LoggerFactory.getLogger("RESTServer");
public static final String REST_SERVER = "rest";

static final String REST_CSRF_ENABLED_KEY = "hbase.rest.csrf.enabled";
static final boolean REST_CSRF_ENABLED_DEFAULT = false;
Expand Down Expand Up @@ -110,6 +113,7 @@ public class RESTServer implements Constants {
private final UserProvider userProvider;
private Server server;
private InfoServer infoServer;
private ServerName serverName;

public RESTServer(Configuration conf) {
RESTServer.conf = conf;
Expand Down Expand Up @@ -143,8 +147,7 @@ void addCSRFFilter(ServletContextHandler ctxHandler, Configuration conf) {
loginServerPrincipal(UserProvider userProvider, Configuration conf) throws Exception {
Class<? extends ServletContainer> containerClass = ServletContainer.class;
if (userProvider.isHadoopSecurityEnabled() && userProvider.isHBaseSecurityEnabled()) {
String machineName = Strings.domainNamePointerToHostName(DNS.getDefaultHost(
conf.get(REST_DNS_INTERFACE, "default"), conf.get(REST_DNS_NAMESERVER, "default")));
String machineName = getHostName(conf);
String keytabFilename = conf.get(REST_KEYTAB_FILE);
Preconditions.checkArgument(keytabFilename != null && !keytabFilename.isEmpty(),
REST_KEYTAB_FILE + " should be set if security is enabled");
Expand Down Expand Up @@ -385,24 +388,46 @@ public synchronized void run() throws Exception {
// Put up info server.
int port = conf.getInt("hbase.rest.info.port", 8085);
if (port >= 0) {
conf.setLong("startcode", EnvironmentEdgeManager.currentTime());
String a = conf.get("hbase.rest.info.bindAddress", "0.0.0.0");
this.infoServer = new InfoServer("rest", a, port, false, conf);
final long startCode = EnvironmentEdgeManager.currentTime();
conf.setLong("startcode", startCode);
this.serverName = ServerName.valueOf(getHostName(conf), servicePort, startCode);

String addr = conf.get("hbase.rest.info.bindAddress", "0.0.0.0");
this.infoServer = new InfoServer(REST_SERVER, addr, port, false, conf);
this.infoServer.addPrivilegedServlet("dump", "/dump", RESTDumpServlet.class);
this.infoServer.setAttribute(REST_SERVER, this);
this.infoServer.setAttribute("hbase.conf", conf);
this.infoServer.start();
}
// start server
server.start();
}

private static String getHostName(Configuration conf) throws UnknownHostException {
return Strings.domainNamePointerToHostName(DNS.getDefaultHost(
conf.get(REST_DNS_INTERFACE, "default"), conf.get(REST_DNS_NAMESERVER, "default")));
}

public synchronized void join() throws Exception {
if (server == null) {
throw new IllegalStateException("Server is not running");
}
server.join();
}

private void stopInfoServer() {
if (this.infoServer != null) {
LOG.info("Stop info server");
try {
this.infoServer.stop();
} catch (Exception e) {
LOG.error("Failed to stop infoServer", e);
}
}
}

public synchronized void stop() throws Exception {
stopInfoServer();
if (server == null) {
throw new IllegalStateException("Server is not running");
}
Expand All @@ -426,6 +451,10 @@ public synchronized int getInfoPort() {
return infoServer.getPort();
}

public ServerName getServerName() {
return serverName;
}

public Configuration getConf() {
return conf;
}
Expand Down
32 changes: 32 additions & 0 deletions hbase-rest/src/main/resources/hbase-webapps/rest/footer.jsp
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
<%--
/**
* 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.
*/
--%>

<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>
<script type="text/javascript">
$(document).ready(function() {
$('div.navbar li.active').removeClass('active');
$('a[href="' + location.pathname + '"]').closest('li').addClass('active');
});
</script>
</body>
</html>

74 changes: 74 additions & 0 deletions hbase-rest/src/main/resources/hbase-webapps/rest/header.jsp
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
<%--
/**
* 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="org.apache.hadoop.hbase.HBaseConfiguration"%>

<!DOCTYPE html>
<?xml version="1.0" encoding="UTF-8" ?>
<html lang="en">
<head>
<meta charset="utf-8">
<title><%= request.getParameter("pageTitle")%></title>
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta name="description" 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="/rest.jsp"><img src="/static/hbase_logo_small.png" alt="HBase Logo"/></a>
</div>
<div class="collapse navbar-collapse">
<ul class="nav navbar-nav">
<li class="active"><a href="/rest.jsp">Home</a></li>
<li><a href="/logs/">Local logs</a></li>
<li><a href="/processRest.jsp">Process Metrics</a></li>
<li><a href="/logLevel">Log Level</a></li>
<li><a href="/dump">Debug Dump</a></li>
<li class="nav-item dropdown">
<a class="nav-link dropdown-toggle" href="#" id="navbarDropdownMenuLink" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
Metrics <span class="caret"></span>
</a>
<ul class="dropdown-menu" aria-labelledby="navbarDropdownMenuLink">
<li><a target="_blank" href="/jmx">JMX</a></li>
<li><a target="_blank" href="/jmx?description=true">JMX with description</a></li>
<li><a target="_blank" href="/prometheus">Prometheus</a></li>
<li><a target="_blank" href="/prometheus?description=true">Prometheus with description</a></li>
</ul>
</li>
<li><a href="/prof">Profiler</a></li>
<% if (HBaseConfiguration.isShowConfInServlet()) { %>
<li><a href="/conf">HBase Configuration</a></li>
<% } %>
</ul>
</div><!--/.nav-collapse -->
</div>
</div>

Loading

0 comments on commit 36bd555

Please sign in to comment.