Как я могу преобразовать трассировку стека в строку?

1503

Какой самый простой способ преобразовать результат Throwable.getStackTrace()в строку, изображающую трассировку стека?

ripper234
источник
6
Потому что ответ jqno на самом деле использует метод Throwable.getStackTrace (), который вы указали в своем вопросе, а Брайан - нет. Вместо этого он использует Throwable.printStackTrace ().
Стейн де Витт
10
Почти каждый Java-проект должен включать Apache commons-lang. Он включает в себя множество удобных методов, реализующих чрезвычайно распространенные потребности разработки.
Рассел Сильва
20
@StijndeWitt Эти три строки кода почти наверняка нуждаются в факторизации вне места, где вы их назвали. Так как вы не знаете, куда их поместить, они войдут в ваш набор утилит со всеми другими полезными фрагментами. Бинго! Вы только что заново изобрели guava / commons-lang / что угодно ... только не очень хорошо. Вместо этого импортируйте разумную библиотеку утилит и сохраните изобретение заново. Истинный признак новичка - думать, что вы можете сделать лучше, чем писатели библиотеки.
Эндрю Спенсер
6
1. Guava имеет - Throwables.getStackTraceAsString (e) 2. Apache Commons Lang - ExceptionUtils.getFullStackTrace 3. Напишите наши собственные пользовательские методы
user2323036
8
@AndrewSpencer Я не понимаю, почему вы, ребята, так стараетесь избить StijndeWitt за то, что хотите достичь этого с помощью небольшого фрагмента. На самом деле нет особой опасности в написании крошечного служебного метода (я не вижу его как «ОЧЕНЬ НЕВЕРНО ОЧЕНЬ ОЧЕНЬ !! он думает, что он лучше, чем Apache !!»). Существует множество проектов, особенно на языках JVM, отличных от Java, которые действительно не хотят включать Guava или Commons Lang просто для регистрации трассировки стека. Я пишу библиотеки Scala & Clojure и, конечно, не буду делать Apache Commons Lang транзитивной зависимостью только для одного метода.
jm0

Ответы:

1039

Можно использовать следующий метод для преобразования Exceptionтрассировки стека в String. Этот класс доступен в Apache commons-lang, наиболее распространенной зависимой библиотеке со многими популярными открытыми источниками.

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

амар
источник
88
@Stijn - если честно (ниже я написал самый высокий голос за ответ), стоит взглянуть на commons-lang для большей функциональности
Брайан Агнью
54
@StijndeWitt Commons Lang довольно распространен. Это уже присутствует в большинстве моих проектов / проектов на работе.
WhyNotHugo
16
@ Спасибо, я собирался использовать StringWriter, чтобы избежать добавления новой библиотеки - оказывается, это уже зависимость от 3 моих зависимостей. В остальном, проверьте, есть ли у вас уже.
Натаниал
33
@MartinAsenov - следуя своей логике, вы никогда не будете использовать такую ​​библиотеку, не так ли? Вы бы не использовали его, если уже не используете?
Брайан Агнью
16
Fyi, пакет изменился и класс теперь по адресу: org.apache.commons.lang3.exception.ExceptionUtils.
schmmd
2201

Используйте 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);
Брайан Агнью
источник
505
Если вам не нравится включать внешнюю библиотеку для чего-то столь же маленького и простого, как этот, используйте этот ответ.
Стейн де Витт
8
Это обрезает трассировку стека так же, как printStackTrace (). Все исключения в стеке видны, но для каждого исключения стек может быть обрезан. Любой, кому требуется полный след, должен рассмотреть это.
Лайла Агаев
5
Как оказалось, это в значительной степени именно то, что делает метод Apache ExceptionUtils.getStackTrace (). Почти на букву на самом деле.
отметка
6
@BrianAgnew, разве вы не должны закрывать StringWriterи PrintWriter?
Мухаммед Гелбана
13
@BrianAgnew, спасибо за ответ. Мне стало любопытно, и вот что я нашел: stackoverflow.com/questions/14542535/…
Мухаммед Гелбана
459

Это должно работать:

