Autor Beitrag
exemadorleos
Hält's aus hier
Beiträge: 6



BeitragVerfasst: So 03.12.17 21:00 
Hallo Community,

ich bin Programmieranfänger und arbeite zur Zeit an meinem ersten Softwareprojekt. Meine Architektur kennt mittlerweile Data- und BusinessLayer, hat eine Präsentationsschicht und für die Netzwerkkommunikation mit REST und WebSockets habe ich weitere Komponenten zugefügt. Mein Gerüst arbeitet also schon recht modular, ich kann Basistechnologien austauschen ohne das andere Module geändert werden müssen. Worauf ich jedoch keine Antwort finde ist der Umgang mit Exceptions.

In den Methoden gehe ich jedes Mal nach demselben aufwändigen Muster vor:


ausblenden C#-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
public void methode(param) {
  string errMsg="";
  if (condA) errMsg="Fehlerbeschreibeung1);
  else if (condB) errMsg="
Fehlerbeschreibeung2);
  else {
    // mache die Arbeit
    // ggf. auch errMsg="Anderer Fehler"; + break;
  }

// Endauswertung
  if (errMsg!="") {
     LoggingKomponente.logError(errMsg);
     throw new Exception (errMsg);
  }
}


Für mich ergeben sich 2 grundlegende Probleme:
1.) DIE KLEINE FRAGE: bevor ich auch nur eine Zeile Code implementieren kann, muss ich jedes Mal diesen Block einfügen. Das Logging und das Werfen der Exception erfolgt jeweils in 2 Zeilen Code. Kann es richtig sein, dies in jeder Methode zu wiederholen? Wie kann man alternativ einen möglichen Fehler genau eingrenzen können, wenn ich nicht jedes Mal an der Stelle wo er auftritt den Logeintrag schreibe?

2.) DIE WICHTIGERE FRAGE: Selbst wenn ich dies so belasse - mir wird einfach nicht klar, was ich mit den Exceptions innerhalb meiner Architektur mache. Fängt der Controller alles ein und entscheidet in jedem Fall einzeln, ob er den Fehler eindämmen kann oder die Anwendung beendet werden muss? Gebe ich einfach jeden erdenklichen Fehler als MessageBox aus? Ist es überhaupt sinnvoll in jeder kritischen Methode spezielle Fehlertexte zu schreiben oder lässt man auf auf unteren Ebene etc. generell alles weg und fängt in höheren Ebenen einfach alle exceptions ein?

Ich habe viel über Archtiektur allgemein, über Patterns etc. gelesen - bzgl. dieses Handlings der exceptions habe ich jedoch keine brauchbare Aussage gefunden.

Moderiert von user profile iconChristian S.: C#-Tags hinzugefügt
Palladin007
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starofftopic star
Beiträge: 1282
Erhaltene Danke: 182

Windows 11 x64 Pro
C# (Visual Studio Preview)
BeitragVerfasst: Mo 04.12.17 02:22 
Erstes Softwareprojekt? Ich hab von Anfängern gelesen, die von Begriffen wie BusinessLayer oder REST höchstens mal gelesen haben :D
Du arbeitest mit C#? Sieht zumindest so aus, könnte aber auch Java sein.
Aber schau dir doch mal die Code-Tags von der Entwickler-Ecke an, die machen das Lesen viel einfacher ;)

Vorweg: Bei dem Thema hat jeder seine eigene Meinung, das ist nur Meine ;)
Außerdem kann sich das sinnvolle Verhalten je nach Situation und Projekt stark ändern.
In den meisten Fällen ist Erfahrung wichtiger als Bilderbuch-Wissen, oft liegt das aber relativ nahe beieinander.
Fehlt die Erfahrung, kann es sinnvoll sein, die Fehler bewusst zu machen um daraus zu lernen. Ich bin aber faul, ich arbeite dann lieber nach dem Bilderbuch, das spart Fehlschläge :D


Weil Du mit Error-Messages herum hantierst:

Bau dir kein eigenes Exception-Handling!!!
Es gibt immer wieder Fälle, wo jemand von irgendeiner alten Sprache kommt und Fehler-Codes einführen will.
Da gibt's dann auf einmal zig Methoden, die ein int zurück geben und der Code muss erst mal mit einer langen Liste von Fehler-Codes abgeglichen werden um zu wissen, wie man reagieren sollte.
Auch strings sind kein guter Exception-Ersatz.
Für die meisten modernen Sprachen und dazu gehörigen Frameworks (C# und .NET) sollte es ein integriertes Exception-Handling geben.
Nutze das, damit lebst Du viel sicherer ;)

Was Du mit Exceptions tun sollst: Erst einmal gar nichts.
Eine Exception bezeichnen eine Ausnahme, die sollte prinzipiell gar nicht erst auftreten.


Welche Arten von Exceptions sollte man behandeln?

Wenn es um Exceptions geht, die Du selber wirfst:
Versuche sie vorher zu verhindern, z.B. mit Validierung der Nutzereingaben.
Wenn eine Exception nicht sinnvoll validiert werden kann, dann fange sie ab und zeige eine verständliche Meldung an, nicht einfach nur Exception.ToString()
Vergiss aber nicht, dem Nutzer z.B. bei der Validierung den Grund mitzuteilen, sonst wird die Validierung schnell als Fehler empfunden.

Wenn es um Exceptions geht, die nicht von dir ausgehen, aber die Du prüfen kannst:
Prüfe immer vorher, bevor Du etwas ausführst. Findet ein Datei-Zugriff statt, dann prüfe, ob der möglich ist und zeige das entsprechend an.
Oder beobachte die Bedingungen permanent und deaktiviere z.B. Buttons, wenn Du voraussagen kannst, dass die Funktion nicht klappen würde.
Auch hier solltest Du dem Nutzer mitteilen, warum etwas nicht geht. Tooltips bieten sich da an.

Wenn es um Exceptions geht, die nicht von dir ausgehen und die Du nicht prüfen kannst:
Schau dir an, welche Exceptions in einer Situation fliegen können und entscheide separat, welche Du fängst und welche nicht.
Netzwerkverbindungen lassen sich z.B. nur sehr schwer im Voraus prüfen, da fliegt schnell mal eine Exception, wenn kurz die Verbindung weg ist.
In dem Fall musst Du immer im Einzelfall entscheiden.
Z.B. kann so eine Netzwerk-bezogene Exception zum Crash der Anwendung führen und auch sinnvoll sein.
Oder Du fängst sie ab, startest ab dem Zeitpunkt einen ständigen Ping um zu prüfen, wann die Verbindung wieder da ist und deaktivierst solange alle Funktionen.
Oder Du fängst sie nur ab und teilst dem Nutzer mit, was schief gelaufen ist.

Du hast also viele verschiedene Exception-Typen.
Wirfst Du eine selber, solltest Du darauf achten, dass der Exception-Typ auch zum Fehler passt, das erleichtert später das Fangen der Exception je nach Typ.
.NET hat da selber einige Exception-Typen mit geliefert, es kann aber auch sinnvoll sein, Eigene zu definieren.


Wo sollte man die Exception behandeln?

Grundsätzlich gilt: fange eine Exception nur dort, wo Du sie auch behandeln kannst.
Wenn Du z.B. eine Klasse hast, die für die Netzwerk-Kommunikation zuständig ist, indem sie Eingang und Ausgang entsprechend in Objekte übersetzt, dann nützt es dir nichts, wenn diese Klasse Netzwerkbezogene Exceptions fängt.
Die Präsentationsschicht dagegen kann da was tun. Sie kann die Exception fangen und den Fehler anzeigen.
Oder Du sagst im Business-Layer, dass der Verbindungsversuch drei mal wiederholt wird, bevor die Exception durch gelassen wird. In dem Fall würde sie zwei mal gefangen werden, beim dritten mal muss die View gerade stehen.

