1700

What is the easiest way to convert the result of Throwable.getStackTrace() to a string that depicts the stacktrace?

james.garriss
  • 12,959
  • 7
  • 83
  • 96
ripper234
  • 222,824
  • 274
  • 634
  • 905
  • 8
    Because jqno's answer actually uses the Throwable.getStackTrace() method that you specified in your question, whereas Brian doesn't. He uses Throwable.printStackTrace() instead. – Stijn de Witt Jan 31 '12 at 18:45
  • 10
    Just about every Java project should include Apache commons-lang. It includes many convenience methods implementing extremely common development needs. – Russell Silva Aug 15 '13 at 21:32
  • 20
    @StijndeWitt Those three lines of code almost certainly need factoring out of the place you've called them. Since you don't know where to put them, they'll go in your utility toolbox with all the other useful snippets. Bingo! you've just reinvented guava / commons-lang / whatever... only not so well. Import a sensible utilities library instead, and save reinventing the wheel. *The true sign of a novice is thinking you can do a better job than the library writers.* – Andrew Spencer Oct 22 '13 at 08:12
  • 2
    NB Single Level of Abstraction Principle is the reason this sort of thing should be factored out. Aids readability and testability, uncovers reusable elements. See: http://www.slideshare.net/guestebde/10-ways-to-improve-your-code-neal-ford – Andrew Spencer Oct 22 '13 at 08:16
  • 9
    1. Guava has - Throwables.getStackTraceAsString(e) 2. Apache Commons Lang - ExceptionUtils.getFullStackTrace 3. Write our own custom methods – user2323036 Mar 10 '14 at 10:52
  • 16
    @AndrewSpencer I don't understand why you guys try so hard to bash StijndeWitt for wanting to achieve this with some small snippet. There is really not much danger in writing a tiny utility method (I don't see it as "SHEER ARROGANCE oh nooooo!! he thinks he's better than Apache!!"). There are tons of projects especially in non-Java JVM languages that really don't want to include Guava or Commons Lang just to log a stacktrace. I write Scala & Clojure libraries and certainly will not be making Apache Commons Lang a transitive dependency just for one method. – jm0 Mar 02 '16 at 16:30
  • 1
    @jm0 Not bashing, just qualified disagreement. It was a bit too snide, I was spoiling for a fight after seeing some bad homebrewed utilities. I agree there are reasons not to add a dependency, I just more often see the opposite mistake. – Andrew Spencer Apr 07 '16 at 13:56
  • @Gewure We're so much more evolved and advanced now in our knowledge and understanding of the code. No need for such petty questions any longer. Let the commonfolk rot in their deficiencies. – Andrew Oct 13 '17 at 16:05
  • 1
    I came here because when writing Amazon Lambda functions in Java you may want to avoid importing libraries, since they make the container startup time suck. So sometimes it's still necessary to reinvent the wheel. Or maybe I could solve this with ProGuard... – Marcelo Glasberg Oct 16 '17 at 22:09
  • @Andrew i don't know wether i interpret your sarcasm? correctly, but i think you didn't interprete me correctly either: I wanted to say, that SO has a Problem. Indeed it has turned elitist. It completely fails to open a connection for newbies to enter. E.g. they didn't want a non-english SO. Which is undemocratic and elitist. There are ppl who can program/want to but don't speak proper english. – Gewure Dec 05 '17 at 15:06
  • 1
    @Gewure It would have helped if you didn't delete your comment... It was obviously sarcasm though. S.O. has always been full of elitists and close-minded snobs. It's not really S.O.'s fault, I don't think, but just a general flaw with mankind that gets put on display very prominently here. As for English speaking: there is some sensibility to both sides; I can only speak to the argument itself though. – Andrew Dec 06 '17 at 04:55
  • 3
    Use commons-lang. Josh Bloch says: *"Know and use the libraries.[...] don’t reinvent the wheel.If you need to do something that seems like it should be reasonably common,there may already be a class in the libraries that does what you want. If there is, use it; if you don’t know, check.[...]Library code is likely to be better than code that you’d write yourself and is likely to improve over time.This is no reflection on your abilities as a programmer.Economies of scale dictate that library code receives far more attention than most developers could afford to devote to the same functionality."* – jpangamarca Dec 26 '17 at 14:53