StringWriter sw = new StringWriter();
e.printStackTrace(new PrintWriter(sw));
String exceptionAsString = sw.toString();
Д. Вроблевский
источник
69
Краткий в контексте Java всегда хиллярный. Это printStackTraceдолжно просто вернуть строку, оставляя решение о том, печатать это пользователю или нет :)
Дмитрий
35
printStackTrace печать в консоли приемлема, но по крайней мере getStackTrace, возвращающий ее как String, должен быть доступен по умолчанию
Mateus Viccari
5
Какая часть этого кратка? Вы должны сконструировать 2 объекта, которые существуют только для того, чтобы поместить трассировку стека в строку.
ArtOfWarfare
7
@dmitry Вызванный метод printXXX()должен вывести XXX.
Маркиз Лорн
2
@ Грег На самом деле это был даже не сарказм, просто требовалось ясное чтение того, что он сказал.
Андрей
226

Если вы разрабатываете для 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)
Вики Кападия
источник
2
Спасибо. В этом случае дата и тег записываются не в каждой строке, как в printStackTrace ().
CoolMind
1
На самом деле Log.getStackTraceString в своем ядре использует вышеупомянутые (D. Wroblewski's) StringWriter и Printwriter.
MikeL
2
Самый простой способ просто распечатать его в журнале Android: Log.e("TAG", "My message", new Throwable());
Эрик М. Шпренгель,
113

ВНИМАНИЕ: Не включает причину (которая обычно является полезным битом!)

public String stackTraceToString(Throwable e) {
    StringBuilder sb = new StringBuilder();
    for (StackTraceElement element : e.getStackTrace()) {
        sb.append(element.toString());
        sb.append("\n");
    }
    return sb.toString();
}
jqno
источник
1
Я бы пошел с расширением этого подхода, если вы хотите обрезать трассировку, например, передать параметр maxLines и добавить только столько строк в трассировку
Rich Seller
Скобки после e.getStackTrace отсутствуют. public static String stackTraceToString (исключение e) {StringBuilder sb = new StringBuilder (); for (элемент StackTraceElement: e.getStackTrace ()) {sb.append (element.toString ()); sb.append ("<br />"); } return sb.toString (); }
rlc
2
Не уверен, но я думаю, что это не будет печатать трассировку стека исключения первопричины.
apoorv020
7
Что бы вы ни делали, не урезайте след. Много, много раз случалось со мной, когда я просматривал следы стеков в журналах GlassFish, у которых были обрезаны полезные части.
cayhorstmann
Используйте String.format("%n")или что-то подобное вместо того, "\n"чтобы осчастливить DOS-ламеров.
ceving
89

Для меня самым чистым и простым способом было:

import java.util.Arrays;
Arrays.toString(e.getStackTrace());
Владас Диржис
источник
37
Код чистый, но вывод нет. Вы должны сделать .replaceAll (",", "\ n") в конце. Однако вы теряете отступ, который предлагает printStackTrace.
Ярость
4
Это полезно, когда вы регистрируете трассировку в одну строку
webjockey
28
public static String getStackTrace(Throwable t) {
    StringWriter sw = new StringWriter();
    t.printStackTrace(new PrintWriter(sw));
    return sw.toString();
}
Акира Ямамото
источник
1
Привет, я просто хотел бы отметить, что комментарии раздел ответов цитируются пункты, что StringWriterи PrintWriterобъекты должны быть closed .... (или я предполагаю , что только PrintWriterдолжен быть закрыт , так как закрытие она должна закрыть StringWriterтоже)
Эрик
8
Под вдохновением ты имеешь ввиду скопированный? Вы только помещаете это в метод ... Чтение ответов здесь похоже на просмотр "Дня Сурка"
Murmel
26

Следующий код позволяет вам получить весь 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.
}
dumonderic
источник
1
да, но это немного громоздко, не правда ли? в любом разумном размере логирование проекта является обязательным, поэтому зачем
mzzzzb
Этот не дает сообщение об ошибке в начале. может быть добавлено, хотя
kommradHomer
Возможно, вы захотите использовать StringBuffer вместо простой конкатенации.
dedda1994
Это полезно, когда вам не разрешено регистрировать сообщение по соображениям конфиденциальности.
1
Имейте в
виду,
19

Вот версия, которую можно копировать прямо в код:

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определяется, верно?
n611x007
16
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});
Раманан Дурайрадж
источник
Работал на меня, ничего не выглядит обрезанным! Проще, чем другие ответы тоже без какой-либо внешней библиотеки, отличный ответ!
Шаунг Ченг
16

