Stacktrace und Debugging

In meinem letzten Beitrag ging es um anonyme Methoden. Heute schauen wir uns das Thema Debugging etwas genauer an. Dazu werden die Fragen „Wie erziele ich eine einfache Methode um zu debuggen?“ und „Wie fange ich Fehler auf?“ geklärt.

Debugging

Wie bereits im vorherigen Beitrag von IgelchenM angefangen zu beschreiben, wie man am besten Fehler feststellt, möchte ich etwas tiefer in die Materie eingreifen.

Wie erziele ich eine einfache Methode um zu debuggen?

Wie in meinem letzten Blogeintrag bereits erwähnt, werden anonyme Methoden nicht nur häufig verwendet, diese sollten auch benannt werden, damit Fehler gefunden werden.

Wir schauen uns das ganze nun mal etwas genauer an. In diesem Beispiel zeige ich, wie das ganze auf LigaTipps ausschaut. Das Projekt ist mittlerweile so groß geworden, dass man nicht umher kommt, strukturiert zu arbeiten.

Wir rufen irgendwo im Code die Klasse “Tipps” auf. Aus unersichtlichen Gründen sehen wir aber nicht viel, sehen nicht, wo genau wir hinsehen müssen. Der AppServer meldet sich nur mit folgender Aussage:

App-Logs (privat an Dich): Ligatipps: ERROR: ReferenceError: "Tipps" is not defined. (knuddelsDEV.30558139.Ligatipps@v0: main.js#67(eval)#1)
     at knuddelsDEV.30558139.Ligatipps@v0: main.js#67(eval):1
     at knuddelsDEV.30558139.Ligatipps@v0: main.js:67 (anonymous)
     at knuddelsDEV.30558139.Ligatipps@v0: main.js:79 (anonymous)
     at knuddelsDEV.30558139.Ligatipps@v0: classes/KFramework.min.js:54 (each)
     at knuddelsDEV.30558139.Ligatipps@v0: main.js:77 (anonymous)

Vorweg müsst ihr wissen, wie dynamisch LigaTipps entwickelt ist, es können Instanzen von Klassen erzeugt und abgerufen werden. Das ganze schaut dann wie folgt aus:

/**
  * Erstellt eine Instanz die über `App.get(name)` geholt werden kann.
  * @method createInstance
  * @return {Class}
*/
this.createInstance = function(context, name) {
     if(name.contains('/')) {
          var split = name.split('/');
          eval(context + '[\'' + name + '\'] = new ' + split[split.size() - 1] + '();');
     } else {
          eval(context + '[\'' + name + '\'] = new ' + name + '();');
     }
};

Anhand der Fehlermeldung sehen wir nur, dass der Fehler auf der letzten eval-Zeile stattfand; Wir wissen zwar, wo überall die Tipps-Klasse verwendet wird, aber wir wissen nicht, an welcher Stelle wir suchen müssen. Sowas kann durchaus mal sehr viel Zeit kosten.

Aus den paar Zeilen der Fehlermeldung werden wir auch nicht schlau. Doch, pass auf!

Wir sehen im StackTrace (so nennt sich das ganze) den Ablauf der Aufrufe. Wenn wir nun hingehen und anonyme Methoden im gesamten Code benennen, finden wir viel einfacher und schneller den Fehler!

Wie von Zauberhand bekommen wir auf einmal die Methodennamen im StackTrace angezeigt und wissen ganz genau, wo wir hinschauen müssen:

App-Logs (privat an Dich): Ligatipps: ERROR: ReferenceError: "Tipps" is not defined. (knuddelsDEV.30558139.Ligatipps@v0: main.js#67(eval)#1)
     at knuddelsDEV.30558139.Ligatipps@v0: main.js#67(eval):1
     at knuddelsDEV.30558139.Ligatipps@v0: main.js:67 (createInstance)
     at knuddelsDEV.30558139.Ligatipps@v0: main.js:79 (CommandsEach)
     at knuddelsDEV.30558139.Ligatipps@v0: classes/KFramework.min.js:54 (each)
     at knuddelsDEV.30558139.Ligatipps@v0: main.js:77 (onAppStart)

Wir sehen nun, dass CommandsEach die Methode createInstance aufruft.

Klar könnte man nun gegenargumentieren, dass doch die Zeilennummern dabei stehen, aber wenn auch noch der Name der Methode auftaucht, fällt es dir wesentlich leichter die stelle zu finden. Du denkst einfach nur “Ach in dieser Funktion ist das Problem zu finden!” anstelle dich erstmal durch die Zeilen zu scrollen; Du weißt direkt wohin du springen musst.

Außerdem kann es vorkommen, dass du mit Repositorys arbeitest, vermutlich schon Veränderungen durchgeführt hast, diese aber noch nicht in der Live-App stellen möchtest. Tritt auf einmal ein Fehler auf, können anhand der Methodennamen wahre Wunder geschehen. Auch TobyB durfte schon mehrfach diese Erfahrung machen, wie er im Forum auf ein “die Zeilennummern reichen doch” antwortete.

Wo war da jetzt der Fehler?

Keine Sorge, bei LigaTipps war alles in Ordnung. Ich habe lediglich die Tipps-Klasse gelöscht um euch einfach nur die Methodik zu zeigen.

Wie fange ich Fehler auf?

Es ist bereits Gang und gäbe in anderen Programmiersprachen: Das auffangen von Fehlern. Umgangssprachlich wird das ganze auch als “Exception” betitelt, was übersetzt eine Ausnahmebehandlung ist.

Hier kommen wir zum Thema der try/catch-Blöcke. Um den Code wird ein separater Bereich angelegt, der wie folgt aussieht:

try {
     // Der Code der ausgeführt wird
} catch(exception) {
     // Hier kannst du den Fehler abfangen
}

Alles das, was im Try passiert, wird im Catch als Ausnahme weiter behandelt, sofern hier eine Exception stattfindet. Ob nun ein Fehler oder gar eine provozierte Exception, alles was dies verursacht, kann im Catch abgefangen werden.

StackTrace, was?

Ein StackTrace wird zu Diagnostikzwecken verwendet. Es stellt die nacheinander aufgerufenen Methoden und Dateien dar. Hier lassen sich Schritt für Schritt nachverfolgen, welche Dinge aufgerufen wurden, bis der Fehler passiert ist.

Hier einmal eine einfache Ausgabe zum Verständnis:

ERROR: Du hast gepupst. (main.js#4)
     at main.js:5 (startePups)
     at main.js:4 (onAppStart)

Es tritt der Fehler “Du hast gepupst.” auf. Durch diesen Stacktrace siehst du den Verlauf:

  1. Die App wird gestartet und es wird die Methode “onAppStart();” auf Zeile 4 aufgerufen.
  2. auf Zeile 5 wird die Methode “startePups” aufgerufen, hier findet der Fehler statt

Kommentar verfassen

Trage deine Daten unten ein oder klicke ein Icon um dich einzuloggen:

WordPress.com-Logo

Du kommentierst mit Deinem WordPress.com-Konto. Abmelden / Ändern )

Twitter-Bild

Du kommentierst mit Deinem Twitter-Konto. Abmelden / Ändern )

Facebook-Foto

Du kommentierst mit Deinem Facebook-Konto. Abmelden / Ändern )

Google+ Foto

Du kommentierst mit Deinem Google+-Konto. Abmelden / Ändern )

Verbinde mit %s