Implementing .NET Core 2.2 health checks

.NET Core 2.2 health checks are introduced right out of the box and with minimal work required in order to get them up and running.

If you don’t already have health checks, it’s something to strongly consider. Knowing whether your application is healthy or not can minimise risk by reducing downtime as well as investigation time, and it can alert you when services go down.

If you do have health checks that you’ve implemented yourself, switching to the native .NET Core implementation will be a breeze.

Nuget packages

You’ll want to install the following nuget package before we begin:

Install-Package Microsoft.Extensions.Diagnostics.HealthChecks

Our previous health checks

Previously in our application, we implemented health checks ourselves. This involved:

- A health check HTTP controller

- A health check response model

- A masterclass containing the logic for each health check test; each defined in separate methods

Controller

Health check response model

Logic class

Switch to .NET Core health check

The route for our health check was /healthcheck, so the first step here was to delete our controller to remove the current endpoint. After doing this, booting up the application and navigating to /healthcheck, we were presented with a 404 Not Found. Perfect.

Now we can create this endpoint again, but using the native .NET Core implementation. Creating this endpoint requires two lines of code in your Startup.cs class.

That’s it. Restart your application and navigate to that endpoint. It should be up and running, and returning healthy.

Custom logic

We aren’t using any custom logic yet to check items that we care about. This could include our API Dependencies, or our database connections etc.

This means that at present, all we know is that our application is running.

Let’s say we have three checks we’d like to do. Two API dependencies and one database connection check. Each check should be split into its own class.

So, we create a class for our database check — and we make it implement the IHealthCheck interface that comes with .NET Core 2.2. This interface will give you a CheckHealthAsync method to implement, and this is where your logic goes to check the health of this service.

Here we have implemented the same logic that we had previously. However, we’ve removed a few things.

Firstly, we were previously using a stopwatch to monitor the response time for this health check. .NET Core automatically times our check for us, so we don’t have to implement our own logic for this.

We’re also not returning unhealthy if we hit an exception. .NET knows that if we’re throwing exceptions in a health check, that this isn’t healthy. It’ll automatically return unhealthy if this happens. This also gives us more potential data, as we can present any exceptions in our health check response if we choose to.

We also have the option of returning HealthCheckResult.Unhealthy()

If we add extra logic, we also can return HealthCheckResult.Degraded()

These HealthCheckResult objects also allow us to explicitly pass in Description, Exception and Data (Data is a dictionary of key-value pairs). All of this can be retrieved and used on our health check page to present more information, which can help us find the causes of unhealthiness.

return HealthCheckResult.Degraded(“Description”, exception, new Dictionary<string, object> { { “foo”, “bar” }});

Adding your health checks

We’ve created separate classes for our separate checks, which all implement IHealthCheck and we have our logic in place, now what?

Return to your Startup.cs where we call services.AddHealthChecks()

We can chain our health checks onto this call, by passing in our class with implements IHealthCheck, and a name to identify what we are checking.

How much cleaner is that than the previous large health check class we had before?

Custom response body

Now if we restart our application, and navigate to /healthcheck, we are still presented with healthy (providing your logic is returning healthy for all your checks). Now that’s great, but it’s not very informative. We want to customise our response.

We opted for a JSON response, showing each check with its name, health status, response time, and then the overall time that the health check took.

To do this, we need to create a HealthCheckOptions object, with custom ResponseWriter logic. It looks like this:

This is fully customisable. You can access the descriptions, exceptions and dictionary data that we mentioned earlier. But this wasn’t a requirement for us.

And to use these options:

app.UseHealthChecks(“/healthcheck”);

becomes:

app.UseHealthChecks(“/healthcheck”, healthCheckOptions);

Restart your application and navigate to /healthcheck

Standard logic

Are you wanting a simple check? Maybe test an endpoint is running, or a database is up, and that’s it? There are a variety of Nuget packages available that allow you to test some common platforms easily, without implementing your own IHealthCheck classes!

Install-Package AspNetCore.HealthChecks.System
Install-Package AspNetCore.HealthChecks.Network
Install-Package AspNetCore.HealthChecks.SqlServer
Install-Package AspNetCore.HealthChecks.MongoDb
Install-Package AspNetCore.HealthChecks.Npgsql
Install-Package AspNetCore.HealthChecks.Elasticsearch
Install-Package AspNetCore.HealthChecks.Redis
Install-Package AspNetCore.HealthChecks.EventStore
Install-Package AspNetCore.HealthChecks.AzureStorage
Install-Package AspNetCore.HealthChecks.AzureServiceBus
Install-Package AspNetCore.HealthChecks.AzureKeyVault
Install-Package AspNetCore.HealthChecks.MySql
Install-Package AspNetCore.HealthChecks.DocumentDb
Install-Package AspNetCore.HealthChecks.SqLite
Install-Package AspNetCore.HealthChecks.RavenDB
Install-Package AspNetCore.HealthChecks.Kafka
Install-Package AspNetCore.HealthChecks.RabbitMQ
Install-Package AspNetCore.HealthChecks.OpenIdConnectServer
Install-Package AspNetCore.HealthChecks.DynamoDB
Install-Package AspNetCore.HealthChecks.Oracle
Install-Package AspNetCore.HealthChecks.Uris
Install-Package AspNetCore.HealthChecks.Aws.S3
Install-Package AspNetCore.HealthChecks.Consul
Install-Package AspNetCore.HealthChecks.Hangfire

And the implementation is as easy as:

All working

If everything is working as expected, the last step is to remove your old redundant code and enjoy your new health check.

— Tom Longhurst is a Quality Assurance Engineer in ASOS Tech.