
Sind Sie es immer noch leid, logger.info('ServiceName.methodName.')
Und logger.info('ServiceName.methodName -> done.')
Für jedes Niesen zu schreiben? Vielleicht haben Sie, wie ich, wiederholt darüber nachgedacht, dieses Geschäft zu automatisieren? In diesem Artikel werden wir über Class-Logger als eine der Lösungen für das Problem mit nur zwei Dekorateuren sprechen.
Warum ist es überhaupt notwendig?
Nicht alle Perfektionisten sind Ingenieure, aber alle Ingenieure sind Perfektionisten (na ja, fast). Wir mögen schöne lakonische Abstraktionen. Wir sehen die Schönheit in der Reihe der Krokodile, die eine Person unvorbereitet ist und nicht lesen kann. Wir fühlen uns gerne wie Götter und erschaffen Mikrouniversen, die nach unseren Regeln leben. Vermutlich stammen alle diese Eigenschaften aus unserer immensen Faulheit. Nein, der Ingenieur hat keine Angst davor, als Klasse zu arbeiten, aber er hasst die sich wiederholenden Routinemaßnahmen, die seine Arme zu automatisieren versuchen.
Nachdem wir mehrere tausend Codezeilen geschrieben haben, die nur für die Protokollierung vorgesehen sind, erstellen wir normalerweise einige unserer eigenen Muster und Standards für das Schreiben von Nachrichten. Leider müssen wir diese Muster immer noch manuell anwenden. Das Haupt-Warum des Klassenloggers besteht darin, eine deklarative, konfigurierbare Möglichkeit bereitzustellen, um Vorlagennachrichten beim Erstellen einer Klasse und beim Aufrufen ihrer Methoden einfach zu protokollieren.
Dame nackt!
Gehen wir direkt zum Code, ohne herumzuwandern.
import { LogClass, Log } from 'class-logger' @LogClass() class ServiceCats { @Log() eat(food: string) { return 'purr' } }
Dieser Dienst erstellt drei Protokolleinträge:
- Vor dem Erstellen mit einer Liste von Argumenten, die an den Konstruktor übergeben wurden.
- Vor dem Aufruf
eat
mit einer Liste von Argumenten. - Nach dem Aufruf von
eat
mit einer Liste von Argumenten und dem Ergebnis der Ausführung.
In Worten des Codes:
// ServiceCats // `ServiceCats.construct. Args: [].` const serviceCats = new ServiceCats() // `eat` // `ServiceCats.eat. Args: [milk].` serviceCats.eat('milk') // `eat` // `ServiceCats.eat -> done. Args: [milk]. Res: purr.`
Poke es live .
Vollständige Liste der Ereignisse, die verpfändet werden können:
- Vor dem Erstellen einer Klasse.
- Vor dem Aufrufen von statischen und asynchronen Methoden und Funktionseigenschaften, sowohl statisch als auch nicht statisch.
- Nach dem Aufruf synchroner und asynchroner Methoden und Funktionseigenschaften, sowohl statisch als auch nicht statisch.
- Fehler beim Aufrufen von statischen und asynchronen Methoden und Funktionseigenschaften, sowohl statisch als auch nicht statisch.
Eine funktionale Eigenschaft ist eine Pfeilfunktion, die einer Eigenschaft zugewiesen ist ( class ServiceCats { private meow = () => null }
). Wird am häufigsten verwendet, um den Ausführungskontext ( this
) aufrechtzuerhalten.
Uns wurde eine "konfigurierbare" Art der Protokollierung versprochen
Klassenlogger drei Ebenen der Konfigurationshierarchie:
Beim Aufrufen jeder Methode werden alle drei Ebenen zusammengeführt. Die Bibliothek bietet eine angemessene globale Standardkonfiguration, sodass sie ohne vorherige Konfiguration verwendet werden kann.
Globale Konfiguration
Es wird für die gesamte Anwendung erstellt. Es kann mit setConfig
überschrieben setConfig
.
import { setConfig } from 'class-logger' setConfig({ log: console.info, })
Klassenkonfiguration
Es ist für jede Klasse eindeutig und wird auf alle Methoden dieser Klasse angewendet. Kann die globale Konfiguration überschreiben.
import { LogClass } from 'class-logger' setConfig({ log: console.info, }) @LogClass({ // log: console.debug, }) class ServiceCats {}
Methodenkonfiguration
Funktioniert nur für die Methode selbst. Es hat Vorrang vor der Klassenkonfiguration und der globalen Konfiguration.
import { LogClass } from 'class-logger' setConfig({ log: console.info, }) @LogClass({ // log: console.debug, }) class ServiceCats { private energy = 100 @Log({ // log: console.warn, }) eat(food: string) { return 'purr' } // `console.debug` sleep() { this.energy += 100 } }
Live stecken
Was kann konfiguriert werden?
Wir haben untersucht, wie die Konfiguration geändert werden kann, haben jedoch noch nicht herausgefunden, was genau daran geändert werden kann.
Hier finden Sie viele Beispiele für Konfigurationsobjekte für verschiedene Fälle .
Link zur Benutzeroberfläche, wenn Sie TypeScript besser verstehen. als russisch :)
Das Konfigurationsobjekt hat die folgenden Eigenschaften:
log
Dies ist eine Funktion, die sich tatsächlich mit der Protokollierung befasst. Sie erhält eine formatierte Nachricht als Zeichenfolge am Eingang. Wird verwendet, um die folgenden Ereignisse zu protokollieren:
- Vor dem Erstellen einer Klasse.
- Vor dem Aufrufen von statischen und asynchronen Methoden und Funktionseigenschaften, sowohl statisch als auch nicht statisch.
- Nach dem Aufruf synchroner und asynchroner Methoden und Funktionseigenschaften, sowohl statisch als auch nicht statisch.
Standardmäßig console.log
.
logError
Dies ist eine Funktion, die sich auch mit der Protokollierung befasst, jedoch nur mit Fehlermeldungen. Sie erhält auch eine formatierte Nachricht als Zeichenfolge. Wird verwendet, um die folgenden Ereignisse zu protokollieren:
- Fehler beim Aufrufen von statischen und asynchronen Methoden und Funktionseigenschaften, sowohl statisch als auch nicht statisch.
Der Standardwert ist console.error
.
Dies ist ein Objekt mit zwei Methoden: start
und end
. Diese Methoden erstellen dieselbe formatierte Nachricht.
start
erstellt Nachrichten für die folgenden Ereignisse:
- Vor dem Erstellen einer Klasse.
- Vor dem Aufrufen von statischen und asynchronen Methoden und Funktionseigenschaften, sowohl statisch als auch nicht statisch.
end
generiert Nachrichten für die folgenden Ereignisse:
- Nach dem Aufruf synchroner und asynchroner Methoden und Funktionseigenschaften, sowohl statisch als auch nicht statisch.
- Fehler beim Aufrufen von statischen und asynchronen Methoden und Funktionseigenschaften, sowohl statisch als auch nicht statisch.
Standardmäßig new ClassLoggerFormatterService()
.
einschließen
Die Konfiguration dessen, was in der endgültigen Nachricht enthalten sein soll.
args
Es kann entweder ein Boolescher Wert oder ein Objekt sein.
Wenn es boolean
, wird festgelegt, ob eine Liste von Argumenten (erinnern Args: [milk]
an Args: [milk]
?) In alle Nachrichten ( start
und end
) aufgenommen werden soll.
Wenn es sich um ein Objekt handelt, muss es zwei boolesche Eigenschaften haben: start
und end
. start
gibt an, ob eine Argumentliste für Startnachrichten, end
für end
eingefügt werden soll.
Der Standardwert ist true
.
konstruieren
Dies ist ein boolean
, der steuert, ob die Klassenerstellung protokolliert werden soll oder nicht.
Der Standardwert ist true
.
Ergebnis
Dies ist ein boolean
Wert, der die Protokollierung des Rückgabewerts von der Methode steuert. Es ist anzumerken, dass der Rückgabewert nicht nur das ist, was die Methode bei erfolgreicher Ausführung zurückgibt, sondern auch der Fehler, den sie bei nicht erfolgreicher Ausführung auslöst. Res: purr
du dich an Res: purr
? Wenn dieses Flag false
, gibt es kein Res: purr
.
Der Standardwert ist true
.
classInstance
Es kann entweder ein Boolescher Wert oder ein Objekt sein. Das Konzept ist dasselbe wie include.args
.
Fügt Posts eine serialisierte Instanzansicht Ihrer Klasse hinzu. Mit anderen Worten, wenn Ihre Klasse Eigenschaften hat, werden diese in JSON serialisiert und den Protokollen hinzugefügt.
Nicht alle Eigenschaften sind serialisierbar. Der Klassenlogger verwendet die folgende Logik:
- Wir übernehmen alle unsere eigenen (diejenigen, die nicht im Prototyp enthalten sind) Eigenschaften von Intansa.
- Warum? Sehr selten ändert sich ein Prototyp dynamisch, daher ist es wenig sinnvoll, seinen Inhalt zu protokollieren.
- Wir werfen aus ihnen alle Typfunktionen heraus.
- Warum? Meistens sind Eigenschaften der Typfunktion einfach Pfeilfunktionen, die nicht mit herkömmlichen Methoden erstellt werden, um den Kontext aufrechtzuerhalten (
this
). Sie sind selten dynamischer Natur, daher macht es keinen Sinn, sie zu protokollieren.
- Wir werfen alle Objekte aus ihnen heraus, die keine einfachen Objekte sind.
- Was sind diese einfachen Objekte?
ClassLoggerFormatterService
betrachtet ein Objekt als einfach, wenn sein Prototyp Object.prototype
. - Warum? Oft fungieren Instanzen anderer Serviceklassen als Eigenschaften. Unsere Protokolle würden auf hässlichste Weise anschwellen, wenn wir mit der Serialisierung beginnen würden.
- Serialisieren Sie alles, was noch übrig ist, in der JSON-Zeichenfolge.
class ServiceA {} @LogClass({ include: { classInstance: true, }, }) class Test { private serviceA = new ServiceA() private prop1 = 42 private prop2 = { test: 42 } private method1 = () => null @Log() public method2() { return 42 } } // `Test` // 'Test.construct. Args: []. Class instance: {"prop1":42,"prop2":{"test":42}}.' const test = new Test() // `method2` // 'Test.method2. Args: []. Class instance: {"prop1":42,"prop2":{"test":42}}.' test.method2() // `method2` // 'Test.method2 -> done. Args: []. Class instance: {"prop1":42,"prop2":{"test":42}}. Res: 42.'
Der Standardwert ist false
.
Was ist, wenn Ihnen die Idee gefällt, Ihre Vorstellung von der Schönheit jedoch auf einem anderen Nachrichtenzeilenformat besteht? Sie können die vollständige Kontrolle über die Nachrichtenformatierung übernehmen, indem Sie Ihren eigenen Formatierer übergeben.
Sie können Ihren eigenen Formatierer von Grund auf neu schreiben. Natürlich. Diese Option wird in diesem Framework jedoch nicht behandelt (wenn Sie an dieser speziellen Option interessiert sind, lesen Sie den Abschnitt "Formatierung" in README).
Der schnellste und einfachste Weg, die Formatierung zu überschreiben, besteht darin, Ihren Formatierer vom Standardformatierer - ClassLoggerFormatterService
- zu ClassLoggerFormatterService
.
ClassLoggerFormatterService
verfügt über die folgenden protected
Methoden, mit denen kleine Blöcke endgültiger Nachrichten erstellt werden:
base
- Gibt den Klassennamen und den Methodennamen zurück. Beispiel:
ServiceCats.eat
.
operation
- Gibt
-> done
oder -> error
je nachdem, ob die Methode erfolgreich abgeschlossen wurde.
args
- Gibt eine serialisierte Argumentliste zurück. Beispiel:.
. Args: [milk]
. Es verwendet Fast-Safe-Stringify, um Objekte unter der Haube zu serialisieren.
classInstance
- Gibt die serialisierte Instanz der Klasse zurück. Beispiel:.
. Class instance: {"prop1":42,"prop2":{"test":42}}
. Wenn Sie include.classInstance
in die Konfiguration aufgenommen haben, die Instanz selbst jedoch aus irgendeinem Grund zum Zeitpunkt der Protokollierung nicht verfügbar ist (z. B. für statische Methoden oder vor dem Erstellen einer Klasse), wird N/A
result
- Gibt ein serialisiertes Ausführungsergebnis oder einen serialisierten Fehler zurück. Verwendet Fast-Safe-Stringify für Objekte unter der Haube. Ein serialisierter Fehler enthält die folgenden Eigenschaften:
- Der Name der Klasse (Funktion), mit der der Fehler erstellt wurde (
error.constructor.name
). - Code (
error.code
). - Nachricht (
error.message
). - Name (
error.name
). error.stack
( error.stack
).
final
Die Startnachricht besteht aus:
base
args
classInstance
final
Die Endnachricht besteht aus:
base
operation
args
classInstance
result
final
Sie können nur die grundlegenden Methoden überschreiben, die Sie benötigen.
Schauen wir uns an, wie Sie allen Posts einen Zeitstempel hinzufügen können.
Ich sage nicht, dass dies in realen Projekten geschehen sollte. Pino , Winston und die meisten anderen Logger können dies selbst tun. Dieses Beispiel dient nur zu Bildungszwecken.
import { ClassLoggerFormatterService, IClassLoggerFormatterStartData, setConfig, } from 'class-logger' class ClassLoggerTimestampFormatterService extends ClassLoggerFormatterService { protected base(data: IClassLoggerFormatterStartData) { const baseSuper = super.base(data) const timestamp = Date.now() const baseWithTimestamp = `${timestamp}:${baseSuper}` return baseWithTimestamp } } setConfig({ formatter: new ClassLoggerTimestampFormatterService(), })
Live stecken
Fazit
Denken Sie daran, die Installationsanweisungen zu befolgen und sich mit den Anforderungen vertraut zu machen , bevor Sie diese Bibliothek in Ihrem Projekt verwenden.
Ich hoffe, Sie haben keine Zeit umsonst verschwendet, und der Artikel war zumindest ein wenig nützlich für Sie. Bitte treten und kritisieren. Wir werden lernen, gemeinsam besser zu codieren.