c# - net - microsoft extensions logging static




Soll ich ILogger, ILogger<T>, ILoggerFactory oder ILoggerProvider für eine Bibliothek verwenden? (4)

Definition

Wir haben 3 Schnittstellen: ILogger , ILoggerProvider und ILoggerFactory . Schauen wir uns den Quellcode an, um ihre Verantwortlichkeiten herauszufinden:

ILogger : Die Hauptverantwortung besteht darin, eine Protokollmeldung mit einer bestimmten Protokollstufe zu schreiben.

ILoggerProvider : hat nur eine Verantwortung und das ist, einen ILogger zu erstellen.

ILoggerFactory : ILoggerFactory 2 Verantwortlichkeiten, um einen ILogger zu erstellen und einen ILogger hinzuzufügen.

Beachten Sie, dass wir einen oder mehrere Anbieter (Konsole, Datei usw.) im Werk registrieren können. Wenn wir einen Logger im Werk erstellen, werden alle registrierten Anbieter verwendet, um den entsprechenden Logger zu erstellen.

ILoggerFactory factory = new LoggerFactory().AddConsole();    // add console provider
factory.AddProvider(new LoggerFileProvider("c:\\log.txt"));   // add file provider
Logger logger = factory.CreateLogger(); // <-- creates a console logger and a file logger

Logger führt also eine Liste von Loggern und schreibt die Logmeldung an alle. Wenn wir uns den Quellcode von Logger ILoggers können wir bestätigen, dass Logger über ein Array von ILoggers (dh LoggerInformation []) verfügt und gleichzeitig die ILogger Schnittstelle implementiert.

Abhängigkeitsspritze

Die MS-Dokumentation bietet zwei Methoden zum Injizieren eines Loggers:

1. Injizieren der Fabrik:

public TodoController(ITodoRepository todoRepository, ILoggerFactory logger)
{
    _todoRepository = todoRepository;
    _logger = logger.CreateLogger("TodoApi.Controllers.TodoController");
}

Erstellt einen Logger mit Category = TodoApi.Controllers.TodoController .

2. Injizieren eines generischen ILogger<T> :

public TodoController(ITodoRepository todoRepository, ILogger<TodoController> logger)
{
    _todoRepository = todoRepository;
    _logger = logger;
}

Erstellt einen Logger mit Kategorie = vollqualifizierter Typname von T

Meiner Meinung nach ist es verwirrend, dass in der Dokumentation nichts über das Injizieren eines nicht generischen ILogger . Im obigen Beispiel wird ein nicht generisches ITodoRepository , und es wird dennoch nicht erklärt, warum wir für ILogger nicht dasselbe ILogger .

Laut Mark Seemann :

Ein Injection Constructor sollte nicht mehr tun, als die Abhängigkeiten zu erhalten.

Das Injizieren einer Factory in den Controller ist kein guter Ansatz, da der Controller nicht dafür verantwortlich ist, den Logger zu initialisieren (Verstoß gegen SRP). Das Injizieren eines generischen ILogger<T> fügt gleichzeitig unnötiges Rauschen hinzu. Siehe Stevens Beitrag im Simple Injector-Blog

Was injiziert werden sollte (zumindest gemäß dem obigen Artikel), ist ein nicht generischer ILogger , aber das kann der integrierte DI-Container von Microsoft nicht, und Sie müssen eine DI-Bibliothek eines Drittanbieters verwenden.

Dies ist ein weiterer Artikel von Nikola Malovic, in dem er seine 5 IoC-Gesetze erklärt.

Das 4. IoC-Gesetz von Nikola

Jeder Konstruktor einer Klasse, die aufgelöst wird, sollte keine andere Implementierung haben, als eine Reihe eigener Abhängigkeiten zu akzeptieren.

Dies kann etwas mit Pass ILogger oder ILoggerFactory für Konstruktoren in AspNet Core zu tun haben. Hierbei geht es jedoch speziell um das Bibliotheksdesign und nicht darum, wie die eigentliche Anwendung, die diese Bibliotheken verwendet, die Protokollierung implementiert.

