Bagikan melalui


Kelas System.Diagnostics.Tracing.EventSource

Artikel ini menyediakan keterangan tambahan untuk dokumentasi referensi untuk API ini.

Kelas EventSource ini dimaksudkan untuk diwariskan oleh kelas pengguna yang menyediakan peristiwa tertentu yang akan digunakan untuk pelacakan peristiwa. Metode dipanggil EventSource.WriteEvent untuk mencatat peristiwa.

Fungsionalitas EventSource dasar cukup untuk sebagian besar aplikasi. Jika Anda ingin kontrol lebih atas metadata peristiwa yang dibuat, Anda dapat menerapkan EventAttribute atribut ke metode . Untuk aplikasi sumber peristiwa tingkat lanjut, dimungkinkan untuk mencegat perintah yang dikirim ke sumber peristiwa turunan dan mengubah pemfilteran, atau menyebabkan tindakan (seperti membuang struktur data) yang akan dilakukan oleh pewaris. Sumber peristiwa dapat diaktifkan dalam proses menggunakan EventListener dan di luar proses menggunakan alat berbasis EventPipe seperti dotnet-trace atau alat berbasis Event Tracing for Windows (ETW) seperti PerfView atau Logman. Dimungkinkan juga untuk mengontrol dan mencegat dispatcher data secara terprogram. Kelas ini EventListener menyediakan fungsionalitas tambahan.

Konvensi

EventSource-turunan kelas harus mengikuti konvensi berikut:

  • Kelas yang ditentukan pengguna harus menerapkan pola singleton. Instans singleton secara tradisional bernama Log. Dengan ekstensi, pengguna tidak boleh memanggil IDisposable.Dispose secara manual dan memungkinkan runtime untuk membersihkan instans singleton di akhir eksekusi kode terkelola.
  • Kelas turunan yang ditentukan pengguna harus ditandai sebagai sealed kecuali mengimplementasikan konfigurasi "Utility EventSource" lanjutan yang dibahas di bagian Penggunaan Tingkat Lanjut.
  • Panggil IsEnabled() sebelum melakukan pekerjaan intensif sumber daya apa pun yang terkait dengan menembakkan peristiwa.
  • Anda dapat secara implisit membuat EventTask objek dengan mendeklarasikan dua metode peristiwa dengan ID peristiwa berikutnya yang memiliki pola <EventName>Start penamaan dan <EventName>Stop. Peristiwa ini harus dideklarasikan di samping satu sama lain dalam definisi kelas dan <EventName>Start metode harus menjadi yang pertama.
  • Coba pertahankan EventSource objek mundur agar kompatibel dan buat versinya dengan tepat. Versi default untuk peristiwa adalah 0. Versi dapat diubah dengan mengatur Version. Ubah versi peristiwa setiap kali Anda mengubah properti payload. Selalu tambahkan properti payload baru ke akhir deklarasi peristiwa. Jika ini tidak memungkinkan, buat peristiwa baru dengan ID baru untuk mengganti yang lama.
  • Saat mendeklarasikan metode peristiwa, tentukan properti payload ukuran tetap sebelum properti berukuran bervariasi.
  • EventKeywords digunakan sebagai masker bit untuk menentukan peristiwa tertentu saat berlangganan penyedia. Anda dapat menentukan kata kunci dengan mendefinisikan public static class Keywords kelas anggota yang memiliki public const EventKeywords anggota.
  • Kaitkan peristiwa mahal dengan EventKeywords menggunakan EventAttribute. Pola ini memungkinkan pengguna Anda EventSource untuk memilih keluar dari operasi mahal ini.

Menjelaskan sendiri (tracelogging) vs. format peristiwa manifes

EventSource dapat dikonfigurasi ke dalam dua mode berbeda berdasarkan konstruktor yang digunakan atau bendera apa yang diatur pada EventSourceOptions.

Secara historis, kedua format ini berasal dari dua format yang digunakan Event Tracing for Windows (ETW). Meskipun kedua mode ini tidak memengaruhi kemampuan Anda untuk menggunakan Pelacakan Peristiwa untuk pendengar berbasis Windows (ETW) atau EventPipe, mode ini menghasilkan metadata untuk peristiwa secara berbeda.

Format peristiwa default adalah EtwManifestEventFormat, yang diatur jika tidak ditentukan pada EventSourceSettings. Objek berbasis EventSource manifes menghasilkan dokumen XML yang mewakili peristiwa yang ditentukan pada kelas setelah inisialisasi. Ini mengharuskan EventSource untuk merefleksikan dirinya sendiri untuk menghasilkan penyedia dan metadata peristiwa.

Untuk menggunakan format peristiwa yang menjelaskan sendiri (tracelogging), buat anda EventSource menggunakan EventSource(String) konstruktor, EventSource(String, EventSourceSettings) konstruktor, atau dengan mengatur EtwSelfDescribingEventFormat bendera pada EventSourceSettings. Sumber yang menjelaskan sendiri menghasilkan metadata penyedia minimal pada inisialisasi dan hanya menghasilkan metadata peristiwa saat Write(String) dipanggil.

Dalam praktiknya, pengaturan format peristiwa ini hanya memengaruhi penggunaan dengan pembaca berdasarkan Pelacakan Peristiwa untuk Windows (ETW). Namun, mereka dapat memiliki efek kecil pada waktu inisialisasi dan waktu penulisan per peristiwa karena waktu yang diperlukan untuk refleksi dan menghasilkan metadata.

Contoh

Contoh berikut menunjukkan implementasi EventSource kelas yang sederhana.

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

Contoh berikut menunjukkan implementasi kelas yang EventSource lebih kompleks.

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

Penggunaan tingkat lanjut

Secara tradisional, objek yang ditentukan EventSource pengguna berharap untuk mewarisi langsung dari EventSource. Namun, untuk skenario tingkat lanjut, Anda dapat membuat abstractEventSource objek, yang disebut Sumber Utilitas, dan menerapkan antarmuka. Menggunakan salah satu atau kedua teknik ini memungkinkan Anda berbagi kode antara sumber turunan yang berbeda.

Penting

Objek abstrak EventSource tidak dapat menentukan kata kunci, tugas, opcode, saluran, atau peristiwa.

Penting

Untuk menghindari tabrakan nama pada waktu proses saat menghasilkan metadata peristiwa, jangan terapkan metode antarmuka secara eksplisit saat menggunakan antarmuka dengan EventSource.

Contoh berikut menunjukkan implementasi EventSource yang menggunakan antarmuka.

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

Contoh berikut menunjukkan implementasi EventSource yang menggunakan pola Utility EventSource.

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

Contoh berikut menunjukkan implementasi EventSource untuk melacak informasi tentang komponen di pustaka.

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