Adding HealthChecks to your application can drastically reduce troubleshooting time

Why would you want to monitor your applications health?

  • Health checks can test an applications dependencies, such as databases and external service endpoints, to confirm availability and normal functioning. 

From Microsoft

Out of the box Health Checks UI

Here is how to monitor SQL Server tables & Custom Services via HealthChecks in Asp.net Core .NET 7

Add the following Health Check nuget packages to your project solution:

  • AspNetCore.HealthChecks.UI
  • AspNetCore.HealthChecks.UI.Client
  • AspNetCore.HealthChecks.UI.Core
  • AspNetCore.HealthChecks.SqlServer
  • AspNetCore.HealthChecks.UI.InMemory.Storage

Create a new class named SampleHealthCheck.cs in a new Folder called 'HealthChecks' in your solution

SampleHealthCheck.cs
				
					public class SampleHealthCheck : IHealthCheck
    {
        IService _service;
        public SampleHealthCheck(IService service)
        {
            _service = service;
        }
        public async Task<HealthCheckResult> CheckHealthAsync(HealthCheckContext context, CancellationToken cancellationToken = default)
        {
            var timer = new Stopwatch();
            timer.Start();
            await _service.GetSampleDataAsync();
            timer.Stop();
            var timeTaken = timer.Elapsed.TotalMilliseconds;
            
            if (timeTaken < 2000)
                return HealthCheckResult.Healthy($"Response Time: {timeTaken}");
            else if (timeTaken < 3000)
                return HealthCheckResult.Degraded($"Response Time: {timeTaken}");
            else
                return HealthCheckResult.Unhealthy($"Response Time: {timeTaken}");
        }
    }
				
			

This new class injects a repository for sample purposes. You can imagine this service responsible for fetching data from an external API or a database. From there we execute a method and measure the time is takes to return a result. You can refine the timeTaken double to the appropriate value depending on the expected execution speed of your method. The HealthCheckResult struct determines the health of the application. Note, if any of your health checks return ‘Degraded’, all your health checks will be considered ‘Degraded’, the same goes for ‘Unhealthy’.

Create a new class named CustomHealthChecks.cs in the application root with the following:

This is our own custom service collection we will later call in Program.cs.

.AddSqlServer executes a simple SELECT query on a table

.AddCheck<SampleHealthCheck> wires up our class we created in the previous step.

This makes these two health checks available and queryable.

CustomHealthChecks.cs
				
					public static class CustomHealthChecks
    {
        public static IServiceCollection AddCustomHealthChecks(this IServiceCollection services, WebApplicationBuilder builder)
        {
            builder.Services
                    .AddHealthChecks()
                    //Query MSSQL table for health check
                    .AddSqlServer(
                        connectionString: builder.Configuration.GetConnectionString("ConnString_Live"),
                        healthQuery: "Select top 1000 * from user_table order by createdt desc",
                        name: "User Table Check"
                        )
                    //Test a repository method for health check
                    .AddCheck<SampleHealthCheck>("Sample Service Check");

            if (builder.Environment.IsDevelopment())
            {
                builder.Services
                        .AddHealthChecksUI(setup => {
                            setup.AddHealthCheckEndpoint("Health Checks Local", "https://localhost:7083/health");
                        })
                        .AddInMemoryStorage();
            }
            else
            {
                builder.Services
                        .AddHealthChecksUI(setup => 
                        { 
                            setup.AddHealthCheckEndpoint("Health Checks", "https://webservice.[publishedapplicationuri].com:4438/health"); 
                        })
                        .AddInMemoryStorage();
            }

            return services;
        }
    }
				
			
Program.cs

Here is where everything comes together.

We add our IService repository so it’s available for Dependency Injection and wire up our service collection: AddCustomHealthChecks.

				
					using HealthChecks.UI.Client;

IConfiguration Configuration;

var builder = WebApplication.CreateBuilder(args);
Configuration = builder.Configuration;

builder.Services.AddSingleton<IService, Service>();
builder.Services.AddCustomHealthChecks(builder);

var app = builder.Build();

app.MapHealthChecks("/health", new HealthCheckOptions
{
    ResponseWriter = UIResponseWriter.WriteHealthCheckUIResponse
});

//Style you HealthChecks UI with this file
app.MapHealthChecksUI(setup =>
{
    setup.AddCustomStylesheet("wwwroot/css/HealthChecks.css");
});

app.Run();
				
			

From there you should be able to query two endpoints for health checks

https://localhost:4438/health

This end point will return the health checks in JSON format. You can query this endpoint to create custom components on your application to display your application health.

https://localhost:4438/healthchecks-ui#/healthchecks

This will load the built in Health Checks UI

Leave a Reply

Your email address will not be published. Required fields are marked *

Post comment

Want to learn more?

Service Collections with Blazor Server

What is a service collection? Service collections are a way of registering and resolving dependencies in C# applications using the built-in dependency injection (DI) system. Blazor Server is a web framework that allows you to run C# code on the server and interact with the user interface through a SignalR connection. To use service collections…

Read more
IHttpClientFactory vs. HttpClient

Why should you use IHttpClientFactory The main reason to use IHttpClientFactoryinstead of HttpClient directly is to manage the life cycle of HttpClient instances. HttpClient was designed to be instantiated once and reused throughout the application life cycle. If you continually instantiate new HttpClients you may unnecessarily be squandering resources. Another use case is for simplicities…

Read more