32 Answers32

2361

Use Throwable.printStackTrace(PrintWriter pw) to send the stack trace to an appropriate writer.

import java.io.StringWriter;
import java.io.PrintWriter;

// ...

StringWriter sw = new StringWriter();
PrintWriter pw = new PrintWriter(sw);
e.printStackTrace(pw);
String sStackTrace = sw.toString(); // stack trace as a string
System.out.println(sStackTrace);
Neuron
  • 5,141
  • 5
  • 38
  • 59
Brian Agnew
  • 268,207
  • 37
  • 334
  • 440
  • 9
    This does trim the stack trace, the same way printStackTrace(). All exceptions in the stack are visible but for each exception the stack may be trimmed. Anyone requiring the entire trace should consider this. – Laila Agaev Nov 29 '13 at 20:20
  • 9
    This, as it turns out, is pretty much exactly what apache's ExceptionUtils.getStackTrace() method does. Almost to the letter actually. – ticktock Mar 04 '14 at 22:30
  • 7
    @BrianAgnew, shouldn't you close the `StringWriter` and `PrintWriter` ? – Muhammad Gelbana Aug 02 '15 at 21:07
  • 6
    @MuhammadGelbana - yes. The above is shown for expediency. I suspect if you don't it wouldn't cause a problem, but I would advocate it on grounds of good practise – Brian Agnew Aug 03 '15 at 08:42
  • 17
    @BrianAgnew, thanks for the reply. It got me curious and here is what I found: http://stackoverflow.com/questions/14542535/will-not-closing-a-stringwriter-cause-a-leak – Muhammad Gelbana Aug 03 '15 at 09:31
  • This should be the chosen answer. no external libraries, simple, quick, and functions perfectly. – Hello234 Aug 01 '21 at 23:38
1137

One can use the following method to convert an Exception stack trace to String. This class is available in Apache commons-lang which is most common dependent library with many popular open sources

org.apache.commons.lang.exception.ExceptionUtils.getStackTrace(Throwable)

amar
  • 12,037
  • 1
  • 24
  • 19
  • 89
    @Stijn - to be fair (I wrote the current highest voted answer below) it's worth looking at commons-lang for a lot more functionality – Brian Agnew Jan 31 '12 at 10:10
  • 55
    @StijndeWitt Commons Lang is pretty common. It's already present in most of my projects/proyects at work. – WhyNotHugo May 21 '12 at 14:54
  • 17
    @Hugo thanks, was going to use the StringWriter to avoid adding a new library--turns out it's already a dependency of 3 of my dependencies. So to the rest, check if you have it already. – Nathanial Apr 10 '13 at 00:53
  • 35
    @MartinAsenov - following your logic you'd never use such a library, would you ? You wouldn't use it unless you're already using it ? – Brian Agnew Aug 28 '13 at 16:31
  • 18
    Fyi, the package has changed and the class is now at: `org.apache.commons.lang3.exception.ExceptionUtils`. – schmmd Nov 13 '13 at 23:18
  • This method was most useful for me to just log the entire error (message + cause + stacktrace) into my database for further debugging: `ExceptionUtils.getFullStackTrace(e)` – takanuva15 Aug 21 '23 at 18:06
503

This should work:

StringWriter sw = new StringWriter();
e.printStackTrace(new PrintWriter(sw));
String exceptionAsString = sw.toString();
D. Wroblewski
  • 7,530
  • 4
  • 27
  • 30
  • 83
    Concise in java context is always hillarious. That `printStackTrace` should just return string, leaving decision on whether print it or not to user :) – dmitry Jul 14 '15 at 11:14
  • 52
    printStackTrace printing in the console is acceptable, but at least a getStackTrace returning it as a String should be available by default – Mateus Viccari Oct 13 '16 at 18:32
  • 7
    What part of this is concise? You have to construct 2 objects which exist for no purpose other than to put the stack trace into a string. – ArtOfWarfare Nov 14 '16 at 23:02
  • 9
    @dmitry A method called `printXXX()` should print XXX. – user207421 Aug 11 '17 at 05:56
  • 2
    @Greg Actually that wasn't even sarcasm, it just required a plain reading of what he said. – Andrew Oct 13 '17 at 16:08
  • Printing an exception without a stack trace is (nearly) worthless most of the time. The default behavior of Throwable.toString() should be to return a human readable explanation followed by the stack trace. If I want only the message, I'll ask for it, likewise the stack. Again, Java developers are mostly schoolkids, who seem to not ever actually do any development except for Java itself. If they did, they would design their stuff to be much easier to use. – Smit-Tay Jul 14 '21 at 10:11
  • 2
    @Smit-Tay well, in a professional environment, I hardly ever had the need to directly print an exception anywhere - it is usually passed to a logging library, and the logging configuration takes care of all the details on what level of detail about the exception ends up in which logfile, and how it gets formatted there. – Hulk Jul 22 '21 at 07:25
  • Do i need to close Printwriter when using this code? – bittap Jul 20 '22 at 06:43
