Ich möchte einen nlogger in meiner Anwendung verwenden, möglicherweise muss ich in Zukunft das Protokollierungssystem ändern. Ich möchte also eine Holzfassade verwenden.
Kennen Sie Empfehlungen für vorhandene Beispiele, wie diese geschrieben werden? Oder geben Sie mir einfach einen Link zu einigen Best Practices in diesem Bereich.
Antworten:
Früher habe ich Protokollierungsfassaden wie Common.Logging verwendet (sogar um meine eigene CuttingEdge.Logging- Bibliothek auszublenden ), aber heute verwende ich das Abhängigkeitsinjektionsmuster. Dadurch kann ich Protokollierer hinter meiner eigenen (einfachen) Abstraktion ausblenden, die beiden Abhängigkeiten entspricht Inversionsprinzip und das Prinzip der Schnittstellentrennung(ISP), weil es ein Mitglied hat und weil die Schnittstelle von meiner Anwendung definiert wird; keine externe Bibliothek. Je besser das Wissen ist, das die Kernteile Ihrer Anwendung über die Existenz externer Bibliotheken haben, desto besser. auch wenn Sie nicht beabsichtigen, Ihre Protokollierungsbibliothek jemals zu ersetzen. Die starke Abhängigkeit von der externen Bibliothek erschwert das Testen Ihres Codes und erschwert Ihre Anwendung mit einer API, die nie speziell für Ihre Anwendung entwickelt wurde.
So sieht die Abstraktion in meinen Anwendungen oft aus:
Optional kann diese Abstraktion mit einigen einfachen Erweiterungsmethoden erweitert werden (so dass die Schnittstelle eng bleibt und weiterhin am ISP festhält). Dies macht den Code für die Benutzer dieser Schnittstelle viel einfacher:
Da die Schnittstelle nur eine einzige Methode enthält, können Sie problemlos eine
ILogger
Implementierung erstellen, die als Proxy für log4net fungiert , zu Serilog , Microsoft.Extensions.Logging , NLog oder jede andere Logging - Bibliothek und Ihre DI - Container konfigurieren es in Klassen zu injizieren , die eine habenILogger
in ihren Konstrukteur.Beachten Sie, dass sich statische Erweiterungsmethoden über einer Schnittstelle mit einer einzelnen Methode erheblich von einer Schnittstelle mit vielen Mitgliedern unterscheiden. Die Erweiterungsmethoden sind nur Hilfsmethoden, die eine
LogEntry
Nachricht erstellen und über die einzige Methode auf derILogger
Schnittstelle weiterleiten. Die Erweiterungsmethoden werden Teil des Verbrauchercodes. nicht Teil der Abstraktion. Dies ermöglicht nicht nur die Weiterentwicklung der Erweiterungsmethoden, ohne dass die Abstraktion, die Erweiterungsmethoden und die geändert werden müssenLogEntry
Konstruktoren werden immer ausgeführt, wenn die Logger-Abstraktion verwendet wird, auch wenn dieser Logger gestubbt / verspottet ist. Dies gibt mehr Sicherheit über die Richtigkeit der Aufrufe des Loggers, wenn er in einer Testsuite ausgeführt wird. Die eingliedrige Oberfläche erleichtert auch das Testen erheblich. Eine Abstraktion mit vielen Mitgliedern macht es schwierig, Implementierungen (wie Mocks, Adapter und Dekoratoren) zu erstellen.Wenn Sie dies tun, ist kaum eine statische Abstraktion erforderlich, die Protokollierungsfassaden (oder eine andere Bibliothek) bieten könnten.
quelle
Ich habe den kleinen Interface Wrapper + Adapter von https://github.com/uhaciogullari/NLog.Interface verwendet , der auch über NuGet verfügbar ist :
quelle
Ab sofort ist es am besten, das Microsoft.Extensions.Logging- Paket zu verwenden ( wie von Julian hervorgehoben) ). Die meisten Protokollierungsframeworks können damit verwendet werden.
Das Definieren Ihrer eigenen Benutzeroberfläche, wie in Stevens Antwort erläutert , ist für einfache Fälle in Ordnung, es fehlen jedoch einige Dinge, die ich für wichtig halte:
IsEnabled(LogLevel)
Sie sie aus Leistungsgründen noch einmal wünschen könntenSie können dies wahrscheinlich alles in Ihrer eigenen Abstraktion implementieren, aber an diesem Punkt werden Sie das Rad neu erfinden.
quelle
Generell ziehe ich es vor, eine Schnittstelle wie zu erstellen
und zur Laufzeit füge ich eine konkrete Klasse ein, die über diese Schnittstelle implementiert wird.
quelle
LogWarning
undLogCritical
Methoden und alle ihre Überlastungen. Wenn Sie dies tun, verletzen Sie das Prinzip der Schnittstellentrennung . Definieren Sie dieILogger
Schnittstelle lieber mit einer einzigenLog
Methode.LogEntry
und damit eine Abhängigkeit von einLoggingEventType
. DieILogger
Implementierung muss sichLoggingEventTypes
wahrscheinlich mit diesen befassencase/switch
, was ein Codegeruch ist . Warum dieLoggingEventTypes
Abhängigkeit verbergen ? Die Implementierung muss ohnehin mit den Protokollierungsstufen umgehen , daher ist es besser, explizit anzugeben, was eine Implementierung tun soll, als sie hinter einer einzelnen Methode mit einem allgemeinen Argument zu verbergen.ICommand
das ein hat, das einHandle
nimmtobject
. Implementierungen müssencase/switch
über mögliche Typen hinausgehen, um den Vertrag der Schnittstelle zu erfüllen. Das ist nicht ideal. Keine Abstraktion, die eine Abhängigkeit verbirgt, die sowieso behandelt werden muss. Verwenden Sie stattdessen eine Schnittstelle, die klar angibt, was erwartet wird: "Ich erwarte, dass alle Logger Warnungen, Fehler, Fatals usw. verarbeiten." Dies ist vorzuziehen gegenüber "Ich erwarte, dass alle Logger Nachrichten verarbeiten , die Warnungen, Fehler, Fatals usw. enthalten."LoggingEventType
sollte aufgerufen werden,LoggingEventLevel
da Typen Klassen sind und in OOP als solche codiert werden sollten. Für mich gibt es keinen Unterschied zwischen der Nichtverwendung einer Schnittstellenmethode und der Nichtverwendung des entsprechendenenum
Werts. Verwenden Sie stattdessenErrorLoggger : ILogger
,InformationLogger : ILogger
wo jeder Logger seine eigene Ebene definiert. Dann muss der DI die benötigten Logger einspeisen, wahrscheinlich über einen Schlüssel (enum), aber dieser Schlüssel ist nicht mehr Teil der Schnittstelle. (Sie sind jetzt fest).Eine großartige Lösung für dieses Problem ist das LibLog- Projekt.
LibLog ist eine Protokollierungsabstraktion mit integrierter Unterstützung für wichtige Protokollierer, einschließlich Serilog, NLog, Log4net und Enterprise Logger. Es wird über den NuGet-Paketmanager in einer Zielbibliothek als Quelldatei (.cs) anstelle einer DLL-Referenz installiert. Dieser Ansatz ermöglicht das Einbeziehen der Protokollierungsabstraktion, ohne dass die Bibliothek gezwungen wird, eine externe Abhängigkeit anzunehmen. Außerdem kann ein Bibliotheksautor die Protokollierung einschließen, ohne dass die konsumierende Anwendung gezwungen ist, der Bibliothek explizit einen Protokollierer bereitzustellen. LibLog verwendet Reflection, um herauszufinden, welcher konkrete Logger verwendet wird, und um ihn ohne expliziten Verdrahtungscode in den Bibliotheksprojekten anzuschließen.
LibLog ist also eine großartige Lösung für die Protokollierung in Bibliotheksprojekten. Referenzieren und konfigurieren Sie einfach einen konkreten Logger (Serilog für den Gewinn) in Ihrer Hauptanwendung oder Ihrem Hauptdienst und fügen Sie LibLog zu Ihren Bibliotheken hinzu!
quelle
Anstatt Ihre eigene Fassade zu schreiben, können Sie entweder Castle Logging Services oder Simple Logging Façade verwenden .
Beide enthalten Adapter für NLog und Log4net.
quelle
Seit 2015 können Sie auch .NET Core Logging verwenden, wenn Sie .NET Core-Anwendungen erstellen.
Das Paket, in das sich NLog einbinden kann, lautet:
quelle