Як можна перетворити слід стека в рядок?


1502

Який найпростіший спосіб перетворити результат у Throwable.getStackTrace()рядок, що зображає стеження?


6
Оскільки відповідь jqno насправді використовує метод Throwable.getStackTrace (), який ви вказали у своєму запитанні, тоді як Брайан - ні. Він замість цього використовує Throwable.printStackTrace ().
Штійн де Вітт

10
Майже кожен проект Java повинен містити Apache commons-lang. Він включає безліч зручних методів, що реалізують надзвичайно загальні потреби розвитку.
Рассел Сілва

20
@StijndeWitt Ці три рядки коду майже напевно потребують факторингу з того місця, де ви їх назвали. Оскільки ви не знаєте, куди їх поставити, вони перейдуть до вашої утиліти з усіма іншими корисними фрагментами. Бінго! Ви щойно винайшли гуаву / commons-lang / що завгодно ... тільки не так добре. Натомість імпортуйте розумну бібліотеку комунальних служб та заощадьте винаходити колесо. Справжньою ознакою новачка вважає, що ти можеш зробити кращу роботу, ніж бібліотечні письменники.
Ендрю Спенсер

6
1. Гуава має - 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 для набагато більшої функціональності
Brian Agnew

54
@StijndeWitt Commons Lang є досить поширеним явищем. Він уже присутній у більшості моїх проектів / проектів на роботі.
WhyNotHugo

16
@Hugo спасибі, збирався використовувати 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
Як виявляється, це дуже точно те, що робить метод ExcecheUtils.getStackTrace () апачі. Майже до листа насправді.
галочка

6
@BrianAgnew, ти не повинен закрити StringWriterі PrintWriter?
Мухаммед Гельбана

13
@BrianAgnew, дякую за відповідь. Це змусило мене цікаво , і ось що я знайшов: stackoverflow.com/questions/14542535 / ...
Мухаммад Gelbana

459

Це має працювати:

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

69
Лаконічність в контексті Java завжди химерна. Це printStackTraceповинно просто повернути рядок, залишаючи рішення про те, друкувати його чи ні користувачеві :)
dmitry

35
Друк printStackTrace в консолі прийнятний, але принаймні getStackTrace, що повертає його як String, повинен бути доступний за замовчуванням
Mateus Viccari

5
Яка частина цього стисло? Ви повинні сконструювати 2 об'єкти, які існують не для того, щоб вписати слід стека в рядок.
ArtOfWarfare

7
@dmitry Метод, який викликається, printXXX()повинен надрукувати XXX.
користувач207421

2
@Greg Насправді це навіть не був сарказм, він просто потребував простого прочитання того, що він сказав.
Андрій

224

Якщо ви розробляєте для 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();
}

1
Я б пішов з розширенням цього підходу, якщо ви хочете обрізати слід, наприклад, передайте параметр maxLines і лише додайте до траси стільки рядків
Rich Seller

Відсутні параметри після e.getStackTrace. загальнодоступний статичний String stackTraceToString (Виняток e) {StringBuilder sb = new StringBuilder (); для (StackTraceElement елемент: e.getStackTrace ()) {sb.append (element.toString ()); sb.append ("<br />"); } повернути sb.toString (); }
rlc

2
Не впевнений, але я думаю, що це не буде надрукувати стек стека виключення першопричини.
apoorv020

7
Що б ви не робили, не обрізайте сліду. Зі мною траплялося багато, багато разів, коли я дивився на стек стежок у журналах GlassFish, у яких були відрізані корисні частини.
cayhorstmann

Використовуйте String.format("%n")або щось подібне замість того, "\n"щоб зробити щасливими ламери DOS.
закінчення

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
Під натхненням ви маєте на увазі скопійовані? Ви лише вкладаєте це в метод ... Читати відповіді тут, як дивитися "День бабака"
Мермель

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.
}

1
так, але це трохи об'ємний ви не думаєте? в будь-якому розумному розмірі рамки реєстрації проектів є обов'язковим, тому чому турбуватися
mzzzzb

Цей не дає повідомлення про помилку на початку. можна додати, хоча
kommradHomer

Ви можете використовувати StringBuffer замість простого конкатенації.
dedda1994

Це корисно, коли вам заборонено реєструвати повідомлення з міркувань конфіденційності.
A1m

Будьте в курсі, що це рішення не
запише

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())

Це найпростіший спосіб перетворення результату в String Я використовую це у своїй програмі для друку трасування стека

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);
}

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()
  }

10
private String getCurrentStackTraceString() {
    StackTraceElement[] stackTrace = Thread.currentThread().getStackTrace();
    return Arrays.stream(stackTrace).map(StackTraceElement::toString)
            .collect(Collectors.joining("\n"));
}

1
Дякую вам, Еддіркокр! Це ідеально підходить для всіх моїх пунктів. Я щойно додав його до базового класу TestNG, і він збирає стек-траси по одному з усіх моїх тестів!
Кшиштоф Валчевський

9

Розумний снайпер у першому наборі коментарів був дуже кумедним, але це дійсно залежить від того, що ви намагаєтеся зробити. Якщо ви ще не маєте правильної бібліотеки, то 3 рядки коду (як у відповіді Д. Врублевського) ідеально. ОТОХ, якщо у вас вже є бібліотека apache.commons (як і більшість великих проектів), відповідь Амара коротший. Гаразд, може знадобитися десять хвилин, щоб отримати бібліотеку та правильно встановити її (менше однієї, якщо ви знаєте, що ви робите). Але годинник тикає, тож, можливо, не вистачить часу на запас. У Jarek Przygódzki був цікавий застереження - "Якщо вам не потрібні вкладені винятки".

Але що робити, якщо мені потрібні цілі стеки стежок, вкладених і все? У цьому випадку секретом є використання apache.common’s getFullStackTrace (див. Http://commons.apache.org/proper/commons-lang/javadocs/api-2.6/org/apache/commons/lang/exception/ExceptionUtils.html # getFullStackTrace% 28java.lang.Throwable% 29 )

Це врятувало моє бекон. Спасибі, Амар, за підказку!


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.


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
}

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"));

Це займе масив мікроелементів стека, перетворить їх у рядок і об'єднається в багаторядковий рядок.


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();
}

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


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, але виняток не було або слід кидати ось що я роблю:

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
}

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


Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.