ASP.NET Core  

Implementing Health Checks with Custom Diagnostics in .NET Core

Production monitoring requires visibility into application health, database connectivity, and downstream service availability. .NET Core provides a built-in Health Checks framework that exposes HTTP endpoints to report status to container orchestrators (like Kubernetes) or load balancers (like Azure App Gateway or NGINX).

Step 1: Install Required NuGet Packages

Add the health checks UI and database diagnostics packages to your project:

Bash

dotnet add package Microsoft.Extensions.Diagnostics.HealthChecks
dotnet add package AspNetCore.Diagnostics.HealthChecks.EntityFrameworkCore

Step 2: Implement a Custom Health Check

Beyond checking database connections, you might want to verify custom dependencies, such as external API reachability, disk space availability, or custom memory thresholds. Implement IHealthCheck:

C#

using Microsoft.Extensions.Diagnostics.HealthChecks;

public class ExternalApiHealthCheck : IHealthCheck
{
    private readonly HttpClient _httpClient;

    public ExternalApiHealthCheck(HttpClient httpClient)
    {
        _httpClient = httpClient;
    }

    public async Task<HealthCheckResult> CheckHealthAsync(
        HealthContext context, 
        CancellationToken cancellationToken = default)
    {
        try
        {
            // Ping an external dependency or internal microservice endpoint
            var response = await _httpClient.GetAsync("https://api.github.com/status", cancellationToken);

            if (response.IsSuccessStatusCode)
            {
                return HealthCheckResult.Healthy("External API is fully operational.");
            }

            return HealthCheckResult.Degraded("External API returned a non-success status code.");
        }
        fatch (Exception ex)
        {
            return HealthCheckResult.Unhealthy("External API is unreachable.", ex);
        }
    }
}

Step 3: Register Health Checks in Program.cs

Configure your built-in and custom health checks in the dependency injection container, segmenting them by operational tiers (e.g., Liveness vs. Readiness probes).

C#

using Microsoft.AspNetCore.Diagnostics.HealthChecks;
using Microsoft.Extensions.Diagnostics.HealthChecks;

var builder = WebApplication.CreateBuilder(args);

// Register Health Checks services
builder.Services.AddHealthChecks()
    // 1. Built-in EF Core database check
    .AddDbContextCheck<AppDbContext>("database", failureStatus: HealthStatus.Unhealthy)
    // 2. Custom registered HTTP dependency check
    .AddCheck<ExternalApiHealthCheck>("external-github-api", failureStatus: HealthStatus.Degraded);

builder.Services.AddHttpClient<ExternalApiHealthCheck>();
builder.Services.AddControllers();

var app = builder.Build();

// Map comprehensive endpoint reporting JSON details
app.MapHealthChecks("/health/detailed", new HealthCheckOptions
    {
        ResponseWriter = async (context, report) =>
        {
            context.Response.ContentType = "application/json";
            var response = new
            {
                Status = report.Status.ToString(),
                Checks = report.Entries.Select(e => new
                {
                    Component = e.Key,
                    Status = e.Value.Status.ToString(),
                    Description = e.Value.Description,
                    Duration = e.Value.Duration
                }),
                TotalDuration = report.TotalDuration
            };
            await context.Response.WriteAsJsonAsync(response);
        }
    });

// Map lightweight liveness endpoint for Kubernetes probes
app.MapHealthChecks("/health/liveness", new HealthCheckOptions
    {
        Predicate = r => r.Tags.Contains("liveness")
    });

app.Run();