wake-up-neo.net

So schreiben Sie eine gute Ausnahmemeldung

Ich mache gerade eine Codeüberprüfung und eines der Dinge, die mir auffallen, ist die Anzahl der Ausnahmen, bei denen die Ausnahmemeldung nur zu wiederholen scheint wo die Ausnahme ist aufgetreten. z.B.

throw new Exception("BulletListControl: CreateChildControls failed.");

Alle drei Punkte in dieser Nachricht kann ich aus dem Rest der Ausnahme herausarbeiten. Ich kenne die Klasse und Methode aus dem Stack-Trace und weiß, dass sie fehlgeschlagen sind (weil ich eine Ausnahme habe).

Ich habe darüber nachgedacht, welche Nachricht ich in Ausnahmemeldungen eingefügt habe. Zuerst erstelle ich aus dem allgemeinen Grund eine Ausnahmeklasse, falls noch keine vorhanden ist (z. B. PropertyNotFoundException - das warum), und dann zeigt die Nachricht beim Auslösen an, was schief gelaufen ist (zB "Die Eigenschaft 'IDontExist' kann auf Node 1234" nicht gefunden werden - das was). Das Wo befindet sich im StackTrace. Das - wann kann im Protokoll landen (falls zutreffend). Das wie ist für den Entwickler zu erarbeiten (und zu beheben).

Haben Sie weitere Tipps zum Auslösen von Ausnahmen? Insbesondere im Hinblick auf das Erstellen neuer Typen und die Ausnahmemeldung.

102
Colin Mackay

Ich werde meine Antwort mehr auf das richten, was nach einer Ausnahme kommt: Wofür ist es gut und wie sollte sich Software verhalten, was sollten Ihre Benutzer mit der Ausnahme tun? Eine großartige Technik, auf die ich zu Beginn meiner Karriere gestoßen bin, bestand darin, Probleme und Fehler immer in drei Teilen zu melden: Kontext, Problem und Lösung. Die Verwendung dieser Disziplin verändert die Fehlerbehandlung enorm und macht die Software für die Bediener erheblich besser.

Hier einige Beispiele.

Context: Saving connection pooling configuration changes to disk.
Problem: Write permission denied on file '/xxx/yyy'.
Solution: Grant write permission to the file.

In diesem Fall weiß der Bediener genau, was zu tun ist und welche Datei betroffen sein muss. Sie wissen auch, dass die Änderungen am Verbindungspooling nicht vorgenommen wurden und wiederholt werden sollten.

Context: Sending email to '[email protected]' regarding 'Blah'.
Problem: SMTP connection refused by server 'mail.xyz.com'.
Solution: Contact the mail server administrator to report a service problem.  The email will be sent later. You may want to tell '[email protected]' about this problem.

Ich schreibe serverseitige Systeme und meine Bediener sind im Allgemeinen technisch versierte First-Line-Unterstützung. Ich würde die Nachrichten für Desktop-Software, die eine andere Zielgruppe hat, aber dieselben Informationen enthält, anders schreiben.

Wenn man diese Technik anwendet, passieren mehrere wundervolle Dinge. Der Softwareentwickler ist häufig am besten in der Lage, die Probleme in seinem eigenen Code zu lösen. Daher ist das Codieren von Lösungen auf diese Weise beim Schreiben des Codes für Endbenutzer von großem Vorteil, die im Nachteil sind, Lösungen zu finden, da ihnen häufig Informationen fehlen was genau die Software tat. Jeder, der jemals eine Oracle-Fehlermeldung gelesen hat, weiß, was ich meine.

Die zweite wunderbare Sache, die Ihnen in den Sinn kommt, ist, wenn Sie versuchen, eine Lösung in Ihrer Ausnahme zu beschreiben, und Sie "Check X und wenn A dann B sonst C" schreiben. Dies ist ein sehr klares und offensichtliches Zeichen dafür, dass Ihre Ausnahme an der falschen Stelle geprüft wird. Sie als Programmierer haben die Fähigkeit, Dinge im Code zu vergleichen. "if" Anweisungen sollten im Code ausgeführt werden. Warum sollten Sie den Benutzer in etwas einbeziehen, das automatisiert werden kann? Es besteht die Möglichkeit, dass es tiefer im Code liegt und jemand die faule Sache gemacht und IOException von einer beliebigen Anzahl von Methoden ausgelöst und potenzielle Fehler von allen in einem Block von aufrufendem Code abgefangen hat, der nicht angemessen beschreiben kann, was schief gelaufen ist, was die spezifischer Kontext ist und wie man ihn behebt. Dies ermutigt Sie, feinere Kornfehler zu schreiben, sie an der richtigen Stelle in Ihrem Code zu erfassen und zu behandeln, damit Sie die Schritte, die der Bediener ausführen sollte, richtig artikulieren können.

