Überwachen von Anwendungen mit System.Diagnostics

Veröffentlicht: 14. Aug 2006

Es ist so sicher wie das Amen in der Kirche: Die Anwendung, die Sie gerade bereitgestellt haben, lief auf dem Entwicklungscomputer hervorragend, aber in der Produktionsumgebung treten Fehler auf. Das Problem zeigt sich möglicherweise sofort, kann aber auch erst mit der Zeit in Erscheinung treten. Und dann?

Allzu oft geben Entwickler die Debugversion ihres Codes heraus und stellen diese bereit. Diese spezielle Version ist häufig mit Anweisungen überladen, mit denen Ausgaben in eine Datei auf dem Datenträger geschrieben werden, die der Entwickler dann später durchforsten kann, um das Programmverhalten genauer zu erforschen. Es muss eine bessere Methode geben.

Die Lösung liegt im System.Diagnostics-Namespace, der Klassen für die Nutzung von Ereignisprotokollen, Leistungsindikatoren und Systemprozessen enthält. Sie können diesen Überwachungscode in der Produktionsanwendung aktiviert lassen und die Informationen betrachten, sobald ein Problem auftritt. Sehen wir uns an, wie Sie diese Klassen in Ihren Anwendungen verwenden können.

Auf dieser Seite

Ereignisprotokolle

Viele Entwickler debuggen ihre Anwendungen, indem sie Informationen in externe Dateien ausgeben lassen. Nicht nur, dass dies die Festplatte "zumüllt", auch ist dieser Ansatz oft zu zeitaufwändig, nicht sortierbar oder filterbar und stellt in den seltensten Fällen aussagekräftige Daten zur Verfügung. Warum wird er dann von so vielen Leuten verfolgt? Sie wissen nicht, wie einfach sich das Ereignisprotokoll verwenden lässt.

Viele Entwickler debuggen ihre Anwendungen, indem sie Informationen in externe Dateien ausgeben lassen. Nicht nur, dass dies die Festplatte "zumüllt", auch ist dieser Ansatz oft zu zeitaufwändig, nicht sortierbar oder filterbar und stellt in den seltensten Fällen aussagekräftige Daten zur Verfügung. Warum wird er dann von so vielen Leuten verfolgt? Sie wissen nicht, wie einfach sich das Ereignisprotokoll verwenden lässt.

‘ Create the source, if it does not already
‘ exist.
  If Not EventLog.SourceExists("MSDNDemo") Then
    EventLog.CreateEventSource("MSDNDemo", _
    "CustomLog")
End If

‘ Create an EventLog instance and assign its
‘ source.
Dim myLog As New EventLog()
myLog.Source = "MSDNDemo"

‘ Write an informational entry to the event log.    
myLog.WriteEntry("Hello from the event log.")

Im oberen Abschnitt wird geprüft, ob Ihre Ereignisquelle bereits erstellt wurde. Die meisten Entwickler benutzen den Namen ihrer Anwendung, damit die Anwendungsereignisse leichter von anderen Einträgen im Ereignisprotokoll zu unterscheiden sind.

Wenn die Quelle nicht vorhanden ist, wird sie erstellt und mit einem Ereignisprotokoll verknüpft. Sie können vorhandene Abschnitte des Ereignisprotokolls, z. B. die Abschnitte "Anwendung" oder "System" benutzen, oder (wie in diesem Demoprogramm) einen eigenen Abschnitt erstellen, indem Sie einen Namen angeben.

Nachdem die Quelle erstellt wurde, muss zum Hinzufügen von Ereignissen zum Ereignisprotokoll einfach nur ein neues EventLog-Objekt erstellt und der Quelle zugeordnet werden. In diesem Beispiel wird einfach "Hello from the event log" ausgegeben, Sie können jedoch detaillierte Informationen, z. B. eine Stapelablaufverfolgung, der WriteEntry-Methode übergeben und beliebige Daten in das Ereignisprotokoll einfügen.

Vom Benutzer erzeugte Ereignisprotokolleinträge in einem benutzerdefinierten Abschnitt
Abbildung 1: Vom Benutzer erzeugte Ereignisprotokolleinträge in einem benutzerdefinierten Abschnitt

Lassen Sie uns diesen Code mehrmals ausführen. Daraufhin wird dieser Vorgang in Ihrem Ereignisprotokoll wie in Abbildung 1 dargestellt angezeigt. Sie können die Windows® Ereignisanzeige (%windir%\system32\eventvwr.exe) öffnen, die in Abbildung 1 zu sehen ist. Außerdem können Sie das Ereignisprotokoll in Visual Studio® im Server-Explorer anzeigen, wie in Abbildung 2 gezeigt. Wenn Sie auf einen dieser Einträge doppelklicken, können Sie die Detailangaben betrachten und sehen Ihre benutzerdefinierte Fehlermeldung (siehe Abbildung 3).

