uk.co.spudsoft.mgmt.ThreadDumpRoute Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of vertx-management-endpoints Show documentation
Show all versions of vertx-management-endpoints Show documentation
A few Vert.x HTTP Server routes for providing functionality similar to Springs actuators.
/*
* Copyright (C) 2022 jtalbut
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*/
package uk.co.spudsoft.mgmt;
import io.netty.handler.codec.http.HttpHeaderNames;
import io.vertx.core.Handler;
import io.vertx.core.http.HttpMethod;
import io.vertx.core.http.HttpServerRequest;
import io.vertx.core.http.HttpServerResponse;
import io.vertx.core.json.JsonArray;
import io.vertx.core.json.JsonObject;
import io.vertx.ext.web.Router;
import io.vertx.ext.web.RoutingContext;
import java.lang.management.ManagementFactory;
import java.lang.management.ThreadInfo;
import java.lang.management.ThreadMXBean;
import java.time.ZoneOffset;
import java.time.ZonedDateTime;
/**
* A Vertx HTTP Server route for allowing users to download a thread dump of the process.
*
* It is strongly recommended that this endpoint be mounted on via a subrouter, the path to which is only accessible from authorised personnel.
* The integration tests demonstrate the use of a suitable subrouter to locate the endpoint at /manage/threaddump.
*
* @author jtalbut
*/
public class ThreadDumpRoute implements Handler {
/**
* The path at which the standardDeploy method will put the router.
*/
public static final String PATH = "threads";
/**
* Constructor.
*/
public ThreadDumpRoute() {
}
/**
* Deploy the route to the router passed in at the normal endpoint.
*
* The router passed in should be a sub router that is inaccessible to normal users.
*
* @param router The router that this handler will be attached to.
*/
public void standardDeploy(Router router) {
router.route(HttpMethod.GET, "/" + PATH)
.handler(this::handle)
.setName("Thread Dump")
.produces(ContentTypes.TYPE_JSON)
.produces(ContentTypes.TYPE_HTML)
.produces(ContentTypes.TYPE_PLAIN)
;
}
/**
* Factory method to do standard deployment on newly constructed route.
*
* The router passed in should be a sub router that is inaccessible to normal users.
*
* @param router The router that this handler will be attached to.
*/
public static void createAndDeploy(Router router) {
ThreadDumpRoute route = new ThreadDumpRoute();
route.standardDeploy(router);
}
@Override
public void handle(RoutingContext rc) {
HttpServerRequest request = rc.request();
if (request.method() == HttpMethod.GET) {
ContentTypes.adjustFromParams(rc);
if (ContentTypes.TYPE_JSON.equals(rc.getAcceptableContentType())) {
HttpServerResponse response = rc.response();
response.setStatusCode(200);
response.putHeader(HttpHeaderNames.CONTENT_TYPE, ContentTypes.TYPE_JSON);
response.end(buildStackTraceJson().toBuffer());
} else if (ContentTypes.TYPE_HTML.equals(rc.getAcceptableContentType())) {
HttpServerResponse response = rc.response();
response.setStatusCode(200);
response.putHeader(HttpHeaderNames.CONTENT_TYPE, ContentTypes.TYPE_HTML);
response.end(buildStackTraceHtml());
} else {
HttpServerResponse response = rc.response();
response.setStatusCode(200);
response.putHeader(HttpHeaderNames.CONTENT_TYPE, ContentTypes.TYPE_PLAIN);
response.end(buildStackTraceText());
}
} else {
rc.next();
}
}
static String buildStackTraceHtml() {
ThreadMXBean threadMxBean = ManagementFactory.getThreadMXBean();
ThreadInfo[] threadInfo = threadMxBean.dumpAllThreads(true, true);
StringBuilder stackTraceString = new StringBuilder();
stackTraceString.append("");
stackTraceString.append("").append(HeapDumpRoute.getProcessName()).append(" @ ").append(ZonedDateTime.now(ZoneOffset.UTC).toString()).append(" ");
stackTraceString.append("");
stackTraceString.append("");
for (ThreadInfo t : threadInfo) {
stackTraceString.append("");
stackTraceString.append("");
stackTraceString.append(t.getThreadName());
stackTraceString.append(" ");
stackTraceString.append("");
stackTraceString.append(t.getThreadId());
stackTraceString.append(" ");
stackTraceString.append("");
stackTraceString.append(t.getThreadState());
stackTraceString.append(" ");
stackTraceString.append("");
stackTraceString.append(t.isDaemon() ? "daemon" : "");
stackTraceString.append(" ");
stackTraceString.append("");
stackTraceString.append(t.isSuspended() ? "suspended" : "");
stackTraceString.append(" ");
stackTraceString.append("");
stackTraceString.append(" ");
stackTraceString.append(" ");
String lockName = t.getLockName();
if (lockName != null) {
stackTraceString
.append("Waiting for ")
.append(lockName);
if (t.getLockOwnerId() >= 0) {
stackTraceString
.append("
Held by ")
.append(t.getLockOwnerId())
.append(" (")
.append(t.getLockOwnerName())
.append(")")
;
}
stackTraceString.append(" ");
}
stackTraceString.append("");
for (StackTraceElement s : t.getStackTrace()) {
stackTraceString.append(s.toString()).append("\n");
}
stackTraceString.append("
\n");
}
stackTraceString.append("
");
return stackTraceString.toString();
}
static JsonObject buildStackTraceJson() {
ThreadMXBean threadMxBean = ManagementFactory.getThreadMXBean();
ThreadInfo[] threadInfo = threadMxBean.dumpAllThreads(true, true);
JsonObject result = new JsonObject();
for (ThreadInfo t : threadInfo) {
JsonObject thread = new JsonObject();
result.put(t.getThreadName(), thread);
thread.put("id", t.getThreadId());
thread.put("daemon", t.isDaemon());
thread.put("suspended", t.isSuspended());
thread.put("state", t.getThreadState());
addNonNullObjectToJson(thread, "lockInfo", t.getLockInfo());
addNonNullObjectToJson(thread, "lockOwnerName", t.getLockOwnerName());
long lockOwnerId = t.getLockOwnerId();
if (lockOwnerId > 0) {
thread.put("lockOwnerId", lockOwnerId);
}
thread.put("blockedCount", t.getBlockedCount());
thread.put("blockedTime", t.getBlockedTime());
StackTraceElement[] stackTrace = t.getStackTrace();
JsonArray jsonStack = new JsonArray();
thread.put("stackTrace", jsonStack);
for (StackTraceElement s : stackTrace) {
JsonObject stackTraceElement = new JsonObject();
addNonNullObjectToJson(stackTraceElement, "classLoaderName", s.getClassLoaderName());
addNonNullObjectToJson(stackTraceElement, "className", s.getClassName());
addNonNullObjectToJson(stackTraceElement, "fileName", s.getFileName());
addNonNullObjectToJson(stackTraceElement, "lineNumber", s.getLineNumber());
addNonNullObjectToJson(stackTraceElement, "methodName", s.getMethodName());
addNonNullObjectToJson(stackTraceElement, "moduleName", s.getModuleName());
addNonNullObjectToJson(stackTraceElement, "moduleVersion", s.getModuleVersion());
jsonStack.add(stackTraceElement);
}
}
return result;
}
static void addNonNullObjectToJson(JsonObject parent, String name, Object value) {
if (name != null) {
parent.put(name, value);
}
}
static String buildStackTraceText() {
ThreadMXBean threadMxBean = ManagementFactory.getThreadMXBean();
ThreadInfo[] threadInfo = threadMxBean.dumpAllThreads(true, true);
StringBuilder stackTraceString = new StringBuilder();
for (ThreadInfo t : threadInfo) {
StackTraceElement[] stackTrace = t.getStackTrace();
stackTraceString.append(t.getThreadName())
.append(" (").append(t.getThreadState()).append(")")
.append(t.isDaemon() ? " Daemon" : "")
.append("\n");
if (t.isSuspended()) {
stackTraceString.append("Suspended\n");
}
String lockName = t.getLockName();
if (lockName != null) {
stackTraceString
.append("Waiting for ")
.append(lockName)
;
if (t.getLockOwnerId() >= 0) {
stackTraceString
.append(" held by ")
.append(t.getLockOwnerId())
.append(" (")
.append(t.getLockOwnerName())
.append(")")
;
}
stackTraceString
.append("\n")
;
}
for (StackTraceElement s : stackTrace) {
stackTraceString.append(" ").append(s.toString()).append("\n");
}
stackTraceString.append("\n");
}
return stackTraceString.toString();
}
}
© 2015 - 2024 Weber Informatics LLC | Privacy Policy