Adding Health Checks to .Net Core Application
Tags: csharp, dotnet, devops, architecture
Published: 2020-09-20 02:15:19

In this series of posts, we will talk about health checks and monitor your web application/web API.

Also, check out my blog with other articles --> [https://rmauro.dev](https://rmauro.dev)

In this blog post, we'll add an endpoint to monitor your application **health status**. 

And you'll see that it's pretty simple in .Net Core application. With just a few lines of code, we have the infrastructure ready to display the health status of our services.

We can monitor services such as:

- Database (SQL Server, Oracle, MySql, MongoDB and others)
- External API connectivity
- Disk connectivity (read/write)
- Cache service (Redis, Memcache, and others)

Basically anything you can think of. If you don't find an implementation that suits you, we can create our custom implementation.

### Health Endpoint Monitoring pattern

But. Why? 

>Implement functional checks in an application that external tools can access through exposed endpoints at regular intervals. This can help to verify that applications and services are performing correctly.

Reference to: https://docs.microsoft.com/en-us/azure/architecture/patterns/health-endpoint-monitoring

### Adding basic health check

First, add on **ConfigureServices** method the *HealthCheck* service.

```csharp
public void ConfigureServices(IServiceCollection services)
{
    //adding health check services to container
    services.AddHealthChecks();
}
```

> `Startup.cs`

Second, add on **Configure** pipeline the *healthcheck* endpoint.

```csharp
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
    if (env.IsDevelopment())
    {
        app.UseDeveloperExceptionPage();
    }

    //adding health check endpoint
    app.UseHealthChecks("/healthcheck");

    app.UseMvcWithDefaultRoute();
}
```

> `Startup.cs` for .Net Core 2.2

```csharp
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
	if (env.IsDevelopment())
	{
		app.UseDeveloperExceptionPage();
	}

	app.UseRouting();

	app.UseEndpoints(endpoints =>
	{
		endpoints.MapHealthChecks("/healthcheck");
		endpoints.MapDefaultControllerRoute();
	});
}
```

> `Startup.cs` for .Net Core 3.1

Always put before the **UseMvc**. We don't want to MVC pipeline executed before our endpoint.

Build, run, and access the URL `http://{YOUR-URL}/healthcheck` you should see something like this.

![basic-health-check](https://res-4.cloudinary.com/ht54oxr6q/image/upload/q_auto/v1/ghost-blog-images/basic-health-check.png)

All set and done to start adding services to the monitoring list. Now let's add one service to the monitor list - **MongoDb**.

#### Adding MongoDb as a monitor service

Let's pick from xabaril's list. xabaril's Github repository contains a bunch of AspNetCore.Diagnostics.HealthChecks packages ready for use.

- AspNetCore.HealthChecks.System
- AspNetCore.HealthChecks.Network
- AspNetCore.HealthChecks.SqlServer
- AspNetCore.HealthChecks.MongoDb
- AspNetCore.HealthChecks.Npgsql
- AspNetCore.HealthChecks.Elasticsearch
- AspNetCore.HealthChecks.Redis
- AspNetCore.HealthChecks.EventStore
- AspNetCore.HealthChecks.AzureStorage
- AspNetCore.HealthChecks.AzureServiceBus
- AspNetCore.HealthChecks.AzureKeyVault
- AspNetCore.HealthChecks.MySql

> https://github.com/xabaril/AspNetCore.Diagnostics.HealthChecks

And many other packages.

###### Back to business.

First, add the Nuget package `AspNetCore.HealthChecks.MongoDb` to your project.

And them modify the AddHealthChecks to include the MongoDB health check.

```csharp
public void ConfigureServices(IServiceCollection services)
{
    //adding health check services to container
    services.AddHealthChecks()
        .AddMongoDb(mongodbConnectionString: "YOUR-CONNECTION-STRING",
        name: "mongo", 
        failureStatus: HealthStatus.Unhealthy); //adding MongoDb Health Check
}
```

> `Startup.cs`

After these changes, it's all set up. You can execute your application and check again the health status page.

![basic-health-check](https://res-4.cloudinary.com/ht54oxr6q/image/upload/q_auto/v1/ghost-blog-images/basic-health-check.png)

#### Adding another endpoint with more details
Now let's create another endpoint that will display more detailed information about our monitored endpoints.

```csharp
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
    //... ommited code

    //adding health check endpoint
    app.UseHealthChecks("/healthcheck");

    //adding custom json response
    app.UseHealthChecks("/hc",
       new HealthCheckOptions
       {
           ResponseWriter = async (context, report) =>
           {
               var result = JsonConvert.SerializeObject(
                   new
                   {
                       status = report.Status.ToString(),
                       errors = report.Entries.Select(e => new { key = e.Key, value = Enum.GetName(typeof(HealthStatus), e.Value.Status) })
                   });
               context.Response.ContentType = MediaTypeNames.Application.Json;
               await context.Response.WriteAsync(result);
           }
       });

    app.UseMvcWithDefaultRoute();
}
```

> `Startup.cs` for .Net 2.2

```csharp
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
	//... ommited code

	app.UseEndpoints(endpoints =>
	{
		endpoints.MapHealthChecks("/healthcheck");
		endpoints.MapHealthChecks("/hc",
		   new HealthCheckOptions
		   {
			   ResponseWriter = async (context, report) =>
			   {   //using the new json serializer
				   var result = JsonSerializer.Serialize(
					   new
					   {
						   status = report.Status.ToString(),
						   errors = report.Entries.Select(e => new { key = e.Key, value = Enum.GetName(typeof(HealthStatus), e.Value.Status) })
					   });

				   context.Response.ContentType = MediaTypeNames.Application.Json;

				   await context.Response.WriteAsync(result);
			   }
		   });

		endpoints.MapGet("/", async context => await context.Response.WriteAsync("Hello World!"));
		endpoints.MapDefaultControllerRoute();
	});
}
```

> `Startup.cs` for .Net 3.1

Run again and access the endpoints `/hc`.

![custom-health-check](https://res-3.cloudinary.com/ht54oxr6q/image/upload/q_auto/v1/ghost-blog-images/custom-health-check.png)

That is it!

In the next article, I'll show you how to put a good UI (user interface) to it.

#### Source Code At - branch Article-1
- https://github.com/ricardodemauro/Health-Check-Series

#### Originally posted at
- https://rmauro.dev/adding-health-checks-to-net-core-application/

#### Reference Articles
- https://docs.microsoft.com/en-us/azure/architecture/patterns/health-endpoint-monitoring

- https://www.hanselman.com/blog/HowToSetUpASPNETCore22HealthChecksWithBeatPulsesAspNetCoreDiagnosticsHealthChecks.aspx

- https://github.com/xabaril/AspNetCore.Diagnostics.HealthChecks