Ereignisprotokolle in Visual Studio
Abbildung 2: Ereignisprotokolle in Visual Studio

Für die EventLog.WriteEntry-Methode sind zehn Überladungen verfügbar, die Ihnen Flexibilität und eine Menge Steuerungsmöglichkeiten bieten. Eine der nützlichsten überladenen Methoden ermöglicht die Angabe des Ereignistyps, der protokolliert wird. Beachten Sie in Abbildung 1, dass jeder Eintrag vom Typ "Information" ist. Für ein Demoprogramm ist dies ausreichend, in der Praxis werden jedoch viele verschiedene Ereignisse auftreten. Mit der WriteEntry-Methode können Sie den Ereignistyp angeben, z. B. Fehler, Warnung, Information, Erfolgsüberwachung oder Fehlerüberwachung.

myLog.WriteEntry("Your error here.", _
    EventLogEntryType.Error)

Vom Benutzer erzeugtes Ereignisprotokoll
Abbildung 3: Vom Benutzer erzeugtes Ereignisprotokoll

Nun wollen wir die schnelle und einfache Methode ausprobieren. In Visual Basic® 2005 wurde das My-Objekt eingeführt, das als Kurzwahl in das Framework fungiert. Sie können mittels My dem Ereignisprotokoll rasch Ereignisse hinzufügen:

My.Application.Log.WriteException( _
    "Written from My", TraceEventType.Warning, _
    "Additional Information")

Zudem können Sie My über die WriteEntry-Methode verwenden:

My.Application.Log.WriteEntry("Written from My", TraceEventType.Error)

Wenn Sie diese Methode aufrufen und das Ereignisprotokoll betrachten, finden Sie die Meldung möglicherweise nicht, weil das My-Objekt, abhängig von Einstellungen Ihrer Konfigurationsdatei, Meldungen an verschiedene Listener weiterleiten kann. Eine eingehendere Erörterung dieser sehr leistungsfähigen Funktionalität würde den Rahmen dieses Artikels sprengen. Sie finden jedoch weitere Informationen hierzu unter "My.Application.Log-Objekt".

Verwenden Sie das Ereignisprotokoll mit Bedacht. Es gibt kaum etwas Störenderes als Anwendungen, die das Ereignisprotokoll mit wertlosen Daten füllen. Wenn ein Fehler, eine wichtige Warnung oder eine Information vorliegt, sollten Sie sie protokollieren. Es ist sehr hilfreich, den Typ des Eintrags anzugeben, weil die meisten Spalten in der Ereignisanzeige von Windows gefiltert werden können.

Leistungsindikatoren

Das Ereignisprotokoll eignet sich hervorragend zur Ausgabe von Meldungen, doch was, wenn Sie sehen möchten, was in Ihrer Anwendung vor sich geht, während sie ausgeführt wird? Hier kommen die Leistungsindikatoren ins Spiel.

Viele Entwickler wissen, wie sie den Systemmonitor von Windows zur Anzeige der CPU- oder Speichernutzung oder von Festplatteninformationen verwenden können (siehe Abbildung 4). Wussten Sie, dass Sie mit der PerformanceCounter-Klasse eigene Indikatoren und Datenpunkte hinzufügen können? Die gleichzeitige Nutzung von PerformanceCounter-Klasse und Windows Systemmonitor ist ungefähr so, als könnten Sie jederzeit ein EKG-Gerät an Ihre Anwendung anschließen.

Standardleistungstools unter Windows
Abbildung 4: Standardleistungstools unter Windows

Bevor ich auf das Hinzufügen benutzerdefinierter Leistungsindikatoren eingehe, wollen wir die Indikatoren untersuchen, die bereits verfügbar sind. Öffnen Sie den Server-Explorer in Visual Studio, und navigieren Sie zum Ordner Leistungsindikatoren, der unter einem Ihrer Computer angezeigt wird. Sie sehen eine lange Liste von Indikatorkategorien, die sich erweitern lassen und jeweils viele einzelne Leistungsindikatoren enthalten (siehe Abbildung 5).

Leistungsindikatoren
Abbildung 5: Leistungsindikatoren

Sie können diese vom System verwalteten Indikatoren in Ihren eigenen Anwendungen benutzen. Erweitern Sie jetzt einmal die Kategorie "Memory", und klicken Sie anschließend auf den Leistungsindikator "Available Bytes". Ziehen Sie den Indikator "Available Bytes" auf ein neues Windows Form Ihrer Anwendung.

Legen Sie auf diesem Formular ein Label-Steuerelement und ein neues Timer-Objekt ab, dessen Enabled-Eigenschaft auf True festgelegt wurde. Fügen Sie folgenden Code hinzu:

