- Introduction to Health Checks
- Historical Context
- Evolution to Built-in Health Checks
- Setting Up Health Checks
- 1. Installing Required Packages
- 2. Configuring Basic Health Checks
- Implementing Custom Health Checks
- Adding Database Health Checks
- SQL Server
- Redis
- Formatting Health Check Responses
- Enabling UI Dashboard
- Best Practices for Production
- Drawing the Line
Introduction to Health Checks
Health checks are a fundamental pattern in modern software architecture that help ensure system reliability and maintainability. At their core, health checks are endpoints that provide information about a system’s operational status, helping operators and monitoring tools determine whether an application is functioning correctly.
Historical Context
Before the introduction of built-in health check APIs in .NET Core, developers had to implement their own solutions, which typically involved:
- Creating custom endpoints (usually at /health or /ping) that would return basic status information
- Writing custom middleware to check various system components
- Implementing custom monitoring logic for each dependency
- Building proprietary solutions for health reporting and monitoring
Common approaches included:
- Simple ping endpoints that returned HTTP 200 OK responses
- Custom controllers that performed basic database connectivity tests
- Homegrown solutions for checking external service availability
- Manual implementation of circuit breakers and timeout logic
These solutions often lacked standardization and required significant maintenance effort. Developers would frequently create their own health check frameworks or rely on third-party libraries, leading to inconsistent implementations across different applications and teams.
Evolution to Built-in Health Checks
With the introduction of .NET Core 2.2, Microsoft incorporated health checks as a first-class feature of the framework. This marked a significant improvement over previous approaches by providing:
- A standardized API for implementing health checks
- Built-in middleware for processing health check requests
- Extension points for custom health check implementation
- Integration with common monitoring and orchestration platforms
Setting Up Health Checks
1. Installing Required Packages
.NET Core provides built-in support for health checks via the Microsoft.AspNetCore.Diagnostics.HealthChecks package. However, for more advanced scenarios, you might need additional packages such as:
<PackageReference Include=”Microsoft.Extensions.Diagnostics.HealthChecks” Version=”7.0.0″ />
<PackageReference Include=”AspNetCore.HealthChecks.SqlServer” Version=”6.0.2″ />
<PackageReference Include=”AspNetCore.HealthChecks.Redis” Version=”6.0.4″ />
<PackageReference Include=”AspNetCore.HealthChecks.UI” Version=”6.0.5″ />
2. Configuring Basic Health Checks
In Program.cs, configure health checks using the built-in AddHealthChecks() method:
123456789 | var builder = WebApplication.CreateBuilder(args);
builder.Services.AddHealthChecks()
.AddCheck("self", () => HealthCheckResult.Healthy())
.AddCheck<DatabaseHealthCheck>("database");
var app = builder.Build();
app.MapHealthChecks("/health"); |
Implementing Custom Health Checks
For complex applications, built-in checks may not be sufficient. You can implement a custom health check by inheriting from IHealthCheck:
12345678910111213141516171819202122232425 | public class DatabaseHealthCheck : IHealthCheck
{
private readonly IConfiguration _configuration;
public DatabaseHealthCheck(IConfiguration configuration)
{
_configuration = configuration;
}
public async Task<HealthCheckResult> CheckHealthAsync(HealthCheckContext context,
CancellationToken cancellationToken = default)
{
try
{
using var connection = new SqlConnection(_configuration.GetConnectionString("DefaultConnection"));
await connection.OpenAsync(cancellationToken);
return HealthCheckResult.Healthy();
}
catch (Exception ex)
{
return HealthCheckResult.Unhealthy(ex.Message);
}
}
} |
Adding Database Health Checks
To monitor database connectivity, use the respective database health check packages:
SQL Server
12345 | builder.Services.AddHealthChecks()
.AddSqlServer(
connectionString: configuration.GetConnectionString("DefaultConnection"),
name: "sql",
tags: new[] { "db", "sql", "sqlserver" }); |
Redis
12345 | builder.Services.AddHealthChecks()
.AddRedis(
redisConnectionString: configuration.GetConnectionString("Redis"),
name: "redis",
tags: new[] { "cache", "redis" }); |
Formatting Health Check Responses
For structured JSON responses, use HealthCheckOptions:
1234567891011121314151617181920 | app.MapHealthChecks("/health", new HealthCheckOptions
{
ResponseWriter = async (context, report) =>
{
var result = JsonSerializer.Serialize(new
{
status = report.Status.ToString(),
checks = report.Entries.Select(entry => new
{
name = entry.Key,
status = entry.Value.Status.ToString(),
description = entry.Value.Description,
data = entry.Value.Data
})
});
context.Response.ContentType = "application/json";
await context.Response.WriteAsync(result);
}
}); |
Enabling UI Dashboard
To visualize health checks, use AspNetCore.HealthChecks.UI:
- Register the UI services:
2. Add the UI middleware:
Best Practices for Production
When implementing health checks in production environments, consider the following best practices:
- Segregate Endpoints: Provide different endpoints for different types of health checks:
- /health/live for liveness probes
- /health/ready for readiness probes
- /health/deep for detailed system checks
2. Performance Considerations:
- Cache health check results where appropriate
- Implement timeouts for external dependency checks
- Use async operations for all I/O operations
3. Security:
- Protect sensitive health check endpoints
- Consider implementing authentication for detailed health information
- Limit the information exposed in public health check endpoints
4. Monitoring Integration:
- Configure alerts based on health check status
- Integrate with monitoring systems like Prometheus or Azure Monitor
- Set up appropriate logging for health check failures
Drawing the Line
Health checks are a critical aspect of modern application monitoring. .NET Core provides a flexible and extensible framework for implementing these checks, allowing you to monitor databases, caches, external services, and application internals. By integrating health checks with a UI and following best practices, you gain better visibility into your system’s health and can ensure the reliable operation of your applications.
Remember that health checks should be designed to be lightweight and non-intrusive while still providing meaningful information about system health. Regular review and updates of health check implementations ensure they remain relevant and effective as your system evolves.