다음을 통해 공유


System.Diagnostics.Tracing.EventSource 클래스

이 문서에서는 이 API에 대한 참조 설명서에 대한 추가 설명서를 제공합니다.

클래스 EventSource 는 이벤트 추적에 사용할 특정 이벤트를 제공하는 사용자 클래스에 의해 상속됩니다. EventSource.WriteEvent 이벤트를 기록하기 위해 메서드가 호출됩니다.

기본 기능을 EventSource 대부분의 애플리케이션에 대 한 부족 합니다. 생성된 이벤트 메타데이터를 더 자세히 제어하려면 메서드에 EventAttribute 특성을 적용할 수 있습니다. 고급 이벤트 소스 애플리케이션에 대 한 파생된 이벤트 소스에 전송 되는 명령을 가로채서 필터링을 변경 하거나 작업 (예: 데이터 구조를 덤프할) 시킬 수는 자가에서 수행할 수 있습니다. 이벤트 원본은 EtW(Windows용 이벤트 추적) 기반 도구와 같은 EventPipe 기반 도구를 사용하여 프로세스 내 및 Out-of-process를 사용하여 EventListener 활성화할 수 있습니다 PerfViewLogman.dotnet-trace 프로그래밍 방식으로 데이터 디스패처를 제어하고 가로챌 수도 있습니다. 클래스는 EventListener 추가 기능을 제공합니다.

규칙

EventSource-파생 클래스는 다음 규칙을 따라야 합니다.

  • 사용자 정의 클래스는 싱글톤 패턴을 구현해야 합니다. 싱글톤 인스턴스는 일반적으로 이름이 지정 Log됩니다. 확장을 통해 사용자는 수동으로 호출 IDisposable.Dispose 하지 않아야 하며 런타임이 관리 코드 실행이 끝날 때 싱글톤 인스턴스를 클린 수 있도록 허용해서는 안 됩니다.
  • 고급 사용량 섹션에서 설명하는 고급 "유틸리티 EventSource" 구성을 구현하지 않는 한 사용자 정의 파생 클래스를 표시 sealed 해야 합니다.
  • 이벤트 발생과 관련된 리소스 집약적 작업을 수행하기 전에 호출 IsEnabled() 합니다.
  • 명명 패턴 <EventName>Start 이 있는 후속 이벤트 ID를 사용하여 두 개의 이벤트 메서드를 선언하여 개체를 암시적으로 만들 EventTask 수 있습니다<EventName>Stop. 이러한 이벤트는 클래스 정의에서 나란히 선언되어야 하며 메서드<EventName>Start 먼저 와야 합니다.
  • 개체를 이전 버전과 호환되도록 유지하고 EventSource 적절하게 버전 관리합니다. 이벤트의 기본 버전은 .입니다 0. 을 설정 Version하여 버전을 변경할 수 있습니다. 페이로드의 속성을 변경할 때마다 이벤트의 버전을 변경합니다. 항상 이벤트 선언의 끝에 새 페이로드 속성을 추가합니다. 이렇게 할 수 없는 경우 새 ID를 사용하여 새 이벤트를 만들어 이전 이벤트를 바꿉니다.
  • 이벤트 메서드를 선언할 때 고정 크기 속성 앞에 고정 크기 페이로드 속성을 지정합니다.
  • EventKeywords 는 공급자를 구독할 때 특정 이벤트를 지정하기 위한 비트 마스크로 사용됩니다. 멤버가 있는 멤버 클래스 public const EventKeywords 를 정의하여 public static class Keywords 키워드(keyword) 지정할 수 있습니다.
  • 비용이 많이 드는 이벤트를 using과 EventKeywords 연결합니다 EventAttribute. 이 패턴을 사용하면 사용자가 이러한 비용이 많이 드는 작업을 옵트아웃할 수 EventSource 있습니다.

자체 설명(추적 로깅) 및 매니페스트 이벤트 형식

EventSource 는 사용되는 생성자 또는 설정된 플래그에 따라 두 가지 모드로 구성할 수 있습니다 EventSourceOptions.

지금까지 이러한 두 형식은 ETW(Windows용 이벤트 추적)가 사용한 두 가지 형식에서 파생되었습니다. 이러한 두 모드는 ETW(Windows용 이벤트 추적) 또는 EventPipe 기반 수신기를 사용하는 기능에 영향을 주지 않지만 이벤트에 대한 메타데이터를 다르게 생성합니다.

기본 이벤트 형식은 EtwManifestEventFormat에 지정되지 않은 경우 설정되는 EventSourceSettings형식입니다. 매니페스트 기반 EventSource 개체는 초기화 시 클래스에 정의된 이벤트를 나타내는 XML 문서를 생성합니다. 이렇게 하려면 공급자 및 이벤트 메타데이터를 생성하기 위해 자체에 대해 반영해야 EventSource 합니다.

자체 설명(추적 로깅) 이벤트 형식 EventSource 을 사용하려면 생성자, EventSource(String, EventSourceSettings) 생성자 또는 플래그를 EventSourceSettings설정 EtwSelfDescribingEventFormat 하여 생성자를 생성 EventSource(String) 합니다. 자체 설명 원본은 초기화 시 최소한의 공급자 메타데이터를 생성하고 호출된 경우에만 이벤트 메타데이터 Write(String) 를 생성합니다.

실제로 이러한 이벤트 형식 설정은 ETW(Windows용 이벤트 추적)를 기반으로 하는 판독기 사용량에만 영향을 줍니다. 그러나 리플렉션 및 메타데이터 생성에 필요한 시간으로 인해 초기화 시간 및 이벤트별 쓰기 시간에 작은 영향을 미칠 수 있습니다.

예제

다음 예제에서는 클래스의 간단한 구현을 EventSource 보여줍니다.

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

다음 예제에서는 클래스의 더 복잡한 구현을 EventSource 보여줍니다.

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

고급 사용

일반적으로 사용자 정의 EventSource 개체는 .에서 EventSource직접 상속해야 합니다. 그러나 고급 시나리오의 경우 유틸리티 원본이라는 개체를 만들고 abstractEventSource 인터페이스를 구현할 수 있습니다. 이러한 기술 중 하나 또는 둘 다를 사용하면 서로 다른 파생 소스 간에 코드를 공유할 수 있습니다.

Important

추상 EventSource 개체는 키워드(keyword), 작업, opcode, 채널 또는 이벤트를 정의할 수 없습니다.

Important

이벤트 메타데이터를 생성할 때 런타임에 이름 충돌을 방지하려면 인터페이스를 사용할 때 인터페이스 메서드를 EventSource명시적으로 구현하지 마세요.

다음 예제에서는 인터페이스를 EventSource 사용하는 구현을 보여줍니다.

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

다음 예제에서는 유틸리티 EventSource 패턴을 사용하는 구현 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!
}

다음 예제에서는 라이브러리의 EventSource 구성 요소에 대한 정보를 추적하기 위한 구현을 보여줍니다.

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