Health Checks für Ressourcen
Health Checks in ASP.NET richtig nutzen, Teil 3
Der zweite Teil der Serie „Health Checks in ASP.NET richtig nutzen“ hat gezeigt, dass sich über die Schnittstelle IHealthCheck verschiedene Health-Check-Klassen implementieren lassen. Beispielsweise für externe Schnittstellen, um diese im Kontext eines Health Checks einer eigenen Endpunkt-Implementierung ebenfalls zu überwachen. Denn wenn ein externer, aber notwendiger Dienst offline ist, dann ist die Wahrscheinlichkeit sehr hoch, dass der eigene Endpunkt beziehungsweise Dienst davon in irgendeiner Weise betroffen ist. Viele Dienste hängen nämlich von externen (RESTful) APIs ab
IHealthCheck und externe APIs
Um externe APIs zu überwachen, lässt sich die Schnittstelle IHealthCheck implementieren und ein HttpClient injizieren. Das hat der zweite Teil dieser Serie an einem Beispiel bereits gezeigt. Das nachfolgende Listing 1 greift diese Demo auf, erweitert aber die interne Protokollierung um einen wichtigen Logger und die Rückgabe um den Status Degraded, wenn das angefragte externe API einen HTTP-Status-Code ungleich 200 OK zurückliefert. Dazu nutzt das Beispiel das fiktive API /api/status und fragt dieses ab. Das ist besser als das Beispiel aus dem zweiten Teil dieser Serie, in dem lediglich ein Aufruf des Endpunkts /ping erfolgte.
Listing 1: Erweiterter Health Check mit besserer Rückgabe und Logging
public async Task<HealthCheckResult> CheckHealthAsync(
HealthCheckContext context,
CancellationToken cancellationToken = default)
{
try
{
var response = await _httpClient.GetAsync("/api/status", cancellationToken);
if (response.IsSuccessStatusCode)
{
return HealthCheckResult.Healthy("External API ist erreichbar");
}
return HealthCheckResult.Degraded($"External API antwortete mit {response.StatusCode}");
}
catch (HttpRequestException ex)
{
_logger.LogError(ex, "External API Health Check fehlgeschlagen");
return HealthCheckResult.Unhealthy("External API ist nicht erreichbar", ex);
}
}
Infrastrukturelle Ressourcen
Neben funktionalen Abhängigkeiten spielen infrastrukturelle Ressourcen eine zentrale Rolle bei Health Checks. Zu wenig Speicherplatz, erschöpfter Arbeitsspeicher oder überfüllte Queues können eine Anwendung ebenso unbrauchbar machen wie eine defekte Datenbank. Solche Checks sind oft sehr projektspezifisch, lassen sich aber mit IHealthCheck ebenfalls gut modellieren.
Ein typischer Anwendungsfall ist das Überwachen von freiem Festplattenspeicher, zum Beispiel für Upload-Verzeichnisse oder Log-Ordner. Ein einfacher Disk-Check könnte aussehen wie in Listing 2 gezeigt.
Listing 2: Health-Check-Implementierung für Speicherverbrauch
using Microsoft.Extensions.Diagnostics.HealthChecks;
namespace dnp.Healthy
{
public class DiskSpaceHealthCheck : IHealthCheck
{
private const long MinimumFreeMegabytes = 1024; // 1 GB
public Task<HealthCheckResult> CheckHealthAsync(
HealthCheckContext context,
CancellationToken cancellationToken = default)
{
var drive = new DriveInfo(Path.GetPathRoot(AppContext.BaseDirectory)!);
var free = drive.AvailableFreeSpace / (1024 * 1024);
return Task.FromResult(
free >= MinimumFreeMegabytes
? HealthCheckResult.Healthy($"Free space: {free} MB")
: HealthCheckResult.Unhealthy($"Low disk space: {free} MB"));
}
}
}
Registriert wird der Check wie gewohnt über:
builder.Services.AddHealthChecks()
.AddCheck<DiskSpaceHealthCheck>("disk");
Bild 1 zeigt die Ausgabe in Postman. Zu sehen ist der Status des Health Checks mit Namen und den wichtigsten Werten. In einer produktionsreifen Variante wird man hier typischerweise Laufwerk und Schwellenwerte aus der Konfiguration einlesen und nur die für den Service relevanten Pfade prüfen.
Ausgabe des Health Checks für den Speicherverbrauch in Postman (Bild 1)
AutorÄhnlich lassen sich Checks für Arbeitsspeicher oder Threadpool-Auslastung implementieren, etwa auf Basis von GC.GetTotalMemory oder Performance-Countern. In Containern ist es sinnvoll, sich an den im Pod gesetzten Limits zu orientieren und relative Schwellenwerte, beispielsweise „80% des RAM-Limits“ zu verwenden, statt absolute Werte hart zu codieren. Ein weiteres Muster sind sogenannte Business-Health-Checks wie die nachfolgenden Beispiele:
- „Gibt es unzustellbare Nachrichten in der Dead-Letter-Queue?“
- „Seit wann steht der Hintergrundjob zur Rechnungsstellung?“
- „Ist die interne Feature-Flag-Konfiguration konsistent geladen?“
Diese Checks sind fachlich getrieben, nutzen aber dieselbe Infrastruktur und tauchen wie alle anderen Checks im /health-Report auf. Der eigentliche Mehrwert eines Health-Systems entsteht genau dort: durch eine Mischung aus generischen Prüfungen, wie denen von Datenbank, Disk und Memory, sowie domänenspezifischen Prüfungen, die gemeinsam ein realistisches Bild der Systemgesundheit zeichnen.
Anpassen der JSON-Antwort
Die Standardausgabe eines Health Checks liefert nur den Gesamtstatus. Für Monitoring-Dashboards sind aber detaillierte Informationen hilfreich. Das Anpassen der Health-Antwort hat bereits der erste Teil dieser Serie über den ResponseWriter gezeigt. Das nachfolgende Beispiel erweitert die Ausgabe um wichtige Informationen wie die Gesamtdauer, mögliche Exceptions und weitere Daten. Listing 3 zeigt ein Beispiel für die Implementierung.
Listing 3: Erweiterte JSON-Ausgabe der Health-Check-Antwort
app.MapHealthChecks("/health", new HealthCheckOptions
{
ResponseWriter = async (context, report) =>
{
context.Response.ContentType = "application/json";
var response = new
{
status = report.Status.ToString(),
totalDuration = report.TotalDuration.TotalMilliseconds,
checks = report.Entries.Select(e => new
{
name = e.Key,
status = e.Value.Status.ToString(),
duration = e.Value.Duration.TotalMilliseconds,
description = e.Value.Description,
exception = e.Value.Exception?.Message,
data = e.Value.Data
})
};
await context.Response.WriteAsJsonAsync(response);
}
});
Die resultierende JSON-Antwort enthält pro Check den Status, die Dauer und optionale Zusatzdaten, was die Fehlersuche erheblich erleichtert. Bild 2 zeigt eine beispielhafte Antwort in Postman.
Ausgabe des Health Checks für den Speicherverbrauch in Postman mit weiteren Daten im JSON-Format (Bild 2)
AutorFazit
Diese dritte Folge der Serie „Health Checks in ASP.NET richtig nutzen“ hat gezeigt, wie externe Dienste, Dateisysteme und Speicherplatz überwacht werden können und wie sich die individuellen JSON-Antworten erweitern lassen. Die Erkenntnisse verbessern die Transparenz der Anwendung und erleichtern das Monitoring. Besonders bei externen APIs ist es wichtig, Time-outs zu definieren, damit der Health Check nicht hängen bleibt. Beim HttpClient lässt sich beispielsweise ein Time-out setzen, um das zu erreichen. Ebenso lassen sich Schwellenwerte für zum Beispiel Arbeitsspeicher oder Festplattenplatz definieren, um frühzeitig einen Degraded-Status zu melden.