From 6c087c7bc8c4ca30966bdc4880e8bf53e8c2ed39 Mon Sep 17 00:00:00 2001 From: David Brassely Date: Wed, 27 Jul 2022 16:34:43 +0200 Subject: [PATCH 1/2] fix: Add endpoints for generating heapdump and threaddump Closes gravitee-io/issues#8222 --- gravitee-node-api/pom.xml | 2 +- gravitee-node-cache/pom.xml | 2 +- gravitee-node-certificates/pom.xml | 2 +- gravitee-node-cluster/pom.xml | 2 +- gravitee-node-container/pom.xml | 2 +- gravitee-node-jetty/pom.xml | 2 +- gravitee-node-kubernetes/pom.xml | 2 +- gravitee-node-management/pom.xml | 2 +- .../http/node/heap/HeapDumpEndpoint.java | 172 ++++++++++++++++++ .../http/node/heap/HeapDumpException.java | 27 +++ .../http/node/heap/HeapDumpSupplier.java | 28 +++ .../node/heap/HotSpotHeapDumpSupplier.java | 70 +++++++ .../node/heap/OpenJ9HeapDumpSupplier.java | 72 ++++++++ .../thread/PlainTextThreadDumpFormatter.java | 170 +++++++++++++++++ .../http/node/thread/ThreadDumpEndpoint.java | 98 ++++++++++ .../http/spring/ManagementConfiguration.java | 12 ++ .../vertx/verticle/ManagementVerticle.java | 34 ++++ gravitee-node-monitoring/pom.xml | 2 +- .../gravitee-node-plugins-service/pom.xml | 2 +- gravitee-node-plugins/pom.xml | 2 +- gravitee-node-reporter/pom.xml | 2 +- gravitee-node-tracing/pom.xml | 2 +- gravitee-node-vertx/pom.xml | 2 +- pom.xml | 2 +- 24 files changed, 698 insertions(+), 15 deletions(-) create mode 100644 gravitee-node-management/src/main/java/io/gravitee/node/management/http/node/heap/HeapDumpEndpoint.java create mode 100644 gravitee-node-management/src/main/java/io/gravitee/node/management/http/node/heap/HeapDumpException.java create mode 100644 gravitee-node-management/src/main/java/io/gravitee/node/management/http/node/heap/HeapDumpSupplier.java create mode 100644 gravitee-node-management/src/main/java/io/gravitee/node/management/http/node/heap/HotSpotHeapDumpSupplier.java create mode 100644 gravitee-node-management/src/main/java/io/gravitee/node/management/http/node/heap/OpenJ9HeapDumpSupplier.java create mode 100644 gravitee-node-management/src/main/java/io/gravitee/node/management/http/node/thread/PlainTextThreadDumpFormatter.java create mode 100644 gravitee-node-management/src/main/java/io/gravitee/node/management/http/node/thread/ThreadDumpEndpoint.java diff --git a/gravitee-node-api/pom.xml b/gravitee-node-api/pom.xml index bdc2ee4be..e04fe3d7a 100644 --- a/gravitee-node-api/pom.xml +++ b/gravitee-node-api/pom.xml @@ -24,7 +24,7 @@ io.gravitee.node gravitee-node - 1.20.4 + 1.20.5-api-dumps-SNAPSHOT gravitee-node-api diff --git a/gravitee-node-cache/pom.xml b/gravitee-node-cache/pom.xml index 672885a28..90e20d19d 100644 --- a/gravitee-node-cache/pom.xml +++ b/gravitee-node-cache/pom.xml @@ -24,7 +24,7 @@ io.gravitee.node gravitee-node - 1.20.4 + 1.20.5-api-dumps-SNAPSHOT gravitee-node-cache diff --git a/gravitee-node-certificates/pom.xml b/gravitee-node-certificates/pom.xml index 27f90a39f..16f1fb83c 100644 --- a/gravitee-node-certificates/pom.xml +++ b/gravitee-node-certificates/pom.xml @@ -24,7 +24,7 @@ io.gravitee.node gravitee-node - 1.20.4 + 1.20.5-api-dumps-SNAPSHOT gravitee-node-certificates diff --git a/gravitee-node-cluster/pom.xml b/gravitee-node-cluster/pom.xml index 88420ef35..18b5e8172 100644 --- a/gravitee-node-cluster/pom.xml +++ b/gravitee-node-cluster/pom.xml @@ -24,7 +24,7 @@ io.gravitee.node gravitee-node - 1.20.4 + 1.20.5-api-dumps-SNAPSHOT gravitee-node-cluster diff --git a/gravitee-node-container/pom.xml b/gravitee-node-container/pom.xml index 7b5042e61..92d804258 100644 --- a/gravitee-node-container/pom.xml +++ b/gravitee-node-container/pom.xml @@ -24,7 +24,7 @@ io.gravitee.node gravitee-node - 1.20.4 + 1.20.5-api-dumps-SNAPSHOT gravitee-node-container diff --git a/gravitee-node-jetty/pom.xml b/gravitee-node-jetty/pom.xml index a06cb1f04..9416a3cc8 100644 --- a/gravitee-node-jetty/pom.xml +++ b/gravitee-node-jetty/pom.xml @@ -24,7 +24,7 @@ io.gravitee.node gravitee-node - 1.20.4 + 1.20.5-api-dumps-SNAPSHOT gravitee-node-jetty diff --git a/gravitee-node-kubernetes/pom.xml b/gravitee-node-kubernetes/pom.xml index a349fb3b7..9322603f1 100644 --- a/gravitee-node-kubernetes/pom.xml +++ b/gravitee-node-kubernetes/pom.xml @@ -24,7 +24,7 @@ io.gravitee.node gravitee-node - 1.20.4 + 1.20.5-api-dumps-SNAPSHOT gravitee-node-kubernetes diff --git a/gravitee-node-management/pom.xml b/gravitee-node-management/pom.xml index 39824754e..70a66359b 100644 --- a/gravitee-node-management/pom.xml +++ b/gravitee-node-management/pom.xml @@ -24,7 +24,7 @@ io.gravitee.node gravitee-node - 1.20.4 + 1.20.5-api-dumps-SNAPSHOT gravitee-node-management diff --git a/gravitee-node-management/src/main/java/io/gravitee/node/management/http/node/heap/HeapDumpEndpoint.java b/gravitee-node-management/src/main/java/io/gravitee/node/management/http/node/heap/HeapDumpEndpoint.java new file mode 100644 index 000000000..9f1957594 --- /dev/null +++ b/gravitee-node-management/src/main/java/io/gravitee/node/management/http/node/heap/HeapDumpEndpoint.java @@ -0,0 +1,172 @@ +/** + * Copyright (C) 2015 The Gravitee team (http://gravitee.io) + * + * Licensed 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 io.gravitee.node.management.http.node.heap; + +import io.gravitee.common.http.HttpMethod; +import io.gravitee.common.http.HttpStatusCode; +import io.gravitee.node.management.http.endpoint.ManagementEndpoint; +import io.vertx.core.AsyncResult; +import io.vertx.core.Handler; +import io.vertx.core.Promise; +import io.vertx.core.http.HttpServerResponse; +import io.vertx.ext.web.RoutingContext; +import java.io.File; +import java.io.IOException; +import java.nio.file.Files; +import java.time.LocalDateTime; +import java.time.format.DateTimeFormatter; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.locks.Lock; +import java.util.concurrent.locks.ReentrantLock; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * @author David BRASSELY (david.brassely at graviteesource.com) + * @author GraviteeSource Team + */ +public class HeapDumpEndpoint implements ManagementEndpoint { + + private final Logger LOGGER = LoggerFactory.getLogger(HeapDumpEndpoint.class); + + private final long timeout = TimeUnit.SECONDS.toMillis(10); + + private final Lock lock = new ReentrantLock(); + + private HeapDumpSupplier heapDumpSupplier; + + @Override + public HttpMethod method() { + return HttpMethod.GET; + } + + @Override + public String path() { + return "/heapdump"; + } + + @Override + public void handle(RoutingContext ctx) { + final HttpServerResponse response = ctx.response(); + final boolean live = Boolean.parseBoolean( + ctx.request().getParam("live", Boolean.FALSE.toString()) + ); + + ctx + .vertx() + .executeBlocking( + new Handler>() { + @Override + public void handle(Promise promise) { + try { + if ( + HeapDumpEndpoint.this.lock.tryLock( + HeapDumpEndpoint.this.timeout, + TimeUnit.MILLISECONDS + ) + ) { + try { + File dumpFile = dump(live); + promise.complete(dumpFile); + } catch (Exception e) { + promise.fail(e); + } + } + } catch (InterruptedException ex) { + promise.fail(ex); + } finally { + HeapDumpEndpoint.this.lock.unlock(); + } + } + }, + new Handler>() { + @Override + public void handle(AsyncResult fileAsyncResult) { + if (fileAsyncResult.succeeded()) { + File file = fileAsyncResult.result(); + response + .setStatusCode(HttpStatusCode.OK_200) + .setChunked(true) + .sendFile(file.getAbsolutePath()) + .onComplete( + new Handler>() { + @Override + public void handle(AsyncResult voidAsyncResult) { + try { + Files.delete(file.toPath()); + } catch (IOException ex) { + LOGGER.warn( + "Failed to delete temporary heap dump file '" + + file.toPath() + + "'", + ex + ); + } + } + } + ); + } else { + LOGGER.error( + "Unable to generate heap dump.", + fileAsyncResult.cause() + ); + response + .setStatusCode(HttpStatusCode.INTERNAL_SERVER_ERROR_500) + .setChunked(true) + .send(fileAsyncResult.cause().getMessage()); + } + } + } + ); + } + + private File dump(boolean live) throws IOException, InterruptedException { + if (this.heapDumpSupplier == null) { + this.heapDumpSupplier = createHeapDumpSupplier(); + } + + File file = createTempFile(); + this.heapDumpSupplier.dump(file, live); + return file; + } + + private File createTempFile() throws IOException { + String date = DateTimeFormatter + .ofPattern("yyyy-MM-dd-HH-mm") + .format(LocalDateTime.now()); + File file = File.createTempFile( + "heap-" + date, + "." + determineDumpSuffix() + ); + file.delete(); + return file; + } + + protected HeapDumpSupplier createHeapDumpSupplier() throws HeapDumpException { + try { + return new HotSpotHeapDumpSupplier(); + } catch (HeapDumpException ex) { + return new OpenJ9HeapDumpSupplier(); + } + } + + private String determineDumpSuffix() { + if (this.heapDumpSupplier instanceof OpenJ9HeapDumpSupplier) { + return "phd"; + } + return "hprof"; + } +} diff --git a/gravitee-node-management/src/main/java/io/gravitee/node/management/http/node/heap/HeapDumpException.java b/gravitee-node-management/src/main/java/io/gravitee/node/management/http/node/heap/HeapDumpException.java new file mode 100644 index 000000000..735928918 --- /dev/null +++ b/gravitee-node-management/src/main/java/io/gravitee/node/management/http/node/heap/HeapDumpException.java @@ -0,0 +1,27 @@ +/** + * Copyright (C) 2015 The Gravitee team (http://gravitee.io) + * + * Licensed 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 io.gravitee.node.management.http.node.heap; + +/** + * @author David BRASSELY (david.brassely at graviteesource.com) + * @author GraviteeSource Team + */ +public class HeapDumpException extends RuntimeException { + + public HeapDumpException(String message, Throwable cause) { + super(message, cause); + } +} diff --git a/gravitee-node-management/src/main/java/io/gravitee/node/management/http/node/heap/HeapDumpSupplier.java b/gravitee-node-management/src/main/java/io/gravitee/node/management/http/node/heap/HeapDumpSupplier.java new file mode 100644 index 000000000..664a8ebc3 --- /dev/null +++ b/gravitee-node-management/src/main/java/io/gravitee/node/management/http/node/heap/HeapDumpSupplier.java @@ -0,0 +1,28 @@ +/** + * Copyright (C) 2015 The Gravitee team (http://gravitee.io) + * + * Licensed 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 io.gravitee.node.management.http.node.heap; + +import java.io.File; +import java.io.IOException; + +/** + * @author David BRASSELY (david.brassely at graviteesource.com) + * @author GraviteeSource Team + */ +@FunctionalInterface +public interface HeapDumpSupplier { + void dump(File file, boolean live) throws IOException, InterruptedException; +} diff --git a/gravitee-node-management/src/main/java/io/gravitee/node/management/http/node/heap/HotSpotHeapDumpSupplier.java b/gravitee-node-management/src/main/java/io/gravitee/node/management/http/node/heap/HotSpotHeapDumpSupplier.java new file mode 100644 index 000000000..896d4e084 --- /dev/null +++ b/gravitee-node-management/src/main/java/io/gravitee/node/management/http/node/heap/HotSpotHeapDumpSupplier.java @@ -0,0 +1,70 @@ +/** + * Copyright (C) 2015 The Gravitee team (http://gravitee.io) + * + * Licensed 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 io.gravitee.node.management.http.node.heap; + +import java.io.File; +import java.lang.management.ManagementFactory; +import java.lang.management.PlatformManagedObject; +import java.lang.reflect.Method; +import org.springframework.util.ClassUtils; +import org.springframework.util.ReflectionUtils; + +/** + * @author David BRASSELY (david.brassely at graviteesource.com) + * @author GraviteeSource Team + */ +public class HotSpotHeapDumpSupplier implements HeapDumpSupplier { + + private Object diagnosticMXBean; + + private Method dumpHeapMethod; + + @SuppressWarnings("unchecked") + HotSpotHeapDumpSupplier() { + try { + Class diagnosticMXBeanClass = ClassUtils.resolveClassName( + "com.sun.management.HotSpotDiagnosticMXBean", + null + ); + this.diagnosticMXBean = + ManagementFactory.getPlatformMXBean( + (Class) diagnosticMXBeanClass + ); + this.dumpHeapMethod = + ReflectionUtils.findMethod( + diagnosticMXBeanClass, + "dumpHeap", + String.class, + Boolean.TYPE + ); + } catch (Throwable ex) { + throw new HeapDumpException( + "Unable to locate HotSpotDiagnosticMXBean", + ex + ); + } + } + + @Override + public void dump(File file, boolean live) { + ReflectionUtils.invokeMethod( + this.dumpHeapMethod, + this.diagnosticMXBean, + file.getAbsolutePath(), + live + ); + } +} diff --git a/gravitee-node-management/src/main/java/io/gravitee/node/management/http/node/heap/OpenJ9HeapDumpSupplier.java b/gravitee-node-management/src/main/java/io/gravitee/node/management/http/node/heap/OpenJ9HeapDumpSupplier.java new file mode 100644 index 000000000..3e457810b --- /dev/null +++ b/gravitee-node-management/src/main/java/io/gravitee/node/management/http/node/heap/OpenJ9HeapDumpSupplier.java @@ -0,0 +1,72 @@ +/** + * Copyright (C) 2015 The Gravitee team (http://gravitee.io) + * + * Licensed 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 io.gravitee.node.management.http.node.heap; + +import java.io.File; +import java.io.IOException; +import java.lang.management.ManagementFactory; +import java.lang.management.PlatformManagedObject; +import java.lang.reflect.Method; +import org.springframework.util.ClassUtils; +import org.springframework.util.ReflectionUtils; + +/** + * @author David BRASSELY (david.brassely at graviteesource.com) + * @author GraviteeSource Team + */ +public class OpenJ9HeapDumpSupplier implements HeapDumpSupplier { + + private Object diagnosticMXBean; + + private Method dumpHeapMethod; + + @SuppressWarnings("unchecked") + OpenJ9HeapDumpSupplier() { + try { + Class mxBeanClass = ClassUtils.resolveClassName( + "openj9.lang.management.OpenJ9DiagnosticsMXBean", + null + ); + this.diagnosticMXBean = + ManagementFactory.getPlatformMXBean( + (Class) mxBeanClass + ); + this.dumpHeapMethod = + ReflectionUtils.findMethod( + mxBeanClass, + "triggerDumpToFile", + String.class, + String.class + ); + } catch (Throwable ex) { + throw new HeapDumpException( + "Unable to locate OpenJ9DiagnosticsMXBean", + ex + ); + } + } + + @Override + public void dump(File file, boolean live) + throws IOException, InterruptedException { + ReflectionUtils.invokeMethod( + this.dumpHeapMethod, + this.diagnosticMXBean, + "heap", + file.getAbsolutePath() + ); + } +} diff --git a/gravitee-node-management/src/main/java/io/gravitee/node/management/http/node/thread/PlainTextThreadDumpFormatter.java b/gravitee-node-management/src/main/java/io/gravitee/node/management/http/node/thread/PlainTextThreadDumpFormatter.java new file mode 100644 index 000000000..dc3519da6 --- /dev/null +++ b/gravitee-node-management/src/main/java/io/gravitee/node/management/http/node/thread/PlainTextThreadDumpFormatter.java @@ -0,0 +1,170 @@ +/** + * Copyright (C) 2015 The Gravitee team (http://gravitee.io) + * + * Licensed 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 io.gravitee.node.management.http.node.thread; + +import java.io.PrintWriter; +import java.io.StringWriter; +import java.lang.management.LockInfo; +import java.lang.management.ManagementFactory; +import java.lang.management.MonitorInfo; +import java.lang.management.RuntimeMXBean; +import java.lang.management.ThreadInfo; +import java.time.LocalDateTime; +import java.time.format.DateTimeFormatter; +import java.util.List; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +/** + * @author David BRASSELY (david.brassely at graviteesource.com) + * @author GraviteeSource Team + */ +class PlainTextThreadDumpFormatter { + + String format(ThreadInfo[] threads) { + StringWriter dump = new StringWriter(); + PrintWriter writer = new PrintWriter(dump); + writePreamble(writer); + for (ThreadInfo info : threads) { + writeThread(writer, info); + } + return dump.toString(); + } + + private void writePreamble(PrintWriter writer) { + DateTimeFormatter dateFormat = DateTimeFormatter.ofPattern( + "yyyy-MM-dd HH:mm:ss" + ); + writer.println(dateFormat.format(LocalDateTime.now())); + RuntimeMXBean runtime = ManagementFactory.getRuntimeMXBean(); + writer.printf( + "Full thread dump %s (%s %s):%n", + runtime.getVmName(), + runtime.getVmVersion(), + System.getProperty("java.vm.info") + ); + writer.println(); + } + + private void writeThread(PrintWriter writer, ThreadInfo info) { + writer.printf( + "\"%s\" - Thread t@%d%n", + info.getThreadName(), + info.getThreadId() + ); + writer.printf( + " %s: %s%n", + Thread.State.class.getCanonicalName(), + info.getThreadState() + ); + writeStackTrace(writer, info, info.getLockedMonitors()); + writer.println(); + writeLockedOwnableSynchronizers(writer, info); + writer.println(); + } + + private void writeStackTrace( + PrintWriter writer, + ThreadInfo info, + MonitorInfo[] lockedMonitors + ) { + int depth = 0; + for (StackTraceElement element : info.getStackTrace()) { + writeStackTraceElement( + writer, + element, + info, + lockedMonitorsForDepth(lockedMonitors, depth), + depth == 0 + ); + depth++; + } + } + + private List lockedMonitorsForDepth( + MonitorInfo[] lockedMonitors, + int depth + ) { + return Stream + .of(lockedMonitors) + .filter(lockedMonitor -> lockedMonitor.getLockedStackDepth() == depth) + .collect(Collectors.toList()); + } + + private void writeStackTraceElement( + PrintWriter writer, + StackTraceElement element, + ThreadInfo info, + List lockedMonitors, + boolean firstElement + ) { + writer.printf("\tat %s%n", element.toString()); + LockInfo lockInfo = info.getLockInfo(); + if (firstElement && lockInfo != null) { + if ( + element.getClassName().equals(Object.class.getName()) && + element.getMethodName().equals("wait") + ) { + writer.printf("\t- waiting on %s%n", format(lockInfo)); + } else { + String lockOwner = info.getLockOwnerName(); + if (lockOwner != null) { + writer.printf( + "\t- waiting to lock %s owned by \"%s\" t@%d%n", + format(lockInfo), + lockOwner, + info.getLockOwnerId() + ); + } else { + writer.printf("\t- parking to wait for %s%n", format(lockInfo)); + } + } + } + writeMonitors(writer, lockedMonitors); + } + + private String format(LockInfo lockInfo) { + return String.format( + "<%x> (a %s)", + lockInfo.getIdentityHashCode(), + lockInfo.getClassName() + ); + } + + private void writeMonitors( + PrintWriter writer, + List lockedMonitorsAtCurrentDepth + ) { + for (MonitorInfo lockedMonitor : lockedMonitorsAtCurrentDepth) { + writer.printf("\t- locked %s%n", format(lockedMonitor)); + } + } + + private void writeLockedOwnableSynchronizers( + PrintWriter writer, + ThreadInfo info + ) { + writer.println(" Locked ownable synchronizers:"); + LockInfo[] lockedSynchronizers = info.getLockedSynchronizers(); + if (lockedSynchronizers == null || lockedSynchronizers.length == 0) { + writer.println("\t- None"); + } else { + for (LockInfo lockedSynchronizer : lockedSynchronizers) { + writer.printf("\t- Locked %s%n", format(lockedSynchronizer)); + } + } + } +} diff --git a/gravitee-node-management/src/main/java/io/gravitee/node/management/http/node/thread/ThreadDumpEndpoint.java b/gravitee-node-management/src/main/java/io/gravitee/node/management/http/node/thread/ThreadDumpEndpoint.java new file mode 100644 index 000000000..2383f4700 --- /dev/null +++ b/gravitee-node-management/src/main/java/io/gravitee/node/management/http/node/thread/ThreadDumpEndpoint.java @@ -0,0 +1,98 @@ +/** + * Copyright (C) 2015 The Gravitee team (http://gravitee.io) + * + * Licensed 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 io.gravitee.node.management.http.node.thread; + +import io.gravitee.common.http.HttpMethod; +import io.gravitee.common.http.HttpStatusCode; +import io.gravitee.node.management.http.endpoint.ManagementEndpoint; +import io.vertx.core.AsyncResult; +import io.vertx.core.Handler; +import io.vertx.core.Promise; +import io.vertx.core.http.HttpHeaders; +import io.vertx.ext.web.RoutingContext; +import java.lang.management.ManagementFactory; +import java.lang.management.ThreadInfo; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * @author David BRASSELY (david.brassely at graviteesource.com) + * @author GraviteeSource Team + */ +public class ThreadDumpEndpoint implements ManagementEndpoint { + + private final Logger LOGGER = LoggerFactory.getLogger( + ThreadDumpEndpoint.class + ); + + private final PlainTextThreadDumpFormatter plainTextFormatter = new PlainTextThreadDumpFormatter(); + + @Override + public HttpMethod method() { + return HttpMethod.GET; + } + + @Override + public String path() { + return "/threaddump"; + } + + @Override + public void handle(RoutingContext context) { + context + .vertx() + .executeBlocking( + new Handler>() { + @Override + public void handle(Promise promise) { + try { + ThreadInfo[] threadInfos = ManagementFactory + .getThreadMXBean() + .dumpAllThreads(true, true); + String dump = plainTextFormatter.format(threadInfos); + + promise.complete(dump); + } catch (Exception ex) { + promise.fail(ex); + } + } + }, + new Handler>() { + @Override + public void handle(AsyncResult threadDumpResult) { + if (threadDumpResult.succeeded()) { + context + .response() + .setStatusCode(HttpStatusCode.OK_200) + .putHeader(HttpHeaders.CONTENT_TYPE, "text/plain;charset=UTF-8") + .setChunked(true) + .send(threadDumpResult.result()); + } else { + LOGGER.error( + "Unable to generate thread dump.", + threadDumpResult.cause() + ); + context + .response() + .setStatusCode(HttpStatusCode.INTERNAL_SERVER_ERROR_500) + .setChunked(true) + .send(threadDumpResult.cause().getMessage()); + } + } + } + ); + } +} diff --git a/gravitee-node-management/src/main/java/io/gravitee/node/management/http/spring/ManagementConfiguration.java b/gravitee-node-management/src/main/java/io/gravitee/node/management/http/spring/ManagementConfiguration.java index ca72f3d36..56ff63710 100644 --- a/gravitee-node-management/src/main/java/io/gravitee/node/management/http/spring/ManagementConfiguration.java +++ b/gravitee-node-management/src/main/java/io/gravitee/node/management/http/spring/ManagementConfiguration.java @@ -20,6 +20,8 @@ import io.gravitee.node.management.http.endpoint.ManagementEndpointManager; import io.gravitee.node.management.http.metrics.prometheus.PrometheusEndpoint; import io.gravitee.node.management.http.node.NodeEndpoint; +import io.gravitee.node.management.http.node.heap.HeapDumpEndpoint; +import io.gravitee.node.management.http.node.thread.ThreadDumpEndpoint; import io.gravitee.node.management.http.vertx.endpoint.ManagementEndpointManagerImpl; import io.gravitee.node.management.http.vertx.spring.HttpServerSpringConfiguration; import io.gravitee.node.management.http.vertx.verticle.ManagementVerticle; @@ -64,4 +66,14 @@ public PrometheusEndpoint prometheusEndpoint() { public ConfigurationEndpoint configurationEndpoint() { return new ConfigurationEndpoint(); } + + @Bean + public HeapDumpEndpoint heapDumpEndpoint() { + return new HeapDumpEndpoint(); + } + + @Bean + public ThreadDumpEndpoint threadDumpEndpoint() { + return new ThreadDumpEndpoint(); + } } diff --git a/gravitee-node-management/src/main/java/io/gravitee/node/management/http/vertx/verticle/ManagementVerticle.java b/gravitee-node-management/src/main/java/io/gravitee/node/management/http/vertx/verticle/ManagementVerticle.java index be1af8e9f..11f24b2c9 100644 --- a/gravitee-node-management/src/main/java/io/gravitee/node/management/http/vertx/verticle/ManagementVerticle.java +++ b/gravitee-node-management/src/main/java/io/gravitee/node/management/http/vertx/verticle/ManagementVerticle.java @@ -20,6 +20,8 @@ import io.gravitee.node.management.http.endpoint.ManagementEndpointManager; import io.gravitee.node.management.http.metrics.prometheus.PrometheusEndpoint; import io.gravitee.node.management.http.node.NodeEndpoint; +import io.gravitee.node.management.http.node.heap.HeapDumpEndpoint; +import io.gravitee.node.management.http.node.thread.ThreadDumpEndpoint; import io.gravitee.node.management.http.vertx.configuration.HttpServerConfiguration; import io.vertx.core.*; import io.vertx.core.http.HttpServer; @@ -75,6 +77,12 @@ public class ManagementVerticle extends AbstractVerticle { @Autowired private NodeEndpoint nodeEndpoint; + @Autowired + private HeapDumpEndpoint heapDumpEndpoint; + + @Autowired + private ThreadDumpEndpoint threadDumpEndpoint; + @Autowired private ConfigurationEndpoint configurationEndpoint; @@ -169,6 +177,32 @@ private void doStart(Promise promise) throws Exception { managementEndpointManager.register(nodeEndpoint); managementEndpointManager.register(configurationEndpoint); + // Heapdump endpoint is disabled by default for security reasons. It is up to the platform administrator + // to configure correctly authentication (ie. not using default credentials, or disabling authentication) + // before exposing such endpoint. + if ( + environment.getProperty( + "services.core.endpoints.heapdump.enabled", + Boolean.class, + false + ) + ) { + managementEndpointManager.register(heapDumpEndpoint); + } + + // Threaddump endpoint is disabled by default for security reasons. It is up to the platform administrator + // to configure correctly authentication (ie. not using default credentials, or disabling authentication) + // before exposing such endpoint. + if ( + environment.getProperty( + "services.core.endpoints.threaddump.enabled", + Boolean.class, + false + ) + ) { + managementEndpointManager.register(threadDumpEndpoint); + } + // Metrics boolean metricsEnabled = environment.getProperty( "services.metrics.enabled", diff --git a/gravitee-node-monitoring/pom.xml b/gravitee-node-monitoring/pom.xml index 6172cbf3b..b6f6984ac 100644 --- a/gravitee-node-monitoring/pom.xml +++ b/gravitee-node-monitoring/pom.xml @@ -24,7 +24,7 @@ io.gravitee.node gravitee-node - 1.20.4 + 1.20.5-api-dumps-SNAPSHOT gravitee-node-monitoring diff --git a/gravitee-node-plugins/gravitee-node-plugins-service/pom.xml b/gravitee-node-plugins/gravitee-node-plugins-service/pom.xml index 48252e6e6..db8e13f91 100644 --- a/gravitee-node-plugins/gravitee-node-plugins-service/pom.xml +++ b/gravitee-node-plugins/gravitee-node-plugins-service/pom.xml @@ -24,7 +24,7 @@ io.gravitee.node gravitee-node-plugins - 1.20.4 + 1.20.5-api-dumps-SNAPSHOT gravitee-node-plugins-service diff --git a/gravitee-node-plugins/pom.xml b/gravitee-node-plugins/pom.xml index e57fa236e..14160487c 100644 --- a/gravitee-node-plugins/pom.xml +++ b/gravitee-node-plugins/pom.xml @@ -24,7 +24,7 @@ io.gravitee.node gravitee-node - 1.20.4 + 1.20.5-api-dumps-SNAPSHOT gravitee-node-plugins diff --git a/gravitee-node-reporter/pom.xml b/gravitee-node-reporter/pom.xml index 3a4872869..616b10708 100644 --- a/gravitee-node-reporter/pom.xml +++ b/gravitee-node-reporter/pom.xml @@ -24,7 +24,7 @@ io.gravitee.node gravitee-node - 1.20.4 + 1.20.5-api-dumps-SNAPSHOT gravitee-node-reporter diff --git a/gravitee-node-tracing/pom.xml b/gravitee-node-tracing/pom.xml index 40e660728..9c252021f 100644 --- a/gravitee-node-tracing/pom.xml +++ b/gravitee-node-tracing/pom.xml @@ -24,7 +24,7 @@ io.gravitee.node gravitee-node - 1.20.4 + 1.20.5-api-dumps-SNAPSHOT gravitee-node-tracing diff --git a/gravitee-node-vertx/pom.xml b/gravitee-node-vertx/pom.xml index 21096f592..4c27ad60f 100644 --- a/gravitee-node-vertx/pom.xml +++ b/gravitee-node-vertx/pom.xml @@ -24,7 +24,7 @@ io.gravitee.node gravitee-node - 1.20.4 + 1.20.5-api-dumps-SNAPSHOT gravitee-node-vertx diff --git a/pom.xml b/pom.xml index 3f4698516..30ab7da6b 100644 --- a/pom.xml +++ b/pom.xml @@ -29,7 +29,7 @@ io.gravitee.node gravitee-node - 1.20.4 + 1.20.5-api-dumps-SNAPSHOT Gravitee.io - Node From bc64d77f1e2c8048e7c7cbe39e46f8a1d253e2f6 Mon Sep 17 00:00:00 2001 From: "Gravitee.io Bot" Date: Thu, 28 Jul 2022 10:08:22 +0000 Subject: [PATCH 2/2] chore(release): 1.20.5 [skip ci] --- CHANGELOG.md | 7 +++++++ gravitee-node-api/pom.xml | 2 +- gravitee-node-cache/pom.xml | 2 +- gravitee-node-certificates/pom.xml | 2 +- gravitee-node-cluster/pom.xml | 2 +- gravitee-node-container/pom.xml | 2 +- gravitee-node-jetty/pom.xml | 2 +- gravitee-node-kubernetes/pom.xml | 2 +- gravitee-node-management/pom.xml | 2 +- gravitee-node-monitoring/pom.xml | 2 +- .../gravitee-node-plugins-service/pom.xml | 2 +- gravitee-node-plugins/pom.xml | 2 +- gravitee-node-reporter/pom.xml | 2 +- gravitee-node-tracing/pom.xml | 2 +- gravitee-node-vertx/pom.xml | 2 +- pom.xml | 2 +- 16 files changed, 22 insertions(+), 15 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index fbe6342bf..2b98ccd94 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,10 @@ +## [1.20.5](https://github.com/gravitee-io/gravitee-node/compare/1.20.4...1.20.5) (2022-07-28) + + +### Bug Fixes + +* Add endpoints for generating heapdump and threaddump ([6c087c7](https://github.com/gravitee-io/gravitee-node/commit/6c087c7bc8c4ca30966bdc4880e8bf53e8c2ed39)), closes [gravitee-io/issues#8222](https://github.com/gravitee-io/issues/issues/8222) + ## [1.20.4](https://github.com/gravitee-io/gravitee-node/compare/1.20.3...1.20.4) (2022-06-09) diff --git a/gravitee-node-api/pom.xml b/gravitee-node-api/pom.xml index e04fe3d7a..a403f367d 100644 --- a/gravitee-node-api/pom.xml +++ b/gravitee-node-api/pom.xml @@ -24,7 +24,7 @@ io.gravitee.node gravitee-node - 1.20.5-api-dumps-SNAPSHOT + 1.20.5 gravitee-node-api diff --git a/gravitee-node-cache/pom.xml b/gravitee-node-cache/pom.xml index 90e20d19d..f3f8acb73 100644 --- a/gravitee-node-cache/pom.xml +++ b/gravitee-node-cache/pom.xml @@ -24,7 +24,7 @@ io.gravitee.node gravitee-node - 1.20.5-api-dumps-SNAPSHOT + 1.20.5 gravitee-node-cache diff --git a/gravitee-node-certificates/pom.xml b/gravitee-node-certificates/pom.xml index 16f1fb83c..016221aaf 100644 --- a/gravitee-node-certificates/pom.xml +++ b/gravitee-node-certificates/pom.xml @@ -24,7 +24,7 @@ io.gravitee.node gravitee-node - 1.20.5-api-dumps-SNAPSHOT + 1.20.5 gravitee-node-certificates diff --git a/gravitee-node-cluster/pom.xml b/gravitee-node-cluster/pom.xml index 18b5e8172..76e586625 100644 --- a/gravitee-node-cluster/pom.xml +++ b/gravitee-node-cluster/pom.xml @@ -24,7 +24,7 @@ io.gravitee.node gravitee-node - 1.20.5-api-dumps-SNAPSHOT + 1.20.5 gravitee-node-cluster diff --git a/gravitee-node-container/pom.xml b/gravitee-node-container/pom.xml index 92d804258..0e147ded3 100644 --- a/gravitee-node-container/pom.xml +++ b/gravitee-node-container/pom.xml @@ -24,7 +24,7 @@ io.gravitee.node gravitee-node - 1.20.5-api-dumps-SNAPSHOT + 1.20.5 gravitee-node-container diff --git a/gravitee-node-jetty/pom.xml b/gravitee-node-jetty/pom.xml index 9416a3cc8..26a9ed20d 100644 --- a/gravitee-node-jetty/pom.xml +++ b/gravitee-node-jetty/pom.xml @@ -24,7 +24,7 @@ io.gravitee.node gravitee-node - 1.20.5-api-dumps-SNAPSHOT + 1.20.5 gravitee-node-jetty diff --git a/gravitee-node-kubernetes/pom.xml b/gravitee-node-kubernetes/pom.xml index 9322603f1..c882343a9 100644 --- a/gravitee-node-kubernetes/pom.xml +++ b/gravitee-node-kubernetes/pom.xml @@ -24,7 +24,7 @@ io.gravitee.node gravitee-node - 1.20.5-api-dumps-SNAPSHOT + 1.20.5 gravitee-node-kubernetes diff --git a/gravitee-node-management/pom.xml b/gravitee-node-management/pom.xml index 70a66359b..63e3bf9b2 100644 --- a/gravitee-node-management/pom.xml +++ b/gravitee-node-management/pom.xml @@ -24,7 +24,7 @@ io.gravitee.node gravitee-node - 1.20.5-api-dumps-SNAPSHOT + 1.20.5 gravitee-node-management diff --git a/gravitee-node-monitoring/pom.xml b/gravitee-node-monitoring/pom.xml index b6f6984ac..2b380eb46 100644 --- a/gravitee-node-monitoring/pom.xml +++ b/gravitee-node-monitoring/pom.xml @@ -24,7 +24,7 @@ io.gravitee.node gravitee-node - 1.20.5-api-dumps-SNAPSHOT + 1.20.5 gravitee-node-monitoring diff --git a/gravitee-node-plugins/gravitee-node-plugins-service/pom.xml b/gravitee-node-plugins/gravitee-node-plugins-service/pom.xml index db8e13f91..a551f24d9 100644 --- a/gravitee-node-plugins/gravitee-node-plugins-service/pom.xml +++ b/gravitee-node-plugins/gravitee-node-plugins-service/pom.xml @@ -24,7 +24,7 @@ io.gravitee.node gravitee-node-plugins - 1.20.5-api-dumps-SNAPSHOT + 1.20.5 gravitee-node-plugins-service diff --git a/gravitee-node-plugins/pom.xml b/gravitee-node-plugins/pom.xml index 14160487c..074c4c734 100644 --- a/gravitee-node-plugins/pom.xml +++ b/gravitee-node-plugins/pom.xml @@ -24,7 +24,7 @@ io.gravitee.node gravitee-node - 1.20.5-api-dumps-SNAPSHOT + 1.20.5 gravitee-node-plugins diff --git a/gravitee-node-reporter/pom.xml b/gravitee-node-reporter/pom.xml index 616b10708..f992d854d 100644 --- a/gravitee-node-reporter/pom.xml +++ b/gravitee-node-reporter/pom.xml @@ -24,7 +24,7 @@ io.gravitee.node gravitee-node - 1.20.5-api-dumps-SNAPSHOT + 1.20.5 gravitee-node-reporter diff --git a/gravitee-node-tracing/pom.xml b/gravitee-node-tracing/pom.xml index 9c252021f..60ead72d1 100644 --- a/gravitee-node-tracing/pom.xml +++ b/gravitee-node-tracing/pom.xml @@ -24,7 +24,7 @@ io.gravitee.node gravitee-node - 1.20.5-api-dumps-SNAPSHOT + 1.20.5 gravitee-node-tracing diff --git a/gravitee-node-vertx/pom.xml b/gravitee-node-vertx/pom.xml index 4c27ad60f..b0c25ecb7 100644 --- a/gravitee-node-vertx/pom.xml +++ b/gravitee-node-vertx/pom.xml @@ -24,7 +24,7 @@ io.gravitee.node gravitee-node - 1.20.5-api-dumps-SNAPSHOT + 1.20.5 gravitee-node-vertx diff --git a/pom.xml b/pom.xml index 30ab7da6b..32c5f55e6 100644 --- a/pom.xml +++ b/pom.xml @@ -29,7 +29,7 @@ io.gravitee.node gravitee-node - 1.20.5-api-dumps-SNAPSHOT + 1.20.5 Gravitee.io - Node