.NET app health checks in C#

In a distributed system, health checks are periodic assessments of the status, availability, and performance of individual nodes or services. These checks ensure that the system functions correctly and efficiently. Health checks are essential for system reliability, and they are typically performed at regular intervals with the results analyzed for decision-making and corrective actions.

The following heath check status results are possible:

In addition, health checks often report various diagnostic metrics. For more information, see Diagnostic Metrics: Microsoft.Extensions.Diagnostics.HealthChecks.

Resource utilization health checks

To perform health checks on the resource utilization of your .NET apps, add a package reference to Microsoft.Extensions.Diagnostics.HealthChecks.ResourceUtilization. On an IServiceCollection instance, chain a call from AddHealthChecks to AddResourceUtilizationHealthCheck. The following example demonstrates how to use the AddResourceUtilizationHealthCheck extension method to add a resource utilization health check to an IServiceCollection instance:

using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Diagnostics.HealthChecks;
using Microsoft.Extensions.Diagnostics.ResourceMonitoring;
using Microsoft.Extensions.Hosting;

var builder = Host.CreateApplicationBuilder(args);

builder.Services.AddResourceMonitoring();

builder.Services.AddHealthChecks()
    .AddResourceUtilizationHealthCheck();

var app = builder.Build();

var healthCheckService = app.Services.GetRequiredService<HealthCheckService>();

var result = await healthCheckService.CheckHealthAsync();

Console.WriteLine($"{result.Status} {result.TotalDuration}");

app.Run();

The preceding code:

Note

The Microsoft.Extensions.Diagnostics.HealthChecks.ResourceUtilization library assumes that the consumer will register the dependent call to AddResourceMonitoring. If you don't register this, when resolving the HealthCheckService an exception is thrown.

Application lifetime health checks

To perform health checks on the application lifetime events of IHostApplicationLifetime, use the AddApplicationLifecycleHealthCheck extension method available in the Microsoft.Extensions.Diagnostics.HealthChecks.Common NuGet package.

This provider will indicate that the application is healthy only when it is fully active. Until the lifetime object indicates the application has started, the provider will report the application as not healthy. When the application starts shutting down, the provider will report the application as unhealthy.

The library exposes a HealthCheckService enabling consumers to request a health check at any time. Consider the following ExampleService implementation:

using System.Runtime.CompilerServices;
using Microsoft.Extensions.Diagnostics.HealthChecks;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;

internal class ExampleLifecycle(
    HealthCheckService healthCheckService,
    ILogger<ExampleLifecycle> logger) : IHostedLifecycleService
{
    Task IHostedService.StartAsync(
        CancellationToken cancellationToken) =>
        CheckHealthAsync(cancellationToken: cancellationToken);

    Task IHostedLifecycleService.StartedAsync(
        CancellationToken cancellationToken) =>
        CheckHealthAsync(cancellationToken: cancellationToken);

    Task IHostedLifecycleService.StartingAsync(
        CancellationToken cancellationToken) =>
        CheckHealthAsync(cancellationToken: cancellationToken);

    Task IHostedService.StopAsync(
        CancellationToken cancellationToken) =>
        CheckHealthAsync(cancellationToken: cancellationToken);

    Task IHostedLifecycleService.StoppedAsync(
        CancellationToken cancellationToken) =>
        CheckHealthAsync(cancellationToken: cancellationToken);

    Task IHostedLifecycleService.StoppingAsync(
        CancellationToken cancellationToken) =>
        CheckHealthAsync(cancellationToken: cancellationToken);

    public Task ReadyAsync() => CheckHealthAsync();

    private async Task CheckHealthAsync(
         [CallerMemberName] string eventName = "",
         CancellationToken cancellationToken = default)
    {
        HealthReport result =
            await healthCheckService.CheckHealthAsync(cancellationToken);

        logger.LogInformation(
            "{EventName}: {Status}", eventName, result.Status);
    }
}

The preceding code:

  • Defines a new ExampleLifecycle class that implements the IHostedService interface.
  • Defines a primary constructor accepting the following parameters:
  • Implements the IHostedLifecycleService interface, with each method invoking the CheckHealthAsync method.
  • Defines a ReadyAsync method that invokes the CheckHealthAsync method.
  • Defines a custom CheckHealthAsync method that captures the caller name and cancellation token, then requests a health check from the HealthCheckService instance. The result is then logged.

The only time that the health check service will report a status of HealthStatus.Healthy is after the app has started and before stopping is called. Please consider the following Program.cs:

using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Diagnostics.HealthChecks;
using Microsoft.Extensions.Hosting;

var builder = Host.CreateApplicationBuilder(args);

var healthChecksBuilder = builder.Services
    .AddHostedService<ExampleLifecycle>()
    .AddHealthChecks()
    .AddApplicationLifecycleHealthCheck();

// You could use the healthChecksBuilder instance to add more checks...

var app = builder.Build();

var services = app.Services.GetRequiredService<IEnumerable<IHostedService>>();

await Task.WhenAll(DelayAndReportAsync(services), app.RunAsync());

static async Task DelayAndReportAsync(IEnumerable<IHostedService> services)
{
    // Ensure app started...
    await Task.Delay(500);

    var service = services.FirstOrDefault(static s => s is ExampleLifecycle);
    if (service is ExampleLifecycle example)
    {
        await example.ReadyAsync();
    }
}

The preceding code:

The app outputs logs in the following order, reporting the health check status as it relates to the lifecycle events:

  1. StartingAsync: Unhealthy
  2. StartAsync: Unhealthy
  3. StartedAsync: Unhealthy
  4. ReadyAsync: Healthy
  5. StoppingAsync: Unhealthy
  6. StopAsync: Unhealthy
  7. StoppedAsync: Unhealthy

In other words, this provider ensures that the application instance only receives traffic when it's ready. If you're developing web apps with ASP.NET Core, there's health checks middleware available. For more information, Health checks in ASP.NET Core.

See also