Hinweis
Für den Zugriff auf diese Seite ist eine Autorisierung erforderlich. Sie können versuchen, sich anzumelden oder das Verzeichnis zu wechseln.
Für den Zugriff auf diese Seite ist eine Autorisierung erforderlich. Sie können versuchen, das Verzeichnis zu wechseln.
Dieser Artikel enthält ergänzende Hinweise zur Referenzdokumentation für diese API.
Die EventSource Klasse soll von einer Benutzerklasse geerbt werden, die bestimmte Ereignisse bereitstellt, die für die Ereignisablaufverfolgung verwendet werden sollen. Die EventSource.WriteEvent Methoden werden aufgerufen, um die Ereignisse zu protokollieren.
Die grundlegenden Funktionen reichen EventSource für die meisten Anwendungen aus. Wenn Sie mehr Kontrolle über die erstellten Ereignismetadaten wünschen, können Sie das EventAttribute Attribut auf die Methoden anwenden. Bei erweiterten Ereignisquellanwendungen ist es möglich, die Befehle abzufangen, die an die abgeleitete Ereignisquelle gesendet werden, und die Filterung zu ändern oder Aktionen (z. B. das Dumpen einer Datenstruktur) durch den Erben ausführen zu lassen. Eine Ereignisquelle kann prozessintern mithilfe von EventListener und außerprozessbasiert mithilfe von EventPipe-basierten Tools wie dotnet-trace oder ETW-basierten Tools (Event Tracing for Windows, ETW) wie PerfView oder Logman aktiviert werden. Es ist auch möglich, den Daten dispatcher programmgesteuert zu steuern und abzufangen. Die EventListener Klasse bietet zusätzliche Funktionen.
Konventionen
EventSource-abgeleitete Klassen sollten den nachstehenden Konventionen entsprechen.
- Benutzerdefinierte Klassen sollten ein Singletonmuster implementieren. Die Singleton-Instanz wird traditionell benannt
Log. Infolgedessen sollten BenutzerIDisposable.Disposenicht manuell aufrufen und stattdessen der Runtime-Umgebung erlauben, die Singleton-Instanz am Ende der Ausführung von verwaltetem Code aufzuräumen. - Eine benutzerdefinierte abgeleitete Klasse sollte als gekennzeichnet
sealedwerden, es sei denn, sie implementiert die erweiterte Konfiguration "Utility EventSource", die im Abschnitt "Erweiterte Nutzung" erläutert wird. - Rufen Sie IsEnabled() auf, bevor Sie ressourcenintensive Arbeiten im Zusammenhang mit dem Auslösen eines Ereignisses ausführen.
- Sie können Objekte implizit erstellenEventTask, indem Sie zwei Ereignismethoden mit nachfolgenden Ereignis-IDs deklarieren, die das Benennungsmuster
<EventName>Startaufweisen.<EventName>StopDiese Ereignisse müssen in der Klassendefinition nebeneinander deklariert werden, und die<EventName>StartMethode muss zuerst erfolgen. - Versuchen Sie, Objekte abwärtskompatibel zu halten EventSource und sie entsprechend zu versionieren. Die Standardversion für ein Ereignis lautet
0. Die Version kann durch Setzen von Version geändert werden. Ändern Sie die Version eines Ereignisses, wenn Sie die Eigenschaften der Nutzlast ändern. Fügen Sie neue Payloadeigenschaften immer am Ende der Ereignisdeklaration hinzu. Wenn dies nicht möglich ist, erstellen Sie ein neues Ereignis mit einer neuen ID, um die alte zu ersetzen. - Geben Sie beim Deklarieren von Ereignismethoden Payloadeigenschaften mit fester Größe vor variabel angepassten Eigenschaften an.
-
EventKeywords werden als Bitmaske zum Angeben bestimmter Ereignisse beim Abonnieren eines Anbieters verwendet. Sie können Schlüsselwörter angeben, indem Sie eine
public static class Keywords-Mitgliedsklasse definieren, diepublic const EventKeywords-Mitglieder enthält. - Ordnen Sie teure Ereignisse mit einem EventKeywords unter Verwendung von EventAttribute zu. Mit diesem Muster können Benutzer Ihres EventSource von diesen kostspieligen Vorgängen absehen.
Selbstbeschreibende Formate (TraceLogging) und Manifestereignisformate im Vergleich
EventSource kann abhängig vom verwendeten Konstruktor oder den gesetzten Flags auf EventSourceOptions in zwei verschiedene Modi konfiguriert werden.
In der Vergangenheit werden diese beiden Formate von zwei Formaten abgeleitet, die von der Ereignisablaufverfolgung für Windows (ETW) verwendet werden. Obwohl sich diese beiden Modi nicht auf die Verwendung der Ereignisablaufverfolgung für Windows (ETW) oder EventPipe-basierte Listener auswirken, generieren sie jedoch die Metadaten für Ereignisse unterschiedlich.
Das Standardereignisformat ist EtwManifestEventFormat, das festgelegt wird, wenn es auf EventSourceSettings nicht angegeben ist. Manifestbasierte EventSource Objekte generieren ein XML-Dokument, das die für die Klasse bei der Initialisierung definierten Ereignisse darstellt. Dies erfordert, dass EventSource sich selbst reflektiert, um die Anbieter- und Ereignismetadaten zu generieren.
Um das selbstbeschreibende Ereignisformat (Tracelogging) zu verwenden, erstellen Sie EventSource mit dem EventSource(String)-Konstruktor, dem EventSource(String, EventSourceSettings)-Konstruktor oder indem Sie das EtwSelfDescribingEventFormat-Flag auf EventSourceSettings festlegen. Selbstbeschreibende Quellen generieren minimale Anbietermetadaten bei der Initialisierung und generieren nur Ereignismetadaten, wenn Write(String) aufgerufen wird. Im Gegensatz zum manifestbasierten Format werden beim Abhören durch ETW nur die Metadaten "Level", "Keyword" und "Opcode" aus dem EventAttribute-Attribut einbezogen. Andere Eigenschaften wie EventId oder Message sind nicht enthalten.
In der Praxis wirken sich diese Ereignisformateinstellungen nur auf die Verwendung mit Lesern basierend auf der Ereignisablaufverfolgung für Windows (ETW) aus. Sie können jedoch einen kleinen Effekt auf die Initialisierungszeit und die Schreibzeiten pro Ereignis haben, da die für die Reflexion und das Generieren der Metadaten benötigte Zeit erforderlich ist.
Beispiele
Das folgende Beispiel zeigt eine einfache Implementierung der EventSource Klasse.
using System.Diagnostics.Tracing;
namespace Demo1
{
sealed class MyCompanyEventSource : EventSource
{
public static MyCompanyEventSource Log = new MyCompanyEventSource();
public void Startup() { WriteEvent(1); }
public void OpenFileStart(string fileName) { WriteEvent(2, fileName); }
public void OpenFileStop() { WriteEvent(3); }
}
class Program1
{
static void Main(string[] args)
{
MyCompanyEventSource.Log.Startup();
// ...
MyCompanyEventSource.Log.OpenFileStart("SomeFile");
// ...
MyCompanyEventSource.Log.OpenFileStop();
}
}
}
Imports System.Diagnostics.Tracing
Class MyCompanyEventSource
Inherits EventSource
Public Shared Log As New MyCompanyEventSource()
Public Sub Startup()
WriteEvent(1)
End Sub
Public Sub OpenFileStart(ByVal fileName As String)
WriteEvent(2, fileName)
End Sub
Public Sub OpenFileStop()
WriteEvent(3)
End Sub
End Class
Class Program
Shared Sub Main(ByVal args() As String)
MyCompanyEventSource.Log.Startup()
' ...
MyCompanyEventSource.Log.OpenFileStart("SomeFile")
' ...
MyCompanyEventSource.Log.OpenFileStop()
End Sub
End Class
Das folgende Beispiel zeigt eine komplexere Implementierung der EventSource Klasse.
using System;
using System.Diagnostics.Tracing;
namespace Demo2
{
enum MyColor { Red, Yellow, Blue };
[EventSource(Name = "MyCompany")]
sealed class MyCompanyEventSource : EventSource
{
public static class Keywords
{
public const EventKeywords Page = (EventKeywords)1;
public const EventKeywords DataBase = (EventKeywords)2;
public const EventKeywords Diagnostic = (EventKeywords)4;
public const EventKeywords Perf = (EventKeywords)8;
}
public static class Tasks
{
public const EventTask Page = (EventTask)1;
public const EventTask DBQuery = (EventTask)2;
}
[Event(1, Message = "Application Failure: {0}", Level = EventLevel.Error, Keywords = Keywords.Diagnostic)]
public void Failure(string message) { WriteEvent(1, message); }
[Event(2, Message = "Starting up.", Keywords = Keywords.Perf, Level = EventLevel.Informational)]
public void Startup() { WriteEvent(2); }
[Event(3, Message = "loading page {1} activityID={0}", Opcode = EventOpcode.Start,
Task = Tasks.Page, Keywords = Keywords.Page, Level = EventLevel.Informational)]
public void PageStart(int ID, string url) { if (IsEnabled()) WriteEvent(3, ID, url); }
[Event(4, Opcode = EventOpcode.Stop, Task = Tasks.Page, Keywords = Keywords.Page, Level = EventLevel.Informational)]
public void PageStop(int ID) { if (IsEnabled()) WriteEvent(4, ID); }
[Event(5, Opcode = EventOpcode.Start, Task = Tasks.DBQuery, Keywords = Keywords.DataBase, Level = EventLevel.Informational)]
public void DBQueryStart(string sqlQuery) { WriteEvent(5, sqlQuery); }
[Event(6, Opcode = EventOpcode.Stop, Task = Tasks.DBQuery, Keywords = Keywords.DataBase, Level = EventLevel.Informational)]
public void DBQueryStop() { WriteEvent(6); }
[Event(7, Level = EventLevel.Verbose, Keywords = Keywords.DataBase)]
public void Mark(int ID) { if (IsEnabled()) WriteEvent(7, ID); }
[Event(8)]
public void LogColor(MyColor color) { WriteEvent(8, (int)color); }
public static MyCompanyEventSource Log = new MyCompanyEventSource();
}
class Program
{
static void Main(string[] args)
{
MyCompanyEventSource.Log.Startup();
Console.WriteLine("Starting up");
MyCompanyEventSource.Log.DBQueryStart("Select * from MYTable");
var url = "http://localhost";
for (int i = 0; i < 10; i++)
{
MyCompanyEventSource.Log.PageStart(i, url);
MyCompanyEventSource.Log.Mark(i);
MyCompanyEventSource.Log.PageStop(i);
}
MyCompanyEventSource.Log.DBQueryStop();
MyCompanyEventSource.Log.LogColor(MyColor.Blue);
MyCompanyEventSource.Log.Failure("This is a failure 1");
MyCompanyEventSource.Log.Failure("This is a failure 2");
MyCompanyEventSource.Log.Failure("This is a failure 3");
}
}
}
Imports System.Diagnostics.Tracing
Enum MyColor
Red
Yellow
Blue
End Enum 'MyColor
<EventSource(Name:="MyCompany")>
Class MyCompanyEventSource1
Inherits EventSource
Public Class Keywords
Public Const Page As EventKeywords = CType(1, EventKeywords)
Public Const DataBase As EventKeywords = CType(2, EventKeywords)
Public Const Diagnostic As EventKeywords = CType(4, EventKeywords)
Public Const Perf As EventKeywords = CType(8, EventKeywords)
End Class
Public Class Tasks
Public Const Page As EventTask = CType(1, EventTask)
Public Const DBQuery As EventTask = CType(1, EventTask)
End Class
<[Event](1, Message:="Application Failure: {0}", Level:=EventLevel.Error, Keywords:=Keywords.Diagnostic)>
Public Sub Failure(ByVal message As String)
WriteEvent(1, message)
End Sub
<[Event](2, Message:="Starting up.", Keywords:=Keywords.Perf, Level:=EventLevel.Informational)>
Public Sub Startup()
WriteEvent(2)
End Sub
<[Event](3, Message:="loading page {1} activityID={0}", Opcode:=EventOpcode.Start, Task:=Tasks.Page, Keywords:=Keywords.Page, Level:=EventLevel.Informational)>
Public Sub PageStart(ByVal ID As Integer, ByVal url As String)
If IsEnabled() Then
WriteEvent(3, ID, url)
End If
End Sub
<[Event](4, Opcode:=EventOpcode.Stop, Task:=Tasks.Page, Keywords:=Keywords.Page, Level:=EventLevel.Informational)>
Public Sub PageStop(ByVal ID As Integer)
If IsEnabled() Then
WriteEvent(4, ID)
End If
End Sub
<[Event](5, Opcode:=EventOpcode.Start, Task:=Tasks.DBQuery, Keywords:=Keywords.DataBase, Level:=EventLevel.Informational)>
Public Sub DBQueryStart(ByVal sqlQuery As String)
WriteEvent(5, sqlQuery)
End Sub
<[Event](6, Opcode:=EventOpcode.Stop, Task:=Tasks.DBQuery, Keywords:=Keywords.DataBase, Level:=EventLevel.Informational)>
Public Sub DBQueryStop()
WriteEvent(6)
End Sub
<[Event](7, Level:=EventLevel.Verbose, Keywords:=Keywords.DataBase)>
Public Sub Mark(ByVal ID As Integer)
If IsEnabled() Then
WriteEvent(7, ID)
End If
End Sub
<[Event](8)>
Public Sub LogColor(ByVal color As MyColor)
WriteEvent(8, Fix(color))
End Sub
Public Shared Log As New MyCompanyEventSource1()
End Class
Class Program1
Shared Sub Main(ByVal args() As String)
MyCompanyEventSource1.Log.Startup()
Console.WriteLine("Starting up")
MyCompanyEventSource1.Log.DBQueryStart("Select * from MYTable")
Dim url As String = "http:'localhost"
Dim i As Integer
For i = 0 To 9
MyCompanyEventSource1.Log.PageStart(i, url)
MyCompanyEventSource1.Log.Mark(i)
MyCompanyEventSource1.Log.PageStop(i)
Next i
MyCompanyEventSource1.Log.DBQueryStop()
MyCompanyEventSource1.Log.LogColor(MyColor.Blue)
MyCompanyEventSource1.Log.Failure("This is a failure 1")
MyCompanyEventSource1.Log.Failure("This is a failure 2")
MyCompanyEventSource1.Log.Failure("This is a failure 3")
End Sub
End Class
Erweiterte Verwendung
Traditionell erwarten benutzerdefinierte EventSource Objekte, dass sie direkt von EventSource erben. Für erweiterte Szenarien können Sie jedoch Objekte erstellen abstractEventSource, die als Hilfsquellen bezeichnet werden, und Schnittstellen implementieren. Mit einer oder beiden dieser Techniken können Sie Code zwischen verschiedenen abgeleiteten Quellen freigeben.
Wichtig
Abstrakte EventSource Objekte können keine Schlüsselwörter, Aufgaben, Opcodes, Kanäle oder Ereignisse definieren.
Wichtig
Um Namenskonflikte zur Laufzeit beim Generieren von Ereignismetadaten zu vermeiden, implementieren Sie beim Verwenden von Schnittstellen mit EventSource Schnittstellenmethoden nicht explizit.
Das folgende Beispiel zeigt eine Implementierung von EventSource, die eine Schnittstelle verwendet.
public interface IMyLogging
{
void Error(int errorCode, string message);
void Warning(string message);
}
public sealed class MySource : EventSource, IMyLogging
{
public static MySource Log = new();
[Event(1)]
public void Error(int errorCode, string message) => WriteEvent(1, errorCode, message);
[Event(2)]
public void Warning(string message) => WriteEvent(2, message);
}
Das folgende Beispiel zeigt eine Implementierung von EventSource, die das Utility EventSource-Muster verwendet.
public abstract class UtilBaseEventSource : EventSource
{
protected UtilBaseEventSource()
: base()
{ }
protected UtilBaseEventSource(bool throwOnEventWriteErrors)
: base(throwOnEventWriteErrors)
{ }
// helper overload of WriteEvent for optimizing writing an event containing
// payload properties that don't align with a provided overload. This prevents
// EventSource from using the object[] overload which is expensive.
protected unsafe void WriteEvent(int eventId, int arg1, short arg2, long arg3)
{
if (IsEnabled())
{
EventSource.EventData* descrs = stackalloc EventSource.EventData[3];
descrs[0] = new EventData { DataPointer = (IntPtr)(&arg1), Size = 4 };
descrs[1] = new EventData { DataPointer = (IntPtr)(&arg2), Size = 2 };
descrs[2] = new EventData { DataPointer = (IntPtr)(&arg3), Size = 8 };
WriteEventCore(eventId, 3, descrs);
}
}
}
public sealed class OptimizedEventSource : UtilBaseEventSource
{
public static OptimizedEventSource Log = new();
public static class Keywords
{
public const EventKeywords Kwd1 = (EventKeywords)1;
}
[Event(1, Keywords = Keywords.Kwd1, Level = EventLevel.Informational, Message = "LogElements called {0}/{1}/{2}.")]
public void LogElements(int n, short sh, long l) => WriteEvent(1, n, sh, l); // uses the overload we added!
}
Das folgende Beispiel zeigt eine Implementierung von EventSource um Verfolgen von Informationen zu einer Komponente in einer Bibliothek.
public class ComplexComponent : IDisposable
{
internal static Dictionary<string, string> _internalState = new();
private string _name;
public ComplexComponent(string name)
{
_name = name ?? throw new ArgumentNullException(nameof(name));
ComplexSource.Log.NewComponent(_name);
}
public void SetState(string key, string value)
{
lock (_internalState)
{
_internalState[key] = value;
ComplexSource.Log.SetState(_name, key, value);
}
}
private void ExpensiveWork1() => System.Threading.Thread.Sleep(TimeSpan.FromMilliseconds(250));
private void ExpensiveWork2() => System.Threading.Thread.Sleep(TimeSpan.FromMilliseconds(250));
private void ExpensiveWork3() => System.Threading.Thread.Sleep(TimeSpan.FromMilliseconds(250));
private void ExpensiveWork4() => System.Threading.Thread.Sleep(TimeSpan.FromMilliseconds(250));
public void DoWork()
{
ComplexSource.Log.ExpensiveWorkStart(_name);
ExpensiveWork1();
ExpensiveWork2();
ExpensiveWork3();
ExpensiveWork4();
ComplexSource.Log.ExpensiveWorkStop(_name);
}
public void Dispose()
{
ComplexSource.Log.ComponentDisposed(_name);
}
}
internal sealed class ComplexSource : EventSource
{
public static ComplexSource Log = new();
public static class Keywords
{
public const EventKeywords ComponentLifespan = (EventKeywords)1;
public const EventKeywords StateChanges = (EventKeywords)(1 << 1);
public const EventKeywords Performance = (EventKeywords)(1 << 2);
public const EventKeywords DumpState = (EventKeywords)(1 << 3);
// a utility keyword for a common combination of keywords users might enable
public const EventKeywords StateTracking = ComponentLifespan & StateChanges & DumpState;
}
protected override void OnEventCommand(EventCommandEventArgs args)
{
base.OnEventCommand(args);
if (args.Command == EventCommand.Enable)
{
DumpComponentState();
}
}
[Event(1, Keywords = Keywords.ComponentLifespan, Message = "New component with name '{0}'.")]
public void NewComponent(string name) => WriteEvent(1, name);
[Event(2, Keywords = Keywords.ComponentLifespan, Message = "Component with name '{0}' disposed.")]
public void ComponentDisposed(string name) => WriteEvent(2, name);
[Event(3, Keywords = Keywords.StateChanges)]
public void SetState(string name, string key, string value) => WriteEvent(3, name, key, value);
[Event(4, Keywords = Keywords.Performance)]
public void ExpensiveWorkStart(string name) => WriteEvent(4, name);
[Event(5, Keywords = Keywords.Performance)]
public void ExpensiveWorkStop(string name) => WriteEvent(5, name);
[Event(6, Keywords = Keywords.DumpState)]
public void ComponentState(string key, string value) => WriteEvent(6, key, value);
[NonEvent]
public void DumpComponentState()
{
if (IsEnabled(EventLevel.Informational, Keywords.DumpState))
{
lock (ComplexComponent._internalState)
{
foreach (var (key, value) in ComplexComponent._internalState)
ComponentState(key, value);
}
}
}
}