Author: rgoers
Date: Thu Sep 29 06:22:45 2011
New Revision: 1177174
URL: http://svn.apache.org/viewvc?rev=1177174&view=rev
Log:
Add extended thread dump information
Added:
logging/log4j/branches/BRANCH_2_0_EXPERIMENTAL/rgoers/log4j2-api/src/main/java/org/apache/logging/log4j/message/BasicThreadInformation.java
logging/log4j/branches/BRANCH_2_0_EXPERIMENTAL/rgoers/log4j2-api/src/main/java/org/apache/logging/log4j/message/ExtendedThreadInformation.java
logging/log4j/branches/BRANCH_2_0_EXPERIMENTAL/rgoers/log4j2-api/src/main/java/org/apache/logging/log4j/message/ThreadInformation.java
Modified:
logging/log4j/branches/BRANCH_2_0_EXPERIMENTAL/rgoers/log4j2-api/src/main/java/org/apache/logging/log4j/message/ThreadDumpMessage.java
logging/log4j/branches/BRANCH_2_0_EXPERIMENTAL/rgoers/log4j2-api/src/test/java/org/apache/logging/log4j/message/ThreadDumpMessageTest.java
Added:
logging/log4j/branches/BRANCH_2_0_EXPERIMENTAL/rgoers/log4j2-api/src/main/java/org/apache/logging/log4j/message/BasicThreadInformation.java
URL:
http://svn.apache.org/viewvc/logging/log4j/branches/BRANCH_2_0_EXPERIMENTAL/rgoers/log4j2-api/src/main/java/org/apache/logging/log4j/message/BasicThreadInformation.java?rev=1177174&view=auto
==============================================================================
---
logging/log4j/branches/BRANCH_2_0_EXPERIMENTAL/rgoers/log4j2-api/src/main/java/org/apache/logging/log4j/message/BasicThreadInformation.java
(added)
+++
logging/log4j/branches/BRANCH_2_0_EXPERIMENTAL/rgoers/log4j2-api/src/main/java/org/apache/logging/log4j/message/BasicThreadInformation.java
Thu Sep 29 06:22:45 2011
@@ -0,0 +1,91 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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 org.apache.logging.log4j.message;
+
+/**
+ *
+ */
+class BasicThreadInformation implements ThreadInformation {
+
+ private final long id;
+ private final String name;
+ private final String longName;
+ private final Thread.State state;
+ private final int priority;
+ private final boolean isAlive;
+ private final boolean isDaemon;
+ private final String threadGroupName;
+
+ public BasicThreadInformation(Thread thread) {
+ this.id = thread.getId();
+ this.name = thread.getName();
+ this.longName = thread.toString();
+ this.state = thread.getState();
+ this.priority = thread.getPriority();
+ this.isAlive = thread.isAlive();
+ this.isDaemon = thread.isDaemon();
+ ThreadGroup group = thread.getThreadGroup();
+ threadGroupName = group == null ? null : group.getName();
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) {
+ return true;
+ }
+ if (o == null || getClass() != o.getClass()) {
+ return false;
+ }
+
+ BasicThreadInformation that = (BasicThreadInformation) o;
+
+ if (id != that.id) {
+ return false;
+ }
+ if (name != null ? !name.equals(that.name) : that.name != null) {
+ return false;
+ }
+
+ return true;
+ }
+
+ @Override
+ public int hashCode() {
+ int result = (int) (id ^ (id >>> 32));
+ result = 31 * result + (name != null ? name.hashCode() : 0);
+ return result;
+ }
+
+ public void printThreadInfo(StringBuilder sb) {
+ sb.append("\"").append(name).append("\" ");
+ if (isDaemon) {
+ sb.append("daemon ");
+ }
+ sb.append("prio=").append(priority).append("
tid=").append(id).append(" ");
+ if (threadGroupName != null) {
+ sb.append("group=\"").append(threadGroupName).append("\"");
+ }
+ sb.append("\n");
+ sb.append("\tThread state: ").append(state.name()).append("\n");
+ }
+
+ public void printStack(StringBuilder sb, StackTraceElement[] trace) {
+ for (StackTraceElement element : trace) {
+ sb.append("\tat ").append(element).append("\n");
+ }
+ }
+}
Added:
logging/log4j/branches/BRANCH_2_0_EXPERIMENTAL/rgoers/log4j2-api/src/main/java/org/apache/logging/log4j/message/ExtendedThreadInformation.java
URL:
http://svn.apache.org/viewvc/logging/log4j/branches/BRANCH_2_0_EXPERIMENTAL/rgoers/log4j2-api/src/main/java/org/apache/logging/log4j/message/ExtendedThreadInformation.java?rev=1177174&view=auto
==============================================================================
---
logging/log4j/branches/BRANCH_2_0_EXPERIMENTAL/rgoers/log4j2-api/src/main/java/org/apache/logging/log4j/message/ExtendedThreadInformation.java
(added)
+++
logging/log4j/branches/BRANCH_2_0_EXPERIMENTAL/rgoers/log4j2-api/src/main/java/org/apache/logging/log4j/message/ExtendedThreadInformation.java
Thu Sep 29 06:22:45 2011
@@ -0,0 +1,163 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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 org.apache.logging.log4j.message;
+
+import java.lang.management.LockInfo;
+import java.lang.management.MonitorInfo;
+import java.lang.management.ThreadInfo;
+
+/**
+ * Provides information on locks and monitors in the thread dump. This class
requires Java 1.6 to compile and
+ * run.
+ */
+class ExtendedThreadInformation implements ThreadInformation {
+
+ private final ThreadInfo info;
+
+
+ public ExtendedThreadInformation(ThreadInfo thread) {
+ this.info = thread;
+ }
+
+ public void printThreadInfo(StringBuilder sb) {
+ sb.append("\"").append(info.getThreadName()).append("\"");
+ sb.append(" Id=").append(info.getThreadId()).append(" ");
+ formatState(sb, info);
+ if (info.isSuspended()) {
+ sb.append(" (suspended)");
+ }
+ if (info.isInNative()) {
+ sb.append(" (in native)");
+ }
+ sb.append('\n');
+ }
+
+ public void printStack(StringBuilder sb, StackTraceElement[] stack) {
+ int i = 0;
+ for (StackTraceElement element : stack) {
+ sb.append("\tat ").append(element.toString());
+ sb.append('\n');
+ if (i == 0 && info.getLockInfo() != null) {
+ Thread.State ts = info.getThreadState();
+ switch (ts) {
+ case BLOCKED:
+ sb.append("\t- blocked on ");
+ formatLock(sb, info.getLockInfo());
+ sb.append('\n');
+ break;
+ case WAITING:
+ sb.append("\t- waiting on ");
+ formatLock(sb, info.getLockInfo());
+ sb.append('\n');
+ break;
+ case TIMED_WAITING:
+ sb.append("\t- waiting on ");
+ formatLock(sb, info.getLockInfo());
+ sb.append('\n');
+ break;
+ default:
+ }
+ }
+
+ for (MonitorInfo mi : info.getLockedMonitors()) {
+ if (mi.getLockedStackDepth() == i) {
+ sb.append("\t- locked ");
+ formatLock(sb, mi);
+ sb.append('\n');
+ }
+ }
+ ++i;
+ }
+
+ LockInfo[] locks = info.getLockedSynchronizers();
+ if (locks.length > 0) {
+ sb.append("\n\tNumber of locked synchronizers =
").append(locks.length).append('\n');
+ for (LockInfo li : locks) {
+ sb.append("\t- ");
+ formatLock(sb, li);
+ sb.append('\n');
+ }
+ }
+ }
+
+ private void formatLock(StringBuilder sb, LockInfo lock) {
+ sb.append("<").append(lock.getIdentityHashCode()).append("> (a ");
+ sb.append(lock.getClassName()).append(")");
+ }
+
+ private void formatState(StringBuilder sb, ThreadInfo info) {
+ Thread.State state = info.getThreadState();
+ sb.append(state);
+ switch (state) {
+ case BLOCKED: {
+ sb.append(" (on object monitor owned by \"");
+ sb.append(info.getLockOwnerName()).append("\"
Id=").append(info.getLockOwnerId()).append(")");
+ break;
+ }
+ case WAITING: {
+ StackTraceElement element = info.getStackTrace()[0];
+ String className = element.getClassName();
+ String method = element.getMethodName();
+ if (className.equals("java.lang.Object") &&
method.equals("wait")) {
+ sb.append(" (on object monitor");
+ if (info.getLockOwnerName() != null) {
+ sb.append(" owned by \"");
+ sb.append(info.getLockOwnerName()).append("\"
Id=").append(info.getLockOwnerId());
+ }
+ sb.append(")");
+ } else if (className.equals("java.lang.Thread") &&
method.equals("join")) {
+ sb.append(" (on completion of thread
").append(info.getLockOwnerId()).append(")");
+ } else {
+ sb.append(" (parking for lock");
+ if (info.getLockOwnerName() != null) {
+ sb.append(" owned by \"");
+ sb.append(info.getLockOwnerName()).append("\"
Id=").append(info.getLockOwnerId());
+ }
+ sb.append(")");
+ }
+ break;
+ }
+ case TIMED_WAITING: {
+ StackTraceElement element = info.getStackTrace()[0];
+ String className = element.getClassName();
+ String method = element.getMethodName();
+ if (className.equals("java.lang.Object") &&
method.equals("wait")) {
+ sb.append(" (on object monitor");
+ if (info.getLockOwnerName() != null) {
+ sb.append(" owned by \"");
+ sb.append(info.getLockOwnerName()).append("\"
Id=").append(info.getLockOwnerId());
+ }
+ sb.append(")");
+ } else if (className.equals("java.lang.Thread") &&
method.equals("sleep")) {
+ sb.append(" (sleeping)");
+ } else if (className.equals("java.lang.Thread") &&
method.equals("join")) {
+ sb.append(" (on completion of thread
").append(info.getLockOwnerId()).append(")");
+ } else {
+ sb.append(" (parking for lock");
+ if (info.getLockOwnerName() != null) {
+ sb.append(" owned by \"");
+ sb.append(info.getLockOwnerName()).append("\"
Id=").append(info.getLockOwnerId());
+ }
+ sb.append(")");
+ }
+ break;
+ }
+ default:
+ break;
+ }
+ }
+}
Modified:
logging/log4j/branches/BRANCH_2_0_EXPERIMENTAL/rgoers/log4j2-api/src/main/java/org/apache/logging/log4j/message/ThreadDumpMessage.java
URL:
http://svn.apache.org/viewvc/logging/log4j/branches/BRANCH_2_0_EXPERIMENTAL/rgoers/log4j2-api/src/main/java/org/apache/logging/log4j/message/ThreadDumpMessage.java?rev=1177174&r1=1177173&r2=1177174&view=diff
==============================================================================
---
logging/log4j/branches/BRANCH_2_0_EXPERIMENTAL/rgoers/log4j2-api/src/main/java/org/apache/logging/log4j/message/ThreadDumpMessage.java
(original)
+++
logging/log4j/branches/BRANCH_2_0_EXPERIMENTAL/rgoers/log4j2-api/src/main/java/org/apache/logging/log4j/message/ThreadDumpMessage.java
Thu Sep 29 06:22:45 2011
@@ -16,7 +16,13 @@
*/
package org.apache.logging.log4j.message;
+import java.io.InvalidObjectException;
+import java.io.ObjectInputStream;
import java.io.Serializable;
+import java.lang.management.ManagementFactory;
+import java.lang.management.ThreadInfo;
+import java.lang.management.ThreadMXBean;
+import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.Map;
@@ -25,12 +31,28 @@ import java.util.Map;
*/
public class ThreadDumpMessage implements Message {
- private final String title;
+ private static final long serialVersionUID = -1103400781608841088L;
+
+ private volatile Map<ThreadInformation, StackTraceElement[]> threads;
- private final Map<ThreadInfo, StackTraceElement[]> threads;
+ private final String title;
private String formattedMessage = null;
+ private static ThreadInfoFactory factory;
+
+ static {
+ Method[] methods = ThreadInfo.class.getMethods();
+ boolean basic = true;
+ for (Method method : methods) {
+ if (method.getName().equals("getLockInfo")) {
+ basic = false;
+ break;
+ }
+ }
+ factory = basic ? new BasicThreadInfoFactory() : new
ExtendedThreadInfoFactory();
+ }
+
/**
* Generate a ThreadDumpMessage with no title.
*/
@@ -45,11 +67,12 @@ public class ThreadDumpMessage implement
*/
public ThreadDumpMessage(String title) {
this.title = title == null ? "" : title;
- Map<Thread, StackTraceElement[]> map = Thread.getAllStackTraces();
- threads = new HashMap<ThreadInfo, StackTraceElement[]>(map.size());
- for (Map.Entry<Thread, StackTraceElement[]> entry : map.entrySet()) {
- threads.put(new ThreadInfo(entry.getKey()), entry.getValue());
- }
+ threads = factory.createThreadInfo();
+ }
+
+ private ThreadDumpMessage(String formattedMsg, String title) {
+ this.formattedMessage = formattedMsg;
+ this.title = title;
}
/**
@@ -64,33 +87,15 @@ public class ThreadDumpMessage implement
if (title.length() > 0) {
sb.append("\n");
}
- for (Map.Entry<ThreadInfo, StackTraceElement[]> entry :
threads.entrySet()) {
- printThreadInfo(sb, entry.getKey());
- printStack(sb, entry.getValue());
+ for (Map.Entry<ThreadInformation, StackTraceElement[]> entry :
threads.entrySet()) {
+ ThreadInformation info = entry.getKey();
+ info.printThreadInfo(sb);
+ info.printStack(sb, entry.getValue());
sb.append("\n");
}
return sb.toString();
}
- private void printThreadInfo(StringBuilder sb, ThreadInfo info) {
- sb.append("\"").append(info.name).append("\" ");
- if (info.isDaemon) {
- sb.append("daemon ");
- }
- sb.append("prio=").append(info.priority).append("
tid=").append(info.id).append(" ");
- if (info.threadGroupName != null) {
- sb.append("group=\"").append(info.threadGroupName).append("\"");
- }
- sb.append("\n");
- sb.append("\tThread state: ").append(info.state.name()).append("\n");
- }
-
- private void printStack(StringBuilder sb, StackTraceElement[] trace) {
- for (StackTraceElement element : trace) {
- sb.append("\tat ").append(element).append("\n");
- }
- }
-
/**
* Returns the title.
* @return the title.
@@ -105,62 +110,72 @@ public class ThreadDumpMessage implement
* @return the "parameters" to this Message.
*/
public Object[] getParameters() {
- return new Object[] {threads};
+ return null;
+ }
+
+ /**
+ * Creates a ThreadDumpMessageProxy that can be serialized.
+ * @return a ThreadDumpMessageProxy.
+ */
+ protected Object writeReplace() {
+ return new ThreadDumpMessageProxy(this);
+ }
+
+ private void readObject(ObjectInputStream stream)
+ throws InvalidObjectException {
+ throw new InvalidObjectException("Proxy required");
}
/**
- * Information describing each thread.
+ * Proxy pattern used to serialize the ThreadDumpMessage.
*/
- public class ThreadInfo implements Serializable {
+ private static class ThreadDumpMessageProxy implements Serializable {
- private static final long serialVersionUID = 6899550135289181860L;
- private final long id;
- private final String name;
- private final String longName;
- private final Thread.State state;
- private final int priority;
- private final boolean isAlive;
- private final boolean isDaemon;
- private final String threadGroupName;
-
- public ThreadInfo(Thread thread) {
- this.id = thread.getId();
- this.name = thread.getName();
- this.longName = thread.toString();
- this.state = thread.getState();
- this.priority = thread.getPriority();
- this.isAlive = thread.isAlive();
- this.isDaemon = thread.isDaemon();
- ThreadGroup group = thread.getThreadGroup();
- threadGroupName = group == null ? null : group.getName();
- }
-
- @Override
- public boolean equals(Object o) {
- if (this == o) {
- return true;
- }
- if (o == null || getClass() != o.getClass()) {
- return false;
- }
+ private static final long serialVersionUID = -3476620450287648269L;
+ private String formattedMsg;
+ private String title;
- ThreadInfo that = (ThreadInfo) o;
+ public ThreadDumpMessageProxy(ThreadDumpMessage msg) {
+ this.formattedMsg = msg.getFormattedMessage();
+ this.title = msg.title;
+ }
- if (id != that.id) {
- return false;
- }
- if (name != null ? !name.equals(that.name) : that.name != null) {
- return false;
- }
+ /**
+ * Return a ThreadDumpMessage using the data in the proxy.
+ * @return a ThreadDumpMessage.
+ */
+ protected Object readResolve() {
+ return new ThreadDumpMessage(formattedMsg, title);
+ }
+ }
+
+ private interface ThreadInfoFactory {
+ Map<ThreadInformation, StackTraceElement[]> createThreadInfo();
+ }
- return true;
+ private static class BasicThreadInfoFactory implements ThreadInfoFactory {
+ public Map<ThreadInformation, StackTraceElement[]> createThreadInfo() {
+ Map<Thread, StackTraceElement[]> map = Thread.getAllStackTraces();
+ Map<ThreadInformation, StackTraceElement[]> threads =
+ new HashMap<ThreadInformation,
StackTraceElement[]>(map.size());
+ for (Map.Entry<Thread, StackTraceElement[]> entry :
map.entrySet()) {
+ threads.put(new BasicThreadInformation(entry.getKey()),
entry.getValue());
+ }
+ return threads;
}
+ }
- @Override
- public int hashCode() {
- int result = (int) (id ^ (id >>> 32));
- result = 31 * result + (name != null ? name.hashCode() : 0);
- return result;
+ private static class ExtendedThreadInfoFactory implements
ThreadInfoFactory {
+ public Map<ThreadInformation, StackTraceElement[]> createThreadInfo() {
+ ThreadMXBean bean = ManagementFactory.getThreadMXBean();
+ ThreadInfo[] array = bean.dumpAllThreads(true, true);
+
+ Map<ThreadInformation, StackTraceElement[]> threads =
+ new HashMap<ThreadInformation,
StackTraceElement[]>(array.length);
+ for (ThreadInfo info : array) {
+ threads.put(new ExtendedThreadInformation(info),
info.getStackTrace());
+ }
+ return threads;
}
}
}
Added:
logging/log4j/branches/BRANCH_2_0_EXPERIMENTAL/rgoers/log4j2-api/src/main/java/org/apache/logging/log4j/message/ThreadInformation.java
URL:
http://svn.apache.org/viewvc/logging/log4j/branches/BRANCH_2_0_EXPERIMENTAL/rgoers/log4j2-api/src/main/java/org/apache/logging/log4j/message/ThreadInformation.java?rev=1177174&view=auto
==============================================================================
---
logging/log4j/branches/BRANCH_2_0_EXPERIMENTAL/rgoers/log4j2-api/src/main/java/org/apache/logging/log4j/message/ThreadInformation.java
(added)
+++
logging/log4j/branches/BRANCH_2_0_EXPERIMENTAL/rgoers/log4j2-api/src/main/java/org/apache/logging/log4j/message/ThreadInformation.java
Thu Sep 29 06:22:45 2011
@@ -0,0 +1,13 @@
+package org.apache.logging.log4j.message;
+
+import java.io.Serializable;
+
+/**
+ *
+ */
+interface ThreadInformation {
+ void printThreadInfo(StringBuilder sb);
+
+ void printStack(StringBuilder sb, StackTraceElement[] trace);
+
+}
Modified:
logging/log4j/branches/BRANCH_2_0_EXPERIMENTAL/rgoers/log4j2-api/src/test/java/org/apache/logging/log4j/message/ThreadDumpMessageTest.java
URL:
http://svn.apache.org/viewvc/logging/log4j/branches/BRANCH_2_0_EXPERIMENTAL/rgoers/log4j2-api/src/test/java/org/apache/logging/log4j/message/ThreadDumpMessageTest.java?rev=1177174&r1=1177173&r2=1177174&view=diff
==============================================================================
---
logging/log4j/branches/BRANCH_2_0_EXPERIMENTAL/rgoers/log4j2-api/src/test/java/org/apache/logging/log4j/message/ThreadDumpMessageTest.java
(original)
+++
logging/log4j/branches/BRANCH_2_0_EXPERIMENTAL/rgoers/log4j2-api/src/test/java/org/apache/logging/log4j/message/ThreadDumpMessageTest.java
Thu Sep 29 06:22:45 2011
@@ -40,24 +40,58 @@ public class ThreadDumpMessageTest {
@Test
- public void testMessageWithLocks() {
+ public void testMessageWithLocks() throws Exception {
ReentrantLock lock = new ReentrantLock();
lock.lock();
+ Thread thread1 = new Thread1(lock);
+ thread1.start();
ThreadDumpMessage msg;
synchronized(this) {
+ Thread thread2 = new Thread2(this);
+ thread2.start();
try {
- msg = new ThreadDumpMessage("Testing"/* , true */);
+ Thread.sleep(200);
+ msg = new ThreadDumpMessage("Testing");
} finally {
lock.unlock();
}
}
String message = msg.getFormattedMessage();
- //System.out.print(message);
+ System.out.print(message);
assertTrue("No header", message.contains("Testing"));
assertTrue("No RUNNABLE", message.contains("RUNNABLE"));
assertTrue("No ThreadDumpMessage",
message.contains("ThreadDumpMessage"));
//assertTrue("No Locks", message.contains("waiting on"));
//assertTrue("No syncronizers", message.contains("locked
syncrhonizers"));
}
+
+ private class Thread1 extends Thread {
+ private ReentrantLock lock;
+
+ public Thread1(ReentrantLock lock) {
+ this.lock = lock;
+ }
+
+ @Override
+ public void run() {
+ lock.lock();
+ lock.unlock();
+ }
+ }
+
+ private class Thread2 extends Thread {
+ private Object obj;
+
+ public Thread2(Object obj) {
+ this.obj = obj;
+ }
+
+ @Override
+ public void run() {
+ synchronized (obj) {
+
+ }
+ }
+ }
}