Skip to main content
Version: 0.2

Diagnostics logger

src/diag/logger.ts is a self-contained, in-app log capture and viewer. It exists because browser DevTools choke when the app emits hundreds of thousands of repeated WebGL errors per second: the in-app logger dedupes and rate-limits so the stream stays readable.

It is imported first in main.ts so that boot-time errors are captured.

What it does

  • Patches the console: wraps console.log/info/warn/error/debug to fan out to both the real console (rate-limited) and an in-memory ring buffer. The originals are preserved on console.__original.
  • Captures global errors: listens for error and unhandledrejection.
  • Dedupes: identical messages collapse into a single row with a count, so a per-frame error firing 60 times a second shows as one entry with a rising count rather than thousands of lines.
  • Rate-limits the native console: identical messages pass through to the real console at most once per RATE_LIMIT_MS (250 ms).
  • Renders a floating panel: with copy, download, clear, and pause controls.

Internals

src/diag/logger.ts
const RING_LIMIT = 2000 // max entries kept in memory
const RATE_LIMIT_MS = 250 // throttle for the native console pass-through
const FLUSH_INTERVAL_MS = 100 // panel re-render cadence

Entries are stored in a ring array capped at RING_LIMIT, with a dedupe map keyed by level + '|' + message. When the ring overflows, the oldest entry is evicted from the ring, the dedupe map, and the lastNativeFlush map so none of the bookkeeping maps grow unbounded. The panel re-renders on a 100 ms timer only when something is dirty.

The panel

Toggle the panel with the small button in the bottom-right corner, or press Ctrl + Shift + L. The panel offers:

  • Copy: copy the full dump to the clipboard.
  • Download: save a .log file (named exepert-brain-diagnostics-<ts>.log).
  • Clear: empty the buffer.
  • Pause: stop capturing new entries.

The window.diag API

The logger exposes a small programmatic API on window.diag for app code that wants to log explicitly or drive the panel:

src/diag/logger.ts
;(window as any).diag = {
log: (msg, stack?) => record('log', msg, stack),
warn: (msg, stack?) => record('warn', msg, stack),
error: (msg, stack?) => record('error', msg, stack),
open: () => togglePanel(true),
close: () => togglePanel(false),
clear: clearAll,
dump: buildTextDump,
copy: copyAll,
download: downloadAll,
entries: () => ring.slice()
}
MethodAction
diag.log/warn/error(msg, stack?)Record an entry at the given level.
diag.open() / diag.close()Show/hide the panel.
diag.clear()Empty the buffer.
diag.dump()Return the full text dump as a string.
diag.copy()Copy the dump to the clipboard.
diag.download()Download the dump as a file.
diag.entries()Return a copy of the ring buffer.

Standalone build for the reference pages

The logger is also bundled as a standalone IIFE so the legacy reference pages can include it via a plain <script src> tag. The bundling is done by scripts/build-vanilla-logger.mjs and runs as part of dev and build. See Reference Pages.