Wenn Du z.B. Datei-Zugriffe hast und die Exception sagt, die Datei gibt's schon: Ändere den Namen und probiere es erneut
Sagt die Exception, die Datei gibt's noch nicht: Erzeuge sie und probiere es erneut
In diesen beiden Fällen kannst Du das aber auch im Voraus prüfen.

Oder ein aktuelles Beispiel von mir: Eine Schleife, die viele Aufgaben von Außen entgegen nimmt und auf irgendeine asynchrone Weise ausführen soll.
Verursacht eine dieser Aufgaben einen Fehler, würde diese Schleife abbrechen, was nicht passieren darf.
Also wird ausnahmslos jede Exception gefangen und irgendwo registriert, damit derjenige, der die Aufgabe gesendet hat, darüber informiert werden kann.
So ist z.B. unser Server aufgebaut. Der bekommt über WCF im Sekundentakt X Commands übermittelt und kümmert sich darum, dass die asynchron ausgeführt werden.
Nur weil ein Command nicht läuft, darf unter keinen Umständen der Server beendet werden, die anderen Commands laufen ja trotzdem weiter.

Es gibt (bei C# bzw. .NET) aber noch einen anderen Weg zu behandeln:
Exception fangen, irgendwas tun und erneut werfen bzw. durch lassen.
Im catch kannst Du einfach nur throw; schreiben, dann bleibt auch der Stacktrace erhalten.
So kannst Du z.B. Logging ermöglichen oder irgendwas anderes tun, kannst dir aber trotzdem sicher sein, dass die Exception nicht unbeachtet bleibt.

Und am Ende gibt's (zumindest unter .NET) immer noch das UnhandledException-Event, je nach Art des Projektes an einer anderen Stelle, aber es ist immer da.
Darüber kannst Du global jede Exception fangen und behandeln.
Darüber haben wir z.B. ein globales Letzte-Instant-Handling gebaut.
Schafft es eine Exception bis dahin, wird eine entsprechende Meldung angezeigt, aber das Programm schmiert nicht ab.
Gleichzeitig senden wir dort aber auch eine Info an uns, damit wir den Fehler beheben können. Und nein, das Programm ist intern und rechtlich ist da auch alles im grünen Bereich ;)
Oder Du schreibst den Fehler in's Log und lässt das Programm trotzdem abstürzen.


Wie läuft das Logging ab?
(Beispiele sind für .NET)
DAS ist immer noch aufwendig, da gibt's kein tolles Konzept gegen :D
Es gibt aber Frameworks, die das vereinfachen, wie z.B. Log4Net.

Auf der anderen Seite gibt's aber auch das Pattern "Aspect-Oriented Programming", kurz "AOP", wofür - wie könnte es anders sein - natürlich auch Frameworks (z.B. PostSharp) existieren.
Damit kannst Du den Aufruf einer Methode automatisch loggen lassen, beim Kompilieren würde der Code dann mit eingebaut werden.

Auf der anderen Seite gibt's aber auch die Variante, die zur Laufzeit umgesetzt wir. Auch hier gibt's Frameworks, wie z.B. Unity.Interception von Microsoft.
Das ist gleichzeitig eine Implementierung vom "Inversion of Control" (lies dazu auch mal "DependencyInjection" nach) Pattern. Du registrierst ein Interface und eine Factory, die die Instanz erzeugt.
Unity kann zur Laufzeit einen Proxy bauen, die alle Aufrufe an deine eigene Instanz durch gibt, aber vorher und nachher noch andere Dinge wie Logging erledigt.

Bei AOP musst Du aber beachten: Du bist schnell in der "Mit Kanonen auf Spatzen"-Situation.
Mit der Compiler-Variante (PostSharp) holst Du dir einen Monoliten ins Haus, der irre viel automatisch und "magisch" macht, der dadurch aber auch so schnell ist, wie als würdest Du alles selber schreiben.
Unity.Intercetion ist da nicht anders, nur dass es mMn. etwas einfacher bzw. nachvollziehbarer ist. Die Runtime-Variante ist aber auch langsamer, das bauen von Proxies frisst viel Zeit, allerdings nur einmal, danach sollte Unity das cachen können.


