DEV Community

Cover image for ASP.NET Core Health Checks
Sahan
Sahan

Posted on • Originally published at sahansera.dev on

ASP.NET Core Health Checks

When you are developing a project where you have multiple services talking to each other, it would be hard to see their service health instantly.

This article will look at how we can configure ASP.NET Core health checks and look into what kind of different metrics we can gather from it.

šŸ’” Follow along with the code samples for this blog post from this repo.

The setup

I will be using ASP.NETā€™s MVC and template, as most of you are familiar with it. However, you can also use other project types such as API, Console or even Blazor.

Letā€™s scaffold an MVC app.

dotnet new mvc -n Monitor
dotnet new sln
dotnet sln add Monitor
Enter fullscreen mode Exit fullscreen mode

First, we need to install the HealthChecks package.

cd Monitor/
dotnet add package AspNetCore.HealthChecks.UI
Enter fullscreen mode Exit fullscreen mode

Now we are ready to hook it up to the middleware pipeline.

Application Configuration

Navigate to the Startup.cs and, letā€™s add the following in the ConfigureServices method.

services.AddHealthChecks();
Enter fullscreen mode Exit fullscreen mode

For starters, we will add an endpoint to spit out a JSON to show the appā€™s current status.

app.UseEndpoints(endpoints =>
{
    endpoints.MapControllerRoute(
        name: "default",
        pattern: "{controller=Home}/{action=Index}/{id?}");

        // Mapping a new endpoint to see the health
    endpoints.MapHealthChecks("/health");
});
Enter fullscreen mode Exit fullscreen mode

aspdotnet-core-health-checks-1.png

Well, that isnā€™t particularly useful. This health checks package we are using provides many different output formats. They have made it extensible so that you can even use a custom output format. If you have many services talking to each other, it will make sense to use a format like JSON.

šŸ’” Feel free to skip this section and jump ahead to ā€œRegistering health checksā€ section to see the final result.

Health checks UI dashboard

We are interested in seeing is a Dashboard that comes out-of-the-box. We will use our current project as a monitoring app to probe in and check the health status of some other applications that are running. The following diagram explains our desired state.

aspdotnet-core-health-checks-2.jpg

Let's get to a point we can see something visually and start extending our solution on top of that. To be able to use the dashboard, it needs to have a backing store. We need to bring in a package called AspNetCore.HealthChecks.UI.InMemory.Storage

dotnet add package AspNetCore.HealthChecks.UI.InMemory.Storage
Enter fullscreen mode Exit fullscreen mode

Letā€™s change the code in our Startup/ConfigureServices class. Replace the previous code with the following.

// ..
services.AddHealthChecksUI()
        .AddInMemoryStorage();
// ..
Enter fullscreen mode Exit fullscreen mode

Next, letā€™s map a new endpoint to be able to see the dashboard.

// ...
app.UseEndpoints(endpoints =>
{
    endpoints.MapControllerRoute(
        name: "default",
        pattern: "{controller=Home}/{action=Index}/{id?}");

    endpoints.MapHealthChecksUI();
});

//...
Enter fullscreen mode Exit fullscreen mode

Now we are ready to run the application and see what we get.

dotnet new webapi -n Service1
dotnet new webapi -n Service2
dotnet sln add Service1 Service2
Enter fullscreen mode Exit fullscreen mode

We need to add the health checks middleware to the two projects we created in the above step.

Startup.cs

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

app.UseEndpoints(endpoints =>
{
    // ...
    endpoints.MapHealthChecks("/health", new HealthCheckOptions()
    {
        ResponseWriter = UIResponseWriter.WriteHealthCheckUIResponse
    });
   // ...
});
Enter fullscreen mode Exit fullscreen mode

Just copy and add the following line to the .csproj files of the two services we created.

<PackageReference Include="AspNetCore.HealthChecks.UI" Version="5.0.1" />
<PackageReference Include="AspNetCore.HealthChecks.UI.Client" Version="5.0.1" />
Enter fullscreen mode Exit fullscreen mode

Finally, you need to add the endpoints of the services to our Monitor project. Letā€™s add the following bit to the appsettings.json file of the Monitor project. Head over to the official docs to learn more about the configuration.

"HealthChecksUI": {
  "HealthChecks": [
    {
      "Name": "Service 1",
      "Uri": "https://localhost:5011/health"
    },
    {
      "Name": "Service 2",
      "Uri": "https://localhost:5021/health"
    }
  ],
  "EvaluationTimeInSeconds": 10
},
Enter fullscreen mode Exit fullscreen mode

šŸ’” Donā€™t forget to take the port numbers your servers are running on from the corresponding launchSettings.json files. This could change depending on whether you are using IIS Express or Kestrel for development.

If you fire up the 3 projects now, you will be able to see the health checks dashboard.

The default URI for the dashboard UI is https://localhost:<app port>/healthchecks-ui

aspdotnet-core-health-checks-3.png

Under the hood, this small SPA dashboard polls the /healthchecks-api URI which returns a JSON.

Registering health checks

Without bringing in any other dependencies letā€™s simulate the 3 states that it will report to us:

Head over here to see a complete list of supported health checks. Letā€™s add some dummy health checks to see them in action in the dashboard.

We will replace the code in Service1/Startup.cs with the following.

