Wer sollte Exception.Message lesen, wenn überhaupt?

27

Soll ich beim Entwerfen von Ausnahmen Nachrichten schreiben, die ein Benutzer oder Entwickler verstehen sollte? Wer sollte eigentlich der Leser von Ausnahmemeldungen sein?

Ich finde, dass Ausnahmemeldungen überhaupt nicht nützlich sind und ich habe immer Schwierigkeiten, sie zu schreiben. Konventionell sollte uns der Typ der Ausnahme bereits sagen, warum etwas nicht funktioniert hat, und benutzerdefinierte Eigenschaften könnten noch mehr Informationen wie Dateinamen, Indizes, Schlüssel usw. hinzufügen. Warum sollte man sie also in der Nachricht selbst wiederholen? Eine automatisch generierte Nachricht könnte auch funktionieren und alles, was sie enthalten musste, ist der Name der Ausnahme mit einer Liste zusätzlicher Eigenschaften. Dies wäre genauso nützlich wie ein handgeschriebener Text.

Wäre es nicht besser, überhaupt keine Nachrichten zu schreiben, sondern spezielle Exception-Renderer zu verwenden, die sich darum kümmern, sinnvolle Nachrichten in verschiedenen Sprachen zu erstellen, anstatt sie fest im Code zu codieren?


Ich wurde gefragt, ob eine dieser Fragen eine Antwort auf meine Frage liefert:

Ich habe beide gelesen und war mit ihren Antworten nicht zufrieden. Sie sprechen allgemein über Benutzer und konzentrieren sich eher auf den Inhalt der Nachricht selbst als auf den Adressaten, und es stellt sich heraus, dass es mindestens zwei von ihnen geben kann: den Endbenutzer und den Entwickler. Ich weiß nie, mit wem ich sprechen soll, wenn ich Ausnahmemeldungen schreibe.

Ich denke sogar, dass die berühmte Nachricht überhaupt keinen wirklichen Wert hat, da sie nur den Namen des Ausnahmetyps in anderen Worten wiederholt. Warum also überhaupt die Mühe machen, sie zu schreiben? Ich kann sie perfekt automatisch generieren.

Mir fehlt bei Ausnahmebotschaften eine Differenzierung der Leser. Für eine perfekte Ausnahme müssen mindestens zwei Versionen der Nachricht bereitgestellt werden: eine für den Endbenutzer und eine für den Entwickler. Nur eine Nachricht zu nennen, ist zu allgemein. Eine Entwicklernachricht sollte dann in Englisch verfasst werden, aber die Nachricht des Endbenutzers muss möglicherweise in andere Sprachen übersetzt werden. Es ist nicht möglich, all dies mit nur einer Nachricht zu erreichen, daher müsste eine Ausnahme der Endbenutzernachricht eine Kennung geben, die, wie ich gerade sagte, möglicherweise in verschiedenen Sprachen verfügbar ist.

Wenn ich alle anderen verknüpften Fragen lese, habe ich den Eindruck, dass eine Ausnahmebedingungsnachricht tatsächlich von einem Endbenutzer und nicht von einem Entwickler gelesen werden soll.

t3chb0t
quelle
4
Zwei weitere Fragen zu Programmierern kommen mir in den Sinn. Ich bin mir nicht sicher, ob es sich um Duplikate handelt oder nicht, aber ich denke, Sie sollten sie lesen und vielleicht werden sie einige oder sogar alle Ihre Probleme angehen: Wie schreibe ich eine gute Ausnahmemeldung und warum enthalten viele Ausnahmemeldungen keine nützlichen Details? ?
Thomas Owens
2
@ThomasOwens Ich habe beide gelesen, aber sie richten sich nicht an den Empfänger einer Ausnahmemeldung. Sie sprechen über Benutzer im Allgemeinen, aber wer ist er? Ist es der Benutzer der Software, der jetzt eine Vorstellung von der Programmierung hat, oder der Entwickler, der analysieren sollte, was schief gelaufen ist? Ich bin mir nie sicher, wen ich beim Schreiben von Nachrichten ansprechen soll.
t3chb0t
2
Ich denke, eine gute Antwort hier könnte sich auf eine oder beide dieser Fragen beziehen, aber sie sind alles andere als doppelt.
Leichtigkeit Rennen mit Monica
1
Warum um alles in der Welt wurde dies als Duplikat geschlossen? Selbst wenn es ein Duplikat dieser anderen Frage wäre (was es nicht ist), hat diese eindeutig eine bessere Antwort, es sollte die andere sein, die als Duplikat markiert ist.
Ben Aaronson
2
@ MichaelT Ich sehe immer noch nicht wirklich viel Überlappung dort, es sei denn, Sie setzen voraus, dass Ausnahmenachrichten für Benutzer sind
Ben Aaronson