Ich schreibe eine .net Standard 2.0-Bibliothek, die über Nuget installiert wird, und damit Benutzer dieser Bibliothek einige Debug-Informationen abrufen können, bin ich auf Microsoft.Extensions.Logging.Abstractions angewiesen, damit ein standardisierter Logger injiziert werden kann.

Es werden jedoch mehrere Schnittstellen ILoggerFactory , und Beispielcode im Web verwendet manchmal ILoggerFactory und erstellt einen Protokollierer im ctor der Klasse. Es gibt auch ILoggerProvider der wie eine schreibgeschützte Version der Factory aussieht, aber Implementierungen können beide Schnittstellen implementieren oder nicht, also müsste ich auswählen. (Fabrik scheint häufiger als Provider).

Einige der von mir gesehenen Codes verwenden die nicht generische ILogger Schnittstelle und verwenden möglicherweise sogar eine Instanz desselben Protokollierers. Einige verwenden ein ILogger<T> in ihrem ctor und erwarten, dass der DI-Container offene generische Typen oder die explizite Registrierung von jedem unterstützt und jede ILogger<T> meine Bibliothek verwendet.

Im Moment denke ich, dass ILogger<T> der richtige Ansatz ist, und vielleicht ein ctor, der dieses Argument nicht ILogger<T> und stattdessen einfach einen Null-Logger übergibt. Auf diese Weise wird keine Protokollierung verwendet, wenn keine Protokollierung erforderlich ist. Einige DI-Container wählen jedoch den größten ctor aus und würden daher trotzdem fehlschlagen.

Ich bin gespannt, was ich hier tun soll, um den Benutzern möglichst wenig Kopfzerbrechen zu bereiten und gleichzeitig auf Wunsch eine ordnungsgemäße Protokollierung zu ermöglichen.


Der ILogger<T> war der eigentliche, der für DI gemacht wurde. Der ILogger wurde entwickelt, um die Implementierung des Factory-Musters zu vereinfachen, anstatt die gesamte DI- und Factory-Logik selbst zu schreiben. Dies war eine der intelligentesten Entscheidungen in asp.net core.

Sie können wählen zwischen:

ILogger<T> Wenn Sie ILogger<T> und DI-Muster in Ihrem Code verwenden müssen oder den ILogger verwenden ILogger , können Sie eine einfache Protokollierung ohne DI implementieren.

ILoggerProvider , dass der ILoggerProvider nur eine Brücke ist, um jede der registrierten Protokollnachrichten zu verarbeiten. Es ist nicht erforderlich, es zu verwenden, da es keine Auswirkungen darauf hat, dass Sie in den Code eingreifen sollten. Es hört den registrierten ILoggerProvider ab und verarbeitet die Nachrichten. Das ist alles.


Diese sind bis auf den ILoggerProvider gültig. ILogger und ILogger <T> sollten für die Protokollierung verwendet werden. Um einen ILogger zu erhalten, verwenden Sie eine ILoggerFactory. Der 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 den ILogger zum Protokollieren verwenden, hat jeder registrierte ILoggerProvider die Möglichkeit, diese Protokollnachricht zu verarbeiten. Das direkte Aufrufen von Code in den ILoggerProvider ist für den Verbrauch nicht wirklich gültig.


Für das Bibliotheksdesign wäre ein guter Ansatz:

1. Erzwingen Sie nicht, dass Verbraucher Ihren Klassen Logger hinzufügen. Erstellen Sie einfach einen anderen ctor, indem Sie NullLoggerFactory übergeben.

class MyClass
{
    private readonly ILoggerFactory _loggerFactory;

    public MyClass():this(NullLoggerFactory.Instance)
    {

    }
    public MyClass(ILoggerFactory loggerFactory)
    {
      this._loggerFactory = loggerFactory ?? NullLoggerFactory.Instance;
    }
}

2. Begrenzen Sie die Anzahl der Kategorien, die Sie beim Erstellen von Protokolldateien verwenden, damit die Benutzer die Protokollfilterung problemlos konfigurieren können. this._loggerFactory.CreateLogger(Consts.CategoryName)





.net-standard