Listing all available ETW events in a .NET application

 
 
  • Gérald Barré

When tracing an application, it can be useful to know which ETW events are available. .NET exposes many events to trace an application as shown in some previous posts (Getting telemetry data from inside or outside a .NET application, or Avoid DNS issues with HttpClient in .NET). It's hard to know which events are available and which ones are sent by the application. In this post, I describe how to list all available events in a .NET application by registering a custom EventListener.

The solution is to modify the application to register an EventListener and log all sent events. As the code runs in the application, it has access to all events and can enable all EventSource that sends events. The following code snippet shows how to do this:

C#
using System.Collections.Concurrent;
using System.Diagnostics.Tracing;

internal sealed class EventSourceEnumerator : EventListener
{
    public ConcurrentDictionary<string, ConcurrentDictionary<int, EventInfo>> Events { get; } = new();

    public EventSourceEnumerator()
    {
        EventSourceCreated += OnEventSourceCreated;
        EventWritten += OnEventWritten;
    }

    private void OnEventWritten(object? sender, EventWrittenEventArgs e)
    {
        var events = Events[e.EventSource.Name];
        if (events != null)
        {
            if (!events.ContainsKey(e.EventId))
            {
                events.TryAdd(e.EventId, new EventInfo(e.EventId, e.EventName, e.Level, e.PayloadNames?.ToArray() ?? Array.Empty<string>()));
            }
        }
    }

    private void OnEventSourceCreated(object? sender, EventSourceCreatedEventArgs e)
    {
        if (e.EventSource is null)
            return;

        if (!Events.ContainsKey(e.EventSource.Name))
        {
            EnableEvents(e.EventSource, EventLevel.LogAlways);
            Events.TryAdd(e.EventSource.Name, new ConcurrentDictionary<int, EventInfo>());
        }
    }
}

internal sealed record EventInfo(int Id, string? Name, EventLevel Level, string[] PayloadNames);

You can then use the EventSourceEnumerator to list all available events:

C#
using var eventSourceEnumerator = new EventSourceEnumerator();

Console.WriteLine("Hello, World!");
using var httpClient = new HttpClient();
await httpClient.GetStringAsync("https://www.meziantou.net");

foreach (var ev in eventSourceEnumerator.Events)
{
    Console.WriteLine(ev.Key); // Event Source name
    foreach (var e in ev.Value)
    {
        // All events for the event source
        Console.WriteLine($"  Id: {e.Key}, Name: {e.Value.Name}, Level: {e.Value.Level}, Payload: {string.Join(",", e.Value.PayloadNames)}");
    }
}

Do you have a question or a suggestion about this post? Contact me!

Follow me:
Enjoy this blog?Buy Me A Coffee💖 Sponsor on GitHub