265

If you are developing for Android, a far easier way is to use this:

import android.util.Log;

String stackTrace = Log.getStackTraceString(exception); 

The format is the same as getStacktrace, for e.g.

09-24 16:09:07.042: I/System.out(4844): java.lang.NullPointerException
09-24 16:09:07.042: I/System.out(4844):   at com.temp.ttscancel.MainActivity.onCreate(MainActivity.java:43)
09-24 16:09:07.042: I/System.out(4844):   at android.app.Activity.performCreate(Activity.java:5248)
09-24 16:09:07.043: I/System.out(4844):   at android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1110)
09-24 16:09:07.043: I/System.out(4844):   at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2162)
09-24 16:09:07.043: I/System.out(4844):   at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2257)
09-24 16:09:07.043: I/System.out(4844):   at android.app.ActivityThread.access$800(ActivityThread.java:139)
09-24 16:09:07.043: I/System.out(4844):   at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1210)
09-24 16:09:07.043: I/System.out(4844):   at android.os.Handler.dispatchMessage(Handler.java:102)
09-24 16:09:07.043: I/System.out(4844):   at android.os.Looper.loop(Looper.java:136)
09-24 16:09:07.044: I/System.out(4844):   at android.app.ActivityThread.main(ActivityThread.java:5097)
09-24 16:09:07.044: I/System.out(4844):   at java.lang.reflect.Method.invokeNative(Native Method)
09-24 16:09:07.044: I/System.out(4844):   at java.lang.reflect.Method.invoke(Method.java:515)
09-24 16:09:07.044: I/System.out(4844):   at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:785)
09-24 16:09:07.044: I/System.out(4844):   at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:601)
Pokechu22
  • 4,984
  • 9
  • 37
  • 62
Vicky Kapadia
  • 6,025
  • 2
  • 24
  • 30
  • If any of the throwables in the cause chain is an `UnknownHostException`, this returns an empty string. See https://developer.android.com/reference/android/util/Log – Pavel Chuchuva May 17 '23 at 05:06
154

Guava's Throwables class

If you have the actual Throwable instance, Google Guava provides Throwables.getStackTraceAsString().

Example:

String s = Throwables.getStackTraceAsString ( myException ) ;
Neuron
  • 5,141
  • 5
  • 38
  • 59
stumbav
  • 1,649
  • 1
  • 10
  • 3
120

WARNING: Does not include cause (which is usually the useful bit!)

public String stackTraceToString(Throwable e) {
    StringBuilder sb = new StringBuilder();
    for (StackTraceElement element : e.getStackTrace()) {
        sb.append(element.toString());
        sb.append("\n");
    }
    return sb.toString();
}
samthebest
  • 30,803
  • 25
  • 102
  • 142
jqno
  • 15,133
  • 7
  • 57
  • 84
108

For me the cleanest and easiest way was:

import java.util.Arrays;
Arrays.toString(e.getStackTrace());
Vladas Diržys
  • 1,358
  • 1
  • 11
  • 9
  • 43
    The code is clean, but the output is not. You have to do a .replaceAll(", ", "\n") in the end. However you lose the indentation that printStackTrace proposes. – fury Jan 09 '12 at 04:03
  • 6
    This is useful when you log trace in a single line – webjockey Jan 24 '17 at 21:23
