Klasse System.Diagnostics.Tracing.EventSource
In dit artikel vindt u aanvullende opmerkingen in de referentiedocumentatie voor deze API.
De EventSource klasse is bedoeld om te worden overgenomen door een gebruikersklasse die specifieke gebeurtenissen biedt die moeten worden gebruikt voor gebeurtenistracering. De EventSource.WriteEvent methoden worden aangeroepen om de gebeurtenissen te registreren.
De basisfunctionaliteit van EventSource is voldoende voor de meeste toepassingen. Als u meer controle wilt over de metagegevens van gebeurtenissen die zijn gemaakt, kunt u het EventAttribute kenmerk toepassen op de methoden. Voor geavanceerde gebeurtenisbrontoepassingen is het mogelijk om de opdrachten die worden verzonden naar de afgeleide gebeurtenisbron te onderscheppen en het filteren te wijzigen, of om ervoor te zorgen dat acties (zoals het dumpen van een gegevensstructuur) door de overnemende gebruiker worden uitgevoerd. Een gebeurtenisbron kan in-process worden geactiveerd met EventListener behulp van en out-of-process met behulp van EventPipe-hulpprogramma's, zoals dotnet-trace
etw-hulpprogramma's (Event Tracing for Windows), zoals PerfView
of Logman
. Het is ook mogelijk programmatisch te beheren en de gegevenszender te onderscheppen. De EventListener klasse biedt extra functionaliteit.
Conventies
EventSource-afgeleide klassen moeten de volgende conventies volgen:
- Door de gebruiker gedefinieerde klassen moeten een singleton-patroon implementeren. De singleton-instantie heeft traditioneel de naam
Log
. Als uitbreiding mogen gebruikers niet handmatig aanroepenIDisposable.Dispose
en toestaan dat de runtime het singleton-exemplaar aan het einde van de uitvoering van beheerde code opschoont. - Een door de gebruiker gedefinieerde, afgeleide klasse moet worden gemarkeerd als
sealed
tenzij de geavanceerde configuratie 'Utility EventSource' wordt geïmplementeerd die wordt besproken in de sectie Geavanceerd gebruik. - Roep IsEnabled() aan voordat u resource-intensief werk uitvoert met betrekking tot het activeren van een gebeurtenis.
- U kunt impliciet objecten maken EventTask door twee gebeurtenismethoden te declareren met volgende gebeurtenis-id's met het naamgevingspatroon
<EventName>Start
en<EventName>Stop
. Deze gebeurtenissen moeten naast elkaar worden gedeclareerd in de klassedefinitie en de<EventName>Start
methode moet eerst komen. - Probeer objecten achterwaarts compatibel te houden EventSource en ze op de juiste manier te versien. De standaardversie voor een gebeurtenis is
0
. De versie kan worden gewijzigd door de instelling in te stellen Version. Wijzig de versie van een gebeurtenis wanneer u eigenschappen van de nettolading wijzigt. Voeg altijd nieuwe nettoladingeigenschappen toe aan het einde van de gebeurtenisdeclaratie. Als dit niet mogelijk is, maakt u een nieuwe gebeurtenis met een nieuwe id om de oude te vervangen. - Bij het declareren van gebeurtenismethoden geeft u eigenschappen voor nettolading met vaste grootte op voordat u de eigenschappen van verschillende grootten kunt wijzigen.
- EventKeywords worden gebruikt als een bitmasker voor het opgeven van specifieke gebeurtenissen bij het abonneren op een provider. U kunt trefwoorden opgeven door een
public static class Keywords
lidklassepublic const EventKeywords
met leden te definiëren. - Koppel dure gebeurtenissen aan een EventKeywords gebruik.EventAttribute Met dit patroon kunnen gebruikers van uw EventSource gebruikers zich afmelden voor deze dure bewerkingen.
Zelfbeschrijfde (tracelogging) versus manifestgebeurtenisindelingen
EventSource kan worden geconfigureerd in twee verschillende modi op basis van de gebruikte constructor of op welke vlaggen zijn ingesteld EventSourceOptions.
In het verleden zijn deze twee indelingen afgeleid van twee indelingen die door Event Tracing for Windows (ETW) worden gebruikt. Hoewel deze twee modi geen invloed hebben op de mogelijkheid om Event Tracing voor Windows (ETW) of EventPipe-listeners te gebruiken, genereren ze de metagegevens voor gebeurtenissen anders.
De standaard gebeurtenisindeling is EtwManifestEventFormat, die is ingesteld als deze niet is opgegeven op EventSourceSettings. Op manifest gebaseerde EventSource objecten genereren een XML-document dat de gebeurtenissen vertegenwoordigt die zijn gedefinieerd in de klasse bij de initialisatie. Hiervoor moeten de EventSource provider- en gebeurtenismetagegevens over zichzelf worden gegenereerd.
Als u een zelfbeschrijfde gebeurtenisindeling (tracelogging) wilt gebruiken, maakt u de EventSource EventSource(String) constructor, de EventSource(String, EventSourceSettings) constructor of door de EtwSelfDescribingEventFormat
vlag in te stellen op EventSourceSettings. Zelfbeschrijfde bronnen genereren minimale metagegevens van providers bij initialisatie en genereren alleen metagegevens van gebeurtenissen wanneer Write(String) deze worden aangeroepen.
In de praktijk zijn deze instellingen voor gebeurtenisindeling alleen van invloed op het gebruik met lezers op basis van Event Tracing for Windows (ETW). Ze kunnen echter een klein effect hebben op initialisatietijd en schrijftijden per gebeurtenis vanwege de tijd die nodig is voor reflectie en het genereren van de metagegevens.
Voorbeelden
In het volgende voorbeeld ziet u een eenvoudige implementatie van de 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
In het volgende voorbeeld ziet u een complexere implementatie van de 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
Geavanceerd gebruik
Traditioneel verwachten door de gebruiker gedefinieerde EventSource objecten rechtstreeks van EventSource. Voor geavanceerde scenario's kunt u echter objecten maken abstract
EventSource, hulpprogrammabronnen genoemd en interfaces implementeren. Met een of beide technieken kunt u code delen tussen verschillende afgeleide bronnen.
Belangrijk
Abstracte EventSource objecten kunnen geen trefwoorden, taken, opcodes, kanalen of gebeurtenissen definiëren.
Belangrijk
Als u naamconflicten tijdens runtime wilt voorkomen bij het genereren van metagegevens van gebeurtenissen, moet u geen interfacemethoden expliciet implementeren bij het gebruik van interfaces met EventSource.
In het volgende voorbeeld ziet u een implementatie van die gebruikmaakt van EventSource een interface.
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);
}
In het volgende voorbeeld ziet u een implementatie van die gebruikmaakt van EventSource het Utility EventSource-patroon.
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!
}
In het volgende voorbeeld ziet u een implementatie van informatie over het traceren van EventSource informatie over een onderdeel in een bibliotheek.
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);
}
}
}
}