Rebin

Rebin

Software Developer focusing on Microsoft development technologies

08 Oct 2021

Implement Health Checks Middleware in ASP.NET Core

“Implement Health Checks Middleware in ASP.NET Core” Photo by Sigmund

Introduction

ASP.NET Core provides an appropriate Middleware which enables you to make aware of the state and health of your applications, API endpoints, containers or microservice components which are running on a production environment and working properly,so this feature in ASP.NET Core is telling us about of availability our applications and its normal functionalities. This article will explain how to configure the (Health Checks Middleware) for monitoring the health of your application.

In this article we are going to create a web app is called (UIHealthChecksApp) that is used for monitoring the health of (WebApp_1, WebApp_2) webapps.

Step 1

Run the following command in Visual Studio Code terminal.



dotnet new sln -n HealthChecksSolution

dotnet new mvc -n UIHealthChecksApp

dotnet sln add HealthcheckerApp

dotnet new mvc -n WebApp_1

dotnet new mvc -n WebApp_2
  
dotnet sln add WebApp_1 WebApp_2


Step 2

On the UIHealthChecksApp we need to install (AspNetCore.HealthChecks.UI) dependency package to display the health checks result.

dotnet add package AspNetCore.HealthChecks.UI --version 5.0.1

also we install another one dependency package is called (AspNetCore.HealthChecks.UI.InMemory.Storage) by using this package it enables us to store all events history in-memory database which they will occur on other web apps.

dotnet add package AspNetCore.HealthChecks.UI.InMemory.Storage --version 5.0.1

Step 3

In this step to enable the UI health checks we should register two services of (UI Health Check) to the Dependency Injection Container in (startup.cs) under theConfigureService method.

services.AddHealthChecksUI() 
        .AddInMemoryStorage();

Step 4

The Health Checks UI-Webapp needs a separate endpoint to display the results so we add another endpoint to the (UseEndpoints) middleware pipeline in the Configure method (startup.cs).


 endpoints.MapHealthChecksUI();

At the end the Map.HealthCheckUI() will generate healthcheck-ui url to access the results.

You can also change the (healthcheck-ui) URL to another name like this one


MapHealthChecksUI(u => u.UIPath = "/myfavorite-ui")

Run the (UIHealthChecksApp) to ensure everything goes well so far it should be has the following result.

https://localhost:5001/myfavorite-ui

“Health Checks UI Middleware in ASP.NET Core”

Step 5

Register the Health Checks services for each (WebApp_1, WebApp_2) in this article we’re going to use (Sqlserver connection,Memory and Uri) Health Checks you can find a bunch of dependency packages through this great repository

Add the below dependency packages to enable the health checks on (WebApp_1, WebApp_2)

dotnet add package AspNetCore.HealthChecks.SqlServer --version 5.0.1

dotnet add package AspNetCore.HealthChecks.System --version 5.0.1

dotnet add package AspNetCore.HealthChecks.Uris --version 5.0.1

We have to register the Health Checks services to the Dependency Injection (DI) Container in the ConfigureService method for each (WebApp_1, WebApp_2) (startup.cs)

public void ConfigureServices(IServiceCollection services)
        {
              //Register HealthChecks for sqlserver Connection
              services.AddHealthChecks().AddSqlServer(
              connectionString: Configuration["ConnectionStrings:DefaultConnection"],
              healthQuery: "SELECT 1;",
              name: "Database WebApp1",
              tags: new string[] { "db", "sql", "sqlserver" });

             //Register HealthChecks for total memory alocated
             services.AddHealthChecks()
                .AddProcessAllocatedMemoryHealthCheck(1024, name: "Memory", tags: new string[] { "memory" });

            //Register HealthChecks for an external endpoint
            services.AddHealthChecks().AddUrlGroup(new Uri("https://localhost:3001/weatherforecast"), "Api endpoint",tags: new string[] { "endpoint","api","uri"});
        }

You can create basic Health Checks by using built-in Health Checks Middleware without using any other dependencies just like the following example. remember configure basic Health Checks are not suitable just they like a way to telling us how the Health Checks are working.

services.AddHealthChecks()

        .AddCheck("Database WebApp1", () => HealthCheckResult.Healthy("Database WebApp1 is working Good"), tags: new[] { "db", "sql", "sqlserver" })
    
        .AddCheck("Memory", () => HealthCheckResult.Unhealthy("Memory is unhealthy!"), tags: new[] { "Memory" })
    
        .AddCheck("Api endpoint", () => HealthCheckResult.Degraded("Api endpoint is very slow or unstable."), tags: new[] { "endpoint", "api", "uri" });

