Get CPU usage in Azure App Service with C#

Viewed 66

I am trying to measure CPU usage for my application hosted with Azure App Service programmatically.

I use Performance Counter but it needs administration privilege which is not possible on App Service. on another hand it only works fully functional if the operation system of App Service is Windows.

Does someone know how I can get the CPU usage of my application on Azure App Service programmatically?

1 Answers

For a cross platform cpu measurement use event counters:

EventCounters are .NET APIs used for lightweight, cross-platform, and near real-time performance metric collection. EventCounters were added as a cross-platform alternative to the "performance counters" of .NET Framework on Windows. In this article, you'll learn what EventCounters are, how to implement them, and how to consume them.

The .NET runtime and a few .NET libraries publish basic diagnostics information using EventCounters starting in .NET Core 3.0. Apart from the EventCounters that are provided by the .NET runtime, you may choose to implement your own EventCounters. EventCounters can be used to track various metrics. Learn more about them in well-known EventCounters in .NET

EventCounters live as a part of an EventSource, and are automatically pushed to listener tools on a regular basis. Like all other events on an EventSource, they can be consumed both in-proc and out-of-proc via EventListener and EventPipe. This article focuses on the cross-platform capabilities of EventCounters, and intentionally excludes PerfView and ETW (Event Tracing for Windows) - although both can be used with EventCounters.

There are several event counter provided by the .Net framework, listened here.

You can listen to these events in-proc using an EventListener. The code below shows an example implementation. The value of EventCounterIntervalSec determines the frequency in seconds of the measurements.

First, we need a hosted service that listens to the runtime event source during the lifetime of the web app process

public class CpuUsageListenerService : BackgroundService
{
    private CpuUsageListener cpuListener;

    protected override Task ExecuteAsync(CancellationToken stoppingToken)
    {
        cpuListener = new CpuUsageListener();

        return Task.CompletedTask;
    }

    public double LastValue => cpuListener.LastValue;
}

public sealed class CpuUsageListener : EventListener
{
    public double LastValue { get; private set; }

    protected override void OnEventSourceCreated(EventSource eventSource)
    {
        if (eventSource.Name.Equals("System.Runtime"))
            EnableEvents(eventSource, EventLevel.LogAlways, EventKeywords.All, new Dictionary<string, string> { { "EventCounterIntervalSec", "1" } });
    }

    protected override void OnEventWritten(EventWrittenEventArgs eventData)
    {
        if (eventData.EventName != "EventCounters")
            return;

        var payload = (IDictionary<string, object>)eventData.Payload[0];
        var isCpuCounterValue = payload["Name"] == "cpu-usage";
        if (!isCpuCounterValue)
            return;

        LastValue = (double)payload["Mean"];
        Console.WriteLine(LastValue); // Output value to console every second
    }
}

Register the service in your startup.cs:

public void ConfigureServices(IServiceCollection services)
{
    ...
            
    services.AddSingleton<CpuUsageListenerService>();
    services.AddHostedService(sp => sp.GetService<CpuUsageListenerService>());
            
    ...
}

The, finally, in your controller you can get the last value like this:

public class ValuesController : ControllerBase
{
    private readonly CpuUsageListenerService cpuUsageListenerService;

    public ValuesController(CpuUsageListenerService cpuUsageListenerService)
    {
        this.cpuUsageListenerService = cpuUsageListenerService;
    }

    [HttpGet("/api/cpu")]
    public ActionResult<double> CpuUsage()
    {
        return cpuUsageListenerService.LastValue;
    }
}
Related