BLOG · .NET · OBSERVABILITÉ

Health Checks en .NET : ce simple outil peut sauver votre prod (et vos nuits)

Illustration : un électrocardiogramme sur l'endpoint /health avec des statuts Healthy, Degraded et Unhealthy

Imaginez : votre base de données est down, mais vous le découvrez seulement quand vos utilisateurs appellent le support.

Ou bien vous dépendez d'une API externe, et elle ne répond plus sans que vous le sachiez. Votre espace disque est plein, et vos services plantent sans prévenir.

Avec les Health Checks, tout ça change : vous êtes informé en temps réel et vous pouvez réagir avant que ça n'impacte les utilisateurs. En moins de 10 minutes, j'ai mis en place un système complet dans mon application pour monitorer mes services critiques.

Ce que vous pouvez surveiller

Bases de données : vérifier que vos bases en lecture et en écriture sont accessibles. APIs externes : surveiller la connectivité à vos fournisseurs tiers (Datadog, par exemple). Système : espace disque disponible, consommation mémoire, latence réseau. Et aussi : files de messages, et même vos règles métier.

Les packages nécessaires

J'en utilise plusieurs car j'ai configuré des vérifications spécifiques pour couvrir différents besoins :

AspNetCore.HealthChecks.Network
AspNetCore.HealthChecks.SqlServer
AspNetCore.HealthChecks.System
AspNetCore.HealthChecks.UI
AspNetCore.HealthChecks.UI.Client
AspNetCore.HealthChecks.UI.InMemory.Storage
AspNetCore.HealthChecks.Uris
Microsoft.Extensions.Diagnostics.HealthChecks

Côté code : Program.cs

builder.Services.ConfigureHealthChecks(builder.Configuration);

var app = builder.Build();

app.MapHealthChecks("/health", new HealthCheckOptions
{
    ResponseWriter = UIResponseWriter.WriteHealthCheckUIResponse
});

app.UseHealthChecksUI(options =>
{
    options.UIPath = "/healthcheck-ui";
});

La configuration centralisée

Une extension HealthCheckExtensions regroupe tous les checks :

public static void ConfigureHealthChecks(this IServiceCollection services, IConfiguration configuration)
{
    var connectionStrings = configuration.GetSection("ConnectionStrings").Get<ConnectionStrings>();
    services.Configure<DatadogSettings>(configuration.GetSection("Datadog"));

    services.AddHealthChecks()
        .AddSqlServer(connectionStrings.ReadDatabase,
            name: "SQL Server - Read Database",
            failureStatus: HealthStatus.Unhealthy,
            tags: new[] { "database", "read" })
        .AddSqlServer(connectionStrings.WriteDatabase,
            name: "SQL Server - Write Database",
            failureStatus: HealthStatus.Unhealthy,
            tags: new[] { "database", "write" })
        .AddCheck<DatadogHealthCheck>("Datadog API Monitoring",
            failureStatus: HealthStatus.Degraded,
            tags: new[] { "monitoring" })
        .AddPrivateMemoryHealthCheck(
            maximumMemoryBytes: 1024L * 1024L * 1024L,
            name: "Private Memory Usage (1GB Limit)",
            tags: new[] { "system" })
        .AddDiskStorageHealthCheck(
            options => options.AddDrive("C:\\", 1024),
            name: "Disk space",
            tags: new[] { "system" })
        .AddPingHealthCheck(
            options => { options.AddHost("8.8.8.8", 100); },
            name: "Network Latency (Google DNS)",
            tags: new[] { "network" });

    services.AddHealthChecksUI(setup =>
    {
        setup.SetEvaluationTimeInSeconds(60);
        setup.MaximumHistoryEntriesPerEndpoint(60);
        setup.AddHealthCheckEndpoint("api", "/health");
    }).AddInMemoryStorage();
}

Les checks sont évalués toutes les 60 secondes, l'historique des 60 derniers résultats est conservé, et tout est exposé sur /health.

Un check personnalisé : l'API Datadog

En consultant la documentation de Datadog, j'ai trouvé un endpoint dédié pour vérifier que leur API est opérationnelle. En général, les fournisseurs d'APIs externes proposent ce type de route : vous pouvez appliquer cette méthode à n'importe quelle API en adaptant votre check :

public async Task<HealthCheckResult> CheckHealthAsync(
    HealthCheckContext context, CancellationToken cancellationToken = default)
{
    try
    {
        var uri = new Uri(_settings.HealthCheckApi);
        var response = await _client.PostAsJsonAsync(uri, new[] { checkData }, cancellationToken);

        return response.IsSuccessStatusCode
            ? HealthCheckResult.Healthy()
            : HealthCheckResult.Unhealthy($"Datadog API returned {response.StatusCode}");
    }
    catch (Exception ex)
    {
        return HealthCheckResult.Unhealthy("Failed to connect to Datadog API", ex);
    }
}

L'interface graphique et les alertes

Tout est visualisable dans une interface claire grâce à HealthChecksUI (/healthcheck-ui) : état de chaque check, durée, historique. Quand un check échoue, la description vous dit exactement quoi : « Datadog API returned Forbidden », « minimum configured megabytes for disk C:\ is… ».

Et pour être alerté sans regarder l'interface : notifications par email (SMTP), Slack / Teams, webhooks vers vos propres services, ou intégration à des outils comme Datadog, PagerDuty ou Opsgenie.

À retenir : ne laissez plus vos utilisateurs être vos testeurs en prod. Quelques lignes de code, et vous avez une vue d'ensemble de l'état de vos services, et la tranquillité de pouvoir réagir vite.

Cet article est tiré d'une de mes publications LinkedIn : rejoignez la discussion ↗