Reputation: 989
Is there a possibility for health checks in aspnet core to be configured after the application starts via a http request. All the documentation and examples I have gone through currently configure the health checks in Startup.cs Configure method
only.
For example, I would like to define a controller HealthChecksController
which has action methods to add remove health checks in my application via an external application.
public class HealthChecksController : Controller
{
[HttpPost]
public Customer CreateHealthCheck(HealthCheck healthCheck)
{
//Add to existing health checks
}
[HttpDelete]
public void DeleteHealthCheck(int id)
{
//Remove from existing health checks
}
}
The reason I am asking this question is I want to develop a monitoring system for checking the status of certain services which are running on my server and the existing health checks framework looks like a proper fit for my requirement rather than reinventing the wheel. The services to be monitored are not known during application development stage and have to be configured after the application is deployed. Hence the need to configure them whenasp.net application is running.
Is this even possible with existing health checks, if not is there any other viable solution?
Upvotes: 1
Views: 5483
Reputation: 1918
A little bit late but yes you can.
At first you need to implemente a custom IHealthCheck
because the default one is internal sealed.
You can clone DelegateHealthCheck
from .net sources:
internal sealed class CustomDelegateHealthCheck : IHealthCheck
{
private readonly Func<CancellationToken, Task<HealthCheckResult>> _check;
/// <summary>
/// Create an instance of <see cref="DelegateHealthCheck"/> from the specified delegate.
/// </summary>
/// <param name="check">A delegate which provides the code to execute when the health check is run.</param>
public CustomDelegateHealthCheck(Func<CancellationToken, Task<HealthCheckResult>> check)
{
_check = check ?? throw new ArgumentNullException(nameof(check));
}
/// <summary>
/// Runs the health check, returning the status of the component being checked.
/// </summary>
/// <param name="context">A context object associated with the current execution.</param>
/// <param name="cancellationToken">A <see cref="CancellationToken"/> that can be used to cancel the health check.</param>
/// <returns>A <see cref="Task{HealthCheckResult}"/> that completes when the health check has finished, yielding the status of the component being checked.</returns>
public Task<HealthCheckResult> CheckHealthAsync(HealthCheckContext context, CancellationToken cancellationToken = default) => _check(cancellationToken);
}
Then you can add healtchecks like this during runtime:
// get HealthCheckServiceOptions, we need this to access the Registrations
var o = _service.GetRequiredService< IOptions<HealthCheckServiceOptions> >();
// define your health check
var healthCheck = new CustomDelegateHealthCheck((ct) => Task.FromResult(HealthCheckResult.Healthy()));
// append your health check to the Registrations
o.Value.Registrations.Add(new HealthCheckRegistration($"name", healthCheck, failureStatus: null, default, default));
Upvotes: 1
Reputation: 8672
Have you seen Health Checks docs
How are you checking the status of the dynamic services? I'm going to assume they are APIs (for simplicity of my example)
One solution could be to create a service to store the URLs to check for health status:
NOTE: This example contains no error checking or concurrency protection.
public class HealthCheckService
{
private readonly List<string> _urls = new List<string>();
public void Add(string url)
{
_urls.Add(url);
}
public void Remove(string url)
{
_urls.Remove(url);
}
public IEnumerable<string> GetServices()
{
return _urls;
}
}
Register this as a singleton in your startup.
services.AddSingleton<HealthCheckService>();
You can inject this into your controller and add the URLs
[ApiController]
[Route("/api/health")]
public class HealthCheckController : ControllerBase
{
private readonly HealthCheckService _service;
public HealthCheckController(HealthCheckService service)
{
_service = service;
}
[HttpPost]
public IActionResult Add(string url)
{
_service.Add(url);
return Ok();
}
[HttpDelete]
public IActionResult Remove(string url)
{
_service.Remove(url);
return Ok();
}
}
Then you need to create a class that inherits from IHealthCheck
public class MyHealthChecks : IHealthCheck
{
private readonly HealthCheckService _service;
public MyHealthChecks(HealthCheckService service)
{
_service = service;
}
public Task<HealthCheckResult> CheckHealthAsync(HealthCheckContext context, CancellationToken cancellationToken = default)
{
foreach(var svc in _service.GetServices())
{
// call the URL to verify?
// var response = await httpClient.SendAsync(url);
//
// do something with response and store it in results[svc]
// if (!response.IsSuccessStatusCode)
// {
// return Task.FromResult(new HealthCheckResult(
// HealthStatus.Unhealthy, svc));
// }
}
return Task.FromResult(new HealthCheckResult(
HealthStatus.Healthy));
}
}
You'll need to modify the code in CheckHealthAsync
to call your services and respond appropriately.
Finally register the health check class during startup:
services.AddHealthChecks()
.AddCheck<MyHealthChecks>("CustomHealthChecks");
You can add a custom response writer if you want to return more detailed health status information.
Upvotes: 2