Implementing Health Check for your application

Implementing Health Check for your application

Here we are going to see about the health check for an application, especially why it is required and how it is useful.

Typically, in an application, we use various components that involve disk I/O ops, 3rd party API calls, data sync, CRON, etc., and the most prominent one is the database.

Either microservice or a monolithic application, it is critical to monitor the status of all the components and their operations periodically and create alerts based on the status.

The common scenarios to monitor in a typical application as follows:

No alt text provided for this image

How do we implement using .Net Core 3.1?

It is quite easy and straightforward, just add "AddHealthChecks()" in the ConfigureServices method and end-point (/health) in Configure method.

The Microsoft documentation explains in detail.

We usually look for a quick & practical and easy way to start doing it.

Let's jump into the action. Take a look at the /health end-point result as JSON:

The results vary by data on the images. Check out the environment variables under the data object.

No alt text provided for this image

Let's see how the result would be when it is Unhealthy with an exception thrown:

No alt text provided for this image

Let's see how it looks when SQL connection doesn't exist:

No alt text provided for this image

All right, the health reports seem self-explanatory, now jump into making.

Let's create HealthInspector class that implements "IHealthCheck" and must have CheckHealthAsync method.

public class HealthInspector : IHealthCheck
{
	private readonly IConfiguration configuration;




	public HealthInspector(IConfiguration configuration)
	{
		this.configuration = configuration;
	}
	
	public Task<HealthCheckResult> CheckHealthAsync(HealthCheckContext context, CancellationToken cancellationToken = default(CancellationToken))
	{
		return DoHealthCheck();
	}




	private async Task<HealthCheckResult> DoHealthCheck()
	{
		var healthCheckData = new Dictionary<string, object>();




		// Check File IO Ops
		string filePath = @$"C:\Temp\healthcheck.txt";
		try
		{
			var file = File.Create(filePath);
			file.Close();
			healthCheckData.Add("File IO", "Healthy");
		}
		catch (Exception ex)
		{
			healthCheckData.Add("File IO", $"Unhealthy - Err: {ex.Message}");
		}
		finally
		{
			if (File.Exists(filePath))
				File.Delete(filePath);
		}




		// Check 3rd Party APIs
		try
		{
			using (HttpClient client = new HttpClient())
			{
				await client.GetStringAsync("https://www.google.com/");
				healthCheckData.Add("3rd Party API", "Healthy");
			}
		}
		catch (Exception ex)
		{
			healthCheckData.Add("3rd Party API", $"Unhealthy - Err: {ex.Message}");
		}




		// Check the App Settings
		try
		{
			var appSetting = configuration.GetValue<string>("MyAppVariable");
			healthCheckData.Add("App Settings", string.IsNullOrEmpty(appSetting) ? "Unhealthy" : "Healthy");
		}
		catch (Exception ex)
		{
			healthCheckData.Add("App Settings", $"Unhealth - Err: {ex.Message}");
		}




		// Check the Environment variables
		try
		{
			string myAppEnv = Environment.GetEnvironmentVariable("MyApp", EnvironmentVariableTarget.User);
			healthCheckData.Add("Environment Variables", string.IsNullOrEmpty(myAppEnv) ? "Unhealthy" : "Healthy");
		}
		catch (Exception ex)
		{
			healthCheckData.Add("Environment Variables", $"Unhealth - Err: {ex.Message}");
		}
		
		var healthResult = new HealthCheckResult(HealthStatus.Healthy, "Application health", null, healthCheckData);




		return healthResult;
	}
}

Let's quickly discuss this class.

The health inspector checks all the components in a dedicated Try/Catch block and captures the result in a Dictionary<Key, Value> object and that's called HealthCheck Data.

The example just shows some ideas and various checks.

In Startup.cs >> ConfigureServices:

services.AddHealthChecks()
		.AddCheck<HealthInspector>("MyApp Health")
		.AddSqlServer(  @"Server=.\sqlexpress,1433;Database=HealthCheckDB;Integrated Security=False;User Id=sa;Password=YourStrongPassword;MultipleActiveResultSets=True", 
						// Following are the optional parameters
						"Select 1", // Default query that perform on the Server
						"SQL Health", 
						HealthStatus.Unhealthy, 
						null, 
						TimeSpan.FromSeconds(30));

In Configure:

