Added printRootCauseStackTrace

git-svn-id: https://svn.apache.org/repos/asf/jakarta/commons/proper/lang/trunk@137191 13f79535-47bb-0310-9956-ffa450edef68
This commit is contained in:
Dmitri Plotnikov 2002-12-16 23:05:29 +00:00
parent 3d82f12dbd
commit f1cc6ec3ff
2 changed files with 157 additions and 4 deletions

View File

@ -54,11 +54,12 @@
* <http://www.apache.org/>.
*/
import java.io.PrintStream;
import java.io.PrintWriter;
import java.io.StringWriter;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.io.PrintWriter;
import java.io.StringWriter;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.LinkedList;
@ -371,7 +372,99 @@ public static int indexOfThrowable(Throwable t, Class type, int fromIndex)
}
return -1;
}
/**
* Prints a compact stack trace for the root cause of a throwable.
* The compact stack trace starts with the root cause and prints
* stack frames up to the place where it was caught and wrapped.
* Then it prints the wrapped exception and continues with stack frames
* until the wrapper exception is caught and wrapped again, etc.
* <p>
* The method is equivalent to t.printStackTrace() for throwables
* that don't have nested causes.
*/
public static void printRootCauseStackTrace(Throwable t, PrintStream stream)
{
String trace[] = getRootCauseStackTrace(t);
for (int i = 0; i < trace.length; i++){
stream.println(trace[i]);
}
stream.flush();
}
/**
* Equivalent to printRootCauseStackTrace(t, System.err)
*/
public static void printRootCauseStackTrace(Throwable t)
{
printRootCauseStackTrace(t, System.err);
}
/**
* Same as printRootCauseStackTrace(t, stream), except it takes
* a PrintWriter as an argument.
*/
public static void printRootCauseStackTrace(Throwable t, PrintWriter writer)
{
String trace[] = getRootCauseStackTrace(t);
for (int i = 0; i < trace.length; i++){
writer.println(trace[i]);
}
writer.flush();
}
/**
* Creates a compact stack trace for the root cause of the supplied
* throwable.
*
* See <code>printRootCauseStackTrace(Throwable t, PrintStream s)</code> */
public static String[] getRootCauseStackTrace(Throwable t)
{
Throwable throwables[] = getThrowables(t);
int count = throwables.length;
ArrayList frames = new ArrayList();
List nextTrace = getStackFrameList(throwables[count-1]);
for (int i = count; --i >= 0;){
List trace = nextTrace;
if (i != 0){
nextTrace = getStackFrameList(throwables[i-1]);
removeCommonFrames(trace, nextTrace);
}
if (i == count - 1){
frames.add(throwables[i].toString());
}
else {
frames.add(" [wrapped] " + throwables[i].toString());
}
for (int j = 0; j < trace.size(); j++){
frames.add(trace.get(j));
}
}
return (String[]) frames.toArray(new String[0]);
}
/**
* Given two stack traces, removes common frames from the cause trace.
* * @param causeFrames stack trace of a cause throwable * @param wrapperFrames stack trace of a wrapper throwable */
private static void removeCommonFrames(List causeFrames, List wrapperFrames)
{
int causeFrameIndex = causeFrames.size() - 1;
int wrapperFrameIndex = wrapperFrames.size() - 1;
while (causeFrameIndex >= 0 && wrapperFrameIndex >= 0)
{
// Remove the frame from the cause trace if it is the same
// as in the wrapper trace
String causeFrame = (String)causeFrames.get(causeFrameIndex);
String wrapperFrame = (String)wrapperFrames.get(wrapperFrameIndex);
if (causeFrame.equals(wrapperFrame)){
causeFrames.remove(causeFrameIndex);
}
causeFrameIndex--;
wrapperFrameIndex--;
}
}
/**
* A convenient way of extracting the stack trace from an
* exception.
@ -418,4 +511,31 @@ static String[] getStackFrames(String stackTrace)
}
return (String []) list.toArray(new String[] {});
}
/**
* Produces a List of stack frames - the message is not included.
* This works in most cases - it will only fail if the exception message
* contains a line that starts with: " at".
* * @param t is any throwable * @return List of stack frames */
static List getStackFrameList(Throwable t){
String stackTrace = getStackTrace(t);
String linebreak = SystemUtils.LINE_SEPARATOR;
StringTokenizer frames = new StringTokenizer(stackTrace, linebreak);
List list = new LinkedList();
boolean traceStarted = false;
while (frames.hasMoreTokens())
{
String token = frames.nextToken();
// Determine if the line starts with <whitespace>at
int at = token.indexOf("at");
if (at != -1 && token.substring(0, at).trim().length() == 0){
traceStarted = true;
list.add(token);
}
else if (traceStarted){
break;
}
}
return list;
}
}

View File

@ -54,8 +54,9 @@
* <http://www.apache.org/>.
*/
import java.io.PrintWriter;
import junit.framework.Test;
import junit.framework.TestCase;
import junit.framework.TestSuite;
/**
@ -83,11 +84,34 @@ public static Test suite()
public void setUp()
{
withoutCause = new ExceptionWithoutCause();
withoutCause = createExceptionWithoutCause();
nested = new NestableException(withoutCause);
withCause = new ExceptionWithCause(nested);
}
private Throwable createExceptionWithoutCause(){
try {
throw new ExceptionWithoutCause();
}
catch (Throwable t){
return t;
}
}
private Throwable createExceptionWithCause(){
try {
try {
throw new ExceptionWithCause(createExceptionWithoutCause());
}
catch (Throwable t){
throw new ExceptionWithCause(t);
}
}
catch (Throwable t){
return t;
}
}
public void testGetCause()
{
assertNull(ExceptionUtils.getCause(withoutCause));
@ -107,6 +131,15 @@ public void testGetThrowableCount()
assertEquals(ExceptionUtils.getThrowableCount(null), 0);
}
public void testPrintThrowables()
{
Throwable withCause = createExceptionWithCause();
ExceptionUtils.printRootCauseStackTrace(withCause,
new PrintWriter(System.out));
ExceptionUtils.printRootCauseStackTrace(withoutCause,
System.out);
}
/**
* Provides a method with a well known chained/nested exception
* name which matches the full signature (e.g. has a return value