From ace5077ad0e3257ea03bcf186c64d531436d8438 Mon Sep 17 00:00:00 2001 From: Phong Nguyen Date: Mon, 16 Dec 2024 11:13:38 +0700 Subject: [PATCH] Health Checks --- .../ClassifiedAds.AspireAppHost/Program.cs | 6 +- .../ClassifiedAds.BlazorServerSide/Program.cs | 24 +++++ src/Monolith/ClassifiedAds.GraphQL/Program.cs | 95 +++++++++++-------- src/Monolith/ClassifiedAds.WebAPI/Program.cs | 29 ++++++ 4 files changed, 112 insertions(+), 42 deletions(-) diff --git a/src/Monolith/ClassifiedAds.AspireAppHost/Program.cs b/src/Monolith/ClassifiedAds.AspireAppHost/Program.cs index 935e67c0d..f729c6fc4 100644 --- a/src/Monolith/ClassifiedAds.AspireAppHost/Program.cs +++ b/src/Monolith/ClassifiedAds.AspireAppHost/Program.cs @@ -2,10 +2,10 @@ var migrator = builder.AddProject("ClassifiedAds-Migrator"); var background = builder.AddProject("ClassifiedAds-Background"); -var graphQL = builder.AddProject("ClassifiedAds-GraphQL"); -var webApi = builder.AddProject("ClassifiedAds-WebAPI"); +var graphQL = builder.AddProject("ClassifiedAds-GraphQL").WithHttpsHealthCheck("/healthz"); +var webApi = builder.AddProject("ClassifiedAds-WebAPI").WithHttpsHealthCheck("/healthz"); var webMvc = builder.AddProject("ClassifiedAds-WebMVC").WithHttpsHealthCheck("/healthz"); -var blazorServerSide = builder.AddProject("ClassifiedAds-BlazorServerSide"); +var blazorServerSide = builder.AddProject("ClassifiedAds-BlazorServerSide").WithHttpsHealthCheck("/healthz"); var blazorWebAssembly = builder.AddProject("ClassifiedAds-BlazorWebAssembly"); var identityServer = builder diff --git a/src/Monolith/ClassifiedAds.BlazorServerSide/Program.cs b/src/Monolith/ClassifiedAds.BlazorServerSide/Program.cs index 1647ab8f7..f9216bea0 100644 --- a/src/Monolith/ClassifiedAds.BlazorServerSide/Program.cs +++ b/src/Monolith/ClassifiedAds.BlazorServerSide/Program.cs @@ -6,14 +6,19 @@ using ClassifiedAds.Blazor.Modules.Users.Services; using ClassifiedAds.BlazorServerSide.ConfigurationOptions; using ClassifiedAds.BlazorServerSide.Services; +using ClassifiedAds.Infrastructure.HealthChecks; +using ClassifiedAds.Infrastructure.HostedServices; using ClassifiedAds.Infrastructure.HttpMessageHandlers; using ClassifiedAds.Infrastructure.Logging; using Microsoft.AspNetCore.Authentication.Cookies; using Microsoft.AspNetCore.Authentication.OpenIdConnect; using Microsoft.AspNetCore.Builder; +using Microsoft.AspNetCore.Diagnostics.HealthChecks; using Microsoft.AspNetCore.Hosting; +using Microsoft.AspNetCore.Http; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Diagnostics.HealthChecks; using Microsoft.Extensions.Hosting; using System; @@ -116,6 +121,13 @@ options.RequireHttpsMetadata = appSettings.OpenIdConnect.RequireHttpsMetadata; }); +services.AddHealthChecks() + .AddHttp(appSettings.OpenIdConnect.Authority, name: "Identity Server", failureStatus: HealthStatus.Degraded) + .AddHttp(appSettings.ResourceServer.Endpoint, name: "Resource (Web API) Server", failureStatus: HealthStatus.Degraded); + +services.Configure(x => x.Interval = TimeSpan.FromMinutes(10)); +services.AddHostedService(); + var app = builder.Build(); // Configure the HTTP request pipeline. @@ -147,4 +159,16 @@ app.MapBlazorHub(); app.MapFallbackToPage("/_Host"); +app.UseHealthChecks("/healthz", new HealthCheckOptions +{ + Predicate = _ => true, + ResponseWriter = HealthChecksResponseWriter.WriteReponse, + ResultStatusCodes = + { + [HealthStatus.Healthy] = StatusCodes.Status200OK, + [HealthStatus.Degraded] = StatusCodes.Status500InternalServerError, + [HealthStatus.Unhealthy] = StatusCodes.Status503ServiceUnavailable, + }, +}); + app.Run(); \ No newline at end of file diff --git a/src/Monolith/ClassifiedAds.GraphQL/Program.cs b/src/Monolith/ClassifiedAds.GraphQL/Program.cs index 984a895e3..5e78b05a2 100644 --- a/src/Monolith/ClassifiedAds.GraphQL/Program.cs +++ b/src/Monolith/ClassifiedAds.GraphQL/Program.cs @@ -1,61 +1,78 @@ -using ClassifiedAds.Infrastructure.Logging; +using ClassifiedAds.GraphQL; +using ClassifiedAds.Infrastructure.HealthChecks; +using ClassifiedAds.Infrastructure.HostedServices; +using ClassifiedAds.Infrastructure.Logging; using GraphQL; using Microsoft.AspNetCore.Builder; +using Microsoft.AspNetCore.Diagnostics.HealthChecks; using Microsoft.AspNetCore.Hosting; +using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Server.Kestrel.Core; using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Diagnostics.HealthChecks; using Microsoft.Extensions.Hosting; using Microsoft.Extensions.Logging; +using System; -namespace ClassifiedAds.GraphQL; +var builder = WebApplication.CreateBuilder(args); -public class Program -{ - public static void Main(string[] args) - { - var builder = WebApplication.CreateBuilder(args); - - // Add services to the container. - var services = builder.Services; - var configuration = builder.Configuration; +// Add services to the container. +var services = builder.Services; +var configuration = builder.Configuration; - builder.WebHost.UseClassifiedAdsLogger(configuration => +builder.WebHost.UseClassifiedAdsLogger(configuration => { return new LoggingOptions(); }); - // If using Kestrel: - services.Configure(options => - { - options.AllowSynchronousIO = true; - }); - - // If using IIS: - services.Configure(options => - { - options.AllowSynchronousIO = true; - }); +// If using Kestrel: +services.Configure(options => +{ + options.AllowSynchronousIO = true; +}); - services.AddLogging(builder => builder.AddConsole()); - services.AddHttpContextAccessor(); +// If using IIS: +services.Configure(options => +{ + options.AllowSynchronousIO = true; +}); - services.AddGraphQL(b => b.AddAutoSchema(s => s.WithMutation()) - .AddSystemTextJson()); +services.AddLogging(builder => builder.AddConsole()); +services.AddHttpContextAccessor(); - // Configure the HTTP request pipeline. - var app = builder.Build(); +services.AddGraphQL(b => b.AddAutoSchema(s => s.WithMutation()) + .AddSystemTextJson()); - if (app.Environment.IsDevelopment()) - { - app.UseDeveloperExceptionPage(); - } +services.AddHealthChecks() + .AddHttp(configuration["ResourceServer:Endpoint"], name: "Resource (Web API) Server", failureStatus: HealthStatus.Degraded); - // add http for Schema at default url /graphql - app.UseGraphQL(); +services.Configure(x => x.Interval = TimeSpan.FromMinutes(10)); +services.AddHostedService(); - // use graphql-playground at default url /ui/playground - app.UseGraphQLPlayground(); +// Configure the HTTP request pipeline. +var app = builder.Build(); - app.Run(); - } +if (app.Environment.IsDevelopment()) +{ + app.UseDeveloperExceptionPage(); } + +app.UseHealthChecks("/healthz", new HealthCheckOptions +{ + Predicate = _ => true, + ResponseWriter = HealthChecksResponseWriter.WriteReponse, + ResultStatusCodes = + { + [HealthStatus.Healthy] = StatusCodes.Status200OK, + [HealthStatus.Degraded] = StatusCodes.Status500InternalServerError, + [HealthStatus.Unhealthy] = StatusCodes.Status503ServiceUnavailable, + }, +}); + +// add http for Schema at default url /graphql +app.UseGraphQL(); + +// use graphql-playground at default url /ui/playground +app.UseGraphQLPlayground(); + +app.Run(); diff --git a/src/Monolith/ClassifiedAds.WebAPI/Program.cs b/src/Monolith/ClassifiedAds.WebAPI/Program.cs index 296e502a9..9c791f112 100644 --- a/src/Monolith/ClassifiedAds.WebAPI/Program.cs +++ b/src/Monolith/ClassifiedAds.WebAPI/Program.cs @@ -5,6 +5,8 @@ using ClassifiedAds.Domain.Identity; using ClassifiedAds.Infrastructure.Csv; using ClassifiedAds.Infrastructure.Excel.ClosedXML; +using ClassifiedAds.Infrastructure.HealthChecks; +using ClassifiedAds.Infrastructure.HostedServices; using ClassifiedAds.Infrastructure.Identity; using ClassifiedAds.Infrastructure.Localization; using ClassifiedAds.Infrastructure.Logging; @@ -20,10 +22,12 @@ using Microsoft.AspNetCore.Authentication.JwtBearer; using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.DataProtection; +using Microsoft.AspNetCore.Diagnostics.HealthChecks; using Microsoft.AspNetCore.Hosting; using Microsoft.AspNetCore.Http; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Diagnostics.HealthChecks; using Microsoft.Extensions.Hosting; using Microsoft.IdentityModel.Tokens; using Microsoft.OpenApi.Models; @@ -221,6 +225,19 @@ services.AddCaches(appSettings.Caching); +services.AddHealthChecks() + .AddSqlServer(connectionString: appSettings.ConnectionStrings.ClassifiedAds, + healthQuery: "SELECT 1;", + name: "Sql Server", + failureStatus: HealthStatus.Degraded) + .AddHttp(appSettings.IdentityServerAuthentication.Authority, + name: "Identity Server", + failureStatus: HealthStatus.Degraded) + .AddStorageManagerHealthCheck(appSettings.Storage); + +services.Configure(x => x.Interval = TimeSpan.FromMinutes(10)); +services.AddHostedService(); + services.AddSingleton(); services.AddScoped(); @@ -284,6 +301,18 @@ app.UseMonitoringServices(appSettings.Monitoring); +app.UseHealthChecks("/healthz", new HealthCheckOptions +{ + Predicate = _ => true, + ResponseWriter = HealthChecksResponseWriter.WriteReponse, + ResultStatusCodes = + { + [HealthStatus.Healthy] = StatusCodes.Status200OK, + [HealthStatus.Degraded] = StatusCodes.Status500InternalServerError, + [HealthStatus.Unhealthy] = StatusCodes.Status503ServiceUnavailable, + }, +}); + app.MapControllers(); app.MapHub("/hubs/notification").RequireCors("SignalRHubs");