Node.js ist eine JavaScript-Laufzeitumgebung, die auf der gleichen V8-Engine basiert, die auch im Chrome-Browser von Google verwendet wird. Sie wird häufig für die Entwicklung von plattformübergreifenden serverseitigen und Terminal-Anwendungen verwendet. Node.js ist in den letzten zehn Jahren immer beliebter geworden, weil es einfach zu installieren, praktisch und schnell ist und es Webentwicklern auf der Client-Seite ermöglicht, ihre Fähigkeiten anderweitig zu nutzen.
Allerdings ist die Softwareentwicklung nach wie vor eine komplexe Aufgabe, und dein Node.js-Code wird irgendwann einmal scheitern. In diesem Lernprogramm werden verschiedene Tools vorgestellt, mit denen du Anwendungen debuggen und die Ursache eines Problems finden kannst.
Fangen wir gleich an.
Schau dir unsere Videoanleitung zum Debuggen von Node.js-Code an
Debugging Überblick
„Debugging“ ist die Bezeichnung für die verschiedenen Möglichkeiten, Softwarefehler zu beheben. Einen Fehler zu beheben, ist oft ganz einfach. Die Ursache des Fehlers zu finden, kann wesentlich komplexer sein und viele Stunden Kopfzerbrechen verursachen.
In den folgenden Abschnitten werden drei allgemeine Fehlertypen beschrieben, denen du begegnen wirst.
Syntax-Fehler
Dein Code hält sich nicht an die Regeln der Sprache – zum Beispiel, wenn du eine schließende Klammer auslässt oder eine Anweisung wie console.lag(x)
falsch schreibst.
Ein guter Code-Editor kann dabei helfen, häufige Probleme zu erkennen:
- Farbliche Kennzeichnung von gültigen und ungültigen Anweisungen
- Typprüfung von Variablen
- Automatische Vervollständigung von Funktions- und Variablennamen
- Hervorheben passender Klammern
- Automatische Einrückung von Codeblöcken
- Erkennen von unerreichbarem Code
- Refactoring unsauberer Funktionen
Kostenlose Editoren wie VS Code und Atom bieten eine gute Unterstützung für Node.js, JavaScript und TypeScript (das sich in JavaScript übertragen lässt). Grundlegende Syntaxprobleme können in der Regel erkannt werden, bevor du deinen Code speicherst und testest.
Ein Code-Linter wie ESLint meldet auch Syntaxfehler, schlechte Einrückungen und nicht deklarierte Variablen. ESLint ist ein Node.js-Tool, das du global installieren kannst:
npm i eslint -g
Du kannst JavaScript-Dateien von der Kommandozeile aus überprüfen:
eslint mycode.js
…aber es ist einfacher, ein Editor-Plugin wie ESLint für VS Code oder linter-eslint für Atom zu verwenden, die den Code automatisch während der Eingabe überprüfen:
Logik-Fehler
Dein Code läuft, aber er funktioniert nicht so, wie du es erwartest. Zum Beispiel wird ein Benutzer nicht abgemeldet, wenn er es verlangt; ein Bericht zeigt falsche Zahlen an; Daten werden nicht vollständig in einer Datenbank gespeichert; usw.
Logikfehler können verursacht werden durch:
- Verwendung der falschen Variablen
- Falsche Bedingungen, z. B.
if (a > 5)
stattif (a < 5)
- Berechnungen, die den Vorrang von Operatoren nicht berücksichtigen, z. B.
1+2*3
ergibt 7 statt 9.
Laufzeitfehler (oder Ausführungsfehler)
Ein Fehler wird erst sichtbar, wenn die Anwendung ausgeführt wird, was oft zu einem Absturz führt. Laufzeitfehler können verursacht werden durch:
- Dividieren durch eine Variable, die auf Null gesetzt wurde
- Der Versuch, auf ein Array-Element zuzugreifen, das nicht existiert
- Versuch, in eine schreibgeschützte Datei zu schreiben
Logik- und Laufzeitfehler sind schwieriger zu erkennen, aber die folgenden Entwicklungstechniken können helfen:
- Verwende testgetriebene Entwicklung: TTD ermutigt dich, Tests zu schreiben, bevor eine Funktion entwickelt wird, z.B. X wird von FunktionY zurückgegeben, wenn Z als Parameter übergeben wird. Diese Tests werden während der ersten Entwicklung und bei späteren Aktualisierungen durchgeführt, um sicherzustellen, dass der Code weiterhin wie erwartet funktioniert.
- Verwende ein Fehlerverfolgungssystem: Es gibt nichts Schlimmeres als eine E-Mail, in der steht : „Deine Software funktioniert nicht“! Problemverfolgungssysteme ermöglichen es dir, bestimmte Probleme zu erfassen, Reproduktionsschritte zu dokumentieren, Prioritäten festzulegen, Entwickler zuzuweisen und den Fortschritt der Korrekturen zu verfolgen.
- Verwende die Versionskontrolle: Ein Versionskontrollsystem wie Git hilft dir, den Code zu sichern, Revisionen zu verwalten und festzustellen, wo ein Fehler aufgetreten ist. Online-Repositories wie Github und Bitbucket bieten kostenlosen Speicherplatz und Tools für kleinere oder Open-Source-Projekte.
Du wirst immer noch auf Node.js-Fehler stoßen, aber die folgenden Abschnitte beschreiben, wie du den schwer fassbaren Fehler finden kannst.
Geeignete Node.js-Umgebungsvariablen setzen
Umgebungsvariablen, die im Host-Betriebssystem gesetzt werden, können die Einstellungen von Node.js-Anwendungen und -Modulen steuern. Die gebräuchlichste ist NODE_ENV
, die beim Debuggen normalerweise auf „Development“ und beim Betrieb auf einem Live-Server auf „Production“ gesetzt wird. Setze Umgebungsvariablen auf macOS oder Linux mit dem Befehl:
NODE_ENV=development
oder in der (klassischen) Windows-Eingabeaufforderung:
set NODE_ENV=development
oder Windows Powershell:
$env:NODE_ENV="development"
Im beliebten Express.js-Framework deaktiviert die Einstellung NODE_ENV auf development das Zwischenspeichern von Vorlagendateien und gibt ausführliche Fehlermeldungen aus, was beim Debuggen hilfreich sein kann. Andere Module bieten möglicherweise ähnliche Funktionen, und du kannst eine NODE_ENV-Bedingung zu deinen Anwendungen hinzufügen, z. B.
// running in development mode?
const devMode = (process.env.NODE_ENV !== 'production');
if (devMode) {
console.log('application is running in development mode');
}
Du kannst auch die util.debuglog-Methode von Node verwenden, um bedingt Fehlermeldungen auszugeben, z. B.
import { debuglog } from 'util';
const myappDebug = debuglog('myapp');
myappDebug('log something');
Diese Anwendung gibt die Logmeldung nur aus, wenn NODE_DEBUG auf myapp oder einen Platzhalter wie * oder my* gesetzt ist.
Node.js Kommandozeilenoptionen verwenden
Node-Skripte werden in der Regel mit node gefolgt vom Namen des Eingangsskripts gestartet:
node app.js
Du kannst auch Kommandozeilenoptionen setzen, um verschiedene Aspekte der Laufzeit zu steuern. Nützliche Flags für das Debugging sind zum Beispiel:
--check
Syntaxprüfung des Skripts ohne Ausführen--trace-warnings
einen Stack-Trace ausgeben, wenn JavaScript Promises nicht aufgelöst oder zurückgewiesen werden--enable-source-maps
Quelltexte anzeigen, wenn ein Transpiler wie TypeScript verwendet wird--throw-deprecation
warnen, wenn veraltete Node.js-Funktionen verwendet werden--redirect-warnings=file
Warnungen in eine Datei statt in stderr ausgeben--trace-exit
einen Stack-Trace ausgeben, wennprocess.exit()
aufgerufen wird.
Nachrichten auf der Konsole ausgeben
Die Ausgabe einer Konsolenmeldung ist eine der einfachsten Möglichkeiten, eine Node.js-Anwendung zu debuggen:
console.log(`someVariable: ${ someVariable }`);
Nur wenige Entwickler wissen, dass es noch viele andere Konsolen-Methoden gibt:
Konsolen-Methode | Beschreibung |
---|---|
.log(msg) |
Standard-Konsolenmeldung |
.log('%j', obj) |
Objekt als kompakten JSON-String ausgeben |
.dir(obj, opt) |
Pretty-print Objekteigenschaften |
.table(obj) |
Arrays und Objekte im Tabellenformat ausgeben |
.error(msg) |
eine Fehlermeldung |
.count(label) |
einen benannten Zähler inkrementieren und ausgeben |
.countReset(label) |
einen benannten Zähler zurücksetzen |
.group(label) |
eine Gruppe von Meldungen einrücken |
.groupEnd(label) |
beenden einer Gruppe |
.time(label) |
einen benannten Timer starten |
.timeLog(label) |
meldet die verstrichene Zeit |
.timeEnd(label) |
einen benannten Timer stoppen |
.trace() |
einen Stack-Trace ausgeben (eine Liste aller Funktionsaufrufe) |
.clear() |
die Konsole löschen |
console.log()
akzeptiert auch eine Liste von kommagetrennten Werten:
let x = 123;
console.log('x:', x);
// x: 123
…obwohl die ES6-Destrukturierung eine ähnliche Ausgabe mit weniger Aufwand bietet:
console.log({ x });
// { x: 123 }
Der Befehl console.dir() druckt Objekteigenschaften auf die gleiche Weise aus wie util.inspect():
console.dir(myObject, { depth: null, color: true });
Kontroverse über die Konsole
Einige Entwickler behaupten, dass du console.log()
nie benutzen solltest, weil:
- Du änderst Code und könntest etwas verändern oder vergessen, es zu entfernen, und
- Es nicht nötig ist, wenn es bessere Debugging-Optionen gibt.
Glaube niemandem, der behauptet, dass er console.log()
nie benutzt! Logging ist schnell und schmutzig, aber jeder benutzt es irgendwann. Verwende das Werkzeug oder die Technik, die du bevorzugst. Das Beheben eines Fehlers ist wichtiger als die Methode, mit der du ihn findest.
Verwende ein Logging-System eines Drittanbieters
Logging-Systeme von Drittanbietern bieten ausgefeiltere Funktionen wie Meldungsebenen, Ausführlichkeit, Sortierung, Dateiausgabe, Profilerstellung, Berichte und mehr. Beliebte Lösungen sind cabin, loglevel, morgan, pino, signale, storyboard, tracer und winston.
Nutze den V8 Inspector
Die V8 JavaScript Engine bietet einen Debugging-Client, den du in Node.js verwenden kannst. Starte eine Anwendung mit Node Inspect, z.B.
node inspect app.js
Der Debugger hält bei der ersten Zeile an und zeigt einen Debug>-Prompt an:
$ node inspect .\mycode.js
< Debugger listening on ws://127.0.0.1:9229/143e23fb
< For help, see: https://nodejs.org/en/docs/inspector
<
ok
< Debugger attached.
<
Break on start in mycode.js:1
> 1 const count = 10;
2
3 for (i = 0; i < counter; i++) {
debug>
Gib Hilfe ein, um eine Liste der Befehle anzuzeigen. Du kannst durch die Anwendung schreiten, indem du eingibst:
- cont oder c: Ausführung fortsetzen
- next oder n: führe den nächsten Befehl aus
- step oder s: in eine aufgerufene Funktion einsteigen
- out oder o: Aus einer Funktion aussteigen und zur aufrufenden Anweisung zurückkehren
- pause: den laufenden Code unterbrechen
- watch(‚myvar‘): eine Variable beobachten
- setBreakPoint() oder sb(): Setzt einen Haltepunkt
- restart: das Skript neu starten
- .exit oder Ctrl | Cmd + D: den Debugger verlassen
Zugegeben, diese Debugging-Option ist zeitaufwändig und unhandlich. Verwende sie nur, wenn es keine andere Möglichkeit gibt, z. B. wenn du Code auf einem entfernten Server ausführst und keine Verbindung von einem anderen Ort aus herstellen oder zusätzliche Software installieren kannst.
Den Chrome-Browser zum Debuggen von Node.js-Code verwenden
Die oben verwendete Option Node.js inspect startet einen Web Socket Server, der auf localhost port 9229 lauscht. Sie startet auch einen textbasierten Debugging-Client, aber es ist auch möglich, grafische Clients zu verwenden – wie den in Google Chrome und Chrome-basierten Browsern wie Chromium, Edge, Opera, Vivaldi und Brave eingebauten.
Um eine typische Webanwendung zu debuggen, starte sie mit der Option –inspect, um den Web Socket Server des V8 Debuggers zu aktivieren:
node --inspect index.js
Hinweis:
- index.js ist vermutlich das Einstiegsskript der Anwendung.
- Achte darauf, dass du
--inspect
mit doppelten Bindestrichen verwendest, um sicherzustellen, dass du nicht den textbasierten Debugger-Client startest. - Du kannst nodemon anstelle von node verwenden, wenn du die Anwendung automatisch neu starten willst, wenn eine Datei geändert wird.
Standardmäßig akzeptiert der Debugger nur eingehende Verbindungen von der lokalen Maschine. Wenn du die Anwendung auf einem anderen Gerät, einer virtuellen Maschine oder einem Docker-Container ausführst, verwende:
node --inspect=0.0.0.0:9229 index.js
Du kannst auch --inspect-brk
anstelle von --inspect
verwenden, um die Verarbeitung in der ersten Zeile anzuhalten (einen Haltepunkt zu setzen), damit du den Code von Anfang an durchgehen kannst.
Öffne einen Chrome-basierten Browser und gib chrome://inspect
in die Adressleiste ein, um lokale und vernetzte Geräte anzuzeigen:
Wenn deine Node.js-Anwendung auch nicht als Remote Target angezeigt wird:
- Klicke auf Dedicated DevTools for Node öffnen und wähle die Adresse und den Port, oder
- Aktiviere Netzwerkziele erkennen, klicke auf Konfigurieren und füge dann die IP-Adresse und den Port des Geräts hinzu, auf dem die Anwendung läuft.
Klicke auf den Inspektionslink des Ziels, um den DevTools Debugger Client zu starten. Das sollte jedem bekannt vorkommen, der DevTools zum Debuggen von Client-seitigem Code verwendet hat:
Wechsle zum Quellen-Panel. Du kannst jede Datei öffnen, indem du Cmd | Strg + P drückst und den Dateinamen eingibst (z. B. index.js).
Es ist jedoch einfacher, deinen Projektordner zum Arbeitsbereich hinzuzufügen. So kannst du Dateien direkt aus DevTools laden, bearbeiten und speichern (ob du das für eine gute Idee hältst, ist eine andere Frage!)
- Klicke auf + Ordner zum Arbeitsbereich hinzufügen
- Wähle den Ort deines Node.js-Projekts
- Klicke auf Zustimmen , um Dateiänderungen zuzulassen
Du kannst jetzt Dateien aus dem linken Verzeichnisbaum laden:
Klicke auf eine beliebige Zeilennummer, um einen Haltepunkt zu setzen, der durch eine blaue Markierung gekennzeichnet ist.
Das Debugging basiert auf Haltepunkten. Sie geben an, an welchen Stellen der Debugger die Programmausführung unterbrechen und den aktuellen Zustand des Programms anzeigen soll (Variablen, Aufrufstapel usw.)
Du kannst eine beliebige Anzahl von Haltepunkten in der Benutzeroberfläche definieren. Eine andere Möglichkeit ist, eine Debugger; Anweisung in deinen Code einzufügen, die anhält, wenn ein Debugger angeschlossen wird.
Lade und benutze deine Webanwendung, um zu der Anweisung zu gelangen, an der ein Haltepunkt gesetzt wurde. Im Beispiel hier wird http://localhost:3000/ in einem beliebigen Browser geöffnet und DevTools hält die Ausführung in Zeile 44 an:
Das rechte Feld zeigt:
- Eine Reihe von Aktionssymbolen (siehe unten).
- Im Watch-Fenster kannst du Variablen überwachen, indem du auf das + klickst und ihre Namen eingibst.
- Ein Breakpoints-Fenster zeigt eine Liste aller Breakpoints an und ermöglicht es, sie zu aktivieren oder zu deaktivieren.
- Der Bereich Scope zeigt den Zustand aller lokalen, Modul- und globalen Variablen an. Diesen Bereich wirst du am häufigsten einsehen.
- Ein Call Stack-Fenster zeigt die Hierarchie der Funktionen, die aufgerufen wurden, um diesen Punkt zu erreichen.
Eine Reihe von Aktionssymbolen wird über Pausiert am Haltepunkt angezeigt:
Von links nach rechts führen sie die folgenden Aktionen aus:
- Ausführung fortsetzen: Setzt die Verarbeitung bis zum nächsten Haltepunkt fort
- weitergehen: Führe den nächsten Befehl aus, bleibe aber innerhalb des aktuellen Codeblocks – springe nicht in eine Funktion, die er aufruft
- step into: Führe den nächsten Befehl aus und springe bei Bedarf in eine beliebige Funktion
- step out: Die Verarbeitung bis zum Ende der Funktion fortsetzen und zum aufrufenden Befehl zurückkehren
- step: Ähnlich wie step into, außer dass es nicht in asynchrone Funktionen springt
- alle Haltepunkte deaktivieren
- pause on exceptions: Die Verarbeitung anhalten, wenn ein Fehler auftritt.
Bedingte Haltepunkte
Manchmal ist es notwendig, ein wenig mehr Kontrolle über Haltepunkte auszuüben. Stell dir vor, du hast eine Schleife, die 1.000 Iterationen durchlaufen hat, aber du interessierst dich nur für den Zustand der letzten Iteration:
for (let i = 0; i < 1000; i++) {
// set breakpoint here
}
Anstatt 999 Mal auf “ Ausführung fortsetzen “ zu klicken, kannst du mit der rechten Maustaste auf die Zeile klicken, “ Bedingten Haltepunkt hinzufügen“ wählen und eine Bedingung wie i = 999
eingeben:
Chrome zeigt bedingte Haltepunkte nicht blau, sondern gelb an. In diesem Fall wird der Haltepunkt erst bei der letzten Iteration der Schleife ausgelöst.
Log-Punkte
Log-Punkte implementieren console.log() ohne jeglichen Code! Ein Ausdruck kann ausgegeben werden, wenn der Code eine beliebige Zeile ausführt, aber er hält die Verarbeitung nicht an, anders als ein Haltepunkt.
Um einen Log-Punkt hinzuzufügen, klickst du mit der rechten Maustaste auf eine beliebige Zeile, wählst Log-Punkt hinzufügen und gibst einen Ausdruck ein, z. B. 'loop counter i', i
:
Die DevTools-Konsole gibt im obigen Beispiel loop counter i: 0
an loop counter i: 999
aus.
VS Code zum Debuggen von Node.js-Anwendungen verwenden
VS Code, oder Visual Studio Code, ist ein kostenloser Code-Editor von Microsoft, der bei Webentwicklern sehr beliebt geworden ist. Die Anwendung ist für Windows, macOS und Linux verfügbar und wird mit Webtechnologien im Electron-Framework entwickelt.
VS Code unterstützt Node.js und hat einen integrierten Debugging-Client. Die meisten Anwendungen können ohne jegliche Konfiguration debuggt werden; der Editor startet automatisch den Debugging-Server und -Client.
Öffne die Startdatei (z. B. index.js), aktiviere den Bereich Ausführen und Debuggen, klicke auf die Schaltfläche Ausführen und Debuggen und wähle die Node.js-Umgebung. Klicke auf eine beliebige Zeile, um einen Haltepunkt zu aktivieren, der als rotes Kreissymbol angezeigt wird. Öffne dann die Anwendung wie zuvor in einem Browser – VS Code hält die Ausführung an, wenn der Haltepunkt erreicht ist:
Die Bereiche Variablen, Beobachten, Aufrufstack und Haltepunkte ähneln denen in Chrome DevTools. Der Bereich Geladene Skripte zeigt an, welche Skripte geladen wurden, auch wenn viele davon intern in Node.js sind.
Mit der Symbolleiste mit Aktionssymbolen kannst du:
- Ausführung fortsetzen: Setzt die Verarbeitung bis zum nächsten Haltepunkt fort
- weitergehen: Führe den nächsten Befehl aus, bleibe aber innerhalb der aktuellen Funktion – springe nicht in eine Funktion, die sie aufruft
- step into: Führe den nächsten Befehl aus und springe in jede Funktion, die er aufruft
- step out: Die Verarbeitung bis zum Ende der Funktion fortsetzen und zum aufrufenden Befehl zurückkehren
- die Anwendung und den Debugger neu starten
- die Anwendung und den Debugger anhalten
Wie bei den Chrome DevTools kannst du mit der rechten Maustaste auf eine beliebige Zeile klicken, um bedingte Haltepunkte und Protokollpunkte hinzuzufügen.
Weitere Informationen findest du unter Debugging in Visual Studio Code.
VS Code Erweiterte Debugging-Konfiguration
Weitere VS Code-Konfigurationen können erforderlich sein, wenn du Code auf einem anderen Gerät oder einer virtuellen Maschine debuggen willst oder alternative Startoptionen wie Nodemon verwenden musst.
VS Code speichert Debugging-Konfigurationen in einer launch.json-Datei in einem .vscode
Verzeichnis in deinem Projekt. Öffne den Bereich Ausführen und Debuggen, klicke auf launch.json-Datei erstellen und wähle die Node.js-Umgebung, um diese Datei zu erstellen. Es wird eine Beispielkonfiguration bereitgestellt:
Eine beliebige Anzahl von Konfigurationseinstellungen kann als Objekte im Array "configurations"
definiert werden. Klicke auf Konfiguration hinzufügen… und wähle eine entsprechende Option.
Eine einzelne Node.js-Konfiguration kann entweder:
- Selbst einen Prozess starten oder
- Anhängen an einen Debugging-Web-Socket-Server, der vielleicht auf einem entfernten Rechner oder Docker-Container läuft.
Um z.B. eine Nodemon-Konfiguration zu definieren, wähle Node.js: Nodemon Setup und ändere ggf. das Skript für den Eintrag „program“:
{
// custom configuration
"version": "0.2.0",
"configurations": [
{
"console": "integratedTerminal",
"internalConsoleOptions": "neverOpen",
"name": "nodemon",
"program": "${workspaceFolder}/index.js",
"request": "launch",
"restart": true,
"runtimeExecutable": "nodemon",
"skipFiles": [
"<node_internals>/**"
],
"type": "pwa-node"
}
]
}
Speichere die Datei launch.json
und nodemon (der „Name“ der Konfiguration) erscheint in der Dropdown-Liste am oberen Rand des Ausführungs- und Debug-Bereichs. Klicke auf das grüne Ausführungssymbol, um diese Konfiguration zu verwenden und die Anwendung mit Nodemon zu starten:
Wie zuvor kannst du Haltepunkte, bedingte Haltepunkte und Protokollpunkte hinzufügen. Der Hauptunterschied ist, dass nodemon deinen Server automatisch neu startet, wenn eine Datei geändert wird.
Weitere Informationen findest du unter VS Code Startkonfigurationen.
Die folgenden VS Code-Erweiterungen können dir auch beim Debuggen von Code helfen, der auf entfernten oder isolierten Serverumgebungen gehostet wird:
- Remote – Container: Verbinde dich mit Anwendungen, die in Docker-Containern laufen
- Remote – SSH: Verbinde dich mit Anwendungen, die auf einem entfernten Server laufen
- Remote – WSL: Verbinde dich mit Anwendungen, die auf dem Windows Subsystem für Linux (WSL) laufen.
Andere Node.js Debugging-Optionen
Der Node.js Debugging Guide bietet Tipps für eine Reihe von Texteditoren und IDEs, darunter Visual Studio, JetBrains WebStorm, Gitpod und Eclipse. Atom bietet eine node-debug-Erweiterung, die den Chrome DevTools Debugger in den Editor integriert.
Sobald deine Anwendung in Betrieb ist, kannst du auch kommerzielle Debugging-Dienste wie LogRocket und Sentry.io nutzen, die Client- und Serverfehler von echten Nutzern aufzeichnen und wiedergeben können.
Zusammenfassung
In der Vergangenheit war das Debuggen von JavaScript schwierig, aber in den letzten zehn Jahren hat es große Verbesserungen gegeben. Die Auswahl ist genauso gut – wenn nicht sogar besser – als bei anderen Sprachen.
Verwende jedes praktische Tool, um ein Problem zu lokalisieren. Gegen console.log() für die schnelle Fehlersuche ist nichts einzuwenden, aber für komplexere Probleme sind Chrome DevTools oder VS Code vielleicht besser geeignet. Diese Werkzeuge können dir helfen, stabileren Code zu erstellen, und du verbringst weniger Zeit mit der Fehlersuche.
Auf welche Debugging-Methode für Node.js schwörst du? Teile es uns in den Kommentaren unten mit!
Schreibe einen Kommentar