diff --git a/services/Directory/FilterLists.Directory.Api/Program.cs b/services/Directory/FilterLists.Directory.Api/Program.cs index 5182d8778..2618a16da 100644 --- a/services/Directory/FilterLists.Directory.Api/Program.cs +++ b/services/Directory/FilterLists.Directory.Api/Program.cs @@ -5,11 +5,17 @@ var builder = WebApplication.CreateBuilder(args); builder.WebHost.ConfigureKestrel(serverOptions => serverOptions.AddServerHeader = false); + +// for health checks: https://learn.microsoft.com/en-us/dotnet/aspire/fundamentals/health-checks#non-development-environments +builder.Services.AddRequestTimeouts(); +builder.Services.AddOutputCache(); + builder.AddServiceDefaults(); builder.Services.AddCors(CorsConfiguration.SetupAction); builder.Services.AddProblemDetails(); builder.Services.AddEndpointsApiExplorer(); builder.Services.AddSwaggerGen(OpenApiGenConfiguration.SetupAction); + builder.AddApplication(); var app = builder.Build(); @@ -17,6 +23,11 @@ app.UseExceptionHandler(); app.UseCors(); app.MapEndpoints(); + +// for health checks: https://learn.microsoft.com/en-us/dotnet/aspire/fundamentals/health-checks#non-development-environments +app.UseRequestTimeouts(); +app.UseOutputCache(); + app.MapDefaultEndpoints(); app.UseSwagger(o => o.RouteTemplate = "{documentName}/openapi.json"); app.UseSwaggerUI(SwaggerUiConfiguration.SetupAction); diff --git a/services/FilterLists.ServiceDefaults/Extensions.cs b/services/FilterLists.ServiceDefaults/Extensions.cs index 7c8919c33..0f1b3d23f 100644 --- a/services/FilterLists.ServiceDefaults/Extensions.cs +++ b/services/FilterLists.ServiceDefaults/Extensions.cs @@ -4,6 +4,7 @@ using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Diagnostics.HealthChecks; using Microsoft.Extensions.Logging; +using Microsoft.Extensions.ServiceDiscovery; using OpenTelemetry; using OpenTelemetry.Metrics; using OpenTelemetry.Trace; @@ -19,7 +20,7 @@ public static class Extensions private const string HealthEndpointPath = "/health"; private const string AlivenessEndpointPath = "/alive"; - public static TBuilder AddServiceDefaults(this TBuilder builder) where TBuilder : IHostApplicationBuilder + public static void AddServiceDefaults(this TBuilder builder) where TBuilder : IHostApplicationBuilder { builder.ConfigureOpenTelemetry(); @@ -36,13 +37,8 @@ public static TBuilder AddServiceDefaults(this TBuilder builder) where http.AddServiceDiscovery(); }); - // Uncomment the following to restrict the allowed schemes for service discovery. - // builder.Services.Configure(options => - // { - // options.AllowedSchemes = ["https"]; - // }); - - return builder; + // restrict the allowed schemes for service discovery + builder.Services.Configure(options => options.AllowedSchemes = ["https"]); } private static void ConfigureOpenTelemetry(this TBuilder builder) where TBuilder : IHostApplicationBuilder @@ -86,37 +82,49 @@ private static void AddOpenTelemetryExporters(this TBuilder builder) if (useOtlpExporter) builder.Services.AddOpenTelemetry().UseOtlpExporter(); - // Uncomment the following lines to enable the Azure Monitor exporter (requires the Azure.Monitor.OpenTelemetry.AspNetCore package) - if (!string.IsNullOrEmpty(builder.Configuration["APPLICATIONINSIGHTS_CONNECTION_STRING"])) + // enable the Azure Monitor exporter + if (!builder.Environment.IsDevelopment()) + { + if (string.IsNullOrEmpty(builder.Configuration["APPLICATIONINSIGHTS_CONNECTION_STRING"])) + throw new InvalidOperationException("APPLICATIONINSIGHTS_CONNECTION_STRING is not set."); + builder.Services.AddOpenTelemetry() .UseAzureMonitor(); + } } - private static void AddDefaultHealthChecks(this TBuilder builder) where TBuilder : IHostApplicationBuilder + // https://learn.microsoft.com/en-us/dotnet/aspire/fundamentals/health-checks#non-development-environments + private static void AddDefaultHealthChecks(this IHostApplicationBuilder builder) { + builder.Services.AddRequestTimeouts(static timeouts => + timeouts.AddPolicy("HealthChecks", TimeSpan.FromSeconds(5))); + + builder.Services.AddOutputCache(static caching => + caching.AddPolicy("HealthChecks", + static policy => policy.Expire(TimeSpan.FromSeconds(10)))); + builder.Services.AddHealthChecks() // Add a default liveness check to ensure the app is responsive .AddCheck("self", () => HealthCheckResult.Healthy(), ["live"]); } - public static WebApplication MapDefaultEndpoints(this WebApplication app) + public static void MapDefaultEndpoints(this WebApplication app) { - // Adding health checks endpoints to applications in non-development environments has security implications. - // See https://aka.ms/dotnet/aspire/healthchecks for details before enabling these endpoints in non-development environments. -#pragma warning disable CA1062 - if (app.Environment.IsDevelopment()) -#pragma warning restore CA1062 + var healthChecks = app.MapGroup(""); + + healthChecks + .CacheOutput("HealthChecks") + .WithRequestTimeout("HealthChecks"); + + // All health checks must pass for the app to be + // considered ready to accept traffic after starting + healthChecks.MapHealthChecks(HealthEndpointPath); + + // Only health checks tagged with the "live" tag + // must pass for the app to be considered alive + healthChecks.MapHealthChecks(AlivenessEndpointPath, new HealthCheckOptions { - // All health checks must pass for the app to be considered ready to accept traffic after starting - app.MapHealthChecks(HealthEndpointPath); - - // Only health checks tagged with the "live" tag must pass for the app to be considered alive - app.MapHealthChecks(AlivenessEndpointPath, new HealthCheckOptions - { - Predicate = r => r.Tags.Contains("live") - }); - } - - return app; + Predicate = static r => r.Tags.Contains("live") + }); } } \ No newline at end of file