Skip to content

Debug de

Edgar Mesquita edited this page Oct 14, 2026 · 1 revision

Debugging und Entwicklungswerkzeuge

🌐 Diese Seite auf: English · Português · Español · Français · Deutsch

eQuantic.UI stellt professionelle Debugging-Werkzeuge bereit, ähnlich denen von Next.js, mit Funktionen nur für die Entwicklung, die dir helfen, Probleme schnell zu finden und zu beheben.

🔍 Erkennung des Entwicklungsmodus

Das Framework erkennt die Umgebung automatisch über IWebHostEnvironment.IsDevelopment() und macht sie dem Browser bekannt über:

window.__EQ_DEV__; // true in development, false in production

Alle Entwicklungswerkzeuge werden abhängig von diesem Flag geladen.

📝 Logger-System

Der Logger bietet eine einheitliche, mit Präfix versehene Protokollierung, die nur im Entwicklungsmodus ausgegeben wird.

Verwendung

import { logger } from "@equantic/ui-runtime";

// Nur Entwicklung (in der Produktion stumm)
logger.debug("Component state:", state);
logger.info("API call completed");

// Protokolliert immer (auch in der Produktion)
logger.warn("Deprecated API used");
logger.error("Failed to load data:", error);

Protokollstufen

Methode Ausgabe Produktion Präfix
debug() Console debug ❌ Stumm [eQuantic.UI]
info() Console info ❌ Stumm [eQuantic.UI]
warn() Console warn ✅ Immer [eQuantic.UI]
error() Console error ✅ Immer [eQuantic.UI]

Protokolle filtern

In den DevTools des Browsers kannst du nach dem Präfix filtern:

[eQuantic.UI]

Implementierung

Der Logger ist in src/eQuantic.UI.Runtime/src/utils/logger.ts implementiert:

const isDev = typeof window !== "undefined" && window.__EQ_DEV__;

export const logger = {
  debug(...args: any[]) {
    if (isDev) console.debug("[eQuantic.UI]", ...args);
  },

  info(...args: any[]) {
    if (isDev) console.info("[eQuantic.UI]", ...args);
  },

  warn(...args: any[]) {
    console.warn("[eQuantic.UI]", ...args);
  },

  error(...args: any[]) {
    console.error("[eQuantic.UI]", ...args);
  },
};

🚨 Fehlerüberlagerung

Die Fehlerüberlagerung bietet eine Vollbild-Fehleroberfläche im Stil von Next.js, die automatisch erscheint, wenn zur Laufzeit Fehler auftreten.

Eigenschaften

  • Automatische Erfassung: fängt unbehandelte Fehler und Promise-Ablehnungen
  • C#-Aufrufstapel ✨: die Überlagerung versteht Source Maps: sie holt die .js.map jedes Bundles, dekodiert sie (src/dev/source-map.ts + src/dev/stack-remapper.ts) und schreibt den Aufrufstapel als die ursprünglichen C#-Rahmen um, dazu einen Ausschnitt der fehlerhaften C#-Quellzeile. Fällt auf die JS-Ansicht zurück, wenn keine Karte verfügbar ist. (Das ist es, was das Versprechen „0 JS-Kenntnisse“ beim Debuggen echt macht: ein C#-Entwickler sieht C#, nicht transpiliertes JavaScript.)
  • Tastaturunterstützung: Esc drücken zum Schließen
  • Nur Entwicklung: erscheint nie in der Produktion (über ein dynamisches import() in der Entwicklung geladen)
  • Saubere UX: roter Kopf, nichtproportionale Schrift, scrollbarer Inhalt

Wann sie erscheint

Die Fehlerüberlagerung zeigt sich automatisch bei:

  1. Unbehandelten Fehlern: jede nicht gefangene Ausnahme in JavaScript
  2. Promise-Ablehnungen: unbehandelte asynchrone Fehler
// Dies löst die Fehlerüberlagerung im Entwicklungsmodus aus
throw new Error("Something went wrong");

Promise.reject("Async error");

await fetch("/api/data"); // Wenn der Fetch fehlschlägt und nicht gefangen wird

Die Oberfläche der Fehlerüberlagerung

