Warum wird "log and throw" als Anti-Pattern angesehen? [geschlossen]

85

Diese Frage wurde durch eine Diskussion um diesen Artikel ausgelöst , auf die ich keine guten Antworten erhielt.

Warum sollte es eine schlechte Idee sein, Ihre Ausnahme zu protokollieren und dann erneut zu werfen (wobei natürlich die ursprüngliche Stapelverfolgung beibehalten wird), wenn Sie sie nicht anders behandeln können?

Manu
quelle
Versuchen Sie, auf softwareengineering.stackexchange.com
chharvey

Antworten:

121

Ich nehme an, die Antwort liegt hauptsächlich darin, warum Sie sie fangen, wenn Sie damit nicht umgehen können. Warum nicht zulassen, dass jeder, der damit umgehen kann (oder wem keine andere Wahl bleibt, als damit umzugehen), es protokolliert, wenn er der Meinung ist, dass es protokollwürdig ist?

Wenn Sie es abfangen und protokollieren und erneut werfen, kann der Upstream-Code nicht erkennen, dass Sie die Ausnahme bereits protokolliert haben, sodass dieselbe Ausnahme möglicherweise zweimal protokolliert wird. Oder schlimmer noch, wenn der gesamte Upstream-Code demselben Muster folgt, wird die Ausnahme möglicherweise beliebig oft protokolliert, einmal für jede Ebene im Code, die beschließt, sie abzufangen, zu protokollieren und dann erneut zu werfen.

Einige könnten auch argumentieren, dass das Auslösen und erneute Werfen Ihrer Laufzeitleistung nicht hilft, da das Werfen und Abfangen von Ausnahmen relativ kostspielig ist. Es hilft Ihrem Code auch nicht in Bezug auf Prägnanz oder Wartbarkeit.

Aroth
quelle
Weitere Informationen finden Sie in den Kommentaren zu Jeffs Antwort
Manu
8
Ein Grund für das Protokollieren und erneute Auslösen einer Ausnahme könnte darin bestehen, den Grund einzugrenzen und eine bestimmte Nachricht zu protokollieren.
Mike Argyriou
11
Antwort: Möglicherweise sind am aktuellen Punkt im Stapel nützliche Debug-Informationen verfügbar, die an einem anderen Punkt in der Stapelverfolgung nicht verfügbar sind
rtconner
Das Fangen und erneute Werfen ist manchmal hilfreich, wenn die neue Ausnahme mit mehr Informationen oder spezifischer hilfreich ist.
Borjab
Um das auszugeben, was @rtconner kommentiert hat: Im asynchronen Code kann es sein, dass der Catcher nicht über den Kontext verfügt, der dem Werfer zur Verfügung steht.
Nir Alfasi
53

Log-and-Throw ist ein gutes Muster, wenn die Entität, die die Ausnahme abfängt und erneut auslöst, Grund zu der Annahme hat, dass sie Informationen enthält, die nicht weiter oben im Aufrufstapel protokolliert werden - zumindest nicht auf die am meisten gewünschte Weise. Dies kann aus mehreren Gründen auftreten:

  1. Die Ausnahme kann an einer Grenze der Anwendungsebene abgefangen und erneut ausgelöst werden und privilegierte Informationen enthalten. Es wäre schlecht für eine Datenbankebene, eine Ausnahme zuzulassen, die besagt: "Versuch, dem Feld" Benutzer "einen doppelten Schlüssel" fnord "hinzuzufügen", um die äußere Anwendungsschicht zu erreichen (die sie wiederum einem Benutzer zugänglich machen könnte), aber dies Dies kann nützlich sein, wenn innere Teile der Datenbank eine solche Ausnahme auslösen und die Anwendungsschnittstelle sie abfängt, sicher protokolliert und eine etwas weniger beschreibende Ausnahme erneut auslöst.
  2. Die Ausnahme kann eine sein, die die äußere Schicht wahrscheinlich ohne Protokollierung verarbeiten würde, aber die innere Schicht weiß möglicherweise etwas, was die äußere Schicht nicht weiß, was darauf hindeuten würde, dass die Protokollierung nützlich sein könnte. Als grobes Beispiel könnte eine mittlere Anwendungsschicht so programmiert werden, dass versucht wird, eine Verbindung zu einem Server herzustellen, und wenn dies nicht funktioniert, versuchen Sie es mit einem anderen. Das Überfluten des Anwendungsprotokolls mit Meldungen "Verbindung fehlgeschlagen", während ein Server wegen Wartungsarbeiten nicht verfügbar ist, ist möglicherweise nicht hilfreich, zumal aus Sicht der Anwendung alles einwandfrei funktioniert hat. Es kann nützlich sein, Informationen über den Verbindungsfehler an eine mit Servern verknüpfte Protokollierungsressource weiterzuleiten, die dann die Protokolle filtern kann, um einen Bericht darüber zu erstellen, wann der Server hoch und runter ging, im Gegensatz zu einem Protokoll jedes einzelnen Verbindungsversuchs .
