This commit is contained in:
master
2026-02-04 19:59:20 +02:00
parent 557feefdc3
commit 5548cf83bf
1479 changed files with 53557 additions and 40339 deletions

View File

@@ -0,0 +1,35 @@
using Microsoft.Extensions.Diagnostics.HealthChecks;
using Microsoft.Extensions.Hosting;
namespace StellaOps.Worker.Health;
/// <summary>
/// Health check that reports healthy once the host has fully started.
/// Before <see cref="IHostApplicationLifetime.ApplicationStarted"/> fires,
/// this check returns <see cref="HealthStatus.Degraded"/> so readiness
/// probes can distinguish startup from running state.
/// </summary>
internal sealed class HostStartedHealthCheck : IHealthCheck, IDisposable
{
private volatile bool _started;
private CancellationTokenRegistration _registration;
public HostStartedHealthCheck(IHostApplicationLifetime lifetime)
{
_registration = lifetime.ApplicationStarted.Register(() => _started = true);
}
public Task<HealthCheckResult> CheckHealthAsync(
HealthCheckContext context,
CancellationToken cancellationToken = default)
{
return Task.FromResult(_started
? HealthCheckResult.Healthy("Host started.")
: HealthCheckResult.Degraded("Host is starting."));
}
public void Dispose()
{
_registration.Dispose();
}
}

View File

@@ -0,0 +1,13 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net10.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
<LangVersion>preview</LangVersion>
</PropertyGroup>
<ItemGroup>
<FrameworkReference Include="Microsoft.AspNetCore.App" />
</ItemGroup>
</Project>

View File

@@ -0,0 +1,49 @@
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Diagnostics.HealthChecks;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Diagnostics.HealthChecks;
namespace StellaOps.Worker.Health;
/// <summary>
/// Extension methods to add lightweight health endpoints to worker services.
/// </summary>
public static class WorkerHealthExtensions
{
/// <summary>
/// Registers a health check that tracks whether the host has fully started.
/// Call this on <c>builder.Services</c> before building the application.
/// </summary>
public static IServiceCollection AddWorkerHealthChecks(this IServiceCollection services)
{
services.AddHealthChecks()
.AddCheck<HostStartedHealthCheck>("host_started", tags: ["ready"]);
return services;
}
/// <summary>
/// Maps <c>/health/liveness</c> (always 200) and <c>/health/readiness</c>
/// (checks registered health checks) endpoints.
/// </summary>
public static WebApplication MapWorkerHealthEndpoints(this WebApplication app)
{
// Liveness: always returns 200 if the process is running
app.MapGet("/health/liveness", () => Results.Ok("Alive"))
.ExcludeFromDescription();
// Readiness: runs all registered health checks tagged "ready"
app.MapHealthChecks("/health/readiness", new HealthCheckOptions
{
Predicate = check => check.Tags.Contains("ready"),
ResultStatusCodes =
{
[HealthStatus.Healthy] = StatusCodes.Status200OK,
[HealthStatus.Degraded] = StatusCodes.Status503ServiceUnavailable,
[HealthStatus.Unhealthy] = StatusCodes.Status503ServiceUnavailable,
},
});
return app;
}
}