Die Turbolog-Engine

Wie Frames per Lauflängenkodierung in einen console.log-Aufruf passen, dazu CellBuffer, Chromium-Details und Benchmarks.

Turbolog macht aus jedem React-Commit einen console.log-Aufruf oder keinen, wenn sich keine Zelle geändert hat.

Ziele

  1. Ein Aufruf pro geändertem Frame.
  2. Keine Aufrufe im Leerlauf. Bei einem unveränderten Frame kehrt die Funktion zurück, bevor sie einen String erstellt.
  3. Wenige Stilargumente. 149 %c-Spans für die 3200 Zellen eines dicht gefüllten 100×32-Dashboards.

Pipeline

React ─▶ Yoga ─▶ CellBuffer ─▶ row-hash gate ─▶ RLE ─▶ console.log

CellBuffer

Der Frame liegt in flachen typisierten Arrays für Zeichen, Farben und Attribute, deren Speicher einmalig reserviert wird.

Änderungsprüfung

Für jede Zeile wird ein Hash berechnet. Wenn sich nichts geändert hat, kehrt der Treiber frühzeitig zurück:

if (frame === null) return SKIPPED; // 0 console callsconsole.log(frame.format, ...frame.args);

Frame-Wechsel

Turbolog hängt Frames an und leert die Konsole nur beim ersten Frame, nach invalidate(), im Leerlauf, sobald sich 100 Frames angesammelt haben, und bei 300. DevTools entfernt gelöschte Meldungen sofort, zeichnet neue aber gedrosselt in Abständen von 50 ms, sodass beim Leeren nach jedem Frame kurz eine leere Konsole aufblitzt. Angehängte Frames wechseln innerhalb eines einzigen Zeichenvorgangs, und ein verdeckender Span mit einer Breite von null hält den vorherigen Frame außerhalb des sichtbaren Bereichs. Scrolle nach oben, um frühere Frames zu sehen.

Frames werden auf etwa 15 fps getaktet. Schnellere Frames setzen die DevTools-Drosselung zurück und werden verworfen.

Lauflängenkodierung

Übereinstimmende Zellen verschmelzen zu einem %c-Span. Spans laufen über Zeilenumbrüche hinweg weiter, und CSS-Strings werden interniert.

console.log("%c▼ Text.js%c ready", "color:#ededed;font-weight:700", "color:#45a557");

Chromium-Details

  • Ein wörtliches % wird als %% maskiert.
  • URLs und path:line-Text erhalten einen Word Joiner, damit DevTools ihren Stil nicht ändert.
  • Spans verwenden nur ausgeschriebene CSS-Eigenschaften. DevTools kopiert jede aufgelöste Eigenschaft auf jede Zeile.
  • Spans sind so abgestimmt, dass Hintergründe lückenlos aneinander anschließen.

Firefox und Safari

Chromium stellt jede Zeile eines %c-Spans als Inline-Block dar. Firefox und Safari belassen Spans inline, ignorieren daher vertikale Außenabstände und brechen lange Zeilen um. CssConsoleDriver erkennt den Browser durch CSS-Funktionstests und setzt für diese beiden Konsolen eine Zeilenhöhe von 14 px ohne Außenabstände, schaltet den Zeilenumbruch mit text-wrap-mode: nowrap aus und schiebt frühere Frames mit einem verdeckenden line-height: 100vh-Span aus dem sichtbaren Bereich. Beim Einpassen in die Konsole bleibt Platz für die Spalte mit Quellverweisen in Firefox. Safari setzt Menlo an die erste Stelle der Schriftartenliste, weil seine Seiten eine vom Benutzer installierte SF Mono nicht verwenden können, mit der Web Inspector möglicherweise Text zeichnet. Übergib browser an createConsoleRoot, um selbst eine Konsole auszuwählen.

Benchmarks

packages/core/test/perf.test.ts auf einem dicht gefüllten 100×32-Dashboard, Mittelwert aus 1000 Frames (Apple M5 Pro, Bun 1.4.4):

FrameZeit%c-Spans
Vollständiges Neuzeichnen (Layout, Zeichnen, Kodieren)0,163 ms149
Änderung einer Zeile0,011 ms149
Leerlauf0,000 ms0

Die Statusseite führt dieselbe Pipeline in deinem Browser aus. Führe text.stats() in DevTools aus, während eine Demo aus dem Playground läuft.

Diese Seite auf GitHub bearbeiten