Bibliothek vs. Anwendung: Grundlegend unterschiedliche Logging-Anforderungen
- Anwendungs-Logging: Explizite Konfiguration und Verwaltung in einer Umgebung, die Entwickler direkt kontrollieren
- Bibliotheks-Logging: Wird in Projekte anderer eingebunden und muss die Benutzerumgebung und Wahlfreiheit respektieren
- Grenzen bestehender Ansätze: Werden anwendungszentrierte Logger (winston, Pino) auf Bibliotheken angewendet, entsteht ein Problem der Aufdringlichkeit
- Dilemma für Bibliotheksautoren: Debugging-Informationen bereitstellen, ohne Nutzer zusätzlich zu belasten
Probleme des aktuellen Bibliotheks-Loggings
- Fragmentiertes Logging-Ökosystem: Express nutzt
DEBUG=express:*, Mongoose mongoose.set('debug', true) und so weiter – jeweils auf unterschiedliche Weise
- Abhängigkeitsdilemma: Beim Einsatz anwendungszentrierter Bibliotheken wie winston oder Pino werden Nutzern unerwünschte Abhängigkeiten und Konfigurationen aufgezwungen
- Schweigen vs. Erzwingen: Entweder ganz auf Logging verzichten oder Nutzern eine Logging-Methode aufzwingen – zwei extreme Optionen
- Komplexität der Dependency Injection: Ein ausgefeilterer Ansatz, aber mit höherer API-Komplexität und mehr Aufwand für Nutzer
Die "Bibliothek-zuerst"-Philosophie von LogTape
- Bedingte Aktivierung: Wenn Logging nicht konfiguriert ist, bleibt es vollständig inaktiv; bei Konfiguration wird es zentral integriert verwaltet
- Wahlfreiheit für Nutzer: Die Bibliothek erzwingt keine Logging-Methode und wird nur aktiviert, wenn Nutzer es möchten
- Zero Dependencies: Mit 5,3 KB werden Supply-Chain-Sicherheitsrisiken vermieden und Versionskonflikte verhindert
- Vollständige ESM/CJS-Unterstützung: Löst Probleme in der Kompatibilitätskette und optimiert Bundles durch Tree Shaking
Praktische Vorteile
- Performance-Optimierung: Im deaktivierten Zustand nahezu kein Overhead, im aktivierten Zustand starke Konsolen-Ausgabe-Performance
- Namespace-Trennung: Hierarchische Kategorien im Format
["my-lib", "feature"] verhindern Kollisionen
- TypeScript-first-Design: Volle Type Safety ohne zusätzliche Typ-Pakete
- Brücke zu bestehenden Systemen: Unterstützt eine schrittweise Einführung über Adapter für winston und Pino
Praktische Überlegungen
- Bedeutung von Adaptern: Anerkennt die Realität, dass es noch keinen Ökosystem-Standard gibt, und bietet einen pragmatischen Kompromiss
- Inspiration aus dem Python-Ökosystem: Verweist auf das Erfolgsbeispiel von Python mit der integrierten Standardbibliothek
logging
- Zukunftsorientierter Ansatz: Wird als eine Option für die schrittweise Verbesserung des Bibliotheks-Ökosystems vorgestellt
7 Kommentare
Ich verstehe nicht so ganz, wie es ohne Konfiguration inaktiv sein soll.
Wenn man
getLoggeraufruft, wird der Logger ja bereits erstellt, und wenn mandebugausgibt, funktioniert es trotzdem.So wie ich den Code gesehen habe, wirkt es für mich nur so, als würde es einfach so aussehen, als würde es nicht funktionieren,
und auch String-Operationen werden dadurch nicht verzögert.
Deshalb verstehe ich nicht so ganz, worin sich dieses „inaktiv“ von anderen Bibliotheken unterscheidet, die bei gesetztem Log-Level die Ausgabe einfach nicht anzeigen.
Hm, werden Logs ausgegeben, obwohl
configure()/configureSync()nicht aufgerufen wurde? Wo werden sie ausgegeben? Erscheinen sie in der Konsole?Ah, mit „funktioniert“ meinte ich hier nicht, dass Logs in der
consoleoder in einer Datei gespeichert werden, sondern ob die Funktion ausgeführt wird und dadurch tatsächlich Overhead entsteht.Da kann es leicht zu einem Missverständnis kommen
Natürlich kann man angesichts der Tatsache, dass der Haupt-Overhead eines Loggers in den System Calls liegt, nicht wirklich sagen, dass es keinen Overhead gibt.
Aber ist das wirklich ein Unterscheidungsmerkmal gegenüber anderen Loggern? Eher nicht, denn andere Logger funktionieren letztlich genauso.
Aha, so war das gemeint. Zunächst einmal schien es, als gäbe es bei Benchmarks mit Null-Output praktisch keinen Overhead. Was ich jedoch für noch wichtiger hielt als den Performance-Overhead, war die Frage, ob das Standardverhalten ein no-op ist oder nicht. Aus Sicht von Bibliotheksautoren ist es nämlich problematisch, wenn zwar innerhalb der Bibliothek Logs geschrieben werden, diese dann aber bei der Ausführung der Anwendung, die die Bibliothek verwendet, ungefragt auf der Konsole oder in Dateien ausgegeben werden.
Ach so, es war SHOW GN.
In letzter Zeit entscheidet sich das Ökosystem oft für einen Ansatz, bei dem der Logger von außen injiziert wird, daher konnte ich mich damit wohl nicht so recht identifizieren.
Wenn er nicht konfiguriert ist, funktioniert er natürlich nicht.
Trotzdem scheint es besser zu sein, weil es zugleich eine Logger-Schnittstelle ist, die es in diesem Ökosystem bisher nicht gab, und weil sie viel Freiheit bietet.
Beim von Ihnen genannten Benchmark werden, soweit ich sehe, null output ohne System Calls ausgegeben,
daher denke ich, dass dieser Punkt je nach Form des internen Loggers eindeutig unterschiedlich ausfallen kann.
An dieser Stelle liegt der Unterschied zu Pino also bei bis zu dem Dreifachen. Krass.
FYI: Die Form des von außen injizierten Loggers, die ich zusätzlich erwähnt hatte, lässt sich recht leicht nachvollziehen, wenn Sie sich nur das Openai Node sdk ansehen, da dort der Logger ebenfalls von außen injiziert wird und dann Ausgaben erzeugt.
https://github.com/dahlia/logtape/…