Ist es sinnvoller, Ausnahmen in einer Sammel- oder einer Basisausnahmeklasse zu protokollieren?

15

Ich bin dabei, eine ziemlich große Web-App umzugestalten. Eines der Hauptprobleme ist die inkonsistente Fehlerbehandlung, und ich versuche, eine vernünftige Strategie zu entwickeln. Ich habe über set_error_handler einen benutzerdefinierten Fehlerhandler erstellt , der im Wesentlichen PHP-Fehler in ErrorExceptions umwandelt, sowie eine benutzerdefinierte Basisausnahmeklasse , die direkt von Exception erbt .

In der Produktion verwende ich über set_exception_handler ein generisches Ausnahmecatch-all und füge dem Mix die Ausnahmenprotokollierung * hinzu . Mein Dilemma ist, wo die eigentliche Protokollierung durchgeführt werden soll, in der Basisausnahmeklasse oder im Catch-All.

Ich habe mir ein paar Gründe ausgedacht, um es in das Catch-All einzutragen:

  • Es gibt einige Ausnahmen im Code, die in ein geeignetes untergeordnetes Element der Basisausnahmeklasse konvertiert werden müssen. Bis dahin werden nicht alle Ausnahmen protokolliert.
  • Es fühlt sich irgendwie natürlicher an, dies im Catch-All zu tun. Eine Basis-Ausnahmeklasse sollte nicht mehr als das tun. (Es kann sich um ein Prinzip mit einer Verantwortung handeln, aber es kann sich auch um ein falsches Gefühl handeln.)

und ein Grund, sich in der Basisausnahmeklasse anzumelden:

  • Gegenwärtig wird das Allheilmittel nur für die Produktion verwendet. Es wäre einfach, es in unseren anderen Umgebungen (Entwicklung, Test) einzuführen, aber das würde einige Anpassungen erfordern, da Fehler je nach Umgebung unterschiedlich behandelt werden, da sie in der Produktion in 404/503 Fehlerseiten übersetzt werden.

Gibt es eine akzeptable Vorgehensweise zum Protokollieren von Ausnahmen?

* Bei der Protokollierung wird zunächst in eine Textdatei geschrieben. Bei bestimmten Ausnahmefällen werden möglicherweise E-Mails gesendet.


Einige Erläuterungen, die durch die Antwort von @ unholysampler veranlasst wurden :

Ich habe es mit einer 2 * 10 ^ 6-Sloc-Codebasis zu tun, mit vielen Sachen von Drittanbietern, über die ich keine Kontrolle habe, und einem Teil des Codes, über den ich Vor-Datums-Ausnahmen in PHP kontrolliere. Und es gibt auch einen beschissenen neuen Code, wir erholen uns von einer langen Zeit intensiven Drucks, in der wir praktisch aufhören mussten zu denken und nur gehackt wurden.

Wir arbeiten aktiv an der Überarbeitung, um alle Inkonsistenzen zu beseitigen und einen vernünftigen Ansatz für die Fehlerbehandlung einzuführen. Dies wird jedoch einige Zeit in Anspruch nehmen. Ich bin mehr daran interessiert, was zu tun ist, bis ich den Punkt erreiche, an dem Fehler angemessen behandelt werden. Ich werde wahrscheinlich irgendwann eine andere Frage zu einer vernünftigen Ausnahmestrategie stellen.

Die Hauptmotivation für die Protokollierung besteht darin, eine E-Mail auf meinem Telefon zu erhalten, wenn in der Produktion etwas Schlimmes passiert. Es ist mir egal, ob Data Dumps riesig werden, wenn doch, muss ich hin und wieder alte löschen.

yannis
quelle

Antworten:

11

Kurz gesagt, Sie sollten das Vorhandensein einer Ausnahme nur dann protokollieren, wenn Sie sie behandeln.

Wenn Sie eine Ausnahme auslösen, liegt dies daran, dass Ihr Code einen Zustand erreicht hat, in dem er nicht ordnungsgemäß ausgeführt werden kann. Indem Sie eine Ausnahme auslösen, geben Sie eine bestimmte Nachricht an Ihr Programm über den aufgetretenen Fehler aus. Sie sollten eine Ausnahme erst dann abfangen, wenn Sie an einem Punkt angelangt sind, an dem sie ordnungsgemäß behandelt werden kann.

