Adding multiple health check endpoint for dotnet core application
Imagine the scenario when we need to expose one health check endpoint for checking application liveliness and another endpoint for checking application readiness.
Lets assume, liveliness endpoint is responsible to see if the application is healthy and ready to receive traffic. On the other hand, readiness endpoint is responsible to check if the application is responsive, if not - the application will be restarted by some way to fix that.
We can use the Health Checks provided by ASP.NET Core to achieve this with filtering by tags.
Lets assume that we have a "/ping" endpoint in our application that responses with "pong". We can add a health check to hit the ping endpoint to see if our application is responsive or not.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
public class ApiHealthCheck : IHealthCheck | |
{ | |
private readonly HttpContext _httpContext; | |
public ApiHealthCheck(IHttpContextAccessor contextAccessor) | |
{ | |
_httpContext = contextAccessor?.HttpContext; | |
} | |
public async Task<HealthCheckResult> CheckHealthAsync(HealthCheckContext context, CancellationToken cancellationToken = default) | |
{ | |
try | |
{ | |
using (var httpClient = new HttpClient()) | |
{ | |
var url = new Uri($"{_httpContext.Request.Scheme}://{_httpContext.Request.Host}/ping"); | |
var response = await httpClient.GetAsync(url); | |
if (!response.IsSuccessStatusCode) | |
{ | |
return HealthCheckResult.Unhealthy($"Didn't receive OK response from /ping endpoint. Response Phrase: {response.ReasonPhrase}."); | |
} | |
} | |
} | |
catch (Exception ex) | |
{ | |
return HealthCheckResult.Unhealthy($"Error ocurred while connecting to /ping endpoint.", ex); | |
} | |
return HealthCheckResult.Healthy("API is ready."); | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
public class EsHealthCheck : IHealthCheck | |
{ | |
private readonly IElasticSearchDataProvider _dataProvider; | |
public EsHealthCheck(IElasticSearchDataProvider dataProvider) | |
{ | |
_dataProvider = dataProvider; | |
} | |
public async Task<HealthCheckResult> CheckHealthAsync(HealthCheckContext context, CancellationToken cancellationToken = default) | |
{ | |
var healthCheckResultHealthy = await _dataProvider.HealthCheck(); | |
if (healthCheckResultHealthy) | |
{ | |
return HealthCheckResult.Healthy("Ok."); | |
} | |
return HealthCheckResult.Unhealthy("ES cluster returned unhealthy status."); | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
public class Startup | |
{ | |
public Startup(IConfiguration configuration) | |
{ | |
Configuration = configuration; | |
} | |
public IConfiguration Configuration { get; } | |
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline. | |
public void Configure(IApplicationBuilder app, IHostingEnvironment env) | |
{ | |
// Using a custom extension for adding health check endpoints | |
app.UseHealthChecks(); | |
// Do other stuffs | |
} | |
// This method gets called by the runtime. Use this method to add services to the container. | |
public void ConfigureServices(IServiceCollection services) | |
{ | |
// Add other services | |
services.AddHealthChecks() | |
.AddCheck<EsHealthCheck>("ElasticSearch", tags: new[] { "Live" }) | |
.AddCheck<ApiHealthCheck>("API", tags: new[] { "Ready" }); | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
public static class ApplicationBuilderExtensions | |
{ | |
public static IApplicationBuilder UseHealthChecks(this IApplicationBuilder builder) | |
{ | |
builder.UseHealthChecks("/health", NewHealthCheckOptions("Live")); | |
builder.UseHealthChecks("/ready", NewHealthCheckOptions("Ready")); | |
return builder; | |
} | |
private static HealthCheckOptions NewHealthCheckOptions(string filterByTag) | |
{ | |
return new HealthCheckOptions() | |
{ | |
Predicate = (x) => x.Tags.Contains(filterByTag), | |
ResponseWriter = (httpContext, report) => | |
{ | |
httpContext.Response.ContentType = "application/json"; | |
return httpContext.Response.WriteAsync(report.ToJObject().ToString(Formatting.Indented)); | |
} | |
}; | |
} | |
private static JObject ToJObject(this HealthReport result) | |
{ | |
if (result == null) | |
{ | |
return default; | |
} | |
return 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("error", pair.Value.Exception?.Message))))))); | |
} | |
} |
- localhost:5000/health - this will check health status of underlying services
- localhost:5000/ready - this will check application responsiveness
HAPPY CODING 👌
Comments
Post a Comment