Jaki jest najłatwiejszy sposób przekonwertowania wyniku Throwable.getStackTrace()
na ciąg znaków przedstawiający stacktrace?
Jaki jest najłatwiejszy sposób przekonwertowania wyniku Throwable.getStackTrace()
na ciąg znaków przedstawiający stacktrace?
Odpowiedzi:
Można użyć następującej metody do konwersji Exception
śledzenia stosu String
. Ta klasa jest dostępna w Apache commons-lang, który jest najczęściej zależną biblioteką z wieloma popularnymi otwartymi źródłami
org.apache.commons.lang.exception.ExceptionUtils.getStackTrace(Throwable)
org.apache.commons.lang3.exception.ExceptionUtils
.
Służy Throwable.printStackTrace(PrintWriter pw)
do wysyłania śledzenia stosu do odpowiedniego programu piszącego.
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
i PrintWriter
?
To powinno działać:
StringWriter sw = new StringWriter();
e.printStackTrace(new PrintWriter(sw));
String exceptionAsString = sw.toString();
printStackTrace
powinien po prostu powrócić ciąg, pozostawiając decyzję, czy wydrukować go lub nie do użytkownika :)
printXXX()
powinna wypisać XXX.
Jeśli tworzysz dla Androida, znacznie łatwiejszym sposobem jest skorzystanie z tego:
import android.util.Log;
String stackTrace = Log.getStackTraceString(exception);
Format jest taki sam jak getStacktrace, np
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
Klasa GuawyJeśli masz rzeczywistą Throwable
instancję, Google Guava zapewnia Throwables.getStackTraceAsString()
.
Przykład:
String s = Throwables.getStackTraceAsString ( myException ) ;
OSTRZEŻENIE: Nie zawiera przyczyny (która zwykle jest użytecznym bitem!)
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")
lub coś podobnego zamiast, "\n"
aby uszczęśliwić lamery DOS.
Dla mnie najczystszym i najłatwiejszym sposobem było:
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
i PrintWriter
powinny być close
d .... (lub chyba tylko PrintWriter
trzeba je zamknąć, ponieważ zamknięcie powinno również zamknąć StringWriter
)
Poniższy kod pozwala uzyskać cały stackTrace w String
formacie bez użycia interfejsów API, takich jak log4J, a nawet 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.
}
Oto wersja, którą można skopiować bezpośrednio do kodu:
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());
Lub w bloku catch
} catch (Throwable t) {
StringWriter sw = new StringWriter();
t.printStackTrace(new PrintWriter(sw));
logger.info("Current stack trace is:\n" + sw.toString());
}
Throwable
jest zdefiniowane, prawda?
Arrays.toString(thrown.getStackTrace())
To najprostszy sposób na przekonwertowanie wyniku na ciąg. Używam tego w moim programie do drukowania śladu stosu
LOGGER.log(Level.SEVERE, "Query Builder Issue Stack Trace : {0} ,Message : {1} objid {2}", new Object[]{Arrays.toString(e.getStackTrace()), e.getMessage(),objId});
Wydrukuj ślad stosu na a PrintStream
, a następnie przekonwertuj go na String
:
// ...
catch (Exception e)
{
ByteArrayOutputStream out = new ByteArrayOutputStream();
e.printStackTrace(new PrintStream(out));
String str = new String(out.toByteArray());
System.out.println(str);
}
Drukowanie śladu stosu na łańcuch
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);
}
}
}
Jest to przydatne, gdy chcesz wydrukować bieżący ślad stosu wątków bez tworzenia instancji Throwable
- ale pamiętaj, że tworzenie nowego Throwable
i uzyskiwanie śledzenia stosu z tego miejsca jest w rzeczywistości szybsze i tańsze niż wywołanie Thread.getStackTrace
.
private String getCurrentStackTraceString() {
StackTraceElement[] stackTrace = Thread.currentThread().getStackTrace();
return Arrays.stream(stackTrace).map(StackTraceElement::toString)
.collect(Collectors.joining("\n"));
}
Sprytne strzelanie w pierwszy zestaw komentarzy było bardzo zabawne, ale tak naprawdę zależy od tego, co próbujesz zrobić. Jeśli nie masz jeszcze właściwej biblioteki, to 3 wiersze kodu (jak w odpowiedzi D. Wróblewskiego) są idealne. OTOH, jeśli masz już bibliotekę apache.commons (jak zrobi to większość dużych projektów), to odpowiedź Amara jest krótsza. OK, uzyskanie biblioteki i zainstalowanie jej może zająć dziesięć minut (mniej niż jedna, jeśli wiesz, co robisz). Ale zegar tyka, więc możesz nie mieć czasu do stracenia. Jarek Przygódzki miał ciekawe zastrzeżenie - „Jeśli nie potrzebujesz zagnieżdżonych wyjątków”.
Ale co, jeśli nie potrzebujemy pełnych ślady stosu, zagnieżdżone i wszystkich? W takim przypadku sekret polega na użyciu getFullStackTrace apache.common (patrz http://commons.apache.org/proper/commons-lang/javadocs/api-2.6/org/apache/commons/lang/exception/ExceptionUtils.html # getFullStackTrace% 28java.lang.Throwable% 29 )
Uratowało mi to bekon. Dzięki, Amar, za podpowiedź!
Kod z 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();
}
Różnica w porównaniu z innymi odpowiedziami polega na tym, że używa autoFlush
on PrintWriter
.
Bez java.io.*
tego można to zrobić w ten sposób.
String trace = e.toString() + "\n";
for (StackTraceElement e1 : e.getStackTrace()) {
trace += "\t at " + e1.toString() + "\n";
}
A potem trace
zmienna przechowuje ślad stosu. Wyjście zawiera również przyczynę początkową, wyjście jest identyczne zprintStackTrace()
Przykład, printStackTrace()
daje:
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
String posiada, gdy drukowanystdout
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)
Wersja Scala
def stackTraceToString(e: Exception): String = {
import java.io.PrintWriter
val sw = new StringWriter()
e.printStackTrace(new PrintWriter(sw))
sw.toString
}
jeśli używasz Java 8, spróbuj tego
Arrays.stream(e.getStackTrace())
.map(s->s.toString())
.collect(Collectors.joining("\n"));
kod getStackTrace()
funkcji można znaleźć Throwable.java
jako:
public StackTraceElement[] getStackTrace() {
return getOurStackTrace().clone();
}
a dla StackTraceElement
dostawców to toString()
jako:
public String toString() {
return getClassName() + "." + methodName +
(isNativeMethod() ? "(Native Method)" :
(fileName != null && lineNumber >= 0 ?
"(" + fileName + ":" + lineNumber + ")" :
(fileName != null ? "("+fileName+")" : "(Unknown Source)")));
}
Po prostu dołącz do StackTraceElement
„\ n”.
rozszerzenie odpowiedzi Gali, które obejmie również przyczyny wyjątku:
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;
}
Rozwiązaniem jest konwersja stackTrace tablicy na ciąg danych typu. Zobacz następujący przykład:
import java.util.Arrays;
try{
}catch(Exception ex){
String stack = Arrays.toString(ex.getStackTrace());
System.out.println("stack "+ stack);
}
Z Java 8 Stream API możesz zrobić coś takiego:
Stream
.of(throwable.getStackTrace())
.map(StackTraceElement::toString)
.collect(Collectors.joining("\n"));
Bierze tablicę elementów śledzenia stosu, konwertuje je na ciąg i łączy w ciąg wielowierszowy.
Mój oneliner do konwersji śledzenia stosu na dołączony ciąg wielu linii:
Stream.of(e.getStackTrace()).map((a) -> a.toString()).collect(Collectors.joining("\n", "[", "]"))
Łatwy do przekazania do rejestratora „jak jest”.
printStackTrace()
tutaj, tracisz: 1) wyjątek, który został zgłoszony; 2) Przyczyny i ich śledzenie stosu
printStackTrace()
nigdy nie była częścią pytania.
Jeśli nie chcesz korzystać z biblioteki zewnętrznej i nie tworzysz aplikacji dla systemu Android , możesz utworzyć metodę „rozszerzenia” w następujący sposób :
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();
}
Stare pytanie, ale chciałbym tylko dodać specjalny przypadek, w którym nie chcesz wydrukować całego stosu , usuwając niektóre części, które tak naprawdę nie są zainteresowane, z wyłączeniem niektórych klas lub pakietów.
Zamiast PrintWriter
użycia 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);
Jeżeli SelectivePrintWriter
klasę podaje:
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);
}
}
}
Należy pamiętać, że tę klasę można łatwo dostosować do filtrowania według Regex contains
lub innych kryteriów. Pamiętaj też, że zależy to od Throwable
szczegółów implementacji (prawdopodobnie nie ulegnie zmianie, ale nadal).
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);
}
}
}
Po uruchomieniu programu wynik będzie podobny:
java.lang.ArithmeticException: / by zero
at PrintStackTrace.main(PrintStackTrace.java:9)
Zastanawiam się, dlaczego nikt nie wspomniał ExceptionUtils.getStackFrames(exception)
Dla mnie jest to najwygodniejszy sposób na zrzucenie stacktrace ze wszystkimi jego przyczynami do końca:
String.join("\n", ExceptionUtils.getStackFrames(exception));
Ostrzeżenie: to może być trochę nie na temat, ale no cóż ...;)
Nie wiem, jaki był pierwotny powód plakatów, że chciałem, aby ślad stosu był ciągiem. Kiedy ślad stosu powinien skończyć się w dzienniku SLF4J / Logback, ale nie było wyjątku ani nie należy go zgłaszać, oto co robię:
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
}
Podoba mi się, ponieważ nie wymaga biblioteki zewnętrznej (innej niż backend logowania, który i tak będzie przez większość czasu na miejscu).
Kilka opcji
StringWriter sw = new StringWriter();
e.printStackTrace(new PrintWriter(sw));
String exceptionAsString = sw.toString();
Korzystanie z Google Guava lib
String stackTrace = Throwables.getStackTraceAsString ( myException ) ;
org.apache.commons.lang.exception.ExceptionUtils.getStackTrace (Throwable)