diff --git a/hadoop-common-project/hadoop-common/src/main/java/org/apache/hadoop/util/ReflectionUtils.java b/hadoop-common-project/hadoop-common/src/main/java/org/apache/hadoop/util/ReflectionUtils.java index da14979a821..f1294e7610a 100644 --- a/hadoop-common-project/hadoop-common/src/main/java/org/apache/hadoop/util/ReflectionUtils.java +++ b/hadoop-common-project/hadoop-common/src/main/java/org/apache/hadoop/util/ReflectionUtils.java @@ -46,6 +46,7 @@ import org.apache.hadoop.io.Writable; import org.apache.hadoop.io.serializer.Deserializer; import org.apache.hadoop.io.serializer.SerializationFactory; import org.apache.hadoop.io.serializer.Serializer; +import org.slf4j.Logger; /** * General reflection utils @@ -228,6 +229,35 @@ public class ReflectionUtils { } } + /** + * Log the current thread stacks at INFO level. + * @param log the logger that logs the stack trace + * @param title a descriptive title for the call stacks + * @param minInterval the minimum time from the last + */ + public static void logThreadInfo(Logger log, + String title, + long minInterval) { + boolean dumpStack = false; + if (log.isInfoEnabled()) { + synchronized (ReflectionUtils.class) { + long now = Time.now(); + if (now - previousLogTime >= minInterval * 1000) { + previousLogTime = now; + dumpStack = true; + } + } + if (dumpStack) { + try { + ByteArrayOutputStream buffer = new ByteArrayOutputStream(); + printThreadInfo(new PrintStream(buffer, false, "UTF-8"), title); + log.info(buffer.toString(Charset.defaultCharset().name())); + } catch (UnsupportedEncodingException ignored) { + } + } + } + } + /** * Return the correctly-typed {@link Class} of the given object. * diff --git a/hadoop-common-project/hadoop-common/src/test/java/org/apache/hadoop/util/TestReflectionUtils.java b/hadoop-common-project/hadoop-common/src/test/java/org/apache/hadoop/util/TestReflectionUtils.java index 56e86ef9ff1..62cd62506f1 100644 --- a/hadoop-common-project/hadoop-common/src/test/java/org/apache/hadoop/util/TestReflectionUtils.java +++ b/hadoop-common-project/hadoop-common/src/test/java/org/apache/hadoop/util/TestReflectionUtils.java @@ -25,9 +25,14 @@ import java.net.URLClassLoader; import java.util.HashMap; import java.util.List; +import static org.hamcrest.CoreMatchers.containsString; import static org.junit.Assert.*; + +import org.apache.hadoop.test.GenericTestUtils.LogCapturer; import org.junit.Before; import org.junit.Test; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; public class TestReflectionUtils { @@ -150,7 +155,19 @@ public class TestReflectionUtils { assertTrue("Missing parent method", containsParentMethod); assertTrue("Missing child method", containsChildMethod); } - + + @Test + public void testLogThreadInfo() throws Exception { + Logger logger = LoggerFactory.getLogger(TestReflectionUtils.class); + LogCapturer logCapturer = LogCapturer.captureLogs(logger); + + final String title = "title"; + ReflectionUtils.logThreadInfo(logger, title, 0L); + + assertThat(logCapturer.getOutput(), + containsString("Process Thread Dump: " + title)); + } + // Used for testGetDeclaredFieldsIncludingInherited private class Parent { private int parentField;