tags parameters are optional they are using to filtering our Health Checks by (Predicate) property in the HealthCheckOptions class if we want to display the result of specific Health Checks.

In asp.net core you can create your own custom Health Checks by implementing the IHealthCheck interface then it should be registered in the services Dependency Injection (DI) Container.

public class CustomHealthCheck : IHealthCheck
    {
        private string url;
        public CustomHealthCheck(string apiUrl)
        {
            url = apiUrl;
        }
        public async Task<HealthCheckResult> CheckHealthAsync(HealthCheckContext context, CancellationToken cancellationToken = default)
        {
            using (var client = new HttpClient())
            {
                client.BaseAddress = new Uri(url);
                client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
                HttpResponseMessage response = await client.GetAsync("/random");
                if (response.IsSuccessStatusCode)
                {
                    var responseContet = await response.Content.ReadAsStringAsync();
                    return await Task.FromResult(HealthCheckResult.Healthy($"A healthy result : {responseContet}"));
                }
                else
                {
                    return await Task.FromResult(new HealthCheckResult(context.Registration.FailureStatus, 
                        $"An unhealthy result, StatusCode : {(int)response.StatusCode}  Reason : {response.ReasonPhrase}"));
                }
            }
           
        }
    }
services.AddHealthChecks()
        .AddCheck(name: "CustomHealthChecks",new CustomHealthCheck("https://api.quotable.io"),
           tags: new string[] { "customEndpoint", "api", "quot" });

Step 6

Adding Health Checks endpoint in (app.UseEndpoints) for each (WebApp_1, WebApp_2) they need to have separate endpoint to get the Health Checks result in the Configure method (startup.cs)

WebApp_1

 endpoints.MapHealthChecks("/webapp1_health", new HealthCheckOptions()
        {
             Predicate = _ => true,
             ResponseWriter = UIResponseWriter.WriteHealthCheckUIResponse,
                      
         });

WebApp_2


 endpoints.MapHealthChecks("/webapp2_health", new HealthCheckOptions()
         {
             Predicate = _ => true,
             ResponseWriter = UIResponseWriter.WriteHealthCheckUIResponse,
                      
        });

You can filter the Health Checks endpoints to return the result of the Health Checks that has only memory or api tags as already mentioned in step 5


Predicate = (hc) => hc.Tags.Contains("memory") || hc.Tags.Contains("api")

Now we have two new endpoints for each (WebApp_1, WebApp_2) that return their health checks result as json format.

https://localhost:1001/webapp1_health

https://localhost:1001/webapp2_health

Step 7

Display Health Checks results as UI friendly to display Health Checks results of the (WebApp_1, WebApp_2) they should be configured over the (UIHealthChecksApp)

There are two ways to configure it by using AddHealthChecksUI(setupSettings: setup =>) method or through the appsettings.json file in (UIHealthChecksApp) webapp.

 services.AddHealthChecksUI(setupSettings: setup =>
        {
         setup.AddHealthCheckEndpoint("WebApp_1_endpoint", "https://localhost:1001/webapp1_health");
         setup.AddHealthCheckEndpoint("WebApp_2_endpoint", "https://localhost:2001/webapp2_health");
         setup.SetEvaluationTimeInSeconds(10);
         setup.SetMinimumSecondsBetweenFailureNotifications(60);
        });

appsettings.json

"HealthChecksUI": {
    "HealthChecks": [
      {
        "Name": "WebApp_1",
        "Uri": "https://localhost:1001/webapp1_health"
      },
      {
        "Name": "WebApp_2",
        "Uri": "https://localhost:2001/webapp2_health"
      }

    ],
    "EvaluationTimeInSeconds": 10,
    "MinimumSecondsBetweenFailureNotifications": 60
  }

EvaluationTimeInSeconds:10 This setting is enables our UI Health Checker app to capture updates every 10 secounds.

MinimumSecondsBetweenFailureNotifications: 60 This setting is used to prevent receiver flooding.

To see entire result of the Health Checks so far we can run the following url.

 https://localhost:5001/myfavorite-ui

“Result Health Checks UI in ASP.NET Core”

The source code of the project can be found on this GitHub repository.

Categories

Series