NLog: Regeln und Filter
Bei Confirmit verwenden wir die NLog- Bibliothek zum Anmelden in unseren .NET-Anwendungen. Obwohl für diese Bibliothek Dokumentation vorhanden ist, war es für mich schwierig zu verstehen, wie alles funktioniert. In diesem Artikel werde ich versuchen zu erklären, wie Regeln und Filter in NLog angewendet werden. Fangen wir an.
So konfigurieren Sie NLog
Und wir beginnen mit einer kleinen Erinnerung daran, was wir mit der NLog-Konfiguration tun können. Im einfachsten Fall handelt es sich bei dieser Konfiguration um eine XML-Datei (z. B. NLog.config):
<?xml version="1.0" encoding="utf-8" ?> <nlog xmlns="http://www.nlog-project.org/schemas/NLog.xsd" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"> <targets> <target name="target1" xsi:type="ColoredConsole" layout="Access Log|${level:uppercase=true}|${logger}|${message}"> <highlight-row condition="true" foregroundColor="red"/> </target> <target name="target2" xsi:type="ColoredConsole" layout="Common Log|${level:uppercase=true}|${logger}|${message}"> <highlight-row condition="true" foregroundColor="green"/> </target> <target name="target3" xsi:type="ColoredConsole" layout="Yellow Log|${level:uppercase=true}|${logger}|${message}"> <highlight-row condition="true" foregroundColor="yellow"/> </target> </targets> <rules> <logger name="*" minlevel="Warn" writeTo="target1,target2,target3" /> </rules> </nlog>
Sie können diese Datei mit einer Codezeile herunterladen:
LogManager.Configuration = new XmlLoggingConfiguration("NLog.config");
Was können wir damit machen? Wir können mehrere Nachrichtenempfänger (Ziel) auf die Regel setzen:
<rules> <logger name="*" minlevel="Warn" writeTo="target1,target2,target3" /> </rules>
Wir können bestimmen, für welche Protokollierungsstufen diese Regel angewendet wird:
<rules> <logger name="*" minlevel="Warn" writeTo="target1" /> <logger name="*" levels="Debug,Warn,Info" writeTo="target2" /> </rules>
Wir können Filter für jede Regel festlegen:
<rules> <logger name="*" minlevel="Info" writeTo="target1"> <filters defaultAction='Log'> <when condition="contains('${message}','Common')" action="Ignore" /> </filters> </logger> </rules>
Und schließlich können wir verschachtelte Regeln definieren:
<rules> <logger name="*" minlevel="Info" writeTo="target1"> <logger name="*" minlevel="Warn" writeTo="target2" /> </logger> </rules>
Es ist Zeit herauszufinden, wie das alles funktioniert.
Erstellen einer Logger-Konfiguration
Wenn Sie eine Logger-Instanz anfordern,
var commonLogger = LogManager.GetLogger("Common");
NLog nimmt entweder eine vorhandene aus dem Cache oder erstellt eine neue (siehe hier ). Im letzteren Fall wird auch eine Konfiguration für den Logger mit dem angegebenen Namen erstellt. Schauen wir uns den Prozess der Erstellung an.
Kurz gesagt, die Logger-Konfiguration besteht aus einer separaten Kette von Empfängern und entsprechenden Filtern für jede Protokollierungsstufe ( Trace
, Debug
, Info
, Warn
, Error
, Fatal
) (siehe hier ). Jetzt werde ich Ihnen zeigen, wie diese Ketten aufgebaut sind.
Die Hauptmethode zum Erstellen dieser Ketten ist GetTargetsByLevelForLogger der LogFactory- Klasse. So funktioniert es. Alle in der NLog-Konfiguration angegebenen Regeln werden nacheinander ausgewählt. Zunächst wird überprüft, ob der Regelname mit dem Loggernamen übereinstimmt. Regelnamen können Platzhalter enthalten, z. B. solche, die wir für Dateisystemobjekte verwenden:
*
- eine beliebige Folge von Zeichen?
- ein einzelnes Zeichen
Daher stimmt der Regelname ' *
' mit jedem Loggernamen überein, und ' Common*
' stimmt mit allen Loggern überein, deren Namen mit ' Common
' beginnen.
Wenn der Regelname nicht mit dem Loggernamen übereinstimmt, wird diese Regel mit allen darin eingebetteten Regeln verworfen. Andernfalls GetTargetsByLevelForLogger
die GetTargetsByLevelForLogger
Methode alle Protokollierungsstufen ab, für die diese Regel aktiviert ist. Für jede dieser Ebenen fügt NLog alle in der Regel angegebenen Nachrichtenempfänger zusammen mit Filtern für diese Regel zu den entsprechenden Empfängerketten hinzu.
Es gibt ein weiteres wichtiges Merkmal beim Aufbau von Empfängerketten. Wenn die aktuelle Regel als final
markiert ist und ihr Name mit dem Namen des Protokollierers übereinstimmt, schließt NLog die Erstellung von Ketten für alle für diese Regel enthaltenen Protokollierungsstufen ab. Dies bedeutet, dass weder die verschachtelten Regeln noch nachfolgende Regeln diesen Empfängerketten etwas hinzufügen. Ihre Erstellung ist vollständig abgeschlossen und sie werden sich nicht ändern. Daraus folgt, dass es keinen Sinn macht, so etwas zu schreiben:
<rules> <logger name="*" minlevel="Info" writeTo="target1" final="true"> <logger name="*" minlevel="Warn" writeTo="target2" /> </logger> </rules>
Keine Nachrichten erreichen target2
. Aber es ist möglich, so etwas zu schreiben:
<rules> <logger name="*" minlevel="Warn" writeTo="target1" final="true"> <logger name="*" minlevel="Info" writeTo="target2" /> </logger> </rules>
Da die externe Regel für die Info
Ebene nicht aktiviert ist, endet die Empfängerkette für diese Ebene nicht mit der externen Regel. Daher fallen alle Nachrichten mit der Info
Ebene in target2
.
Nachdem alle Empfänger dieser Regel zu den entsprechenden Ketten hinzugefügt wurden, verarbeitet das Verfahren alle verschachtelten Regeln der aktuellen Regel rekursiv nach demselben Algorithmus. Dies geschieht unabhängig von den für die übergeordnete Regel aktivierten Protokollierungsstufen.
Insgesamt ist die Konfiguration für den Logger fertig. Es enthält Empfängerketten mit Filtern für jede mögliche Protokollierungsstufe:

Es ist Zeit zu sehen, wie diese Konfiguration verwendet wird.
Verwenden der Logger-Konfiguration
Beginnen wir mit einfachen Dingen. Die Logger
Klasse verfügt über eine IsEnabled
Methode und zugehörige IsXXXEnabled
Eigenschaften ( IsDebugEnabled
, IsInfoEnabled
, ...). Wie arbeiten sie? Tatsächlich prüfen sie einfach, ob die Empfängerketten für eine bestimmte Protokollierungsstufe mindestens ein Glied enthalten (siehe hier ). Dies bedeutet, dass Filter niemals die Werte dieser Eigenschaften beeinflussen.
Lassen Sie mich als Nächstes erklären, was passiert, wenn Sie versuchen, eine Nachricht zu sichern. Wie Sie vielleicht vermutet haben, nimmt der Logger eine Empfängerkette für die Protokollierungsstufe dieser Nachricht. Dann beginnt er, die Glieder dieser Kette nacheinander zu verarbeiten. Für jede Verbindung entscheidet der Logger, ob die Nachricht an den in der Verbindung angegebenen Empfänger geschrieben werden soll und ob die Kette danach weiter verarbeitet werden soll. Diese Entscheidungen werden mithilfe von Filtern getroffen. Lassen Sie mich Ihnen zeigen, wie Filter in NLog funktionieren.
So werden die Filter konfiguriert:
<rules> <logger name="*" minlevel="Info" writeTo="target1"> <filters defaultAction='Log'> <when condition="contains('${message}','Common')" action="Ignore" /> </filters> </logger> </rules>
Normalerweise enthält ein Filter eine boolesche Bedingung. Hier können Sie entscheiden, ob der Filter für jede Nachricht true
oder false
zurückgibt. Aber das ist nicht so. Das Ergebnis ihrer Arbeit ist ein Wert vom Typ FilterResult
. Wenn die Filterbedingung true
zurückgibt, wird das Ergebnis des Filters zu dem im action
angegebenen Wert (in unserem Beispiel ist dies Ignore
). Wenn die Bedingung false
zurückgibt, ist das Ergebnis des Filters Neutral
. Dies bedeutet, dass der Filter nicht entscheiden möchte, was mit der Nachricht geschehen soll.
Hier können Sie sehen, wie die Empfängerkette verarbeitet wird. Für jeden Empfänger wird das Ergebnis der entsprechenden Filter in der GetFilterResult
Methode GetFilterResult
. Dies entspricht dem Ergebnis des ersten Filters, der nicht Neutral
. Dies bedeutet, dass nicht alle nachfolgenden Filter ausgeführt werden, wenn ein Filter einen anderen Wert als Neutral
zurückgibt.
Aber was passiert, wenn alle Filter Neutral
? In diesem Fall wird der Standardwert verwendet. Dieser Wert wird mithilfe des defaultAction
Attributs des defaultAction
für die Regel festgelegt. Was ist Ihrer Meinung nach der Standardwert für defaultAction
? Sie haben Recht, wenn Sie denken, dass dies Neutral
. Das heißt, die gesamte Filterkette kann dadurch Neutral
. In diesem Fall verhält sich NLog genauso wie das Empfangen von Log
. Die Nachricht wird an den Empfänger geschrieben (siehe hier ).
Wie Sie vielleicht vermutet haben, wird die Nachricht nicht in den Empfänger geschrieben, wenn ein Filter Ignore
oder IgnoreFinal
zurückgibt. Wenn das Ergebnis des Filters Log
oder LogFinal
, wird die Nachricht aufgezeichnet. Aber was ist der Unterschied zwischen Ignore
und IgnoreFinal
und zwischen Log
oder LogFinal
? Das ist einfach. Im Fall von IgnoreFinal
und LogFinal
NLog die Verarbeitung der Empfängerkette und schreibt nichts in die in den nachfolgenden Links enthaltenen Empfänger.
Fazit
Durch die Analyse des NLog-Codes konnte ich besser verstehen, wie Regeln und Filter funktionieren. Ich hoffe, dieser Artikel wird Ihnen nützlich sein. Viel Glück