Wie wir Logging umgesetzt haben:
Wir haben ja unsere Commands und der CommandDispatcher (die Schleife, die ich erwähnt habe) schreibt vor dem Aufruf die Parameter detailliert ins Log.
Beim Ausführen wird so Zeug mit geloggt, wie Laufzeit, die SQL-Commands, die abgeschickt werden (NHibernate machts möglich, EntityFramework müsste das aber auch können), etc.
Zwischen drin für kritische Bereiche haben wir noch eine Art InCode-Umsetzung, der das gleiche tut, aber auf den kleinen eingekreisten Bereich bezogen.
Die winzigen Details wie Methoden-Aufrufe loggen wir explizit nicht mit. Durch debuggen müssen wir sowieso, da hilft ein irre komplexes Log nicht und wir haben ja alle Daten, die wir brauchen.
Eventuell könnte man noch einbauen, dass besondere Fehler von bestimmten Commands automatisch zu einem ein Backup der Datenbank führen würden, damit wir die zum Testen haben :gruebel:

Für diesen Beitrag haben gedankt: Christian S., doublecross, exemadorleos
Palladin007
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starofftopic star
Beiträge: 1282
Erhaltene Danke: 182

Windows 11 x64 Pro
C# (Visual Studio Preview)
BeitragVerfasst: Mo 04.12.17 11:25 
PS:

Ich sag ja, bau kein eigenes Exception-Handling.
Richtig wäre es so:

ausblenden C#-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
public void methode(param)
{
    if (condA)
        throw new MyCoolException1("Fehlerbeschreibeung1");
    if (condB)
        throw new MyCoolException2("Fehlerbeschreibeung2");

    // Do some work
}


Du musst aber nicht immer eigene Exception-Typen bauen.
.NET hat einiges recht allgemein gehaltene Typen, wie z.B. ArgumentException, ArgumentNullException, NotSupportedException, InvalidOperationException, etc.
Die kannst Du wieder verwenden und wenn Du's brauchst, baust Du eigene Typen.
Zu viele eigene Typen machen das ganze nämlich auch wieder unübersichtlich, wenn Du beim behandeln tausend catches brauchst ^^

ausblenden C#-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
19:
public void aufruf()
{
    var param = GetParam();

    try
    {
        methode(param);
    }
    catch (MyCoolException1 ex)
    {
        LogError(ex);
        // Do something with Exception1
    }
    catch (MyCoolException2 ex)
    {
        LogError(ex);
        // Do something with Exception1
    }
}


Aber wie gesagt: Nur fangen, wenn Du auch behandeln kannst.
Und auch nur, wenn sie gefangen wird, wird auch geloggt.
Und die Methode selber loggt nicht die Exceptions, die sie selber wirft, nur die, die sie auch fängt.

Oder Du sparst dir das ganz und baust ein globales Handling über das UnhandledException-Event.
exemadorleos Threadstarter
Hält's aus hier
Beiträge: 6



BeitragVerfasst: Mo 04.12.17 13:01 
Danke für diese umfassende (!) Antwort und das Einbetten in einen Kontext, das hat mir schonmal viel geholfen!

Wenn ich Dich richtig verstanden habe, baue ich faktisch kein eigenes Exception Handling in dem Sinne, dass aufrufende Methoden einen Fehler-String auswerten o.ä.. Ich prüfe in den Methoden lediglich PRE-CONDITIONS der Methode und belege / erweitere meinen String "errMsg" um Fehlerbeschreibungen. Bevor die Methode dann die eigentliche Arbeit macht, wird der String nochmal =="" geprüft. Im Ergebnis wird der eigentliche Arbeitsteil der Methode nur ausgeführt, wenn die PRECONDITIONS gegeben sind. Nach der Arbeit erfolgt entweder der return oder die exception wird gebaut. Aufwändiger wird es dann natürlich, wenn der ExceptionTyp je nach Fehler unterschiedlich sein soll oder im eigentlichen Arbeitsteil der Methode Fehler auftreten. Da mache ich es dann wie in Deinem letzten Codebeispiel und werfe die exception direkt.