┌─────────────────────────────────────────────┐
│ ⚠️ Build Error                    Close (Esc)│
├─────────────────────────────────────────────┤
│                                             │
│ Error message here                          │
│                                             │
│ ┌─────────────────────────────────────────┐ │
│ │ Stack trace:                            │ │
│ │   at MyComponent.render (page.js:42)    │ │
│ │   at Reconciler.patch (reconciler.js:12)│ │
│ │   ...                                   │ │
│ └─────────────────────────────────────────┘ │
│                                             │
├─────────────────────────────────────────────┤
│ This error overlay only appears in          │
│ development. Fix the error to continue.     │
└─────────────────────────────────────────────┘

Einen Fehler von Hand zeigen

Du kannst Fehler von Hand in der Überlagerung zeigen:

import { errorOverlay } from "@equantic/ui-runtime/dev";

if (window.__EQ_DEV__) {
  errorOverlay.show({
    message: "Custom error message",
    stack: error.stack,
    componentStack: "Component hierarchy...",
  });
}

Die Überlagerung leeren

// Programmatisch leeren
errorOverlay.clear();

// Aktionen des Nutzers
// - Die Esc-Taste drücken
// - Den Knopf "Close" anklicken

Implementierung

Die Fehlerüberlagerung ist in src/eQuantic.UI.Runtime/src/dev/error-overlay.ts implementiert:

class ErrorOverlay {
  private overlay: HTMLDivElement | null = null;
  private errors: ErrorInfo[] = [];

  show(error: ErrorInfo) {
    if (!window.__EQ_DEV__) return; // Nur Entwicklung

    this.errors.push(error);
    this.render();
  }

  clear() {
    this.errors = [];
    if (this.overlay) {
      this.overlay.remove();
      this.overlay = null;
    }
  }

  private render() {
    // Erzeugt eine Vollbild-Überlagerung mit den Details des Fehlers
  }
}

export const errorOverlay = new ErrorOverlay();

// Automatische Fehlererfassung
if (window.__EQ_DEV__) {
  window.addEventListener("error", (event) => {
    errorOverlay.show({
      message: event.message,
      stack: event.error?.stack,
    });
  });

  window.addEventListener("unhandledrejection", (event) => {
    errorOverlay.show({
      message: `Unhandled Promise Rejection: ${event.reason}`,
      stack: event.reason?.stack,
    });
  });
}

🛠️ Komponenten debuggen

DevTools des Browsers

Ein Debug-Build schreibt neben jedes Modul eine Source Map, von ihm verlinkt und mit dem C#, aus dem das Modul kam, sodass der Debugger des Browsers das C# selbst zeigt.

Chrome DevTools:

  1. Öffne die DevTools (F12)
  2. Gehe zum Tab Sources
  3. Finde die C#-Datei im Baum, benannt nach ihrem Pfad innerhalb des Projekts (Pages/Home.cs), nie nach dem der Build-Maschine
  4. Setze Haltepunkte direkt in der C#-Quelle
  5. Untersuche Zustand, Props und lokale Variablen

Source Maps

Was die Karte eines Moduls trägt, ist die Eigenschaft EQuanticSourceMaps, und ihre Vorgabe folgt der Konfiguration:

EQuanticSourceMaps Vorgabe für Was der Build schreibt
full Debug eine Karte neben jedem Modul, von ihm verlinkt, mit dem C# darin, für den Debugger des Browsers
external (ausdrücklich zu wählen) eine Karte ohne das C# und ohne den Link, für einen Fehlerberichter, der Karten selbst hochlädt; buns debugId bleibt erhalten
none jede andere Konfiguration keine Karte, und kein Link

Eine Karte mit dem C# darin ist die Quelle der Komponente, der Körper eines [ServerAction] eingeschlossen, und der Ausgabeordner ist ein Web-Root: darum schreibt nur Debug eine. Was die Eigenschaft auch sagt, eine Veröffentlichung nimmt keine Karte aus der Ausgabe des Compilers. Um Karten für einen bereitgestellten Build zu behalten, schreibe sie ohne das C#:

<PropertyGroup Condition="'$(Configuration)' == 'Release'">
  <EQuanticSourceMaps>external</EQuanticSourceMaps>
</PropertyGroup>

