Dies kann etwas mit Pass ILogger oder ILoggerFactory an Konstruktoren in AspNet Core zusammenhängen? Hier geht es jedoch speziell um das Bibliotheksdesign und nicht darum, wie die eigentliche Anwendung, die diese Bibliotheken verwendet, ihre Protokollierung implementiert.
Ich schreibe eine .net Standard 2.0-Bibliothek, die über Nuget installiert wird. Damit Benutzer dieser Bibliothek Debug-Informationen abrufen können, bin ich auf Microsoft.Extensions.Logging.Abstractions angewiesen, damit ein standardisierter Logger injiziert werden kann.
Ich sehe jedoch mehrere Schnittstellen, und Beispielcode im Web verwendet manchmal ILoggerFactory
einen Logger im ctor der Klasse und erstellt ihn. Es gibt auch ILoggerProvider
eine schreibgeschützte Version der Factory, aber Implementierungen können beide Schnittstellen implementieren oder nicht, also müsste ich mich entscheiden. (Fabrik scheint häufiger als Anbieter).
Einige Codes, die ich gesehen habe, verwenden die nicht generische ILogger
Schnittstelle und teilen möglicherweise sogar eine Instanz desselben Loggers. Einige nehmen eine ILogger<T>
in ihren ctor und erwarten, dass der DI-Container offene generische Typen oder die explizite Registrierung jeder einzelnen ILogger<T>
Variation meiner Bibliothek unterstützt Verwendet.
Im Moment denke ich, dass dies ILogger<T>
der richtige Ansatz ist, und vielleicht ein Ctor, der dieses Argument nicht akzeptiert und stattdessen nur einen Null-Logger übergibt. Auf diese Weise wird keine verwendet, wenn keine Protokollierung erforderlich ist. Einige DI-Container wählen jedoch den größten ctor und würden daher trotzdem ausfallen.
Ich bin gespannt, was ich soll hier zu tun , die geringste Menge an Kopfschmerzen für die Nutzer zu schaffen , während immer noch die richtige Protokollierung Unterstützung ermöglicht , falls gewünscht.
Diese sind alle gültig bis auf
ILoggerProvider
.ILogger
undILogger<T>
sind das, was Sie für die Protokollierung verwenden sollen. Um eine zu erhaltenILogger
, verwenden Sie eineILoggerFactory
.ILogger<T>
ist eine Verknüpfung zum Abrufen eines Loggers für eine bestimmte Kategorie (Verknüpfung für den Typ als Kategorie).Wenn Sie die
ILogger
Protokollierung verwenden,ILoggerProvider
erhält jeder Registrierte die Möglichkeit, diese Protokollnachricht zu verarbeiten. Es ist nicht wirklich gültig, Code zu verbrauchen, umILoggerProvider
direkt in den zu rufen .quelle
ILoggerFactory
großartige Möglichkeit wäre, DI für Verbraucher zu verkabeln, indem nur eine Abhängigkeit besteht ( "Gib mir einfach eine Factory und ich erstelle meine eigenen Logger" ), aber die Verwendung eines vorhandenen Loggers verhindern würde ( es sei denn, der Verbraucher verwendet einen Wrapper). Wenn Sie einILogger
- generisches oder nicht - nehmen, kann der Verbraucher mir einen speziell vorbereiteten Logger geben, aber das DI-Setup ist möglicherweise viel komplexer (es sei denn, es wird ein DI-Container verwendet, der Open Generics unterstützt). Klingt das richtig? In diesem Fall denke ich, dass ich mit der Fabrik gehen werde.ILogger<T>
, nehme ich eineILogger<MyClass>
oder sogar nurILogger
- auf diese Weise kann der Benutzer sie mit nur einer einzigen Registrierung verkabeln, ohne dass offene Generika in seinem DI-Container erforderlich sind. Ich neige zum Nicht-GenerikumILogger
, werde aber am Wochenende viel experimentieren.Das
ILogger<T>
war das eigentliche, das für DI gemacht ist. Der ILogger wurde entwickelt, um das Factory-Muster viel einfacher zu implementieren, anstatt die gesamte DI- und Factory-Logik selbst zu schreiben. Dies war eine der klügsten Entscheidungen im asp.net-Kern.Sie können wählen zwischen:
ILogger<T>
Wenn Sie Factory- und DI-Muster in Ihrem Code verwenden müssen oder das verwenden könntenILogger
, um eine einfache Protokollierung zu implementieren, ohne dass DI erforderlich ist.Angesichts dessen ist The
ILoggerProvider
nur eine Brücke, um die Nachrichten des registrierten Protokolls zu verarbeiten. Es ist nicht erforderlich, es zu verwenden, da es nichts bewirkt, was Sie in den Code eingreifen sollten. Es hört auf den registrierten ILoggerProvider und verarbeitet die Nachrichten. Das ist alles.quelle
this ILogger
.Ich
ILogger<T>
halte mich an die Frage und halte sie für die richtige Option, wenn man die Nachteile anderer Optionen berücksichtigt:ILoggerFactory
wird Ihr Benutzer gezwungen, die Kontrolle über die veränderbare globale Logger-Factory an Ihre Klassenbibliothek weiterzugeben. Wenn SieILoggerFactory
Ihre Klasse jetzt akzeptieren, können Sie außerdem mit derCreateLogger
Methode in ein beliebiges Kategorienamen schreiben . WährendILoggerFactory
es normalerweise als Singleton im DI-Container verfügbar ist, würde ich als Benutzer bezweifeln, warum eine Bibliothek es verwenden müsste.ILoggerProvider.CreateLogger
aussieht, ist sie nicht für die Injektion vorgesehen. Es wird mit verwendet,ILoggerFactory.AddProvider
damit die Factory aggregierteILogger
Schreibvorgänge erstellen kann, die auf mehrereILogger
von jedem registrierten Anbieter erstellte Schreibvorgänge geschrieben werden. Dies wird deutlich, wenn Sie die Implementierung von überprüfenLoggerFactory.CreateLogger
ILogger
ebenfalls der richtige Weg zu sein, ist jedoch mit .NET Core DI nicht möglich. Dies klingt tatsächlich nach dem Grund, warum sie überhaupt zur VerfügungILogger<T>
stellen mussten.Wir haben also keine bessere Wahl, als
ILogger<T>
wenn wir aus diesen Klassen wählen würden.Ein anderer Ansatz wäre, etwas anderes zu injizieren, das nicht generisch umschließt
ILogger
, was in diesem Fall nicht generisch sein sollte. Die Idee ist, dass Sie durch das Umschließen mit Ihrer eigenen Klasse die volle Kontrolle darüber übernehmen, wie der Benutzer sie konfigurieren kann.quelle
Der Standardansatz soll sein
ILogger<T>
. Dies bedeutet, dass im Protokoll die Protokolle der jeweiligen Klasse deutlich sichtbar sind, da sie den vollständigen Klassennamen als Kontext enthalten. Wenn der vollständige Name Ihrer Klasse beispielsweise lautetMyLibrary.MyClass
, wird dies in den von dieser Klasse erstellten Protokolleinträgen angezeigt. Beispielsweise:Sie sollten das verwenden,
ILoggerFactory
wenn Sie Ihren eigenen Kontext angeben möchten. Zum Beispiel, dass alle Protokolle aus Ihrer Bibliothek anstelle jeder Klasse denselben Protokollkontext haben. Beispielsweise:Und dann sieht das Protokoll folgendermaßen aus:
Wenn Sie dies in allen Klassen tun, ist der Kontext nur MyLibrary für alle Klassen. Ich kann mir vorstellen, dass Sie dies für eine Bibliothek tun möchten, wenn Sie die innere Klassenstruktur in den Protokollen nicht verfügbar machen möchten.
In Bezug auf die optionale Protokollierung. Ich denke, Sie sollten immer den ILogger oder die ILoggerFactory im Konstruktor benötigen und es dem Konsumenten der Bibliothek überlassen, sie zu deaktivieren oder einen Logger bereitzustellen, der nichts in der Abhängigkeitsinjektion tut, wenn er keine Protokollierung wünscht. Es ist sehr einfach, die Protokollierung für einen bestimmten Kontext in der Konfiguration zu deaktivieren. Beispielsweise:
quelle
Für das Bibliotheksdesign wäre ein guter Ansatz:
1. Erzwingen Sie keine Verbraucher, Logger in Ihre Klassen einzufügen. Erstellen Sie einfach einen anderen Ctor, der NullLoggerFactory übergibt.
2. Begrenzen Sie die Anzahl der Kategorien, die Sie beim Erstellen von Protokollierern verwenden, damit Verbraucher die Filterung von Protokollen einfach konfigurieren können.
this._loggerFactory.CreateLogger(Consts.CategoryName)
quelle