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 memanggilIDisposable.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 memilikipublic 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 abstract
EventSource 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);
}
}
}
}
Saran dan Komentar
https://aka.ms/ContentUserFeedback.
Segera hadir: Sepanjang tahun 2024 kami akan menghentikan penggunaan GitHub Issues sebagai mekanisme umpan balik untuk konten dan menggantinya dengan sistem umpan balik baru. Untuk mengetahui informasi selengkapnya, lihat:Kirim dan lihat umpan balik untuk