eqc setzt jede Karte selbst zusammen, in C#, ohne dass etwas installiert ist: bun bildet das JavaScript auf das TypeScript ab, das eqc geschrieben hat, eqcs eigene V3-Karte führt von diesem TypeScript zum C#, und die beiden werden eine Karte von dem JavaScript, das der Browser ausführt, zum C#. Ihre Quellen werden relativ zur Karte und innerhalb des Projekts benannt, sodass ein Debugger den eigenen Pfad des Projekts zeigt; eine Datei von außerhalb des Projekts (die Quellen eines Pakets im NuGet-Cache) wird allein nach ihrem Dateinamen benannt. Ein Modul, das aus mehr als einer Datei geschrieben ist, benennt jede davon, mit ihrem eigenen C#: eine Klasse, die den Default nimmt, den eine Schnittstelle liefert, führt die Zeilen des Defaults zur Datei der Schnittstelle, wo der Debugger sie zeigt (Seit 0.2.0-preview.60). Eine Karte ist JSON, was das C# auch enthält, ein Seitenvorschub oder jedes andere Steuerzeichen in einem Kommentar eingeschlossen (Seit 0.2.0-preview.60). Eine full-Karte, gekürzt:

{
  "version": 3,
  "sources": ["../../Pages/Home.cs"],
  "sourcesContent": ["…the C# of Pages/Home.cs…"],
  "mappings": "AAAA;AACA;..."
}

Jede Anweisung hat ein eigenes Segment (Seit 0.2.0-preview.58): ein Rahmen oder ein Haltepunkt landet auf der C#-Anweisung, die geworfen hat oder auf der er gesetzt wurde, nicht auf der ersten Zeile ihrer Methode. Eine Zeile, die der Compiler von sich aus schreibt, gehört zu der Anweisung, die sie erzeugt hat: das else if eines Muster-Switch bildet auf sein case ab, das Dispose eines using auf das using, die Bedingung eines do auf sich selbst, und die Anweisungen einer lokalen Funktion und der Ausdruck eines Mitglieds mit Ausdruckskörper auf ihre eigenen Zeilen. Der Block einer Lambda bildet ebenfalls Anweisung für Anweisung ab (Seit 0.2.0-preview.60), und was dem Block auf seiner Schlusszeile folgt, bildet wieder auf die Anweisung ab. Das gilt für eine Lambda, die an eine Instanz- oder statische Methode der App selbst übergeben wird, an eine eigene Methode einer Liste (ForEach, Find, Exists…) oder an Where, Select, Any, All, Aggregate und die anderen LINQ-Operatoren, die als eine Gestalt geschrieben sind, für eine in einem Sammlungsausdruck (children: [ … ]), und für eine, die von einer lokalen Variable, einer lokalen Funktion oder einem delegate gehalten wird; und seit derselben Version für eine innerhalb eines Mitglieds mit Ausdruckskörper, einer Objekterzeugung, eines Objekt- oder Sammlungsinitialisierers oder eines anonymen Objekts. Jede Anweisung eines Körpers mit einem out- oder ref-Parameter bildet ebenfalls auf ihre eigene Zeile ab. Dieser Körper läuft innerhalb einer Pfeilfunktion, die der Compiler hinzufügt, also hat ein im Browser gelesener Stapel einen Rahmen mehr als .NETs zwischen dem Werfen und dem Aufrufer. Anderswo bildet der Block einer Lambda vorerst auf die Anweisung ab, die ihn hält: an eine Erweiterungsmethode, an einen Delegaten oder über ?. übergeben, an First, Count, Sum, OrderBy, GroupBy und die anderen Operatoren mit eigener Übersetzung gegeben.

Das erlaubt dir:

  • Haltepunkte in C#-Code zu setzen
  • Durch C#-Logik zu schreiten
  • C#-Variablennamen zu untersuchen
  • Ursprüngliche Zeilennummern in Aufrufstapeln zu sehen

Komponenteninspektion

Um Zustand und Props einer Komponente zu untersuchen:

// In der Konsole des Browsers
window.__EQ_DEBUG = true; // Aktiviert den Debug-Modus

// Komponenten machen ihren Zustand bekannt
const component = document.querySelector(
  '[data-component-id="abc"]',
).__component;
console.log(component.state);
console.log(component.props);

🧪 Testen und Debuggen

Integrationstests mit Playwright

Zum Debuggen von Rendering-Problemen zwischen SSR und CSR:

