AutoMate Technisch Help

HealthCheck

Overzicht

AutoMate.API heeft twee lagen van health endpoints:

  1. Aggregerende endpoints (/health/basic, /health/hangfire, /health/graph) — gebruiken Microsoft.Extensions.Diagnostics.HealthChecks en zijn bedoeld voor Azure App Service health probes en de support-afdeling.

  2. Per-module endpoints (/health/{module}) — lichtgewicht minimale endpoints buiten FastEndpoints, bedoeld voor de publieke statuspagina. Elk endpoint sondeert één functionele module afzonderlijk.

Aggregerende healthchecks

We gebruiken het standaard Microsoft.Extensions.Diagnostics.Healthchecks package. Een andere source die van pas kwam is een blog van Rob Bowman met een simpel voorbeeld.

Registreren van een healthcheck is relatief simpel:

public static void ConfigureHealthChecks(this IServiceCollection services) { services.AddHealthChecks() .AddCheck<GraphHealthCheck>(name: "Graph", failureStatus: HealthStatus.Degraded, tags: ["graph"]) .AddDbContextCheck<UserTypeContext>(name: "Database", failureStatus: HealthStatus.Unhealthy, tags: ["db"]); }

Voor EF Core is een apart package beschikbaar: Microsoft.Extensions.Diagnostics.HealthChecks.EntityFrameworkCore. Dit roept intern CanConnectAsync() aan op de DbContext.

Voor Graph is een eigen implementatie geschreven:

internal class GraphHealthCheck(IGraphService graphService) : IHealthCheck { public async Task<HealthCheckResult> CheckHealthAsync(HealthCheckContext context, CancellationToken cancellationToken = new CancellationToken()) { var uri = "v1.0/groups"; var tenantId = new Guid("8f3d46f0-9151-4904-be93-69420b7d9d61"); var response = await graphService.GetAsync(tenantId, uri, null, cancellationToken); if (response.IsSuccessStatusCode) return HealthCheckResult.Healthy(); return HealthCheckResult.Degraded(); } }

Response

Wat dit endpoint uiteindelijk genereert is een HealthReport response. Let hierbij op dat voor het opvangen van deze response je niet dezelfde class kan gebruiken omdat daarbinnen een HealthCheckEntry struct wordt gebruikt en bij het serialiseren zal deze altijd de "default" geven, en die heeft een status van Unhealthy.

{ "Entries": { "Graph": { "Data": {}, "Description": "Ok", "Duration": "00:00:00.3773475", "Exception": null, "Status": 2, "Tags": ["graph"] }, "Database": { "Data": {}, "Description": null, "Duration": "00:00:00.0681115", "Exception": null, "Status": 2, "Tags": ["db"] } }, "Status": 2, "TotalDuration": "00:00:00.3848577" }

Per-module healthchecks

Ticket #3974 introduceerde een dynamisch GET /health/{module} endpoint zodat de publieke statuspagina elke functionele module afzonderlijk kan pollen — in plaats van alles via /ping.

Architectuur

Het endpoint is een minimaal ASP.NET Core endpoint (geen FastEndpoints, geen TenantGuard pre-processor). Het is anoniem toegankelijk: de statuspagina-poller stuurt geen Authorization - of X-Tenant-Id-header. Bestaande letterlijke routes /health/basic, /health/hangfire en /health/graph hebben voorrang boven het dynamische template.

Elke module implementeert de interface IModuleHealthCheck (Features/Common/Health/Modules/IModuleHealthCheck.cs):

internal interface IModuleHealthCheck { /// <summary>Weergavenaam van de module (bijv. "Branding").</summary> string ModuleName { get; } /// <summary>URL-segment onder /health/ (bijv. "branding"). Mag niet botsen met basic/hangfire/graph.</summary> string RouteSegment { get; } Task<ModuleHealthResult> CheckAsync(CancellationToken cancellationToken = default); }

Alle implementaties worden als IEnumerable<IModuleHealthCheck> geïnjecteerd. Het route-template kiest op RouteSegment met case-insensitive vergelijking; een onbekende module geeft 404.

Geregistreerde modules

Registratie vindt plaats in ModuleHealthComposer.AddModuleHealthChecks():

Route

Weergavenaam

Probe

Verbinding

/health/branding

Branding

SELECT TOP (1) 1 op [dbo].[Signatures]

SignatureDb

/health/identity

Identity

SELECT TOP (1) 1 op [dbo].[IAM_Employee_Sync_Settings]

UserTypeDb

/health/persona

Persona

SELECT TOP (1) 1 op [dbo].[Personas_Definitions]

UserTypeDb

/health/rbac

Role Based Access

SELECT TOP (1) 1 op [dbo].[AccessControl_Policies]

UserTypeDb

/health/msgraph

Microsoft Graph

Graph organization call via GraphHealthCheck

Status mapping

Resultaat

HTTP-statuscode

Healthy (probe < 1 s)

200 OK

Degraded (probe ≥ 1 s)

200 OK

Unhealthy (exception of timeout na 5 s)

503 Service Unavailable

Onbekende module

404 Not Found

JSON response

De response heeft een vaste shape die de statuspagina-poller verwacht. Het veld status op het hoogste niveau is leidend:

{ "status": "Healthy", "module": "Branding", "description": "Branding database OK", "durationMs": 12.3, "data": { "database": "SignatureDb", "table": "[dbo].[Signatures]" } }

In tegenstelling tot de aggregerende endpoints gebruikt dit endpoint geen HealthReport-serialisatie; de response is een plain anonymous object zodat er geen serialisatieprobleem met HealthCheckEntry kan optreden.

Een nieuwe module toevoegen

  1. Maak een klasse die IModuleHealthCheck implementeert in Features/Common/Health/Modules/.

  2. Registreer de klasse in ModuleHealthComposer.AddModuleHealthChecks():

services.AddSingleton<IModuleHealthCheck, MijnNieuweHealthCheck>();

De route /health/{jouw-route-segment} verschijnt automatisch — er is geen extra route-registratie nodig.

SQL-gebaseerde modules kunnen ISqlHealthProbe injecteren en SqlHealthProbe.ExecuteAsync(connectionStringName, sql, cancellationToken) aanroepen. De SqlHealthProbe begrenst de volledige probe (verbinding openen + query) op 5 seconden via een linked CancellationTokenSource; een onbereikbare server kan de statuspagina dus niet langer dan 5 seconden laten wachten.

Koppeling Azure

De App Service health probe wordt via Bicep geconfigureerd met de healthCheckPath-parameter van Infrastructure/Src/Modules/Portal/WebApp.bicep (toegepast op zowel het production- als het staging-slot). Het pad verschilt per app — de API exposeert geen kale /health:

App

healthCheckPath (App Service probe)

Slot-swap warm-up gate (WEBSITE_SWAP_WARMUP_PING_PATH)

Web API

/health/basic (override in WebApiComponents.bicep)

/health/basic

Portal

/health (default van de param)

/health/live — dependency-vrije liveness-route, bewust niet /health omdat die via ApiPingHealthCheck aan de API gekoppeld is

@description('App Service health check path (prod + staging slot). Moet een route zijn die 200 geeft, anders blijft een slot-swap hangen op de warm-up health gate. Portal serveert /health; de API serveert /health/basic.') param healthCheckPath string = '/health'

De per-module endpoints (/health/{module}) worden niet door Azure geprobed; die zijn er uitsluitend voor de publieke statuspagina.

Healthcheck Azure
Last modified: 08 July 2026