Skip to content

Commit

Permalink
Add per-path configurability (#1891)
Browse files Browse the repository at this point in the history
Allows URIs served by the `AdminServlet` to be individually enabled/disabled.
  • Loading branch information
mschechter-bellese committed Apr 30, 2021
1 parent 7fd7269 commit 32b6e3f
Show file tree
Hide file tree
Showing 3 changed files with 216 additions and 26 deletions.
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package com.codahale.metrics.servlets;

import javax.servlet.ServletConfig;
import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
Expand All @@ -16,14 +17,19 @@ public class AdminServlet extends HttpServlet {
public static final String DEFAULT_THREADS_URI = "/threads";
public static final String DEFAULT_CPU_PROFILE_URI = "/pprof";

public static final String METRICS_ENABLED_PARAM_KEY = "metrics-enabled";
public static final String METRICS_URI_PARAM_KEY = "metrics-uri";
public static final String PING_ENABLED_PARAM_KEY = "ping-enabled";
public static final String PING_URI_PARAM_KEY = "ping-uri";
public static final String THREADS_ENABLED_PARAM_KEY = "threads-enabled";
public static final String THREADS_URI_PARAM_KEY = "threads-uri";
public static final String HEALTHCHECK_ENABLED_PARAM_KEY = "healthcheck-enabled";
public static final String HEALTHCHECK_URI_PARAM_KEY = "healthcheck-uri";
public static final String SERVICE_NAME_PARAM_KEY = "service-name";
public static final String CPU_PROFILE_ENABLED_PARAM_KEY = "cpu-profile-enabled";
public static final String CPU_PROFILE_URI_PARAM_KEY = "cpu-profile-uri";

private static final String TEMPLATE = String.format(
private static final String BASE_TEMPLATE =
"<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.01 Transitional//EN\"%n" +
" \"http://www.w3.org/TR/html4/loose.dtd\">%n" +
"<html>%n" +
Expand All @@ -33,16 +39,18 @@ public class AdminServlet extends HttpServlet {
"<body>%n" +
" <h1>Operational Menu{10}</h1>%n" +
" <ul>%n" +
" <li><a href=\"{0}{1}?pretty=true\">Metrics</a></li>%n" +
" <li><a href=\"{2}{3}\">Ping</a></li>%n" +
" <li><a href=\"{4}{5}\">Threads</a></li>%n" +
" <li><a href=\"{6}{7}?pretty=true\">Healthcheck</a></li>%n" +
" <li><a href=\"{8}{9}\">CPU Profile</a></li>%n" +
" <li><a href=\"{8}{9}?state=blocked\">CPU Contention</a></li>%n" +
"%s" +
" </ul>%n" +
"</body>%n" +
"</html>"
);
"</html>";
private static final String METRICS_LINK = " <li><a href=\"{0}{1}?pretty=true\">Metrics</a></li>%n";
private static final String PING_LINK = " <li><a href=\"{2}{3}\">Ping</a></li>%n" ;
private static final String THREADS_LINK = " <li><a href=\"{4}{5}\">Threads</a></li>%n" ;
private static final String HEALTHCHECK_LINK = " <li><a href=\"{6}{7}?pretty=true\">Healthcheck</a></li>%n" ;
private static final String CPU_PROFILE_LINK = " <li><a href=\"{8}{9}\">CPU Profile</a></li>%n" +
" <li><a href=\"{8}{9}?state=blocked\">CPU Contention</a></li>%n";


private static final String CONTENT_TYPE = "text/html";
private static final long serialVersionUID = -2850794040708785318L;

Expand All @@ -51,38 +59,74 @@ public class AdminServlet extends HttpServlet {
private transient PingServlet pingServlet;
private transient ThreadDumpServlet threadDumpServlet;
private transient CpuProfileServlet cpuProfileServlet;
private transient boolean metricsEnabled;
private transient String metricsUri;
private transient boolean pingEnabled;
private transient String pingUri;
private transient boolean threadsEnabled;
private transient String threadsUri;
private transient boolean healthcheckEnabled;
private transient String healthcheckUri;
private transient String cpuprofileUri;
private transient boolean cpuProfileEnabled;
private transient String cpuProfileUri;
private transient String serviceName;
private transient String pageContentTemplate;

@Override
public void init(ServletConfig config) throws ServletException {
super.init(config);

this.healthCheckServlet = new HealthCheckServlet();
healthCheckServlet.init(config);
final ServletContext context = config.getServletContext();
final StringBuilder servletLinks = new StringBuilder();

this.metricsEnabled =
Boolean.parseBoolean(getParam(context.getInitParameter(METRICS_ENABLED_PARAM_KEY), "true"));
if (this.metricsEnabled) {
servletLinks.append(METRICS_LINK);
}
this.metricsServlet = new MetricsServlet();
metricsServlet.init(config);

this.pingEnabled =
Boolean.parseBoolean(getParam(context.getInitParameter(PING_ENABLED_PARAM_KEY), "true"));
if (this.pingEnabled) {
servletLinks.append(PING_LINK);
}
this.pingServlet = new PingServlet();
pingServlet.init(config);

this.threadsEnabled =
Boolean.parseBoolean(getParam(context.getInitParameter(THREADS_ENABLED_PARAM_KEY), "true"));
if (this.threadsEnabled) {
servletLinks.append(THREADS_LINK);
}
this.threadDumpServlet = new ThreadDumpServlet();
threadDumpServlet.init(config);

this.healthcheckEnabled =
Boolean.parseBoolean(getParam(context.getInitParameter(HEALTHCHECK_ENABLED_PARAM_KEY), "true"));
if (this.healthcheckEnabled) {
servletLinks.append(HEALTHCHECK_LINK);
}
this.healthCheckServlet = new HealthCheckServlet();
healthCheckServlet.init(config);

this.cpuProfileEnabled =
Boolean.parseBoolean(getParam(context.getInitParameter(CPU_PROFILE_ENABLED_PARAM_KEY), "true"));
if (this.cpuProfileEnabled) {
servletLinks.append(CPU_PROFILE_LINK);
}
this.cpuProfileServlet = new CpuProfileServlet();
cpuProfileServlet.init(config);

this.metricsUri = getParam(config.getInitParameter(METRICS_URI_PARAM_KEY), DEFAULT_METRICS_URI);
this.pingUri = getParam(config.getInitParameter(PING_URI_PARAM_KEY), DEFAULT_PING_URI);
this.threadsUri = getParam(config.getInitParameter(THREADS_URI_PARAM_KEY), DEFAULT_THREADS_URI);
this.healthcheckUri = getParam(config.getInitParameter(HEALTHCHECK_URI_PARAM_KEY), DEFAULT_HEALTHCHECK_URI);
this.cpuprofileUri = getParam(config.getInitParameter(CPU_PROFILE_URI_PARAM_KEY), DEFAULT_CPU_PROFILE_URI);
this.serviceName = getParam(config.getInitParameter(SERVICE_NAME_PARAM_KEY), null);
pageContentTemplate = String.format(BASE_TEMPLATE, String.format(servletLinks.toString()));

this.metricsUri = getParam(context.getInitParameter(METRICS_URI_PARAM_KEY), DEFAULT_METRICS_URI);
this.pingUri = getParam(context.getInitParameter(PING_URI_PARAM_KEY), DEFAULT_PING_URI);
this.threadsUri = getParam(context.getInitParameter(THREADS_URI_PARAM_KEY), DEFAULT_THREADS_URI);
this.healthcheckUri = getParam(context.getInitParameter(HEALTHCHECK_URI_PARAM_KEY), DEFAULT_HEALTHCHECK_URI);
this.cpuProfileUri = getParam(context.getInitParameter(CPU_PROFILE_URI_PARAM_KEY), DEFAULT_CPU_PROFILE_URI);
this.serviceName = getParam(context.getInitParameter(SERVICE_NAME_PARAM_KEY), null);
}

@Override
Expand All @@ -93,8 +137,8 @@ protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws Se
resp.setHeader("Cache-Control", "must-revalidate,no-cache,no-store");
resp.setContentType(CONTENT_TYPE);
try (PrintWriter writer = resp.getWriter()) {
writer.println(MessageFormat.format(TEMPLATE, path, metricsUri, path, pingUri, path,
threadsUri, path, healthcheckUri, path, cpuprofileUri,
writer.println(MessageFormat.format(pageContentTemplate, path, metricsUri, path, pingUri, path,
threadsUri, path, healthcheckUri, path, cpuProfileUri,
serviceName == null ? "" : " (" + serviceName + ")"));
}
}
Expand All @@ -105,15 +149,35 @@ protected void service(HttpServletRequest req, HttpServletResponse resp) throws
if (uri == null || uri.equals("/")) {
super.service(req, resp);
} else if (uri.equals(healthcheckUri)) {
healthCheckServlet.service(req, resp);
if (healthcheckEnabled) {
healthCheckServlet.service(req, resp);
} else {
resp.sendError(HttpServletResponse.SC_NOT_FOUND);
}
} else if (uri.startsWith(metricsUri)) {
metricsServlet.service(req, resp);
if (metricsEnabled) {
metricsServlet.service(req, resp);
} else {
resp.sendError(HttpServletResponse.SC_NOT_FOUND);
}
} else if (uri.equals(pingUri)) {
pingServlet.service(req, resp);
if (pingEnabled) {
pingServlet.service(req, resp);
} else {
resp.sendError(HttpServletResponse.SC_NOT_FOUND);
}
} else if (uri.equals(threadsUri)) {
threadDumpServlet.service(req, resp);
} else if (uri.equals(cpuprofileUri)) {
cpuProfileServlet.service(req, resp);
if (threadsEnabled) {
threadDumpServlet.service(req, resp);
} else {
resp.sendError(HttpServletResponse.SC_NOT_FOUND);
}
} else if (uri.equals(cpuProfileUri)) {
if (cpuProfileEnabled) {
cpuProfileServlet.service(req, resp);
} else {
resp.sendError(HttpServletResponse.SC_NOT_FOUND);
}
} else {
resp.sendError(HttpServletResponse.SC_NOT_FOUND);
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
package com.codahale.metrics.servlets;

import com.codahale.metrics.MetricRegistry;
import com.codahale.metrics.health.HealthCheckRegistry;
import static org.assertj.core.api.Assertions.assertThat;
import org.eclipse.jetty.http.HttpHeader;
import org.eclipse.jetty.servlet.ServletTester;
import org.junit.Before;
import org.junit.Test;

public class AdminServletExclusionTest extends AbstractServletTest {
private final MetricRegistry registry = new MetricRegistry();
private final HealthCheckRegistry healthCheckRegistry = new HealthCheckRegistry();

@Override
protected void setUp(ServletTester tester) {
tester.setContextPath("/context");

tester.setAttribute("com.codahale.metrics.servlets.MetricsServlet.registry", registry);
tester.setAttribute("com.codahale.metrics.servlets.HealthCheckServlet.registry", healthCheckRegistry);
tester.setInitParameter("threads-enabled", "false");
tester.setInitParameter("cpu-profile-enabled", "false");
tester.addServlet(AdminServlet.class, "/admin");
}

@Before
public void setUp() {
request.setMethod("GET");
request.setURI("/context/admin");
request.setVersion("HTTP/1.0");
}

@Test
public void returnsA200() throws Exception {
processRequest();

assertThat(response.getStatus())
.isEqualTo(200);
assertThat(response.getContent())
.isEqualTo(String.format(
"<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.01 Transitional//EN\"%n" +
" \"http://www.w3.org/TR/html4/loose.dtd\">%n" +
"<html>%n" +
"<head>%n" +
" <title>Metrics</title>%n" +
"</head>%n" +
"<body>%n" +
" <h1>Operational Menu</h1>%n" +
" <ul>%n" +
" <li><a href=\"/context/admin/metrics?pretty=true\">Metrics</a></li>%n" +
" <li><a href=\"/context/admin/ping\">Ping</a></li>%n" +
" <li><a href=\"/context/admin/healthcheck?pretty=true\">Healthcheck</a></li>%n" +
" </ul>%n" +
"</body>%n" +
"</html>%n"
));
assertThat(response.get(HttpHeader.CONTENT_TYPE))
.isEqualTo("text/html;charset=UTF-8");
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
package com.codahale.metrics.servlets;

import com.codahale.metrics.MetricRegistry;
import com.codahale.metrics.health.HealthCheckRegistry;
import static org.assertj.core.api.Assertions.assertThat;
import org.eclipse.jetty.http.HttpHeader;
import org.eclipse.jetty.servlet.ServletTester;
import org.junit.Before;
import org.junit.Test;

public class AdminServletUriTest extends AbstractServletTest {
private final MetricRegistry registry = new MetricRegistry();
private final HealthCheckRegistry healthCheckRegistry = new HealthCheckRegistry();

@Override
protected void setUp(ServletTester tester) {
tester.setContextPath("/context");

tester.setAttribute("com.codahale.metrics.servlets.MetricsServlet.registry", registry);
tester.setAttribute("com.codahale.metrics.servlets.HealthCheckServlet.registry", healthCheckRegistry);
tester.setInitParameter("metrics-uri", "/metrics-test");
tester.setInitParameter("ping-uri", "/ping-test");
tester.setInitParameter("threads-uri", "/threads-test");
tester.setInitParameter("healthcheck-uri", "/healthcheck-test");
tester.setInitParameter("cpu-profile-uri", "/pprof-test");
tester.addServlet(AdminServlet.class, "/admin");
}

@Before
public void setUp() {
request.setMethod("GET");
request.setURI("/context/admin");
request.setVersion("HTTP/1.0");
}

@Test
public void returnsA200() throws Exception {
processRequest();

assertThat(response.getStatus())
.isEqualTo(200);
assertThat(response.getContent())
.isEqualTo(String.format(
"<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.01 Transitional//EN\"%n" +
" \"http://www.w3.org/TR/html4/loose.dtd\">%n" +
"<html>%n" +
"<head>%n" +
" <title>Metrics</title>%n" +
"</head>%n" +
"<body>%n" +
" <h1>Operational Menu</h1>%n" +
" <ul>%n" +
" <li><a href=\"/context/admin/metrics-test?pretty=true\">Metrics</a></li>%n" +
" <li><a href=\"/context/admin/ping-test\">Ping</a></li>%n" +
" <li><a href=\"/context/admin/threads-test\">Threads</a></li>%n" +
" <li><a href=\"/context/admin/healthcheck-test?pretty=true\">Healthcheck</a></li>%n" +
" <li><a href=\"/context/admin/pprof-test\">CPU Profile</a></li>%n" +
" <li><a href=\"/context/admin/pprof-test?state=blocked\">CPU Contention</a></li>%n" +
" </ul>%n" +
"</body>%n" +
"</html>%n"
));
assertThat(response.get(HttpHeader.CONTENT_TYPE))
.isEqualTo("text/html;charset=UTF-8");
}
}

0 comments on commit 32b6e3f

Please sign in to comment.