ASP.NET Core 2.2 Health Checks Explained
Tags: dotnet, csharp, aspnetcore
Published: 2019-04-22 18:01:09

---
title: ASP.NET Core 2.2 Health Checks Explained
published: true
description: Get the full overview of how to implement health checks with ASP.NET Core 2.2. Health checks can help you monitor your websites and their dependencies.
cover_image: https://thepracticaldev.s3.amazonaws.com/i/dtux715kljlyy9038ptt.png
tags: dotnet, csharp, aspnetcore
canonical_url: https://blog.elmah.io/asp-net-core-2-2-health-checks-explained/
---

ASP.NET Core 2.2 introduces a range of new features. One of the more interesting (IMO) is Health Checks. You may use tools like Pingdom or [elmah.io Uptime Monitoring](https://elmah.io/features/uptimemonitoring/) to ping your website in a specified interval. Pinging a single HTML page may or may not reveal if your application is healthy or not. Health Checks to the rescue! Before trying out the code yourself, make sure to install the recent version of ASP.NET Core 2.2 and Visual Studio 2017.

When developing a new service, I typically define a custom route (like `/working`). This endpoint is requested by Uptime Monitoring, and a notification is sent if things stop working. How `/working` is implemented, is up to each API. Some APIs may require a database connection, while others need something completely else. Until now, one of my custom health endpoints could have looked something like this:

```csharp
[Route("working")]
public ActionResult Working()
{
    using (var connection = new SqlConnection(_connectionString))
    {
        try
        {
            connection.Open();
        }
        catch (SqlException)
        {
            return new HttpStatusCodeResult(503, "Generic error");
        }
    }

    return new EmptyResult();
}
```

In this example, the `/working` endpoint returns `503` if we couldn't open a connection to a SQL Server, but could be another database or any other resource.

With the new Health Checks feature in ASP.NET Core 2.2, health checks no longer require you to create new controllers like in the example above. Health checks are enabled and configured through `Startup.cs`:

```csharp
public void ConfigureServices(IServiceCollection services)
{
    ...
    services.AddHealthChecks();
    ...
}

public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
    ...
    app.UseHealthChecks("/working");
    ...
}
```

With the sample above, a new endpoint named `/working` can be requested by your preferred uptime monitoring system or simply through the browser:

![Healthy](https://blog.elmah.io/content/images/2018/11/health-check-healthy.png)

In this example, requesting the endpoint will return a status code `200` together with the body `Healthy`, indicating that the API is reachable. If we want to replicate the controller action in the first example, we have a couple of options. Health checks that you only need in a single API can be specified directly as a new check:

```csharp
public void ConfigureServices(IServiceCollection services)
{
    ...
    services
        .AddHealthChecks()
        .AddCheck("sql", () =>
        {
            using (var connection = new SqlConnection(_connectionString))
            {
                try
                {
                    connection.Open();
                }
                catch (SqlException)
                {
                    return HealthCheckResult.Unhealthy();
                }
            }

            return HealthCheckResult.Healthy();
        });
    ...
}
```

Requesting the `/working` endpoint automatically executes all lambdas provided to invocations of the `AddCheck`-method. In the case of a `SqlException` thrown inside the health check, a status code of `503` and body `Unhealthy` is returned:

![Unhealthy](https://blog.elmah.io/content/images/2018/11/health-check-unhealthy.png)

A better approach is to write a re-usable health check, contained in its class:

```csharp
public class SqlServerHealthCheck : IHealthCheck
{
    SqlConnection _connection;

    public string Name => "sql";

    public SqlServerHealthCheck(SqlConnection connection)
    {
        _connection = connection;
    }

    public async Task<HealthCheckResult> CheckHealthAsync(CancellationToken cancellationToken = default(CancellationToken))
    {
        try
        {
            connection.Open();
        }
        catch (SqlException)
        {
            return HealthCheckResult.Unhealthy();
        }
        
        return HealthCheckResult.Healthy();
    }
}
```

Configuring the new health check only requires a single line of code:

```csharp
public void ConfigureServices(IServiceCollection services)
{
    ...
    services
        .AddHealthChecks()
        .AddCheck<SqlServerHealthCheck>("sql");
    ...
}
```

Both lambda-based and `IHealthCheck`-based checks can be mixed and matched. When adding multiple checks, ASP.NET Core executes each check in the order they were added. All checks needs to return `HealthCheckResult.Healthy()` in order for the request to be successful. This means that a single failing health check, will cause an `Unhealthy` response.

Health checks are a great addition to an already excellent platform. In time, health checks will (hopefully) exist for most possible dependencies. In fact, the BeatPulse project already migrated <a href="https://github.com/Xabaril/AspNetCore.Diagnostics.HealthChecks" target="_blank" rel="noopener noreferrer">their checks</a> to ASP.NET Core Health Checks. Checks for popular databases like SQL Server, MySQL and Oracle already exists. I hope for .NET compatible software as well as the community to pick up the task of writing reusable health checks for all sorts of resources.

## Dependency injection

As shown in the code for `SqlServerHealthCheck`, health checks added using the `AddCheck<T>`-method can use dependency injection, just as you know if from other areas of ASP.NET Core. Health checks are created as transient objects, meaning that a new instance is created every time the `/working` endpoint is requested. If you want another lifecycle, health checks can be added manually like this:

```csharp
public void ConfigureServices(IServiceCollection services)
{
    ...
    services
        .AddHealthChecks()
        .AddCheck<SqlServerHealthCheck>("sql");
    
    services.AddSingleton<SqlServerHealthCheck>();
    ...
}
```

Here, the `SqlServerHealthCheck` is added as a singleton.

## Custom status codes

Health checks comes with a range of status codes out of the box. Like `503` for unhealthy responses. In some cases you may want to change the codes, in order to make the health endpoint work with a client expecting a certain status code.

To change a status code, you can provide custom options when calling the `UseHealthChecks`-method:

```csharp
var options = new HealthCheckOptions();
options.ResultStatusCodes[HealthStatus.Unhealthy] = 418;
app.UseHealthChecks("/working", options);
```

In this example, I replace the status code for the `Unhealthy` state with `418` (<a href="https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/418" target="_blank" rel="noopener noreferrer">I'm a teapot</a>).

## Custom response

Much like the returned status codes, the body of the response can be customized as well:

```csharp
var options = new HealthCheckOptions();
options.ResponseWriter = async (c, r) =>
{
    c.Response.ContentType = "application/json";

    var result = JsonConvert.SerializeObject(new
    {
        status = r.Status.ToString(),
        errors = r.Entries.Select(e => new { key = e.Key, value = e.Value.Status.ToString() })
    });
    await c.Response.WriteAsync(result);
};
app.UseHealthChecks("/working", options);
```

In the example, a JSON object containing the overall status and status of each health check is returned.

![Custom response](https://blog.elmah.io/content/images/2018/11/health-check-custom.png)

I expect Microsoft and/or the community to come up with multiple health check response writers. A writer able to serialize the result to <a href="https://tools.ietf.org/html/draft-inadarei-api-health-check-02" target="_blank" rel="noopener noreferrer">draft-inadarei-api-health-check-02</a> or <a href="https://tools.ietf.org/html/rfc7807" target="_blank" rel="noopener noreferrer">rfc7807</a> would make a lot of sense.

## Publishing health check results

If you are using an uptime monitoring solution like elmah.io, we automatically log the response from the `/working` endpoint. In some scenarios, publishing health check results directly from your application may be a better option.

ASP.NET Core Health Checks provide a concept named *Publishers*. By setting up one or more publishers, health check results can be stored in an internal or external storage like Application Insights or elmah.io.

To configure elmah.io as storage for health check results, install the `Elmah.Io.AspNetCore.HealthChecks` NuGet package:

```ps
Install-Package Elmah.Io.AspNetCore.HealthChecks -IncludePrerelease
```

Then add the elmah.io publisher:

```csharp
public void ConfigureServices(IServiceCollection services)
{
    ...
    services
        .AddHealthChecks()
        // Configure your health checks here
        .AddElmahIoPublisher("API_KEY", new Guid("LOG_ID"));
    ...
}
```

All degraded or unhealthy results are now persisted in elmah.io, where additional notification rules can be configured. For more information about our integration with health checks, check out [Publishing ASP.NET Core 2.2 health check results to elmah.io](https://blog.elmah.io/publishing-asp-net-core-2-2-health-check-results-to-elmah-io/).

More of an Application Insights kind of guy? Luckily, AI is supported as well:

```ps
Install-Package AspNetcore.HealthChecks.Publisher.ApplicationInsights
```

Then configure the AI publisher:

```csharp
public void ConfigureServices(IServiceCollection services)
{
    ...
    services
        .AddHealthChecks()
        // Configure your health checks here
        .AddApplicationInsightsPublisher();
    ...
}
```

## Visualizing health checks

If you want your health check results visualized as more than a list of errors, there's a nice open source visualization too named HealthChecksUI.

To enable HealthChecksUI, install the `AspNetCore.HealthChecks.UI` package:

```ps
Install-Package AspNetCore.HealthChecks.UI
```

Enable the UI in `Startup.cs`:

```csharp
public void ConfigureServices(IServiceCollection services)
{
    services.AddHealthChecksUI();
}

public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
    app.UseHealthChecksUI();
}
```

HealthChecksUI can be accessed on `/healthchecks-ui`:

![HealthChecksUI](https://blog.elmah.io/content/images/2018/11/ui-home.png)<small>
<center>Credit: <a href="https://github.com/Xabaril/AspNetCore.Diagnostics.HealthChecks" rel="noopener noreferrer" target="_blank">Xabaril</a></small></center>

<hr/>

## Would your users appreciate fewer errors?

elmah.io is the easy error logging and uptime monitoring service for .NET. Take back control of your errors with support for all .NET web and logging frameworks.

➡️ [Error Monitoring for .NET Web Applications](https://elmah.io/?utm_source=devto&utm_medium=social&utm_campaign=devtoposts) ⬅️

This article first appeared on the elmah.io blog at https://blog.elmah.io/asp-net-core-2-2-health-checks-explained/