Antworten:

46

Diese Nachrichten sind für andere Entwickler

Es wird erwartet, dass diese Nachrichten von Entwicklern gelesen werden , um sie beim Debuggen der Anwendung zu unterstützen. Dies kann zwei Formen annehmen:

  • Aktives Debuggen. Sie führen tatsächlich einen Debugger aus, während Sie Code schreiben und versuchen, herauszufinden, was passiert. In diesem Zusammenhang hilft Ihnen eine hilfreiche Ausnahme, indem Sie leicht nachvollziehen können, was falsch läuft, oder eine Problemumgehung vorschlagen (obwohl dies optional ist).

  • Passives Debuggen. Der Code wird in der Produktion ausgeführt und schlägt fehl. Die Ausnahme wird protokolliert, aber Sie erhalten nur die Nachricht und den Stack-Trace. In diesem Zusammenhang hilft Ihnen eine hilfreiche Ausnahmemeldung, den Fehler schnell zu lokalisieren.

    Da diese Nachrichten häufig protokolliert werden, sollten Sie dort auch keine vertraulichen Informationen (z. B. private Schlüssel oder Kennwörter) angeben, auch wenn das Debuggen der App hilfreich sein könnte.

Beispielsweise ist der IOSecurityExceptionTyp einer Ausnahme, die beim Schreiben einer Datei ausgelöst wird, für das Problem nicht eindeutig: Liegt es daran, dass wir keine Zugriffsberechtigung für eine Datei haben? Oder können wir es vielleicht lesen, aber nicht schreiben? Oder existiert die Datei nicht und wir haben keine Berechtigung, Dateien dort zu erstellen? Oder vielleicht ist es gesperrt (hoffentlich ist der Typ der Ausnahme in diesem Fall anders, aber in der Praxis können Typen manchmal kryptisch sein). Oder verhindert Code Access Security, dass wir E / A-Vorgänge ausführen können?

Stattdessen:

IOSecurityException: Die Datei wurde gefunden, die Berechtigung zum Lesen des Inhalts wurde jedoch verweigert.

ist viel expliziter. Hier wissen wir sofort, dass die Berechtigungen für das Verzeichnis richtig eingestellt sind (andernfalls können wir nicht wissen, dass die Datei vorhanden ist), aber Berechtigungen auf Dateiebene sind problematisch.