Der Code, den Sie als Teil Ihrer Hauptanwendung schreiben, sollte wissen, welche Arten von Ausnahmen ausgelöst werden können und wann sie möglicherweise ausgelöst werden. Wenn Sie mit einer Ausnahme nichts Produktives tun können, fangen Sie sie nicht. Protokollieren Sie keine Ausnahme, bis sie behandelt wird. Nur der Bearbeitungscode weiß, was die Ausnahme im Kontext des Programmflusses bedeutet und wie er darauf reagieren soll. Hier kann es sinnvoll sein, eine Protokollnachricht zu schreiben. Wenn Sie ein Protokollierungsframework verwenden, können Sie eine Protokollstufe für die Nachricht festlegen und diese möglicherweise filtern. Dies funktioniert gut für Ausnahmen, die auftreten können, aber nicht kritisch sind und sauber wiederhergestellt werden können.

Ihre Ausnahme ist Ihre letzte Anstrengung, um zu verhindern, dass Ihr Code zu einem hässlichen Tod führt. Wenn Sie so weit gekommen sind, protokollieren Sie alle Status- und Fehlerinformationen, die Sie können. Dann geben Sie Ihr Bestes, um dem Benutzer mitzuteilen, dass das Programm abstürzt, bevor alles stoppt. Ihr Ziel sollte es sein, diesen Code niemals ausführen zu lassen.

Das Einbetten der Protokollierung in die Basisklasse entspricht nicht den oben genannten Richtlinien. Die Basisklasse weiß nichts über den Status des Codes. (Die Stapelablaufverfolgung zählt nicht, da Sie keinen Code schreiben, der Entscheidungen auf der Grundlage des Parsens trifft.) Die Basisklasse kann nichts tun, um den Schweregrad oder die Behandlung der Ausnahme anzugeben. Sie möchten nicht jedes Mal riesige Daten-Dumps und Stack-Traces, wenn es eine einfache Ausnahme gibt, die Sie sauber handhaben und wiederherstellen können.

unholysampler
quelle
Ich habe einige Klarstellungen zu der Frage hinzugefügt, die durch Ihre Antwort veranlasst wurde. Soweit ich weiß, schlagen Sie auf der praktischen Seite der Frage vor, sich in das Catch-All einzuloggen?
Yannis
1
@YannisRizos: Ja, Sie sollten das Catch-All als ersten Schritt implementieren. Was ich über das Catch-All gesagt habe, war mehr, um sicherzustellen, dass Sie es nicht als normalen Teil Ihres Code-Flows verwenden. Das Implementieren eines nicht behandelten Ausnahmebehandlungsprogramms ist wichtig, da es Ihnen ermöglicht, jedes Mal, wenn Ihr Code etwas Schlechtes tut, viele Informationen abzurufen.
Unholysampler
Gibt es keine Protokollierungs-Frameworks, die das Konzept von "Hier ist eine Reihe von Dingen, die protokolliert werden sollten, wenn sie nicht ersetzt werden" handhaben könnten? Jeder Layer, auf dem eine Ausnahme auftritt, kann die Daten des vorherigen Layers ersetzen, mit der Ausnahme, dass der letzte Protokollbericht nicht überschrieben und somit aufgezeichnet wird, wenn während des Abwickelns des Stapels eine neue Ausnahme ausgelöst wird. Unterstützen keine Frameworks ein solches Muster?
Supercat
3

Wenn Ihre Sprache / Laufzeit es nicht einfach macht, die Quelle der Ausnahme zu bestimmen, gibt es einen Grund, sie beim Auslösen zu protokollieren. C ++ und einige JS-Engines machen die Datei + Zeile oder den Aufrufstapel der Ausnahme nicht verfügbar, wenn Sie sie abgefangen haben. Diese Informationen sind jedoch zum Zeitpunkt der Erstellung der Ausnahme verfügbar.

Unsere Lösung bestand darin, einen Mechanismus bereitzustellen, bei dem die Laufzeitkonfiguration verwendet wurde, um den Typ der Ausnahme zusammen mit einem billigen Stack zu protokollieren, wenn versucht wurde, diese Probleme zu diagnostizieren.

JBRWilkinson
quelle
+1 Das ist definitiv ein guter Fall für das Protokollieren nach dem Wurf ... PHP bietet jedoch einen vollständigen Stack-Trace bei catch, also gehe ich wahrscheinlich in die andere Richtung ...
yannis