Superkatze
quelle
4
# 1 ist in der Tat ein vernünftiger allgemeiner Anwendungsfall, jedoch hat das OP ausdrücklich gefragt, ob es erneut geworfen werden soll (wobei natürlich die ursprüngliche Stapelspur beibehalten werden soll), sodass # 1 nicht die richtige Antwort ist.
Geoffrey Zheng
@GeoffreyZheng: Das hängt davon ab, ob die sichere Protokollierung des ursprünglichen Stack-Trace als "Erhalt" gilt.
Supercat
2
In Bezug auf Nummer 1: Das Anzeigen von Ausnahmeinhalten (wie das Anzeigen e.getMessage()/ Stapeln von Traces auf der Benutzeroberfläche sowie das Senden als REST-Antwort) sollte als Sicherheitsanfälligkeit selbst behandelt werden, da Laufzeitausnahmen jede Art von vertraulichen Informationen enthalten können. Zu # 2: Sie können eine Ausnahme erneut auslösen und alle Informationen hinzufügen, die der Client wissen soll (+ Grundursache), ohne dass etwas protokolliert werden muss.
Nikita Bosik
@NikitaBosik: Unabhängig davon, ob Ausnahmen clientseitig verfügbar gemacht werden sollen oder nicht, sind Anwendungen, die dies tun, so häufig, dass das Prinzip der "Sicherheit in der Tiefe" vorschlägt, dass Ausnahmemeldungen von vertraulichen Informationen gelöscht werden sollten. Wenn die äußere Schicht nicht erwartet, einen bestimmten Ausnahmetyp zu verwerfen, ohne ihn zu protokollieren oder Informationen an eine möglicherweise interessierende externe Entität weiterzuleiten, hilft es nicht, wenn eine mittlere Schicht Informationen hinzufügt, die die äußere Schicht ignorieren wird etwas.
Supercat
20

Ich denke, der einfachste Grund ist, dass Sie normalerweise einen einzigen Top-Level-Handler haben, der dies für Sie erledigt, sodass Sie den Code mit dieser Ausnahmebehandlung nicht verschmutzen müssen.

Das Argument der Querschnittsthemen ist im Grunde, dass es Zeitverschwendung ist, Fehler zu behandeln, die Sie nicht betreffen. Es ist weitaus besser, den Fehler im Aufrufstapel aufblasen zu lassen, bis ein geeigneter Handler gefunden wurde.

Meiner Meinung nach sollten Sie nur dann eine Ausnahme abfangen, wenn Sie mit den Ergebnissen etwas Nützliches anfangen können. Es ist nicht sinnvoll, nur zu protokollieren, da Sie diese Arbeit weiter oben zentralisieren könnten.

Jeff Foster
quelle
1
Ich bin damit einverstanden, dass Sie einen einzigen Top-Level-Handler haben, der dies für Sie erledigt. Aber meiner Meinung nach sollte dieser Top-Level-Handler "log and throw" sein. Es geht also darum, an welchem ​​Punkt "loggen und werfen" soll, nicht ob oder überhaupt nicht?!?
Manu
@ Manu, ich denke, der Punkt ist, dass wenn es sich um einen einzelnen Handler (zentralisiert) handelt, das in Ordnung ist. Wenn Sie Code duplizieren, ist das nicht in Ordnung. Ich glaube nicht, dass mehr dahinter steckt!
Jeff Foster
5
@Manu - Was wirft der Top-Level-Handler in diesem Fall? Es scheint mir, dass wenn es etwas gibt, auf das es werfen kann, es nicht wirklich der Top-Level-Handler ist. Und Sie möchten definitiv keine Ausnahme bis zur Laufzeitumgebung selbst erneut auslösen. Dadurch werden die meisten Anwendungen zum Absturz gebracht.
aroth
1
@aroth: Ich verstehe, was Sie sagen, ist entweder "log and handle" (wenn Sie der Top-Level-Handler sind) oder "nicht log and throw (oder anderweitig handhaben)", wenn Sie es nicht sind. Vielen Dank für den Hinweis.
Manu
9

IMO Log and Throw ist ein klarer Verstoß gegen das Prinzip der geringsten Überraschung.

Wenn die Ausnahme weiter oben im Aufrufstapel ordnungsgemäß behandelt wird, ist ein Fehlerprotokolleintrag möglicherweise überhaupt nicht wert. Und dann ist es verwirrend, einen Fehlerprotokolleintrag zu finden.

Bastian Voigt
quelle
Was ist mit fehlerfreien Protokollen?
Su Zhang
6
@SuZhang Ich verstehe deine Frage nicht ganz. Wenn es keinen Fehler gibt, gibt es nichts zu werfen. Natürlich können und sollten Sie fehlerfreie Protokolle schreiben.
Bastian Voigt