Dies bedeutet auch, dass Sie die Nachricht leer lassen können , wenn Sie keine zusätzlichen Informationen angeben können, die nicht bereits im Typ der Ausnahme enthalten sind. DivisionByZeroExceptionist ein gutes Beispiel, wo die Nachricht redundant ist. Die Tatsache, dass Sie in den meisten Sprachen eine Ausnahme ohne Angabe der Nachricht auslösen können, hat einen anderen Grund: Entweder weil die Standardnachricht bereits verfügbar ist oder weil sie bei Bedarf später generiert wird (und die Generierung dieser Nachricht ist in den Ausnahmetyp eingeschlossen, was durchaus Sinn macht ( "OOPly" ).

Beachten Sie, dass einige Nachrichten aus technischen Gründen (häufig aus Gründen der Leistung) wesentlich kryptischer sind, als sie sollten. .NET's NullReferenceException:

Der Objektverweis wurde nicht auf eine Instanz eines Objekts festgelegt.

ist ein hervorragendes Beispiel für eine Nachricht, die nicht hilfreich ist. Eine hilfreiche Nachricht wäre:

Die productObjektreferenz wurde beim Aufrufen nicht auf eine Instanz eines Objekts festgelegt product.Price.

Diese Nachrichten sind nicht für Endbenutzer!

Es wird nicht erwartet, dass Endbenutzer Ausnahmemeldungen sehen. Noch nie. Obwohl einige Entwickler diese Nachrichten den Benutzern zeigen, führt dies zu einer schlechten Benutzererfahrung und Frustration. Nachrichten wie:

Der Objektverweis wurde nicht auf eine Instanz eines Objekts festgelegt.

Für den Endverbraucher bedeutet dies absolut nichts und sollte unter allen Umständen vermieden werden.

Das schlimmste Szenario ist ein globaler Try / Catch, der die Ausnahme für den Benutzer auslöst und die App beendet. Eine Anwendung, die sich um ihre Benutzer kümmert:

  • Behandelt Ausnahmen an erster Stelle. Die meisten können gehandhabt werden, ohne den Benutzer zu stören. Netzwerk ist ausgefallen? Warum nicht ein paar Sekunden warten und es erneut versuchen?

  • Verhindert, dass ein Benutzer die Anwendung in einen Ausnahmefall führt. Wenn Sie den Benutzer auffordern, zwei Zahlen einzugeben und die erste durch die zweite zu teilen, warum sollten Sie den Benutzer im zweiten Fall die Eingabe von Null zulassen, um ihm einige Sekunden später die Schuld zu geben ? Wie wäre es, wenn Sie das Textfeld rot markieren (mit einem hilfreichen Tooltipp, der besagt, dass die Zahl nicht Null sein sollte) und die Schaltfläche zum Überprüfen deaktivieren, bis das Feld rot bleibt?

  • Fordert einen Benutzer auf, eine Aktion in einer Form auszuführen, die kein Fehler ist. Es sind nicht genügend Berechtigungen vorhanden, um auf eine Datei zuzugreifen. Bitten Sie den Benutzer, Administratorberechtigungen zu erteilen, oder wählen Sie eine andere Datei aus.

  • Wenn nichts anderes funktioniert, wird ein hilfreicher, freundlicher Fehler angezeigt, der speziell geschrieben wurde, um die Frustration des Benutzers zu verringern, dem Benutzer zu helfen, zu verstehen, was schief gelaufen ist, und schließlich das Problem zu lösen, und ihm zu helfen, den Fehler in Zukunft zu verhindern (falls zutreffend).

    In Ihrer Frage haben Sie vorgeschlagen, zwei Nachrichten in einer Ausnahme zu haben: eine technische für Entwickler und eine für Endbenutzer. Obwohl dies in einigen geringfügigen Fällen ein gültiger Vorschlag ist, werden die meisten Ausnahmen auf einer Ebene erstellt, auf der es unmöglich ist, eine aussagekräftige Nachricht für die Benutzer zu erstellen. DivisionByZeroExceptionStellen Sie sich vor, wir könnten die Ausnahme nicht verhindern und können sie nicht selbst handhaben. Weiß das Framework (da es sich um das Framework und nicht um den Geschäftscode handelt, der die Ausnahme auslöst), was für einen Benutzer eine hilfreiche Nachricht sein wird, wenn die Aufteilung erfolgt? Absolut nicht:

    Die Division durch Null ist aufgetreten. [OKAY]

    Stattdessen kann man es die Ausnahme auslösen lassen und sie dann auf einer höheren Ebene abfangen, auf der wir den Geschäftskontext kannten, und dementsprechend handeln, um dem Endbenutzer tatsächlich zu helfen. Auf diese Weise wird Folgendes angezeigt:

    Das Feld D13 kann nicht den gleichen Wert wie das Feld E6 haben, da die Subtraktion dieser Werte als Divisor verwendet wird. [OKAY]

    oder vielleicht:

    Die vom ATP-Service gemeldeten Werte stimmen nicht mit den lokalen Daten überein. Dies kann daran liegen, dass die lokalen Daten nicht synchron sind. Möchten Sie die Versandinformationen synchronisieren und erneut versuchen? [Ja Nein]

Diese Nachrichten sind nicht zum Parsen gedacht

Es wird nicht erwartet, dass Ausnahmenachrichten analysiert oder programmgesteuert verwendet werden. Wenn Sie der Meinung sind, dass der Anrufer zusätzliche Informationen benötigen kann, fügen Sie diese neben der Nachricht in die Ausnahme ein. Dies ist wichtig, da die Nachricht ohne vorherige Ankündigung geändert werden kann. Type ist ein Teil der Schnittstelle, die Meldung jedoch nicht: Verlassen Sie sich bei der Ausnahmebehandlung niemals darauf.

Stellen Sie sich die Ausnahmemeldung vor:

Die Verbindung zum Caching-Server wurde unterbrochen, nachdem 500 ms gewartet wurden. Erhöhen Sie entweder das Zeitlimit oder überprüfen Sie die Leistungsüberwachung, um einen Rückgang der Serverleistung festzustellen. Die durchschnittliche Wartezeit für den Caching-Server betrug 6 ms. für den letzten Monat 4 ms. für die letzte Woche und 377 ms. für die letzte Stunde.

Sie möchten die Werte "500", "6", "4" und "377" extrahieren. Denken Sie ein wenig über den Ansatz nach, den Sie für das Parsen verwenden, und lesen Sie dann erst weiter.

Sie haben die Idee? Groß.

Nun entdeckte der ursprüngliche Entwickler einen Tippfehler:

Connecting to the caching sever timed out after waiting [...]

sollte sein:

                            ↓
Connecting to the caching server timed out after waiting [...]

Darüber hinaus ist der Entwickler der Ansicht, dass Monat / Woche / eine Stunde nicht besonders relevant sind, weshalb er auch eine zusätzliche Änderung vornimmt:

Die durchschnittliche Wartezeit für den Caching-Server betrug 6 ms. für den letzten Monat 5 ms. für die letzten 24 Stunden und 377 ms. für die letzte Stunde.

Was passiert mit deinem Parsen?

Anstatt zu analysieren, könnten Sie Ausnahmeeigenschaften verwenden (die enthalten können, was immer Sie wollen, sobald die Daten serialisiert werden können):

{
    message: "Connecting to the caching [...]",
    properties: {
        "timeout": 500,
        "statistics": [
            { "timespan": 1, "unit": "month", "average-timeout": 6 },
            { "timespan": 7, "unit": "day", "average-timeout": 4 },
            { "timespan": 1, "unit": "hour", "average-timeout": 377 },
        ]
    }
}

Wie einfach ist es jetzt, diese Daten zu verwenden?

Manchmal (z. B. in .NET) kann die Nachricht sogar in die Sprache des Benutzers übersetzt werden (IMHO ist die Übersetzung dieser Nachrichten absolut falsch, da erwartet wird, dass jeder Entwickler in der Lage ist, auf Englisch zu lesen). Das Parsen solcher Nachrichten ist nahezu unmöglich.

Arseni Mourzenko
quelle
2
Guter Punkt über den Endbenutzer. Ich möchte hinzufügen, dass der Endbenutzer auch aus Sicherheitsgründen keine Ausnahmemeldungen sehen sollte. Die genaue Art eines Fehlers einem Nicht-Laien-Benutzer bekannt zu machen, kann einen Exploit darstellen. Der Endbenutzer sollte den Fehler nur im Zusammenhang mit seiner Tätigkeit kennen.
Neil
1
@Neil: das ist ein bisschen zu theoretisch. In der Praxis überwiegt der Vorteil, die Protokolle vor dem Benutzer zu verbergen, durch die Komplexität der Online-Protokollierung. Den benutzerfreundlichen Aspekt nicht mitgerechnet. Stellen Sie sich vor, Sie installieren Visual Studio auf Ihrer neuen Windows-VM. Plötzlich schlägt die Installation fehl. Möchten Sie lieber selbst in die Protokolle gehen und das Problem diagnostizieren (mithilfe von Stack Exchange und Blogs) oder warten, bis Microsoft das Problem behebt und einen Hotfix veröffentlicht?
Arseni Mourzenko
4
Ich denke, jeder ist sich bewusst, dass die Meldung "Objektreferenz ..." nicht hilfreich ist. Die relevante Frage ist jedoch, welchen Maschinencode der Jitter erzeugen soll, der die gewünschte Nachricht erzeugt. Wenn Sie diese Übung durchführen, stellen Sie schnell fest, dass die Nachricht nicht einfach generiert werden kann, ohne dass enorme Leistungskosten für alle nicht außergewöhnlichen Fälle anfallen . Der Vorteil, die Arbeit eines sorglosen Entwicklers ein wenig zu erleichtern, wird vom Leistungsverlust für den Endbenutzer nicht bezahlt.
Eric Lippert
7
In Bezug auf Sicherheitsausnahmen: Je weniger Informationen in der Ausnahme, desto besser. Meine Lieblingsanekdote ist, dass in einer Vor-Beta-Version von .NET 1.0 eine Ausnahmemeldung wie "Sie haben keine Berechtigung, den Namen der Datei C: \ foo.txt zu bestimmen" angezeigt wird. Toll, danke für die detaillierte Ausnahmemeldung!
Eric Lippert
2
Ich bin nicht einverstanden, dem Endbenutzer niemals eine detaillierte Fehlermeldung zu zeigen. Es ist mir passiert viele Male , dass ich eine kryptische Fehlermeldung aus starten mein Spiel / Software verhindert mich bekommen, aber wenn ich es google ich herausfinden, gibt es eine einfache Abhilfe. Ohne diese Fehlermeldung gibt es keine Möglichkeit, die Problemumgehung zu finden. Vielleicht ist es besser, die Details einfach unter einem "Mehr Details" -Button auszublenden?
BlueRaja - Danny Pflughoeft
2

Die Antwort darauf hängt ganz von der Ausnahme ab.

Die wichtigste Frage lautet: "Wer kann das Problem beheben?" Wenn die Ausnahme durch einen Dateisystemfehler beim Öffnen einer Datei verursacht wird, ist es möglicherweise sinnvoll, diese Meldung dem Endbenutzer zu übermitteln. Die Nachricht enthält möglicherweise weitere Informationen zum Beheben des Fehlers. Ich kann mir einen konkreten Fall in meiner Arbeit vorstellen, in dem ich ein Pluginsystem hatte, das DLLs geladen hat. Wenn ein Benutzer in der Befehlszeile einen Tippfehler machte, um das falsche Plugin zu laden, enthielt die zugrunde liegende Systemfehlermeldung tatsächlich nützliche Informationen, damit er das Problem beheben konnte.

Meistens kann jedoch eine nicht erfasste Ausnahme nicht vom Benutzer behoben werden. In den meisten Fällen müssen Änderungen am Code vorgenommen werden. In diesen Fällen ist der Verbraucher der Nachricht eindeutig ein Entwickler.

Der Fall abgefangener Ausnahmen ist komplizierter, da Sie die Möglichkeit haben, die Ausnahme ordnungsgemäß zu verarbeiten und eine freundlichere auszulösen. Eine von mir geschriebene Skriptsprache warf Ausnahmen auf, deren Botschaft eindeutig für einen Entwickler bestimmt war. Als sich der Stapel auflöste, fügte ich jedoch benutzerlesbare Stapelspuren aus der Skriptsprache heraus hinzu. Meine Benutzer konnten die Nachricht nur sehr selten fälschen, aber sie konnten sich den Stack-Trace in ihrem Skript ansehen und herausfinden, was in 95% der Fälle passiert ist. Für die restlichen 5% hatten wir, als sie mich anriefen, nicht nur einen Stack-Trace, sondern eine Nachricht, die für Entwickler bereit war, um herauszufinden, was los war.

Insgesamt behandle ich die Ausnahmebedingungsnachricht als unbekannten Inhalt. Jemand weiter oben, der mehr über die Implementierungen wusste, hat meiner Software eine Ausnahme gemacht. Manchmal habe ich die Vermutung, dass unbekannte Inhalte für einen Endbenutzer nützlich sind, manchmal nicht.

Cort Ammon - Setzen Sie Monica wieder ein
quelle
1
"Wenn die Ausnahme durch einen Dateisystemfehler beim Öffnen einer Datei verursacht wird, ist es möglicherweise sinnvoll, diese Meldung dem Endbenutzer zu übermitteln." Wenn Sie den Dateisystemfehler auslösen, kennen Sie den Kontext jedoch nicht: Es kann sich um die Benutzeraktion handeln oder um etwas völlig Unabhängiges (z. B. das Erstellen einer Sicherungskopie einer Konfiguration durch Ihre App, ohne dass der Benutzer dies bemerkt). Dies impliziert, dass Sie einen Try / Catch-Block haben, der eine Fehlermeldung anzeigt , die sich stark von der direkten Anzeige der Ausnahmemeldung unterscheidet .
Arseni Mourzenko
@MainMa Möglicherweise kennen Sie den Kontext nicht explizit, aber es gibt unzählige Hinweise. Wenn beispielsweise gerade eine Datei geöffnet wird und die Ausnahme "IOError: Berechtigung verweigert" lautet, besteht eine vernünftige Chance, dass der Benutzer 2 und 2 zusammenfügt und die betreffende Datei herausfindet. Mein Lieblingseditor tut dies - er gibt nur den Ausnahmetext in einem Dialogfeld ohne Flusen aus. Wenn ich andererseits meine Anwendung geladen habe und beim Laden einer Reihe von Bildern die Berechtigung verweigert wurde, ist es weitaus weniger vernünftig anzunehmen, dass der Benutzer mit den Informationen etwas anfangen kann.
Cort Ammon - Reinstate Monica
Ich habe noch nie einen Wert darin gesehen, einem Benutzer eine NullReferenceException anzuzeigen, außer dass die bloße Anzeige dieser Meldung zeigt, dass Ihre Software keine Ahnung hatte, wie sie wiederhergestellt werden kann! Diese Ausnahmemeldung ist für einen Endbenutzer niemals nützlich.
Cort Ammon - Reinstate Monica