Allerdings - und hier werde ich künftig Deinem Beispiel folgen - erzeuge ich vor jeder Exception die ich werfe einen Logging-Eintrag. Ich denke Dein Ansatz hier einfach erstmal die Exception zu werfen und das Logging an der Stelle vorzunehmen, wo auch behandelt wird, dürfte meinen Aufwand zumindest deutlich reduzieren.

Dürfte ich mal fragen wie Du praktisch bei der Entwicklung vorgehst? Einer der Hauptgründe für meinen Logging-Exzess ist die Komplexität meiner Anwendung. Da ich bei Fehlern bereits im DataLayer anfange zu loggen und hier oftmals gleich Objektzustände einfach mit ins Log serialisiere, kann ich relativ schnell die Fehlerursache finden, also ermitteln, welche andere Ebene einen Wert nicht korrekt weitergegeben hat. Meine Logging-Einträge sind also sehr technisch geprägt - und eigentlich auch nicht für den Endanwender gedacht. Ich bin wie gesagt noch weit weg von einer fertigen Software - aber nach Deinen Ausführungen wäre mein erster Ansatz für das Release erst auf der Ebene der View zu catchen und die Einstiegsmethode mit einem overall-catch / bzw. dem Unhandled-Event abzusichern. Selbst wenn ich für das Release so arbeiten würde - ich müsste tausende Zeilen Code überarbeiten, um entwicklungsbedingte Logeinträge zu entfernen.

Arbeitest Du bei der Entwicklung mit den Exceptions auf höchster Ebene und arbeitest Dich im StackTrace der InnerExceptions bis zur Ursache durch?
Palladin007
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starofftopic star
Beiträge: 1282
Erhaltene Danke: 182

Windows 11 x64 Pro
C# (Visual Studio Preview)
BeitragVerfasst: Mo 04.12.17 15:39 
Zitat:
Aufwändiger wird es dann natürlich, wenn der ExceptionTyp je nach Fehler unterschiedlich sein soll oder im eigentlichen Arbeitsteil der Methode Fehler auftreten. Da mache ich es dann wie in Deinem letzten Codebeispiel und werfe die exception direkt.


So sollte es aber immer sein ;)
Wenn Du einen Error-String zurück gibst, baust Du dir effektiv ein eigenes Exception-System.
Eine Methode kümmert sich nicht darum, wer sie aufruft und wie das geschieht.
Sie kümmert sich nur darum, dass die Parameter, die sie bekommt, richtig sind und sie arbeiten kann.
Passt der aktuelle Zustand nicht, wird eben eine Exception geworfen und der Aufrufer kann sich um das Problem kümmern.

Wenn Du mit Strings als Exception-Ersatz arbeitest, hast Du umgekehrt auch immer das Problem, dass Du mit Strings arbeiten musst.
Irgendwann änderst Du - warum auch immer - einen Fehler-String und Hallelujah: Jeder einzelne Aufruf darf angepasst werden.
Einfacher ist es, wenn Du gleich eine echte Exception wirfst. Der kannst Du eine Message mit geben und bearbeiten wie Du lustig bist.
Wenn Du mal einen Fall hast, dass Du mehrere gleichartige Exceptions fangen aber auf völlig unterschiedliche Weise bearbeiten musst, dann ist es hier vielleicht angebracht, mehrere verschiedene Exception-Typen zu werfen.


Ein Beispiel aus einem aktuellen Projekt von mir:
Ein Server bietet X verschiedene Services an. Darunter ist auch ein Event-Service, über den der Server den Clients über wichtige Änderungen in mehr oder weniger Echt-Zeit informieren kann.
Damit es flexibel bleibt und das ganze System leicht erweitert werden kann, basiert der Handshake, welche Events existieren, auf String-IDs.
Der Server muss dabei den Event-Service nicht unterstützen und wenn doch, muss er das Event nicht unterstützen - aber er kann.
Der Client spricht sich nun mit dem Server ab um heraus zu finden, was er unterstützt.
Will er nun über ein Event informiert werden, dann gibt es zwei mögliche Fehlerfälle:

  1. Der Event-Service wird nicht unterstützt
  2. Das einzelne Event wird nicht unterstützt