30
public static String getStackTrace(Throwable t) {
    StringWriter sw = new StringWriter();
    t.printStackTrace(new PrintWriter(sw));
    return sw.toString();
}
Akira Yamamoto
  • 4,685
  • 4
  • 42
  • 43
  • 1
    Hi, I would just like to point out that the comments section of the answer being cited points out that the `StringWriter` and `PrintWriter` objects ought to be `close`d....(or i guess only the `PrintWriter` needs to be closed since closing it should close the `StringWriter` too) – Eric Aug 16 '16 at 02:38
27

The following code allows you to get the entire stackTrace with a String format, without using APIs like log4J or even java.util.Logger:

catch (Exception e) {
    StackTraceElement[] stack = e.getStackTrace();
    String exception = "";
    for (StackTraceElement s : stack) {
        exception = exception + s.toString() + "\n\t\t";
    }
    System.out.println(exception);
    // then you can send the exception string to a external file.
}
Murilo Vasconcelos
  • 4,677
  • 24
  • 27
dumonderic
  • 1,219
  • 1
  • 10
  • 3
24

Here is a version that is copy-pastable directly into code:

import java.io.StringWriter; 
import java.io.PrintWriter;

//Two lines of code to get the exception into a StringWriter
StringWriter sw = new StringWriter();
new Throwable().printStackTrace(new PrintWriter(sw));

//And to actually print it
logger.info("Current stack trace is:\n" + sw.toString());

Or, in a catch block

} catch (Throwable t) {
    StringWriter sw = new StringWriter();
    t.printStackTrace(new PrintWriter(sw));
    logger.info("Current stack trace is:\n" + sw.toString());
}
rouble
  • 16,364
  • 16
  • 107
  • 102
23

Print the stack trace to a PrintStream, then convert it to a String:

// ...

catch (Exception e)
{
    ByteArrayOutputStream out = new ByteArrayOutputStream(); 
    e.printStackTrace(new PrintStream(out));
    String str = new String(out.toByteArray());

    System.out.println(str);
}
Ihor Patsian
  • 1,288
  • 2
  • 15
  • 25
Baked Inhalf
  • 3,375
  • 1
  • 31
  • 45
21
Arrays.toString(thrown.getStackTrace())

Is the easiest way to convert the result into String I am using this in my program to print the stack trace

LOGGER.log(Level.SEVERE, "Query Builder Issue Stack Trace : {0} ,Message : {1} objid {2}", new Object[]{Arrays.toString(e.getStackTrace()), e.getMessage(),objId});
Panu Haaramo
  • 2,932
  • 19
  • 41
19

Kotlin >= 1.4

Use the built-in function stackTraceToString() on a Throwable.

Kotlin < 1.4

Extending the Throwable class will give you the String property error.stackTraceString:

val Throwable.stackTraceString: String
  get() {
    val sw = StringWriter()
    val pw = PrintWriter(sw)
    this.printStackTrace(pw)
    return sw.toString()
  }
vonox7
  • 1,081
  • 13
  • 24
  • Starting Kotlin 1.4 there is now a built-in API to do this https://kotlinlang.org/api/latest/jvm/stdlib/kotlin/stack-trace-to-string.html – Aguragorn Mar 25 '21 at 03:26
14

if you are using Java 8, try this

Arrays.stream(e.getStackTrace())
                .map(s->s.toString())
                .collect(Collectors.joining("\n"));

you can find the code for getStackTrace() function provided by Throwable.java as :

public StackTraceElement[] getStackTrace() {
    return getOurStackTrace().clone();
}

and for StackTraceElement, it provides toString() as follows:

public String toString() {
    return getClassName() + "." + methodName +
        (isNativeMethod() ? "(Native Method)" :
         (fileName != null && lineNumber >= 0 ?
          "(" + fileName + ":" + lineNumber + ")" :
          (fileName != null ?  "("+fileName+")" : "(Unknown Source)")));
}

So just join the StackTraceElement with "\n".

Brooklyn99
  • 987
  • 13
  • 24
Pengcheng Zhang
  • 141
  • 1
  • 2
13

Printing stack trace to string

import java.io.PrintWriter;
import java.io.StringWriter;

public class StackTraceUtils {
    public static String stackTraceToString(StackTraceElement[] stackTrace) {
        StringWriter sw = new StringWriter();
        printStackTrace(stackTrace, new PrintWriter(sw));
        return sw.toString();
    }
    public static void printStackTrace(StackTraceElement[] stackTrace, PrintWriter pw) {
        for(StackTraceElement stackTraceEl : stackTrace) {
            pw.println(stackTraceEl);
        }
    }
}

