using Microsoft.AspNetCore.Builder; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; using OpenTelemetry.Metrics; using OpenTelemetry.Resources; using OpenTelemetry.Trace; namespace Baya.Infrastructure.Monitoring.Configurations; /// /// The single OpenTelemetry stack (refinement-phase-9, §9.1). Metrics and traces share one resource /// (service.name = Baya.Web.Api). Metrics are scraped by Prometheus (the /metrics endpoint, /// wired in ); traces cover ASP.NET Core requests and EF Core /// commands so a cross-service money flow (webhook → confirm → ledger) can be followed end to end. The /// request's trace id is the ApiResult.RequestId the client sees, so a support ticket maps 1:1 to a /// trace. /// The prior duplicate prometheus-net stack was removed — OpenTelemetry is now the only metrics source. /// OTLP export (traces + metrics) is opt-in: it is wired only when OpenTelemetry:Otlp:Endpoint /// is configured, so an MVP deployment with Prometheus alone runs unchanged and no exporter spams connection /// errors against an absent collector. /// public static class OpenTelemetryConfigurations { public const string ServiceName = "Baya.Web.Api"; public static WebApplicationBuilder SetupOpenTelemetry(this WebApplicationBuilder builder) { var otlpEndpoint = builder.Configuration["OpenTelemetry:Otlp:Endpoint"]; var hasOtlp = !string.IsNullOrWhiteSpace(otlpEndpoint); builder.Services.AddOpenTelemetry() .ConfigureResource(resource => resource.AddService(ServiceName)) .WithMetrics(metricsBuilder => { metricsBuilder.AddRuntimeInstrumentation() .AddAspNetCoreInstrumentation() .AddMeter("Microsoft.AspNetCore.Hosting" , "Microsoft.AspNetCore.Server.Kestrel" , "System.Net.Http" , "Baya.Web.Api" , "ControllerMetrics" // The mediator request-duration histogram (MetricsBehaviour) — captured here so the one // metrics stack actually exports it (the removed prometheus-net stack never did). , "mediator_meter") .AddPrometheusExporter(); if (hasOtlp) metricsBuilder.AddOtlpExporter(options => options.Endpoint = new Uri(otlpEndpoint!)); }) .WithTracing(tracingBuilder => { tracingBuilder.AddAspNetCoreInstrumentation() .AddEntityFrameworkCoreInstrumentation(); if (hasOtlp) tracingBuilder.AddOtlpExporter(options => options.Endpoint = new Uri(otlpEndpoint!)); }); builder.Services.AddMetrics(); return builder; } }