test("SSR matches CSR", async ({ page }) => {
  // Das HTML des SSR holen
  const ssrResponse = await page.goto("http://localhost:5000");
  const ssrHtml = await ssrResponse.text();

  // Auf die Hydration des CSR warten
  await page.waitForLoadState("networkidle");
  const csrHtml = await page.content();

  // Vergleichen
  expect(normalizeHtml(ssrHtml)).toBe(normalizeHtml(csrHtml));
});

Debuggen auf der Serverseite

Debugge C#-Code normal mit Visual Studio oder VS Code:

  1. Setze Haltepunkte in .cs-Dateien
  2. Starte mit angehängtem Debugger: dotnet run
  3. Haltepunkte werden erreicht bei:
    • Dem Rendern auf der Serverseite (SSR)
    • Den Aufrufen von Server Actions
    • Der Kompilierung von Komponenten

Netzwerk-Debugging

Beobachte die Server Actions in den DevTools des Browsers:

  1. Öffne den Tab Network
  2. Filtere nach _equantic/actions
  3. Untersuche:
    • Die Nutzlast der Anfrage (Methodenname, Argumente)
    • Die Daten der Antwort
    • Die Zeitangaben
    • Die Fehler (mit Aufrufstapeln)

Debuggen des Asset-Anbieters

Wenn du IRequireAssets verwendest, prüfe, ob die Abhängigkeiten korrekt eingespeist werden:

  1. Quelle untersuchen: öffne im Browser „Seitenquelltext anzeigen“ und suche nach den Script- und Style-Tags.
  2. Network-Tab: prüfe, ob die externen URLs (z. B. CDNs) erfolgreich laden (Status 200).
  3. Deduplizierung: prüfe, dass mehrere Komponenten dasselbe Script nicht zweimal eingespeist haben.
  4. Reihenfolge: Stylesheets sollten für ein korrektes Rendern vor den Scripts erscheinen.

Assets beim Rendern auf der Serverseite (SSR)

Wenn im anfänglichen HTML Assets fehlen:

  1. Prüfe, dass die Komponente IRequireAssets implementiert.
  2. Stelle sicher, dass AddUI() in Program.cs aufgerufen wird.
  3. Prüfe, ob die AssetCollection die Assets während des Renderdurchgangs korrekt sammelt.

📊 Performance-Debugging

Performance zur Laufzeit

Der Reconciler verfolgt im Entwicklungsmodus Performance-Metriken:

// Aktiviert die Performance-Verfolgung
window.__EQ_PERF = true;

// Die Metriken ansehen
console.table(window.__EQ_PERF_DATA);

Die Metriken umfassen:

  • Renderzeit: wie lange jede Komponente zum Rendern brauchte
  • Diff-Zeit: im Reconciler verbrachte Zeit
  • DOM-Operationen: Zahl der tatsächlichen DOM-Änderungen
  • Ereignis-Listener: Zahl der aktiven Listener

Performance des Builds

Beobachte die Kompilierzeiten:

dotnet build -v:detailed

Achte auf:

  • Die Dauer des Targets CompileEQuanticUI
  • Die Zahl der kompilierten Komponenten
  • Die Zeit für die TypeScript-Erzeugung
  • Die Zeit für das Bündeln mit Bun

🔧 Häufige Probleme

runtime.js lädt nicht

Symptom: boot() wird nie ausgeführt, GET /_equantic/runtime.js gibt 404 zurück

Lösung: dem Browser antwortet der Server, nicht die Datei in wwwroot/_equantic/: app.MapUI() bildet /_equantic/runtime.js auf das Bundle ab, das die Assembly eQuantic.UI.Server einbettet. Ein 404 kommt entweder von diesem Endpunkt, dessen Antwort dann die Ressourcen benennt, die die Assembly tatsächlich trägt, oder von überhaupt keinem Endpunkt, prüfe also, dass app.MapUI() ausgeführt wird. Die eigene Kopie des Builds, wwwroot/_equantic/runtime.js, sind dieselben Bytes für die Werkzeuge, die die Module außerhalb eines Servers laden; das Target CopyEQuanticRuntime nimmt sie aus dem Server-Paket, und der Build scheitert mit „the served runtime was not found“, wenn es das nicht kann (siehe Paketarchitektur)

# The copy comes from the Server package (not the SDK's)
ls ~/.nuget/packages/equantic.ui.server/<version>/tools/runtime/

# Force rebuild
dotnet clean
dotnet build -v:n

