Table of contents
- Key Features of Health Checks
- 1. Multiple Checks for Different Dependencies
- 2. Degraded Responses for Non-Critical Dependencies
- 3. Detailed Status for Each Dependency
- 4. Error Details
- 5. Custom Data in Responses
- 6. Filtering Health Checks Based on Tags
- 7. Kubernetes Readiness and Liveness Probes
- Running the Demo
- Sample Health Check Response
- Conclusion
- References
Monitoring the health of your application is essential, particularly when it relies on multiple dependencies such as databases, external services, or other resources. API health checks in .NET provide a simple way to monitor the state of these dependencies and ensure everything is functioning as expected. In this blog, we'll walk through implementing API health checks in .NET, using a demo project as an example.
The code repository can be found at https://github.com/vaishnavkishan/HealthCheck-Demo
Key Features of Health Checks
1. Multiple Checks for Different Dependencies
In .NET, you can configure health checks for various dependencies like databases, cache services, or external APIs. Each check evaluates a specific component's status, helping you identify issues before they become critical.
builder.Services.AddHealthChecks()
.AddCheck<SampleHealthCheck>("basic_check")
.AddCheck<SampleHealthCheck1>("advance_check", HealthStatus.Degraded);
Here, SampleHealthCheck
and SampleHealthCheck1
represent different health checks. You can register as many checks as needed for your application.
2. Degraded Responses for Non-Critical Dependencies
In cases where a non-critical dependency fails, you can configure the health check to return a Degraded
status instead of Unhealthy
. This shows that while some parts of the system are underperforming, the system as a whole is still operational.
builder.Services.AddHealthChecks()
.AddCheck<SampleHealthCheck1>("advance_check", HealthStatus.Degraded);
In this example, SampleHealthCheck1
returns a Degraded
status, indicating that the dependency is non-critical.
3. Detailed Status for Each Dependency
The health check returns a detailed status for each dependency, helping you understand which parts of your system are facing issues.
app.MapHealthChecks("health", new HealthCheckOptions
{
ResponseWriter = UIResponseWriter.WriteHealthCheckUIResponse
});
When you hit the /health
endpoint, you'll get a JSON response detailing the status of each dependency.
4. Error Details
In case of failures, the health check response includes exception message, making it easier to diagnose and fix issues. This is particularly helpful for pinpointing the root cause of problems. Whether to send exception details in the response or not can also be configured based on environment.
public class SampleHealthCheck1 : IHealthCheck
{
public Task<HealthCheckResult> CheckHealthAsync(HealthCheckContext context, CancellationToken cancellationToken = default)
{
return Task.FromResult(
new HealthCheckResult(
context.Registration.FailureStatus, "An unhealthy result.",new Exception("This is exception")));
}
}
Here, SampleHealthCheck1
returns exception message along with the degraded status.
5. Custom Data in Responses
You can include custom data in the health check responses, providing additional context for each check. This is useful for debugging and monitoring.
Code Example:
public class SampleHealthCheck1 : IHealthCheck
{
public Task<HealthCheckResult> CheckHealthAsync(HealthCheckContext context, CancellationToken cancellationToken = default)
{
Dictionary<string, object> data = new Dictionary<string, object> {
{ "SomeData", "value1" },
{ "OtherData", "value2" }
};
return Task.FromResult(
new HealthCheckResult(
context.Registration.FailureStatus, "An unhealthy result.", null, data));
}
}
In this code, custom data is returned along with the health check result.
6. Filtering Health Checks Based on Tags
You can assign tags to different health checks and filter them based on these tags. For example, you might have a separate health check endpoint for critical dependencies and another for non-critical checks.
Code Example:
builder.Services.AddHealthChecks()
.AddCheck<StartupHealthCheck>("startup", tags: new[] { "ready" });
app.MapHealthChecks("health/ready", new HealthCheckOptions
{
ResponseWriter = UIResponseWriter.WriteHealthCheckUIResponse,
Predicate = healthCheck => healthCheck.Tags.Contains("ready")
});
In this example, the StartupHealthCheck
is tagged with ready
, and the /health/ready
endpoint only returns checks with that tag.
7. Kubernetes Readiness and Liveness Probes
Health checks can be integrated with Kubernetes' readiness and liveness probes, ensuring your application is only exposed to traffic when it's ready, and that it can automatically recover if it becomes unhealthy.
Code Example:
// Liveness probe, runs all checks and returns healthy if all checks are healthy
app.MapHealthChecks("health/live", new HealthCheckOptions
{
ResponseWriter = UIResponseWriter.WriteHealthCheckUIResponse
});
// Readiness probe, waits for 15 seconds after app starts and then returns healthy
app.MapHealthChecks("health/ready", new HealthCheckOptions
{
ResponseWriter = UIResponseWriter.WriteHealthCheckUIResponse,
Predicate = healthCheck => healthCheck.Tags.Contains("ready")
});
In this setup, the /health/live
endpoint checks the liveness of all components, while the /health/ready
endpoint focuses on readiness.
Running the Demo
To try out the API health check implementation in .NET, follow these simple steps:
Clone the repository:
git clone https://github.com/vaishnavkishan/HealthCheck-Demo.git
Install the .NET 8 SDK: Make sure you have the .NET 8 SDK installed. You can download it from the official .NET website if you don't have it installed yet.
Run the application:
dotnet run
Access the health check endpoints: Once the application is running, you can check the health of your API by navigating to the following URLs in your browser or using tools like
curl
or Postman:
Sample Health Check Response
Below is a sample response from a health check endpoint:
{
"status": "Unhealthy",
"totalDuration": "00:00:00.0104583",
"entries": {
"basic_check": {
"data": {},
"description": "A healthy result.",
"duration": "00:00:00.0006799",
"status": "Healthy",
"tags": []
},
"advance_check": {
"data": {
"SomeData": "value1",
"key2": "value2"
},
"description": "An unhealthy result.",
"duration": "00:00:00.0007156",
"exception": "This is exception",
"status": "Degraded",
"tags": []
},
"startup": {
"data": {},
"description": "That startup task is still running.",
"duration": "00:00:00.0010888",
"status": "Unhealthy",
"tags": [
"ready"
]
}
}
}
As you can see from the response, different checks can return different statuses like Healthy
, Degraded
, or Unhealthy
, and you can also include custom data and error details for deeper insights.
Conclusion
Implementing API health checks in .NET is a straightforward and effective way to monitor the health of your application and its dependencies. With features like multiple checks, degraded responses, custom data, and Kubernetes integration, you can ensure your application remains resilient and easy to troubleshoot.
Now it's your turn to try it out in your own project!