Skip to Content
Low-CodeTips and TricksErweiterte Debugging-Techniken

Erweiterte Debugging-Techniken

Ueber die grundlegenden Debug-Funktionen hinaus bietet Node-RED zahlreiche Moeglichkeiten, komplexe Fehler systematisch aufzuspueren. Diese Seite beschreibt fortgeschrittene Techniken, die in Produktivumgebungen besonders wertvoll sind.

Catch-All-Error-Handler

Ein globaler Catch Node faengt alle Fehler ab, die nicht von spezifischen Catch Nodes behandelt werden. Platzieren Sie ihn auf einem separaten Tab (z.B. “Error Handling”).

// Function Node: Fehlermeldung aufbereiten const errorInfo = { timestamp: new Date().toISOString(), flowName: msg.error.source.name || "unbekannt", nodeId: msg.error.source.id, nodeType: msg.error.source.type, message: msg.error.message }; msg.payload = errorInfo; msg.topic = "error/global"; return msg;

Flow-Aufbau:

[catch (alle Nodes)] → [function: Fehler aufbereiten] → [debug: Fehler-Log] → [http request: Fehler an Monitoring senden]

Wichtig: Setzen Sie die Catch Node auf “Catch errors from all nodes” und aktivieren Sie “Include errors handled by other Catch nodes” nur, wenn Sie eine zentrale Protokollierung wuenschen.

Message-Tracing mit Debug Nodes

Bei komplexen Flows ist es hilfreich, den Nachrichtenfluss an jedem Schritt zu verfolgen. Platzieren Sie Debug Nodes an strategischen Stellen und versehen Sie diese mit eindeutigen Namen.

// Function Node: Trace-Information hinzufuegen if (!msg._trace) { msg._trace = []; } msg._trace.push({ node: "Validierung", timestamp: Date.now(), payload: JSON.stringify(msg.payload).substring(0, 200) }); return msg;

Systematisches Tracing einrichten

  1. Einstiegspunkt markieren: Am Anfang jedes Flows eine _trace-Property initialisieren
  2. Checkpoints setzen: Nach jeder wichtigen Transformation einen Trace-Eintrag hinzufuegen
  3. Ausgangspunkt protokollieren: Am Ende des Flows die gesamte Trace-History ausgeben
// Function Node: Trace-Zusammenfassung am Ende des Flows const trace = msg._trace || []; const duration = trace.length > 1 ? trace[trace.length - 1].timestamp - trace[0].timestamp : 0; node.warn(`Flow abgeschlossen in ${duration}ms mit ${trace.length} Schritten`); return msg;

History-Logging ueber den Context

Nutzen Sie den Flow-Context, um eine Historie der letzten Ausfuehrungen zu speichern. Das ist besonders nuetzlich fuer intermittierende Fehler.

// Function Node: Ausfuehrungshistorie im Context speichern const maxEntries = 50; let history = flow.get("executionHistory") || []; history.unshift({ timestamp: new Date().toISOString(), msgId: msg._msgid, input: JSON.stringify(msg.payload).substring(0, 500), success: true }); // Nur die letzten Eintraege behalten if (history.length > maxEntries) { history = history.slice(0, maxEntries); } flow.set("executionHistory", history); return msg;

Historie abfragen

Erstellen Sie einen separaten HTTP-Endpunkt, um die Historie jederzeit abfragen zu koennen:

// Function Node: Historie ausgeben (verbunden mit http-in GET /debug/history) msg.payload = flow.get("executionHistory") || []; msg.headers = { "Content-Type": "application/json" }; return msg;

Performance-Profiling mit Zeitstempeln

Messen Sie die Ausfuehrungszeit einzelner Abschnitte, um Engpaesse zu finden.

// Function Node: Timer starten (am Anfang eines kritischen Abschnitts) msg._profiling = { start: process.hrtime.bigint(), section: "Datenbankabfrage" }; return msg;
// Function Node: Timer stoppen (am Ende des Abschnitts) if (msg._profiling) { const elapsed = Number(process.hrtime.bigint() - msg._profiling.start) / 1e6; node.warn(`[Profiling] ${msg._profiling.section}: ${elapsed.toFixed(2)}ms`); // Ergebnis im Context sammeln let metrics = flow.get("profilingMetrics") || {}; const section = msg._profiling.section; if (!metrics[section]) { metrics[section] = { count: 0, totalMs: 0, maxMs: 0 }; } metrics[section].count++; metrics[section].totalMs += elapsed; metrics[section].maxMs = Math.max(metrics[section].maxMs, elapsed); flow.set("profilingMetrics", metrics); delete msg._profiling; } return msg;

Bedingte Debug-Ausgabe

Statt Debug Nodes zu aktivieren und deaktivieren, koennen Sie eine Umgebungsvariable nutzen:

// Function Node: Nur bei aktiviertem Debug-Modus ausgeben const debugEnabled = env.get("DEBUG_FLOWS") === "true"; if (debugEnabled) { node.warn(`[DEBUG] Payload: ${JSON.stringify(msg.payload)}`); node.warn(`[DEBUG] Headers: ${JSON.stringify(msg.headers)}`); } return msg;

docker-compose.yaml:

environment: - DEBUG_FLOWS=true # Auf "false" setzen fuer Produktion

Fehler in asynchronem Code finden

Function Nodes mit asynchronem Code erfordern besondere Aufmerksamkeit:

// Function Node: Asynchronen Fehler korrekt abfangen try { const response = await fetch(env.get("API_URL") + "/data"); if (!response.ok) { node.error(`API-Fehler: HTTP ${response.status}`, msg); return null; } msg.payload = await response.json(); return msg; } catch (err) { node.error(`Netzwerkfehler: ${err.message}`, msg); msg.payload = { error: err.message }; msg.statusCode = 500; return msg; }

Zusammenfassung

TechnikEinsatzgebietAufwand
Catch-All-HandlerUnbehandelte Fehler auffangenGering
Message-TracingNachrichtenfluss nachvollziehenMittel
History-LoggingIntermittierende Fehler findenMittel
Performance-ProfilingEngpaesse identifizierenMittel
Bedingte Debug-AusgabeProduktion vs. EntwicklungGering
Async-FehlerbehandlungAPI-Aufrufe absichernGering

Weiterführende Informationen