Warum wird exception.printStackTrace () als schlechte Methode betrachtet?

Es gibt eine Menge Material , das darauf hindeutet, dass das Drucken der Stapelspur einer Ausnahme eine schlechte Übung ist.

ZB von der RegexpSingleline Check in Checkstyle:

Diese Überprüfung kann verwendet werden, […] um gängige schlechte Praktiken wie den Aufruf von ex.printStacktrace () zu finden.

Allerdings habe ich Mühe, irgendwo zu finden, was einen triftigen Grund dafür liefert, dass der Stack-Trace sicherlich nützlich ist, um herauszufinden, was die Ausnahme verursacht hat. Dinge, die mir bekannt sind:

  1. Ein Stack-Trace sollte für Endbenutzer niemals sichtbar sein (aus Gründen der Benutzerfreundlichkeit und Sicherheit)

  2. Das Generieren eines Stack-Trace ist ein relativ teurer process (obwohl dies in den meisten Ausnahmefällen kein Problem darstellt).

  3. Viele Protokoll-Frameworks drucken den Stack-Trace für Sie (unseren nicht und nein, wir können es nicht einfach ändern)

  4. Das Drucken der Stapelüberwachung stellt keine Fehlerbehandlung dar. Es sollte mit anderer Informationsprotokollierung und Ausnahmebehandlung kombiniert werden.

