System.Diagnostics.Tracing.EventSource-klass

Anmärkning

Den här artikeln innehåller ytterligare kommentarer till referensdokumentationen för det här API:et.

Klassen EventSource är avsedd att ärvas av en användarklass som tillhandahåller specifika händelser som ska användas för händelsespårning. Metoderna EventSource.WriteEvent anropas för att logga händelserna.

De grundläggande funktionerna EventSource i räcker för de flesta program. Om du vill ha mer kontroll över händelsemetadata som skapas kan du använda EventAttribute attributet för metoderna. För avancerade händelsekällprogram är det möjligt att fånga upp kommandon som skickas till den härledda händelsekällan och ändra filtreringen, eller att orsaka att åtgärder (till exempel dumpning av en datastruktur) utförs av ärvern. En händelsekälla kan aktiveras in-process med hjälp av EventListener och out-of-process med hjälp av verktyg baserade på EventPipe, som dotnet-trace, eller på Event Tracing for Windows (ETW), som PerfView eller Logman. Det är också möjligt att programmatiskt kontrollera och fånga upp datautskickaren. Klassen EventListener ger ytterligare funktioner.

Konventioner

EventSource-härledda klasser bör följa följande konventioner:

  • Användardefinierade klasser bör implementera ett singleton-mönster. Singleton-instansen heter Logtraditionellt . Således bör användarna inte anropa IDisposable.Dispose manuellt utan låta runtime rensa singleton-instansen i slutet av exekveringen av hanterad kod.
  • En användardefinierad härledd klass bör markeras som sealed om den inte implementerar den avancerade "Utility EventSource"-konfigurationen som beskrivs i avsnittet Avancerad användning.
  • Anropa IsEnabled() innan du utför något resursintensivt arbete som har att göra med att trigga en händelse.
  • Du kan implicit skapa EventTask objekt genom att deklarera två händelsemetoder med efterföljande händelse-ID:t som har namngivningsmönstret <EventName>Start och <EventName>Stop. Dessa händelser måste deklareras bredvid varandra i klassdefinitionen <EventName>Start och metoden måste komma först.
  • Försök att hålla EventSource objekt bakåtkompatibla och versionera dem korrekt. Standardversionen för en händelse är 0. Du kan ändra versionen genom att ange Version. Ändra versionen av en händelse när du ändrar nyttolastens egenskaper. Lägg alltid till nya nyttolastegenskaper i slutet av händelsedeklarationen. Om detta inte är möjligt skapar du en ny händelse med ett nytt ID för att ersätta den gamla.
  • När du deklarerar händelsemetoder anger du egenskaper för nyttolast med fast storlek före egenskaper med variabilitetsstorlek.
  • EventKeywords används som en bitmask för att ange specifika händelser när du prenumererar på en provider. Du kan ange nyckelord genom att definiera en public static class Keywords medlemsklass som har public const EventKeywords medlemmar.
  • Associera dyra händelser med en EventKeywords genom att använda EventAttribute. Med det här mönstret kan användare av din EventSource välja bort dessa dyra åtgärder.

Självbeskrivande (spårloggning) jämfört med manifesthändelseformat

EventSource kan konfigureras till två olika lägen baserat på konstruktorn som används eller vilka flaggor som anges på EventSourceOptions.

Historiskt sett härleds dessa två format från två format som Händelsespårning för Windows (ETW) använde. Även om dessa två lägen inte påverkar din möjlighet att använda händelsespårning för Windows (ETW) eller EventPipe-baserade lyssnare, genererar de metadata för händelser på olika sätt.

Standardhändelseformatet är EtwManifestEventFormat, som anges om det inte anges på EventSourceSettings. Manifestbaserade EventSource objekt genererar ett XML-dokument som representerar de händelser som definierats i klassen vid initiering. Detta kräver EventSource att reflektera över sig själv för att generera providern och händelsemetadatan.

Om du vill använda självbeskrivande händelseformat (spårningsloggning) skapar du med EventSource konstruktorn EventSource(String) , EventSource(String, EventSourceSettings) konstruktorn eller genom att ange EtwSelfDescribingEventFormat flaggan på EventSourceSettings. Självbeskrivande källor genererar minimala providermetadata vid initiering och genererar endast händelsemetadata när Write(String) anropas. Till skillnad från det manifestbaserade formatet, ingår endast metadata för nivå, nyckelord och Opcode från EventAttribute attributet när man lyssnar via ETW. Andra egenskaper som EventId eller Meddelande ingår inte.

I praktiken påverkar dessa inställningar för händelseformat endast användningen med läsare baserat på händelsespårning för Windows (ETW). De kan dock ha en liten effekt på initieringstiden och skrivtiderna per händelse på grund av den tid som krävs för reflektion och generering av metadata.

Exempel

I följande exempel visas en enkel implementering av EventSource klassen.

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

I följande exempel visas en mer komplex implementering av EventSource klassen.

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

Avancerad användning

Traditionellt förväntas användardefinierade EventSource objekt ärva direkt från EventSource. För avancerade scenarier kan du dock skapa abstractEventSource objekt, som kallas verktygskällor, och implementera gränssnitt. Med en eller båda dessa tekniker kan du dela kod mellan olika härledda källor.

Viktigt!

Abstrakta EventSource objekt kan inte definiera nyckelord, uppgifter, opcodes, kanaler eller händelser.

Viktigt!

Om du vill undvika namnkollisioner vid körning vid generering av händelsemetadata ska du inte uttryckligen implementera gränssnittsmetoder när du använder gränssnitt med EventSource.

I följande exempel visas en implementering av EventSource som använder ett gränssnitt.

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);
}

I följande exempel visas en implementering av EventSource som använder Utility EventSource-mönstret.

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!
}

I följande exempel visas en implementering av EventSource för att spåra information om en komponent i ett bibliotek.

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);
            }
        }
    }
}