Выведите трассировку стека в 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);
}
Запеченный Inhalf
источник
1
это самый прямой ответ
DaSqy Stc
11

Печать трассировки стека в строку

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.

Ярек Пшигодзки
источник
11

Котлин

Расширение класса Throwable даст вам свойство String error.stackTraceString:

val Throwable.stackTraceString: String
  get() {
    val sw = StringWriter()
    val pw = PrintWriter(sw)
    this.printStackTrace(pw)
    return sw.toString()
  }
vonox7
источник
10
private String getCurrentStackTraceString() {
    StackTraceElement[] stackTrace = Thread.currentThread().getStackTrace();
    return Arrays.stream(stackTrace).map(StackTraceElement::toString)
            .collect(Collectors.joining("\n"));
}
eddyrokr
источник
1
Спасибо, eddyrkokr! Это прекрасно работает для всех моих предложений. Я только что добавил его в базовый класс TestNG, и он собирает трассировки стека по одному из всех моих тестов!
Кшиштоф Вальчевский
9

Умный обстрел в первом наборе комментариев был очень забавным, но это действительно зависит от того, что вы пытаетесь сделать. Если у вас еще нет правильной библиотеки, то 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 )

Это спасло мой бекон. Спасибо, Амар, за подсказку!

Tihamér
источник
9

Код из 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.

IvanRF
источник
8

Без 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)
гала
источник
5

Scala версия

def stackTraceToString(e: Exception): String = {
  import java.io.PrintWriter
  val sw = new StringWriter()
  e.printStackTrace(new PrintWriter(sw))
  sw.toString
}
samthebest
источник
5

если вы используете 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".

Пэнчэн Чжан
источник
Это решение не учитывает отступ вкладки трассировки стека, в противном случае он работает отлично!
krizajb
4

описание ответа Гала, в котором также будут указаны причины исключения:

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;
}
идо лен
источник
4

Решением является преобразование stackTrace массива в строковый тип данных. Смотрите следующий пример:

import java.util.Arrays;

try{

}catch(Exception ex){
    String stack = Arrays.toString(ex.getStackTrace());
    System.out.println("stack "+ stack);
}
Хорхе Сантос
источник
4

С Java 8 Stream API вы можете сделать что-то вроде этого:

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

Он возьмет массив элементов трассировки стека, преобразует их в строку и объединяет в многострочную строку.

ivanjermakov
источник
2
Это решение удаляет сообщение и игнорирует всю родительскую трассировку
judovana
3

Мой oneliner для преобразования трассировки стека во вложенную многострочную строку:

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

Легко перейти на регистратор "как есть".

Андрей Лебеденко
источник
Вы получите то, что отличается от printStackTrace()Здесь вы потеряете: 1) Исключение, которое было брошено; 2) Причины и их следы стека
valijon
Разница вполне ожидаемая, так как обращение printStackTrace()никогда не было частью вопроса.
Андрей Лебеденко
2

Если вы не хотите использовать внешнюю библиотеку и не разрабатываете для 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();
}
Kape
источник
2

Старый вопрос, но я просто хотел бы добавить специальный случай, когда вы не хотите печатать всю стопку , удалив некоторые части, которые вам на самом деле не интересны, за исключением определенных классов или пакетов.

Вместо 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деталей реализации (вряд ли изменится, но все же).

MarcG
источник
1
Да, это работает хорошо и на самом деле довольно полезная идея.
gil.fernandes
2
 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)
Прахар Нигам
источник
1

Интересно, почему никто не упомянул ExceptionUtils.getStackFrames(exception)

Для меня это самый удобный способ вывести стек трассировки со всеми причинами до конца:

String.join("\n", ExceptionUtils.getStackFrames(exception));
Андрей Сарул
источник
0

Предупреждение: это может быть немного не по теме, ну да ладно ...;)

Я не знаю, какова была причина первоначальных постеров для того, чтобы в первую очередь желать трассировки стека как строки. Когда трассировка стека должна заканчиваться в 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
}

Мне это нравится, потому что для него не требуется внешняя библиотека (кроме вашего бэкэнда для ведения журналов, который, конечно же, будет в большинстве случаев установлен).

Александр Вессель
источник