Verwendung von log4net mit Dependency Injection

78

Ich versuche herauszufinden, was das richtige Muster und die richtige Verwendung von log4net mit einem Abhängigkeitsinjektionsframework ist.

Log4Net verwendet die ILog-Schnittstelle, erfordert jedoch einen Anruf

LogManager.GetLogger(Reflection.MethodBase.GetCurrentMethod().DeclaringType)

in jeder Klasse oder Methode, in der ich Informationen protokollieren muss. Dies scheint gegen die IoC-Prinzipien zu verstoßen und verbindet mich mit der Verwendung von Log4Net.

Sollte ich irgendwo eine andere Abstraktionsebene einfügen?

Außerdem muss ich benutzerdefinierte Eigenschaften wie den aktuellen Benutzernamen wie folgt protokollieren:

log4net.ThreadContext.Properties["userName"] = ApplicationCache.CurrentUserName;

Wie kann ich dies kapseln, damit ich nicht jedes Mal daran denken muss, es zu tun, und trotzdem die aktuelle Protokollierungsmethode beibehalten muss? soll ich so etwas machen oder verpasse ich die Marke total?

public static class Logger
{
    public static void LogException(Type declaringType, string message, Exception ex)
    {
        log4net.ThreadContext.Properties["userName"] = ApplicationCache.CurrentUserName;
        ILog log = LogManager.GetLogger(declaringType);
        log.Error(message, ex);
    }
}
Micah
quelle
1
Die interessantere Frage ist, welchen DI-Container Sie verwendet haben.
CodeMonkeyKing

Antworten:

61

Ich glaube, Sie sehen hier nicht den Wald vor lauter Bäumen. ILog und LogManager sind eine leichte Fassade, die fast 1: 1 der Apache-Commons-Protokollierung entspricht und Ihren Code nicht mit dem Rest von log4net koppelt.

<rant>
Ich habe auch festgestellt, dass fast immer, wenn jemand einen MyCompanyLogger-Wrapper um log4net erstellt, er den Punkt stark verfehlt und entweder wichtige und nützliche Funktionen des Frameworks verliert, nützliche Informationen wegwirft oder die möglichen Leistungssteigerungen verliert, die selbst über die vereinfachte ILog-Oberfläche möglich sind. oder alle oben genannten. Mit anderen Worten, das Umschließen von log4net, um eine Kopplung mit log4net zu vermeiden, ist ein Anti-Pattern .
</rant>

Wenn Sie das Bedürfnis haben, es zu injizieren, machen Sie Ihre Logger-Instanz über eine Eigenschaft zugänglich, um die Injektion zu aktivieren, aber erstellen Sie eine Standardinstanz auf die altmodische Weise.

Um den Kontextstatus in jede Protokollnachricht aufzunehmen, müssen Sie eine globale Eigenschaft hinzufügen, deren ToString()Auflösung dem entspricht, wonach Sie suchen. Als Beispiel für die aktuelle Heap-Größe:

public class TotalMemoryProperty
{
    public override string ToString()
    {
        return GC.GetTotalMemory(false).ToString();
    }
}

So schließen Sie es beim Start an:

GlobalContext.Properties["TotalMemory"] = new TotalMemoryProperty();
Jeffrey Hantin
quelle
7
Ihr Geschwätz ist hier weit von der Basis entfernt. Dieser Typ fragte, wie er eine Instanz von ILog in seine Objekte injizieren könne, die sich in einem Container befinden. Dafür gibt es viele Gründe.
CodeMonkeyKing
1
@CodeMonkeyKing: Der Rant war auf seinen vorgeschlagenen Ansatz gerichtet, eine Abstraktionsschicht zwischen log4net und seinem Code zu erstellen, und insbesondere auf den Beispielcodeblock am Ende der Frage.
Jeffrey Hantin
4
@ JeffreyHantin Es mag Ihrer Meinung nach ein Anti-Muster sein, aber ich habe festgestellt, dass es so viele Meinungen gibt, wie Ingenieure Code schreiben. Meine Meinung natürlich :)
CodeMonkeyKing
1
Mir ist klar, dass dies ein alter Beitrag ist, aber können Sie bitte erklären, warum ein solcher Wrapper "schlecht" ist? ILog und LogManager befinden sich im log4net-Namespace. Wenn ich diese Typen im gesamten Code verwende, bin ich dann nicht mit log4net verbunden? Was vermisse ich hier?
Andrew Stephens
1
Das Einwickeln von log4net ist eine gute Praxis. Das Problem ist nicht, die Bibliothek zu verpacken, es läuft so schlecht.
Dan
2

Ich habe meine eigene Schnittstelle erstellt und eine Klasse diese Schnittstelle implementieren lassen, die Log4Net verwendet, und diese Klasse injiziert. Auf diese Weise sind Sie nicht an Log4Net gebunden. Sie können einfach eine weitere Klasse für einen neuen Logger erstellen und diese Klasse einfügen.

CSharpAtl
quelle
2

Wenn Sie wirklich Bedenken haben, mehrere Protokollierer zu haben, können Sie jederzeit einen globalen Protokollierer deklarieren und diesen für alle Ihre Protokollierungsanforderungen aufrufen. Der Nachteil dabei ist, dass Sie die integrierte Fähigkeit verlieren, die Klasse zu identifizieren, von der das Protokoll ausgegeben wurde. Sie können jedoch auch Ihre eigene benutzerdefinierte Nachricht in das Protokoll einfügen, um die Klasse zu identifizieren.

Dies hängt davon ab, wie robust die Protokollierung sein soll. Sie können den Logger auch einfach in die Hauptklasse stellen und alle nicht behandelten Ausnahmen zur Protokollierung in die Luft sprudeln lassen.

Dillie-O
quelle
Aber sicherlich möchten Sie normalerweise mehr als nur Ausnahmen protokollieren?
UpTheCreek
Schlagen Sie hier das Singleton-Muster vor? Ich hoffe nicht.
Nate Zaugg
2

Eine andere Möglichkeit besteht darin, die Registrierungssequenz für den log4net ILog-Schnittstellencontainer wie folgt zu verkabeln: (unter Verwendung von Castle in einem ASP.NET-Kontext unten als Beispiel)

container.Register(Component.For<ILog>().LifeStyle
    .PerWebRequest.UsingFactoryMethod(() => LogManager.GetLogger(
    MethodBase.GetCurrentMethod().DeclaringType)));

und einfach ILog konstruieren, wann immer Sie es brauchen.

Santos
quelle
Das funktioniert besser, denke ich, wie Sie den richtigen Logger für die Implementierungsklasse bekommen aufgebaut ist:Component.For<ILog>().UsingFactoryMethod((kernel, model, cc) => LogManager.GetLogger(cc.Handler.ComponentModel.Implementation))
Mark