app.UseEndpoints(endpoints =>
{
	endpoints.MapControllers();
	endpoints.MapHealthChecks("/health", new HealthCheckOptions()
	{
		ResponseWriter = WriteResponse
	});
});

"WriteResponse" is a Func delegate which we need to write as follows:

private static Task WriteResponse(HttpContext context, HealthReport result)
{
	context.Response.ContentType = "application/json";


	JObject json;


	if (result.Status == HealthStatus.Healthy)
	{
		if (result.Entries.Count > 0)
		{
			json = new JObject(
					new JProperty("status", result.Status.ToString()),


					new JProperty("results", new JObject(result.Entries.Select(pair =>
						new JProperty(pair.Key, new JObject(
							new JProperty("status", pair.Value.Status.ToString()),
							new JProperty("description", pair.Value.Description),
							new JProperty("data", new JObject(pair.Value.Data.Select(
								p => new JProperty(p.Key, p.Value))))))))));
		}
		else
		{
			json = new JObject(new JProperty("status", result.Status.ToString()));
		}


	}
	else
	{
		json = new JObject(
		new JProperty("status", result.Status.ToString()),


		new JProperty("results", new JObject(result.Entries.Select(pair =>
			new JProperty(pair.Key, new JObject(
				new JProperty("status", pair.Value.Status.ToString()),
				new JProperty("description", pair.Value.Description),
				new JProperty("data", new JObject(pair.Value.Data.Select(
					p => new JProperty(p.Key, p.Value))))))))),


		new JProperty("exception", new JObject(result.Entries.Select(pair =>
			new JProperty(pair.Key, new JObject(
				new JProperty("message", (pair.Value.Exception == null) ? string.Empty : pair.Value.Exception.Message),
				new JProperty("stackTrace", (pair.Value.Exception == null) ? string.Empty : pair.Value.Exception.StackTrace))))))
		);
	}


	return context.Response.WriteAsync(
		json.ToString(Formatting.Indented));
}

That's it. We are done with health monitoring.

The complete project is available on GitHub.

The improvements we can add like creating email alerts or raising an event but usually this end-point will be called from DevOps/Site-Reliability-Engineering teams on their platform and they will create alerts that notify appropriate teams.

It is vital to add all critical components involved in the project to this health monitoring to prevent any issues which we can sense upfront and take action.

Appreciate your interest and time.

Happy coding!

要查看或添加评论,请登录

Senthil Kumaran的更多文章

  • SMS Segments Calculator

    SMS Segments Calculator

    We use 3rd party services like Twilio, Infobip, etc. to delivery SMS messages to end-user.

    1 条评论
  • Creating Excel Spreadsheet in .NET using DocumentFormat.OpenXML

    Creating Excel Spreadsheet in .NET using DocumentFormat.OpenXML

    We have few ways to create Microsoft Excel spreadsheet. Here we are going to discuss about creating using the…

  • Tools I use to make better me!

    Tools I use to make better me!

    I would like to express my ways to learning, in this journey, I share here the tools I use for my knowledge management.…

  • Quick intro to CQRS and Eventual Consitency

    Quick intro to CQRS and Eventual Consitency

    CQRS - Command Query Responsibility Segregation The command (insert/update/delete) commit the data in to the database…

  • Circuit Breaker Pattern

    Circuit Breaker Pattern

    The failures are usually categorized into 3 types: Immediate failure Timeout failure Cascading failure The response is…

  • Software Architecture Patterns

    Software Architecture Patterns

    Thinking how the enterprise systems are designed? Well, you are with me. Let's begin.

  • Secure .Net Core API calling from Twilio using Signature

    Secure .Net Core API calling from Twilio using Signature

    The requirement here is to secure the API developed in .Net Core 3.

  • AWS DynamoDB with .Net Core

    AWS DynamoDB with .Net Core

    In this blog, we are going to connect & query DynamoDB using .Net Core 3.

    3 条评论
  • AWS CDK with .Net Core

    AWS CDK with .Net Core

    CDK helps to build the cloud application with our convenient programming language and produce IaC (Infrastructure as…

    2 条评论
  • Setup MongoDB in Docker (Linux Container) on Windows 10 with Volume Mapping

    Setup MongoDB in Docker (Linux Container) on Windows 10 with Volume Mapping

    Let's see how to setup MongoDB in Docker on Windows 10 using Linux container. Let's pull the latest MongoDB image…

    5 条评论

社区洞察

其他会员也浏览了