Какой самый простой способ преобразовать результат Throwable.getStackTrace()
в строку, изображающую трассировку стека?
Какой самый простой способ преобразовать результат Throwable.getStackTrace()
в строку, изображающую трассировку стека?
Ответы:
Можно использовать следующий метод для преобразования Exception
трассировки стека в String
. Этот класс доступен в Apache commons-lang, наиболее распространенной зависимой библиотеке со многими популярными открытыми источниками.
org.apache.commons.lang.exception.ExceptionUtils.getStackTrace(Throwable)
org.apache.commons.lang3.exception.ExceptionUtils
.
Используйте Throwable.printStackTrace(PrintWriter pw)
для отправки трассировки стека соответствующему устройству записи.
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);
StringWriter
и PrintWriter
?
Это должно работать:
StringWriter sw = new StringWriter();
e.printStackTrace(new PrintWriter(sw));
String exceptionAsString = sw.toString();
printStackTrace
должно просто вернуть строку, оставляя решение о том, печатать это пользователю или нет :)
printXXX()
должен вывести XXX.
Если вы разрабатываете для Android, гораздо проще использовать это:
import android.util.Log;
String stackTrace = Log.getStackTraceString(exception);
Формат такой же, как у getStacktrace, например,
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)
Log.e("TAG", "My message", new Throwable());
Throwables
Класс гуавыЕсли у вас есть фактический Throwable
экземпляр, Google Guava предоставляет Throwables.getStackTraceAsString()
.
Пример:
String s = Throwables.getStackTraceAsString ( myException ) ;
ВНИМАНИЕ: Не включает причину (которая обычно является полезным битом!)
public String stackTraceToString(Throwable e) {
StringBuilder sb = new StringBuilder();
for (StackTraceElement element : e.getStackTrace()) {
sb.append(element.toString());
sb.append("\n");
}
return sb.toString();
}
String.format("%n")
или что-то подобное вместо того, "\n"
чтобы осчастливить DOS-ламеров.
Для меня самым чистым и простым способом было:
import java.util.Arrays;
Arrays.toString(e.getStackTrace());
public static String getStackTrace(Throwable t) {
StringWriter sw = new StringWriter();
t.printStackTrace(new PrintWriter(sw));
return sw.toString();
}
StringWriter
и PrintWriter
объекты должны быть close
d .... (или я предполагаю , что только PrintWriter
должен быть закрыт , так как закрытие она должна закрыть StringWriter
тоже)
Следующий код позволяет вам получить весь stackTrace в String
формате без использования таких API, как log4J или даже 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.
}
Вот версия, которую можно копировать прямо в код:
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());
Или в блоке улова
} catch (Throwable t) {
StringWriter sw = new StringWriter();
t.printStackTrace(new PrintWriter(sw));
logger.info("Current stack trace is:\n" + sw.toString());
}
Throwable
определяется, верно?
Arrays.toString(thrown.getStackTrace())
Это самый простой способ преобразовать результат в строку, которую я использую в своей программе для печати трассировки стека
LOGGER.log(Level.SEVERE, "Query Builder Issue Stack Trace : {0} ,Message : {1} objid {2}", new Object[]{Arrays.toString(e.getStackTrace()), e.getMessage(),objId});
Выведите трассировку стека в a PrintStream
, затем преобразуйте ее в String
:
// ...
catch (Exception e)
{
ByteArrayOutputStream out = new ByteArrayOutputStream();
e.printStackTrace(new PrintStream(out));
String str = new String(out.toByteArray());
System.out.println(str);
}
Печать трассировки стека в строку
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);
}
}
}
Это полезно, когда вы хотите напечатать текущую трассировку стека потоков без создания экземпляра Throwable
- но учтите, что создание новой Throwable
и получение оттуда трассировки стека на самом деле быстрее и дешевле, чем вызов Thread.getStackTrace
.
private String getCurrentStackTraceString() {
StackTraceElement[] stackTrace = Thread.currentThread().getStackTrace();
return Arrays.stream(stackTrace).map(StackTraceElement::toString)
.collect(Collectors.joining("\n"));
}
Умный обстрел в первом наборе комментариев был очень забавным, но это действительно зависит от того, что вы пытаетесь сделать. Если у вас еще нет правильной библиотеки, то 3 строки кода (как в ответе Д. Вроблевского) идеальны. OTOH, если у вас уже есть библиотека apache.commons (как и большинство крупных проектов), то ответ Амара будет короче. ОК, вам может потребоваться десять минут, чтобы получить библиотеку и установить ее правильно (меньше, чем один, если вы знаете, что делаете). Но часы тикают, поэтому у вас может не быть свободного времени. У Ярека Пшигодзки была интересная оговорка - «Если вам не нужны вложенные исключения».
Но что , если я действительно нужен полные трассировки стеки, вложенные и все? В этом случае, секрет заключается в getFullStackTrace использования apache.common (см http://commons.apache.org/proper/commons-lang/javadocs/api-2.6/org/apache/commons/lang/exception/ExceptionUtils.html # getFullStackTrace% 28java.lang.Throwable% 29 )
Это спасло мой бекон. Спасибо, Амар, за подсказку!
Код из 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();
}
Разница с другими ответами в том, что он использует autoFlush
на PrintWriter
.
Без java.io.*
этого можно обойтись так.
String trace = e.toString() + "\n";
for (StackTraceElement e1 : e.getStackTrace()) {
trace += "\t at " + e1.toString() + "\n";
}
И тогда trace
переменная содержит вашу трассировку стека. Выход также содержит первоначальную причину, выход идентиченprintStackTrace()
Пример, printStackTrace()
дает:
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)
trace
Строка выполняется, когда печатается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)
Scala версия
def stackTraceToString(e: Exception): String = {
import java.io.PrintWriter
val sw = new StringWriter()
e.printStackTrace(new PrintWriter(sw))
sw.toString
}
если вы используете Java 8, попробуйте это
Arrays.stream(e.getStackTrace())
.map(s->s.toString())
.collect(Collectors.joining("\n"));
Вы можете найти код для getStackTrace()
функции, предоставленной Throwable.java
как:
public StackTraceElement[] getStackTrace() {
return getOurStackTrace().clone();
}
и для StackTraceElement
, это провайдеры toString()
как:
public String toString() {
return getClassName() + "." + methodName +
(isNativeMethod() ? "(Native Method)" :
(fileName != null && lineNumber >= 0 ?
"(" + fileName + ":" + lineNumber + ")" :
(fileName != null ? "("+fileName+")" : "(Unknown Source)")));
}
Так что просто присоединяйтесь к StackTraceElement
"\ n".
описание ответа Гала, в котором также будут указаны причины исключения:
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;
}
Решением является преобразование stackTrace массива в строковый тип данных. Смотрите следующий пример:
import java.util.Arrays;
try{
}catch(Exception ex){
String stack = Arrays.toString(ex.getStackTrace());
System.out.println("stack "+ stack);
}
С Java 8 Stream API вы можете сделать что-то вроде этого:
Stream
.of(throwable.getStackTrace())
.map(StackTraceElement::toString)
.collect(Collectors.joining("\n"));
Он возьмет массив элементов трассировки стека, преобразует их в строку и объединяет в многострочную строку.
Мой oneliner для преобразования трассировки стека во вложенную многострочную строку:
Stream.of(e.getStackTrace()).map((a) -> a.toString()).collect(Collectors.joining("\n", "[", "]"))
Легко перейти на регистратор "как есть".
printStackTrace()
Здесь вы потеряете: 1) Исключение, которое было брошено; 2) Причины и их следы стека
printStackTrace()
никогда не было частью вопроса.
Если вы не хотите использовать внешнюю библиотеку и не разрабатываете для Android , вы можете создать метод «расширения», например:
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();
}
Старый вопрос, но я просто хотел бы добавить специальный случай, когда вы не хотите печатать всю стопку , удалив некоторые части, которые вам на самом деле не интересны, за исключением определенных классов или пакетов.
Вместо PrintWriter
использования 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);
Где SelectivePrintWriter
класс дается:
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);
}
}
}
Обратите внимание, что этот класс может быть легко адаптирован для фильтрации по Regex contains
или другим критериям. Также обратите внимание, что это зависит от Throwable
деталей реализации (вряд ли изменится, но все же).
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);
}
}
}
Когда вы запустите программу, результат будет примерно таким:
java.lang.ArithmeticException: / by zero
at PrintStackTrace.main(PrintStackTrace.java:9)
Интересно, почему никто не упомянул ExceptionUtils.getStackFrames(exception)
Для меня это самый удобный способ вывести стек трассировки со всеми причинами до конца:
String.join("\n", ExceptionUtils.getStackFrames(exception));
Предупреждение: это может быть немного не по теме, ну да ладно ...;)
Я не знаю, какова была причина первоначальных постеров для того, чтобы в первую очередь желать трассировки стека как строки. Когда трассировка стека должна заканчиваться в SLF4J / Logback LOG, но не было и не должно быть выброшено вот что я делаю:
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
}
Мне это нравится, потому что для него не требуется внешняя библиотека (кроме вашего бэкэнда для ведения журналов, который, конечно же, будет в большинстве случаев установлен).
Несколько вариантов
StringWriter sw = new StringWriter();
e.printStackTrace(new PrintWriter(sw));
String exceptionAsString = sw.toString();
Использование Google Guava lib
String stackTrace = Throwables.getStackTraceAsString ( myException ) ;
org.apache.commons.lang.exception.ExceptionUtils.getStackTrace (Throwable)