Die Theme-Tokens fehlen im CSR

Symptom: das auf dem Server gerenderte HTML hat ein Theme, das Rendern auf der Clientseite nicht

Ursache: der Blob der Theme-Brücke wurde beim Start nicht übernommen

Lösung:

  1. Prüfe, dass runtime.js vor den Skripten der Komponenten lädt
  2. Suche in der Konsole des Browsers nach [eQuantic.UI] Boot process started
  3. Untersuche window.__EQ_THEME__ in der Konsole, es sollte das serialisierte Theme enthalten

Die Source Maps funktionieren nicht

Symptom: der ursprüngliche C#-Code lässt sich in den DevTools des Browsers nicht debuggen

Lösung:

  1. Baue in Debug, oder setze EQuanticSourceMaps auf full: ein Build in jeder anderen Konfiguration schreibt keine Karte, und eine external-Karte ist nicht von ihrem Modul verlinkt, also findet der Browser sie nicht von selbst
  2. Prüfe, dass die .map-Dateien in wwwroot/_equantic/ existieren
  3. Aktiviere Source Maps in den Einstellungen der DevTools des Browsers
  4. Leere den Cache des Browsers und baue neu
  5. Ein Rahmen, der im TypeScript-Zwischenstand (obj/eQuantic/ts/…) stehen bleibt, kommt von einer Karte, die eqc nicht zusammensetzen konnte, und eqc meldet jede davon im Build-Protokoll („… leads to the TypeScript only“)

Die Fehlerüberlagerung erscheint nicht

Symptom: Fehler werden in der Konsole protokolliert, aber es gibt keine Überlagerung

Prüfungen:

  1. Ist window.__EQ_DEV__ true? (In der Konsole prüfen)
  2. Ist die Fehlerüberlagerung importiert? (Prüfen, dass runtime.js sie einschließt)
  3. Ist das CSS der Fehlerüberlagerung geladen? (Nach Stilen von #equantic-error-overlay suchen)

Anzeige erzwingen:

// Manually trigger overlay
import { errorOverlay } from "@equantic/ui-runtime/dev";
errorOverlay.show({ message: "Test error" });

🎯 Bewährte Vorgehensweisen

Entwicklungsablauf

  1. Nutze den Logger freigebig: füge während der Entwicklung Debug-Protokolle hinzu, sie sind in der Produktion kostenlos
  2. Teste beide Modi: teste immer mit den Umgebungen Development und Production
  3. Beobachte das Netzwerk: halte den Network-Tab der DevTools offen, um fehlgeschlagene Server Actions zu erwischen
  4. Source Maps kommen mit Debug: ein Debug-Build schreibt sie (EQuanticSourceMaps ist dort full), es gibt also nichts zu aktivieren
  5. Nutze die Fehlerüberlagerung: unterdrücke Fehler nicht, lass sie die Überlagerung zeigen

Debuggen in der Produktion

Für Probleme in der Produktion:

  1. Serverprotokolle: sieh in den Protokollen von ASP.NET Core nach Fehlern von Server Actions
  2. Browser-Konsole: nur warn- und error-Protokolle erscheinen
  3. Sentry/AppInsights: binde Dienste zur Fehlerverfolgung ein
  4. Source Maps: eine Veröffentlichung liefert keine. Baue mit EQuanticSourceMaps=external und lade die Karten, die sie in wwwroot/_equantic/ schreibt, zu deinem Fehler-Tracker hoch; serviere niemals eine full-Karte aus einem Web-Root, da sie die Quelle der Komponente ist

Debugging-Checkliste

Bevor du Probleme meldest:

  • Sieh in der Konsole des Browsers nach Fehlern
  • Prüfe, dass window.__EQ_DEV__ true (Entwicklung) oder false (Produktion) ist
  • Bestätige, dass runtime.js lädt (Network-Tab)
  • Prüfe den Blob der Theme-Brücke (window.__EQ_THEME__)
  • Teste mit deaktiviertem Browser-Cache
  • Versuche es im Inkognito- oder privaten Modus
  • Vergleiche das HTML des SSR mit dem des CSR
  • Sieh die Ausgabe von MSBuild nach Warnungen durch
  • Prüfe, dass die NuGet-Pakete in den richtigen Versionen vorliegen

📚 Verwandte Dokumentation

Clone this wiki locally