It's useful when you want to print the current thread stack trace without creating instance of Throwable - but note that creating new Throwable and getting stack trace from there is actually faster and cheaper than calling Thread.getStackTrace.

Jarek Przygódzki
  • 4,284
  • 2
  • 31
  • 41
11
private String getCurrentStackTraceString() {
    StackTraceElement[] stackTrace = Thread.currentThread().getStackTrace();
    return Arrays.stream(stackTrace).map(StackTraceElement::toString)
            .collect(Collectors.joining("\n"));
}
eddyrokr
  • 414
  • 1
  • 8
  • 23
10

Code from Apache Commons Lang 3.4 (JavaDoc):

public static String getStackTrace(final Throwable throwable) {
    final StringWriter sw = new StringWriter();
    final PrintWriter pw = new PrintWriter(sw, true);
    throwable.printStackTrace(pw);
    return sw.getBuffer().toString();
}

The difference with the other answers is that it uses autoFlush on the PrintWriter.

IvanRF
  • 7,115
  • 5
  • 47
  • 71
9

The clever sniping in the first set of comments was very amusing, but it really depends on what you are trying to do. If you don't already have the correct library, then 3 lines of code (as in D. Wroblewski's answer) is perfect. OTOH, if you already have the apache.commons library (as most large projects will), then Amar's answer is shorter. OK, it might take you ten minutes to get the library and install it correctly (less than one if you know what you're doing). But the clock is ticking, so you may not have the time to spare. Jarek Przygódzki had an interesting caveat--"If you don't need nested exceptions".

But what if I do need the full stack traces, nested and all? In that case, the secret is to use apache.common's getFullStackTrace (see http://commons.apache.org/proper/commons-lang/javadocs/api-2.6/org/apache/commons/lang/exception/ExceptionUtils.html#getFullStackTrace%28java.lang.Throwable%29)

It saved my bacon. Thanks, Amar, for the hint!

Tihamer
  • 935
  • 10
  • 8
8

Without java.io.* it can be done like this.

String trace = e.toString() + "\n";                     

for (StackTraceElement e1 : e.getStackTrace()) {
    trace += "\t at " + e1.toString() + "\n";
}   

And then the trace variable holds your stack trace. Output also holds the initial cause, the output is identical to printStackTrace()

Example, printStackTrace() yields:

java.io.FileNotFoundException: / (Is a directory)
    at java.io.FileOutputStream.open0(Native Method)
    at java.io.FileOutputStream.open(FileOutputStream.java:270)
    at java.io.FileOutputStream.<init>(FileOutputStream.java:213)
    at java.io.FileOutputStream.<init>(FileOutputStream.java:101)
    at Test.main(Test.java:9)

The trace String holds, when printed to stdout

java.io.FileNotFoundException: / (Is a directory)
     at java.io.FileOutputStream.open0(Native Method)
     at java.io.FileOutputStream.open(FileOutputStream.java:270)
     at java.io.FileOutputStream.<init>(FileOutputStream.java:213)
     at java.io.FileOutputStream.<init>(FileOutputStream.java:101)
     at Test.main(Test.java:9)
Gala
  • 2,592
  • 3
  • 25
  • 33
8

With Java 8 Stream API you can do something like this:

Stream
    .of(throwable.getStackTrace())
    .map(StackTraceElement::toString)
    .collect(Collectors.joining("\n"));

It will take array of stack trace elements, convert them to string and join into multiline string.

ivanjermakov
  • 1,131
  • 13
  • 24
6

Scala version

def stackTraceToString(e: Exception): String = {
  import java.io.{PrintWriter, StringWriter}
  val sw = new StringWriter()
  e.printStackTrace(new PrintWriter(sw))
  sw.toString
}
Seth Tisue
  • 29,985
  • 11
  • 82
  • 149
samthebest
  • 30,803
  • 25
  • 102
  • 142
5

an exapansion on Gala's answer that will also include the causes for the exception:

private String extrapolateStackTrace(Exception ex) {
    Throwable e = ex;
    String trace = e.toString() + "\n";
    for (StackTraceElement e1 : e.getStackTrace()) {
        trace += "\t at " + e1.toString() + "\n";
    }
    while (e.getCause() != null) {
        e = e.getCause();
        trace += "Cause by: " + e.toString() + "\n";
        for (StackTraceElement e1 : e.getStackTrace()) {
            trace += "\t at " + e1.toString() + "\n";
        }
    }
    return trace;
}
ido flax
  • 528
  • 1
  • 10
  • 19
4

If you don't want to use an external library and you're not developing for Android, you could create an 'extension' method like this:

public static String getStackTraceString(Throwable e) {
    return getStackTraceString(e, "");
}

private static String getStackTraceString(Throwable e, String indent) {
    StringBuilder sb = new StringBuilder();
    sb.append(e.toString());
    sb.append("\n");

    StackTraceElement[] stack = e.getStackTrace();
    if (stack != null) {
        for (StackTraceElement stackTraceElement : stack) {
            sb.append(indent);
            sb.append("\tat ");
            sb.append(stackTraceElement.toString());
            sb.append("\n");
        }
    }

    Throwable[] suppressedExceptions = e.getSuppressed();
    // Print suppressed exceptions indented one level deeper.
    if (suppressedExceptions != null) {
        for (Throwable throwable : suppressedExceptions) {
            sb.append(indent);
            sb.append("\tSuppressed: ");
            sb.append(getStackTraceString(throwable, indent + "\t"));
        }
    }

    Throwable cause = e.getCause();
    if (cause != null) {
        sb.append(indent);
        sb.append("Caused by: ");
        sb.append(getStackTraceString(cause, indent));
    }

    return sb.toString();
}
Community
  • 1
  • 1
Kapé
  • 4,411
  • 3
  • 37
  • 54
4

The solution is to convert the stackTrace of array to string data type. See the following example:

import java.util.Arrays;

try{

}catch(Exception ex){
    String stack = Arrays.toString(ex.getStackTrace());
    System.out.println("stack "+ stack);
}
Jorge Santos
  • 397
  • 4
  • 3
3

My oneliner to convert stack trace to the enclosed multi-line string:

Stream.of(e.getStackTrace()).map((a) -> a.toString()).collect(Collectors.joining("\n", "[", "]"))

Easy to pass to the logger "as is".

Andrey Lebedenko
  • 1,850
  • 17
  • 24
  • You get something that differs from `printStackTrace()` Here you will loose: 1) Exception which was thrown; 2) Causes and their stacktrace – valijon Dec 11 '18 at 10:51
  • The difference is quite expected, since converting of the `printStackTrace()` never was a part of the question. – Andrey Lebedenko Dec 11 '18 at 14:29
3

Few options

  1. StringWriter sw = new StringWriter(); e.printStackTrace(new PrintWriter(sw)); String exceptionAsString = sw.toString();

  2. Using Google Guava lib String stackTrace = Throwables.getStackTraceAsString ( myException ) ;

  3. org.apache.commons.lang.exception.ExceptionUtils.getStackTrace(Throwable)

Eric
  • 1,171
  • 2
  • 14
  • 27
3
 import java.io.PrintWriter;
import java.io.StringWriter;

public class PrintStackTrace {

    public static void main(String[] args) {

        try {
            int division = 0 / 0;
        } catch (ArithmeticException e) {
            StringWriter sw = new StringWriter();
            e.printStackTrace(new PrintWriter(sw));
            String exceptionAsString = sw.toString();
            System.out.println(exceptionAsString);
        }
    }
}

When you run the program, the output will be something similar:

java.lang.ArithmeticException: / by zero
at PrintStackTrace.main(PrintStackTrace.java:9)
Prakhar Nigam
  • 678
  • 3
  • 15
3

I wonder why no one mentioned ExceptionUtils.getStackFrames(exception)

For me it's the most convenient way to dump stacktrace with all its causes to the end:

String.join("\n", ExceptionUtils.getStackFrames(exception));
Andrey Sarul
  • 1,382
  • 2
  • 16
  • 20
2

Warning: This may be a bit off topic, but oh well... ;)

I don't know what the original posters reason was for wanting the stack trace as string in the first place. When the stack trace should end up in an SLF4J/Logback LOG, but no exception was or should be thrown here's what I do:

public void remove(List<String> ids) {
    if(ids == null || ids.isEmpty()) {
        LOG.warn(
            "An empty list (or null) was passed to {}.remove(List). " +
            "Clearly, this call is unneccessary, the caller should " + 
            "avoid making it. A stacktrace follows.", 
            getClass().getName(),
            new Throwable ("Stacktrace")
        );

        return;
    }

    // actual work, remove stuff
}

I like it because it does not require an external library (other than your logging backend, which will be in place most of the time anyway, of course).

2

I wrote a few methods for this a while ago, so I figured why not throw my two cents at this.

/** @param stackTraceElements The elements to convert
 * @return The resulting string */
public static final String stackTraceElementsToStr(StackTraceElement[] stackTraceElements) {
    return stackTraceElementsToStr(stackTraceElements, "\n");
}

/** @param stackTraceElements The elements to convert
 * @param lineSeparator The line separator to use
 * @return The resulting string */
public static final String stackTraceElementsToStr(StackTraceElement[] stackTraceElements, String lineSeparator) {
    return stackTraceElementsToStr(stackTraceElements, lineSeparator, "");
}

/** @param stackTraceElements The elements to convert
 * @param lineSeparator The line separator to use
 * @param padding The string to be used at the start of each line
 * @return The resulting string */
public static final String stackTraceElementsToStr(StackTraceElement[] stackTraceElements, String lineSeparator, String padding) {
    String str = "";
    if(stackTraceElements != null) {
        for(StackTraceElement stackTrace : stackTraceElements) {
            str += padding + (!stackTrace.toString().startsWith("Caused By") ? "\tat " : "") + stackTrace.toString() + lineSeparator;
        }
    }
    return str;
}

/** @param stackTraceElements The elements to convert
 * @return The resulting string */
public static final String stackTraceCausedByElementsOnlyToStr(StackTraceElement[] stackTraceElements) {
    return stackTraceCausedByElementsOnlyToStr(stackTraceElements, "\n");
}

/** @param stackTraceElements The elements to convert
 * @param lineSeparator The line separator to use
 * @return The resulting string */
public static final String stackTraceCausedByElementsOnlyToStr(StackTraceElement[] stackTraceElements, String lineSeparator) {
    return stackTraceCausedByElementsOnlyToStr(stackTraceElements, lineSeparator, "");
}

/** @param stackTraceElements The elements to convert
 * @param lineSeparator The line separator to use
 * @param padding The string to be used at the start of each line
 * @return The resulting string */
public static final String stackTraceCausedByElementsOnlyToStr(StackTraceElement[] stackTraceElements, String lineSeparator, String padding) {
    String str = "";
    if(stackTraceElements != null) {
        for(StackTraceElement stackTrace : stackTraceElements) {
            str += (!stackTrace.toString().startsWith("Caused By") ? "" : padding + stackTrace.toString() + lineSeparator);
        }
    }
    return str;
}

/** @param e The {@link Throwable} to convert
 * @return The resulting String */
public static final String throwableToStrNoStackTraces(Throwable e) {
    return throwableToStrNoStackTraces(e, "\n");
}

/** @param e The {@link Throwable} to convert
 * @param lineSeparator The line separator to use
 * @return The resulting String */
public static final String throwableToStrNoStackTraces(Throwable e, String lineSeparator) {
    return throwableToStrNoStackTraces(e, lineSeparator, "");
}

/** @param e The {@link Throwable} to convert
 * @param lineSeparator The line separator to use
 * @param padding The string to be used at the start of each line
 * @return The resulting String */
public static final String throwableToStrNoStackTraces(Throwable e, String lineSeparator, String padding) {
    if(e == null) {
        return "null";
    }
    String str = e.getClass().getName() + ": ";
    if((e.getMessage() != null) && !e.getMessage().isEmpty()) {
        str += e.getMessage() + lineSeparator;
    } else {
        str += lineSeparator;
    }
    str += padding + stackTraceCausedByElementsOnlyToStr(e.getStackTrace(), lineSeparator, padding);
    for(Throwable suppressed : e.getSuppressed()) {
        str += padding + throwableToStrNoStackTraces(suppressed, lineSeparator, padding + "\t");
    }
    Throwable cause = e.getCause();
    while(cause != null) {
        str += padding + "Caused by:" + lineSeparator + throwableToStrNoStackTraces(e.getCause(), lineSeparator, padding);
        cause = cause.getCause();
    }
    return str;
}

