.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:
- Creates a new HostApplicationBuilder instance.
- Adds resource monitoring by calling AddResourceMonitoring.
- Adds a health check for resource utilization by chaining a call from the AddHealthChecks call to the AddResourceUtilizationHealthCheck extension method.
- Builds the IHost instance as the
app
variable. - Gets an instance of the HealthCheckService class from the service provider.
- Performs a health check and displays the result.
- Runs the application.
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:
- An instance of the HealthCheckService class.
- An instance of the ILogger<TCategoryName> class.
- Implements the IHostedLifecycleService interface, with each method invoking the
CheckHealthAsync
method. - Defines a
ReadyAsync
method that invokes theCheckHealthAsync
method. - Defines a custom
CheckHealthAsync
method that captures the caller name and cancellation token, then requests a health check from theHealthCheckService
instance. Theresult
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:
- Creates a new HostApplicationBuilder instance assigning to as the
builder
variable. - Registers the
ExampleService
as the app's only IHostedService. - Adds a health check for the application lifetime events of IHostApplicationLifetime by chaining a call from the IHealthChecksBuilder instance returned by the AddHealthChecks call to the AddApplicationLifecycleHealthCheck extension method.
- The
healthChecksBuilder
instance can be used to add more health checks.
- The
- Builds the IHost instance as the
app
variable. - Gets an
IHostedService
from the service provider, this is theExampleService
instance. - Calls Task.WhenAll given two task references:
- The
DelayAndReportAsync
method, which delays for 500 milliseconds and then invokes theReadyAsync
method on theExampleService
instance, will evaluate the health check. - The RunAsync(IHost, CancellationToken) method, starts the
app
.
- The
The app outputs logs in the following order, reporting the health check status as it relates to the lifecycle events:
StartingAsync
: UnhealthyStartAsync
: UnhealthyStartedAsync
: UnhealthyReadyAsync
: HealthyStoppingAsync
: UnhealthyStopAsync
: UnhealthyStoppedAsync
: 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.