Logging mit der Enterprise Library (Teil 2)

Im ersten Teil des Artikels Logging mit der Enterprise Library (Teil 1) wurden vor allem die Installation und die Konfiguration des Logging Application Blocks gezeigt. In diesem Teil geht es nun um die einzelnen Features des Logging Application Blocks und wie man diese einsetzen kann.

Erzeugen und Schreiben von Log-Einträgen

Log-Einträge werden mit Hilfe einer LogWriter-Instanz geschrieben. Dafür werden der Write-Methode (unserer LogWriter-Instanz) die zu loggenden Informationen als Paramter mitgegeben. Es gibt insgesamt 19-Überladungen der Write-Methode, die es u.a. erlauben den Message-Text, die Kategorie, die Priorität, die Event-ID, den Schweregrad (ein Wert der TraceEventType-Enumeration, z.B. Information, Warnung, Error, usw.) und einen Titel als Parameter mitzugeben. Darüber hinaus gibt es eine Überladung, die es ermöglicht benutzerdefinierte Werte in Form eines Dictionaries mit Name-Wert-Paaren zu loggen (z.B. könnte man die Werte von Membervariablen oder spezielle Eigenschaften protokollieren). Die folgenden Beispiele zeigen nun einige dieser Überladungen und deren Verwendung.

Hinweis: Je nach verwendetem Betriebssystem müssen die Beispiele mit Administratorberechtigungen ausgeführt werden!

Im ersten Beispiel wird eine LogWriter-Instanz erzeugt und danach die Write-Methode verwendet um Log-Einträge zu erzeugen.

Die Write-Methode ist mehrfach überladen und nimmt die dabei die folgenden Parameter entgegen:

ParameterBeschreibung
object messageHier wird die zu loggende Nachricht mitgegeben. Dafür wird auf dem zu loggenden Objekt die ToString-Methode aufgerufen.
string category oder
ICollection categories
Hier werden die Log-Kategorien angegeben. Wird keine Kategorie angegeben, verwendet der Application-Block die Default-Kategorie. Ein Log-Eintrag kann auch direkt in mehrere Kategorien geschrieben werden, dazu wird einfach der Parameter categories verwendet. Dabei handelt es sich um einen Verteilungsmechanismus, z.B. ist es denkbar einen Log-Eintrag in eine Textdatei und in die Datenbank zu loggen und gleichzeitig noch eine Mail zu versenden. Dabei kann man pro Kategorie noch unterschiedliche Log-Formate für die eigentliche Nachricht hinterlegen. Für einfache Logging Aufgaben kann der Parameter auch weggelassen werden.
IDictionary propertiesEnthält beliebig viele Key/Value-Paare, die im Log-Eintrag erscheinen sollen. Dieser Parameter kann dazu verwendet werden um weitergehende Umstände eines Log-Eintags zu speichern, z.B. bestimmte Datenkonstellationen usw.
int priorityFür Einträge im Eventlog ist die Priorität ohnehin wichtig. Darüberhinaus bietet die Konfiguration die Möglichkeit Log-Einträge unterhalb einer eingestellten Priorität zu unterdrücken.
int eventIdÜber die Event-Id ist ebenfalls eine Filterung möglich.
TraceEventType severityVerwendet die Enumeration aus dem System.DiagnosticNamespace, um den Schweregrad eines Log-Eintrags zu bestimmen. Dieser Wert kann ebenfalls dazu genutzt werden um Log-Einträge zu filtern. Das ist besonders praktisch, da man so zu Diagnosezwecken auch Verbose- oder Information-Einträge erzeugen kann, die im Produktivbetrieb aber das Logging-System nicht belasten sollen, weil die Schwelle dort auf Warning oder Error eingestellt wird.
string titleErgänzt den Log-Eintrag um eine zusätzliche Beschreibung.

Hinweis: In dem oben gezeigten Beispiel wird zunächst geprüft, ob das Logging überhaupt aktiviert ist. Das ist vor allem aus Sicht der Performance von Bedeutung! Es gibt nämlich keinen Grund den Prozessor und Speicher mit Logging zu belasten, wenn die Log-Einträge später sowieso nicht sichtbar sind bzw. nicht in ein entsprechendes Logging-Ziel (z.B. Text-Datei, Datenbank, usw.) geschrieben werden! Dies kann einfach konfiguriert werden in dem man der Logging-Configruation den Filter „Logging Enabled Filter“ hinzufügt. Dieser Filter hat eine Eigenschaft namens „All Logging enabled“, die mit der Methode IsLoggingEnabled geprüft werden kann. Somit ist es möglich das Logging global ein- oder auszuschalten.

