using Microsoft.Extensions.Configuration; using OpenTelemetry.Exporter; using OpenTelemetry.Logs; namespace FiscalOS.ServiceDefaults; public static class ServiceDefaultsExtensions { private const string HealthEndpointPath = "/health"; private const string AlivenessEndpointPath = "/alive"; public static TBuilder AddServiceDefaults(this TBuilder builder) where TBuilder : IHostApplicationBuilder { builder.ConfigureOpenTelemetry(); builder.AddDefaultHealthChecks(); builder.Services.AddServiceDiscovery(); builder.Services.ConfigureHttpClientDefaults(static http => { http.AddStandardResilienceHandler(); http.AddServiceDiscovery(); }); return builder; } public static TBuilder ConfigureOpenTelemetry(this TBuilder builder) where TBuilder : IHostApplicationBuilder { builder.Logging.AddOpenTelemetry(logging => { logging.IncludeFormattedMessage = true; logging.IncludeScopes = true; logging.ParseStateValues = true; }); builder.Services.AddOpenTelemetry() .WithMetrics(metrics => metrics.AddAspNetCoreInstrumentation() .AddHttpClientInstrumentation() .AddRuntimeInstrumentation() ) .WithTracing(tracing => tracing.AddSource(builder.Environment.ApplicationName) .AddAspNetCoreInstrumentation(tracing => tracing.Filter = context => context.Request.Path.StartsWithSegments(HealthEndpointPath, StringComparison.InvariantCultureIgnoreCase) is false && context.Request.Path.StartsWithSegments(AlivenessEndpointPath, StringComparison.InvariantCultureIgnoreCase) is false ) .AddHttpClientInstrumentation() ); builder.AddOpenTelemetryExporters(); return builder; } public static TBuilder AddDefaultHealthChecks(this TBuilder builder) where TBuilder : IHostApplicationBuilder { builder.Services.AddHealthChecks() .AddCheck("self", static () => HealthCheckResult.Healthy(), ["live"]); return builder; } public static WebApplication MapDefaultEndpoints(this WebApplication app) { ArgumentNullException.ThrowIfNull(app, nameof(app)); if (app.Environment.IsDevelopment()) { app.MapHealthChecks(HealthEndpointPath); app.MapHealthChecks(AlivenessEndpointPath, new HealthCheckOptions { Predicate = static r => r.Tags.Contains("live") }); } return app; } private static TBuilder AddOpenTelemetryExporters(this TBuilder builder) where TBuilder : IHostApplicationBuilder { var seqOptions = new SeqOptions(); builder.Configuration.GetSection(nameof(SeqOptions)).Bind(seqOptions); var useOtlpExporter = !string.IsNullOrWhiteSpace(builder.Configuration["OTEL_EXPORTER_OTLP_ENDPOINT"]); var otel = builder.Services.AddOpenTelemetry(); if (seqOptions.IsEnabled) { otel .WithLogging(lb => { lb.AddOtlpExporter(o => { o.Protocol = OtlpExportProtocol.HttpProtobuf; o.Endpoint = new Uri(seqOptions.LogEndpoint); o.Headers = seqOptions.AuthHeader; }); }) .WithTracing(tb => { tb.AddOtlpExporter(o => { o.Protocol = OtlpExportProtocol.HttpProtobuf; o.Endpoint = new Uri(seqOptions.TraceEndpoint); o.Headers = seqOptions.AuthHeader; }); }); } else if (useOtlpExporter) { otel.UseOtlpExporter(); } return builder; } }