In beiden Fällen würde ich eine NotSupportedException werfen.
Beide Situation muss ich aber anders behandeln:

  1. Der ganze Dialog für den Service wird deaktiviert bzw. als "Nicht unterstützt" angezeigt
  2. Das einzelne Event wird deaktiviert bzw. als "Nicht unterstützt" angezeigt

Daher hab ich zwei Exceptions gebaut, beide leiten von NotSupportedException ab:

  1. NotSupportedServiecException
  2. NotSupportedEventException

Der Aufruf ist dann z.B. so:
ausblenden C#-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
try
{
    var eventArgs = Server.WaitForEvent(Event.Id);
}
catch (NotSupportedServiecException)
{
    Service.IsSupported = false;
}
catch (NotSupportedEventException)
{
    Service.IsSupported = false;
}

Ein alternativer Weg:
Der Client fragt vorher beim Server nach ob er das gefragt überhaupt unterstützt und reagiert entsprechend.


Zitat:
Meine Logging-Einträge sind also sehr technisch geprägt


So ist es auch richtig, der User bekommt - wenn gewollt - ein eigenes Logging.
Du solltest das mit dem Logging aber auch nicht übertreiben.

Ein Beispiel von einem Arbeitskollegen bzw. seiner alten Firma:
Die Anwendung selber ist irre komplex, macht viele komplexe Dinge und da kann eben auch einiges schief laufen.
Die Anwendung hat ein klasse Logging, das Problem dabei war nur: Kaum einer konnte damit etwas anfangen ;)
Das Logging war zu komplex um damit effektiv zu arbeiten.

Du solltest also lieber einzelne Punkte raus suchen, wo Logging später wichtig sein könnte, weil z.B. eine wichtige zentrale Methode aufgerufen wird.
Oder das Beispiel von unserem CommandDispatcher (stark vereinfacht):

ausblenden C#-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
void ExecuteCommand(ExecutionContext context, Command cmd)
{
    context.Log("Command " + cmd.Name + "startet. Parameters: " + SerializeJSON(cmd.Parameters));

    try
    {
        OnCommandStarted(cmd); // Stellvertretend für irgendwas, was über die Ausführung informiert werden soll
        var watch = Stopwatch.Start(); // startet die Laufzeit-Messung
        var result = cmd.Execute(context);
        context.Log("Command " + cmd.Name + "finished: " + watch.ElapsedMilliseconds + " ms");
        OnCommandFinished(cmd, result);
    }
    catch (Exception ex)
    {
        OnCommandFailed(cmd, ex);
        context.Log("Command " + cmd.Name + "faild: " + ex.ToString());
    }
}


Die Command-Implementierung selber loggt danach nichts mehr.
Ausnahme sind zentrale Punkte, wie z.B. die Methode zum komplexen Vorbereiten der DB-Änderung und die Methode zum Schreiben der Änderungen.
In beiden Fällen sieht das dann z.B. so aus:

ausblenden C#-Quelltext
1:
2:
3:
4:
5:
public object Execute(ExecutionContext context)
{
    var myObj = context.ObserveExecution(() => PrepareOperation(context.Session), nameof(PrepareOperation));
    return context.ObserveExecution(() => ExecuteOperation(context.Session), nameof(ExecuteOperation));
}


Die ObserveExecution-Methode tut dann den ganzen lustigen Exception-Logging kram:

ausblenden C#-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
TResult ExecuteCommand<TResult>(Func<TResult> method, string name)
{
    context.Log("Method " + name + "startet");

    try
    {
        var watch = Stopwatch.Start(); // startet die Laufzeit-Messung
        var result = method(context);
        Log("Method " + cmd.Name + "finished: " + watch.ElapsedMilliseconds + " ms");
        return result;
    }
    catch (Exception ex)
    {
        Log("Method " + cmd.Name + "faild: " + ex.ToString());
        throw// Wirft die Exception weiter, ich kann sie hier ja nicht behandeln
    }
}