EntLib_AddLoggingEnabledFilter

Die erzeugten Log-Einträge werden alle in die Kategorie „General“ geschrieben. Diese Kategorie ist so konfiguriert, dass alles in das Windows Application Event Log geschrieben wird. Öffnet man nun den Windows Event Viewer sieht man folgendes Ergebnis:

EntLib_EventLog

Gibt man bei der Write-Methode einige Paramter nicht an werden diese mit den folgenden Standardwerten versorgt:

Standardwerte für Parameter der Write-Methode

ParameterStandardwert
CategoryGeneral
Priority-1
Event ID1
SeverityInformation
Titleleer

Erzeugung und Verwendung LogEntry-Objekten

In den oben gezeigten Beispielen wurde die Write-Methode verwendet um Log-Einträge zu erzeugen. Ein alternativer Ansatz ist die Verwendung von LogEntry-Objekten. Diese LogEntry-Objekte können separat erstellt werden und ist immer dann hilfreich wenn die Log-Informationen zwischen Methoden oder Prozessen ausgetauscht werden sollen. Das folgende Beispiel verwendet nun ein LogEntry-Objekt um einige Eigenschaften des späteren Log-Eintrags zu setzen, bevor dieser dann mit der Write-Methode der LogWriter-Instanz in das entsprechende Ziel geschrieben wird.

Logging Kategorien

Kategorien werden innerhalb des Logging Application Blocks dazu verwendet, die einzelnen Log-Meldungen den entsprechenden Logging-Zielen (z.B. Text-Dateien, Datenbanken, E-Mail, usw.) zuzuordnen. Im ersten Beispiel wurde die Standardkonfiguration des Logging Application Blocks verwendet. Wird das Konfigurationstool der Entperprise Library verwendet, wird automatisch eine einzelne Kategorie namens „General“ erzeugt, die in das Windows Event Log schreibt. Das Verhalten der einzelnen Kategorien lässt sich beliebig ändern bzw. anpassen (z.B. könnte man die Formatierung der Log-Einträge oder das Logging-Ziel ändern).

In Unternehmensanwendungen ist es nun oft so, dass man für unterschiedliche Aufgaben (im Bereich des Logging sind das Fehler, Warnungen, Informationen, Statusmeldungen, usw.) unterschiedliche Arten der Protokollierung benötigt. Dies kann man mit unterschiedlichen Kategorien erreichen. Es wäre denkbar, dass man einige Informationen in einer Text- oder XML-Datei protokollieren möchte, andere Informationen in einer Datenbank und schwerwiegende Fehler sollen direkt per E-Mail an die Aministratoren oder das Support-Team gesendet werden (dieses Verhalten ist weniger von der Kategorie als vom Schweregrad (Severity) des Log-Eintrags abhängig).

Das Anlegen einer neuen Kategorie erfolgt ganz einfach über die mitgelieferte Konfigurationssoftware.

EntLib_AddNewCategory

Folgende Parameter können für eine Kategorie konfiguriert werden:

Konfigurationsmöglichkeiten einer Logging-Kategorie

ParameterBeschreibung
NameHier wird der Name der Kategorie hinterlegt
Auto FlushMit der Auto Flush Eigenschaft kann gesteuert werden zu welchem Zeitpunkt die Log-Meldungen in das entsprechende Ziel geschrieben werden. Ist die Eigenschaft auf true gesetzt werden die Log-Meldungen direkt (d.h. ohne Verzörgerung) weggeschrieben. Wenn die Eigenschaft auf false gesetzt ist, werden die Log-Meldungen solange zwischengespeichert bis die Flush-Methode des Listeners aufgerufen wird. Erst wenn die Flush-Methode aufgerufen wird werden die Log-Meldungen final in das Logging-Ziel geschrieben. Hier sollte man jedoch im Fehlerfall darauf achten, dass diese Methode auch wirklich aufgerufen wird, da sonst alle bis dahin aufgelaufenen Log-Meldungen verloren gehen.
ListenersHier werden die einzelnen Logging-Ziele konfiguriert, z.B. Textfile, Database, ...
Minimum SeverityMit der Eigenschaft "Minimum Severity" können Log-Meldungen gefiltert werden. Hier wird konfiguriert welchen minimalen Schweregrad (z.B. Warning oder Error) eine Log-Meldung haben muss damit diese in das Logging-Ziel geschrieben wird. Alle Log-Meldungen, die einen niedrigeren Schweregrad aufweisen werden geblockt. Der Defaultwert ist "All".

