001/* 002 * Licensed to the Apache Software Foundation (ASF) under one 003 * or more contributor license agreements. See the NOTICE file 004 * distributed with this work for additional information 005 * regarding copyright ownership. The ASF licenses this file 006 * to you under the Apache License, Version 2.0 (the 007 * "License"); you may not use this file except in compliance 008 * with the License. You may obtain a copy of the License at 009 * 010 * http://www.apache.org/licenses/LICENSE-2.0 011 * 012 * Unless required by applicable law or agreed to in writing, 013 * software distributed under the License is distributed on an 014 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 015 * KIND, either express or implied. See the License for the 016 * specific language governing permissions and limitations 017 * under the License. 018 */ 019package org.apache.reef.util; 020 021import java.lang.management.LockInfo; 022import java.lang.management.MonitorInfo; 023import java.lang.management.ThreadInfo; 024import java.util.Map; 025import java.util.logging.Level; 026import java.util.logging.Logger; 027 028/** 029 * Methods to log the currently active set of threads with their stack traces. This is useful to log abnormal 030 * process exit situations, for instance the Driver timeout in the tests. 031 */ 032public final class ThreadLogger { 033 034 /** 035 * This is a utility class that shall not be instantiated. 036 */ 037 private ThreadLogger() { 038 } 039 040 /** 041 * Same as <code>logThreads(logger, level, prefix, "\n\t", "\n\t\t")</code>. 042 */ 043 public static void logThreads(final Logger logger, final Level level, final String prefix) { 044 logThreads(logger, level, prefix, "\n\t", "\n\t\t"); 045 } 046 047 /** 048 * Logs the currently active threads and their stack trace to the given Logger and Level. 049 * 050 * @param logger the Logger instance to log to. 051 * @param level the Level to log into. 052 * @param prefix a prefix of the log message. 053 * @param threadPrefix logged before each thread, e.g. "\n\t" to create an indented list. 054 * @param stackElementPrefix logged before each stack trace element, e.g. "\n\t\t" to create an indented list. 055 */ 056 public static void logThreads( 057 final Logger logger, final Level level, final String prefix, 058 final String threadPrefix, final String stackElementPrefix) { 059 logger.log(level, getFormattedThreadList(prefix, threadPrefix, stackElementPrefix)); 060 } 061 062 /** 063 * Produces a String representation of the currently running threads. 064 * 065 * @param prefix The prefix of the string returned. 066 * @param threadPrefix Printed before each thread, e.g. "\n\t" to create an indented list. 067 * @param stackElementPrefix Printed before each stack trace element, e.g. "\n\t\t" to create an indented list. 068 * @return a String representation of the currently running threads. 069 */ 070 public static String getFormattedThreadList( 071 final String prefix, final String threadPrefix, final String stackElementPrefix) { 072 final StringBuilder message = new StringBuilder(prefix); 073 for (final Map.Entry<Thread, StackTraceElement[]> entry : Thread.getAllStackTraces().entrySet()) { 074 message.append(threadPrefix).append("Thread '").append(entry.getKey().getName()).append("':"); 075 for (final StackTraceElement element : entry.getValue()) { 076 message.append(stackElementPrefix).append(element.toString()); 077 } 078 } 079 return message.toString(); 080 } 081 082 /** 083 * Same as <code>getFormattedThreadList(prefix, "\n\t", "\n\t\t")</code>. 084 */ 085 public static String getFormattedThreadList(final String prefix) { 086 return getFormattedThreadList(prefix, "\n\t", "\n\t\t"); 087 } 088 089 /** 090 * Produces a String representation of threads that are deadlocked, including lock information. 091 * @param prefix The prefix of the string returned. 092 * @param threadPrefix Printed before each thread, e.g. "\n\t" to create an indented list. 093 * @param stackElementPrefix Printed before each stack trace element, e.g. "\n\t\t" to create an indented list. 094 * @return a String representation of threads that are deadlocked, including lock information 095 */ 096 public static String getFormattedDeadlockInfo( 097 final String prefix, final String threadPrefix, final String stackElementPrefix) { 098 final StringBuilder message = new StringBuilder(prefix); 099 100 final DeadlockInfo deadlockInfo = new DeadlockInfo(); 101 102 final ThreadInfo[] deadlockedThreads = deadlockInfo.getDeadlockedThreads(); 103 104 if (0 == deadlockedThreads.length) { 105 message.append(" none "); 106 return message.toString(); 107 } 108 109 for (final ThreadInfo threadInfo : deadlockedThreads) { 110 message.append(threadPrefix).append("Thread '").append(threadInfo.getThreadName()) 111 .append("' with state ").append(threadInfo.getThreadState()); 112 113 boolean firstElement = true; 114 for (final StackTraceElement stackTraceElement : threadInfo.getStackTrace()) { 115 message.append(stackElementPrefix).append("at ").append(stackTraceElement); 116 if (firstElement) { 117 final String waitingLockString = deadlockInfo.getWaitingLockString(threadInfo); 118 if (waitingLockString != null) { 119 message.append(stackElementPrefix).append("- waiting to lock: ").append(waitingLockString); 120 } 121 firstElement = false; 122 } 123 for (final MonitorInfo info : deadlockInfo.getMonitorLockedElements(threadInfo, stackTraceElement)) { 124 message.append(stackElementPrefix).append("- locked: ").append(info); 125 } 126 } 127 for (final LockInfo lockInfo : threadInfo.getLockedSynchronizers()) { 128 message.append(stackElementPrefix).append("* holds locked synchronizer: ").append(lockInfo); 129 } 130 } 131 132 return message.toString(); 133 } 134 135 /** 136 * Same as <code>getFormattedDeadlockInfo(prefix, "\n\t", "\n\t\t")</code>. 137 */ 138 public static String getFormattedDeadlockInfo(final String prefix) { 139 return getFormattedDeadlockInfo(prefix, "\n\t", "\n\t\t"); 140 } 141 142 /** 143 * An example how to use the above methods. 144 * 145 * @param args ignored. 146 */ 147 public static void main(final String[] args) { 148 logThreads(Logger.getAnonymousLogger(), Level.INFO, "Threads active:"); 149 } 150}