diff --git a/docs-builder.sln.DotSettings b/docs-builder.sln.DotSettings index 571a4f3ad..7d809a9c7 100644 --- a/docs-builder.sln.DotSettings +++ b/docs-builder.sln.DotSettings @@ -4,4 +4,5 @@ True True True + True True \ No newline at end of file diff --git a/src/Elastic.Documentation.ServiceDefaults/Extensions.cs b/src/Elastic.Documentation.ServiceDefaults/Extensions.cs index 63d479679..eaa86b9d0 100644 --- a/src/Elastic.Documentation.ServiceDefaults/Extensions.cs +++ b/src/Elastic.Documentation.ServiceDefaults/Extensions.cs @@ -37,6 +37,11 @@ public static TBuilder AddServiceDefaults(this TBuilder builder) where public static TBuilder AddOpenTelemetryDefaults(this TBuilder builder) where TBuilder : IHostApplicationBuilder { + + var useOtlpExporter = !string.IsNullOrWhiteSpace(builder.Configuration["OTEL_EXPORTER_OTLP_ENDPOINT"]); + if (!useOtlpExporter) + return builder; + _ = builder.Logging.AddOpenTelemetry(logging => { logging.IncludeFormattedMessage = true; @@ -86,14 +91,6 @@ private static TBuilder AddOpenTelemetryExporters(this TBuilder builde _ = 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"])) - //{ - // builder.Services.AddOpenTelemetry() - // .UseAzureMonitor(); - //} - return builder; } diff --git a/src/api/Elastic.Documentation.Api.Infrastructure/MappingsExtensions.cs b/src/api/Elastic.Documentation.Api.Infrastructure/MappingsExtensions.cs index 7a69179cb..ef756d7c8 100644 --- a/src/api/Elastic.Documentation.Api.Infrastructure/MappingsExtensions.cs +++ b/src/api/Elastic.Documentation.Api.Infrastructure/MappingsExtensions.cs @@ -14,13 +14,15 @@ namespace Elastic.Documentation.Api.Infrastructure; public static class MappingsExtension { - public static void MapElasticDocsApiEndpoints(this IEndpointRouteBuilder group) + public static void MapElasticDocsApiEndpoints(this IEndpointRouteBuilder group, bool mapOtlpEndpoints = true) { + _ = group.MapGet("/", () => Results.Empty); _ = group.MapPost("/", () => Results.Empty); MapAskAiEndpoint(group); MapNavigationSearch(group); - MapOtlpProxyEndpoint(group); + if (mapOtlpEndpoints) + MapOtlpProxyEndpoint(group); } private static void MapAskAiEndpoint(IEndpointRouteBuilder group) diff --git a/src/api/Elastic.Documentation.Api.Infrastructure/OpenTelemetry/OpenTelemetryExtensions.cs b/src/api/Elastic.Documentation.Api.Infrastructure/OpenTelemetry/OpenTelemetryExtensions.cs index b7a1cf168..0a839781d 100644 --- a/src/api/Elastic.Documentation.Api.Infrastructure/OpenTelemetry/OpenTelemetryExtensions.cs +++ b/src/api/Elastic.Documentation.Api.Infrastructure/OpenTelemetry/OpenTelemetryExtensions.cs @@ -71,10 +71,13 @@ public static TracerProviderBuilder AddDocsApiTracing(this TracerProviderBuilder /// /// The web application builder /// The builder for chaining - public static TBuilder AddDocsApiOpenTelemetry( - this TBuilder builder) + public static TBuilder AddDocsApiOpenTelemetry(this TBuilder builder) where TBuilder : IHostApplicationBuilder { + var useOtlpExporter = !string.IsNullOrWhiteSpace(builder.Configuration["OTEL_EXPORTER_OTLP_ENDPOINT"]); + if (!useOtlpExporter) + return builder; + var options = new ElasticOpenTelemetryOptions { // In AOT mode, assembly scanning is not supported, so we skip it diff --git a/src/api/Elastic.Documentation.Api.Lambda/Program.cs b/src/api/Elastic.Documentation.Api.Lambda/Program.cs index a31227d91..abd16dff2 100644 --- a/src/api/Elastic.Documentation.Api.Lambda/Program.cs +++ b/src/api/Elastic.Documentation.Api.Lambda/Program.cs @@ -49,7 +49,9 @@ _ = app.UseDeveloperExceptionPage(); var v1 = app.MapGroup("/docs/_api/v1"); - v1.MapElasticDocsApiEndpoints(); + + var mapOtlpEndpoints = !string.IsNullOrWhiteSpace(builder.Configuration["OTEL_EXPORTER_OTLP_ENDPOINT"]); + v1.MapElasticDocsApiEndpoints(mapOtlpEndpoints); Console.WriteLine("API endpoints mapped"); Console.WriteLine("Application startup completed successfully"); diff --git a/src/tooling/docs-builder/Http/DocumentationWebHost.cs b/src/tooling/docs-builder/Http/DocumentationWebHost.cs index 3c8442fba..381c6f584 100644 --- a/src/tooling/docs-builder/Http/DocumentationWebHost.cs +++ b/src/tooling/docs-builder/Http/DocumentationWebHost.cs @@ -142,7 +142,8 @@ private void SetUpRoutes() var apiV1 = _webApplication.MapGroup("/docs/_api/v1"); #if DEBUG - apiV1.MapElasticDocsApiEndpoints(); + var mapOtlpEndpoints = !string.IsNullOrWhiteSpace(_webApplication.Configuration["OTEL_EXPORTER_OTLP_ENDPOINT"]); + apiV1.MapElasticDocsApiEndpoints(mapOtlpEndpoints); #endif _ = _webApplication.MapGet("{**slug}", (string slug, ReloadableGeneratorState holder, Cancel ctx) => diff --git a/src/tooling/docs-builder/Http/StaticWebHost.cs b/src/tooling/docs-builder/Http/StaticWebHost.cs index 131ce3271..f1e85072d 100644 --- a/src/tooling/docs-builder/Http/StaticWebHost.cs +++ b/src/tooling/docs-builder/Http/StaticWebHost.cs @@ -85,7 +85,8 @@ private void SetUpRoutes() var apiV1 = WebApplication.MapGroup("/docs/_api/v1"); #if DEBUG - apiV1.MapElasticDocsApiEndpoints(); + var mapOtlpEndpoints = !string.IsNullOrWhiteSpace(WebApplication.Configuration["OTEL_EXPORTER_OTLP_ENDPOINT"]); + apiV1.MapElasticDocsApiEndpoints(mapOtlpEndpoints); #endif } diff --git a/tests-integration/Elastic.Documentation.Api.IntegrationTests/EuidEnrichmentIntegrationTests.cs b/tests-integration/Elastic.Documentation.Api.IntegrationTests/EuidEnrichmentIntegrationTests.cs index df7c446dc..3dfd28bd3 100644 --- a/tests-integration/Elastic.Documentation.Api.IntegrationTests/EuidEnrichmentIntegrationTests.cs +++ b/tests-integration/Elastic.Documentation.Api.IntegrationTests/EuidEnrichmentIntegrationTests.cs @@ -17,8 +17,23 @@ namespace Elastic.Documentation.Api.IntegrationTests; /// Integration tests for euid cookie enrichment in OpenTelemetry traces and logging. /// Uses WebApplicationFactory to test the real API configuration with mocked AskAi services. /// -public class EuidEnrichmentIntegrationTests +public class EuidEnrichmentIntegrationTests : IAsyncLifetime { + private const string OtlpEndpoint = "http://localhost:4318"; + + public ValueTask InitializeAsync() + { + Environment.SetEnvironmentVariable("OTEL_EXPORTER_OTLP_ENDPOINT", OtlpEndpoint); + return ValueTask.CompletedTask; + } + + public ValueTask DisposeAsync() + { + GC.SuppressFinalize(this); + Environment.SetEnvironmentVariable("OTEL_EXPORTER_OTLP_ENDPOINT", null); + return ValueTask.CompletedTask; + } + /// /// Test that verifies euid cookie is added to both HTTP span and custom AskAi span, /// and appears in log entries - using the real API configuration. diff --git a/tests-integration/Elastic.Documentation.Api.IntegrationTests/OtlpProxyIntegrationTests.cs b/tests-integration/Elastic.Documentation.Api.IntegrationTests/OtlpProxyIntegrationTests.cs index f8fbd177d..7f32fcaf8 100644 --- a/tests-integration/Elastic.Documentation.Api.IntegrationTests/OtlpProxyIntegrationTests.cs +++ b/tests-integration/Elastic.Documentation.Api.IntegrationTests/OtlpProxyIntegrationTests.cs @@ -13,8 +13,23 @@ namespace Elastic.Documentation.Api.IntegrationTests; -public class OtlpProxyIntegrationTests +public class OtlpProxyIntegrationTests : IAsyncLifetime { + private const string OtlpEndpoint = "http://localhost:4318"; + + public ValueTask InitializeAsync() + { + Environment.SetEnvironmentVariable("OTEL_EXPORTER_OTLP_ENDPOINT", OtlpEndpoint); + return ValueTask.CompletedTask; + } + + public ValueTask DisposeAsync() + { + GC.SuppressFinalize(this); + Environment.SetEnvironmentVariable("OTEL_EXPORTER_OTLP_ENDPOINT", null); + return ValueTask.CompletedTask; + } + [Fact] public async Task OtlpProxyTracesEndpointForwardsToCorrectUrl() { @@ -71,7 +86,7 @@ public async Task OtlpProxyTracesEndpointForwardsToCorrectUrl() response.StatusCode.Should().Be(HttpStatusCode.NoContent); capturedRequest.Should().NotBeNull(); capturedRequest!.RequestUri.Should().NotBeNull(); - capturedRequest.RequestUri!.ToString().Should().Be("http://localhost:4318/v1/traces"); + capturedRequest.RequestUri!.ToString().Should().Be($"{OtlpEndpoint}/v1/traces"); capturedRequest.Method.Should().Be(HttpMethod.Post); capturedRequest.Content.Should().NotBeNull(); capturedRequest.Content!.Headers.ContentType!.MediaType.Should().Be("application/json"); @@ -131,7 +146,7 @@ public async Task OtlpProxyLogsEndpointForwardsToCorrectUrl() // Assert - verify the enum ToStringFast() generates "logs" (lowercase) response.StatusCode.Should().Be(HttpStatusCode.NoContent); capturedRequest.Should().NotBeNull(); - capturedRequest!.RequestUri!.ToString().Should().Be("http://localhost:4318/v1/logs"); + capturedRequest!.RequestUri!.ToString().Should().Be($"{OtlpEndpoint}/v1/logs"); // Cleanup mock response mockResponse.Dispose(); @@ -184,7 +199,7 @@ public async Task OtlpProxyMetricsEndpointForwardsToCorrectUrl() // Assert - verify the enum ToStringFast() generates "metrics" (lowercase) response.StatusCode.Should().Be(HttpStatusCode.NoContent); capturedRequest.Should().NotBeNull(); - capturedRequest!.RequestUri!.ToString().Should().Be("http://localhost:4318/v1/metrics"); + capturedRequest!.RequestUri!.ToString().Should().Be($"{OtlpEndpoint}/v1/metrics"); // Cleanup mock response mockResponse.Dispose();