Je nach gewählter Strategie wäre z.B. eine Kategorie pro Logging-Ziel denkbar (z.B. Text Files, Database, XML, usw.) oder Kategorien nach Schweregrad der entsprechenden Log-Meldung (Information, Warning, Error, …).

Schreiben von Log-Meldungen in mehrere Kategorien

Zusätzlich zu der Möglichkeit mehrere Kategorien zu definieren besteht die Möglichkeit einen Log-Eintrag in mehrere Kategorien zu schreiben und das innerhalb einer Operation. Das folgende Beispiel verwendet zwei Kategorien namens DiskFiles und Database (beide Kategorien sind in der Konfiguration hinterlegt). Die DiskFiles-Kategorie enthält Verweise auf zwei Trace Listener: FlatFile und XML File. Für die Database-Kategorie ist ein Trace Listener konfiguriert: Database Trace Listener (schreibt Log-Meldungen in die Datenbank). Siehe dazu die folgende Konfiguration:

EntLib_MultipleCategoryLogging

Um nun Log-Meldungen in beiden Kategorien zu erstellen, erzeugt man einen String-Array mit den Namen der Kategorien und gibt dieses Array als Parameter an die Write-Methode. Beim Aufruf der Write-Methode werden jetzt drei Log-Einträge (1 x Textdatei, 1 x XML-Datei, 1 x Datenbank) erzeugt. Dazu das folgende Beispiel:

Zusätzliche Informationen loggen

In manchen Fällen kann es hilfreich sein zusätzliche Informationen zu loggen (z.B. Umgebungsvariablen wie angemeldeter Benutzer, Werte von Variablen, Konfigurationsinformationen, usw.). Für diese Informationen gibt es die LogEntry-Eigenschaft ExtendedProperties. Diese Eigenschaft ist vom Typ Dictionary und kann mit beliebigen Informationen gefüllt werden. Dazu das folgende Beispiel:

Als Ergebnis wird der folgende Log-Eintrag erzeugt:

EntLib_ExtProperties01

Innerhalb des Logging Application Blocks gibt es vier Hilfsklassen, die es ermöglichen zusäztliche Kontextinformationen in dieses Dictionary zu schreiben:

Logging Application Block Hilfsklassen Kontextinformationen

KlassennameBeschreibung
DebugInformationProviderErmittelt Informationen über den aktuellen Stack Trace
ManagedSecurityContextInformationProviderErmittelt Informationen über den angemeldeten Benutzer, Authorisierungstyp und -status
UnmanagedSecurityContextInformationProviderErmittelt Informationen über den aktuellen Benutzer und Prozess
ComPlusInformationProviderErmittelt Informationen wie Application ID, Transaction ID (wenn vorhanden), Aufrufername, usw.

Hier ein kleines Beispiel für die Verwendung dieser Klassen:

Log-Errors

Wie bereits geschrieben kann es beim Logging selbst ebenfalls zu Fehlern kommen. Dafür gibt es die sogenannten „Special Categories“ wo Fehler, die beim Logging auftreten behandelt werden. Die folgenden Kategorien stehen hierfür zur Verfügung:

Logging Application Block Special Categories

NameBeschreibung
All EventsIn dieser Kategorie landen alle Log-Einträge unabhängig davon was sonst noch alles konfiguriert ist. Standardmäßig ist für diese Kategorie kein Logging Target Trace Listener konfiguriert.
Unprocessed CategoryIn die Kategorie "Unprocessed Category" wird geloggt sobald versucht wird in eine Kategorie zu loggen, die nicht konfiguriert ist. Standardmäßig hat diese Kategorie keinen Trace Listener konfiguriert.
Logging Errors & WarningsIn diese Kategorie wird geloggt wenn innerhalb des Logging-Prozesses ein Fehler auftritt. Standardmäßig ist für diese Kategorie ein Trace Listener konfiguriert, der in das Windows Event Log protokolliert (kann umkonfiguriert werden).

Die folgenden zwei Szenarien sind für Logging-Fehler denkbar:

  • beim Logging wird eine Kategorie angegeben, die nicht in der Konfiguration hinterlegt ist.
  • ein Logging Target Listener ist nicht verfügbar, z.B. wenn in eine Datenbank geloggt werden soll und diese nicht verfügbar ist

Im nächsten Beispiel wird folgendes versucht

  • in eine Kategorie zu loggen, die nicht verfügbar bzw. konfiguriert ist
  • eine E-Mail über einen SMTP-Server zu versenden, der nicht erreichbar ist

Literaturverzeichnis und Weblinks

Abk.Quelle
[1]http://entlib.codeplex.com/
[2]Developer's Guide to Microsoft Enterprise Library, 2nd Edition, Patterns&Practices Team

Fork me on GitHub