EnglishGitHub
Prototyppackages/logger/src

Logger

@ralphschuler/logger

Formatiert Meldungen mit korrigiertem Level-Filter, begrenzter Historie und expliziter Ausgabe.

structured logsterminallevels

01 · Problem

Wofür braucht man das?

Diagnose braucht konsistente Level, strukturierte Metadaten und austauschbare Ausgaben, ohne Geschäftslogik an process.stdout oder process.exit zu koppeln.

02 · Denkmodell

Das mentale Modell

Ein LogRecord ist Daten. Ein Formatter übersetzt ihn, ein Sink schreibt ihn; TTY-Animationen sind ein optionaler Renderer oberhalb dieses Kerns.

Im Repository

Formatiert Meldungen mit korrigiertem Level-Filter, begrenzter Historie und expliziter Ausgabe. Das Singleton bleibt kompatibel; direkte Konstruktion erlaubt isolierte Tests. Fatal-Logs beenden den Prozess nicht.

03 · Kontrollfluss

Was passiert in welcher Reihenfolge?

  1. Level, Nachricht, Timestamp und Metadaten zu einem Record formen.
  2. Record vor Formatierung gegen eine klar geordnete Schwelle prüfen.
  3. Formatter erzeugt Text oder JSON ohne die Ursprungsdaten zu verändern.
  4. Ein injizierter Sink schreibt; Terminaleffekte laufen nur bei isTTY.

04 · Bauteile

Die entscheidenden Verträge

Logger / getInstanceErzeugt einen isolierten Logger oder liefert das kompatible Singleton.
trace / debug / info / warn / error / fatalFiltert nach Schweregrad und schreibt ohne Prozessbeendigung.
profile / profileSyncMisst Arbeit und räumt ihre Animation auf.

05 · Selbst bauen

Selbst implementieren

Baue einen kleinen Logger ohne Globals; JSON, hübsche Konsole und Animationen können danach als getrennte Adapter folgen.

  1. Definiere eine monotone Levelreihenfolge und teste die Schwelle als reine Funktion.
  2. Injiziere Sink, Uhr und Formatter über den Konstruktor.
  3. Lass fatal nie im Kern den Prozess beenden; der Aufrufer entscheidet über Lifecycle.
Konzeptskizze · kann ausgelassene Helfer verwenden
type Level = 10 | 20 | 30 | 40 | 50;
type Record = { at: Date; level: Level; message: string; meta?: object };

class Logger {
  constructor(
    private threshold: Level,
    private write: (record: Record) => void,
    private now = () => new Date(),
  ) {}

  log(level: Level, message: string, meta?: object) {
    if (level < this.threshold) return;
    this.write({ at: this.now(), level, message, meta });
  }
}

06 · Verifizieren

Was solltest du testen?

  • Für jede Schwelle werden exakt die erlaubten Level an den Sink weitergegeben.
  • Metadaten bleiben strukturiert und Formatterfehler verändern den Record nicht.
  • Nicht-TTY-Sinks starten keine Animation; Exceptions stoppen Timer zuverlässig.

Selbst ausprobieren

Formuliere die erste Prüfaussage als fehlschlagenden Test, implementiere die kleinste Korrektur und ergänze einen Grenzfall.

Für jede Schwelle werden exakt die erlaubten Level an den Sink weitergegeben.

Ansatz vergleichen

Dein Test sollte beim gezielten Verletzen der Invariante fehlschlagen und nach der Korrektur wieder bestehen. Halte den Aufbau deterministisch und prüfe ein beobachtbares Ergebnis.

07 · Grenzen

Kompromisse und Stolperfallen

  • Synchrone Sinks sind einfach, können unter Last aber blockieren.
  • Unbegrenzte Breadcrumbs und Metadaten können Speicher und sensible Daten akkumulieren.
  • Terminalsteuerung ist plattform- und TTY-abhängig.
Wichtig

Das Singleton bleibt kompatibel; direkte Konstruktion erlaubt isolierte Tests. Fatal-Logs beenden den Prozess nicht.

08 · Weiterdenken

Quellcode und verwandte Ideen

yarn tsx packages/logger/examples/basic.ts