All Downloads are FREE. Search and download functionalities are using the official Maven repository.

io.micronaut.management.health.indicator.threads.DeadlockedThreadsHealthIndicator Maven / Gradle / Ivy

The newest version!
/*
 * Copyright 2017-2024 original authors
 *
 * 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
 *
 * https://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.micronaut.management.health.indicator.threads;

import io.micronaut.context.annotation.Requires;
import io.micronaut.context.condition.NotInNativeImage;
import io.micronaut.core.annotation.Internal;
import io.micronaut.core.util.StringUtils;
import io.micronaut.health.HealthStatus;
import io.micronaut.management.endpoint.health.HealthEndpoint;
import io.micronaut.management.health.indicator.AbstractHealthIndicator;
import io.micronaut.management.health.indicator.HealthIndicator;
import io.micronaut.management.health.indicator.annotation.Liveness;
import jakarta.inject.Singleton;

import java.lang.management.ManagementFactory;
import java.lang.management.MonitorInfo;
import java.lang.management.ThreadInfo;
import java.lang.management.ThreadMXBean;
import java.util.Arrays;
import java.util.LinkedHashMap;
import java.util.Map;

/**
 * 

A {@link HealthIndicator} that uses the {@link ThreadMXBean} to check for deadlocked threads. * Returns {@link HealthStatus#DOWN} if any are found and their {@link ThreadInfo} in the details.

* * @author Andreas Brenk * @since 4.8.0 */ @Singleton @Liveness @Requires(condition = NotInNativeImage.class) @Requires(property = HealthEndpoint.PREFIX + ".deadlocked-threads.enabled", notEquals = StringUtils.FALSE) @Requires(beans = HealthEndpoint.class) @Internal class DeadlockedThreadsHealthIndicator extends AbstractHealthIndicator { private static final String NAME = "deadlockedThreads"; private static final String KEY_THREAD_ID = "threadId"; private static final String KEY_THREAD_NAME = "threadName"; private static final String KEY_THREAD_STATE = "threadState"; private static final String KEY_DAEMON = "daemon"; private static final String KEY_PRIORITY = "priority"; private static final String KEY_SUSPENDED = "suspended"; private static final String KEY_IN_NATIVE = "inNative"; private static final String KEY_LOCK_NAME = "lockName"; private static final String KEY_LOCK_OWNER_NAME = "lockOwnerName"; private static final String KEY_LOCK_OWNER_ID = "lockOwnerId"; private static final String KEY_LOCKED_SYNCHRONIZERS = "lockedSynchronizers"; private static final String KEY_STACK_TRACE = "stackTrace"; @Override protected Object getHealthInformation() { ThreadMXBean threadMXBean = ManagementFactory.getThreadMXBean(); long[] deadlockedThreads = null; if (threadMXBean.isSynchronizerUsageSupported()) { deadlockedThreads = threadMXBean.findDeadlockedThreads(); } else if (threadMXBean.isObjectMonitorUsageSupported()) { deadlockedThreads = threadMXBean.findMonitorDeadlockedThreads(); } if (deadlockedThreads == null) { this.healthStatus = HealthStatus.UP; return null; } this.healthStatus = HealthStatus.DOWN; return Arrays.stream(threadMXBean.getThreadInfo(deadlockedThreads, true, true, Integer.MAX_VALUE)) .map(DeadlockedThreadsHealthIndicator::getDetails) .toList(); } @Override public String getName() { return NAME; } private static Map getDetails(ThreadInfo threadInfo) { Map details = new LinkedHashMap<>(); details.put(KEY_THREAD_ID, String.valueOf(threadInfo.getThreadId())); details.put(KEY_THREAD_NAME, threadInfo.getThreadName()); details.put(KEY_THREAD_STATE, threadInfo.getThreadState().name()); details.put(KEY_DAEMON, String.valueOf(threadInfo.isDaemon())); details.put(KEY_PRIORITY, String.valueOf(threadInfo.getPriority())); details.put(KEY_SUSPENDED, String.valueOf(threadInfo.isSuspended())); details.put(KEY_IN_NATIVE, String.valueOf(threadInfo.isInNative())); details.put(KEY_LOCK_NAME, threadInfo.getLockName()); details.put(KEY_LOCK_OWNER_NAME, threadInfo.getLockOwnerName()); details.put(KEY_LOCK_OWNER_ID, String.valueOf(threadInfo.getLockOwnerId())); details.put(KEY_LOCKED_SYNCHRONIZERS, Arrays.stream(threadInfo.getLockedSynchronizers()).map(String::valueOf).toList()); details.put(KEY_STACK_TRACE, formatStackTrace(threadInfo)); return details; } private static String formatStackTrace(ThreadInfo threadInfo) { StringBuilder sb = new StringBuilder(); int i = 0; StackTraceElement[] stackTrace = threadInfo.getStackTrace(); for (; i < stackTrace.length; i++) { StackTraceElement ste = stackTrace[i]; sb.append(ste.toString()); sb.append('\n'); if (i == 0 && threadInfo.getLockInfo() != null) { switch (threadInfo.getThreadState()) { case BLOCKED: sb.append("- blocked on "); sb.append(threadInfo.getLockInfo()); sb.append('\n'); break; case WAITING, TIMED_WAITING: sb.append("- waiting on "); sb.append(threadInfo.getLockInfo()); sb.append('\n'); break; default: } } for (MonitorInfo mi : threadInfo.getLockedMonitors()) { if (mi.getLockedStackDepth() == i) { sb.append("- locked "); sb.append(mi); sb.append('\n'); } } } return sb.toString(); } }




© 2015 - 2025 Weber Informatics LLC | Privacy Policy