Welche anderen Gründe gibt es, um das Drucken einer Stapelverfolgung in Ihrem Code zu vermeiden?

    Throwable.printStackTrace() schreibt den Stack-Trace in System.err PrintStream. Der System.err Stream und der zugrundeliegende Standarderrors-Ausgabestream des JVM-processes können umgeleitet werden

    • Aufrufen von System.setErr() das das Ziel ändert, auf das System.err .
    • oder indem der Fehler-Ausgabestrom des processes umgeleitet wird. Der Fehler-Ausgabestream kann zu einer Datei / einem Gerät umgeleitet werden
      • deren Inhalte vom Personal ignoriert werden können,
      • Die Datei / das Gerät ist möglicherweise nicht in der Lage, eine Protokollrotation durchzuführen, was darauf schließen lässt, dass ein processneustart erforderlich ist, um die geöffnete Datei- / Gerätekennung zu schließen, bevor der vorhandene Inhalt der Datei / des Geräts archiviert wird.
      • oder die Datei / das Gerät verwirft tatsächlich alle Daten, die darauf geschrieben wurden, wie es bei /dev/null der Fall ist.

    Nach dem oben Gesagten stellt das Aufrufen von Throwable.printStackTrace() ein gültiges (nicht gutes / großes) Ausnahmebehandlungsverhalten dar

    • wenn System.err nicht während der gesamten Lebensdauer der Anwendung neu zugewiesen wird,
    • und wenn Sie während der Ausführung der Anwendung keine Protokollrotation benötigen,
    • und wenn akzeptierte / entworfene Protokollierungspraxis der Anwendung ist, in System.err zu schreiben (und den JVM-Standarderrors-Ausgabestrom).

    In den meisten Fällen sind die obigen Bedingungen nicht erfüllt. Möglicherweise ist anderer Code, der in der JVM ausgeführt wird, nicht bekannt, und die Größe der Protokolldatei oder die Laufzeitdauer des processes kann nicht vorhergesagt werden. Eine gut konzipierte Protokollierungspraxis würde sich um das Schreiben von “maschinell analysierbaren” Protokolldateien drehen (a bevorzugtes, aber optionales Merkmal in einem Logger) in einem bekannten Ziel, um Unterstützung zu unterstützen.

    Schließlich sollte man sich daran erinnern, dass die Ausgabe von Throwable.printStackTrace() definitiv mit anderen Inhalten verschachtelt wird, die in System.err geschrieben System.err (und möglicherweise sogar in System.out wenn beide zu derselben Datei / demselben Gerät umgeleitet werden). Dies ist ein Ärgernis (für Singlethread-Anwendungen), mit dem man sich auseinandersetzen muss, da die Daten zu Ausnahmen in einem solchen Fall nicht einfach analysiert werden können. Schlimmer noch, es ist sehr wahrscheinlich, dass eine Multithread-Anwendung als Throwable.printStackTrace() sehr verwirrende Protokolle erzeugt. Throwable.printStackTrace() ist nicht Thread-sicher .

    Es gibt keinen Synchronisierungsmechanismus, um das Schreiben der System.err auf Throwable.printStackTrace() zu System.err wenn mehrere Threads Throwable.printStackTrace() gleichzeitig aufrufen. Um dies zu System.err muss Ihr Code auf dem Monitor synchronisiert werden, der System.err (und auch System.out , wenn die System.err / das Zielgerät identisch ist), und das ist ein ziemlich hoher Preis für die Protokolldateiqualität. Um ein Beispiel zu nennen, sind die classn ConsoleHandler und StreamHandler verantwortlich für das Anhängen von Protokolldatensätzen an die Konsole in der Protokollierungsfunktion von java.util.logging ; Der eigentliche Vorgang der Veröffentlichung von Protokolldatensätzen wird synchronisiert. Jeder Thread, der versucht, einen Protokolldatensatz zu veröffentlichen, muss auch die Sperre auf dem der StreamHandler Instanz zugeordneten Monitor StreamHandler . Wenn Sie mit System.out / System.err die gleiche Garantie für nicht verschachtelte Protokollsätze haben System.err , müssen Sie sicherstellen, dass die Nachrichten in diesen Datenströmen serialisierbar veröffentlicht werden.

    In Anbetracht all dessen und der sehr eingeschränkten Szenarios, in denen Throwable.printStackTrace() tatsächlich nützlich ist, stellt es sich oft heraus, dass es nicht Throwable.printStackTrace() ist, sie aufzurufen.


    Wenn Sie das Argument in einem der vorherigen Absätze erweitern, ist es auch eine schlechte Wahl, Throwable.printStackTrace in Verbindung mit einem Logger zu verwenden, der auf die Konsole schreibt. Dies liegt zum Teil daran, dass der Logger auf einem anderen Monitor synchronisiert wird, während Ihre Anwendung (möglicherweise, wenn Sie keine verschachtelten Log-Datensätze wünschen) auf einem anderen Monitor synchronisiert wird. Das Argument gilt auch, wenn Sie in Ihrer Anwendung zwei verschiedene Protokollierer verwenden, die auf dasselbe Ziel schreiben.

    Sie berühren hier mehrere Probleme:

    1) Ein Stack-Trace sollte für Endbenutzer niemals sichtbar sein (aus Gründen der Benutzererfahrung und Sicherheit)

    Ja, es sollte zugänglich sein, um Probleme von Endbenutzern zu diagnostizieren, aber der Endanwender sollte sie aus zwei Gründen nicht sehen:

    • Sie sind sehr dunkel und unlesbar, die Anwendung wird sehr benutzerunfreundlich aussehen.
    • Das Anzeigen eines Stack-Trace für den Endbenutzer könnte ein potenzielles Sicherheitsrisiko darstellen. Korrigieren Sie mich, wenn ich falsch liege, PHP tatsächlich functionsparameter in Stack-Trace – brillant, aber sehr gefährlich – wenn Sie erhalten würde Ausnahme beim Verbinden mit der database, was sind Sie wahrscheinlich in der Stacktrace?

    2) Das Generieren eines Stack-Trace ist ein relativ teurer process (obwohl dies in den meisten Ausnahmefällen kein Problem darstellt)

    Das Generieren eines Stack-Trace geschieht, wenn die Exception erstellt / geworfen wird (deshalb ist das Auslösen einer Exception mit einem Preis verbunden), das Drucken ist nicht so teuer. Tatsächlich können Sie Throwable#fillInStackTrace() in Ihrer benutzerdefinierten Ausnahme überschreiben, was eine Ausnahme fast so billig macht wie eine einfache GOTO-statement.

    3) Viele Protokoll-Frameworks drucken die Stack-Trace für Sie (unsere nicht und nein, wir können es nicht leicht ändern)

    Sehr guter Punkt. Das Hauptproblem hierbei ist: Wenn das Framework die Ausnahme für Sie protokolliert, tun Sie nichts (aber stellen Sie sicher, dass es funktioniert!) Wenn Sie die Ausnahme selbst protokollieren möchten, verwenden Sie Logging-Framework wie Logback oder Log4J , um sie nicht auf der Rohkonsole zu platzieren weil es sehr schwer ist, es zu kontrollieren.

    Mit dem Logging-Framework können Sie Stack-Traces einfach in eine Datei redirect, konsolen oder sogar an eine angegebene E-Mail-Adresse senden. Mit printStackTrace() Sie mit dem sysout .

    4) Das Drucken des Stack-Trace stellt keine Fehlerbehandlung dar. Es sollte mit anderer Informationsprotokollierung und Ausnahmebehandlung kombiniert werden.

    Erneut: SQLException korrekt protokollieren (mit dem vollständigen Stack-Trace unter Verwendung des Logging-Frameworks) und nice SQLException : ” Sorry, wir können Ihre Anfrage derzeit nicht SQLException “. Glauben Sie wirklich, dass der Benutzer an den Gründen interessiert ist? Haben Sie den StackOverflow-Fehlerbildschirm gesehen? Es ist sehr humorvoll, verrät aber keine Details. Es stellt jedoch dem Benutzer sicher, dass das Problem untersucht wird.

    Aber er wird Sie sofort anrufen und Sie müssen in der Lage sein, das Problem zu diagnostizieren. Sie brauchen also beides: eine korrekte Protokollierung der Ausnahme und benutzerfreundliche Nachrichten.


    Um dies zu verdeutlichen: Protokollieren Sie immer Ausnahmen (vorzugsweise mit dem Logging Framework ), aber stellen Sie sie nicht dem Endbenutzer zur Verfügung. Überlegen Sie sorgfältig und über Fehlermeldungen in Ihrer GUI, zeigen Sie Stack-Traces nur im Entwicklungsmodus an.

    Die erste Sache printStackTrace () ist nicht teuer, wie Sie sagen , da der Stack-Trace gefüllt ist, wenn die Ausnahme selbst erstellt wird.

    Die Idee besteht darin, alles, was zu Protokollen gehört, durch ein Logger-Framework zu übergeben, damit die Protokollierung gesteuert werden kann. Verwenden Sie daher anstelle von printStackTrace etwas wie Logger.log(msg, exception);

    Das Ausdrucken des Stapel-Trace der Ausnahmebedingung an sich stellt keine schlechte Praxis dar, aber wahrscheinlich ist das Problem hier nur das Drucken des Stace-Trace, wenn eine Ausnahme auftritt – oft genügt es nicht, nur einen Stapel-Trace zu drucken.

    Außerdem besteht die Tendenz zu vermuten, dass eine ordnungsgemäße Ausnahmebehandlung nicht durchgeführt wird, wenn alles, was in einem catch Block ausgeführt wird, ein e.printStackTrace . Unsachgemäße Handhabung kann bestenfalls bedeuten, dass ein Problem ignoriert wird, und schlimmstenfalls ein Programm, das weiterhin in einem undefinierten oder unerwarteten Zustand ausgeführt wird.

    Beispiel

    Betrachten wir das folgende Beispiel:

     try { initializeState(); } catch (TheSkyIsFallingEndOfTheWorldException e) { e.printStackTrace(); } continueProcessingAssumingThatTheStateIsCorrect(); 

    Hier möchten wir eine Initialisierungsverarbeitung durchführen, bevor wir mit der Verarbeitung fortfahren, für die die Initialisierung erforderlich war.

    In dem obigen Code sollte die Ausnahme abgefangen und ordnungsgemäß behandelt worden sein, um zu verhindern, dass das Programm zur continueProcessingAssumingThatTheStateIsCorrect Methode übergeht, von der wir annehmen könnten, dass sie zu Problemen führen würde.

    In vielen Fällen ist e.printStackTrace() ein Hinweis darauf, dass eine Ausnahme verschluckt wird und die Verarbeitung so e.printStackTrace() , als ob kein Problem aufgetreten wäre.

    Warum ist das ein Problem geworden?

    Wahrscheinlich ist einer der Hauptgründe dafür, dass eine schlechte Ausnahmebehandlung häufiger geworden ist, darin begründet, dass IDEs wie Eclipse automatisch Code generieren, der eine e.printStackTrace für die Ausnahmebehandlung e.printStackTrace :

     try { Thread.sleep(1000); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } 

    (Das obige Beispiel ist ein von Eclipse automatisch generierter try-catch , der eine InterruptedException , die von Thread.sleep ausgetriggers wird.)

    Für die meisten Anwendungen reicht es wahrscheinlich nicht aus, nur den Stack-Trace auf Standarderrors zu drucken. Eine errorshafte Ausnahmebehandlung kann in vielen Fällen dazu führen, dass eine Anwendung in einem unerwarteten Zustand ausgeführt wird und zu unerwartetem und nicht definiertem Verhalten führen kann.

    Ich denke, Ihre Liste der Gründe ist ziemlich umfassend.

    Ein besonders schlechtes Beispiel, dem ich schon öfter begegnet bin, lautet:

      try { // do stuff } catch (Exception e) { e.printStackTrace(); // and swallow the exception } 

    Das Problem mit dem obigen Code besteht darin, dass die Behandlung vollständig aus dem printStackTrace Aufruf besteht: Die Ausnahme wird nicht wirklich korrekt gehandhabt, und es ist auch nicht erlaubt, zu entkommen.

    Auf der anderen Seite protokolliere ich den Stack-Trace in der Regel immer, wenn eine unerwartete Ausnahme in meinem Code vorliegt. Im Laufe der Jahre hat mir diese Richtlinie viel Zeit für das Debugging erspart.

    Schließlich, auf eine hellere Note, Gottes perfekte Ausnahme .

    printStackTrace() druckt auf eine Konsole. In der Produktion sieht das niemand zu. Suraj ist richtig, sollte diese Informationen an einen Logger weitergeben.

    In Serveranwendungen wird der stacktrace Ihre stdout / stderr-Datei in die Höhe schießen. Es kann größer und größer werden und ist mit nutzlosen Daten gefüllt, weil Sie normalerweise keinen Kontext und keinen Zeitstempel usw. haben.

    zB catalina.out bei Verwendung von Tomcat als Container

    Es ist keine schlechte Übung, weil etwas an PrintStackTrace () “falsch” ist, sondern weil es “Code-Geruch” ist. Meistens ist der PrintStackTrace () – Aufruf vorhanden, da jemand die Ausnahme nicht ordnungsgemäß verarbeitet hat. Sobald Sie die Ausnahme richtig behandelt haben, ist Ihnen der StackTrace nicht mehr wichtig.

    Darüber hinaus ist das Anzeigen des StackTrace auf stderr im Allgemeinen nur beim Debuggen nützlich, nicht in der Produktion, da stderr oft nicht weiterkommt. Logging macht mehr Sinn. Wenn Sie jedoch PrintStackTrace () durch das Protokollieren der Ausnahme ersetzen, bleibt eine Anwendung bestehen, die fehlgeschlagen ist, aber weiterläuft, als ob nichts passiert wäre.

    Wie einige Leute schon erwähnt haben, ist das Problem mit Ausnahme des Schluckens, wenn Sie e.printStackTrace() im catch Block aufrufen. Es wird die Ausführung des Threads nicht stoppen und wird nach dem try-Block wie in normalem Zustand fortgesetzt.

    Stattdessen müssen Sie entweder versuchen, die Exception wiederherzustellen (falls es wiederherstellbar ist) oder RuntimeException oder die Exception an den Aufrufer zu RuntimeException , um stille Abstürze zu vermeiden (z. B. aufgrund einer falschen Logger-Konfiguration).