services.AddHealthChecks()
    .AddCheck("Foo", () =>
        HealthCheckResult.Healthy("Foo is OK!"), tags: new[] { "foo_tag" })
    .AddCheck("Bar", () =>
        HealthCheckResult.Unhealthy("Bar is unhealthy!"), tags: new[] { "bar_tag" })
    .AddCheck("Baz", () =>
        HealthCheckResult.Degraded("Baz is degraded!"), tags: new[] { "baz_tag" });
Enter fullscreen mode Exit fullscreen mode

aspdotnet-core-health-checks-4.png

n my previous blog post, I showed you how we could easily integrate with the Redis. I will be using AspNetCore.HealthChecks.Redis package to configure the health checks.

dotnet add package AspNetCore.HealthChecks.Redis
Enter fullscreen mode Exit fullscreen mode

We will also add this configuration bit to its appsettings.json file.

"Redis": {
  "ConnectionString": "localhost:5002" 
},
Enter fullscreen mode Exit fullscreen mode

Letā€™s spin up a Redis docker container on port 5002.

docker run --name redis-cache -p 5002:6379 -d redis
Enter fullscreen mode Exit fullscreen mode

Dashboard:

aspdotnet-core-health-checks-5.png

Feel free to stop the docker container and see how the errors get displayed in the dashboard.

aspdotnet-core-health-checks-6.png

Adding custom health checks

Now letā€™s switch to our Service2 project and do something interesting with it as well. We are going to be adding our custom health check.

public class RemoteHealthCheck : IHealthCheck
{
    public Task<HealthCheckResult> CheckHealthAsync(HealthCheckContext context, CancellationToken cancellationToken = new CancellationToken())
    {
        var isHealthy = CheckRemoteEndpointHealth();

        return Task.FromResult(isHealthy ? 
            HealthCheckResult.Healthy("Remote endpoint is healthy.") :
            HealthCheckResult.Unhealthy("Remote endpoint is unhealthy"));
    }

    private bool CheckRemoteEndpointHealth()
    {
        // Just stubbing it out for demo
        var rnd = new Random().Next(1, 5);
        return rnd % 2 != 0;
    }
}
Enter fullscreen mode Exit fullscreen mode

We can now register RemoteHealthCheck in the Startup class like so.

services.AddHealthChecks()
        .AddCheck<RemoteHealthCheck>(nameof(RemoteHealthCheck));
Enter fullscreen mode Exit fullscreen mode

Now the dashboard will show you the status of both services.

aspdotnet-core-health-check-7.png

Youā€™ll see that the RemoteHealthCheck will be going down from time to time because we have set it to return an unhealthy randomly. You can also set the EvaluationTimeInSeconds setting to like 2s to see the result quickly.

aspdotnet-core-health-checks-8.png

Conclusion

Today we looked at how you can improve your developer experience by leveraging ASP.NETā€™s health checks. There are so many providers and configurations to play around with. Feel free to head over to the docs to learn more.

References

  1. https://github.com/Xabaril/AspNetCore.Diagnostics.HealthChecks
  2. https://docs.microsoft.com/en-us/aspnet/core/host-and-deploy/health-checks?view=aspnetcore-5.0

Top comments (5)

Collapse
 
shaijut profile image
Shaiju T

Nice one šŸ˜„, Suppose I host all 3 applications health check dashboard, service 1 and service 2 in same IIS server , and for some reason my IIS server is down. In this case i cant get to know the health status. Any solution ?

Collapse
 
sahan profile image
Sahan

Thanks for your comment :)

That's a different use case than that's served by ASP.NET's health checks. ASP.NET Health checks can be used at an application-level monitoring than infrastructure. If your infrastructure is down, you will know when you hit it. In which case, you can do an HTTP monitoring (which pings your server and returns a result - much like you hitting a remote endpoint and seeing that server is down etc.).

The solution really depends on your infrastructure setup. Some options I could think of off the top of my head.

  • In local env, check IIS logs in inetmgr to figure out what's going on. Or you can take out the Monitor project and host in Kerstel rather than running IIS. That way, if your services are unreachable, you'll know that IIS is also down.

  • If in a hosted env, like Azure

    • If using a load balancer, you could set up health checks to probe if it can ping
    • If using VMs, you can do the health checks with VMSS
    • Using Azure Monitor and Log Analytics

As I mentioned in the article, I would prefer to use ASP.NET Health Checks in the local development environment to get an idea of how the services are running quickly.

Collapse
 
shaijut profile image
Shaiju T

Thanks for detailed explanation. So what is the use case of health checks in Asp.Net Core ? Just to check status between services hosted in a single IIS server ?

Thread Thread
 
gustavobmichel profile image
Gustavo Borges Michel

You can use to check if your app can connect to a database as well. Plus you can write custom checks for other things such as external apis for example. I think it's a very easy way to check critical things in your app are working OK.

Thread Thread
 
sahan profile image
Sahan • Edited

@shaijut It doesn't necessarily have to be on IIS. They can be distributed across many hosting servers, PaaS offerings like App Service etc. Better to think of it in the context of running many applications rather than infrastructure. As @gustavobmichel already mentioned you can monitor the status of many different services depending on your use case.

Check out this full list of health checks available:

github.com/Xabaril/AspNetCore.Diag...

Hope this helps to clarify it.