In einem Unternehmen hatten wir erstklassige Betreiber, die die Software sehr gut kennen lernten und ein eigenes "Run Book" führten, das unsere Fehlerberichterstattung erweiterte und Lösungen vorschlug. Um dies zu erkennen, begann die Software, in Ausnahmen Wiki-Links zum Laufbuch aufzunehmen, sodass eine grundlegende Erklärung sowie Links zu weiterführenden Diskussionen und Beobachtungen der Bediener im Laufe der Zeit verfügbar waren.

Wenn Sie die Disziplin hatten, diese Technik auszuprobieren, wird es viel offensichtlicher, wie Sie Ihre Ausnahmen im Code benennen sollten, wenn Sie Ihre eigenen erstellen. NonRecoverableConfigurationReadFailedException wird zu einer Abkürzung für das, was Sie dem Bediener ausführlicher beschreiben möchten. Ich mag es, ausführlich zu sein, und ich denke, dass dies für den nächsten Entwickler, der meinen Code berührt, einfacher zu interpretieren ist.

72
Sir Wobin

In diese neuere Frage habe ich darauf hingewiesen, dass Ausnahmen überhaupt keine Nachricht enthalten sollten. Meiner Meinung nach ist die Tatsache, dass sie dies tun, ein großes Missverständnis. Was ich vorschlage, ist das

Die "Nachricht" der Ausnahme ist der (vollständig qualifizierte) Klassenname der Ausnahme.

Eine Ausnahme sollte in ihren eigenen Mitgliedsvariablen so viele Details wie möglich darüber enthalten, was genau passiert ist. Beispielsweise sollte ein IndexOutOfRangeException den Indexwert enthalten, der als ungültig befunden wurde, sowie die oberen und unteren Werte, die zum Zeitpunkt des Auslösens der Ausnahme gültig waren. Auf diese Weise können Sie mithilfe von Reflection automatisch eine Nachricht erstellen, die wie folgt lautet: IndexOutOfRangeException: index = -1; min=0; max=5. Dies sollte zusammen mit dem Stack-Trace alle objektiven Informationen enthalten, die Sie zur Behebung des Problems benötigen. Das Formatieren in eine hübsche Nachricht wie "Index -1 war nicht zwischen 0 und 5" bringt keinen Wert.

In Ihrem speziellen Beispiel enthält die Klasse NodePropertyNotFoundException den Namen der Eigenschaft, die nicht gefunden wurde, und einen Verweis auf den Knoten, der die Eigenschaft nicht enthielt. Dies ist wichtig: Es sollte nicht den Namen des Knotens enthalten. Es sollte einen Verweis auf den tatsächlichen Knoten enthalten. In Ihrem speziellen Fall ist dies möglicherweise nicht erforderlich, aber es ist eine Grundsatzfrage und eine bevorzugte Denkweise: Das Hauptanliegen bei der Erstellung einer Ausnahme ist, dass sie von Code verwendet werden kann, der sie möglicherweise abfängt. Die Benutzerfreundlichkeit durch den Menschen ist ein wichtiges, aber nur zweitrangiges Anliegen.

Dies behebt die sehr frustrierende Situation, die Sie möglicherweise zu einem bestimmten Zeitpunkt in Ihrer Karriere erlebt haben und in der Sie möglicherweise eine Ausnahme festgestellt haben, die wichtige Informationen darüber enthält, was im Nachrichtentext passiert ist, jedoch nicht in den Mitgliedsvariablen Ich musste den Text mit einer Zeichenfolge analysieren, um herauszufinden, was passiert ist, in der Hoffnung, dass der Nachrichtentext in zukünftigen Versionen der zugrunde liegenden Ebene gleich bleibt, und beten, dass der Nachrichtentext in Ihrem Programm nicht in einer Fremdsprache vorliegt in anderen Ländern laufen.

Da der Klassenname der Ausnahme die Nachricht der Ausnahme ist (und die Mitgliedsvariablen der Ausnahme die spezifischen Details sind), bedeutet dies natürlich, dass Sie viele, viele verschiedene Ausnahmen benötigen, um alle verschiedenen Nachrichten zu übermitteln, und das ist gut.

Jetzt stoßen wir manchmal beim Schreiben von Code auf eine fehlerhafte Situation, für die wir nur schnell eine throw - Anweisung codieren und unseren Code schreiben möchten, anstatt unterbrechen zu müssen, was wir tun, um einen neuen zu erstellen Ausnahmeklasse, damit wir es genau dort werfen können. In diesen Fällen habe ich eine GenericException -Klasse, die tatsächlich eine Zeichenfolgennachricht als Konstruktionszeitparameter akzeptiert, aber der Konstruktor dieser Ausnahmeklasse ist mit einem großen, riesigen, hellen, violetten FIXME XXX TODO Kommentar, der besagt, dass jede einzelne Instanziierung dieser Klasse durch eine Instanziierung einer spezielleren Ausnahmeklasse ersetzt werden muss, bevor das Softwaresystem freigegeben wird, vorzugsweise bevor der Code festgeschrieben wird.

