Arbeit · 2026
jsDevLogging
Eine Logging-Bibliothek für Browser und Node — ohne Laufzeitabhängigkeiten, und per Script-Tag auch für PHP-Seiten.
Auf einen Blick
- Keine Laufzeitabhängigkeiten — 17 KB minifiziert als Browser-Bundle
- Transports für Console, HTTP, WebSocket, localStorage und Datei
- Puffert im Offline-Zustand und sendet nach der Rückkehr nach
- Middleware-Pipeline zum Anreichern, Filtern und Samplen
- Sub-Path-Exports, damit ungenutzte Transports nie im Bundle landen
- 152 Tests, ESM und CJS parallel, vollständige Typdefinitionen
Warum noch eine Logging-Bibliothek
Es gibt pino, winston, consola und ein Dutzend weitere. Über Features gewinnt man diesen Vergleich nicht. Interessant ist die Stelle, an der sie alle aussteigen: Sie setzen einen Bundler voraus.
Eine PHP-Seite hat keinen. Dort liegt ein Template, darin ein Script-Tag, und wenn im Browser
etwas schiefgeht, erfährt der Server davon nichts. Genau diese Lücke schließt jsDevLogging —
ein IIFE-Bundle von 17 KB, ein einzelnes Script-Tag, ein HttpTransport gegen ein
selbstgeschriebenes logs.php, und die Fehler aus dem Browser landen dort, wo man sie
tatsächlich sieht.
<script src="/assets/browser.min.js"></script>
<script>
const logger = jsDevLogging.createLogger({
transports: [new jsDevLogging.HttpTransport({ url: '/api/logs.php' })],
});
logger.error('Checkout fehlgeschlagen', { orderId: 4711 });
</script>
Null Abhängigkeiten, wörtlich gemeint
dependencies ist ein leeres Objekt. Kein Emitter-Polyfill, kein Formatierer, nichts —
was die Bibliothek braucht, steht in der Bibliothek, inklusive eines eigenen kleinen
Event-Emitters mit genau den drei Methoden, die der ConfigManager benutzt.
Das ist keine Prinzipienreiterei, sondern eine Rechnung. Ein Logger ist Infrastruktur: Er läuft in jedem Request, er läuft vor allem dann noch, wenn schon einiges andere kaputt ist, und er ist der denkbar schlechteste Ort für eine transitive Abhängigkeit, die man nie gelesen hat.
Der interessanteste Teil ist der Offline-Fall
Logs braucht man am dringendsten, wenn etwas nicht funktioniert — und „etwas funktioniert nicht“ heißt im Browser oft genug „die Verbindung ist weg“. Ein Transport, der ausgerechnet dann seine Einträge verliert, ist in dem Moment nutzlos, für den er gebaut wurde.
Der BufferedTransport hört deshalb auf die Offline-Events des Browsers, sammelt weiter im
Puffer statt zu senden, und schickt alles nach, sobald die Verbindung zurück ist. Der
HTTP-Transport bündelt Einträge ohnehin und wiederholt fehlgeschlagene Sendungen mit
wachsendem Abstand.
Was man nicht benutzt, lädt man auch nicht
Transports, Formatter und Middleware liegen hinter eigenen Sub-Path-Exports. Wer nur in die Konsole schreibt, bekommt den WebSocket-Transport nicht ins Bundle — nicht weil ein Bundler klug genug wäre, ihn wieder herauszuwerfen, sondern weil er nie importiert wurde.
Dazu ESM und CJS nebeneinander, vollständige .d.ts-Typen und 152 Tests, die bei jedem
npm publish durchlaufen müssen.