Private Sub Timer1_Tick(ByVal sender As System.Object, _
     ByVal e As System.EventArgs) Handles Timer1.Tick
    Label1.Text = Me.PerformanceCounter1.NextValue
End Sub

Dies ist ein sehr einfaches Beispiel, das in einer realen Anwendung so wahrscheinlich nicht zum Einsatz käme, aber es zeigt, wie Sie einer Anwendung grundlegende Leistungsindikatoren zum Überwachen oder Abrufen von Systemdaten hinzufügen können. In der Praxis würden Sie u. U. abhängig vom Status eines Werts bestimmte Maßnahmen ergreifen, diese Daten protokollieren oder irgend etwas anderes tun.

Wahrscheinlicher ist noch, dass Sie eigene Leistungsindikatoren definieren möchten, um die Funktionsfähigkeit und Leistung Ihrer Anwendungen zu jedem beliebigen Zeitpunkt überwachen zu können. Um einen neuen Indikator hinzuzufügen, klicken Sie im Server-Explorer mit der rechten Maustaste auf den Eintrag "Leistungsindikatoren" und wählen dann "Neue Kategorie erstellen" aus.

In diesem Beispiel wollen wir eine Kategorie namens TestCategory erstellen und einen neuen Indikator namens TestCounter hinzufügen. Diesmal greifen Sie über den Code auf den Indikator zu.

Ziehen und legen Sie zuerst eine neue Timer-Komponente und ein Button-Steuerelement auf dem Formular ab, und fügen Sie dem Formular eine private Variable namens counter hinzu:

Private counter As PerformanceCounter

Fügen Sie dem Click-Ereignishandler des Button-Steuerelements folgenden Code hinzu:

‘Init the counter object
counter = New PerformanceCounter("TestCategory", "TestCounter", False)
   ‘ Set its value to 0
   counter.RawValue = 0
   ‘ Start the time to update the counter
   Timer2.Enabled = True

Fügen Sie dem Timer-Ereignishandler diesen Code hinzu:

Counter.IncrementBy(New Random().Next(-5, 10))

Dieser Code bewirkt, dass der Indikator um eine Zufallszahl zwischen -5 und 10 erhöht wird. In diesem Beispiel wird mit einer Zufallszahl gearbeitet, es könnte sich jedoch auch um die Anzahl von Verkäufen oder bearbeiteten Bestellungen, die Anzahl der angemeldeten Benutzer, die protokollierte Fehleranzahl oder anderen Wert handeln, dessen Überwachung in Ihrer Anwendung sinnvoll ist.

Laden Sie den Systemmonitor aus der Programmgruppe "Verwaltung", bevor Sie die Beispielanwendung starten. Diesmal klicken Sie auf das Pluszeichen (+), um einen neuen Indikator hinzuzufügen. Unter "Datenobjekt" wird jetzt die neue Kategorie TestCategory angezeigt, die Sie erstellt haben. Sie können diese Kategorie in der Dropdown-Liste und in der Liste der Indikatoren den Indikator TestCounter auswählen.

Lassen Sie den Systemmonitor geöffnet, und klicken Sie auf die Schaltfläche der Beispielanwendung. Sie sollten ein Diagramm sehen, das dem in Abbildung 6 dargestellten ähnelt.

Wie Sie dem Beispielcode entnehmen können, wurde die IncrementBy-Methode verwendet, um den Indikatorwert zu vergrößern bzw. zu verringern. Wenn der Indikator um eins nach oben oder unten bewegt werden soll, können Sie einfach die Methode Increment bzw. Decrement aufrufen.

Darstellung der TestCounter-Ergebnisse im Systemmonitor
Abbildung 6: Darstellung der TestCounter-Ergebnisse im Systemmonitor

Bei einem Beispiel wie diesem, in dem der Server-Explorer verwendet wird, fragen sich manche Entwickler vielleicht, ob sie das alles programmieren können. Ja, Sie können beispielsweise folgenden Code schreiben

If Not 
     PerformanceCounterCategory.Exists("MSDNSample")
   Then
     PerformanceCounterCategory.Create("MSDNSample", _
       "Built to show how to add custom counters", _
        PerformanceCounterCategoryType.SingleInstance, _
       "MSDNSampleCounter", _
       "Built to show how to add custom counters")
   End If

und ihn dem Click-Ereignishandler der Schaltfläche hinzufügen, um ausschließlich durch Programmcode eine andere Kategorie und einen Indikator zu erstellen. Wenn Sie den anderen Code mit Verweisen auf diese Kategorie und Indikator aktualisieren, werden Sie feststellen, dass das Projekt genau wie das Beispiel ausgeführt wird und funktioniert.

Process-Klasse

