-
-
Ich habe die letzten 2 Tage an einem Phänomen gekämpft, das hat mich wirklich an meinen Fähigkeiten als Entwickler zweifeln lassen.
Folgende Situation:
- Ein Testfall erzeugt durch Systemzeitumstellungen definierte Einträge in verschiedene Ereignisprotokolle.
- Nach jedem Schreiben gibt es im Testframework eine Sicherheitsüberprüfung, ob der Eintrag vorhanden ist.
- Nachdem alle Einträge geschrieben wurden, wird meine Komponente getestet, dessen Aufgabe es ist Ereignisse von verchiedenen Rechnern und Ereignisprotokollen zu holen.
- Aufgrund der hohen Datenmengen werden die Ereignisse zeitscheibenweise per WMI abgeholt (siehe auch: Blog Rehl - Quota violation)
Fehlerbeschreibung:
Unter Umständen kam es vor, dass für den ersten konfigurierten Rechner und das erste Ereignisprotokoll in der ersten Zeitscheibe keine Ergebnisse vorlagen (trotz richtig konzipiertem Testfall), ich schätze das Auftreten auf ca. 30%.
Details:
Meine Vorgehensweise ist so codetechnisch umgesetzt worden:
ManagementObjectSearcher searcher = null;
ManagementScope scope = null;
SelectQuery query = null;
// Vorbereitungen und WMI Query Objekt Initialisierungen
searcher = new ManagementObjectSearcher();
scope = new ManagementScope(string.Format("\\\\{0}", computerName));
scope.Path = new ManagementPath("root\\CIMV2");
scope.Path.Server = computerName;
scope.Connect();
searcher.Scope = scope;
query = new SelectQuery();
// Datum WMI gerecht umwandeln
strIntervalStartTime = ManagementDateTimeConverter.ToDmtfDateTime(intervalStartTime);
strIntervalEndTime = ManagementDateTimeConverter.ToDmtfDateTime(intervalEndTime);
// Hiermit werden die Eventlogs ausgelesen
query.QueryString = string.Format("SELECT * FROM Win32_NTLogEvent WHERE (LogFile = '{0}' And TimeGenerated > '{1}' And TimeGenerated < '{2}')", eventLog, strIntervalStartTime, strIntervalEndTime);
// Die Abfrage dem Suchobjekt zuweisen
searcher.Query = query;
// Ergebnisse ermitteln
retVal = searcher.Get();
Analyse:
Zunächst dachte ich, dass es etwas mit den Zeitscheiben zu tun hatte, die Trace-Ausgaben waren aber eindeutig, dass alles richtig laufen sollte. Ein nachträglich eingebautes Retry (10 Stück an der Zahl), das durchgeführt wurde, wenn keine Ergebnisse ermittelt wurden brachte kein Ergebnis.
Also haben wir einen Alternativweg gesucht um Ereignisse zu ermitteln und stießen im .NET Framework auf System.Diagnostic.EventLog. Die EventLog Klasse verwendet intern die Windows API anstatt WMI. Erste Tests brachten hier ein besseres Ergebnis. Es wurden immer alle Ereignisse zurückgeliefert. Problem: Die EventLog Klasse kann keine Filterung durchführen. Es holt immer alle Ereignisse aus einem Protokoll (zumindest die Headerinformationen hierzu). Ein Lasttest brachte auch die Erkenntnis, dass das bei großen Ereignisprotokollen nicht zu empfehlen ist.
EventLog log = new EventLog(eventLog, computerName);
foreach (EventLogEntry entry in log.Entries)
{
if (entry.TimeGenerated > startTime && entry.TimeGenerated < endTime)
{
retVal = false;
break;
}
}
Ergebnis:
Also machten wir einen abschließenden Test: Bringt die WMI Abfrage keine Ergebnisse, dann ermitteln wir mittels der EventLog Klasse die Anzahl der Ereignisse, die in der Zeitscheibe liegen. Nur dann wenn WMI kein Ergebnis liefert, und die Windows API auch nicht, dann überspringen wir die Zeitscheibe, ansonsten wird die Verarbeitung aufgrund des Datenverlusts abgebrochen.
Fazit:
Ich würde aufgrund unserer Tests, WMI als nicht zuverlässig einstufen, wenn es um das Holen von Ereignissen geht!
Übrigens ein Count(*) ist grundsätzlich für WMI gültig, aber für die Ereignisse nicht verfügbar.