Zitat:
Dürfte ich mal fragen wie Du praktisch bei der Entwicklung vorgehst?


Kann ich so nicht sagen, das ist je nach Situation anders.

Zitat:
Arbeitest Du bei der Entwicklung mit den Exceptions auf höchster Ebene und arbeitest Dich im StackTrace der InnerExceptions bis zur Ursache durch?


Wenn ich einen Fehler finden muss, gehe ich meist nach drei Möglichkeiten vor:

  • Ich kenne den Fehler und weiß direkt, woher er kommt und muss nur noch heraus finden, wie ich ihn z.B. in UnitTests reproduzieren kann
  • Ich bringe in Erfahrung, wie man ihn aus dem Client heraus reproduzieren kann und debugge dann so lange durch, bis ich den Fehler habe
  • Ich lese den StackTrace - das ist die ätzendste Variante, aber manchmal geht's nicht anders

Hilfreich kann aber auch sein, wenn Du einen Memory-Dump hast. VisualStudio kann den öffnen, das zeigt dir dann die genaue Position des Fehlers, die Exception und alle zuständigen Informationen und den CallStack an. Letzteres ist das Spannendste, weil Du dort auch an die jeweilige Position springen und die Variablen-Werte anzeigen lassen kannst.
exemadorleos Threadstarter
Hält's aus hier
Beiträge: 6



BeitragVerfasst: Mi 06.12.17 10:41 
Guten Morgen!

Was Du sagst leuchtet ein. Ich gebe zwar keine unterschiedlichen Strings zurück die ich auswerte, allerdings werfe ich oftmals denselben exception-Typ nur mit dann eben unterschiedlichen Strings als Message. Im Ergebnis behältst Du aber Recht - unterschiedliche Typen für unterschiedliche Fehler vereinfachen die Exception-Behandlung ( die ich bislang nicht habe :-P ) enorm. Dann macht es auch mit selbst erstellten Exception-Klassen wieder Sinn.

Beim Thema Logging gebe ich Dir ebenfalls Recht. Ich werde es für meine Zwecke jetzt so lösen, dass die "Entwickler-Logs" nur mit einer DEBUG-Compiler-Direktive geloggt werden, während die Logs, die im Bedarfsfall auch der Endanwender sieht, auf höheren Ebenen allgemeiner gehalten werden. Damit habe ich denke ich was ich möchte.

Vielen Dank nochmal für die guten Ansätze!
Palladin007
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starofftopic star
Beiträge: 1282
Erhaltene Danke: 182

Windows 11 x64 Pro
C# (Visual Studio Preview)
BeitragVerfasst: Mi 06.12.17 11:18 
Zitat:
Ich gebe zwar keine unterschiedlichen Strings zurück die ich auswerte


Dann hab ich dich falsch verstanden.
Wenn Du es so machen würdest, wäre das auf Dauer nämlich ein ziemliches Chaos :D

Zitat:
Dann macht es auch mit selbst erstellten Exception-Klassen wieder Sinn.


Schau dir aber trotzdem lieber vorhandene Exception-Typen an.
Du kannst dir ja mal hier eine Liste der Exceptions anschauen.
Ist - soweit ich das gesehen habe - nach Namespace gruppiert.

Zitat:
die "Entwickler-Logs" nur mit einer DEBUG-Compiler-Direktive geloggt werden


Ich würde sie von außen aktivier/deaktivierbar machen, z.B. über einen Settings-Eintrag.
Wenn irgendein Endanwender immer wieder ein Fehler hat, aber keine Feste Regel zum Reproduzieren findet, wirst Du froh sein, wenn er dir das Log geben kann :D
Beide Logs kannst Du ja in unterschiedliche Dateien schreiben lassen.