Bislang haben Sie in diesem Artikel die Verwendung des Ereignisprotokolls und der Leistungsindikatoren kennen gelernt. Jetzt wollen wir uns mit einem dritten Bereich des System.Diagnostics-Namespace beschäftigen, nämlich der Process-Klasse.

Zuerst wollen wir einen Prozess definieren: Ein Prozess ist einfach eine aktive Anwendung. Mit der Process-Klasse und den richtigen Berechtigungen können Sie auf Informationen zugreifen und Aktionen durchführen, z. B. Starten oder Beenden, die verschiedene Prozesse auf dem System des Benutzers betreffen.

Die Process-Klasse gewährt Ihnen Zugriff auf eine Menge Daten über Ihre Anwendungen oder andere Prozesse. Für das vorliegende Beispiel wollen wir die Basispriorität der Beispielanwendung erhöhen.

Hier ist das Szenario: Ihre Anwendung wird auf einem Server ausgeführt, sie ist die primäre Anwendung und Funktion auf diesem Server, zeigt aber ein schlechtes Leistungsverhalten, weil Hintergrundtasks zu viel Prozessorzeit in Anspruch nehmen. Sie können dann entweder die Priorität der Hintergrundtasks herabsetzen oder die Priorität Ihrer Tasks erhöhen. (In den meisten Fällen müssen Sie die Priorität eines Prozesses nicht anpassen, weil Windows die Ausführung mehrerer Prozesse und deren Aktivierung und Auslagerung gut handhabt.)

Für den Einsatz der Process-Klasse müssen Sie zuerst einen Verweis auf Ihren Prozess oder einen anderen Prozess abrufen. Da die Priorität des aktuellen Prozesses erhöht werden soll, verwenden Sie folgenden Code:

Dim myProcess As Process = _
       Process.GetCurrentProcess()

Für den Zugriff auf andere Prozesse auf Ihrem Computer oder auf Remotecomputern benutzen Sie stattdessen die GetProcessessByName-Methode:

Dim theProcesses() As Process = _
       myprocess.GetProcessesByName( _
       "ProcessName", "MachineName")

Sobald Sie über eine Verweis auf einen Prozess verfügen, können Sie über dessen Eigenschaften eine Menge über den Prozess in Erfahrung bringen. Eine umfassende Liste der Eigenschaften finden Sie unter ".NET Framework-Klassenbibliothek Process-Member".

Die Priorität lässt sich mit nur einer Codezeile erhöhen:

myProcess.PriorityClass = ProcessPriorityClass.RealTime

Sie haben der Anwendung die Priorität "RealTime" (Echtzeit) zugewiesen. Dies ist die höchste Prioritätsstufe, die Sie im Betriebssystem festlegen können und die der Anwendung praktisch Priorität über alle anderen Tasks gibt. Dies kann zu Problemen führen, wenn Ihre Anwendung alle Systemressourcen belegt. Starten Sie den Task-Manager, um sich zu vergewissern, dass das Betriebssystem die Anwendung und deren angehobene Priorität erkennt, wie in Abbildung 7 dargestellt.

Anhebung der Priorität auf RealTime
Abbildung 7: Anhebung der Priorität auf RealTime

Sie können mit dem Prozessbefehl viele gängige Aufgaben ausführen. Angenommen, Sie möchten Microsoft Internet Explorer® mit minimiertem Fenster starten und zum Visual Basic Developer Center navigieren. Sie würden dann einfach folgenden Code verwenden:

Dim startInfo As New ProcessStartInfo("IExplore.exe")
startInfo.WindowStyle = ProcessWindowStyle.Minimized
startInfo.Arguments = "msdn.microsoft.com/vbasic"
Process.Start(startInfo)

Die Process-Klasse lässt sich auch gut zum Drucken einsetzen.

Dim myDocumentsPath As String = _
    Environment.GetFolderPath(Environment.SpecialFolder.Personal)
myProcess.StartInfo.FileName = myDocumentsPath + "\MyFile.doc"
myProcess.StartInfo.Verb = "Print"
myProcess.StartInfo.CreateNoWindow = True
myProcess.Start()

Wie Sie sehen, kann der System.Diagnostics-Namespace zu vielerlei Zwecken verwendet werden, vom Protokollieren von Ereignissen im Ereignisprotokoll über das Lesen und Schreiben von Leistungsindikatoren bis zur Arbeit mit Systemprozessen. Dies ist ein unglaublich nützlicher und leistungsfähiger Satz an Tools, den jeder Entwickler kennen und in seiner Arbeit verwenden sollte.

Senden Sie Fragen und Kommentare in englischer Sprache an basics@microsoft.com.

Der Autor

Brad McCabe ist Program Manager bei Microsoft und unter anderem für das Visual Basic Developer Center von MSDN verantwortlich.

Aus der Ausgabe Juli 2006 des MSDN Magazine.