Health Monitoring In ASP.NET Core

The dream of every software engineer is to write a code in such a way that there won’t be any defects and none of the infrastructure will ever go down. But, that is not the case in the real world and with the Microservices architecture it has become even more difficult to identify the state of the container.
 
In fact, we need a mechanism in place to quickly identify and fix the issue at the earliest unless it turns out to be a bigger problem. This is where Health Monitoring comes into picture.
 
Health Monitoring in ASP.NET Core allows you to get near real-time state of the container. These monitoring mechanisms are handy when your application is dealing with components such as database, cache, url, message broker etc.
 

Implementing basic health monitoring

 
When developing ASP.NET Core Microservices, you can use a built-in health monitoring feature by using a nuget package Microsoft.Extension.Diagnostic.HealthCheck. These health monitoring features can be enabled by using a set of services and middleware.
  1. public void ConfigureServices    
  2.        (IServiceCollection services)    
  3. {    
  4.    services.AddControllers();    
  5.    services.AddHealthChecks();    
  6. }     
  7. public void Configure(IApplicationBuilder app,    
  8. IWebHostEnvironment env)    
  9. {    
  10.    if (env.IsDevelopment())    
  11.    {    
  12.       app.UseDeveloperExceptionPage();    
  13.    }    
  14.       app.UseHttpsRedirection();    
  15.       app.UseRouting();    
  16.       app.UseAuthorization();    
  17.       app.UseEndpoints(endpoints =>    
  18.       {    
  19.         endpoints.MapControllers();    
  20.         endpoints.MapHealthChecks("/api/health");    
  21.       }    
  22. }   
When you run the application, you will see the output as Healthy
 
Health Monitoring In ASP.NET Core
For two lines of code, not too bad. However, we can do much better.
 

Returning status in JSON format

 
By default, the output of the health monitoring is in “plain/text”. Therefore, we can see the health status as Healthy or UnHealthy. In order to see the detailed output with all the dependencies, the application has to be customized with “ResponseWriter” property which is available in AspNetCore.HealthChecks.UI.Client
 
Firstly, add the nuget package
  1. dotnet add package AspNetCore.HealthChecks.UI    
  2. dotnet add package AspNetCore.HealthChecks.UI.Client    
Now, let’s configure the application
  1. endpoints.MapHealthChecks("/api/health",     
  2. new HealthCheckOptions()    
  3.  {    
  4.     Predicate = _ => true,    
  5.     ResponseWriter = UIResponseWriter.     
  6.                 WriteHealthCheckUIResponse    
  7.  });   
Now, run the application and you will see the output in json format
  1. {    
  2.   "status""Healthy",    
  3.   "totalDuration""00:00:00.0038176"    
  4. }  

Health Status for URI’s

 
You can easily verify the status of the endpoints/uri’s by using nuget package
  1. dotnet add package AspNetCore.HealthChecks.uris    
Now, let's modify our code to accommodate the uri’s
  1. public void ConfigureServices    
  2.      (IServiceCollection services)    
  3. {    
  4.      
  5.    services.AddControllers();    
  6.    services.AddHealthChecks()    
  7.      .AddUrlGroup(new Uri    
  8.             ("https://localhost:5001/weatherforecast"),    
  9.              name: "base URL", failureStatus:     
  10.              HealthStatus.Degraded)    
  11. }   
You need to use AddUrlGroup method to verify the uri’s and in case of failure, the status of the url will be displayed as Degraded.
 
Now, run the application and the output will look similar.
  1. {    
  2.   "status""Healthy",    
  3.   "totalDuration""00:00:00.1039166",    
  4.   "entries": {    
  5.     "base URL": {    
  6.       "data": {},    
  7.       "duration""00:00:00.0904980",    
  8.       "status""Healthy",    
  9.       "tags": []    
  10.     }    
  11. }   

Health Status for SQL Server

 
In order to verify the status of SQL Server database, I did database installation in docker; however, you can use local instance of database server.
 
You can install SQL Server in docker using below commands
  1. //Docker pull command to install    
  2. docker pull mcr.microsoft.com/mssql/server    
  3.      
  4. //Docker Run command     
  5. docker run --privileged -e 'ACCEPT_EULA=Y'     
  6. -e 'SA_PASSWORD=Winter2019' -p 1433:1433     
  7. --name=MSSQL -d     
  8. mcr.microsoft.com/mssql/server:latest    
 Once the database is up and running, add the below nuget package.
  1. dotnet add package AspNetCore.HealthChecks.SqlServer    
  2. public void ConfigureServices    
  3.  (IServiceCollection services)    
  4.         {    
  5.      
  6.             services.AddControllers();    
  7.             services.AddHealthChecks()    
  8.                 .AddUrlGroup(new Uri("https://localhost:5001/weatherforecast"), name: "base URL", failureStatus: HealthStatus.Degraded)              .AddSqlServer(Configuration.GetConnectionString("DefaultConnection"),    
  9.                 healthQuery: "select 1",    
  10.                 failureStatus: HealthStatus.Degraded,    
  11.                 name: "SQL Server");    
  12.         }  
Note
In the HealthQuery, don’t use any fancy queries to verify the Database connection. The main purpose of using “Select 1” is that it takes less execution time.
 
Now run the application and your output will look similiar.
  1. {    
  2.   "status""Healthy",    
  3.   "totalDuration""00:00:00.1039166",    
  4.   "entries": {    
  5.     "base URL": {    
  6.       "data": {},    
  7.       "duration""00:00:00.0904980",    
  8.       "status""Healthy",    
  9.       "tags": []    
  10.     },    
  11.     "SQL Server": {    
  12.       "data": {},    
  13.       "duration""00:00:00.0517363",    
  14.       "status""Healthy",    
  15.       "tags": []    
  16.     }    
  17.   }    
  18. }   

Custom Health Check

 
Custom Health Check can be easily implemented by using IHealthCheck interface.
  1. public class TodoHealthCheck : IHealthCheck    
  2.     {    
  3.         public Task<HealthCheckResult> CheckHealthAsync(HealthCheckContext context, CancellationToken cancellationToken = default)    
  4.         {    
  5.             //Implement you logic here    
  6.             var healthy = true;    
  7.             if (healthy)    
  8.                 return Task.FromResult(HealthCheckResult.Healthy());    
  9.             return Task.FromResult(HealthCheckResult.Unhealthy());    
  10.         }    
  11.     }    
The AddCheck method in Configure services is used to add health check with the specified name.
  1. public void ConfigureServices(IServiceCollection services)    
  2.        {    
  3.            services.AddControllers();    
  4.            services.AddHealthChecks()    
  5.                .AddUrlGroup(new Uri("https://localhost:5001/weatherforecast"), name: "base URL", failureStatus: HealthStatus.Degraded)    
  6.                .AddSqlServer(Configuration.GetConnectionString("DefaultConnection"),    
  7.                healthQuery: "select 1",    
  8.                failureStatus: HealthStatus.Degraded,    
  9.                name: "SQL Server")    
  10.                .AddCheck<TodoHealthCheck>("Todo Health Check",failureStatus:HealthStatus.Unhealthy);    
  11.        }  
Now, run the application
  1. {  
  2.     "status""Healthy",  
  3.     "totalDuration""00:00:00.0544065",  
  4.     "entries": {  
  5.         "base URL": {  
  6.             "data": {},  
  7.             "duration""00:00:00.0527285",  
  8.             "status""Healthy",  
  9.             "tags": []  
  10.         },  
  11.         "SQL Server": {  
  12.             "data": {},  
  13.             "duration""00:00:00.0386450",  
  14.             "status""Healthy",  
  15.             "tags": []  
  16.         },  
  17.         "Todo Health Check": {  
  18.             "data": {},  
  19.             "duration""00:00:00.0001681",  
  20.             "status""Healthy",  
  21.             "tags": []  
  22.         }  
  23.     }  
  24. }  
Let’s visualize.
 
Display the output in the JSON format looks reasonable; however, visualizing the UI makes more sense and can be easily understandable for non-technical background people as well.
 
Add nuget package.
  1. dotnet add package AspNetCore.HealthChecks.UI.InMemory.Storage    
To visualize the UI health check, you need to amend changes in services and middleware.
  1. public void ConfigureServices(IServiceCollection services)    
  2.         {    
  3.      
  4.             services.AddControllers();    
  5.             services.AddHealthChecks()    
  6.                 .AddUrlGroup(new Uri("https://localhost:5001/weatherforecast"), name: "base URL", failureStatus: HealthStatus.Degraded)    
  7.                 .AddSqlServer(Configuration.GetConnectionString("DefaultConnection"),    
  8.                 healthQuery: "select 1",    
  9.                 failureStatus: HealthStatus.Degraded,    
  10.                 name: "SQL Server")    
  11.                 .AddCheck<TodoHealthCheck>("Todo Health Check",failureStatus:HealthStatus.Unhealthy);    
  12.      
  13.             services.AddHealthChecksUI(opt =>    
  14.             {    
  15.                 opt.SetEvaluationTimeInSeconds(10); //time in seconds between check    
  16.                 opt.MaximumHistoryEntriesPerEndpoint(60); //maximum history of checks    
  17.                 opt.SetApiMaxActiveRequests(1); //api requests concurrency    
  18.                 opt.AddHealthCheckEndpoint("default api""/api/health"); //map health check api    
  19.             })    
  20.             .AddInMemoryStorage();    
  21.         }   
The Health Check UI endpoint comes by default as “/healthchecks-ui“. You can change this value by customizing it through the MapHealthCheckUI method.
 
In the code, I have set the polling interval as 10 seconds. It checks whether all the endpoints/databases etc within the application are working as expected.
 
Now run the application.
 
Health Monitoring In ASP.NET Core
 
Now, let’s stop the SQL Server from Docker container and verify the output
  1. //Get Container ID    
  2. docker ps    
  3.      
  4. //Stop Docker container for SQL Server    
  5. docker stop <Container Id here>   
Health Monitoring In ASP.NET Core
 
Other Health checksFeatures.
 
Health Monitoring is not limited to SQL Server and URI’s. However, it supports a large number of features and details can be found here 
 

Conclusion

 
In Microservices or container based architecture, it’s a must to include to health monitoring, and without this feature, life will be miserable. With this health monitoring feature, you and your team can act upon the health of your services in a robust and standardized way.
 
Asp.Net Core team has added top notch support for health checks, and made it effortless for developers to build and customize.
 
Cheers!
 
I hope you like the article. In case you found the article as interesting then kindly like and share it.