Skip to content

Commit 3851985

Browse files
Merge pull request #98 from AxonIQ/feature/thread-dumps-of-applications
Thread dumps of applications connected to the AxonIQ Console
2 parents c9265f7 + 0d7661c commit 3851985

File tree

11 files changed

+87
-5
lines changed

11 files changed

+87
-5
lines changed

console-framework-client-api/pom.xml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@
2121
<parent>
2222
<groupId>io.axoniq.console</groupId>
2323
<artifactId>console-framework-client-parent</artifactId>
24-
<version>1.9.0-SNAPSHOT</version>
24+
<version>1.9.1-SNAPSHOT</version>
2525
</parent>
2626
<modelVersion>4.0.0</modelVersion>
2727

console-framework-client-api/src/main/java/io/axoniq/console/framework/api/Routes.kt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,8 @@ object Routes {
3030
const val STOP_REPORTS = "client-reporting-stop"
3131
// Request to start sending reports again.
3232
const val START_REPORTS = "client-reporting-start"
33+
// Request to send a thread dump
34+
const val THREAD_DUMP = "client-thread-dump"
3335
}
3436

3537
object EventProcessor {

console-framework-client-api/src/main/java/io/axoniq/console/framework/api/clientIdentification.kt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -101,6 +101,8 @@ data class SupportedFeatures(
101101
val logDirect: Boolean? = false,
102102
/* Whether the client supports pause/resume of reports.*/
103103
val pauseReports: Boolean? = false,
104+
/* Whether the client supports thread dumps.*/
105+
val threadDump: Boolean? = false
104106
)
105107

106108
data class Versions(
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
package io.axoniq.console.framework.api
2+
3+
data class ThreadDumpEntry(
4+
val instance: String,
5+
val thread: String,
6+
val trace: String,
7+
)
8+
9+
data class ThreadDumpResult(
10+
val timestamp: Long,
11+
val threadDumpEntries: List<ThreadDumpEntry>
12+
)
13+
14+
data class ThreadDumpQuery(
15+
val instance: String
16+
)

console-framework-client-spring-boot-starter/pom.xml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@
2121
<parent>
2222
<groupId>io.axoniq.console</groupId>
2323
<artifactId>console-framework-client-parent</artifactId>
24-
<version>1.9.0-SNAPSHOT</version>
24+
<version>1.9.1-SNAPSHOT</version>
2525
</parent>
2626
<modelVersion>4.0.0</modelVersion>
2727

console-framework-client/pom.xml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@
2121
<parent>
2222
<groupId>io.axoniq.console</groupId>
2323
<artifactId>console-framework-client-parent</artifactId>
24-
<version>1.9.0-SNAPSHOT</version>
24+
<version>1.9.1-SNAPSHOT</version>
2525
</parent>
2626
<modelVersion>4.0.0</modelVersion>
2727

console-framework-client/src/main/java/io/axoniq/console/framework/AxoniqConsoleConfigurerModule.java

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,8 @@
1919
import io.axoniq.console.framework.application.ApplicationMetricRegistry;
2020
import io.axoniq.console.framework.application.ApplicationMetricReporter;
2121
import io.axoniq.console.framework.application.ApplicationReportCreator;
22+
import io.axoniq.console.framework.application.ApplicationThreadDumpProvider;
23+
import io.axoniq.console.framework.application.RSocketThreadDumpResponder;
2224
import io.axoniq.console.framework.client.AxoniqConsoleRSocketClient;
2325
import io.axoniq.console.framework.client.ClientSettingsService;
2426
import io.axoniq.console.framework.client.RSocketHandlerRegistrar;
@@ -218,11 +220,19 @@ public void configureModule(@NotNull Configurer configurer) {
218220
dlqDiagnosticsWhitelist,
219221
managementTaskExecutor
220222
))
223+
.registerComponent(ApplicationThreadDumpProvider.class,
224+
c -> new ApplicationThreadDumpProvider()
225+
)
221226
.registerComponent(RSocketDlqResponder.class,
222227
c -> new RSocketDlqResponder(
223228
c.getComponent(DeadLetterManager.class),
224229
c.getComponent(RSocketHandlerRegistrar.class)
225230
))
231+
.registerComponent(RSocketThreadDumpResponder.class,
232+
c -> new RSocketThreadDumpResponder(
233+
c.getComponent(ApplicationThreadDumpProvider.class),
234+
c.getComponent(RSocketHandlerRegistrar.class)
235+
))
226236
.eventProcessing()
227237
.registerDefaultHandlerInterceptor((
228238
c, name) -> new AxoniqConsoleProcessorInterceptor(
@@ -248,6 +258,7 @@ public void configureModule(@NotNull Configurer configurer) {
248258
c.getComponent(RSocketProcessorResponder.class);
249259
c.getComponent(RSocketDlqResponder.class);
250260
c.getComponent(HandlerMetricsRegistry.class);
261+
c.getComponent(RSocketThreadDumpResponder.class);
251262
});
252263

253264
configurer.onStart(() -> {
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
package io.axoniq.console.framework.application
2+
import io.axoniq.console.framework.api.ThreadDumpEntry
3+
import io.axoniq.console.framework.api.ThreadDumpResult
4+
import java.lang.management.ManagementFactory
5+
6+
class ApplicationThreadDumpProvider() {
7+
private val threadBean = ManagementFactory.getThreadMXBean()
8+
9+
fun collectThreadDumps(instance: String): ThreadDumpResult {
10+
val threadDumpEntries = threadBean.dumpAllThreads(true, true).map { threadInfo ->
11+
ThreadDumpEntry(
12+
instance = instance,
13+
thread = threadInfo.threadName,
14+
trace = threadInfo.toString()
15+
)
16+
}
17+
return ThreadDumpResult(timestamp = System.currentTimeMillis(), threadDumpEntries = threadDumpEntries)
18+
}
19+
}
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
package io.axoniq.console.framework.application
2+
3+
import io.axoniq.console.framework.api.*
4+
import io.axoniq.console.framework.client.RSocketHandlerRegistrar
5+
import org.axonframework.lifecycle.Lifecycle
6+
import org.axonframework.lifecycle.Phase
7+
import org.slf4j.LoggerFactory
8+
9+
open class RSocketThreadDumpResponder (
10+
private val applicationThreadDumpProvider: ApplicationThreadDumpProvider,
11+
private val registrar: RSocketHandlerRegistrar
12+
) : Lifecycle {
13+
private val logger = LoggerFactory.getLogger(this::class.java)
14+
15+
override fun registerLifecycleHandlers(registry: Lifecycle.LifecycleRegistry) {
16+
registry.onStart(Phase.EXTERNAL_CONNECTIONS, this::start)
17+
}
18+
19+
fun start() {
20+
registrar.registerHandlerWithPayload(
21+
Routes.Management.THREAD_DUMP,
22+
ThreadDumpQuery::class.java,
23+
this::handleThreadDumpQuery
24+
)
25+
}
26+
27+
private fun handleThreadDumpQuery(query: ThreadDumpQuery): ThreadDumpResult {
28+
logger.debug("Handling AxonIQ Console THREAD_DUMP query for request [{}]", query)
29+
return applicationThreadDumpProvider.collectThreadDumps(query.instance)
30+
}
31+
}

console-framework-client/src/main/java/io/axoniq/console/framework/client/SetupPayloadCreator.kt

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,8 @@ class SetupPayloadCreator(
5252
versions = versionInformation(),
5353
upcasters = upcasters(),
5454
features = SupportedFeatures(
55-
heartbeat = true
55+
heartbeat = true,
56+
threadDump = true,
5657
)
5758
)
5859
}

0 commit comments

Comments
 (0)