/** @param e The {@link Throwable} to convert
 * @return The resulting String */
public static final String throwableToStr(Throwable e) {
    return throwableToStr(e, "\n");
}

/** @param e The {@link Throwable} to convert
 * @param lineSeparator The line separator to use
 * @return The resulting String */
public static final String throwableToStr(Throwable e, String lineSeparator) {
    return throwableToStr(e, lineSeparator, "");
}

/** @param e The {@link Throwable} to convert
 * @param lineSeparator The line separator to use
 * @param padding The string to be used at the start of each line
 * @return The resulting String */
public static final String throwableToStr(Throwable e, String lineSeparator, String padding) {
    if(e == null) {
        return "null";
    }
    String str = padding + e.getClass().getName() + ": ";
    if((e.getMessage() != null) && !e.getMessage().isEmpty()) {
        str += e.getMessage() + lineSeparator;
    } else {
        str += lineSeparator;
    }
    str += padding + stackTraceElementsToStr(e.getStackTrace(), lineSeparator, padding);
    for(Throwable suppressed : e.getSuppressed()) {
        str += padding + "Suppressed: " + throwableToStr(suppressed, lineSeparator, padding + "\t");
    }
    Throwable cause = e.getCause();
    while(cause != null) {
        str += padding + "Caused by:" + lineSeparator + throwableToStr(e.getCause(), lineSeparator, padding);
        cause = cause.getCause();
    }
    return str;
}

Example:

try(InputStream in = new FileInputStream(file)) {
    ...
} catch(IOException e) {
    String exceptionToString = throwableToStr(e);
    someLoggingUtility.println(exceptionToString);
    ...
}

Prints:

java.io.FileNotFoundException: C:\test.txt (The system cannot find the file specified)
    at java.io.FileInputStream.open0(Native Method)
    at java.io.FileInputStream.open(Unknown Source)
    at java.io.FileInputStream.<init>(Unknown Source)
    at com.gmail.br45entei.Example.main(Example.java:32)
Brian_Entei
  • 513
  • 1
  • 8
  • 18
1

Old question, but I would just like to add the special case where you don't want to print all the stack, by removing some parts you are not actually interested in, excluding certain classes or packages.

Instead of a PrintWriter use a SelectivePrintWriter:

// This filters out this package and up.
String packageNameToFilter = "org.springframework";

StringWriter sw = new StringWriter();
PrintWriter pw = new SelectivePrintWriter(sw, packageNameToFilter);
e.printStackTrace(pw);
String sStackTrace = sw.toString(); 
System.out.println(sStackTrace);

Where the SelectivePrintWriter class is given by:

public class SelectivePrintWriter extends PrintWriter {
    private boolean on = true;
    private static final String AT = "\tat";
    private String internal;

    public SelectivePrintWriter(Writer out, String packageOrClassName) {
        super(out);
        internal = "\tat " + packageOrClassName;
    }

    public void println(Object obj) {
        if (obj instanceof String) {
            String txt = (String) obj;
            if (!txt.startsWith(AT)) on = true;
            else if (txt.startsWith(internal)) on = false;
            if (on) super.println(txt);
        } else {
            super.println(obj);
        }
    }
}

Please note this class may be easily adapted to filter out by Regex, contains or other criteria. Also note it depends upon Throwable implementation details (not likely to change, but still).

gil.fernandes
  • 12,978
  • 5
  • 63
  • 76
Marcelo Glasberg
  • 29,013
  • 23
  • 109
  • 133
0

Shortcut Kotlin to write a stacktrace into an existing StringBuilder:

This version is very fast as it avoids creating a temporary string and copying it around

private fun StringBuilder.appendStackTrace(thr: Throwable) =
    thr.printStackTrace(object : PrintWriter(StringWriter()) {
        override fun println(x: Any?) {
            this@appendStackTrace.append(x)
            this@appendStackTrace.append('\n')
        }
    })

You can call it like:

val s = buildString {
    append("Some text before the stack trace ...\n")
    appendStackTrace(throwable)
    append("Some other stuff")
}
Oliver Hoffmann
  • 146
  • 2
  • 11