ASP.Net Core 2.2 runtime events

ASP.Net Core 2.2 was recently released and announce at the Microsoft Connect() conference in Las Vegas. Sure, there was a lot of hype on the .Net Core 3.0 announcement but ASP.Net Core 2.2 is now GA, while .Net Core 3.0 is only in preview. And since ASP.Net Core 2.2 is now globally available it also brings some new features that can be used in production. In this article I want to briefly describe one feature that in my opinion is the most useful one: runtime events.

It is often desirable to monitor runtime services such as the GC, JIT, and ThreadPool of the current process to understand how these services are behaving while running your application. On Windows systems, this is commonly done using ETW and monitoring the ETW events of the current process. While this continues to work well, it is not always easy or possible to use ETW. Whether you’re running in a low-privilege environment or running on Linux or macOS, it may not be possible to use ETW.

Starting with .NET Core 2.2, CoreCLR events can now be consumed using the EventListener class. These events describe the behavior of GC, JIT, ThreadPool, and interop. They are the same events that are exposed as part of the CoreCLR ETW provider on Windows. This allows for applications to consume these events or use a transport mechanism to send them to a telemetry aggregation service.

Let’s see it in action

To see how it works I have created a Web API template in ASP.Net Core 2.2. To get it running one needs to simply create a class that inherits the EventListener class and overrides the OnEventSourceCreated() and OnEventWritten() methods.


public class Diagnostics<T> : EventListener where T : class
{
    // Called whenever an EventSource is created.
    protected override void OnEventSourceCreated(EventSource eventSource)
    {
        // Watch for the .NET runtime EventSource and enable all of its events.
        if (eventSource.Name.Equals("Microsoft-Windows-DotNETRuntime"))
        {
            EnableEvents(eventSource, EventLevel.Verbose, (EventKeywords)(-1));
        }
    }

    // Called whenever an event is written.
    protected override void OnEventWritten(EventWrittenEventArgs eventData)
    {
        // Write the contents of the event to the console.
        Console.WriteLine($"ThreadID = {eventData.OSThreadId} " +
        $"ID = {eventData.EventId} " +
        $"Name = {eventData.EventName}");
        for (int i = 0; i < eventData.Payload.Count; i++)
        {
            string payloadString = eventData.Payload[i] != null
            ? eventData.Payload[i].ToString()
            : string.Empty;
            Console.WriteLine($"\tName = \"{eventData.PayloadNames[i]}\" Value = \"{payloadString}\"");
        }
        Console.WriteLine("\n");
    }
}

I have decided to create my EventListener as a generic class so that I can add it as a typed service where ever I might need it. In fact, that the next step. One should add the listener as a service in the ConfigureServices() method in the Startup.cs file.


services.AddScoped(typeof(Diagnostics<>));

Afterwards our listener is available for dependency injection so the next step is to inject it in the ValuesController that comes with the Web API template.  Too do that, add the following private field and constructor to the ValuesController:


private Diagnostics<ValuesController> _diag;
public ValuesController(Diagnostics<ValuesController> diag)
{
    _diag = diag;
}

That’s it. Now if you start the API and hit the ValuesController with a request from Postman, you’ll notice in the console a lot of the above mentioned runtime events. Remember that this is only a getting started guide on ASP.Net Core runtime events to familiarize yourself. In real application you might want to be a little bit more creative on what you do once you receive such event, where exactly you want to listen to runtime events in your application and so on. Still, from my point of view this might be the most important feature in ASP.Net Core 2.2 so have fun playing with it!

Leave a Reply

Your email address will not be published. Required fields are marked *