23
Mike Nakis

In der Regel ist eine Ausnahme sollte Entwicklern helfen, die Ursache zu bestimmen, indem nützliche Informationen (erwartete Werte, tatsächlicher Wert, mögliche Ursachen/Lösung usw.) angegeben werden.

Neue Ausnahmetypen sollten erstellt werden, wenn keiner der integrierten Typen sinnvoll ist.. Mit einem bestimmten Typ können andere Entwickler eine bestimmte Ausnahme abfangen und behandeln. Wenn der Entwickler wissen würde, wie er mit Ihrer Ausnahme umgehen soll, der Typ jedoch Exception ist, kann er sie nicht richtig behandeln.

13
mbillard

In .NET niemals throw new Exception("...") (wie der Autor der Frage gezeigt hat). Die Ausnahme ist der Root-Ausnahmetyp und sollte niemals direkt ausgelöst werden. Wirf stattdessen einen der abgeleiteten .NET-Ausnahmetypen aus oder erstelle deine eigene benutzerdefinierte Ausnahme, die von Exception (oder einem anderen Ausnahmetyp) abgeleitet ist.

Warum nicht Exception werfen? Weil das Auslösen von Exception Ihre Ausnahme nicht beschreibt und Ihren aufrufenden Code dazu zwingt, Code wie catch(Exception ex) { ... } zu schreiben, was im Allgemeinen keine gute Sache ist! :-).

4
bytedev

Die Dinge, nach denen Sie suchen möchten, um der Ausnahme "hinzuzufügen", sind diejenigen Datenelemente, die der Ausnahme oder der Stapelverfolgung nicht inhärent sind. Ob diese Teil der "Nachricht" sind oder bei der Protokollierung angehängt werden müssen, ist eine interessante Frage.

Wie Sie bereits bemerkt haben, sagt Ihnen die Ausnahme wahrscheinlich was, der Stacktrace sagt Ihnen wahrscheinlich, wo, aber das "Warum" kann mehr involviert sein (sollte man hoffen), als nur ein oder zwei Zeilen zu betrachten und zu sagen " doh! Natürlich ". Dies gilt umso mehr, wenn Fehler im Produktionscode protokolliert werden. Ich bin allzu oft von schlechten Daten gebissen worden, die in ein Live-System gelangt sind, das in unseren Testsystemen nicht vorhanden ist. Etwas so Einfaches wie das Wissen um die ID des Datensatzes in der Datenbank, der den Fehler verursacht (oder dazu beiträgt), kann erheblich Zeit sparen.

Also ... Entweder aufgelistet oder für .NET zur protokollierten Ausnahmedatenerfassung hinzugefügt (vgl. @Plip!):

  • Parameter (dies kann etwas interessant werden - Sie können der Datenerfassung nichts hinzufügen, wenn sie nicht serialisiert wird und manchmal ein einzelner Parameter überraschend komplex sein kann.)
  • Die zusätzlichen Daten, die von ADO.NET oder Linq an SQL oder ähnliches zurückgegeben werden (dies kann auch etwas interessant werden!).
  • Was auch immer sonst nicht offensichtlich sein mag.

Einige Dinge wissen Sie natürlich erst, wenn Sie sie nicht in Ihrem ersten Fehlerbericht/Protokoll haben. Einige Dinge, von denen Sie nicht wissen, dass Sie sie bekommen können, bis Sie feststellen, dass Sie sie brauchen.

2
Murph

Was sind Ausnahmen für ?

(1) Dem Benutzer mitteilen, dass etwas schief gelaufen ist?

Dies sollte ein letzter Ausweg sein, da Ihr Code für sie intervenieren und ihnen etwas "Schöneres" als eine Ausnahme zeigen sollte.

Die "Fehler" -Nachricht sollte klar und prägnant angeben , was schief gelaufen ist und was der Benutzer gegebenenfalls tun kann ) Wiederherstellen von der Fehlerbedingung.

z.B. "Bitte drücken Sie diese Taste nicht noch einmal"

(2) Einem Entwickler mitteilen, wann ein Fehler aufgetreten ist?

Auf diese Weise melden Sie sich für die nachfolgende Analyse in einer Datei an.
Der Stack Trace teilt dem Entwickler mit, wo der Code gebrochen ist. Die Nachricht sollte erneut anzeigen, was schief gelaufen ist.

(3) Einem Ausnahmehandler (d. H. Code) mitteilen, dass ein Fehler aufgetreten ist?

Der Typ der Ausnahme entscheidet, welcher Ausnahmebehandler ihn anzeigen darf, und der Eigenschaften, der für das Ausnahmeobjekt definiert ist, ermöglicht es dem Handler, damit umzugehen.

Die Nachricht der Ausnahme ist völlig irrelevant .

0
Phill W.