Question

I have a Factory method that returns an object from a ID call.

Mock code:

public static Object getById(String id) {
    Object o = CRUD.doRecovery(Class, id);
    if(o == null) {
         printLogMessage("recovery by ID returned Null: " + id);
         // would really like to show only a few lines of stack trace.
    }
    return o;
}

How can I show only the first N lines of the stack trace (so I know the caller of the method) without dumping the whole stack trace on the log or having to rely on external libs?

Was it helpful?

Solution 2

I'm assuming from what you are asking, that you don't have an exception to deal with. In which case you can get the current stack trace from:

StackTraceElement[] elements = Thread.currentThread().getStackTrace()

This will tell you pretty much everything you need to know about where you've come from in the code.

OTHER TIPS

You can use the ex.getStackTrace() to get the stack elements, the StackTraceElement contains one line of the full stacks, then print print what ever you want.

StackTraceElement[] elements = ex.getStackTrace();
print(elements[0]);

If you just want to truncate the stack trace, you can print the entire stack trace to a StringWriter then remove what you don't want:

public static void main(String[] args) throws ParseException {
    try {
        throw new Exception("Argh!");
    } catch (Exception e) {
        System.err.println(shortenedStackTrace(e, 1));
    }
}

public static String shortenedStackTrace(Exception e, int maxLines) {
    StringWriter writer = new StringWriter();
    e.printStackTrace(new PrintWriter(writer));
    String[] lines = writer.toString().split("\n");
    StringBuilder sb = new StringBuilder();
    for (int i = 0; i < Math.min(lines.length, maxLines); i++) {
        sb.append(lines[i]).append("\n");
    }
    return sb.toString();
}

Alternatively, use e.getStackTrace() to obtain a StackTraceElement[] array. This gives you the caller stack (from inner to outer), but not the error message. You'll have to use e.getMessage() to get the error message.

Some logging frameworks can be configured to truncate stack traces automatically. E.g. see this question and answer about log4j configuration.

If you just want to see the stack trace at any point in the code, you can get the elements from the Thread.currentThread() object:

Thread.currentThread().getStackTrace();

This method displays i lines of the stack trace, skipping the first two.

public static String traceCaller(Exception ex, int i) {
    StringWriter sw = new StringWriter();
    PrintWriter pw = new PrintWriter(sw);
    StringBuilder sb = new StringBuilder();
    ex.printStackTrace(pw);
    String ss = sw.toString();
    String[] splitted = ss.split("\n");
    sb.append("\n");
    if(splitted.length > 2 + i) {
        for(int x = 2; x < i+2; x++) {
            sb.append(splitted[x].trim());
            sb.append("\n");
        }
        return sb.toString();
    }
    return "Trace too Short.";
}

The first two lines are the exception name and the method that called traceCaller(). Tweak it if you want to show these lines.

Thanks go to @BrianAgnew (stackoverflow.com/a/1149712/1532705) for the StringWriter PrintWriter idea

Guava could help. For example we want to see only first ten rows:

log.error("Error:", Joiner.on("\n").join(Iterables.limit(asList(ex.getStackTrace()), 10)));

For an abbreviated version of e.printStackTrace():

        Exception e = ...
        System.out.println(e.toString());
        StackTraceElement[] elements = e.getStackTrace();
        for(int i = 0; i<elements.length && i < STACK_TRACE_LIMIT; i++) {
            System.out.println("\tat "+elements[i]);
        }

Replace STACK_TRACE_LIMIT with the limit you want or remove && i < STACK_TRACE_LIMIT to reproduce the output of a simple stack trace (e.g., no nested exceptions)

The innermost method calls are at index 0, main is at index length-1.

exemple to display the fifth first lines:

        final int nbLinesToShow = 5;
        try {
            /* your code here */
        } catch (final NullPointerException e) {
            // catch error
            final StackTraceElement[] elements = e.getStackTrace();
            System.err.println(
                    "===================================== \n" + "[ERROR] lorem ipsum");
            for (int i = 0; i < nbLinesToShow; i++) {
                System.err.println(elements[i]);
            }
        }

Snippet in below link works for N lines.

Below snippet helps to strip off exception stacktrace.

public class LoggerHelper {

private static final String SEPARATOR = "\r\n";
private static final String CAUSE_CAPTION = "Caused by: ";
private static final String SUPPRESSED_CAPTION = "Suppressed: ";

/**
 * The first 10 lines of exception stack information are returned by default
 *
 * @param e
 * @return
 */
public static String printTop10StackTrace(Throwable e) {
    if (e == null) {
        return "";
    }
    return printStackTrace(e, 20);
}

public static String printStackTrace(Throwable e, int maxLineCount) {
    if (e == null || maxLineCount <= 0) {
        return "";
    }
    StringBuilder sb = new StringBuilder(maxLineCount * 10);
    sb.append(e.toString()).append(SEPARATOR);
    StackTraceElement[] trace = e.getStackTrace();
    if (trace == null) {
        return e.toString();
    }
    int count = maxLineCount > trace.length ? trace.length : maxLineCount;
    int framesInCommon = trace.length - count;
    for (int i = 0; i < count; i++) {
        sb.append("\tat ").append(trace[i]).append(SEPARATOR);
    }
    if (framesInCommon != 0) {
        sb.append("\t... ").append(framesInCommon).append(" more").append(SEPARATOR);
    }
    // Print suppressed exceptions, if any
    Throwable[] suppressedExceptions = e.getSuppressed();
    if (ArrayUtils.isNotEmpty(suppressedExceptions)) {
        for (Throwable suppressedException : suppressedExceptions) {
            sb.append(printEnclosedStackTrace(suppressedException, maxLineCount, trace, SUPPRESSED_CAPTION, "\t"));
        }
    }
    // Print cause, if any
    Throwable cause = e.getCause();
    if (cause != null) {
        sb.append(printEnclosedStackTrace(cause, maxLineCount, trace, CAUSE_CAPTION, ""));
    }
    return sb.toString();
}

private static String printEnclosedStackTrace(Throwable e, int maxLineCount, StackTraceElement[] enclosingTrace,
                                              String caption, String prefix) {
    StringBuilder sb = new StringBuilder(maxLineCount * 5);
    StackTraceElement[] trace = e.getStackTrace();
    int m = trace.length - 1;
    int n = enclosingTrace.length - 1;
    while (m >= 0 && n >= 0 && trace[m].equals(enclosingTrace[n])) {
        m--;
        n--;
    }
    int count = maxLineCount > (m + 1) ? (m + 1) : maxLineCount;
    int framesInCommon = trace.length - count;
    // Print our stack trace
    sb.append(prefix).append(caption).append(e.toString()).append(SEPARATOR);
    for (int i = 0; i < count; i++) {
        sb.append(prefix).append("\tat ").append(trace[i]).append(SEPARATOR);
    }
    if (framesInCommon != 0) {
        sb.append(prefix).append("\t... ").append(framesInCommon).append(" more").append(SEPARATOR);
    }
    // Print suppressed exceptions, if any
    Throwable[] suppressedExceptions = e.getSuppressed();
    if (ArrayUtils.isNotEmpty(suppressedExceptions)) {
        for (Throwable suppressedException : suppressedExceptions) {
            sb.append(printEnclosedStackTrace(suppressedException, maxLineCount, trace, SUPPRESSED_CAPTION, prefix + "\t"));
        }
    }
    // Print cause, if any
    Throwable cause = e.getCause();
    if (cause != null) {
        sb.append(printEnclosedStackTrace(cause, maxLineCount, trace, CAUSE_CAPTION, prefix));
    }
    return sb.toString();
 }

}

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top