From e7a55822d55c3b6f5b5d39da24d4ddad879067fd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Piotr=20Kie=C5=82kowicz?= Date: Wed, 8 May 2024 09:43:43 +0200 Subject: [PATCH 01/15] Drop AspNetCore tests --- OpenTelemetry.sln | 6 - .../AttributesExtensions.cs | 12 - .../BasicTests.cs | 1273 ----------------- .../DependencyInjectionConfigTests.cs | 54 - .../EventSourceTest.cs | 17 - ...stsCollectionsIsAccordingToTheSpecTests.cs | 171 --- .../MetricTests.cs | 419 ------ ...ry.Instrumentation.AspNetCore.Tests.csproj | 47 - .../RouteTests/README.md | 204 --- .../RouteTests/README.net6.0.md | 612 -------- .../RouteTests/README.net7.0.md | 654 --------- .../RouteTests/README.net8.0.md | 654 --------- .../RouteTests/RoutingTestCases.cs | 69 - .../RouteTests/RoutingTestCases.json | 211 --- .../RouteTests/RoutingTestFixture.cs | 108 -- .../RouteTests/RoutingTestResult.cs | 33 - .../RouteTests/RoutingTests.cs | 139 -- .../Controllers/AnotherAreaController.cs | 14 - .../ControllerForMyAreaController.cs | 16 - .../Controllers/AttributeRouteController.cs | 23 - .../ConventionalRouteController.cs | 17 - .../TestApplication/Pages/Index.cshtml | 2 - .../Pages/PageThatThrowsException.cshtml | 4 - .../RouteTests/TestApplication/RouteInfo.cs | 138 -- .../RouteInfoDiagnosticObserver.cs | 110 -- .../TestApplication/TestApplicationFactory.cs | 199 --- .../TestApplication/wwwroot/js/site.js | 4 - 27 files changed, 5210 deletions(-) delete mode 100644 test/OpenTelemetry.Instrumentation.AspNetCore.Tests/AttributesExtensions.cs delete mode 100644 test/OpenTelemetry.Instrumentation.AspNetCore.Tests/BasicTests.cs delete mode 100644 test/OpenTelemetry.Instrumentation.AspNetCore.Tests/DependencyInjectionConfigTests.cs delete mode 100644 test/OpenTelemetry.Instrumentation.AspNetCore.Tests/EventSourceTest.cs delete mode 100644 test/OpenTelemetry.Instrumentation.AspNetCore.Tests/IncomingRequestsCollectionsIsAccordingToTheSpecTests.cs delete mode 100644 test/OpenTelemetry.Instrumentation.AspNetCore.Tests/MetricTests.cs delete mode 100644 test/OpenTelemetry.Instrumentation.AspNetCore.Tests/OpenTelemetry.Instrumentation.AspNetCore.Tests.csproj delete mode 100644 test/OpenTelemetry.Instrumentation.AspNetCore.Tests/RouteTests/README.md delete mode 100644 test/OpenTelemetry.Instrumentation.AspNetCore.Tests/RouteTests/README.net6.0.md delete mode 100644 test/OpenTelemetry.Instrumentation.AspNetCore.Tests/RouteTests/README.net7.0.md delete mode 100644 test/OpenTelemetry.Instrumentation.AspNetCore.Tests/RouteTests/README.net8.0.md delete mode 100644 test/OpenTelemetry.Instrumentation.AspNetCore.Tests/RouteTests/RoutingTestCases.cs delete mode 100644 test/OpenTelemetry.Instrumentation.AspNetCore.Tests/RouteTests/RoutingTestCases.json delete mode 100644 test/OpenTelemetry.Instrumentation.AspNetCore.Tests/RouteTests/RoutingTestFixture.cs delete mode 100644 test/OpenTelemetry.Instrumentation.AspNetCore.Tests/RouteTests/RoutingTestResult.cs delete mode 100644 test/OpenTelemetry.Instrumentation.AspNetCore.Tests/RouteTests/RoutingTests.cs delete mode 100644 test/OpenTelemetry.Instrumentation.AspNetCore.Tests/RouteTests/TestApplication/Areas/AnotherArea/Controllers/AnotherAreaController.cs delete mode 100644 test/OpenTelemetry.Instrumentation.AspNetCore.Tests/RouteTests/TestApplication/Areas/MyArea/Controllers/ControllerForMyAreaController.cs delete mode 100644 test/OpenTelemetry.Instrumentation.AspNetCore.Tests/RouteTests/TestApplication/Controllers/AttributeRouteController.cs delete mode 100644 test/OpenTelemetry.Instrumentation.AspNetCore.Tests/RouteTests/TestApplication/Controllers/ConventionalRouteController.cs delete mode 100644 test/OpenTelemetry.Instrumentation.AspNetCore.Tests/RouteTests/TestApplication/Pages/Index.cshtml delete mode 100644 test/OpenTelemetry.Instrumentation.AspNetCore.Tests/RouteTests/TestApplication/Pages/PageThatThrowsException.cshtml delete mode 100644 test/OpenTelemetry.Instrumentation.AspNetCore.Tests/RouteTests/TestApplication/RouteInfo.cs delete mode 100644 test/OpenTelemetry.Instrumentation.AspNetCore.Tests/RouteTests/TestApplication/RouteInfoDiagnosticObserver.cs delete mode 100644 test/OpenTelemetry.Instrumentation.AspNetCore.Tests/RouteTests/TestApplication/TestApplicationFactory.cs delete mode 100644 test/OpenTelemetry.Instrumentation.AspNetCore.Tests/RouteTests/TestApplication/wwwroot/js/site.js diff --git a/OpenTelemetry.sln b/OpenTelemetry.sln index ef9480592f7..d78c6c78015 100644 --- a/OpenTelemetry.sln +++ b/OpenTelemetry.sln @@ -56,8 +56,6 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "OpenTelemetry.Api", "src\Op EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "OpenTelemetry.Instrumentation.AspNetCore", "src\OpenTelemetry.Instrumentation.AspNetCore\OpenTelemetry.Instrumentation.AspNetCore.csproj", "{752D2182-A351-41D8-99EE-DD363D7D5B43}" EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "OpenTelemetry.Instrumentation.AspNetCore.Tests", "test\OpenTelemetry.Instrumentation.AspNetCore.Tests\OpenTelemetry.Instrumentation.AspNetCore.Tests.csproj", "{2A47F6A8-63E5-4237-8046-94CAF321E797}" -EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "testdata", "testdata", "{77C7929A-2EED-4AA6-8705-B5C443C8AA0F}" EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "examples", "examples", "{E359BB2B-9AEC-497D-B321-7DF2450C3B8E}" @@ -371,10 +369,6 @@ Global {752D2182-A351-41D8-99EE-DD363D7D5B43}.Debug|Any CPU.Build.0 = Debug|Any CPU {752D2182-A351-41D8-99EE-DD363D7D5B43}.Release|Any CPU.ActiveCfg = Release|Any CPU {752D2182-A351-41D8-99EE-DD363D7D5B43}.Release|Any CPU.Build.0 = Release|Any CPU - {2A47F6A8-63E5-4237-8046-94CAF321E797}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {2A47F6A8-63E5-4237-8046-94CAF321E797}.Debug|Any CPU.Build.0 = Debug|Any CPU - {2A47F6A8-63E5-4237-8046-94CAF321E797}.Release|Any CPU.ActiveCfg = Release|Any CPU - {2A47F6A8-63E5-4237-8046-94CAF321E797}.Release|Any CPU.Build.0 = Release|Any CPU {AAC408FE-40EF-4479-97D9-697F2C1A0B28}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {AAC408FE-40EF-4479-97D9-697F2C1A0B28}.Debug|Any CPU.Build.0 = Debug|Any CPU {AAC408FE-40EF-4479-97D9-697F2C1A0B28}.Release|Any CPU.ActiveCfg = Release|Any CPU diff --git a/test/OpenTelemetry.Instrumentation.AspNetCore.Tests/AttributesExtensions.cs b/test/OpenTelemetry.Instrumentation.AspNetCore.Tests/AttributesExtensions.cs deleted file mode 100644 index 7ad43bdaea1..00000000000 --- a/test/OpenTelemetry.Instrumentation.AspNetCore.Tests/AttributesExtensions.cs +++ /dev/null @@ -1,12 +0,0 @@ -// Copyright The OpenTelemetry Authors -// SPDX-License-Identifier: Apache-2.0 - -namespace OpenTelemetry.Instrumentation.AspNetCore.Tests; - -internal static class AttributesExtensions -{ - public static object GetValue(this IEnumerable> attributes, string key) - { - return attributes.FirstOrDefault(kvp => kvp.Key == key).Value; - } -} diff --git a/test/OpenTelemetry.Instrumentation.AspNetCore.Tests/BasicTests.cs b/test/OpenTelemetry.Instrumentation.AspNetCore.Tests/BasicTests.cs deleted file mode 100644 index 03f6f4c162e..00000000000 --- a/test/OpenTelemetry.Instrumentation.AspNetCore.Tests/BasicTests.cs +++ /dev/null @@ -1,1273 +0,0 @@ -// Copyright The OpenTelemetry Authors -// SPDX-License-Identifier: Apache-2.0 - -using System.Diagnostics; -using System.Text.Json; -using Microsoft.AspNetCore.Builder; -using Microsoft.AspNetCore.Hosting; -using Microsoft.AspNetCore.Http; -using Microsoft.AspNetCore.Mvc.Testing; -using Microsoft.AspNetCore.TestHost; -using Microsoft.Extensions.Configuration; -using Microsoft.Extensions.DependencyInjection; -using Microsoft.Extensions.Hosting; -using Microsoft.Extensions.Logging; -using OpenTelemetry.Context.Propagation; -using OpenTelemetry.Instrumentation.AspNetCore.Implementation; -using OpenTelemetry.Tests; -using OpenTelemetry.Trace; -using TestApp.AspNetCore; -using TestApp.AspNetCore.Filters; -using Xunit; - -namespace OpenTelemetry.Instrumentation.AspNetCore.Tests; - -// See https://github.com/aspnet/Docs/tree/master/aspnetcore/test/integration-tests/samples/2.x/IntegrationTestsSample -public sealed class BasicTests - : IClassFixture>, IDisposable -{ - private readonly WebApplicationFactory factory; - private TracerProvider tracerProvider = null; - - public BasicTests(WebApplicationFactory factory) - { - this.factory = factory; - } - - [Fact] - public void AddAspNetCoreInstrumentation_BadArgs() - { - TracerProviderBuilder builder = null; - Assert.Throws(() => builder.AddAspNetCoreInstrumentation()); - } - - [Theory] - [InlineData(true)] - [InlineData(false)] - public async Task StatusIsUnsetOn200Response(bool disableLogging) - { - var exportedItems = new List(); - void ConfigureTestServices(IServiceCollection services) - { - this.tracerProvider = Sdk.CreateTracerProviderBuilder() - .AddAspNetCoreInstrumentation() - .AddInMemoryExporter(exportedItems) - .Build(); - } - - // Arrange - using (var client = this.factory - .WithWebHostBuilder(builder => - { - builder.ConfigureTestServices(ConfigureTestServices); - if (disableLogging) - { - builder.ConfigureLogging(loggingBuilder => loggingBuilder.ClearProviders()); - } - }) - .CreateClient()) - { - // Act - using var response = await client.GetAsync("/api/values"); - - // Assert - response.EnsureSuccessStatusCode(); // Status Code 200-299 - - WaitForActivityExport(exportedItems, 1); - } - - Assert.Single(exportedItems); - var activity = exportedItems[0]; - - Assert.Equal(200, activity.GetTagValue(SemanticConventions.AttributeHttpResponseStatusCode)); - Assert.Equal(ActivityStatusCode.Unset, activity.Status); - ValidateAspNetCoreActivity(activity, "/api/values"); - } - - [Theory] - [InlineData(true)] - [InlineData(false)] - public async Task SuccessfulTemplateControllerCallGeneratesASpan(bool shouldEnrich) - { - var exportedItems = new List(); - void ConfigureTestServices(IServiceCollection services) - { - this.tracerProvider = Sdk.CreateTracerProviderBuilder() - .AddAspNetCoreInstrumentation(options => - { - if (shouldEnrich) - { - options.EnrichWithHttpRequest = (activity, request) => { activity.SetTag("enrichedOnStart", "yes"); }; - options.EnrichWithHttpResponse = (activity, response) => { activity.SetTag("enrichedOnStop", "yes"); }; - } - }) - .AddInMemoryExporter(exportedItems) - .Build(); - } - - // Arrange - using (var client = this.factory - .WithWebHostBuilder(builder => - { - builder.ConfigureTestServices(ConfigureTestServices); - builder.ConfigureLogging(loggingBuilder => loggingBuilder.ClearProviders()); - }) - .CreateClient()) - { - // Act - using var response = await client.GetAsync("/api/values"); - - // Assert - response.EnsureSuccessStatusCode(); // Status Code 200-299 - - WaitForActivityExport(exportedItems, 1); - } - - Assert.Single(exportedItems); - var activity = exportedItems[0]; - - if (shouldEnrich) - { - Assert.NotEmpty(activity.Tags.Where(tag => tag.Key == "enrichedOnStart" && tag.Value == "yes")); - Assert.NotEmpty(activity.Tags.Where(tag => tag.Key == "enrichedOnStop" && tag.Value == "yes")); - } - - ValidateAspNetCoreActivity(activity, "/api/values"); - } - - [Fact] - public async Task SuccessfulTemplateControllerCallUsesParentContext() - { - var exportedItems = new List(); - var expectedTraceId = ActivityTraceId.CreateRandom(); - var expectedSpanId = ActivitySpanId.CreateRandom(); - - // Arrange - using (var testFactory = this.factory - .WithWebHostBuilder(builder => - { - builder.ConfigureTestServices(services => - { - this.tracerProvider = Sdk.CreateTracerProviderBuilder() - .AddAspNetCoreInstrumentation() - .AddInMemoryExporter(exportedItems) - .Build(); - }); - - builder.ConfigureLogging(loggingBuilder => loggingBuilder.ClearProviders()); - })) - { - using var client = testFactory.CreateClient(); - var request = new HttpRequestMessage(HttpMethod.Get, "/api/values/2"); - request.Headers.Add("traceparent", $"00-{expectedTraceId}-{expectedSpanId}-01"); - - // Act - var response = await client.SendAsync(request); - - // Assert - response.EnsureSuccessStatusCode(); // Status Code 200-299 - - WaitForActivityExport(exportedItems, 1); - } - - Assert.Single(exportedItems); - var activity = exportedItems[0]; - - Assert.Equal("Microsoft.AspNetCore.Hosting.HttpRequestIn", activity.OperationName); - - Assert.Equal(expectedTraceId, activity.Context.TraceId); - Assert.Equal(expectedSpanId, activity.ParentSpanId); - - ValidateAspNetCoreActivity(activity, "/api/values/2"); - } - - [Theory] - [InlineData(true)] - [InlineData(false)] - public async Task CustomPropagator(bool addSampler) - { - try - { - var exportedItems = new List(); - var expectedTraceId = ActivityTraceId.CreateRandom(); - var expectedSpanId = ActivitySpanId.CreateRandom(); - - var propagator = new CustomTextMapPropagator - { - TraceId = expectedTraceId, - SpanId = expectedSpanId, - }; - - // Arrange - using (var testFactory = this.factory - .WithWebHostBuilder(builder => - { - builder.ConfigureTestServices(services => - { - Sdk.SetDefaultTextMapPropagator(propagator); - var tracerProviderBuilder = Sdk.CreateTracerProviderBuilder(); - - if (addSampler) - { - tracerProviderBuilder - .SetSampler(new TestSampler(SamplingDecision.RecordAndSample, new Dictionary { { "SomeTag", "SomeKey" }, })); - } - - this.tracerProvider = tracerProviderBuilder - .AddAspNetCoreInstrumentation() - .AddInMemoryExporter(exportedItems) - .Build(); - }); - builder.ConfigureLogging(loggingBuilder => loggingBuilder.ClearProviders()); - })) - { - using var client = testFactory.CreateClient(); - using var response = await client.GetAsync("/api/values/2"); - response.EnsureSuccessStatusCode(); // Status Code 200-299 - - WaitForActivityExport(exportedItems, 1); - } - - Assert.Single(exportedItems); - var activity = exportedItems[0]; - - Assert.True(activity.Duration != TimeSpan.Zero); - - Assert.Equal(expectedTraceId, activity.Context.TraceId); - Assert.Equal(expectedSpanId, activity.ParentSpanId); - - ValidateAspNetCoreActivity(activity, "/api/values/2"); - } - finally - { - Sdk.SetDefaultTextMapPropagator(new CompositeTextMapPropagator(new TextMapPropagator[] - { - new TraceContextPropagator(), - new BaggagePropagator(), - })); - } - } - - [Fact] - public async Task RequestNotCollectedWhenFilterIsApplied() - { - var exportedItems = new List(); - - void ConfigureTestServices(IServiceCollection services) - { - this.tracerProvider = Sdk.CreateTracerProviderBuilder() - .AddAspNetCoreInstrumentation((opt) => opt.Filter = (ctx) => ctx.Request.Path != "/api/values/2") - .AddInMemoryExporter(exportedItems) - .Build(); - } - - // Arrange - using (var testFactory = this.factory - .WithWebHostBuilder(builder => - { - builder.ConfigureTestServices(ConfigureTestServices); - builder.ConfigureLogging(loggingBuilder => loggingBuilder.ClearProviders()); - })) - { - using var client = testFactory.CreateClient(); - - // Act - using var response1 = await client.GetAsync("/api/values"); - using var response2 = await client.GetAsync("/api/values/2"); - - // Assert - response1.EnsureSuccessStatusCode(); // Status Code 200-299 - response2.EnsureSuccessStatusCode(); // Status Code 200-299 - - WaitForActivityExport(exportedItems, 1); - } - - Assert.Single(exportedItems); - var activity = exportedItems[0]; - - ValidateAspNetCoreActivity(activity, "/api/values"); - } - - [Fact] - public async Task RequestNotCollectedWhenFilterThrowException() - { - var exportedItems = new List(); - - void ConfigureTestServices(IServiceCollection services) - { - this.tracerProvider = Sdk.CreateTracerProviderBuilder() - .AddAspNetCoreInstrumentation((opt) => opt.Filter = (ctx) => - { - if (ctx.Request.Path == "/api/values/2") - { - throw new Exception("from InstrumentationFilter"); - } - else - { - return true; - } - }) - .AddInMemoryExporter(exportedItems) - .Build(); - } - - // Arrange - using (var testFactory = this.factory - .WithWebHostBuilder(builder => - { - builder.ConfigureTestServices(ConfigureTestServices); - builder.ConfigureLogging(loggingBuilder => loggingBuilder.ClearProviders()); - })) - { - using var client = testFactory.CreateClient(); - - // Act - using (var inMemoryEventListener = new InMemoryEventListener(AspNetCoreInstrumentationEventSource.Log)) - { - using var response1 = await client.GetAsync("/api/values"); - using var response2 = await client.GetAsync("/api/values/2"); - - response1.EnsureSuccessStatusCode(); // Status Code 200-299 - response2.EnsureSuccessStatusCode(); // Status Code 200-299 - Assert.Single(inMemoryEventListener.Events.Where((e) => e.EventId == 3)); - } - - WaitForActivityExport(exportedItems, 1); - } - - // As InstrumentationFilter threw, we continue as if the - // InstrumentationFilter did not exist. - - Assert.Single(exportedItems); - var activity = exportedItems[0]; - ValidateAspNetCoreActivity(activity, "/api/values"); - } - - [Theory] - [InlineData(SamplingDecision.Drop)] - [InlineData(SamplingDecision.RecordOnly)] - [InlineData(SamplingDecision.RecordAndSample)] - public async Task ExtractContextIrrespectiveOfSamplingDecision(SamplingDecision samplingDecision) - { - try - { - var expectedTraceId = ActivityTraceId.CreateRandom(); - var expectedParentSpanId = ActivitySpanId.CreateRandom(); - var expectedTraceState = "rojo=1,congo=2"; - var activityContext = new ActivityContext(expectedTraceId, expectedParentSpanId, ActivityTraceFlags.Recorded, expectedTraceState, true); - var expectedBaggage = Baggage.SetBaggage("key1", "value1").SetBaggage("key2", "value2"); - Sdk.SetDefaultTextMapPropagator(new ExtractOnlyPropagator(activityContext, expectedBaggage)); - - // Arrange - using var testFactory = this.factory - .WithWebHostBuilder(builder => - { - builder.ConfigureTestServices(services => { this.tracerProvider = Sdk.CreateTracerProviderBuilder().SetSampler(new TestSampler(samplingDecision)).AddAspNetCoreInstrumentation().Build(); }); - builder.ConfigureLogging(loggingBuilder => loggingBuilder.ClearProviders()); - }); - using var client = testFactory.CreateClient(); - - // Test TraceContext Propagation - var request = new HttpRequestMessage(HttpMethod.Get, "/api/GetChildActivityTraceContext"); - var response = await client.SendAsync(request); - var childActivityTraceContext = JsonSerializer.Deserialize>(await response.Content.ReadAsStringAsync()); - - response.EnsureSuccessStatusCode(); - - Assert.Equal(expectedTraceId.ToString(), childActivityTraceContext["TraceId"]); - Assert.Equal(expectedTraceState, childActivityTraceContext["TraceState"]); - Assert.NotEqual(expectedParentSpanId.ToString(), childActivityTraceContext["ParentSpanId"]); // there is a new activity created in instrumentation therefore the ParentSpanId is different that what is provided in the headers - - // Test Baggage Context Propagation - request = new HttpRequestMessage(HttpMethod.Get, "/api/GetChildActivityBaggageContext"); - - response = await client.SendAsync(request); - var childActivityBaggageContext = JsonSerializer.Deserialize>(await response.Content.ReadAsStringAsync()); - - response.EnsureSuccessStatusCode(); - - Assert.Single(childActivityBaggageContext, item => item.Key == "key1" && item.Value == "value1"); - Assert.Single(childActivityBaggageContext, item => item.Key == "key2" && item.Value == "value2"); - } - finally - { - Sdk.SetDefaultTextMapPropagator(new CompositeTextMapPropagator(new TextMapPropagator[] - { - new TraceContextPropagator(), - new BaggagePropagator(), - })); - } - } - - [Fact] - public async Task ExtractContextIrrespectiveOfTheFilterApplied() - { - try - { - var expectedTraceId = ActivityTraceId.CreateRandom(); - var expectedParentSpanId = ActivitySpanId.CreateRandom(); - var expectedTraceState = "rojo=1,congo=2"; - var activityContext = new ActivityContext(expectedTraceId, expectedParentSpanId, ActivityTraceFlags.Recorded, expectedTraceState); - var expectedBaggage = Baggage.SetBaggage("key1", "value1").SetBaggage("key2", "value2"); - Sdk.SetDefaultTextMapPropagator(new ExtractOnlyPropagator(activityContext, expectedBaggage)); - - // Arrange - bool isFilterCalled = false; - using var testFactory = this.factory - .WithWebHostBuilder(builder => - { - builder.ConfigureTestServices(services => - { - this.tracerProvider = Sdk.CreateTracerProviderBuilder() - .AddAspNetCoreInstrumentation(options => - { - options.Filter = context => - { - isFilterCalled = true; - return false; - }; - }) - .Build(); - }); - builder.ConfigureLogging(loggingBuilder => loggingBuilder.ClearProviders()); - }); - using var client = testFactory.CreateClient(); - - // Test TraceContext Propagation - var request = new HttpRequestMessage(HttpMethod.Get, "/api/GetChildActivityTraceContext"); - var response = await client.SendAsync(request); - - // Ensure that filter was called - Assert.True(isFilterCalled); - - var childActivityTraceContext = JsonSerializer.Deserialize>(await response.Content.ReadAsStringAsync()); - - response.EnsureSuccessStatusCode(); - - Assert.Equal(expectedTraceId.ToString(), childActivityTraceContext["TraceId"]); - Assert.Equal(expectedTraceState, childActivityTraceContext["TraceState"]); - Assert.NotEqual(expectedParentSpanId.ToString(), childActivityTraceContext["ParentSpanId"]); // there is a new activity created in instrumentation therefore the ParentSpanId is different that what is provided in the headers - - // Test Baggage Context Propagation - request = new HttpRequestMessage(HttpMethod.Get, "/api/GetChildActivityBaggageContext"); - - response = await client.SendAsync(request); - var childActivityBaggageContext = JsonSerializer.Deserialize>(await response.Content.ReadAsStringAsync()); - - response.EnsureSuccessStatusCode(); - - Assert.Single(childActivityBaggageContext, item => item.Key == "key1" && item.Value == "value1"); - Assert.Single(childActivityBaggageContext, item => item.Key == "key2" && item.Value == "value2"); - } - finally - { - Sdk.SetDefaultTextMapPropagator(new CompositeTextMapPropagator(new TextMapPropagator[] - { - new TraceContextPropagator(), - new BaggagePropagator(), - })); - } - } - - [Fact] - public async Task BaggageIsNotClearedWhenActivityStopped() - { - int? baggageCountAfterStart = null; - int? baggageCountAfterStop = null; - using EventWaitHandle stopSignal = new EventWaitHandle(false, EventResetMode.ManualReset); - - void ConfigureTestServices(IServiceCollection services) - { - this.tracerProvider = Sdk.CreateTracerProviderBuilder() - .AddAspNetCoreInstrumentation( - new TestHttpInListener(new AspNetCoreTraceInstrumentationOptions()) - { - OnEventWrittenCallback = (name, payload) => - { - switch (name) - { - case HttpInListener.OnStartEvent: - { - baggageCountAfterStart = Baggage.Current.Count; - } - - break; - case HttpInListener.OnStopEvent: - { - baggageCountAfterStop = Baggage.Current.Count; - stopSignal.Set(); - } - - break; - } - }, - }) - .Build(); - } - - // Arrange - using (var client = this.factory - .WithWebHostBuilder(builder => - { - builder.ConfigureTestServices(ConfigureTestServices); - builder.ConfigureLogging(loggingBuilder => loggingBuilder.ClearProviders()); - }) - .CreateClient()) - { - using var request = new HttpRequestMessage(HttpMethod.Get, "/api/values"); - - request.Headers.TryAddWithoutValidation("baggage", "TestKey1=123,TestKey2=456"); - - // Act - using var response = await client.SendAsync(request); - } - - stopSignal.WaitOne(5000); - - // Assert - Assert.NotNull(baggageCountAfterStart); - Assert.Equal(2, baggageCountAfterStart); - Assert.NotNull(baggageCountAfterStop); - Assert.Equal(2, baggageCountAfterStop); - } - - [Theory] - [InlineData(SamplingDecision.Drop, false, false)] - [InlineData(SamplingDecision.RecordOnly, true, true)] - [InlineData(SamplingDecision.RecordAndSample, true, true)] - public async Task FilterAndEnrichAreOnlyCalledWhenSampled(SamplingDecision samplingDecision, bool shouldFilterBeCalled, bool shouldEnrichBeCalled) - { - bool filterCalled = false; - bool enrichWithHttpRequestCalled = false; - bool enrichWithHttpResponseCalled = false; - void ConfigureTestServices(IServiceCollection services) - { - this.tracerProvider = Sdk.CreateTracerProviderBuilder() - .SetSampler(new TestSampler(samplingDecision)) - .AddAspNetCoreInstrumentation(options => - { - options.Filter = (context) => - { - filterCalled = true; - return true; - }; - options.EnrichWithHttpRequest = (activity, request) => - { - enrichWithHttpRequestCalled = true; - }; - options.EnrichWithHttpResponse = (activity, request) => - { - enrichWithHttpResponseCalled = true; - }; - }) - .Build(); - } - - // Arrange - using var client = this.factory - .WithWebHostBuilder(builder => - { - builder.ConfigureTestServices(ConfigureTestServices); - builder.ConfigureLogging(loggingBuilder => loggingBuilder.ClearProviders()); - }) - .CreateClient(); - - // Act - using var response = await client.GetAsync("/api/values"); - - // Assert - Assert.Equal(shouldFilterBeCalled, filterCalled); - Assert.Equal(shouldEnrichBeCalled, enrichWithHttpRequestCalled); - Assert.Equal(shouldEnrichBeCalled, enrichWithHttpResponseCalled); - } - - [Fact] - public async Task ActivitiesStartedInMiddlewareShouldNotBeUpdated() - { - var exportedItems = new List(); - - var activitySourceName = "TestMiddlewareActivitySource"; - var activityName = "TestMiddlewareActivity"; - - void ConfigureTestServices(IServiceCollection services) - { - services.AddSingleton(new TestActivityMiddlewareImpl(activitySourceName, activityName)); - this.tracerProvider = Sdk.CreateTracerProviderBuilder() - .AddAspNetCoreInstrumentation() - .AddSource(activitySourceName) - .AddInMemoryExporter(exportedItems) - .Build(); - } - - // Arrange - using (var client = this.factory - .WithWebHostBuilder(builder => - { - builder.ConfigureTestServices(ConfigureTestServices); - builder.ConfigureLogging(loggingBuilder => loggingBuilder.ClearProviders()); - }) - .CreateClient()) - { - using var response = await client.GetAsync("/api/values/2"); - response.EnsureSuccessStatusCode(); - WaitForActivityExport(exportedItems, 2); - } - - Assert.Equal(2, exportedItems.Count); - - var middlewareActivity = exportedItems[0]; - - var aspnetcoreframeworkactivity = exportedItems[1]; - - // Middleware activity name should not be changed - Assert.Equal(ActivityKind.Internal, middlewareActivity.Kind); - Assert.Equal(activityName, middlewareActivity.OperationName); - Assert.Equal(activityName, middlewareActivity.DisplayName); - - // tag http.method should be added on activity started by asp.net core - Assert.Equal("GET", aspnetcoreframeworkactivity.GetTagValue(SemanticConventions.AttributeHttpRequestMethod) as string); - Assert.Equal("Microsoft.AspNetCore.Hosting.HttpRequestIn", aspnetcoreframeworkactivity.OperationName); - } - - [Theory] - [InlineData("CONNECT", "CONNECT", null, "CONNECT")] - [InlineData("DELETE", "DELETE", null, "DELETE")] - [InlineData("GET", "GET", null, "GET")] - [InlineData("PUT", "PUT", null, "PUT")] - [InlineData("HEAD", "HEAD", null, "HEAD")] - [InlineData("OPTIONS", "OPTIONS", null, "OPTIONS")] - [InlineData("PATCH", "PATCH", null, "PATCH")] - [InlineData("Get", "GET", "Get", "GET")] - [InlineData("POST", "POST", null, "POST")] - [InlineData("TRACE", "TRACE", null, "TRACE")] - [InlineData("CUSTOM", "_OTHER", "CUSTOM", "HTTP")] - public async Task HttpRequestMethodAndActivityDisplayIsSetAsPerSpec(string originalMethod, string expectedMethod, string expectedOriginalMethod, string expectedDisplayName) - { - var exportedItems = new List(); - - void ConfigureTestServices(IServiceCollection services) - { - this.tracerProvider = Sdk.CreateTracerProviderBuilder() - .AddAspNetCoreInstrumentation() - .AddInMemoryExporter(exportedItems) - .Build(); - } - - // Arrange - using var client = this.factory - .WithWebHostBuilder(builder => - { - builder.ConfigureTestServices(ConfigureTestServices); - builder.ConfigureLogging(loggingBuilder => loggingBuilder.ClearProviders()); - }) - .CreateClient(); - - var message = new HttpRequestMessage(); - - message.Method = new HttpMethod(originalMethod); - - try - { - using var response = await client.SendAsync(message); - response.EnsureSuccessStatusCode(); - } - catch - { - // ignore error. - } - - WaitForActivityExport(exportedItems, 1); - - Assert.Single(exportedItems); - - var activity = exportedItems[0]; - - Assert.Equal(expectedMethod, activity.GetTagValue(SemanticConventions.AttributeHttpRequestMethod)); - Assert.Equal(expectedOriginalMethod, activity.GetTagValue(SemanticConventions.AttributeHttpRequestMethodOriginal)); - Assert.Equal(expectedDisplayName, activity.DisplayName); - } - - [Fact] - public async Task ActivitiesStartedInMiddlewareBySettingHostActivityToNullShouldNotBeUpdated() - { - var exportedItems = new List(); - - var activitySourceName = "TestMiddlewareActivitySource"; - var activityName = "TestMiddlewareActivity"; - - // Arrange - using (var client = this.factory - .WithWebHostBuilder(builder => - { - builder.ConfigureTestServices((IServiceCollection services) => - { - services.AddSingleton(new TestNullHostActivityMiddlewareImpl(activitySourceName, activityName)); - services.AddOpenTelemetry() - .WithTracing(builder => builder - .AddAspNetCoreInstrumentation() - .AddSource(activitySourceName) - .AddInMemoryExporter(exportedItems)); - }); - builder.ConfigureLogging(loggingBuilder => loggingBuilder.ClearProviders()); - }) - .CreateClient()) - { - using var response = await client.GetAsync("/api/values/2"); - response.EnsureSuccessStatusCode(); - WaitForActivityExport(exportedItems, 2); - } - - Assert.Equal(2, exportedItems.Count); - - var middlewareActivity = exportedItems[0]; - - var aspnetcoreframeworkactivity = exportedItems[1]; - - // Middleware activity name should not be changed - Assert.Equal(ActivityKind.Internal, middlewareActivity.Kind); - Assert.Equal(activityName, middlewareActivity.OperationName); - Assert.Equal(activityName, middlewareActivity.DisplayName); - - // tag http.method should be added on activity started by asp.net core - Assert.Equal("GET", aspnetcoreframeworkactivity.GetTagValue(SemanticConventions.AttributeHttpRequestMethod) as string); - Assert.Equal("Microsoft.AspNetCore.Hosting.HttpRequestIn", aspnetcoreframeworkactivity.OperationName); - } - -#if NET7_0_OR_GREATER - [Fact] - public async Task UserRegisteredActivitySourceIsUsedForActivityCreationByAspNetCore() - { - var exportedItems = new List(); - void ConfigureTestServices(IServiceCollection services) - { - services.AddOpenTelemetry() - .WithTracing(builder => builder - .AddAspNetCoreInstrumentation() - .AddInMemoryExporter(exportedItems)); - - // Register ActivitySource here so that it will be used - // by ASP.NET Core to create activities - // https://github.com/dotnet/aspnetcore/blob/0e5cbf447d329a1e7d69932c3decd1c70a00fbba/src/Hosting/Hosting/src/Internal/WebHost.cs#L152 - services.AddSingleton(sp => new ActivitySource("UserRegisteredActivitySource")); - } - - // Arrange - using (var client = this.factory - .WithWebHostBuilder(builder => - { - builder.ConfigureTestServices(ConfigureTestServices); - builder.ConfigureLogging(loggingBuilder => loggingBuilder.ClearProviders()); - }) - .CreateClient()) - { - // Act - using var response = await client.GetAsync("/api/values"); - - // Assert - response.EnsureSuccessStatusCode(); // Status Code 200-299 - - WaitForActivityExport(exportedItems, 1); - } - - Assert.Single(exportedItems); - var activity = exportedItems[0]; - - Assert.Equal("UserRegisteredActivitySource", activity.Source.Name); - } -#endif - - [Theory] - [InlineData(1)] - [InlineData(2)] - public async Task ShouldExportActivityWithOneOrMoreExceptionFilters(int mode) - { - var exportedItems = new List(); - - // Arrange - using (var client = this.factory - .WithWebHostBuilder(builder => - { - builder.ConfigureTestServices( - (s) => this.ConfigureExceptionFilters(s, mode, ref exportedItems)); - builder.ConfigureLogging(loggingBuilder => loggingBuilder.ClearProviders()); - }) - .CreateClient()) - { - // Act - using var response = await client.GetAsync("/api/error"); - - WaitForActivityExport(exportedItems, 1); - } - - // Assert - AssertException(exportedItems); - } - - [Fact] - public async Task DiagnosticSourceCallbacksAreReceivedOnlyForSubscribedEvents() - { - int numberOfUnSubscribedEvents = 0; - int numberofSubscribedEvents = 0; - - this.tracerProvider = Sdk.CreateTracerProviderBuilder() - .AddAspNetCoreInstrumentation( - new TestHttpInListener(new AspNetCoreTraceInstrumentationOptions()) - { - OnEventWrittenCallback = (name, payload) => - { - switch (name) - { - case HttpInListener.OnStartEvent: - { - numberofSubscribedEvents++; - } - - break; - case HttpInListener.OnStopEvent: - { - numberofSubscribedEvents++; - } - - break; - default: - { - numberOfUnSubscribedEvents++; - } - - break; - } - }, - }) - .Build(); - - // Arrange - using (var client = this.factory - .WithWebHostBuilder(builder => - { - builder.ConfigureLogging(loggingBuilder => loggingBuilder.ClearProviders()); - }) - .CreateClient()) - { - using var request = new HttpRequestMessage(HttpMethod.Get, "/api/values"); - - // Act - using var response = await client.SendAsync(request); - } - - Assert.Equal(0, numberOfUnSubscribedEvents); - Assert.Equal(2, numberofSubscribedEvents); - } - - [Fact] - public async Task DiagnosticSourceExceptionCallbackIsReceivedForUnHandledException() - { - int numberOfUnSubscribedEvents = 0; - int numberofSubscribedEvents = 0; - int numberOfExceptionCallbacks = 0; - - this.tracerProvider = Sdk.CreateTracerProviderBuilder() - .AddAspNetCoreInstrumentation( - new TestHttpInListener(new AspNetCoreTraceInstrumentationOptions()) - { - OnEventWrittenCallback = (name, payload) => - { - switch (name) - { - case HttpInListener.OnStartEvent: - { - numberofSubscribedEvents++; - } - - break; - case HttpInListener.OnStopEvent: - { - numberofSubscribedEvents++; - } - - break; - - // TODO: Add test case for validating name for both the types - // of exception event. - case HttpInListener.OnUnhandledHostingExceptionEvent: - case HttpInListener.OnUnHandledDiagnosticsExceptionEvent: - { - numberofSubscribedEvents++; - numberOfExceptionCallbacks++; - } - - break; - default: - { - numberOfUnSubscribedEvents++; - } - - break; - } - }, - }) - .Build(); - - // Arrange - using (var client = this.factory - .WithWebHostBuilder(builder => - { - builder.ConfigureLogging(loggingBuilder => loggingBuilder.ClearProviders()); - }) - .CreateClient()) - { - try - { - using var request = new HttpRequestMessage(HttpMethod.Get, "/api/error"); - - // Act - using var response = await client.SendAsync(request); - } - catch - { - // ignore exception - } - } - - Assert.Equal(1, numberOfExceptionCallbacks); - Assert.Equal(0, numberOfUnSubscribedEvents); - Assert.Equal(3, numberofSubscribedEvents); - } - - [Fact] - public async Task DiagnosticSourceExceptionCallBackIsNotReceivedForExceptionsHandledInMiddleware() - { - int numberOfUnSubscribedEvents = 0; - int numberOfSubscribedEvents = 0; - int numberOfExceptionCallbacks = 0; - bool exceptionHandled = false; - - // configure SDK - this.tracerProvider = Sdk.CreateTracerProviderBuilder() - .AddAspNetCoreInstrumentation( - new TestHttpInListener(new AspNetCoreTraceInstrumentationOptions()) - { - OnEventWrittenCallback = (name, payload) => - { - switch (name) - { - case HttpInListener.OnStartEvent: - { - numberOfSubscribedEvents++; - } - - break; - case HttpInListener.OnStopEvent: - { - numberOfSubscribedEvents++; - } - - break; - - // TODO: Add test case for validating name for both the types - // of exception event. - case HttpInListener.OnUnhandledHostingExceptionEvent: - case HttpInListener.OnUnHandledDiagnosticsExceptionEvent: - { - numberOfSubscribedEvents++; - numberOfExceptionCallbacks++; - } - - break; - default: - { - numberOfUnSubscribedEvents++; - } - - break; - } - }, - }) - .Build(); - - TestMiddleware.Create(builder => builder - .UseExceptionHandler(handler => - handler.Run(async (ctx) => - { - exceptionHandled = true; - await ctx.Response.WriteAsync("handled"); - }))); - - using (var client = this.factory - .WithWebHostBuilder(builder => - { - builder.ConfigureLogging(loggingBuilder => loggingBuilder.ClearProviders()); - }) - .CreateClient()) - { - try - { - using var request = new HttpRequestMessage(HttpMethod.Get, "/api/error"); - using var response = await client.SendAsync(request); - } - catch - { - // ignore exception - } - } - - Assert.Equal(0, numberOfExceptionCallbacks); - Assert.Equal(0, numberOfUnSubscribedEvents); - Assert.Equal(2, numberOfSubscribedEvents); - Assert.True(exceptionHandled); - } - -#if NET6_0_OR_GREATER - [Fact] - public async Task NoSiblingActivityCreatedWhenTraceFlagsNone() - { - this.tracerProvider = Sdk.CreateTracerProviderBuilder() - .SetSampler(new AlwaysOnSampler()) - .AddAspNetCoreInstrumentation() - .Build(); - - using var testFactory = this.factory - .WithWebHostBuilder(builder => - { - builder.ConfigureTestServices(services => - { - this.tracerProvider = Sdk.CreateTracerProviderBuilder() - .AddAspNetCoreInstrumentation() - .Build(); - }); - - builder.ConfigureLogging(loggingBuilder => loggingBuilder.ClearProviders()); - }); - using var client = testFactory.CreateClient(); - var request = new HttpRequestMessage(HttpMethod.Get, "/api/GetActivityEquality"); - var traceId = ActivityTraceId.CreateRandom(); - var spanId = ActivitySpanId.CreateRandom(); - request.Headers.Add("traceparent", $"00-{traceId}-{spanId}-00"); - - var response = await client.SendAsync(request); - var result = bool.Parse(await response.Content.ReadAsStringAsync()); - - Assert.True(response.IsSuccessStatusCode); - - // Confirm that Activity.Current and IHttpActivityFeature activity are same - Assert.True(result); - } -#endif - - [Theory] - [InlineData("?a", "?a", false)] - [InlineData("?a=bdjdjh", "?a=Redacted", false)] - [InlineData("?a=b&", "?a=Redacted&", false)] - [InlineData("?c=b&", "?c=Redacted&", false)] - [InlineData("?c=a", "?c=Redacted", false)] - [InlineData("?a=b&c", "?a=Redacted&c", false)] - [InlineData("?a=b&c=1123456&", "?a=Redacted&c=Redacted&", false)] - [InlineData("?a=b&c=1&a1", "?a=Redacted&c=Redacted&a1", false)] - [InlineData("?a=ghgjgj&c=1deedd&a1=", "?a=Redacted&c=Redacted&a1=Redacted", false)] - [InlineData("?a=b&c=11&a1=&", "?a=Redacted&c=Redacted&a1=Redacted&", false)] - [InlineData("?c&c&c&", "?c&c&c&", false)] - [InlineData("?a&a&a&a", "?a&a&a&a", false)] - [InlineData("?&&&&&&&", "?&&&&&&&", false)] - [InlineData("?c", "?c", false)] - [InlineData("?a", "?a", true)] - [InlineData("?a=bdfdfdf", "?a=bdfdfdf", true)] - [InlineData("?a=b&", "?a=b&", true)] - [InlineData("?c=b&", "?c=b&", true)] - [InlineData("?c=a", "?c=a", true)] - [InlineData("?a=b&c", "?a=b&c", true)] - [InlineData("?a=b&c=111111&", "?a=b&c=111111&", true)] - [InlineData("?a=b&c=1&a1", "?a=b&c=1&a1", true)] - [InlineData("?a=b&c=1&a1=", "?a=b&c=1&a1=", true)] - [InlineData("?a=b123&c=11&a1=&", "?a=b123&c=11&a1=&", true)] - [InlineData("?c&c&c&", "?c&c&c&", true)] - [InlineData("?a&a&a&a", "?a&a&a&a", true)] - [InlineData("?&&&&&&&", "?&&&&&&&", true)] - [InlineData("?c", "?c", true)] - [InlineData("?c=%26&", "?c=Redacted&", false)] - public async Task ValidateUrlQueryRedaction(string urlQuery, string expectedUrlQuery, bool disableQueryRedaction) - { - var exportedItems = new List(); - - var configuration = new ConfigurationBuilder() - .AddInMemoryCollection(new Dictionary { ["OTEL_DOTNET_EXPERIMENTAL_ASPNETCORE_DISABLE_URL_QUERY_REDACTION"] = disableQueryRedaction.ToString() }) - .Build(); - - var path = "/api/values" + urlQuery; - - // Arrange - using var traceprovider = Sdk.CreateTracerProviderBuilder() - .ConfigureServices(services => services.AddSingleton(configuration)) - .AddAspNetCoreInstrumentation() - .AddInMemoryExporter(exportedItems) - .Build(); - - using (var client = this.factory - .WithWebHostBuilder(builder => - { - builder.ConfigureLogging(loggingBuilder => loggingBuilder.ClearProviders()); - }) - .CreateClient()) - { - try - { - using var response = await client.GetAsync(path); - } - catch (Exception) - { - // ignore errors - } - - WaitForActivityExport(exportedItems, 1); - } - - Assert.Single(exportedItems); - var activity = exportedItems[0]; - - Assert.Equal(expectedUrlQuery, activity.GetTagValue(SemanticConventions.AttributeUrlQuery)); - } - - public void Dispose() - { - this.tracerProvider?.Dispose(); - } - - private static void WaitForActivityExport(List exportedItems, int count) - { - // We need to let End callback execute as it is executed AFTER response was returned. - // In unit tests environment there may be a lot of parallel unit tests executed, so - // giving some breezing room for the End callback to complete - Assert.True(SpinWait.SpinUntil( - () => - { - Thread.Sleep(10); - return exportedItems.Count >= count; - }, - TimeSpan.FromSeconds(1))); - } - - private static void ValidateAspNetCoreActivity(Activity activityToValidate, string expectedHttpPath) - { - Assert.Equal(ActivityKind.Server, activityToValidate.Kind); -#if NET7_0_OR_GREATER - Assert.Equal(HttpInListener.AspNetCoreActivitySourceName, activityToValidate.Source.Name); - Assert.Empty(activityToValidate.Source.Version); -#else - Assert.Equal(HttpInListener.ActivitySourceName, activityToValidate.Source.Name); - Assert.Equal(HttpInListener.Version.ToString(), activityToValidate.Source.Version); -#endif - Assert.Equal(expectedHttpPath, activityToValidate.GetTagValue(SemanticConventions.AttributeUrlPath) as string); - } - - private static void AssertException(List exportedItems) - { - Assert.Single(exportedItems); - var activity = exportedItems[0]; - - var exMessage = "something's wrong!"; - Assert.Single(activity.Events); - Assert.Equal("System.Exception", activity.Events.First().Tags.FirstOrDefault(t => t.Key == SemanticConventions.AttributeExceptionType).Value); - Assert.Equal(exMessage, activity.Events.First().Tags.FirstOrDefault(t => t.Key == SemanticConventions.AttributeExceptionMessage).Value); - - ValidateAspNetCoreActivity(activity, "/api/error"); - } - - private void ConfigureExceptionFilters(IServiceCollection services, int mode, ref List exportedItems) - { - switch (mode) - { - case 1: - services.AddMvc(x => x.Filters.Add()); - break; - case 2: - services.AddMvc(x => x.Filters.Add()); - services.AddMvc(x => x.Filters.Add()); - break; - default: - break; - } - - this.tracerProvider = Sdk.CreateTracerProviderBuilder() - .AddAspNetCoreInstrumentation(x => x.RecordException = true) - .AddInMemoryExporter(exportedItems) - .Build(); - } - - private class ExtractOnlyPropagator(ActivityContext activityContext, Baggage baggage) : TextMapPropagator - { - private readonly ActivityContext activityContext = activityContext; - private readonly Baggage baggage = baggage; - - public override ISet Fields => throw new NotImplementedException(); - - public override PropagationContext Extract(PropagationContext context, T carrier, Func> getter) - { - return new PropagationContext(this.activityContext, this.baggage); - } - - public override void Inject(PropagationContext context, T carrier, Action setter) - { - throw new NotImplementedException(); - } - } - - private class TestSampler(SamplingDecision samplingDecision, IEnumerable> attributes = null) : Sampler - { - private readonly SamplingDecision samplingDecision = samplingDecision; - private readonly IEnumerable> attributes = attributes; - - public override SamplingResult ShouldSample(in SamplingParameters samplingParameters) - { - return new SamplingResult(this.samplingDecision, this.attributes); - } - } - - private class TestHttpInListener(AspNetCoreTraceInstrumentationOptions options) : HttpInListener(options) - { - public Action OnEventWrittenCallback; - - public override void OnEventWritten(string name, object payload) - { - base.OnEventWritten(name, payload); - - this.OnEventWrittenCallback?.Invoke(name, payload); - } - } - - private class TestNullHostActivityMiddlewareImpl(string activitySourceName, string activityName) : ActivityMiddleware.ActivityMiddlewareImpl - { - private readonly ActivitySource activitySource = new(activitySourceName); - private readonly string activityName = activityName; - private Activity activity; - - public override void PreProcess(HttpContext context) - { - // Setting the host activity i.e. activity started by asp.net core - // to null here will have no impact on middleware activity. - // This also means that asp.net core activity will not be found - // during OnEventWritten event. - Activity.Current = null; - this.activity = this.activitySource.StartActivity(this.activityName); - } - - public override void PostProcess(HttpContext context) - { - this.activity?.Stop(); - } - } - - private class TestActivityMiddlewareImpl(string activitySourceName, string activityName) : ActivityMiddleware.ActivityMiddlewareImpl - { - private readonly ActivitySource activitySource = new(activitySourceName); - private readonly string activityName = activityName; - private Activity activity; - - public override void PreProcess(HttpContext context) - { - this.activity = this.activitySource.StartActivity(this.activityName); - } - - public override void PostProcess(HttpContext context) - { - this.activity?.Stop(); - } - } -} diff --git a/test/OpenTelemetry.Instrumentation.AspNetCore.Tests/DependencyInjectionConfigTests.cs b/test/OpenTelemetry.Instrumentation.AspNetCore.Tests/DependencyInjectionConfigTests.cs deleted file mode 100644 index 766a81b40dd..00000000000 --- a/test/OpenTelemetry.Instrumentation.AspNetCore.Tests/DependencyInjectionConfigTests.cs +++ /dev/null @@ -1,54 +0,0 @@ -// Copyright The OpenTelemetry Authors -// SPDX-License-Identifier: Apache-2.0 - -using Microsoft.AspNetCore.Mvc.Testing; -using Microsoft.AspNetCore.TestHost; -using Microsoft.Extensions.DependencyInjection; -using Microsoft.Extensions.Options; -using OpenTelemetry.Metrics; -using OpenTelemetry.Trace; -using Xunit; - -namespace OpenTelemetry.Instrumentation.AspNetCore.Tests; - -public class DependencyInjectionConfigTests - : IClassFixture> -{ - private readonly WebApplicationFactory factory; - - public DependencyInjectionConfigTests(WebApplicationFactory factory) - { - this.factory = factory; - } - - [Theory] - [InlineData(null)] - [InlineData("CustomName")] - public void TestTracingOptionsDIConfig(string name) - { - name ??= Options.DefaultName; - - bool optionsPickedFromDI = false; - void ConfigureTestServices(IServiceCollection services) - { - services.AddOpenTelemetry() - .WithTracing(builder => builder - .AddAspNetCoreInstrumentation(name, configureAspNetCoreTraceInstrumentationOptions: null)); - - services.Configure(name, options => - { - optionsPickedFromDI = true; - }); - } - - // Arrange - using (var client = this.factory - .WithWebHostBuilder(builder => - builder.ConfigureTestServices(ConfigureTestServices)) - .CreateClient()) - { - } - - Assert.True(optionsPickedFromDI); - } -} diff --git a/test/OpenTelemetry.Instrumentation.AspNetCore.Tests/EventSourceTest.cs b/test/OpenTelemetry.Instrumentation.AspNetCore.Tests/EventSourceTest.cs deleted file mode 100644 index 5bae1e25f4d..00000000000 --- a/test/OpenTelemetry.Instrumentation.AspNetCore.Tests/EventSourceTest.cs +++ /dev/null @@ -1,17 +0,0 @@ -// Copyright The OpenTelemetry Authors -// SPDX-License-Identifier: Apache-2.0 - -using OpenTelemetry.Instrumentation.AspNetCore.Implementation; -using OpenTelemetry.Tests; -using Xunit; - -namespace OpenTelemetry.Instrumentation.AspNetCore.Tests; - -public class EventSourceTest -{ - [Fact] - public void EventSourceTest_AspNetCoreInstrumentationEventSource() - { - EventSourceTestHelper.MethodsAreImplementedConsistentlyWithTheirAttributes(AspNetCoreInstrumentationEventSource.Log); - } -} diff --git a/test/OpenTelemetry.Instrumentation.AspNetCore.Tests/IncomingRequestsCollectionsIsAccordingToTheSpecTests.cs b/test/OpenTelemetry.Instrumentation.AspNetCore.Tests/IncomingRequestsCollectionsIsAccordingToTheSpecTests.cs deleted file mode 100644 index d5777dbc6f3..00000000000 --- a/test/OpenTelemetry.Instrumentation.AspNetCore.Tests/IncomingRequestsCollectionsIsAccordingToTheSpecTests.cs +++ /dev/null @@ -1,171 +0,0 @@ -// Copyright The OpenTelemetry Authors -// SPDX-License-Identifier: Apache-2.0 - -using System.Diagnostics; -using Microsoft.AspNetCore.Hosting; -using Microsoft.AspNetCore.Http; -using Microsoft.AspNetCore.Http.Features; -using Microsoft.AspNetCore.Mvc.Testing; -using Microsoft.AspNetCore.TestHost; -using Microsoft.Extensions.DependencyInjection; -using Microsoft.Extensions.Logging; -using OpenTelemetry.Trace; -using TestApp.AspNetCore; -using Xunit; - -namespace OpenTelemetry.Instrumentation.AspNetCore.Tests; - -public class IncomingRequestsCollectionsIsAccordingToTheSpecTests - : IClassFixture> -{ - private readonly WebApplicationFactory factory; - - public IncomingRequestsCollectionsIsAccordingToTheSpecTests(WebApplicationFactory factory) - { - this.factory = factory; - } - - [Theory] - [InlineData("/api/values", null, "user-agent", 200, null)] - [InlineData("/api/values", null, null, 200, null)] - [InlineData("/api/exception", null, null, 503, null)] - [InlineData("/api/exception", null, null, 503, null, true)] - public async Task SuccessfulTemplateControllerCallGeneratesASpan_New( - string urlPath, - string query, - string userAgent, - int statusCode, - string reasonPhrase, - bool recordException = false) - { - var exportedItems = new List(); - - // Arrange - using (var client = this.factory - .WithWebHostBuilder(builder => - { - builder.ConfigureTestServices((IServiceCollection services) => - { - services.AddSingleton(new TestCallbackMiddlewareImpl(statusCode, reasonPhrase)); - services.AddOpenTelemetry() - .WithTracing(builder => builder - .AddAspNetCoreInstrumentation(options => - { - options.RecordException = recordException; - }) - .AddInMemoryExporter(exportedItems)); - }); - builder.ConfigureLogging(loggingBuilder => loggingBuilder.ClearProviders()); - }) - .CreateClient()) - { - try - { - if (!string.IsNullOrEmpty(userAgent)) - { - client.DefaultRequestHeaders.Add("User-Agent", userAgent); - } - - // Act - var path = urlPath; - if (query != null) - { - path += query; - } - - using var response = await client.GetAsync(path); - } - catch (Exception) - { - // ignore errors - } - - for (var i = 0; i < 10; i++) - { - if (exportedItems.Count == 1) - { - break; - } - - // We need to let End callback execute as it is executed AFTER response was returned. - // In unit tests environment there may be a lot of parallel unit tests executed, so - // giving some breezing room for the End callback to complete - await Task.Delay(TimeSpan.FromSeconds(1)); - } - } - - Assert.Single(exportedItems); - var activity = exportedItems[0]; - - Assert.Equal(ActivityKind.Server, activity.Kind); - Assert.Equal("localhost", activity.GetTagValue(SemanticConventions.AttributeServerAddress)); - Assert.Equal("GET", activity.GetTagValue(SemanticConventions.AttributeHttpRequestMethod)); - Assert.Equal("1.1", activity.GetTagValue(SemanticConventions.AttributeNetworkProtocolVersion)); - Assert.Equal("http", activity.GetTagValue(SemanticConventions.AttributeUrlScheme)); - Assert.Equal(urlPath, activity.GetTagValue(SemanticConventions.AttributeUrlPath)); - Assert.Equal(query, activity.GetTagValue(SemanticConventions.AttributeUrlQuery)); - Assert.Equal(statusCode, activity.GetTagValue(SemanticConventions.AttributeHttpResponseStatusCode)); - - if (statusCode == 503) - { - Assert.Equal(ActivityStatusCode.Error, activity.Status); - Assert.Equal("System.Exception", activity.GetTagValue(SemanticConventions.AttributeErrorType)); - } - else - { - Assert.Equal(ActivityStatusCode.Unset, activity.Status); - } - - // Instrumentation is not expected to set status description - // as the reason can be inferred from SemanticConventions.AttributeHttpStatusCode - Assert.Null(activity.StatusDescription); - - if (recordException) - { - Assert.Single(activity.Events); - Assert.Equal("exception", activity.Events.First().Name); - } - - ValidateTagValue(activity, SemanticConventions.AttributeUserAgentOriginal, userAgent); - - activity.Dispose(); - } - - private static void ValidateTagValue(Activity activity, string attribute, string expectedValue) - { - if (string.IsNullOrEmpty(expectedValue)) - { - Assert.Null(activity.GetTagValue(attribute)); - } - else - { - Assert.Equal(expectedValue, activity.GetTagValue(attribute)); - } - } - - public class TestCallbackMiddlewareImpl : CallbackMiddleware.CallbackMiddlewareImpl - { - private readonly int statusCode; - private readonly string reasonPhrase; - - public TestCallbackMiddlewareImpl(int statusCode, string reasonPhrase) - { - this.statusCode = statusCode; - this.reasonPhrase = reasonPhrase; - } - - public override async Task ProcessAsync(HttpContext context) - { - context.Response.StatusCode = this.statusCode; - context.Response.HttpContext.Features.Get().ReasonPhrase = this.reasonPhrase; - await context.Response.WriteAsync("empty"); - - if (context.Request.Path.Value.EndsWith("exception")) - { - throw new Exception("exception description"); - } - - return false; - } - } -} diff --git a/test/OpenTelemetry.Instrumentation.AspNetCore.Tests/MetricTests.cs b/test/OpenTelemetry.Instrumentation.AspNetCore.Tests/MetricTests.cs deleted file mode 100644 index ddec198dc4c..00000000000 --- a/test/OpenTelemetry.Instrumentation.AspNetCore.Tests/MetricTests.cs +++ /dev/null @@ -1,419 +0,0 @@ -// Copyright The OpenTelemetry Authors -// SPDX-License-Identifier: Apache-2.0 - -#if NET8_0_OR_GREATER -using System.Threading.RateLimiting; -using Microsoft.AspNetCore.Builder; -#endif -using Microsoft.AspNetCore.Hosting; -#if NET8_0_OR_GREATER -using Microsoft.AspNetCore.Http; -#endif -using Microsoft.AspNetCore.Mvc.Testing; -#if NET8_0_OR_GREATER -using Microsoft.AspNetCore.RateLimiting; -#endif -#if NET8_0_OR_GREATER -using Microsoft.Extensions.DependencyInjection; -using Microsoft.Extensions.Hosting; -#endif -using Microsoft.Extensions.Logging; -using OpenTelemetry.Metrics; -using OpenTelemetry.Trace; -using Xunit; - -namespace OpenTelemetry.Instrumentation.AspNetCore.Tests; - -public class MetricTests(WebApplicationFactory factory) - : IClassFixture>, IDisposable -{ - private const int StandardTagsCount = 6; - - private readonly WebApplicationFactory factory = factory; - private MeterProvider meterProvider; - - [Fact] - public void AddAspNetCoreInstrumentation_BadArgs() - { - MeterProviderBuilder builder = null; - Assert.Throws(builder.AddAspNetCoreInstrumentation); - } - -#if NET8_0_OR_GREATER - [Fact] - public async Task ValidateNet8MetricsAsync() - { - var exportedItems = new List(); - this.meterProvider = Sdk.CreateMeterProviderBuilder() - .AddAspNetCoreInstrumentation() - .AddInMemoryExporter(exportedItems) - .Build(); - - var builder = WebApplication.CreateBuilder(); - builder.WebHost.UseUrls("http://*:0"); - var app = builder.Build(); - - app.MapGet("/", () => "Hello"); - - _ = app.RunAsync(); - - var url = app.Urls.ToArray()[0]; - var portNumber = url.Substring(url.LastIndexOf(':') + 1); - - using var client = new HttpClient(); - var res = await client.GetAsync($"http://localhost:{portNumber}/"); - Assert.True(res.IsSuccessStatusCode); - - // We need to let metric callback execute as it is executed AFTER response was returned. - // In unit tests environment there may be a lot of parallel unit tests executed, so - // giving some breezing room for the callbacks to complete - await Task.Delay(TimeSpan.FromSeconds(1)); - - this.meterProvider.Dispose(); - - var requestDurationMetric = exportedItems - .Count(item => item.Name == "http.server.request.duration"); - - var activeRequestsMetric = exportedItems. - Count(item => item.Name == "http.server.active_requests"); - - var routeMatchingMetric = exportedItems. - Count(item => item.Name == "aspnetcore.routing.match_attempts"); - - var kestrelActiveConnectionsMetric = exportedItems. - Count(item => item.Name == "kestrel.active_connections"); - - var kestrelQueuedConnectionMetric = exportedItems. - Count(item => item.Name == "kestrel.queued_connections"); - - Assert.Equal(1, requestDurationMetric); - Assert.Equal(1, activeRequestsMetric); - Assert.Equal(1, routeMatchingMetric); - Assert.Equal(1, kestrelActiveConnectionsMetric); - Assert.Equal(1, kestrelQueuedConnectionMetric); - - // TODO - // kestrel.queued_requests - // kestrel.upgraded_connections - // kestrel.rejected_connections - // kestrel.tls_handshake.duration - // kestrel.active_tls_handshakes - - await app.DisposeAsync(); - } - - [Fact] - public async Task ValidateNet8RateLimitingMetricsAsync() - { - var exportedItems = new List(); - - void ConfigureTestServices(IServiceCollection services) - { - this.meterProvider = Sdk.CreateMeterProviderBuilder() - .AddAspNetCoreInstrumentation() - .AddInMemoryExporter(exportedItems) - .Build(); - - services.AddRateLimiter(_ => _ - .AddFixedWindowLimiter(policyName: "fixed", options => - { - options.PermitLimit = 4; - options.Window = TimeSpan.FromSeconds(12); - options.QueueProcessingOrder = QueueProcessingOrder.OldestFirst; - options.QueueLimit = 2; - })); - } - - var builder = WebApplication.CreateBuilder(); - builder.WebHost.UseUrls("http://*:0"); - ConfigureTestServices(builder.Services); - - builder.Logging.ClearProviders(); - var app = builder.Build(); - - app.UseRateLimiter(); - - static string GetTicks() => (DateTime.Now.Ticks & 0x11111).ToString("00000"); - - app.MapGet("/", () => Results.Ok($"Hello {GetTicks()}")) - .RequireRateLimiting("fixed"); - - _ = app.RunAsync(); - - var url = app.Urls.ToArray()[0]; - var portNumber = url.Substring(url.LastIndexOf(':') + 1); - - using var client = new HttpClient(); - var res = await client.GetAsync($"http://localhost:{portNumber}/"); - Assert.NotNull(res); - - // We need to let metric callback execute as it is executed AFTER response was returned. - // In unit tests environment there may be a lot of parallel unit tests executed, so - // giving some breezing room for the callbacks to complete - await Task.Delay(TimeSpan.FromSeconds(1)); - - this.meterProvider.Dispose(); - - var activeRequestLeasesMetric = exportedItems - .Where(item => item.Name == "aspnetcore.rate_limiting.active_request_leases") - .ToArray(); - - var requestLeaseDurationMetric = exportedItems. - Where(item => item.Name == "aspnetcore.rate_limiting.request_lease.duration") - .ToArray(); - - var limitingRequestsMetric = exportedItems. - Where(item => item.Name == "aspnetcore.rate_limiting.requests") - .ToArray(); - - Assert.Single(activeRequestLeasesMetric); - Assert.Single(requestLeaseDurationMetric); - Assert.Single(limitingRequestsMetric); - - // TODO - // aspnetcore.rate_limiting.request.time_in_queue - // aspnetcore.rate_limiting.queued_requests - - await app.DisposeAsync(); - } -#endif - - [Theory] - [InlineData("/api/values/2", "api/Values/{id}", null, 200)] - [InlineData("/api/Error", "api/Error", "System.Exception", 500)] - public async Task RequestMetricIsCaptured(string api, string expectedRoute, string expectedErrorType, int expectedStatusCode) - { - var metricItems = new List(); - - this.meterProvider = Sdk.CreateMeterProviderBuilder() - .AddAspNetCoreInstrumentation() - .AddInMemoryExporter(metricItems) - .Build(); - - using (var client = this.factory - .WithWebHostBuilder(builder => - { - builder.ConfigureLogging(loggingBuilder => loggingBuilder.ClearProviders()); - }) - .CreateClient()) - { - try - { - using var response = await client.GetAsync(api); - response.EnsureSuccessStatusCode(); - } - catch - { - // ignore error. - } - } - - // We need to let End callback execute as it is executed AFTER response was returned. - // In unit tests environment there may be a lot of parallel unit tests executed, so - // giving some breezing room for the End callback to complete - await Task.Delay(TimeSpan.FromSeconds(1)); - - this.meterProvider.Dispose(); - - var requestMetrics = metricItems - .Where(item => item.Name == "http.server.request.duration") - .ToArray(); - - var metric = Assert.Single(requestMetrics); - - Assert.Equal("s", metric.Unit); - var metricPoints = GetMetricPoints(metric); - Assert.Single(metricPoints); - - AssertMetricPoints( - metricPoints: metricPoints, - expectedRoutes: new List { expectedRoute }, - expectedErrorType, - expectedStatusCode, - expectedTagsCount: expectedErrorType == null ? 5 : 6); - } - - [Theory] - [InlineData("CONNECT", "CONNECT")] - [InlineData("DELETE", "DELETE")] - [InlineData("GET", "GET")] - [InlineData("PUT", "PUT")] - [InlineData("HEAD", "HEAD")] - [InlineData("OPTIONS", "OPTIONS")] - [InlineData("PATCH", "PATCH")] - [InlineData("Get", "GET")] - [InlineData("POST", "POST")] - [InlineData("TRACE", "TRACE")] - [InlineData("CUSTOM", "_OTHER")] - public async Task HttpRequestMethodIsCapturedAsPerSpec(string originalMethod, string expectedMethod) - { - var metricItems = new List(); - - this.meterProvider = Sdk.CreateMeterProviderBuilder() - .AddAspNetCoreInstrumentation() - .AddInMemoryExporter(metricItems) - .Build(); - - using var client = this.factory - .WithWebHostBuilder(builder => - { - builder.ConfigureLogging(loggingBuilder => loggingBuilder.ClearProviders()); - }) - .CreateClient(); - - var message = new HttpRequestMessage(); - message.Method = new HttpMethod(originalMethod); - - try - { - using var response = await client.SendAsync(message); - } - catch - { - // ignore error. - } - - // We need to let End callback execute as it is executed AFTER response was returned. - // In unit tests environment there may be a lot of parallel unit tests executed, so - // giving some breezing room for the End callback to complete - await Task.Delay(TimeSpan.FromSeconds(1)); - - this.meterProvider.Dispose(); - - var requestMetrics = metricItems - .Where(item => item.Name == "http.server.request.duration") - .ToArray(); - - var metric = Assert.Single(requestMetrics); - - Assert.Equal("s", metric.Unit); - var metricPoints = GetMetricPoints(metric); - Assert.Single(metricPoints); - - var mp = metricPoints[0]; - - // Inspect Metric Attributes - var attributes = new Dictionary(); - foreach (var tag in mp.Tags) - { - attributes[tag.Key] = tag.Value; - } - - Assert.Contains(attributes, kvp => kvp.Key == SemanticConventions.AttributeHttpRequestMethod && kvp.Value.ToString() == expectedMethod); - - Assert.DoesNotContain(attributes, t => t.Key == SemanticConventions.AttributeHttpRequestMethodOriginal); - } - - public void Dispose() - { - this.meterProvider?.Dispose(); - GC.SuppressFinalize(this); - } - - private static List GetMetricPoints(Metric metric) - { - Assert.NotNull(metric); - Assert.True(metric.MetricType == MetricType.Histogram); - var metricPoints = new List(); - foreach (var p in metric.GetMetricPoints()) - { - metricPoints.Add(p); - } - - return metricPoints; - } - - private static void AssertMetricPoints( - List metricPoints, - List expectedRoutes, - string expectedErrorType, - int expectedStatusCode, - int expectedTagsCount) - { - // Assert that one MetricPoint exists for each ExpectedRoute - foreach (var expectedRoute in expectedRoutes) - { - MetricPoint? metricPoint = null; - - foreach (var mp in metricPoints) - { - foreach (var tag in mp.Tags) - { - if (tag.Key == SemanticConventions.AttributeHttpRoute && tag.Value.ToString() == expectedRoute) - { - metricPoint = mp; - } - } - } - - if (metricPoint.HasValue) - { - AssertMetricPoint(metricPoint.Value, expectedStatusCode, expectedRoute, expectedErrorType, expectedTagsCount); - } - else - { - Assert.Fail($"A metric for route '{expectedRoute}' was not found"); - } - } - } - - private static void AssertMetricPoint( - MetricPoint metricPoint, - int expectedStatusCode, - string expectedRoute, - string expectedErrorType, - int expectedTagsCount) - { - var count = metricPoint.GetHistogramCount(); - var sum = metricPoint.GetHistogramSum(); - - Assert.Equal(1L, count); - Assert.True(sum > 0); - - var attributes = new KeyValuePair[metricPoint.Tags.Count]; - int i = 0; - foreach (var tag in metricPoint.Tags) - { - attributes[i++] = tag; - } - - // Inspect Attributes - Assert.Equal(expectedTagsCount, attributes.Length); - - var method = new KeyValuePair(SemanticConventions.AttributeHttpRequestMethod, "GET"); - var scheme = new KeyValuePair(SemanticConventions.AttributeUrlScheme, "http"); - var statusCode = new KeyValuePair(SemanticConventions.AttributeHttpResponseStatusCode, expectedStatusCode); - var flavor = new KeyValuePair(SemanticConventions.AttributeNetworkProtocolVersion, "1.1"); - var route = new KeyValuePair(SemanticConventions.AttributeHttpRoute, expectedRoute); - Assert.Contains(method, attributes); - Assert.Contains(scheme, attributes); - Assert.Contains(statusCode, attributes); - Assert.Contains(flavor, attributes); - Assert.Contains(route, attributes); - - if (expectedErrorType != null) - { - var errorType = new KeyValuePair(SemanticConventions.AttributeErrorType, expectedErrorType); - - Assert.Contains(errorType, attributes); - } - - // Inspect Histogram Bounds - var histogramBuckets = metricPoint.GetHistogramBuckets(); - var histogramBounds = new List(); - foreach (var t in histogramBuckets) - { - histogramBounds.Add(t.ExplicitBound); - } - - // TODO: Remove the check for the older bounds once 1.7.0 is released. This is a temporary fix for instrumentation libraries CI workflow. - - var expectedHistogramBoundsOld = new List { 0, 0.005, 0.01, 0.025, 0.05, 0.075, 0.1, 0.25, 0.5, 0.75, 1, 2.5, 5, 7.5, 10, double.PositiveInfinity }; - var expectedHistogramBoundsNew = new List { 0.005, 0.01, 0.025, 0.05, 0.075, 0.1, 0.25, 0.5, 0.75, 1, 2.5, 5, 7.5, 10, double.PositiveInfinity }; - - var histogramBoundsMatchCorrectly = Enumerable.SequenceEqual(expectedHistogramBoundsOld, histogramBounds) || - Enumerable.SequenceEqual(expectedHistogramBoundsNew, histogramBounds); - - Assert.True(histogramBoundsMatchCorrectly); - } -} diff --git a/test/OpenTelemetry.Instrumentation.AspNetCore.Tests/OpenTelemetry.Instrumentation.AspNetCore.Tests.csproj b/test/OpenTelemetry.Instrumentation.AspNetCore.Tests/OpenTelemetry.Instrumentation.AspNetCore.Tests.csproj deleted file mode 100644 index 26722a5dd51..00000000000 --- a/test/OpenTelemetry.Instrumentation.AspNetCore.Tests/OpenTelemetry.Instrumentation.AspNetCore.Tests.csproj +++ /dev/null @@ -1,47 +0,0 @@ - - - Unit test project for OpenTelemetry ASP.NET Core instrumentation - $(TargetFrameworksForAspNetCoreTests) - - disable - - - - - - - - - runtime; build; native; contentfiles; analyzers - - - - - - - - - - - - - - - - - - - - - - - - - - - - RoutingTestCases.json - Always - - - diff --git a/test/OpenTelemetry.Instrumentation.AspNetCore.Tests/RouteTests/README.md b/test/OpenTelemetry.Instrumentation.AspNetCore.Tests/RouteTests/README.md deleted file mode 100644 index 38ae9f93fd9..00000000000 --- a/test/OpenTelemetry.Instrumentation.AspNetCore.Tests/RouteTests/README.md +++ /dev/null @@ -1,204 +0,0 @@ -# ASP.NET Core `http.route` tests - -This folder contains a test suite that validates the instrumentation produces -the expected `http.route` attribute on both the activity and metric it emits. -When available, the `http.route` is also a required component of the -`Activity.DisplayName`. - -The test suite covers a variety of different routing scenarios available for -ASP.NET Core: - -* [Conventional routing](https://learn.microsoft.com/aspnet/core/mvc/controllers/routing#conventional-routing) -* [Conventional routing using areas](https://learn.microsoft.com/aspnet/core/mvc/controllers/routing#areas) -* [Attribute routing](https://learn.microsoft.com/aspnet/core/mvc/controllers/routing#attribute-routing-for-rest-apis) -* [Razor pages](https://learn.microsoft.com/aspnet/core/razor-pages/razor-pages-conventions) -* [Minimal APIs](https://learn.microsoft.com/aspnet/core/fundamentals/minimal-apis/route-handlers) - -The individual test cases are defined in RoutingTestCases.json. - -The test suite is unique in that, when run, it generates README files for each -target framework which aids in documenting how the instrumentation behaves for -each test case. These files are source-controlled, so if the behavior of the -instrumentation changes, the README files will be updated to reflect the change. - -* [.NET 6](./README.net6.0.md) -* [.NET 7](./README.net7.0.md) -* [.NET 8](./README.net8.0.md) - -For each test case a request is made to an ASP.NET Core application with a -particular routing configuration. ASP.NET Core offers a -[variety of APIs](#aspnet-core-apis-for-retrieving-route-information) for -retrieving the route information of a given request. The README files include -detailed information documenting the route information available using the -various APIs in each test case. For example, here is the detailed result -generated for a test case: - -```json -{ - "IdealHttpRoute": "ConventionalRoute/ActionWithStringParameter/{id?}", - "ActivityDisplayName": "/ConventionalRoute/ActionWithStringParameter/2", - "ActivityHttpRoute": "", - "MetricHttpRoute": "{controller=ConventionalRoute}/{action=Default}/{id?}", - "RouteInfo": { - "HttpMethod": "GET", - "Path": "/ConventionalRoute/ActionWithStringParameter/2?num=3", - "RoutePattern.RawText": "{controller=ConventionalRoute}/{action=Default}/{id?}", - "IRouteDiagnosticsMetadata.Route": null, - "HttpContext.GetRouteData()": { - "controller": "ConventionalRoute", - "action": "ActionWithStringParameter", - "id": "2" - }, - "ActionDescriptor": { - "AttributeRouteInfo.Template": null, - "Parameters": [ - "id", - "num" - ], - "ControllerActionDescriptor": { - "ControllerName": "ConventionalRoute", - "ActionName": "ActionWithStringParameter" - }, - "PageActionDescriptor": null - } - } -} -``` - -> [!NOTE] -> The test result currently includes an `IdealHttpRoute` property. This is -> temporary, and is meant to drive a conversation to determine the best way -> for generating the `http.route` attribute under different routing scenarios. -> In the example above, the path invoked is -> `/ConventionalRoute/ActionWithStringParameter/2?num=3`. Currently, we see -> that the `http.route` attribute on the metric emitted is -> `{controller=ConventionalRoute}/{action=Default}/{id?}` which was derived -> using `RoutePattern.RawText`. This is not ideal -> because the route template does not include the actual action that was -> invoked `ActionWithStringParameter`. The invoked action could be derived -> using either the `ControllerActionDescriptor` -> or `HttpContext.GetRouteData()`. - -## ASP.NET Core APIs for retrieving route information - -Included below are short snippets illustrating the use of the various -APIs available for retrieving route information. - -### Retrieving the route template - -The route template can be obtained from `HttpContext` by retrieving the -`RouteEndpoint` using the following two APIs. - -For attribute routing and minimal API scenarios, using the route template alone -is sufficient for deriving `http.route` in all test cases. - -The route template does not well describe the `http.route` in conventional -routing and some Razor page scenarios. - -#### [RoutePattern.RawText](https://learn.microsoft.com/dotnet/api/microsoft.aspnetcore.routing.patterns.routepattern.rawtext) - -```csharp -(httpContext.GetEndpoint() as RouteEndpoint)?.RoutePattern.RawText; -``` - -#### [IRouteDiagnosticsMetadata.Route](https://learn.microsoft.com/dotnet/api/microsoft.aspnetcore.http.metadata.iroutediagnosticsmetadata.route) - -This API was introduced in .NET 8. - -```csharp -httpContext.GetEndpoint()?.Metadata.GetMetadata()?.Route; -``` - -### RouteData - -`RouteData` can be retrieved from `HttpContext` using the `GetRouteData()` -extension method. The values obtained from `RouteData` identify the controller/ -action or Razor page invoked by the request. - -#### [HttpContext.GetRouteData()](https://learn.microsoft.com/dotnet/api/microsoft.aspnetcore.routing.routinghttpcontextextensions.getroutedata) - -```csharp -foreach (var value in httpContext.GetRouteData().Values) -{ - Console.WriteLine($"{value.Key} = {value.Value?.ToString()}"); -} -``` - -For example, the above code produces something like: - -```text -controller = ConventionalRoute -action = ActionWithStringParameter -id = 2 -``` - -### Information from the ActionDescriptor - -For requests that invoke an action or Razor page, the `ActionDescriptor` can -be used to access route information. - -#### [AttributeRouteInfo.Template](https://learn.microsoft.com/dotnet/api/microsoft.aspnetcore.mvc.routing.attributerouteinfo.template) - -The `AttributeRouteInfo.Template` is equivalent to using -[other APIs for retrieving the route template](#retrieving-the-route-template) -when using attribute routing. For conventional routing and Razor pages it will -be `null`. - -```csharp -actionDescriptor.AttributeRouteInfo?.Template; -``` - -#### [ControllerActionDescriptor](https://learn.microsoft.com/dotnet/api/microsoft.aspnetcore.mvc.controllers.controlleractiondescriptor) - -For requests that invoke an action on a controller, the `ActionDescriptor` -will be of type `ControllerActionDescriptor` which includes the controller and -action name. - -```csharp -(actionDescriptor as ControllerActionDescriptor)?.ControllerName; -(actionDescriptor as ControllerActionDescriptor)?.ActionName; -``` - -#### [PageActionDescriptor](https://learn.microsoft.com/dotnet/api/microsoft.aspnetcore.mvc.razorpages.pageactiondescriptor) - -For requests that invoke a Razor page, the `ActionDescriptor` -will be of type `PageActionDescriptor` which includes the path to the invoked -page. - -```csharp -(actionDescriptor as PageActionDescriptor)?.RelativePath; -(actionDescriptor as PageActionDescriptor)?.ViewEnginePath; -``` - -#### [Parameters](https://learn.microsoft.com/dotnet/api/microsoft.aspnetcore.mvc.abstractions.actiondescriptor.parameters#microsoft-aspnetcore-mvc-abstractions-actiondescriptor-parameters) - -The `ActionDescriptor.Parameters` property is interesting because it describes -the actual parameters (type and name) of an invoked action method. Some APM -products use `ActionDescriptor.Parameters` to more precisely describe the -method an endpoint invokes since not all parameters may be present in the -route template. - -Consider the following action method: - -```csharp -public IActionResult SomeActionMethod(string id, int num) { ... } -``` - -Using conventional routing assuming a default route template -`{controller=ConventionalRoute}/{action=Default}/{id?}`, the `SomeActionMethod` -may match this route template. The route template describes the `id` parameter -but not the `num` parameter. - -```csharp -foreach (var parameter in actionDescriptor.Parameters) -{ - Console.WriteLine($"{parameter.Name}"); -} -``` - -The above code produces: - -```text -id -num -``` diff --git a/test/OpenTelemetry.Instrumentation.AspNetCore.Tests/RouteTests/README.net6.0.md b/test/OpenTelemetry.Instrumentation.AspNetCore.Tests/RouteTests/README.net6.0.md deleted file mode 100644 index 6582c757155..00000000000 --- a/test/OpenTelemetry.Instrumentation.AspNetCore.Tests/RouteTests/README.net6.0.md +++ /dev/null @@ -1,612 +0,0 @@ -# Test results for ASP.NET Core 6 - -| http.route | App | Test Name | -| - | - | - | -| :broken_heart: | ConventionalRouting | [Root path](#conventionalrouting-root-path) | -| :broken_heart: | ConventionalRouting | [Non-default action with route parameter and query string](#conventionalrouting-non-default-action-with-route-parameter-and-query-string) | -| :broken_heart: | ConventionalRouting | [Non-default action with query string](#conventionalrouting-non-default-action-with-query-string) | -| :green_heart: | ConventionalRouting | [Not Found (404)](#conventionalrouting-not-found-404) | -| :green_heart: | ConventionalRouting | [Route template with parameter constraint](#conventionalrouting-route-template-with-parameter-constraint) | -| :green_heart: | ConventionalRouting | [Path that does not match parameter constraint](#conventionalrouting-path-that-does-not-match-parameter-constraint) | -| :broken_heart: | ConventionalRouting | [Area using `area:exists`, default controller/action](#conventionalrouting-area-using-areaexists-default-controlleraction) | -| :broken_heart: | ConventionalRouting | [Area using `area:exists`, non-default action](#conventionalrouting-area-using-areaexists-non-default-action) | -| :broken_heart: | ConventionalRouting | [Area w/o `area:exists`, default controller/action](#conventionalrouting-area-wo-areaexists-default-controlleraction) | -| :green_heart: | AttributeRouting | [Default action](#attributerouting-default-action) | -| :green_heart: | AttributeRouting | [Action without parameter](#attributerouting-action-without-parameter) | -| :green_heart: | AttributeRouting | [Action with parameter](#attributerouting-action-with-parameter) | -| :green_heart: | AttributeRouting | [Action with parameter before action name in template](#attributerouting-action-with-parameter-before-action-name-in-template) | -| :green_heart: | AttributeRouting | [Action invoked resulting in 400 Bad Request](#attributerouting-action-invoked-resulting-in-400-bad-request) | -| :broken_heart: | RazorPages | [Root path](#razorpages-root-path) | -| :broken_heart: | RazorPages | [Index page](#razorpages-index-page) | -| :broken_heart: | RazorPages | [Throws exception](#razorpages-throws-exception) | -| :green_heart: | RazorPages | [Static content](#razorpages-static-content) | -| :green_heart: | MinimalApi | [Action without parameter](#minimalapi-action-without-parameter) | -| :green_heart: | MinimalApi | [Action with parameter](#minimalapi-action-with-parameter) | -| :green_heart: | ExceptionMiddleware | [Exception Handled by Exception Handler Middleware](#exceptionmiddleware-exception-handled-by-exception-handler-middleware) | - -## ConventionalRouting: Root path - -```json -{ - "IdealHttpRoute": "ConventionalRoute/Default/{id?}", - "ActivityDisplayName": "GET {controller=ConventionalRoute}/{action=Default}/{id?}", - "ActivityHttpRoute": "{controller=ConventionalRoute}/{action=Default}/{id?}", - "MetricHttpRoute": "{controller=ConventionalRoute}/{action=Default}/{id?}", - "RouteInfo": { - "HttpMethod": "GET", - "Path": "/", - "RoutePattern.RawText": "{controller=ConventionalRoute}/{action=Default}/{id?}", - "IRouteDiagnosticsMetadata.Route": null, - "HttpContext.GetRouteData()": { - "controller": "ConventionalRoute", - "action": "Default" - }, - "ActionDescriptor": { - "AttributeRouteInfo.Template": null, - "Parameters": [], - "ControllerActionDescriptor": { - "ControllerName": "ConventionalRoute", - "ActionName": "Default" - }, - "PageActionDescriptor": null - } - } -} -``` - -## ConventionalRouting: Non-default action with route parameter and query string - -```json -{ - "IdealHttpRoute": "ConventionalRoute/ActionWithStringParameter/{id?}", - "ActivityDisplayName": "GET {controller=ConventionalRoute}/{action=Default}/{id?}", - "ActivityHttpRoute": "{controller=ConventionalRoute}/{action=Default}/{id?}", - "MetricHttpRoute": "{controller=ConventionalRoute}/{action=Default}/{id?}", - "RouteInfo": { - "HttpMethod": "GET", - "Path": "/ConventionalRoute/ActionWithStringParameter/2?num=3", - "RoutePattern.RawText": "{controller=ConventionalRoute}/{action=Default}/{id?}", - "IRouteDiagnosticsMetadata.Route": null, - "HttpContext.GetRouteData()": { - "controller": "ConventionalRoute", - "action": "ActionWithStringParameter", - "id": "2" - }, - "ActionDescriptor": { - "AttributeRouteInfo.Template": null, - "Parameters": [ - "id", - "num" - ], - "ControllerActionDescriptor": { - "ControllerName": "ConventionalRoute", - "ActionName": "ActionWithStringParameter" - }, - "PageActionDescriptor": null - } - } -} -``` - -## ConventionalRouting: Non-default action with query string - -```json -{ - "IdealHttpRoute": "ConventionalRoute/ActionWithStringParameter/{id?}", - "ActivityDisplayName": "GET {controller=ConventionalRoute}/{action=Default}/{id?}", - "ActivityHttpRoute": "{controller=ConventionalRoute}/{action=Default}/{id?}", - "MetricHttpRoute": "{controller=ConventionalRoute}/{action=Default}/{id?}", - "RouteInfo": { - "HttpMethod": "GET", - "Path": "/ConventionalRoute/ActionWithStringParameter?num=3", - "RoutePattern.RawText": "{controller=ConventionalRoute}/{action=Default}/{id?}", - "IRouteDiagnosticsMetadata.Route": null, - "HttpContext.GetRouteData()": { - "controller": "ConventionalRoute", - "action": "ActionWithStringParameter" - }, - "ActionDescriptor": { - "AttributeRouteInfo.Template": null, - "Parameters": [ - "id", - "num" - ], - "ControllerActionDescriptor": { - "ControllerName": "ConventionalRoute", - "ActionName": "ActionWithStringParameter" - }, - "PageActionDescriptor": null - } - } -} -``` - -## ConventionalRouting: Not Found (404) - -```json -{ - "IdealHttpRoute": "", - "ActivityDisplayName": "GET", - "ActivityHttpRoute": "", - "MetricHttpRoute": "", - "RouteInfo": { - "HttpMethod": "GET", - "Path": "/ConventionalRoute/NotFound", - "RoutePattern.RawText": null, - "IRouteDiagnosticsMetadata.Route": null, - "HttpContext.GetRouteData()": {}, - "ActionDescriptor": null - } -} -``` - -## ConventionalRouting: Route template with parameter constraint - -```json -{ - "IdealHttpRoute": "SomePath/{id}/{num:int}", - "ActivityDisplayName": "GET SomePath/{id}/{num:int}", - "ActivityHttpRoute": "SomePath/{id}/{num:int}", - "MetricHttpRoute": "SomePath/{id}/{num:int}", - "RouteInfo": { - "HttpMethod": "GET", - "Path": "/SomePath/SomeString/2", - "RoutePattern.RawText": "SomePath/{id}/{num:int}", - "IRouteDiagnosticsMetadata.Route": null, - "HttpContext.GetRouteData()": { - "controller": "ConventionalRoute", - "action": "ActionWithStringParameter", - "id": "SomeString", - "num": "2" - }, - "ActionDescriptor": { - "AttributeRouteInfo.Template": null, - "Parameters": [ - "id", - "num" - ], - "ControllerActionDescriptor": { - "ControllerName": "ConventionalRoute", - "ActionName": "ActionWithStringParameter" - }, - "PageActionDescriptor": null - } - } -} -``` - -## ConventionalRouting: Path that does not match parameter constraint - -```json -{ - "IdealHttpRoute": "", - "ActivityDisplayName": "GET", - "ActivityHttpRoute": "", - "MetricHttpRoute": "", - "RouteInfo": { - "HttpMethod": "GET", - "Path": "/SomePath/SomeString/NotAnInt", - "RoutePattern.RawText": null, - "IRouteDiagnosticsMetadata.Route": null, - "HttpContext.GetRouteData()": {}, - "ActionDescriptor": null - } -} -``` - -## ConventionalRouting: Area using `area:exists`, default controller/action - -```json -{ - "IdealHttpRoute": "{area:exists}/ControllerForMyArea/Default/{id?}", - "ActivityDisplayName": "GET {area:exists}/{controller=ControllerForMyArea}/{action=Default}/{id?}", - "ActivityHttpRoute": "{area:exists}/{controller=ControllerForMyArea}/{action=Default}/{id?}", - "MetricHttpRoute": "{area:exists}/{controller=ControllerForMyArea}/{action=Default}/{id?}", - "RouteInfo": { - "HttpMethod": "GET", - "Path": "/MyArea", - "RoutePattern.RawText": "{area:exists}/{controller=ControllerForMyArea}/{action=Default}/{id?}", - "IRouteDiagnosticsMetadata.Route": null, - "HttpContext.GetRouteData()": { - "controller": "ControllerForMyArea", - "action": "Default", - "area": "MyArea" - }, - "ActionDescriptor": { - "AttributeRouteInfo.Template": null, - "Parameters": [], - "ControllerActionDescriptor": { - "ControllerName": "ControllerForMyArea", - "ActionName": "Default" - }, - "PageActionDescriptor": null - } - } -} -``` - -## ConventionalRouting: Area using `area:exists`, non-default action - -```json -{ - "IdealHttpRoute": "{area:exists}/ControllerForMyArea/NonDefault/{id?}", - "ActivityDisplayName": "GET {area:exists}/{controller=ControllerForMyArea}/{action=Default}/{id?}", - "ActivityHttpRoute": "{area:exists}/{controller=ControllerForMyArea}/{action=Default}/{id?}", - "MetricHttpRoute": "{area:exists}/{controller=ControllerForMyArea}/{action=Default}/{id?}", - "RouteInfo": { - "HttpMethod": "GET", - "Path": "/MyArea/ControllerForMyArea/NonDefault", - "RoutePattern.RawText": "{area:exists}/{controller=ControllerForMyArea}/{action=Default}/{id?}", - "IRouteDiagnosticsMetadata.Route": null, - "HttpContext.GetRouteData()": { - "controller": "ControllerForMyArea", - "area": "MyArea", - "action": "NonDefault" - }, - "ActionDescriptor": { - "AttributeRouteInfo.Template": null, - "Parameters": [], - "ControllerActionDescriptor": { - "ControllerName": "ControllerForMyArea", - "ActionName": "NonDefault" - }, - "PageActionDescriptor": null - } - } -} -``` - -## ConventionalRouting: Area w/o `area:exists`, default controller/action - -```json -{ - "IdealHttpRoute": "SomePrefix/AnotherArea/Index/{id?}", - "ActivityDisplayName": "GET SomePrefix/{controller=AnotherArea}/{action=Index}/{id?}", - "ActivityHttpRoute": "SomePrefix/{controller=AnotherArea}/{action=Index}/{id?}", - "MetricHttpRoute": "SomePrefix/{controller=AnotherArea}/{action=Index}/{id?}", - "RouteInfo": { - "HttpMethod": "GET", - "Path": "/SomePrefix", - "RoutePattern.RawText": "SomePrefix/{controller=AnotherArea}/{action=Index}/{id?}", - "IRouteDiagnosticsMetadata.Route": null, - "HttpContext.GetRouteData()": { - "area": "AnotherArea", - "controller": "AnotherArea", - "action": "Index" - }, - "ActionDescriptor": { - "AttributeRouteInfo.Template": null, - "Parameters": [], - "ControllerActionDescriptor": { - "ControllerName": "AnotherArea", - "ActionName": "Index" - }, - "PageActionDescriptor": null - } - } -} -``` - -## AttributeRouting: Default action - -```json -{ - "IdealHttpRoute": "AttributeRoute", - "ActivityDisplayName": "GET AttributeRoute", - "ActivityHttpRoute": "AttributeRoute", - "MetricHttpRoute": "AttributeRoute", - "RouteInfo": { - "HttpMethod": "GET", - "Path": "/AttributeRoute", - "RoutePattern.RawText": "AttributeRoute", - "IRouteDiagnosticsMetadata.Route": null, - "HttpContext.GetRouteData()": { - "action": "Get", - "controller": "AttributeRoute" - }, - "ActionDescriptor": { - "AttributeRouteInfo.Template": "AttributeRoute", - "Parameters": [], - "ControllerActionDescriptor": { - "ControllerName": "AttributeRoute", - "ActionName": "Get" - }, - "PageActionDescriptor": null - } - } -} -``` - -## AttributeRouting: Action without parameter - -```json -{ - "IdealHttpRoute": "AttributeRoute/Get", - "ActivityDisplayName": "GET AttributeRoute/Get", - "ActivityHttpRoute": "AttributeRoute/Get", - "MetricHttpRoute": "AttributeRoute/Get", - "RouteInfo": { - "HttpMethod": "GET", - "Path": "/AttributeRoute/Get", - "RoutePattern.RawText": "AttributeRoute/Get", - "IRouteDiagnosticsMetadata.Route": null, - "HttpContext.GetRouteData()": { - "action": "Get", - "controller": "AttributeRoute" - }, - "ActionDescriptor": { - "AttributeRouteInfo.Template": "AttributeRoute/Get", - "Parameters": [], - "ControllerActionDescriptor": { - "ControllerName": "AttributeRoute", - "ActionName": "Get" - }, - "PageActionDescriptor": null - } - } -} -``` - -## AttributeRouting: Action with parameter - -```json -{ - "IdealHttpRoute": "AttributeRoute/Get/{id}", - "ActivityDisplayName": "GET AttributeRoute/Get/{id}", - "ActivityHttpRoute": "AttributeRoute/Get/{id}", - "MetricHttpRoute": "AttributeRoute/Get/{id}", - "RouteInfo": { - "HttpMethod": "GET", - "Path": "/AttributeRoute/Get/12", - "RoutePattern.RawText": "AttributeRoute/Get/{id}", - "IRouteDiagnosticsMetadata.Route": null, - "HttpContext.GetRouteData()": { - "action": "Get", - "controller": "AttributeRoute", - "id": "12" - }, - "ActionDescriptor": { - "AttributeRouteInfo.Template": "AttributeRoute/Get/{id}", - "Parameters": [ - "id" - ], - "ControllerActionDescriptor": { - "ControllerName": "AttributeRoute", - "ActionName": "Get" - }, - "PageActionDescriptor": null - } - } -} -``` - -## AttributeRouting: Action with parameter before action name in template - -```json -{ - "IdealHttpRoute": "AttributeRoute/{id}/GetWithActionNameInDifferentSpotInTemplate", - "ActivityDisplayName": "GET AttributeRoute/{id}/GetWithActionNameInDifferentSpotInTemplate", - "ActivityHttpRoute": "AttributeRoute/{id}/GetWithActionNameInDifferentSpotInTemplate", - "MetricHttpRoute": "AttributeRoute/{id}/GetWithActionNameInDifferentSpotInTemplate", - "RouteInfo": { - "HttpMethod": "GET", - "Path": "/AttributeRoute/12/GetWithActionNameInDifferentSpotInTemplate", - "RoutePattern.RawText": "AttributeRoute/{id}/GetWithActionNameInDifferentSpotInTemplate", - "IRouteDiagnosticsMetadata.Route": null, - "HttpContext.GetRouteData()": { - "action": "GetWithActionNameInDifferentSpotInTemplate", - "controller": "AttributeRoute", - "id": "12" - }, - "ActionDescriptor": { - "AttributeRouteInfo.Template": "AttributeRoute/{id}/GetWithActionNameInDifferentSpotInTemplate", - "Parameters": [ - "id" - ], - "ControllerActionDescriptor": { - "ControllerName": "AttributeRoute", - "ActionName": "GetWithActionNameInDifferentSpotInTemplate" - }, - "PageActionDescriptor": null - } - } -} -``` - -## AttributeRouting: Action invoked resulting in 400 Bad Request - -```json -{ - "IdealHttpRoute": "AttributeRoute/{id}/GetWithActionNameInDifferentSpotInTemplate", - "ActivityDisplayName": "GET AttributeRoute/{id}/GetWithActionNameInDifferentSpotInTemplate", - "ActivityHttpRoute": "AttributeRoute/{id}/GetWithActionNameInDifferentSpotInTemplate", - "MetricHttpRoute": "AttributeRoute/{id}/GetWithActionNameInDifferentSpotInTemplate", - "RouteInfo": { - "HttpMethod": "GET", - "Path": "/AttributeRoute/NotAnInt/GetWithActionNameInDifferentSpotInTemplate", - "RoutePattern.RawText": "AttributeRoute/{id}/GetWithActionNameInDifferentSpotInTemplate", - "IRouteDiagnosticsMetadata.Route": null, - "HttpContext.GetRouteData()": { - "action": "GetWithActionNameInDifferentSpotInTemplate", - "controller": "AttributeRoute", - "id": "NotAnInt" - }, - "ActionDescriptor": { - "AttributeRouteInfo.Template": "AttributeRoute/{id}/GetWithActionNameInDifferentSpotInTemplate", - "Parameters": [ - "id" - ], - "ControllerActionDescriptor": { - "ControllerName": "AttributeRoute", - "ActionName": "GetWithActionNameInDifferentSpotInTemplate" - }, - "PageActionDescriptor": null - } - } -} -``` - -## RazorPages: Root path - -```json -{ - "IdealHttpRoute": "/Index", - "ActivityDisplayName": "GET", - "ActivityHttpRoute": "", - "MetricHttpRoute": "", - "RouteInfo": { - "HttpMethod": "GET", - "Path": "/", - "RoutePattern.RawText": "", - "IRouteDiagnosticsMetadata.Route": null, - "HttpContext.GetRouteData()": { - "page": "/Index" - }, - "ActionDescriptor": { - "AttributeRouteInfo.Template": "", - "Parameters": [], - "ControllerActionDescriptor": null, - "PageActionDescriptor": { - "RelativePath": "/Pages/Index.cshtml", - "ViewEnginePath": "/Index" - } - } - } -} -``` - -## RazorPages: Index page - -```json -{ - "IdealHttpRoute": "/Index", - "ActivityDisplayName": "GET Index", - "ActivityHttpRoute": "Index", - "MetricHttpRoute": "Index", - "RouteInfo": { - "HttpMethod": "GET", - "Path": "/Index", - "RoutePattern.RawText": "Index", - "IRouteDiagnosticsMetadata.Route": null, - "HttpContext.GetRouteData()": { - "page": "/Index" - }, - "ActionDescriptor": { - "AttributeRouteInfo.Template": "Index", - "Parameters": [], - "ControllerActionDescriptor": null, - "PageActionDescriptor": { - "RelativePath": "/Pages/Index.cshtml", - "ViewEnginePath": "/Index" - } - } - } -} -``` - -## RazorPages: Throws exception - -```json -{ - "IdealHttpRoute": "/PageThatThrowsException", - "ActivityDisplayName": "GET PageThatThrowsException", - "ActivityHttpRoute": "PageThatThrowsException", - "MetricHttpRoute": "PageThatThrowsException", - "RouteInfo": { - "HttpMethod": "GET", - "Path": "/PageThatThrowsException", - "RoutePattern.RawText": "PageThatThrowsException", - "IRouteDiagnosticsMetadata.Route": null, - "HttpContext.GetRouteData()": { - "page": "/PageThatThrowsException" - }, - "ActionDescriptor": { - "AttributeRouteInfo.Template": "PageThatThrowsException", - "Parameters": [], - "ControllerActionDescriptor": null, - "PageActionDescriptor": { - "RelativePath": "/Pages/PageThatThrowsException.cshtml", - "ViewEnginePath": "/PageThatThrowsException" - } - } - } -} -``` - -## RazorPages: Static content - -```json -{ - "IdealHttpRoute": "", - "ActivityDisplayName": "GET", - "ActivityHttpRoute": "", - "MetricHttpRoute": "", - "RouteInfo": { - "HttpMethod": "GET", - "Path": "/js/site.js", - "RoutePattern.RawText": null, - "IRouteDiagnosticsMetadata.Route": null, - "HttpContext.GetRouteData()": {}, - "ActionDescriptor": null - } -} -``` - -## MinimalApi: Action without parameter - -```json -{ - "IdealHttpRoute": "/MinimalApi", - "ActivityDisplayName": "GET /MinimalApi", - "ActivityHttpRoute": "/MinimalApi", - "MetricHttpRoute": "/MinimalApi", - "RouteInfo": { - "HttpMethod": "GET", - "Path": "/MinimalApi", - "RoutePattern.RawText": "/MinimalApi", - "IRouteDiagnosticsMetadata.Route": null, - "HttpContext.GetRouteData()": {}, - "ActionDescriptor": null - } -} -``` - -## MinimalApi: Action with parameter - -```json -{ - "IdealHttpRoute": "/MinimalApi/{id}", - "ActivityDisplayName": "GET /MinimalApi/{id}", - "ActivityHttpRoute": "/MinimalApi/{id}", - "MetricHttpRoute": "/MinimalApi/{id}", - "RouteInfo": { - "HttpMethod": "GET", - "Path": "/MinimalApi/123", - "RoutePattern.RawText": "/MinimalApi/{id}", - "IRouteDiagnosticsMetadata.Route": null, - "HttpContext.GetRouteData()": { - "id": "123" - }, - "ActionDescriptor": null - } -} -``` - -## ExceptionMiddleware: Exception Handled by Exception Handler Middleware - -```json -{ - "IdealHttpRoute": "/Exception", - "ActivityDisplayName": "GET /Exception", - "ActivityHttpRoute": "/Exception", - "MetricHttpRoute": "/Exception", - "RouteInfo": { - "HttpMethod": "GET", - "Path": "/Exception", - "RoutePattern.RawText": null, - "IRouteDiagnosticsMetadata.Route": null, - "HttpContext.GetRouteData()": {}, - "ActionDescriptor": null - } -} -``` diff --git a/test/OpenTelemetry.Instrumentation.AspNetCore.Tests/RouteTests/README.net7.0.md b/test/OpenTelemetry.Instrumentation.AspNetCore.Tests/RouteTests/README.net7.0.md deleted file mode 100644 index 49d8224155c..00000000000 --- a/test/OpenTelemetry.Instrumentation.AspNetCore.Tests/RouteTests/README.net7.0.md +++ /dev/null @@ -1,654 +0,0 @@ -# Test results for ASP.NET Core 7 - -| http.route | App | Test Name | -| - | - | - | -| :broken_heart: | ConventionalRouting | [Root path](#conventionalrouting-root-path) | -| :broken_heart: | ConventionalRouting | [Non-default action with route parameter and query string](#conventionalrouting-non-default-action-with-route-parameter-and-query-string) | -| :broken_heart: | ConventionalRouting | [Non-default action with query string](#conventionalrouting-non-default-action-with-query-string) | -| :green_heart: | ConventionalRouting | [Not Found (404)](#conventionalrouting-not-found-404) | -| :green_heart: | ConventionalRouting | [Route template with parameter constraint](#conventionalrouting-route-template-with-parameter-constraint) | -| :green_heart: | ConventionalRouting | [Path that does not match parameter constraint](#conventionalrouting-path-that-does-not-match-parameter-constraint) | -| :broken_heart: | ConventionalRouting | [Area using `area:exists`, default controller/action](#conventionalrouting-area-using-areaexists-default-controlleraction) | -| :broken_heart: | ConventionalRouting | [Area using `area:exists`, non-default action](#conventionalrouting-area-using-areaexists-non-default-action) | -| :broken_heart: | ConventionalRouting | [Area w/o `area:exists`, default controller/action](#conventionalrouting-area-wo-areaexists-default-controlleraction) | -| :green_heart: | AttributeRouting | [Default action](#attributerouting-default-action) | -| :green_heart: | AttributeRouting | [Action without parameter](#attributerouting-action-without-parameter) | -| :green_heart: | AttributeRouting | [Action with parameter](#attributerouting-action-with-parameter) | -| :green_heart: | AttributeRouting | [Action with parameter before action name in template](#attributerouting-action-with-parameter-before-action-name-in-template) | -| :green_heart: | AttributeRouting | [Action invoked resulting in 400 Bad Request](#attributerouting-action-invoked-resulting-in-400-bad-request) | -| :broken_heart: | RazorPages | [Root path](#razorpages-root-path) | -| :broken_heart: | RazorPages | [Index page](#razorpages-index-page) | -| :broken_heart: | RazorPages | [Throws exception](#razorpages-throws-exception) | -| :green_heart: | RazorPages | [Static content](#razorpages-static-content) | -| :green_heart: | MinimalApi | [Action without parameter](#minimalapi-action-without-parameter) | -| :green_heart: | MinimalApi | [Action with parameter](#minimalapi-action-with-parameter) | -| :green_heart: | MinimalApi | [Action without parameter (MapGroup)](#minimalapi-action-without-parameter-mapgroup) | -| :green_heart: | MinimalApi | [Action with parameter (MapGroup)](#minimalapi-action-with-parameter-mapgroup) | -| :green_heart: | ExceptionMiddleware | [Exception Handled by Exception Handler Middleware](#exceptionmiddleware-exception-handled-by-exception-handler-middleware) | - -## ConventionalRouting: Root path - -```json -{ - "IdealHttpRoute": "ConventionalRoute/Default/{id?}", - "ActivityDisplayName": "GET {controller=ConventionalRoute}/{action=Default}/{id?}", - "ActivityHttpRoute": "{controller=ConventionalRoute}/{action=Default}/{id?}", - "MetricHttpRoute": "{controller=ConventionalRoute}/{action=Default}/{id?}", - "RouteInfo": { - "HttpMethod": "GET", - "Path": "/", - "RoutePattern.RawText": "{controller=ConventionalRoute}/{action=Default}/{id?}", - "IRouteDiagnosticsMetadata.Route": null, - "HttpContext.GetRouteData()": { - "controller": "ConventionalRoute", - "action": "Default" - }, - "ActionDescriptor": { - "AttributeRouteInfo.Template": null, - "Parameters": [], - "ControllerActionDescriptor": { - "ControllerName": "ConventionalRoute", - "ActionName": "Default" - }, - "PageActionDescriptor": null - } - } -} -``` - -## ConventionalRouting: Non-default action with route parameter and query string - -```json -{ - "IdealHttpRoute": "ConventionalRoute/ActionWithStringParameter/{id?}", - "ActivityDisplayName": "GET {controller=ConventionalRoute}/{action=Default}/{id?}", - "ActivityHttpRoute": "{controller=ConventionalRoute}/{action=Default}/{id?}", - "MetricHttpRoute": "{controller=ConventionalRoute}/{action=Default}/{id?}", - "RouteInfo": { - "HttpMethod": "GET", - "Path": "/ConventionalRoute/ActionWithStringParameter/2?num=3", - "RoutePattern.RawText": "{controller=ConventionalRoute}/{action=Default}/{id?}", - "IRouteDiagnosticsMetadata.Route": null, - "HttpContext.GetRouteData()": { - "controller": "ConventionalRoute", - "action": "ActionWithStringParameter", - "id": "2" - }, - "ActionDescriptor": { - "AttributeRouteInfo.Template": null, - "Parameters": [ - "id", - "num" - ], - "ControllerActionDescriptor": { - "ControllerName": "ConventionalRoute", - "ActionName": "ActionWithStringParameter" - }, - "PageActionDescriptor": null - } - } -} -``` - -## ConventionalRouting: Non-default action with query string - -```json -{ - "IdealHttpRoute": "ConventionalRoute/ActionWithStringParameter/{id?}", - "ActivityDisplayName": "GET {controller=ConventionalRoute}/{action=Default}/{id?}", - "ActivityHttpRoute": "{controller=ConventionalRoute}/{action=Default}/{id?}", - "MetricHttpRoute": "{controller=ConventionalRoute}/{action=Default}/{id?}", - "RouteInfo": { - "HttpMethod": "GET", - "Path": "/ConventionalRoute/ActionWithStringParameter?num=3", - "RoutePattern.RawText": "{controller=ConventionalRoute}/{action=Default}/{id?}", - "IRouteDiagnosticsMetadata.Route": null, - "HttpContext.GetRouteData()": { - "controller": "ConventionalRoute", - "action": "ActionWithStringParameter" - }, - "ActionDescriptor": { - "AttributeRouteInfo.Template": null, - "Parameters": [ - "id", - "num" - ], - "ControllerActionDescriptor": { - "ControllerName": "ConventionalRoute", - "ActionName": "ActionWithStringParameter" - }, - "PageActionDescriptor": null - } - } -} -``` - -## ConventionalRouting: Not Found (404) - -```json -{ - "IdealHttpRoute": "", - "ActivityDisplayName": "GET", - "ActivityHttpRoute": "", - "MetricHttpRoute": "", - "RouteInfo": { - "HttpMethod": "GET", - "Path": "/ConventionalRoute/NotFound", - "RoutePattern.RawText": null, - "IRouteDiagnosticsMetadata.Route": null, - "HttpContext.GetRouteData()": {}, - "ActionDescriptor": null - } -} -``` - -## ConventionalRouting: Route template with parameter constraint - -```json -{ - "IdealHttpRoute": "SomePath/{id}/{num:int}", - "ActivityDisplayName": "GET SomePath/{id}/{num:int}", - "ActivityHttpRoute": "SomePath/{id}/{num:int}", - "MetricHttpRoute": "SomePath/{id}/{num:int}", - "RouteInfo": { - "HttpMethod": "GET", - "Path": "/SomePath/SomeString/2", - "RoutePattern.RawText": "SomePath/{id}/{num:int}", - "IRouteDiagnosticsMetadata.Route": null, - "HttpContext.GetRouteData()": { - "controller": "ConventionalRoute", - "action": "ActionWithStringParameter", - "id": "SomeString", - "num": "2" - }, - "ActionDescriptor": { - "AttributeRouteInfo.Template": null, - "Parameters": [ - "id", - "num" - ], - "ControllerActionDescriptor": { - "ControllerName": "ConventionalRoute", - "ActionName": "ActionWithStringParameter" - }, - "PageActionDescriptor": null - } - } -} -``` - -## ConventionalRouting: Path that does not match parameter constraint - -```json -{ - "IdealHttpRoute": "", - "ActivityDisplayName": "GET", - "ActivityHttpRoute": "", - "MetricHttpRoute": "", - "RouteInfo": { - "HttpMethod": "GET", - "Path": "/SomePath/SomeString/NotAnInt", - "RoutePattern.RawText": null, - "IRouteDiagnosticsMetadata.Route": null, - "HttpContext.GetRouteData()": {}, - "ActionDescriptor": null - } -} -``` - -## ConventionalRouting: Area using `area:exists`, default controller/action - -```json -{ - "IdealHttpRoute": "{area:exists}/ControllerForMyArea/Default/{id?}", - "ActivityDisplayName": "GET {area:exists}/{controller=ControllerForMyArea}/{action=Default}/{id?}", - "ActivityHttpRoute": "{area:exists}/{controller=ControllerForMyArea}/{action=Default}/{id?}", - "MetricHttpRoute": "{area:exists}/{controller=ControllerForMyArea}/{action=Default}/{id?}", - "RouteInfo": { - "HttpMethod": "GET", - "Path": "/MyArea", - "RoutePattern.RawText": "{area:exists}/{controller=ControllerForMyArea}/{action=Default}/{id?}", - "IRouteDiagnosticsMetadata.Route": null, - "HttpContext.GetRouteData()": { - "controller": "ControllerForMyArea", - "action": "Default", - "area": "MyArea" - }, - "ActionDescriptor": { - "AttributeRouteInfo.Template": null, - "Parameters": [], - "ControllerActionDescriptor": { - "ControllerName": "ControllerForMyArea", - "ActionName": "Default" - }, - "PageActionDescriptor": null - } - } -} -``` - -## ConventionalRouting: Area using `area:exists`, non-default action - -```json -{ - "IdealHttpRoute": "{area:exists}/ControllerForMyArea/NonDefault/{id?}", - "ActivityDisplayName": "GET {area:exists}/{controller=ControllerForMyArea}/{action=Default}/{id?}", - "ActivityHttpRoute": "{area:exists}/{controller=ControllerForMyArea}/{action=Default}/{id?}", - "MetricHttpRoute": "{area:exists}/{controller=ControllerForMyArea}/{action=Default}/{id?}", - "RouteInfo": { - "HttpMethod": "GET", - "Path": "/MyArea/ControllerForMyArea/NonDefault", - "RoutePattern.RawText": "{area:exists}/{controller=ControllerForMyArea}/{action=Default}/{id?}", - "IRouteDiagnosticsMetadata.Route": null, - "HttpContext.GetRouteData()": { - "controller": "ControllerForMyArea", - "area": "MyArea", - "action": "NonDefault" - }, - "ActionDescriptor": { - "AttributeRouteInfo.Template": null, - "Parameters": [], - "ControllerActionDescriptor": { - "ControllerName": "ControllerForMyArea", - "ActionName": "NonDefault" - }, - "PageActionDescriptor": null - } - } -} -``` - -## ConventionalRouting: Area w/o `area:exists`, default controller/action - -```json -{ - "IdealHttpRoute": "SomePrefix/AnotherArea/Index/{id?}", - "ActivityDisplayName": "GET SomePrefix/{controller=AnotherArea}/{action=Index}/{id?}", - "ActivityHttpRoute": "SomePrefix/{controller=AnotherArea}/{action=Index}/{id?}", - "MetricHttpRoute": "SomePrefix/{controller=AnotherArea}/{action=Index}/{id?}", - "RouteInfo": { - "HttpMethod": "GET", - "Path": "/SomePrefix", - "RoutePattern.RawText": "SomePrefix/{controller=AnotherArea}/{action=Index}/{id?}", - "IRouteDiagnosticsMetadata.Route": null, - "HttpContext.GetRouteData()": { - "area": "AnotherArea", - "controller": "AnotherArea", - "action": "Index" - }, - "ActionDescriptor": { - "AttributeRouteInfo.Template": null, - "Parameters": [], - "ControllerActionDescriptor": { - "ControllerName": "AnotherArea", - "ActionName": "Index" - }, - "PageActionDescriptor": null - } - } -} -``` - -## AttributeRouting: Default action - -```json -{ - "IdealHttpRoute": "AttributeRoute", - "ActivityDisplayName": "GET AttributeRoute", - "ActivityHttpRoute": "AttributeRoute", - "MetricHttpRoute": "AttributeRoute", - "RouteInfo": { - "HttpMethod": "GET", - "Path": "/AttributeRoute", - "RoutePattern.RawText": "AttributeRoute", - "IRouteDiagnosticsMetadata.Route": null, - "HttpContext.GetRouteData()": { - "action": "Get", - "controller": "AttributeRoute" - }, - "ActionDescriptor": { - "AttributeRouteInfo.Template": "AttributeRoute", - "Parameters": [], - "ControllerActionDescriptor": { - "ControllerName": "AttributeRoute", - "ActionName": "Get" - }, - "PageActionDescriptor": null - } - } -} -``` - -## AttributeRouting: Action without parameter - -```json -{ - "IdealHttpRoute": "AttributeRoute/Get", - "ActivityDisplayName": "GET AttributeRoute/Get", - "ActivityHttpRoute": "AttributeRoute/Get", - "MetricHttpRoute": "AttributeRoute/Get", - "RouteInfo": { - "HttpMethod": "GET", - "Path": "/AttributeRoute/Get", - "RoutePattern.RawText": "AttributeRoute/Get", - "IRouteDiagnosticsMetadata.Route": null, - "HttpContext.GetRouteData()": { - "action": "Get", - "controller": "AttributeRoute" - }, - "ActionDescriptor": { - "AttributeRouteInfo.Template": "AttributeRoute/Get", - "Parameters": [], - "ControllerActionDescriptor": { - "ControllerName": "AttributeRoute", - "ActionName": "Get" - }, - "PageActionDescriptor": null - } - } -} -``` - -## AttributeRouting: Action with parameter - -```json -{ - "IdealHttpRoute": "AttributeRoute/Get/{id}", - "ActivityDisplayName": "GET AttributeRoute/Get/{id}", - "ActivityHttpRoute": "AttributeRoute/Get/{id}", - "MetricHttpRoute": "AttributeRoute/Get/{id}", - "RouteInfo": { - "HttpMethod": "GET", - "Path": "/AttributeRoute/Get/12", - "RoutePattern.RawText": "AttributeRoute/Get/{id}", - "IRouteDiagnosticsMetadata.Route": null, - "HttpContext.GetRouteData()": { - "action": "Get", - "controller": "AttributeRoute", - "id": "12" - }, - "ActionDescriptor": { - "AttributeRouteInfo.Template": "AttributeRoute/Get/{id}", - "Parameters": [ - "id" - ], - "ControllerActionDescriptor": { - "ControllerName": "AttributeRoute", - "ActionName": "Get" - }, - "PageActionDescriptor": null - } - } -} -``` - -## AttributeRouting: Action with parameter before action name in template - -```json -{ - "IdealHttpRoute": "AttributeRoute/{id}/GetWithActionNameInDifferentSpotInTemplate", - "ActivityDisplayName": "GET AttributeRoute/{id}/GetWithActionNameInDifferentSpotInTemplate", - "ActivityHttpRoute": "AttributeRoute/{id}/GetWithActionNameInDifferentSpotInTemplate", - "MetricHttpRoute": "AttributeRoute/{id}/GetWithActionNameInDifferentSpotInTemplate", - "RouteInfo": { - "HttpMethod": "GET", - "Path": "/AttributeRoute/12/GetWithActionNameInDifferentSpotInTemplate", - "RoutePattern.RawText": "AttributeRoute/{id}/GetWithActionNameInDifferentSpotInTemplate", - "IRouteDiagnosticsMetadata.Route": null, - "HttpContext.GetRouteData()": { - "action": "GetWithActionNameInDifferentSpotInTemplate", - "controller": "AttributeRoute", - "id": "12" - }, - "ActionDescriptor": { - "AttributeRouteInfo.Template": "AttributeRoute/{id}/GetWithActionNameInDifferentSpotInTemplate", - "Parameters": [ - "id" - ], - "ControllerActionDescriptor": { - "ControllerName": "AttributeRoute", - "ActionName": "GetWithActionNameInDifferentSpotInTemplate" - }, - "PageActionDescriptor": null - } - } -} -``` - -## AttributeRouting: Action invoked resulting in 400 Bad Request - -```json -{ - "IdealHttpRoute": "AttributeRoute/{id}/GetWithActionNameInDifferentSpotInTemplate", - "ActivityDisplayName": "GET AttributeRoute/{id}/GetWithActionNameInDifferentSpotInTemplate", - "ActivityHttpRoute": "AttributeRoute/{id}/GetWithActionNameInDifferentSpotInTemplate", - "MetricHttpRoute": "AttributeRoute/{id}/GetWithActionNameInDifferentSpotInTemplate", - "RouteInfo": { - "HttpMethod": "GET", - "Path": "/AttributeRoute/NotAnInt/GetWithActionNameInDifferentSpotInTemplate", - "RoutePattern.RawText": "AttributeRoute/{id}/GetWithActionNameInDifferentSpotInTemplate", - "IRouteDiagnosticsMetadata.Route": null, - "HttpContext.GetRouteData()": { - "action": "GetWithActionNameInDifferentSpotInTemplate", - "controller": "AttributeRoute", - "id": "NotAnInt" - }, - "ActionDescriptor": { - "AttributeRouteInfo.Template": "AttributeRoute/{id}/GetWithActionNameInDifferentSpotInTemplate", - "Parameters": [ - "id" - ], - "ControllerActionDescriptor": { - "ControllerName": "AttributeRoute", - "ActionName": "GetWithActionNameInDifferentSpotInTemplate" - }, - "PageActionDescriptor": null - } - } -} -``` - -## RazorPages: Root path - -```json -{ - "IdealHttpRoute": "/Index", - "ActivityDisplayName": "GET", - "ActivityHttpRoute": "", - "MetricHttpRoute": "", - "RouteInfo": { - "HttpMethod": "GET", - "Path": "/", - "RoutePattern.RawText": "", - "IRouteDiagnosticsMetadata.Route": null, - "HttpContext.GetRouteData()": { - "page": "/Index" - }, - "ActionDescriptor": { - "AttributeRouteInfo.Template": "", - "Parameters": [], - "ControllerActionDescriptor": null, - "PageActionDescriptor": { - "RelativePath": "/Pages/Index.cshtml", - "ViewEnginePath": "/Index" - } - } - } -} -``` - -## RazorPages: Index page - -```json -{ - "IdealHttpRoute": "/Index", - "ActivityDisplayName": "GET Index", - "ActivityHttpRoute": "Index", - "MetricHttpRoute": "Index", - "RouteInfo": { - "HttpMethod": "GET", - "Path": "/Index", - "RoutePattern.RawText": "Index", - "IRouteDiagnosticsMetadata.Route": null, - "HttpContext.GetRouteData()": { - "page": "/Index" - }, - "ActionDescriptor": { - "AttributeRouteInfo.Template": "Index", - "Parameters": [], - "ControllerActionDescriptor": null, - "PageActionDescriptor": { - "RelativePath": "/Pages/Index.cshtml", - "ViewEnginePath": "/Index" - } - } - } -} -``` - -## RazorPages: Throws exception - -```json -{ - "IdealHttpRoute": "/PageThatThrowsException", - "ActivityDisplayName": "GET PageThatThrowsException", - "ActivityHttpRoute": "PageThatThrowsException", - "MetricHttpRoute": "PageThatThrowsException", - "RouteInfo": { - "HttpMethod": "GET", - "Path": "/PageThatThrowsException", - "RoutePattern.RawText": "PageThatThrowsException", - "IRouteDiagnosticsMetadata.Route": null, - "HttpContext.GetRouteData()": { - "page": "/PageThatThrowsException" - }, - "ActionDescriptor": { - "AttributeRouteInfo.Template": "PageThatThrowsException", - "Parameters": [], - "ControllerActionDescriptor": null, - "PageActionDescriptor": { - "RelativePath": "/Pages/PageThatThrowsException.cshtml", - "ViewEnginePath": "/PageThatThrowsException" - } - } - } -} -``` - -## RazorPages: Static content - -```json -{ - "IdealHttpRoute": "", - "ActivityDisplayName": "GET", - "ActivityHttpRoute": "", - "MetricHttpRoute": "", - "RouteInfo": { - "HttpMethod": "GET", - "Path": "/js/site.js", - "RoutePattern.RawText": null, - "IRouteDiagnosticsMetadata.Route": null, - "HttpContext.GetRouteData()": {}, - "ActionDescriptor": null - } -} -``` - -## MinimalApi: Action without parameter - -```json -{ - "IdealHttpRoute": "/MinimalApi", - "ActivityDisplayName": "GET /MinimalApi", - "ActivityHttpRoute": "/MinimalApi", - "MetricHttpRoute": "/MinimalApi", - "RouteInfo": { - "HttpMethod": "GET", - "Path": "/MinimalApi", - "RoutePattern.RawText": "/MinimalApi", - "IRouteDiagnosticsMetadata.Route": null, - "HttpContext.GetRouteData()": {}, - "ActionDescriptor": null - } -} -``` - -## MinimalApi: Action with parameter - -```json -{ - "IdealHttpRoute": "/MinimalApi/{id}", - "ActivityDisplayName": "GET /MinimalApi/{id}", - "ActivityHttpRoute": "/MinimalApi/{id}", - "MetricHttpRoute": "/MinimalApi/{id}", - "RouteInfo": { - "HttpMethod": "GET", - "Path": "/MinimalApi/123", - "RoutePattern.RawText": "/MinimalApi/{id}", - "IRouteDiagnosticsMetadata.Route": null, - "HttpContext.GetRouteData()": { - "id": "123" - }, - "ActionDescriptor": null - } -} -``` - -## MinimalApi: Action without parameter (MapGroup) - -```json -{ - "IdealHttpRoute": "/MinimalApiUsingMapGroup/", - "ActivityDisplayName": "GET /MinimalApiUsingMapGroup/", - "ActivityHttpRoute": "/MinimalApiUsingMapGroup/", - "MetricHttpRoute": "/MinimalApiUsingMapGroup/", - "RouteInfo": { - "HttpMethod": "GET", - "Path": "/MinimalApiUsingMapGroup", - "RoutePattern.RawText": "/MinimalApiUsingMapGroup/", - "IRouteDiagnosticsMetadata.Route": null, - "HttpContext.GetRouteData()": {}, - "ActionDescriptor": null - } -} -``` - -## MinimalApi: Action with parameter (MapGroup) - -```json -{ - "IdealHttpRoute": "/MinimalApiUsingMapGroup/{id}", - "ActivityDisplayName": "GET /MinimalApiUsingMapGroup/{id}", - "ActivityHttpRoute": "/MinimalApiUsingMapGroup/{id}", - "MetricHttpRoute": "/MinimalApiUsingMapGroup/{id}", - "RouteInfo": { - "HttpMethod": "GET", - "Path": "/MinimalApiUsingMapGroup/123", - "RoutePattern.RawText": "/MinimalApiUsingMapGroup/{id}", - "IRouteDiagnosticsMetadata.Route": null, - "HttpContext.GetRouteData()": { - "id": "123" - }, - "ActionDescriptor": null - } -} -``` - -## ExceptionMiddleware: Exception Handled by Exception Handler Middleware - -```json -{ - "IdealHttpRoute": "/Exception", - "ActivityDisplayName": "GET /Exception", - "ActivityHttpRoute": "/Exception", - "MetricHttpRoute": "/Exception", - "RouteInfo": { - "HttpMethod": "GET", - "Path": "/Exception", - "RoutePattern.RawText": null, - "IRouteDiagnosticsMetadata.Route": null, - "HttpContext.GetRouteData()": {}, - "ActionDescriptor": null - } -} -``` diff --git a/test/OpenTelemetry.Instrumentation.AspNetCore.Tests/RouteTests/README.net8.0.md b/test/OpenTelemetry.Instrumentation.AspNetCore.Tests/RouteTests/README.net8.0.md deleted file mode 100644 index 40b63a1ca45..00000000000 --- a/test/OpenTelemetry.Instrumentation.AspNetCore.Tests/RouteTests/README.net8.0.md +++ /dev/null @@ -1,654 +0,0 @@ -# Test results for ASP.NET Core 8 - -| http.route | App | Test Name | -| - | - | - | -| :broken_heart: | ConventionalRouting | [Root path](#conventionalrouting-root-path) | -| :broken_heart: | ConventionalRouting | [Non-default action with route parameter and query string](#conventionalrouting-non-default-action-with-route-parameter-and-query-string) | -| :broken_heart: | ConventionalRouting | [Non-default action with query string](#conventionalrouting-non-default-action-with-query-string) | -| :green_heart: | ConventionalRouting | [Not Found (404)](#conventionalrouting-not-found-404) | -| :green_heart: | ConventionalRouting | [Route template with parameter constraint](#conventionalrouting-route-template-with-parameter-constraint) | -| :green_heart: | ConventionalRouting | [Path that does not match parameter constraint](#conventionalrouting-path-that-does-not-match-parameter-constraint) | -| :broken_heart: | ConventionalRouting | [Area using `area:exists`, default controller/action](#conventionalrouting-area-using-areaexists-default-controlleraction) | -| :broken_heart: | ConventionalRouting | [Area using `area:exists`, non-default action](#conventionalrouting-area-using-areaexists-non-default-action) | -| :broken_heart: | ConventionalRouting | [Area w/o `area:exists`, default controller/action](#conventionalrouting-area-wo-areaexists-default-controlleraction) | -| :green_heart: | AttributeRouting | [Default action](#attributerouting-default-action) | -| :green_heart: | AttributeRouting | [Action without parameter](#attributerouting-action-without-parameter) | -| :green_heart: | AttributeRouting | [Action with parameter](#attributerouting-action-with-parameter) | -| :green_heart: | AttributeRouting | [Action with parameter before action name in template](#attributerouting-action-with-parameter-before-action-name-in-template) | -| :green_heart: | AttributeRouting | [Action invoked resulting in 400 Bad Request](#attributerouting-action-invoked-resulting-in-400-bad-request) | -| :broken_heart: | RazorPages | [Root path](#razorpages-root-path) | -| :broken_heart: | RazorPages | [Index page](#razorpages-index-page) | -| :broken_heart: | RazorPages | [Throws exception](#razorpages-throws-exception) | -| :green_heart: | RazorPages | [Static content](#razorpages-static-content) | -| :green_heart: | MinimalApi | [Action without parameter](#minimalapi-action-without-parameter) | -| :green_heart: | MinimalApi | [Action with parameter](#minimalapi-action-with-parameter) | -| :green_heart: | MinimalApi | [Action without parameter (MapGroup)](#minimalapi-action-without-parameter-mapgroup) | -| :green_heart: | MinimalApi | [Action with parameter (MapGroup)](#minimalapi-action-with-parameter-mapgroup) | -| :green_heart: | ExceptionMiddleware | [Exception Handled by Exception Handler Middleware](#exceptionmiddleware-exception-handled-by-exception-handler-middleware) | - -## ConventionalRouting: Root path - -```json -{ - "IdealHttpRoute": "ConventionalRoute/Default/{id?}", - "ActivityDisplayName": "GET {controller=ConventionalRoute}/{action=Default}/{id?}", - "ActivityHttpRoute": "{controller=ConventionalRoute}/{action=Default}/{id?}", - "MetricHttpRoute": "{controller=ConventionalRoute}/{action=Default}/{id?}", - "RouteInfo": { - "HttpMethod": "GET", - "Path": "/", - "RoutePattern.RawText": "{controller=ConventionalRoute}/{action=Default}/{id?}", - "IRouteDiagnosticsMetadata.Route": "{controller=ConventionalRoute}/{action=Default}/{id?}", - "HttpContext.GetRouteData()": { - "controller": "ConventionalRoute", - "action": "Default" - }, - "ActionDescriptor": { - "AttributeRouteInfo.Template": null, - "Parameters": [], - "ControllerActionDescriptor": { - "ControllerName": "ConventionalRoute", - "ActionName": "Default" - }, - "PageActionDescriptor": null - } - } -} -``` - -## ConventionalRouting: Non-default action with route parameter and query string - -```json -{ - "IdealHttpRoute": "ConventionalRoute/ActionWithStringParameter/{id?}", - "ActivityDisplayName": "GET {controller=ConventionalRoute}/{action=Default}/{id?}", - "ActivityHttpRoute": "{controller=ConventionalRoute}/{action=Default}/{id?}", - "MetricHttpRoute": "{controller=ConventionalRoute}/{action=Default}/{id?}", - "RouteInfo": { - "HttpMethod": "GET", - "Path": "/ConventionalRoute/ActionWithStringParameter/2?num=3", - "RoutePattern.RawText": "{controller=ConventionalRoute}/{action=Default}/{id?}", - "IRouteDiagnosticsMetadata.Route": "{controller=ConventionalRoute}/{action=Default}/{id?}", - "HttpContext.GetRouteData()": { - "controller": "ConventionalRoute", - "action": "ActionWithStringParameter", - "id": "2" - }, - "ActionDescriptor": { - "AttributeRouteInfo.Template": null, - "Parameters": [ - "id", - "num" - ], - "ControllerActionDescriptor": { - "ControllerName": "ConventionalRoute", - "ActionName": "ActionWithStringParameter" - }, - "PageActionDescriptor": null - } - } -} -``` - -## ConventionalRouting: Non-default action with query string - -```json -{ - "IdealHttpRoute": "ConventionalRoute/ActionWithStringParameter/{id?}", - "ActivityDisplayName": "GET {controller=ConventionalRoute}/{action=Default}/{id?}", - "ActivityHttpRoute": "{controller=ConventionalRoute}/{action=Default}/{id?}", - "MetricHttpRoute": "{controller=ConventionalRoute}/{action=Default}/{id?}", - "RouteInfo": { - "HttpMethod": "GET", - "Path": "/ConventionalRoute/ActionWithStringParameter?num=3", - "RoutePattern.RawText": "{controller=ConventionalRoute}/{action=Default}/{id?}", - "IRouteDiagnosticsMetadata.Route": "{controller=ConventionalRoute}/{action=Default}/{id?}", - "HttpContext.GetRouteData()": { - "controller": "ConventionalRoute", - "action": "ActionWithStringParameter" - }, - "ActionDescriptor": { - "AttributeRouteInfo.Template": null, - "Parameters": [ - "id", - "num" - ], - "ControllerActionDescriptor": { - "ControllerName": "ConventionalRoute", - "ActionName": "ActionWithStringParameter" - }, - "PageActionDescriptor": null - } - } -} -``` - -## ConventionalRouting: Not Found (404) - -```json -{ - "IdealHttpRoute": "", - "ActivityDisplayName": "GET", - "ActivityHttpRoute": "", - "MetricHttpRoute": "", - "RouteInfo": { - "HttpMethod": "GET", - "Path": "/ConventionalRoute/NotFound", - "RoutePattern.RawText": null, - "IRouteDiagnosticsMetadata.Route": null, - "HttpContext.GetRouteData()": {}, - "ActionDescriptor": null - } -} -``` - -## ConventionalRouting: Route template with parameter constraint - -```json -{ - "IdealHttpRoute": "SomePath/{id}/{num:int}", - "ActivityDisplayName": "GET SomePath/{id}/{num:int}", - "ActivityHttpRoute": "SomePath/{id}/{num:int}", - "MetricHttpRoute": "SomePath/{id}/{num:int}", - "RouteInfo": { - "HttpMethod": "GET", - "Path": "/SomePath/SomeString/2", - "RoutePattern.RawText": "SomePath/{id}/{num:int}", - "IRouteDiagnosticsMetadata.Route": "SomePath/{id}/{num:int}", - "HttpContext.GetRouteData()": { - "controller": "ConventionalRoute", - "action": "ActionWithStringParameter", - "id": "SomeString", - "num": "2" - }, - "ActionDescriptor": { - "AttributeRouteInfo.Template": null, - "Parameters": [ - "id", - "num" - ], - "ControllerActionDescriptor": { - "ControllerName": "ConventionalRoute", - "ActionName": "ActionWithStringParameter" - }, - "PageActionDescriptor": null - } - } -} -``` - -## ConventionalRouting: Path that does not match parameter constraint - -```json -{ - "IdealHttpRoute": "", - "ActivityDisplayName": "GET", - "ActivityHttpRoute": "", - "MetricHttpRoute": "", - "RouteInfo": { - "HttpMethod": "GET", - "Path": "/SomePath/SomeString/NotAnInt", - "RoutePattern.RawText": null, - "IRouteDiagnosticsMetadata.Route": null, - "HttpContext.GetRouteData()": {}, - "ActionDescriptor": null - } -} -``` - -## ConventionalRouting: Area using `area:exists`, default controller/action - -```json -{ - "IdealHttpRoute": "{area:exists}/ControllerForMyArea/Default/{id?}", - "ActivityDisplayName": "GET {area:exists}/{controller=ControllerForMyArea}/{action=Default}/{id?}", - "ActivityHttpRoute": "{area:exists}/{controller=ControllerForMyArea}/{action=Default}/{id?}", - "MetricHttpRoute": "{area:exists}/{controller=ControllerForMyArea}/{action=Default}/{id?}", - "RouteInfo": { - "HttpMethod": "GET", - "Path": "/MyArea", - "RoutePattern.RawText": "{area:exists}/{controller=ControllerForMyArea}/{action=Default}/{id?}", - "IRouteDiagnosticsMetadata.Route": "{area:exists}/{controller=ControllerForMyArea}/{action=Default}/{id?}", - "HttpContext.GetRouteData()": { - "controller": "ControllerForMyArea", - "action": "Default", - "area": "MyArea" - }, - "ActionDescriptor": { - "AttributeRouteInfo.Template": null, - "Parameters": [], - "ControllerActionDescriptor": { - "ControllerName": "ControllerForMyArea", - "ActionName": "Default" - }, - "PageActionDescriptor": null - } - } -} -``` - -## ConventionalRouting: Area using `area:exists`, non-default action - -```json -{ - "IdealHttpRoute": "{area:exists}/ControllerForMyArea/NonDefault/{id?}", - "ActivityDisplayName": "GET {area:exists}/{controller=ControllerForMyArea}/{action=Default}/{id?}", - "ActivityHttpRoute": "{area:exists}/{controller=ControllerForMyArea}/{action=Default}/{id?}", - "MetricHttpRoute": "{area:exists}/{controller=ControllerForMyArea}/{action=Default}/{id?}", - "RouteInfo": { - "HttpMethod": "GET", - "Path": "/MyArea/ControllerForMyArea/NonDefault", - "RoutePattern.RawText": "{area:exists}/{controller=ControllerForMyArea}/{action=Default}/{id?}", - "IRouteDiagnosticsMetadata.Route": "{area:exists}/{controller=ControllerForMyArea}/{action=Default}/{id?}", - "HttpContext.GetRouteData()": { - "controller": "ControllerForMyArea", - "area": "MyArea", - "action": "NonDefault" - }, - "ActionDescriptor": { - "AttributeRouteInfo.Template": null, - "Parameters": [], - "ControllerActionDescriptor": { - "ControllerName": "ControllerForMyArea", - "ActionName": "NonDefault" - }, - "PageActionDescriptor": null - } - } -} -``` - -## ConventionalRouting: Area w/o `area:exists`, default controller/action - -```json -{ - "IdealHttpRoute": "SomePrefix/AnotherArea/Index/{id?}", - "ActivityDisplayName": "GET SomePrefix/{controller=AnotherArea}/{action=Index}/{id?}", - "ActivityHttpRoute": "SomePrefix/{controller=AnotherArea}/{action=Index}/{id?}", - "MetricHttpRoute": "SomePrefix/{controller=AnotherArea}/{action=Index}/{id?}", - "RouteInfo": { - "HttpMethod": "GET", - "Path": "/SomePrefix", - "RoutePattern.RawText": "SomePrefix/{controller=AnotherArea}/{action=Index}/{id?}", - "IRouteDiagnosticsMetadata.Route": "SomePrefix/{controller=AnotherArea}/{action=Index}/{id?}", - "HttpContext.GetRouteData()": { - "area": "AnotherArea", - "controller": "AnotherArea", - "action": "Index" - }, - "ActionDescriptor": { - "AttributeRouteInfo.Template": null, - "Parameters": [], - "ControllerActionDescriptor": { - "ControllerName": "AnotherArea", - "ActionName": "Index" - }, - "PageActionDescriptor": null - } - } -} -``` - -## AttributeRouting: Default action - -```json -{ - "IdealHttpRoute": "AttributeRoute", - "ActivityDisplayName": "GET AttributeRoute", - "ActivityHttpRoute": "AttributeRoute", - "MetricHttpRoute": "AttributeRoute", - "RouteInfo": { - "HttpMethod": "GET", - "Path": "/AttributeRoute", - "RoutePattern.RawText": "AttributeRoute", - "IRouteDiagnosticsMetadata.Route": "AttributeRoute", - "HttpContext.GetRouteData()": { - "action": "Get", - "controller": "AttributeRoute" - }, - "ActionDescriptor": { - "AttributeRouteInfo.Template": "AttributeRoute", - "Parameters": [], - "ControllerActionDescriptor": { - "ControllerName": "AttributeRoute", - "ActionName": "Get" - }, - "PageActionDescriptor": null - } - } -} -``` - -## AttributeRouting: Action without parameter - -```json -{ - "IdealHttpRoute": "AttributeRoute/Get", - "ActivityDisplayName": "GET AttributeRoute/Get", - "ActivityHttpRoute": "AttributeRoute/Get", - "MetricHttpRoute": "AttributeRoute/Get", - "RouteInfo": { - "HttpMethod": "GET", - "Path": "/AttributeRoute/Get", - "RoutePattern.RawText": "AttributeRoute/Get", - "IRouteDiagnosticsMetadata.Route": "AttributeRoute/Get", - "HttpContext.GetRouteData()": { - "action": "Get", - "controller": "AttributeRoute" - }, - "ActionDescriptor": { - "AttributeRouteInfo.Template": "AttributeRoute/Get", - "Parameters": [], - "ControllerActionDescriptor": { - "ControllerName": "AttributeRoute", - "ActionName": "Get" - }, - "PageActionDescriptor": null - } - } -} -``` - -## AttributeRouting: Action with parameter - -```json -{ - "IdealHttpRoute": "AttributeRoute/Get/{id}", - "ActivityDisplayName": "GET AttributeRoute/Get/{id}", - "ActivityHttpRoute": "AttributeRoute/Get/{id}", - "MetricHttpRoute": "AttributeRoute/Get/{id}", - "RouteInfo": { - "HttpMethod": "GET", - "Path": "/AttributeRoute/Get/12", - "RoutePattern.RawText": "AttributeRoute/Get/{id}", - "IRouteDiagnosticsMetadata.Route": "AttributeRoute/Get/{id}", - "HttpContext.GetRouteData()": { - "action": "Get", - "controller": "AttributeRoute", - "id": "12" - }, - "ActionDescriptor": { - "AttributeRouteInfo.Template": "AttributeRoute/Get/{id}", - "Parameters": [ - "id" - ], - "ControllerActionDescriptor": { - "ControllerName": "AttributeRoute", - "ActionName": "Get" - }, - "PageActionDescriptor": null - } - } -} -``` - -## AttributeRouting: Action with parameter before action name in template - -```json -{ - "IdealHttpRoute": "AttributeRoute/{id}/GetWithActionNameInDifferentSpotInTemplate", - "ActivityDisplayName": "GET AttributeRoute/{id}/GetWithActionNameInDifferentSpotInTemplate", - "ActivityHttpRoute": "AttributeRoute/{id}/GetWithActionNameInDifferentSpotInTemplate", - "MetricHttpRoute": "AttributeRoute/{id}/GetWithActionNameInDifferentSpotInTemplate", - "RouteInfo": { - "HttpMethod": "GET", - "Path": "/AttributeRoute/12/GetWithActionNameInDifferentSpotInTemplate", - "RoutePattern.RawText": "AttributeRoute/{id}/GetWithActionNameInDifferentSpotInTemplate", - "IRouteDiagnosticsMetadata.Route": "AttributeRoute/{id}/GetWithActionNameInDifferentSpotInTemplate", - "HttpContext.GetRouteData()": { - "action": "GetWithActionNameInDifferentSpotInTemplate", - "controller": "AttributeRoute", - "id": "12" - }, - "ActionDescriptor": { - "AttributeRouteInfo.Template": "AttributeRoute/{id}/GetWithActionNameInDifferentSpotInTemplate", - "Parameters": [ - "id" - ], - "ControllerActionDescriptor": { - "ControllerName": "AttributeRoute", - "ActionName": "GetWithActionNameInDifferentSpotInTemplate" - }, - "PageActionDescriptor": null - } - } -} -``` - -## AttributeRouting: Action invoked resulting in 400 Bad Request - -```json -{ - "IdealHttpRoute": "AttributeRoute/{id}/GetWithActionNameInDifferentSpotInTemplate", - "ActivityDisplayName": "GET AttributeRoute/{id}/GetWithActionNameInDifferentSpotInTemplate", - "ActivityHttpRoute": "AttributeRoute/{id}/GetWithActionNameInDifferentSpotInTemplate", - "MetricHttpRoute": "AttributeRoute/{id}/GetWithActionNameInDifferentSpotInTemplate", - "RouteInfo": { - "HttpMethod": "GET", - "Path": "/AttributeRoute/NotAnInt/GetWithActionNameInDifferentSpotInTemplate", - "RoutePattern.RawText": "AttributeRoute/{id}/GetWithActionNameInDifferentSpotInTemplate", - "IRouteDiagnosticsMetadata.Route": "AttributeRoute/{id}/GetWithActionNameInDifferentSpotInTemplate", - "HttpContext.GetRouteData()": { - "action": "GetWithActionNameInDifferentSpotInTemplate", - "controller": "AttributeRoute", - "id": "NotAnInt" - }, - "ActionDescriptor": { - "AttributeRouteInfo.Template": "AttributeRoute/{id}/GetWithActionNameInDifferentSpotInTemplate", - "Parameters": [ - "id" - ], - "ControllerActionDescriptor": { - "ControllerName": "AttributeRoute", - "ActionName": "GetWithActionNameInDifferentSpotInTemplate" - }, - "PageActionDescriptor": null - } - } -} -``` - -## RazorPages: Root path - -```json -{ - "IdealHttpRoute": "/Index", - "ActivityDisplayName": "GET", - "ActivityHttpRoute": "", - "MetricHttpRoute": "", - "RouteInfo": { - "HttpMethod": "GET", - "Path": "/", - "RoutePattern.RawText": "", - "IRouteDiagnosticsMetadata.Route": "", - "HttpContext.GetRouteData()": { - "page": "/Index" - }, - "ActionDescriptor": { - "AttributeRouteInfo.Template": "", - "Parameters": [], - "ControllerActionDescriptor": null, - "PageActionDescriptor": { - "RelativePath": "/Pages/Index.cshtml", - "ViewEnginePath": "/Index" - } - } - } -} -``` - -## RazorPages: Index page - -```json -{ - "IdealHttpRoute": "/Index", - "ActivityDisplayName": "GET Index", - "ActivityHttpRoute": "Index", - "MetricHttpRoute": "Index", - "RouteInfo": { - "HttpMethod": "GET", - "Path": "/Index", - "RoutePattern.RawText": "Index", - "IRouteDiagnosticsMetadata.Route": "Index", - "HttpContext.GetRouteData()": { - "page": "/Index" - }, - "ActionDescriptor": { - "AttributeRouteInfo.Template": "Index", - "Parameters": [], - "ControllerActionDescriptor": null, - "PageActionDescriptor": { - "RelativePath": "/Pages/Index.cshtml", - "ViewEnginePath": "/Index" - } - } - } -} -``` - -## RazorPages: Throws exception - -```json -{ - "IdealHttpRoute": "/PageThatThrowsException", - "ActivityDisplayName": "GET PageThatThrowsException", - "ActivityHttpRoute": "PageThatThrowsException", - "MetricHttpRoute": "PageThatThrowsException", - "RouteInfo": { - "HttpMethod": "GET", - "Path": "/PageThatThrowsException", - "RoutePattern.RawText": "PageThatThrowsException", - "IRouteDiagnosticsMetadata.Route": "PageThatThrowsException", - "HttpContext.GetRouteData()": { - "page": "/PageThatThrowsException" - }, - "ActionDescriptor": { - "AttributeRouteInfo.Template": "PageThatThrowsException", - "Parameters": [], - "ControllerActionDescriptor": null, - "PageActionDescriptor": { - "RelativePath": "/Pages/PageThatThrowsException.cshtml", - "ViewEnginePath": "/PageThatThrowsException" - } - } - } -} -``` - -## RazorPages: Static content - -```json -{ - "IdealHttpRoute": "", - "ActivityDisplayName": "GET", - "ActivityHttpRoute": "", - "MetricHttpRoute": "", - "RouteInfo": { - "HttpMethod": "GET", - "Path": "/js/site.js", - "RoutePattern.RawText": null, - "IRouteDiagnosticsMetadata.Route": null, - "HttpContext.GetRouteData()": {}, - "ActionDescriptor": null - } -} -``` - -## MinimalApi: Action without parameter - -```json -{ - "IdealHttpRoute": "/MinimalApi", - "ActivityDisplayName": "GET /MinimalApi", - "ActivityHttpRoute": "/MinimalApi", - "MetricHttpRoute": "/MinimalApi", - "RouteInfo": { - "HttpMethod": "GET", - "Path": "/MinimalApi", - "RoutePattern.RawText": "/MinimalApi", - "IRouteDiagnosticsMetadata.Route": "/MinimalApi", - "HttpContext.GetRouteData()": {}, - "ActionDescriptor": null - } -} -``` - -## MinimalApi: Action with parameter - -```json -{ - "IdealHttpRoute": "/MinimalApi/{id}", - "ActivityDisplayName": "GET /MinimalApi/{id}", - "ActivityHttpRoute": "/MinimalApi/{id}", - "MetricHttpRoute": "/MinimalApi/{id}", - "RouteInfo": { - "HttpMethod": "GET", - "Path": "/MinimalApi/123", - "RoutePattern.RawText": "/MinimalApi/{id}", - "IRouteDiagnosticsMetadata.Route": "/MinimalApi/{id}", - "HttpContext.GetRouteData()": { - "id": "123" - }, - "ActionDescriptor": null - } -} -``` - -## MinimalApi: Action without parameter (MapGroup) - -```json -{ - "IdealHttpRoute": "/MinimalApiUsingMapGroup/", - "ActivityDisplayName": "GET /MinimalApiUsingMapGroup/", - "ActivityHttpRoute": "/MinimalApiUsingMapGroup/", - "MetricHttpRoute": "/MinimalApiUsingMapGroup/", - "RouteInfo": { - "HttpMethod": "GET", - "Path": "/MinimalApiUsingMapGroup", - "RoutePattern.RawText": "/MinimalApiUsingMapGroup/", - "IRouteDiagnosticsMetadata.Route": "/MinimalApiUsingMapGroup/", - "HttpContext.GetRouteData()": {}, - "ActionDescriptor": null - } -} -``` - -## MinimalApi: Action with parameter (MapGroup) - -```json -{ - "IdealHttpRoute": "/MinimalApiUsingMapGroup/{id}", - "ActivityDisplayName": "GET /MinimalApiUsingMapGroup/{id}", - "ActivityHttpRoute": "/MinimalApiUsingMapGroup/{id}", - "MetricHttpRoute": "/MinimalApiUsingMapGroup/{id}", - "RouteInfo": { - "HttpMethod": "GET", - "Path": "/MinimalApiUsingMapGroup/123", - "RoutePattern.RawText": "/MinimalApiUsingMapGroup/{id}", - "IRouteDiagnosticsMetadata.Route": "/MinimalApiUsingMapGroup/{id}", - "HttpContext.GetRouteData()": { - "id": "123" - }, - "ActionDescriptor": null - } -} -``` - -## ExceptionMiddleware: Exception Handled by Exception Handler Middleware - -```json -{ - "IdealHttpRoute": "/Exception", - "ActivityDisplayName": "GET /Exception", - "ActivityHttpRoute": "/Exception", - "MetricHttpRoute": "/Exception", - "RouteInfo": { - "HttpMethod": "GET", - "Path": "/Exception", - "RoutePattern.RawText": "/Exception", - "IRouteDiagnosticsMetadata.Route": "/Exception", - "HttpContext.GetRouteData()": {}, - "ActionDescriptor": null - } -} -``` diff --git a/test/OpenTelemetry.Instrumentation.AspNetCore.Tests/RouteTests/RoutingTestCases.cs b/test/OpenTelemetry.Instrumentation.AspNetCore.Tests/RouteTests/RoutingTestCases.cs deleted file mode 100644 index a3ec3818886..00000000000 --- a/test/OpenTelemetry.Instrumentation.AspNetCore.Tests/RouteTests/RoutingTestCases.cs +++ /dev/null @@ -1,69 +0,0 @@ -// Copyright The OpenTelemetry Authors -// SPDX-License-Identifier: Apache-2.0 - -#nullable enable - -using System.Reflection; -using System.Text.Json; -using System.Text.Json.Serialization; -using RouteTests.TestApplication; - -namespace RouteTests; - -public static class RoutingTestCases -{ - public static IEnumerable GetTestCases() - { - var assembly = Assembly.GetExecutingAssembly(); - var input = JsonSerializer.Deserialize( - assembly.GetManifestResourceStream("RoutingTestCases.json")!, - new JsonSerializerOptions - { - PropertyNamingPolicy = JsonNamingPolicy.CamelCase, - Converters = { new JsonStringEnumConverter() }, - }); - return GetArgumentsFromTestCaseObject(input!); - } - - private static IEnumerable GetArgumentsFromTestCaseObject(IEnumerable input) - { - var result = new List(); - - foreach (var testCase in input) - { - if (testCase.MinimumDotnetVersion.HasValue && Environment.Version.Major < testCase.MinimumDotnetVersion.Value) - { - continue; - } - - result.Add(new object[] { testCase }); - } - - return result; - } - - public class TestCase - { - public string Name { get; set; } = string.Empty; - - public int? MinimumDotnetVersion { get; set; } - - public TestApplicationScenario TestApplicationScenario { get; set; } - - public string? HttpMethod { get; set; } - - public string Path { get; set; } = string.Empty; - - public int ExpectedStatusCode { get; set; } - - public string? ExpectedHttpRoute { get; set; } - - public string? CurrentHttpRoute { get; set; } - - public override string ToString() - { - // This is used by Visual Studio's test runner to identify the test case. - return $"{this.TestApplicationScenario}: {this.Name}"; - } - } -} diff --git a/test/OpenTelemetry.Instrumentation.AspNetCore.Tests/RouteTests/RoutingTestCases.json b/test/OpenTelemetry.Instrumentation.AspNetCore.Tests/RouteTests/RoutingTestCases.json deleted file mode 100644 index 2d1fa584ee8..00000000000 --- a/test/OpenTelemetry.Instrumentation.AspNetCore.Tests/RouteTests/RoutingTestCases.json +++ /dev/null @@ -1,211 +0,0 @@ -[ - { - "name": "Root path", - "testApplicationScenario": "ConventionalRouting", - "httpMethod": "GET", - "path": "/", - "expectedStatusCode": 200, - "currentHttpRoute": "{controller=ConventionalRoute}/{action=Default}/{id?}", - "expectedHttpRoute": "ConventionalRoute/Default/{id?}" - }, - { - "name": "Non-default action with route parameter and query string", - "testApplicationScenario": "ConventionalRouting", - "httpMethod": "GET", - "path": "/ConventionalRoute/ActionWithStringParameter/2?num=3", - "expectedStatusCode": 200, - "currentHttpRoute": "{controller=ConventionalRoute}/{action=Default}/{id?}", - "expectedHttpRoute": "ConventionalRoute/ActionWithStringParameter/{id?}" - }, - { - "name": "Non-default action with query string", - "testApplicationScenario": "ConventionalRouting", - "httpMethod": "GET", - "path": "/ConventionalRoute/ActionWithStringParameter?num=3", - "expectedStatusCode": 200, - "currentHttpRoute": "{controller=ConventionalRoute}/{action=Default}/{id?}", - "expectedHttpRoute": "ConventionalRoute/ActionWithStringParameter/{id?}" - }, - { - "name": "Not Found (404)", - "testApplicationScenario": "ConventionalRouting", - "httpMethod": "GET", - "path": "/ConventionalRoute/NotFound", - "expectedStatusCode": 404, - "currentHttpRoute": null, - "expectedHttpRoute": "" - }, - { - "name": "Route template with parameter constraint", - "testApplicationScenario": "ConventionalRouting", - "httpMethod": "GET", - "path": "/SomePath/SomeString/2", - "expectedStatusCode": 200, - "currentHttpRoute": null, - "expectedHttpRoute": "SomePath/{id}/{num:int}" - }, - { - "name": "Path that does not match parameter constraint", - "testApplicationScenario": "ConventionalRouting", - "httpMethod": "GET", - "path": "/SomePath/SomeString/NotAnInt", - "expectedStatusCode": 404, - "currentHttpRoute": null, - "expectedHttpRoute": "" - }, - { - "name": "Area using `area:exists`, default controller/action", - "testApplicationScenario": "ConventionalRouting", - "httpMethod": "GET", - "path": "/MyArea", - "expectedStatusCode": 200, - "currentHttpRoute": "{area:exists}/{controller=ControllerForMyArea}/{action=Default}/{id?}", - "expectedHttpRoute": "{area:exists}/ControllerForMyArea/Default/{id?}" - }, - { - "name": "Area using `area:exists`, non-default action", - "testApplicationScenario": "ConventionalRouting", - "httpMethod": "GET", - "path": "/MyArea/ControllerForMyArea/NonDefault", - "expectedStatusCode": 200, - "currentHttpRoute": "{area:exists}/{controller=ControllerForMyArea}/{action=Default}/{id?}", - "expectedHttpRoute": "{area:exists}/ControllerForMyArea/NonDefault/{id?}" - }, - { - "name": "Area w/o `area:exists`, default controller/action", - "testApplicationScenario": "ConventionalRouting", - "httpMethod": "GET", - "path": "/SomePrefix", - "expectedStatusCode": 200, - "currentHttpRoute": "SomePrefix/{controller=AnotherArea}/{action=Index}/{id?}", - "expectedHttpRoute": "SomePrefix/AnotherArea/Index/{id?}" - }, - { - "name": "Default action", - "testApplicationScenario": "AttributeRouting", - "httpMethod": "GET", - "path": "/AttributeRoute", - "expectedStatusCode": 200, - "currentHttpRoute": null, - "expectedHttpRoute": "AttributeRoute" - }, - { - "name": "Action without parameter", - "testApplicationScenario": "AttributeRouting", - "httpMethod": "GET", - "path": "/AttributeRoute/Get", - "expectedStatusCode": 200, - "currentHttpRoute": null, - "expectedHttpRoute": "AttributeRoute/Get" - }, - { - "name": "Action with parameter", - "testApplicationScenario": "AttributeRouting", - "httpMethod": "GET", - "path": "/AttributeRoute/Get/12", - "expectedStatusCode": 200, - "currentHttpRoute": null, - "expectedHttpRoute": "AttributeRoute/Get/{id}" - }, - { - "name": "Action with parameter before action name in template", - "testApplicationScenario": "AttributeRouting", - "httpMethod": "GET", - "path": "/AttributeRoute/12/GetWithActionNameInDifferentSpotInTemplate", - "expectedStatusCode": 200, - "currentHttpRoute": null, - "expectedHttpRoute": "AttributeRoute/{id}/GetWithActionNameInDifferentSpotInTemplate" - }, - { - "name": "Action invoked resulting in 400 Bad Request", - "testApplicationScenario": "AttributeRouting", - "httpMethod": "GET", - "path": "/AttributeRoute/NotAnInt/GetWithActionNameInDifferentSpotInTemplate", - "expectedStatusCode": 400, - "currentHttpRoute": null, - "expectedHttpRoute": "AttributeRoute/{id}/GetWithActionNameInDifferentSpotInTemplate" - }, - { - "name": "Root path", - "testApplicationScenario": "RazorPages", - "httpMethod": "GET", - "path": "/", - "expectedStatusCode": 200, - "currentHttpRoute": "", - "expectedHttpRoute": "/Index" - }, - { - "name": "Index page", - "testApplicationScenario": "RazorPages", - "httpMethod": "GET", - "path": "/Index", - "expectedStatusCode": 200, - "currentHttpRoute": "Index", - "expectedHttpRoute": "/Index" - }, - { - "name": "Throws exception", - "testApplicationScenario": "RazorPages", - "httpMethod": "GET", - "path": "/PageThatThrowsException", - "expectedStatusCode": 500, - "currentHttpRoute": "PageThatThrowsException", - "expectedHttpRoute": "/PageThatThrowsException" - }, - { - "name": "Static content", - "testApplicationScenario": "RazorPages", - "httpMethod": "GET", - "path": "/js/site.js", - "expectedStatusCode": 200, - "currentHttpRoute": null, - "expectedHttpRoute": "" - }, - { - "name": "Action without parameter", - "testApplicationScenario": "MinimalApi", - "httpMethod": "GET", - "path": "/MinimalApi", - "expectedStatusCode": 200, - "currentHttpRoute": null, - "expectedHttpRoute": "/MinimalApi" - }, - { - "name": "Action with parameter", - "testApplicationScenario": "MinimalApi", - "httpMethod": "GET", - "path": "/MinimalApi/123", - "expectedStatusCode": 200, - "currentHttpRoute": null, - "expectedHttpRoute": "/MinimalApi/{id}" - }, - { - "name": "Action without parameter (MapGroup)", - "minimumDotnetVersion": 7, - "testApplicationScenario": "MinimalApi", - "httpMethod": "GET", - "path": "/MinimalApiUsingMapGroup", - "expectedStatusCode": 200, - "currentHttpRoute": null, - "expectedHttpRoute": "/MinimalApiUsingMapGroup/" - }, - { - "name": "Action with parameter (MapGroup)", - "minimumDotnetVersion": 7, - "testApplicationScenario": "MinimalApi", - "httpMethod": "GET", - "path": "/MinimalApiUsingMapGroup/123", - "expectedStatusCode": 200, - "currentHttpRoute": null, - "expectedHttpRoute": "/MinimalApiUsingMapGroup/{id}" - }, - { - "name": "Exception Handled by Exception Handler Middleware", - "testApplicationScenario": "ExceptionMiddleware", - "httpMethod": "GET", - "path": "/Exception", - "expectedStatusCode": 500, - "currentHttpRoute": null, - "expectedHttpRoute": "/Exception" - } -] diff --git a/test/OpenTelemetry.Instrumentation.AspNetCore.Tests/RouteTests/RoutingTestFixture.cs b/test/OpenTelemetry.Instrumentation.AspNetCore.Tests/RouteTests/RoutingTestFixture.cs deleted file mode 100644 index 1b949e26740..00000000000 --- a/test/OpenTelemetry.Instrumentation.AspNetCore.Tests/RouteTests/RoutingTestFixture.cs +++ /dev/null @@ -1,108 +0,0 @@ -// Copyright The OpenTelemetry Authors -// SPDX-License-Identifier: Apache-2.0 - -#nullable enable - -using System.Text; -using Microsoft.AspNetCore.Builder; -using RouteTests.TestApplication; - -namespace RouteTests; - -public class RoutingTestFixture : IDisposable -{ - private static readonly HttpClient HttpClient = new(); - private readonly Dictionary apps = new(); - private readonly RouteInfoDiagnosticObserver diagnostics = new(); - private readonly List testResults = new(); - - public RoutingTestFixture() - { - foreach (var scenario in Enum.GetValues()) - { - var app = TestApplicationFactory.CreateApplication(scenario); - if (app != null) - { - this.apps.Add(scenario, app); - } - } - - foreach (var app in this.apps) - { - app.Value.RunAsync(); - } - } - - public async Task MakeRequest(TestApplicationScenario scenario, string path) - { - var app = this.apps[scenario]; - var baseUrl = app.Urls.First(); - var url = $"{baseUrl}{path}"; - await HttpClient.GetAsync(url); - } - - public void AddTestResult(RoutingTestResult result) - { - this.testResults.Add(result); - } - - public void Dispose() - { - foreach (var app in this.apps) - { - app.Value.DisposeAsync().GetAwaiter().GetResult(); - } - - HttpClient.Dispose(); - this.diagnostics.Dispose(); - - this.GenerateReadme(); - } - - private void GenerateReadme() - { - var sb = new StringBuilder(); - sb.AppendLine($"# Test results for ASP.NET Core {Environment.Version.Major}"); - sb.AppendLine(); - sb.AppendLine("| http.route | App | Test Name |"); - sb.AppendLine("| - | - | - |"); - - for (var i = 0; i < this.testResults.Count; ++i) - { - var result = this.testResults[i]; - var emoji = result.TestCase.CurrentHttpRoute == null ? ":green_heart:" : ":broken_heart:"; - sb.AppendLine($"| {emoji} | {result.TestCase.TestApplicationScenario} | [{result.TestCase.Name}]({GenerateLinkFragment(result.TestCase.TestApplicationScenario, result.TestCase.Name)}) |"); - } - - for (var i = 0; i < this.testResults.Count; ++i) - { - var result = this.testResults[i]; - sb.AppendLine(); - sb.AppendLine($"## {result.TestCase.TestApplicationScenario}: {result.TestCase.Name}"); - sb.AppendLine(); - sb.AppendLine("```json"); - sb.AppendLine(result.ToString()); - sb.AppendLine("```"); - } - - var readmeFileName = $"README.net{Environment.Version.Major}.0.md"; - File.WriteAllText(Path.Combine("..", "..", "..", "RouteTests", readmeFileName), sb.ToString()); - - // Generates a link fragment that should comply with markdownlint rule MD051 - // https://github.com/DavidAnson/markdownlint/blob/main/doc/md051.md - static string GenerateLinkFragment(TestApplicationScenario scenario, string name) - { - var chars = name.ToCharArray() - .Where(c => (!char.IsPunctuation(c) && c != '`') || c == '-') - .Select(c => c switch - { - '-' => '-', - ' ' => '-', - _ => char.ToLower(c), - }) - .ToArray(); - - return $"#{scenario.ToString().ToLower()}-{new string(chars)}"; - } - } -} diff --git a/test/OpenTelemetry.Instrumentation.AspNetCore.Tests/RouteTests/RoutingTestResult.cs b/test/OpenTelemetry.Instrumentation.AspNetCore.Tests/RouteTests/RoutingTestResult.cs deleted file mode 100644 index 8217fa09872..00000000000 --- a/test/OpenTelemetry.Instrumentation.AspNetCore.Tests/RouteTests/RoutingTestResult.cs +++ /dev/null @@ -1,33 +0,0 @@ -// Copyright The OpenTelemetry Authors -// SPDX-License-Identifier: Apache-2.0 - -#nullable enable - -using System.Text.Json; -using System.Text.Json.Serialization; -using RouteTests.TestApplication; - -namespace RouteTests; - -public class RoutingTestResult -{ - private static readonly JsonSerializerOptions JsonSerializerOptions = new() { WriteIndented = true }; - - public string? IdealHttpRoute { get; set; } - - public string ActivityDisplayName { get; set; } = string.Empty; - - public string? ActivityHttpRoute { get; set; } - - public string? MetricHttpRoute { get; set; } - - public RouteInfo RouteInfo { get; set; } = new RouteInfo(); - - [JsonIgnore] - public RoutingTestCases.TestCase TestCase { get; set; } = new RoutingTestCases.TestCase(); - - public override string ToString() - { - return JsonSerializer.Serialize(this, JsonSerializerOptions); - } -} diff --git a/test/OpenTelemetry.Instrumentation.AspNetCore.Tests/RouteTests/RoutingTests.cs b/test/OpenTelemetry.Instrumentation.AspNetCore.Tests/RouteTests/RoutingTests.cs deleted file mode 100644 index 6098aaa08bf..00000000000 --- a/test/OpenTelemetry.Instrumentation.AspNetCore.Tests/RouteTests/RoutingTests.cs +++ /dev/null @@ -1,139 +0,0 @@ -// Copyright The OpenTelemetry Authors -// SPDX-License-Identifier: Apache-2.0 - -#nullable enable - -using System.Diagnostics; -using OpenTelemetry; -using OpenTelemetry.Metrics; -using OpenTelemetry.Trace; -using RouteTests.TestApplication; -using Xunit; - -namespace RouteTests; - -public class RoutingTests : IClassFixture -{ - private const string HttpStatusCode = "http.response.status_code"; - private const string HttpMethod = "http.request.method"; - private const string HttpRoute = "http.route"; - - private readonly RoutingTestFixture fixture; - private readonly List exportedActivities = new(); - private readonly List exportedMetrics = new(); - - public RoutingTests(RoutingTestFixture fixture) - { - this.fixture = fixture; - } - - public static IEnumerable TestData => RoutingTestCases.GetTestCases(); - - [Theory] - [MemberData(nameof(TestData))] - public async Task TestHttpRoute(RoutingTestCases.TestCase testCase) - { - using var tracerProvider = Sdk.CreateTracerProviderBuilder() - .AddAspNetCoreInstrumentation() - .AddInMemoryExporter(this.exportedActivities) - .Build()!; - - using var meterProvider = Sdk.CreateMeterProviderBuilder() - .AddAspNetCoreInstrumentation() - .AddInMemoryExporter(this.exportedMetrics) - .Build()!; - - await this.fixture.MakeRequest(testCase.TestApplicationScenario, testCase.Path); - - for (var i = 0; i < 10; i++) - { - if (this.exportedActivities.Count > 0) - { - break; - } - - await Task.Delay(TimeSpan.FromSeconds(1)); - } - - meterProvider.ForceFlush(); - - var durationMetric = this.exportedMetrics.Single(x => x.Name == "http.server.request.duration" || x.Name == "http.server.duration"); - var metricPoints = new List(); - foreach (var mp in durationMetric.GetMetricPoints()) - { - metricPoints.Add(mp); - } - - var activity = Assert.Single(this.exportedActivities); - var metricPoint = Assert.Single(metricPoints); - - GetTagsFromActivity(activity, out var activityHttpStatusCode, out var activityHttpMethod, out var activityHttpRoute); - GetTagsFromMetricPoint(Environment.Version.Major < 8, metricPoint, out var metricHttpStatusCode, out var metricHttpMethod, out var metricHttpRoute); - - Assert.Equal(testCase.ExpectedStatusCode, activityHttpStatusCode); - Assert.Equal(testCase.ExpectedStatusCode, metricHttpStatusCode); - Assert.Equal(testCase.HttpMethod, activityHttpMethod); - Assert.Equal(testCase.HttpMethod, metricHttpMethod); - - // TODO: The CurrentHttpRoute property will go away. It They only serve to capture status quo. - // If CurrentHttpRoute is null, then that means we already conform to the correct behavior. - var expectedHttpRoute = testCase.CurrentHttpRoute != null ? testCase.CurrentHttpRoute : testCase.ExpectedHttpRoute; - Assert.Equal(expectedHttpRoute, activityHttpRoute); - Assert.Equal(expectedHttpRoute, metricHttpRoute); - - // Activity.DisplayName should be a combination of http.method + http.route attributes, see: - // https://github.com/open-telemetry/semantic-conventions/blob/main/docs/http/http-spans.md#name - var expectedActivityDisplayName = string.IsNullOrEmpty(expectedHttpRoute) - ? testCase.HttpMethod - : $"{testCase.HttpMethod} {expectedHttpRoute}"; - - Assert.Equal(expectedActivityDisplayName, activity.DisplayName); - - var testResult = new RoutingTestResult - { - IdealHttpRoute = testCase.ExpectedHttpRoute, - ActivityDisplayName = activity.DisplayName, - ActivityHttpRoute = activityHttpRoute, - MetricHttpRoute = metricHttpRoute, - TestCase = testCase, - RouteInfo = RouteInfo.Current, - }; - - this.fixture.AddTestResult(testResult); - } - - private static void GetTagsFromActivity(Activity activity, out int httpStatusCode, out string httpMethod, out string? httpRoute) - { - var expectedStatusCodeKey = HttpStatusCode; - var expectedHttpMethodKey = HttpMethod; - httpStatusCode = Convert.ToInt32(activity.GetTagItem(expectedStatusCodeKey)); - httpMethod = (activity.GetTagItem(expectedHttpMethodKey) as string)!; - httpRoute = activity.GetTagItem(HttpRoute) as string ?? string.Empty; - } - - private static void GetTagsFromMetricPoint(bool useLegacyConventions, MetricPoint metricPoint, out int httpStatusCode, out string httpMethod, out string? httpRoute) - { - var expectedStatusCodeKey = HttpStatusCode; - var expectedHttpMethodKey = HttpMethod; - - httpStatusCode = 0; - httpMethod = string.Empty; - httpRoute = string.Empty; - - foreach (var tag in metricPoint.Tags) - { - if (tag.Key.Equals(expectedStatusCodeKey)) - { - httpStatusCode = Convert.ToInt32(tag.Value); - } - else if (tag.Key.Equals(expectedHttpMethodKey)) - { - httpMethod = (tag.Value as string)!; - } - else if (tag.Key.Equals(HttpRoute)) - { - httpRoute = tag.Value as string; - } - } - } -} diff --git a/test/OpenTelemetry.Instrumentation.AspNetCore.Tests/RouteTests/TestApplication/Areas/AnotherArea/Controllers/AnotherAreaController.cs b/test/OpenTelemetry.Instrumentation.AspNetCore.Tests/RouteTests/TestApplication/Areas/AnotherArea/Controllers/AnotherAreaController.cs deleted file mode 100644 index 7754255edfd..00000000000 --- a/test/OpenTelemetry.Instrumentation.AspNetCore.Tests/RouteTests/TestApplication/Areas/AnotherArea/Controllers/AnotherAreaController.cs +++ /dev/null @@ -1,14 +0,0 @@ -// Copyright The OpenTelemetry Authors -// SPDX-License-Identifier: Apache-2.0 - -#nullable disable - -using Microsoft.AspNetCore.Mvc; - -namespace RouteTests.Controllers; - -[Area("AnotherArea")] -public class AnotherAreaController : Controller -{ - public IActionResult Index() => this.Ok(); -} diff --git a/test/OpenTelemetry.Instrumentation.AspNetCore.Tests/RouteTests/TestApplication/Areas/MyArea/Controllers/ControllerForMyAreaController.cs b/test/OpenTelemetry.Instrumentation.AspNetCore.Tests/RouteTests/TestApplication/Areas/MyArea/Controllers/ControllerForMyAreaController.cs deleted file mode 100644 index 762f4a95e82..00000000000 --- a/test/OpenTelemetry.Instrumentation.AspNetCore.Tests/RouteTests/TestApplication/Areas/MyArea/Controllers/ControllerForMyAreaController.cs +++ /dev/null @@ -1,16 +0,0 @@ -// Copyright The OpenTelemetry Authors -// SPDX-License-Identifier: Apache-2.0 - -#nullable disable - -using Microsoft.AspNetCore.Mvc; - -namespace RouteTests.Controllers; - -[Area("MyArea")] -public class ControllerForMyAreaController : Controller -{ - public IActionResult Default() => this.Ok(); - - public IActionResult NonDefault() => this.Ok(); -} diff --git a/test/OpenTelemetry.Instrumentation.AspNetCore.Tests/RouteTests/TestApplication/Controllers/AttributeRouteController.cs b/test/OpenTelemetry.Instrumentation.AspNetCore.Tests/RouteTests/TestApplication/Controllers/AttributeRouteController.cs deleted file mode 100644 index b1e5783b0af..00000000000 --- a/test/OpenTelemetry.Instrumentation.AspNetCore.Tests/RouteTests/TestApplication/Controllers/AttributeRouteController.cs +++ /dev/null @@ -1,23 +0,0 @@ -// Copyright The OpenTelemetry Authors -// SPDX-License-Identifier: Apache-2.0 - -#nullable disable - -using Microsoft.AspNetCore.Mvc; - -namespace RouteTests.Controllers; - -[ApiController] -[Route("[controller]")] -public class AttributeRouteController : ControllerBase -{ - [HttpGet] - [HttpGet("[action]")] - public IActionResult Get() => this.Ok(); - - [HttpGet("[action]/{id}")] - public IActionResult Get(int id) => this.Ok(); - - [HttpGet("{id}/[action]")] - public IActionResult GetWithActionNameInDifferentSpotInTemplate(int id) => this.Ok(); -} diff --git a/test/OpenTelemetry.Instrumentation.AspNetCore.Tests/RouteTests/TestApplication/Controllers/ConventionalRouteController.cs b/test/OpenTelemetry.Instrumentation.AspNetCore.Tests/RouteTests/TestApplication/Controllers/ConventionalRouteController.cs deleted file mode 100644 index 977ee36a136..00000000000 --- a/test/OpenTelemetry.Instrumentation.AspNetCore.Tests/RouteTests/TestApplication/Controllers/ConventionalRouteController.cs +++ /dev/null @@ -1,17 +0,0 @@ -// Copyright The OpenTelemetry Authors -// SPDX-License-Identifier: Apache-2.0 - -#nullable disable - -using Microsoft.AspNetCore.Mvc; - -namespace RouteTests.Controllers; - -public class ConventionalRouteController : Controller -{ - public IActionResult Default() => this.Ok(); - - public IActionResult ActionWithParameter(int id) => this.Ok(); - - public IActionResult ActionWithStringParameter(string id, int num) => this.Ok(); -} diff --git a/test/OpenTelemetry.Instrumentation.AspNetCore.Tests/RouteTests/TestApplication/Pages/Index.cshtml b/test/OpenTelemetry.Instrumentation.AspNetCore.Tests/RouteTests/TestApplication/Pages/Index.cshtml deleted file mode 100644 index 51c350f9565..00000000000 --- a/test/OpenTelemetry.Instrumentation.AspNetCore.Tests/RouteTests/TestApplication/Pages/Index.cshtml +++ /dev/null @@ -1,2 +0,0 @@ -@page -Hello, OpenTelemetry! diff --git a/test/OpenTelemetry.Instrumentation.AspNetCore.Tests/RouteTests/TestApplication/Pages/PageThatThrowsException.cshtml b/test/OpenTelemetry.Instrumentation.AspNetCore.Tests/RouteTests/TestApplication/Pages/PageThatThrowsException.cshtml deleted file mode 100644 index cf6ac0d5b81..00000000000 --- a/test/OpenTelemetry.Instrumentation.AspNetCore.Tests/RouteTests/TestApplication/Pages/PageThatThrowsException.cshtml +++ /dev/null @@ -1,4 +0,0 @@ -@page -@{ - throw new Exception("Oops."); -} diff --git a/test/OpenTelemetry.Instrumentation.AspNetCore.Tests/RouteTests/TestApplication/RouteInfo.cs b/test/OpenTelemetry.Instrumentation.AspNetCore.Tests/RouteTests/TestApplication/RouteInfo.cs deleted file mode 100644 index 78587210394..00000000000 --- a/test/OpenTelemetry.Instrumentation.AspNetCore.Tests/RouteTests/TestApplication/RouteInfo.cs +++ /dev/null @@ -1,138 +0,0 @@ -// Copyright The OpenTelemetry Authors -// SPDX-License-Identifier: Apache-2.0 - -#nullable enable - -using System.Text.Json.Serialization; -using Microsoft.AspNetCore.Http; -#if NET8_0_OR_GREATER -using Microsoft.AspNetCore.Http.Metadata; -#endif -using Microsoft.AspNetCore.Mvc.Abstractions; -using Microsoft.AspNetCore.Mvc.Controllers; -using Microsoft.AspNetCore.Mvc.RazorPages; -using Microsoft.AspNetCore.Routing; - -namespace RouteTests.TestApplication; - -public class RouteInfo -{ - public static RouteInfo Current { get; set; } = new(); - - public string? HttpMethod { get; set; } - - public string? Path { get; set; } - - [JsonPropertyName("RoutePattern.RawText")] - public string? RawText { get; set; } - - [JsonPropertyName("IRouteDiagnosticsMetadata.Route")] - public string? RouteDiagnosticMetadata { get; set; } - - [JsonPropertyName("HttpContext.GetRouteData()")] - public IDictionary? RouteData { get; set; } - - public ActionDescriptorInfo? ActionDescriptor { get; set; } - - public void SetValues(HttpContext context) - { - this.HttpMethod = context.Request.Method; - this.Path = $"{context.Request.Path}{context.Request.QueryString}"; - var endpoint = context.GetEndpoint(); - this.RawText = (endpoint as RouteEndpoint)?.RoutePattern.RawText; -#if NET8_0_OR_GREATER - this.RouteDiagnosticMetadata = endpoint?.Metadata.GetMetadata()?.Route; -#endif - this.RouteData = new Dictionary(); - foreach (var value in context.GetRouteData().Values) - { - this.RouteData[value.Key] = value.Value?.ToString(); - } - } - - public void SetValues(ActionDescriptor actionDescriptor) - { - if (this.ActionDescriptor == null) - { - this.ActionDescriptor = new ActionDescriptorInfo(actionDescriptor); - } - } - - public class ActionDescriptorInfo - { - public ActionDescriptorInfo() - { - } - - public ActionDescriptorInfo(ActionDescriptor actionDescriptor) - { - this.AttributeRouteInfo = actionDescriptor.AttributeRouteInfo?.Template; - - this.ActionParameters = new List(); - foreach (var item in actionDescriptor.Parameters) - { - this.ActionParameters.Add(item.Name); - } - - if (actionDescriptor is PageActionDescriptor pad) - { - this.PageActionDescriptorSummary = new PageActionDescriptorInfo(pad.RelativePath, pad.ViewEnginePath); - } - - if (actionDescriptor is ControllerActionDescriptor cad) - { - this.ControllerActionDescriptorSummary = new ControllerActionDescriptorInfo(cad.ControllerName, cad.ActionName); - } - } - - [JsonPropertyName("AttributeRouteInfo.Template")] - public string? AttributeRouteInfo { get; set; } - - [JsonPropertyName("Parameters")] - public IList? ActionParameters { get; set; } - - [JsonPropertyName("ControllerActionDescriptor")] - public ControllerActionDescriptorInfo? ControllerActionDescriptorSummary { get; set; } - - [JsonPropertyName("PageActionDescriptor")] - public PageActionDescriptorInfo? PageActionDescriptorSummary { get; set; } - } - - public class ControllerActionDescriptorInfo - { - public ControllerActionDescriptorInfo() - { - } - - public ControllerActionDescriptorInfo(string controllerName, string actionName) - { - this.ControllerActionDescriptorControllerName = controllerName; - this.ControllerActionDescriptorActionName = actionName; - } - - [JsonPropertyName("ControllerName")] - public string ControllerActionDescriptorControllerName { get; set; } = string.Empty; - - [JsonPropertyName("ActionName")] - public string ControllerActionDescriptorActionName { get; set; } = string.Empty; - } - - public class PageActionDescriptorInfo - { - public PageActionDescriptorInfo() - { - } - - public PageActionDescriptorInfo(string relativePath, string viewEnginePath) - { - this.PageActionDescriptorRelativePath = relativePath; - this.PageActionDescriptorViewEnginePath = viewEnginePath; - } - - [JsonPropertyName("RelativePath")] - public string PageActionDescriptorRelativePath { get; set; } = string.Empty; - - [JsonPropertyName("ViewEnginePath")] - public string PageActionDescriptorViewEnginePath { get; set; } = string.Empty; - } -} diff --git a/test/OpenTelemetry.Instrumentation.AspNetCore.Tests/RouteTests/TestApplication/RouteInfoDiagnosticObserver.cs b/test/OpenTelemetry.Instrumentation.AspNetCore.Tests/RouteTests/TestApplication/RouteInfoDiagnosticObserver.cs deleted file mode 100644 index 3b3feb59e15..00000000000 --- a/test/OpenTelemetry.Instrumentation.AspNetCore.Tests/RouteTests/TestApplication/RouteInfoDiagnosticObserver.cs +++ /dev/null @@ -1,110 +0,0 @@ -// Copyright The OpenTelemetry Authors -// SPDX-License-Identifier: Apache-2.0 - -#nullable enable - -using System.Diagnostics; -using Microsoft.AspNetCore.Http; -using Microsoft.AspNetCore.Mvc.Diagnostics; - -namespace RouteTests.TestApplication; - -/// -/// This observer captures all the available route information for a request. -/// This route information is used for generating a README file for analyzing -/// what information is available in different scenarios. -/// -internal sealed class RouteInfoDiagnosticObserver : IDisposable, IObserver, IObserver> -{ - internal const string OnStartEvent = "Microsoft.AspNetCore.Hosting.HttpRequestIn.Start"; - internal const string OnStopEvent = "Microsoft.AspNetCore.Hosting.HttpRequestIn.Stop"; - internal const string OnMvcBeforeActionEvent = "Microsoft.AspNetCore.Mvc.BeforeAction"; - - private readonly List listenerSubscriptions = new(); - private IDisposable? allSourcesSubscription; - private long disposed; - - public RouteInfoDiagnosticObserver() - { - this.allSourcesSubscription = DiagnosticListener.AllListeners.Subscribe(this); - } - - public void OnNext(DiagnosticListener value) - { - if (value.Name == "Microsoft.AspNetCore") - { - var subscription = value.Subscribe(this); - - lock (this.listenerSubscriptions) - { - this.listenerSubscriptions.Add(subscription); - } - } - } - - public void OnNext(KeyValuePair value) - { - HttpContext? context; - BeforeActionEventData? actionMethodEventData; - RouteInfo? info; - - switch (value.Key) - { - case OnStartEvent: - context = value.Value as HttpContext; - Debug.Assert(context != null, "HttpContext was null"); - info = new RouteInfo(); - info.SetValues(context); - RouteInfo.Current = info; - break; - case OnMvcBeforeActionEvent: - actionMethodEventData = value.Value as BeforeActionEventData; - Debug.Assert(actionMethodEventData != null, $"expected {nameof(BeforeActionEventData)}"); - RouteInfo.Current.SetValues(actionMethodEventData.HttpContext); - RouteInfo.Current.SetValues(actionMethodEventData.ActionDescriptor); - break; - case OnStopEvent: - context = value.Value as HttpContext; - Debug.Assert(context != null, "HttpContext was null"); - RouteInfo.Current.SetValues(context); - break; - default: - break; - } - } - - public void OnCompleted() - { - } - - public void OnError(Exception error) - { - } - - public void Dispose() - { - this.Dispose(true); - GC.SuppressFinalize(this); - } - - private void Dispose(bool disposing) - { - if (Interlocked.CompareExchange(ref this.disposed, 1, 0) == 1) - { - return; - } - - lock (this.listenerSubscriptions) - { - foreach (var listenerSubscription in this.listenerSubscriptions) - { - listenerSubscription?.Dispose(); - } - - this.listenerSubscriptions.Clear(); - } - - this.allSourcesSubscription?.Dispose(); - this.allSourcesSubscription = null; - } -} diff --git a/test/OpenTelemetry.Instrumentation.AspNetCore.Tests/RouteTests/TestApplication/TestApplicationFactory.cs b/test/OpenTelemetry.Instrumentation.AspNetCore.Tests/RouteTests/TestApplication/TestApplicationFactory.cs deleted file mode 100644 index b030ab7f42b..00000000000 --- a/test/OpenTelemetry.Instrumentation.AspNetCore.Tests/RouteTests/TestApplication/TestApplicationFactory.cs +++ /dev/null @@ -1,199 +0,0 @@ -// Copyright The OpenTelemetry Authors -// SPDX-License-Identifier: Apache-2.0 - -#nullable enable - -using System.Diagnostics; -using Microsoft.AspNetCore.Builder; -using Microsoft.AspNetCore.Diagnostics; -using Microsoft.AspNetCore.Http; -using Microsoft.Extensions.DependencyInjection; -using Microsoft.Extensions.FileProviders; -using Microsoft.Extensions.Logging; - -namespace RouteTests.TestApplication; - -public enum TestApplicationScenario -{ - /// - /// An application that uses conventional routing. - /// - ConventionalRouting, - - /// - /// An application that uses attribute routing. - /// - AttributeRouting, - - /// - /// A Minimal API application. - /// - MinimalApi, - - /// - /// An Razor Pages application. - /// - RazorPages, - - /// - /// Application with Exception Handling Middleware. - /// - ExceptionMiddleware, -} - -internal class TestApplicationFactory -{ - private static readonly string AspNetCoreTestsPath = new FileInfo(typeof(RoutingTests)!.Assembly!.Location)!.Directory!.Parent!.Parent!.Parent!.FullName; - private static readonly string ContentRootPath = Path.Combine(AspNetCoreTestsPath, "RouteTests", "TestApplication"); - - public static WebApplication? CreateApplication(TestApplicationScenario config) - { - Debug.Assert(Directory.Exists(ContentRootPath), $"Cannot find ContentRootPath: {ContentRootPath}"); - switch (config) - { - case TestApplicationScenario.ConventionalRouting: - return CreateConventionalRoutingApplication(); - case TestApplicationScenario.AttributeRouting: - return CreateAttributeRoutingApplication(); - case TestApplicationScenario.MinimalApi: - return CreateMinimalApiApplication(); - case TestApplicationScenario.RazorPages: - return CreateRazorPagesApplication(); - case TestApplicationScenario.ExceptionMiddleware: - return CreateExceptionHandlerApplication(); - default: - throw new ArgumentException($"Invalid {nameof(TestApplicationScenario)}"); - } - } - - private static WebApplication CreateConventionalRoutingApplication() - { - var builder = WebApplication.CreateBuilder(new WebApplicationOptions { ContentRootPath = ContentRootPath }); - builder.Logging.ClearProviders(); - - builder.Services - .AddControllersWithViews() - .AddApplicationPart(typeof(RoutingTests).Assembly); - - var app = builder.Build(); - app.Urls.Clear(); - app.Urls.Add("http://[::1]:0"); - app.UseStaticFiles(); - app.UseRouting(); - - app.MapAreaControllerRoute( - name: "AnotherArea", - areaName: "AnotherArea", - pattern: "SomePrefix/{controller=AnotherArea}/{action=Index}/{id?}"); - - app.MapControllerRoute( - name: "MyArea", - pattern: "{area:exists}/{controller=ControllerForMyArea}/{action=Default}/{id?}"); - - app.MapControllerRoute( - name: "FixedRouteWithConstraints", - pattern: "SomePath/{id}/{num:int}", - defaults: new { controller = "ConventionalRoute", action = "ActionWithStringParameter" }); - - app.MapControllerRoute( - name: "default", - pattern: "{controller=ConventionalRoute}/{action=Default}/{id?}"); - - return app; - } - - private static WebApplication CreateAttributeRoutingApplication() - { - var builder = WebApplication.CreateBuilder(); - builder.Logging.ClearProviders(); - - builder.Services - .AddControllers() - .AddApplicationPart(typeof(RoutingTests).Assembly); - - var app = builder.Build(); - app.Urls.Clear(); - app.Urls.Add("http://[::1]:0"); - app.MapControllers(); - - return app; - } - - private static WebApplication CreateMinimalApiApplication() - { - var builder = WebApplication.CreateBuilder(); - builder.Logging.ClearProviders(); - - var app = builder.Build(); - app.Urls.Clear(); - app.Urls.Add("http://[::1]:0"); - - app.MapGet("/MinimalApi", () => Results.Ok()); - app.MapGet("/MinimalApi/{id}", (int id) => Results.Ok()); - -#if NET7_0_OR_GREATER - var api = app.MapGroup("/MinimalApiUsingMapGroup"); - api.MapGet("/", () => Results.Ok()); - api.MapGet("/{id}", (int id) => Results.Ok()); -#endif - - return app; - } - - private static WebApplication CreateRazorPagesApplication() - { - var builder = WebApplication.CreateBuilder(new WebApplicationOptions { ContentRootPath = ContentRootPath }); - builder.Logging.ClearProviders(); - - builder.Services - .AddRazorPages() - .AddRazorRuntimeCompilation(options => - { - options.FileProviders.Add(new PhysicalFileProvider(ContentRootPath)); - }) - .AddApplicationPart(typeof(RoutingTests).Assembly); - - var app = builder.Build(); - app.Urls.Clear(); - app.Urls.Add("http://[::1]:0"); - app.UseStaticFiles(); - app.UseRouting(); - app.MapRazorPages(); - - return app; - } - - private static WebApplication CreateExceptionHandlerApplication() - { - var builder = WebApplication.CreateBuilder(); - builder.Logging.ClearProviders(); - - var app = builder.Build(); - - app.UseExceptionHandler(exceptionHandlerApp => - { - exceptionHandlerApp.Run(async context => - { - context.Response.StatusCode = StatusCodes.Status500InternalServerError; - var exceptionHandlerPathFeature = context.Features.Get(); - await context.Response.WriteAsync(exceptionHandlerPathFeature?.Error.Message ?? "An exception was thrown."); - }); - }); - - app.Urls.Clear(); - app.Urls.Add("http://[::1]:0"); - - // TODO: Remove this condition once ASP.NET Core 8.0.2. - // Currently, .NET 8 has a different behavior than .NET 6 and 7. - // This is because ASP.NET Core 8+ has native metric instrumentation. - // When ASP.NET Core 8.0.2 is released then its behavior will align with .NET 6/7. - // See: https://github.com/dotnet/aspnetcore/issues/52648#issuecomment-1853432776 -#if !NET8_0_OR_GREATER - app.MapGet("/Exception", (ctx) => throw new ApplicationException()); -#else - app.MapGet("/Exception", () => Results.Content(content: "Error", contentType: null, contentEncoding: null, statusCode: 500)); -#endif - - return app; - } -} diff --git a/test/OpenTelemetry.Instrumentation.AspNetCore.Tests/RouteTests/TestApplication/wwwroot/js/site.js b/test/OpenTelemetry.Instrumentation.AspNetCore.Tests/RouteTests/TestApplication/wwwroot/js/site.js deleted file mode 100644 index dcc7262061a..00000000000 --- a/test/OpenTelemetry.Instrumentation.AspNetCore.Tests/RouteTests/TestApplication/wwwroot/js/site.js +++ /dev/null @@ -1,4 +0,0 @@ -// Please see documentation at https://learn.microsoft.com/aspnet/core/client-side/bundling-and-minification -// for details on configuring this project to bundle and minify static web assets. - -// Write your JavaScript code. From fdf5b0cad869f3621b8f3568739f3537bfe44ccc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Piotr=20Kie=C5=82kowicz?= Date: Wed, 8 May 2024 10:26:14 +0200 Subject: [PATCH 02/15] Drop AspNetCore Benchmarks --- test/Benchmarks/Benchmarks.csproj | 1 - .../AspNetCoreInstrumentationBenchmarks.cs | 189 ---------------- .../AspNetCoreInstrumentationNewBenchmarks.cs | 210 ------------------ 3 files changed, 400 deletions(-) delete mode 100644 test/Benchmarks/Instrumentation/AspNetCoreInstrumentationBenchmarks.cs delete mode 100644 test/Benchmarks/Instrumentation/AspNetCoreInstrumentationNewBenchmarks.cs diff --git a/test/Benchmarks/Benchmarks.csproj b/test/Benchmarks/Benchmarks.csproj index 6644ad51afa..915a68730e1 100644 --- a/test/Benchmarks/Benchmarks.csproj +++ b/test/Benchmarks/Benchmarks.csproj @@ -20,7 +20,6 @@ - diff --git a/test/Benchmarks/Instrumentation/AspNetCoreInstrumentationBenchmarks.cs b/test/Benchmarks/Instrumentation/AspNetCoreInstrumentationBenchmarks.cs deleted file mode 100644 index 85c1f5fa2d3..00000000000 --- a/test/Benchmarks/Instrumentation/AspNetCoreInstrumentationBenchmarks.cs +++ /dev/null @@ -1,189 +0,0 @@ -// Copyright The OpenTelemetry Authors -// SPDX-License-Identifier: Apache-2.0 - -#if !NETFRAMEWORK -using BenchmarkDotNet.Attributes; -using Microsoft.AspNetCore.Builder; -using Microsoft.AspNetCore.Http; -using Microsoft.Extensions.Logging; -using OpenTelemetry; -using OpenTelemetry.Metrics; -using OpenTelemetry.Trace; - -/* -BenchmarkDotNet=v0.13.5, OS=Windows 11 (10.0.22621.1702/22H2/2022Update/SunValley2) -Intel Core i7-8850H CPU 2.60GHz (Coffee Lake), 1 CPU, 12 logical and 6 physical cores -.NET SDK=7.0.203 - [Host] : .NET 7.0.5 (7.0.523.17405), X64 RyuJIT AVX2 - DefaultJob : .NET 7.0.5 (7.0.523.17405), X64 RyuJIT AVX2 - - -| Method | EnableInstrumentation | Mean | Error | StdDev | Gen0 | Allocated | -|--------------------------- |---------------------- |---------:|--------:|--------:|-------:|----------:| -| GetRequestForAspNetCoreApp | None | 136.8 us | 1.56 us | 1.46 us | 0.4883 | 2.45 KB | -| GetRequestForAspNetCoreApp | Traces | 148.1 us | 0.88 us | 0.82 us | 0.7324 | 3.57 KB | -| GetRequestForAspNetCoreApp | Metrics | 144.4 us | 1.16 us | 1.08 us | 0.4883 | 2.92 KB | -| GetRequestForAspNetCoreApp | Traces, Metrics | 163.0 us | 1.60 us | 1.49 us | 0.7324 | 3.63 KB | - -Allocation details for .NET 7: - -// Traces -* Activity creation + `Activity.Start()` = 416 B -* Casting of the struct `Microsoft.Extensions.Primitives.StringValues` to `IEnumerable` by `HttpRequestHeaderValuesGetter` - - `TraceContextPropagator.Extract` = 24 B - - `BaggageContextPropagator.Extract` = 24 B -* String creation for `HttpRequest.HostString.Host` = 40 B -* `Activity.TagsLinkedList` (this is allocated on the first Activity.SetTag call) = 40 B -* Boxing of `Port` number when adding it as a tag = 24 B -* String creation in `GetUri` method for adding the http url tag = 66 B -* Setting `Baggage` (Setting AsyncLocal values causes allocation) - - `BaggageHolder` creation = 24 B - - `System.Threading.AsyncLocalValueMap.TwoElementAsyncLocalValueMap` = 48 B - - `System.Threading.ExecutionContext` = 40 B -* `DiagNode>` - - This is allocated eight times for the eight tags that are added = 8 * 40 = 320 B -* `Activity.Stop()` trying to set `Activity.Current` (This happens because of setting another AsyncLocal variable which is `Baggage` - - System.Threading.AsyncLocalValueMap.OneElementAsyncLocalValueMap = 32 B - - System.Threading.ExecutionContext = 40 B - -Baseline = 2.45 KB -With Traces = 2.45 + (1138 / 1024) = 2.45 + 1.12 = 3.57 KB - - -// Metrics -* Activity creation + `Activity.Start()` = 416 B -* Boxing of `Port` number when adding it as a tag = 24 B -* String creation for `HttpRequest.HostString.Host` = 40 B - -Baseline = 2.45 KB -With Metrics = 2.45 + (416 + 40 + 24) / 1024 = 2.45 + 0.47 = 2.92 KB - -// With Traces and Metrics - -Baseline = 2.45 KB -With Traces and Metrics = Baseline + With Traces + (With Metrics - (Activity creation + `Acitivity.Stop()`)) (they use the same activity) - = 2.45 + (1138 + 64) / 1024 = 2.45 + 1.17 = ~3.63KB -*/ - -namespace Benchmarks.Instrumentation; - -public class AspNetCoreInstrumentationBenchmarks -{ - private HttpClient httpClient; - private WebApplication app; - private TracerProvider tracerProvider; - private MeterProvider meterProvider; - - [Flags] - public enum EnableInstrumentationOption - { - /// - /// Instrumentation is not enabled for any signal. - /// - None = 0, - - /// - /// Instrumentation is enbled only for Traces. - /// - Traces = 1, - - /// - /// Instrumentation is enbled only for Metrics. - /// - Metrics = 2, - } - - [Params(0, 1, 2, 3)] - public EnableInstrumentationOption EnableInstrumentation { get; set; } - - [GlobalSetup(Target = nameof(GetRequestForAspNetCoreApp))] - public void GetRequestForAspNetCoreAppGlobalSetup() - { - if (this.EnableInstrumentation == EnableInstrumentationOption.None) - { - this.StartWebApplication(); - this.httpClient = new HttpClient(); - } - else if (this.EnableInstrumentation == EnableInstrumentationOption.Traces) - { - this.StartWebApplication(); - this.httpClient = new HttpClient(); - - this.tracerProvider = Sdk.CreateTracerProviderBuilder() - .AddAspNetCoreInstrumentation() - .Build(); - } - else if (this.EnableInstrumentation == EnableInstrumentationOption.Metrics) - { - this.StartWebApplication(); - this.httpClient = new HttpClient(); - - this.meterProvider = Sdk.CreateMeterProviderBuilder() - .AddAspNetCoreInstrumentation() - .Build(); - } - else if (this.EnableInstrumentation.HasFlag(EnableInstrumentationOption.Traces) && - this.EnableInstrumentation.HasFlag(EnableInstrumentationOption.Metrics)) - { - this.StartWebApplication(); - this.httpClient = new HttpClient(); - - this.tracerProvider = Sdk.CreateTracerProviderBuilder() - .AddAspNetCoreInstrumentation() - .Build(); - - this.meterProvider = Sdk.CreateMeterProviderBuilder() - .AddAspNetCoreInstrumentation() - .Build(); - } - } - - [GlobalCleanup(Target = nameof(GetRequestForAspNetCoreApp))] - public void GetRequestForAspNetCoreAppGlobalCleanup() - { - if (this.EnableInstrumentation == EnableInstrumentationOption.None) - { - this.httpClient.Dispose(); - this.app.DisposeAsync().GetAwaiter().GetResult(); - } - else if (this.EnableInstrumentation == EnableInstrumentationOption.Traces) - { - this.httpClient.Dispose(); - this.app.DisposeAsync().GetAwaiter().GetResult(); - this.tracerProvider.Dispose(); - } - else if (this.EnableInstrumentation == EnableInstrumentationOption.Metrics) - { - this.httpClient.Dispose(); - this.app.DisposeAsync().GetAwaiter().GetResult(); - this.meterProvider.Dispose(); - } - else if (this.EnableInstrumentation.HasFlag(EnableInstrumentationOption.Traces) && - this.EnableInstrumentation.HasFlag(EnableInstrumentationOption.Metrics)) - { - this.httpClient.Dispose(); - this.app.DisposeAsync().GetAwaiter().GetResult(); - this.tracerProvider.Dispose(); - this.meterProvider.Dispose(); - } - } - - [Benchmark] - public async Task GetRequestForAspNetCoreApp() - { - var httpResponse = await this.httpClient.GetAsync("http://localhost:5000").ConfigureAwait(false); - httpResponse.EnsureSuccessStatusCode(); - } - - private void StartWebApplication() - { - var builder = WebApplication.CreateBuilder(); - builder.Logging.ClearProviders(); - var app = builder.Build(); - app.MapGet("/", async context => await context.Response.WriteAsync($"Hello World!")); - app.RunAsync(); - - this.app = app; - } -} -#endif diff --git a/test/Benchmarks/Instrumentation/AspNetCoreInstrumentationNewBenchmarks.cs b/test/Benchmarks/Instrumentation/AspNetCoreInstrumentationNewBenchmarks.cs deleted file mode 100644 index 4ddc7b56018..00000000000 --- a/test/Benchmarks/Instrumentation/AspNetCoreInstrumentationNewBenchmarks.cs +++ /dev/null @@ -1,210 +0,0 @@ -// Copyright The OpenTelemetry Authors -// SPDX-License-Identifier: Apache-2.0 - -#if !NETFRAMEWORK -using BenchmarkDotNet.Attributes; -using Microsoft.AspNetCore.Builder; -using Microsoft.AspNetCore.Http; -using Microsoft.Extensions.Configuration; -using Microsoft.Extensions.DependencyInjection; -using Microsoft.Extensions.Logging; -using OpenTelemetry; -using OpenTelemetry.Metrics; -using OpenTelemetry.Trace; - -/* -// * Summary * - -BenchmarkDotNet=v0.13.5, OS=Windows 11 (10.0.22621.1992/22H2/2022Update/SunValley2), VM=Hyper-V -AMD EPYC 7763, 1 CPU, 16 logical and 8 physical cores -.NET SDK=7.0.306 - [Host] : .NET 7.0.9 (7.0.923.32018), X64 RyuJIT AVX2 - DefaultJob : .NET 7.0.9 (7.0.923.32018), X64 RyuJIT AVX2 - - -| Method | EnableInstrumentation | Mean | Error | StdDev | Allocated | -|--------------------------- |---------------------- |---------:|--------:|--------:|----------:| -| GetRequestForAspNetCoreApp | None | 150.7 us | 1.68 us | 1.57 us | 2.45 KB | -| GetRequestForAspNetCoreApp | Traces | 156.6 us | 3.12 us | 6.37 us | 3.46 KB | -| GetRequestForAspNetCoreApp | Metrics | 148.8 us | 2.87 us | 2.69 us | 2.92 KB | -| GetRequestForAspNetCoreApp | Traces, Metrics | 164.0 us | 3.19 us | 6.22 us | 3.52 KB | - -Allocation details for .NET 7: - -// Traces -* Activity creation + `Activity.Start()` = 416 B -* Casting of the struct `Microsoft.Extensions.Primitives.StringValues` to `IEnumerable` by `HttpRequestHeaderValuesGetter` - - `TraceContextPropagator.Extract` = 24 B - - `BaggageContextPropagator.Extract` = 24 B -* String creation for `HttpRequest.HostString.Host` = 40 B -* `Activity.TagsLinkedList` (this is allocated on the first Activity.SetTag call) = 40 B -* Boxing of `Port` number when adding it as a tag = 24 B -* Setting `Baggage` (Setting AsyncLocal values causes allocation) - - `BaggageHolder` creation = 24 B - - `System.Threading.AsyncLocalValueMap.TwoElementAsyncLocalValueMap` = 48 B - - `System.Threading.ExecutionContext` = 40 B -* `DiagNode>` - - This is allocated seven times for the seven (eight if query string is available) tags that are added = 7 * 40 = 280 B -* `Activity.Stop()` trying to set `Activity.Current` (This happens because of setting another AsyncLocal variable which is `Baggage` - - System.Threading.AsyncLocalValueMap.OneElementAsyncLocalValueMap = 32 B - - System.Threading.ExecutionContext = 40 B - -Baseline = 2.45 KB -With Traces = 2.45 + (1032 / 1024) = 2.45 + 1.01 = 3.46 KB - - -// Metrics -* Activity creation + `Activity.Start()` = 416 B -* Boxing of `Port` number when adding it as a tag = 24 B -* String creation for `HttpRequest.HostString.Host` = 40 B - -Baseline = 2.45 KB -With Metrics = 2.45 + (416 + 40 + 24) / 1024 = 2.45 + 0.47 = 2.92 KB - -// With Traces and Metrics - -Baseline = 2.45 KB -With Traces and Metrics = Baseline + With Traces + (With Metrics - (Activity creation + `Acitivity.Stop()`)) (they use the same activity) - = 2.45 + (1032 + 64) / 1024 = 2.45 + 1.07 = ~3.52KB -*/ -namespace Benchmarks.Instrumentation; - -public class AspNetCoreInstrumentationNewBenchmarks -{ - private HttpClient httpClient; - private WebApplication app; - private TracerProvider tracerProvider; - private MeterProvider meterProvider; - - [Flags] - public enum EnableInstrumentationOption - { - /// - /// Instrumentation is not enabled for any signal. - /// - None = 0, - - /// - /// Instrumentation is enbled only for Traces. - /// - Traces = 1, - - /// - /// Instrumentation is enbled only for Metrics. - /// - Metrics = 2, - } - - [Params(0, 1, 2, 3)] - public EnableInstrumentationOption EnableInstrumentation { get; set; } - - [GlobalSetup(Target = nameof(GetRequestForAspNetCoreApp))] - public void GetRequestForAspNetCoreAppGlobalSetup() - { - KeyValuePair[] config = new KeyValuePair[] { new KeyValuePair("OTEL_SEMCONV_STABILITY_OPT_IN", "http") }; - var configuration = new ConfigurationBuilder() - .AddInMemoryCollection(config) - .Build(); - - if (this.EnableInstrumentation == EnableInstrumentationOption.None) - { - this.StartWebApplication(); - this.httpClient = new HttpClient(); - } - else if (this.EnableInstrumentation == EnableInstrumentationOption.Traces) - { - this.StartWebApplication(); - this.httpClient = new HttpClient(); - - this.tracerProvider = Sdk.CreateTracerProviderBuilder() - .ConfigureServices(services => services.AddSingleton(configuration)) - .AddAspNetCoreInstrumentation() - .Build(); - } - else if (this.EnableInstrumentation == EnableInstrumentationOption.Metrics) - { - this.StartWebApplication(); - this.httpClient = new HttpClient(); - - var exportedItems = new List(); - this.meterProvider = Sdk.CreateMeterProviderBuilder() - .ConfigureServices(services => services.AddSingleton(configuration)) - .AddAspNetCoreInstrumentation() - .AddInMemoryExporter(exportedItems, metricReaderOptions => - { - metricReaderOptions.PeriodicExportingMetricReaderOptions.ExportIntervalMilliseconds = 1000; - }) - .Build(); - } - else if (this.EnableInstrumentation.HasFlag(EnableInstrumentationOption.Traces) && - this.EnableInstrumentation.HasFlag(EnableInstrumentationOption.Metrics)) - { - this.StartWebApplication(); - this.httpClient = new HttpClient(); - - this.tracerProvider = Sdk.CreateTracerProviderBuilder() - .ConfigureServices(services => services.AddSingleton(configuration)) - .AddAspNetCoreInstrumentation() - .Build(); - - var exportedItems = new List(); - this.meterProvider = Sdk.CreateMeterProviderBuilder() - .ConfigureServices(services => services.AddSingleton(configuration)) - .AddAspNetCoreInstrumentation() - .AddInMemoryExporter(exportedItems, metricReaderOptions => - { - metricReaderOptions.PeriodicExportingMetricReaderOptions.ExportIntervalMilliseconds = 1000; - }) - .Build(); - } - } - - [GlobalCleanup(Target = nameof(GetRequestForAspNetCoreApp))] - public void GetRequestForAspNetCoreAppGlobalCleanup() - { - if (this.EnableInstrumentation == EnableInstrumentationOption.None) - { - this.httpClient.Dispose(); - this.app.DisposeAsync().GetAwaiter().GetResult(); - } - else if (this.EnableInstrumentation == EnableInstrumentationOption.Traces) - { - this.httpClient.Dispose(); - this.app.DisposeAsync().GetAwaiter().GetResult(); - this.tracerProvider.Dispose(); - } - else if (this.EnableInstrumentation == EnableInstrumentationOption.Metrics) - { - this.httpClient.Dispose(); - this.app.DisposeAsync().GetAwaiter().GetResult(); - this.meterProvider.Dispose(); - } - else if (this.EnableInstrumentation.HasFlag(EnableInstrumentationOption.Traces) && - this.EnableInstrumentation.HasFlag(EnableInstrumentationOption.Metrics)) - { - this.httpClient.Dispose(); - this.app.DisposeAsync().GetAwaiter().GetResult(); - this.tracerProvider.Dispose(); - this.meterProvider.Dispose(); - } - } - - [Benchmark] - public async Task GetRequestForAspNetCoreApp() - { - var httpResponse = await this.httpClient.GetAsync("http://localhost:5000").ConfigureAwait(false); - httpResponse.EnsureSuccessStatusCode(); - } - - private void StartWebApplication() - { - var builder = WebApplication.CreateBuilder(); - builder.Logging.ClearProviders(); - var app = builder.Build(); - app.MapGet("/", async context => await context.Response.WriteAsync($"Hello World!")); - app.RunAsync(); - - this.app = app; - } -} -#endif From 4fb18004ed1faf85674fc272a874625b17b07772 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Piotr=20Kie=C5=82kowicz?= Date: Wed, 8 May 2024 10:28:07 +0200 Subject: [PATCH 03/15] Use released AspNetCore instrumentation package --- Directory.Packages.props | 1 + .../getting-started-aspnetcore.csproj | 4 +++- .../getting-started-aspnetcore.csproj | 4 +++- examples/AspNetCore/Examples.AspNetCore.csproj | 2 +- examples/GrpcService/Examples.GrpcService.csproj | 2 +- examples/MicroserviceExample/WebApi/WebApi.csproj | 2 +- ...OpenTelemetry.Instrumentation.W3cTraceContext.Tests.csproj | 2 +- 7 files changed, 11 insertions(+), 6 deletions(-) diff --git a/Directory.Packages.props b/Directory.Packages.props index 885a1979ef2..6469f47b892 100644 --- a/Directory.Packages.props +++ b/Directory.Packages.props @@ -86,6 +86,7 @@ + diff --git a/docs/metrics/getting-started-aspnetcore/getting-started-aspnetcore.csproj b/docs/metrics/getting-started-aspnetcore/getting-started-aspnetcore.csproj index 375079fc34c..9e3fb55e1a1 100644 --- a/docs/metrics/getting-started-aspnetcore/getting-started-aspnetcore.csproj +++ b/docs/metrics/getting-started-aspnetcore/getting-started-aspnetcore.csproj @@ -1,7 +1,9 @@ + + + - diff --git a/docs/trace/getting-started-aspnetcore/getting-started-aspnetcore.csproj b/docs/trace/getting-started-aspnetcore/getting-started-aspnetcore.csproj index 375079fc34c..9e3fb55e1a1 100644 --- a/docs/trace/getting-started-aspnetcore/getting-started-aspnetcore.csproj +++ b/docs/trace/getting-started-aspnetcore/getting-started-aspnetcore.csproj @@ -1,7 +1,9 @@ + + + - diff --git a/examples/AspNetCore/Examples.AspNetCore.csproj b/examples/AspNetCore/Examples.AspNetCore.csproj index a283b3245bc..e476b1ed4d8 100644 --- a/examples/AspNetCore/Examples.AspNetCore.csproj +++ b/examples/AspNetCore/Examples.AspNetCore.csproj @@ -6,6 +6,7 @@ + @@ -14,7 +15,6 @@ - diff --git a/examples/GrpcService/Examples.GrpcService.csproj b/examples/GrpcService/Examples.GrpcService.csproj index cd934426fb7..c9893c84122 100644 --- a/examples/GrpcService/Examples.GrpcService.csproj +++ b/examples/GrpcService/Examples.GrpcService.csproj @@ -10,6 +10,7 @@ + @@ -17,7 +18,6 @@ - diff --git a/examples/MicroserviceExample/WebApi/WebApi.csproj b/examples/MicroserviceExample/WebApi/WebApi.csproj index 57bf07e763f..2d5846e580f 100644 --- a/examples/MicroserviceExample/WebApi/WebApi.csproj +++ b/examples/MicroserviceExample/WebApi/WebApi.csproj @@ -4,11 +4,11 @@ + - diff --git a/test/OpenTelemetry.Instrumentation.W3cTraceContext.Tests/OpenTelemetry.Instrumentation.W3cTraceContext.Tests.csproj b/test/OpenTelemetry.Instrumentation.W3cTraceContext.Tests/OpenTelemetry.Instrumentation.W3cTraceContext.Tests.csproj index b048b5c5913..8d9e8800c51 100644 --- a/test/OpenTelemetry.Instrumentation.W3cTraceContext.Tests/OpenTelemetry.Instrumentation.W3cTraceContext.Tests.csproj +++ b/test/OpenTelemetry.Instrumentation.W3cTraceContext.Tests/OpenTelemetry.Instrumentation.W3cTraceContext.Tests.csproj @@ -9,6 +9,7 @@ + runtime; build; native; contentfiles; analyzers @@ -16,7 +17,6 @@ - From c48d17fc8011fd21f99b835b9adfbf80d72cceaa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Piotr=20Kie=C5=82kowicz?= Date: Wed, 8 May 2024 10:30:23 +0200 Subject: [PATCH 04/15] Drop AspNetCore instrumentation package from AOT tests --- .../OpenTelemetry.AotCompatibility.TestApp.csproj | 1 - 1 file changed, 1 deletion(-) diff --git a/test/OpenTelemetry.AotCompatibility.TestApp/OpenTelemetry.AotCompatibility.TestApp.csproj b/test/OpenTelemetry.AotCompatibility.TestApp/OpenTelemetry.AotCompatibility.TestApp.csproj index 84c4d746e18..4c53546e3bb 100644 --- a/test/OpenTelemetry.AotCompatibility.TestApp/OpenTelemetry.AotCompatibility.TestApp.csproj +++ b/test/OpenTelemetry.AotCompatibility.TestApp/OpenTelemetry.AotCompatibility.TestApp.csproj @@ -18,7 +18,6 @@ - From d39624fb84765ac2e67d7247a9271c2f9b59c4f2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Piotr=20Kie=C5=82kowicz?= Date: Wed, 8 May 2024 10:32:03 +0200 Subject: [PATCH 05/15] Drop Instrumentation AspNetCore --- OpenTelemetry.sln | 6 - .../.publicApi/PublicAPI.Shipped.txt | 18 - .../.publicApi/PublicAPI.Unshipped.txt | 0 .../netstandard2.0/PublicAPI.Shipped.txt | 0 .../netstandard2.0/PublicAPI.Unshipped.txt | 0 .../AspNetCoreInstrumentation.cs | 38 -- ...mentationMeterProviderBuilderExtensions.cs | 52 -- ...entationTracerProviderBuilderExtensions.cs | 123 ---- .../AspNetCoreMetrics.cs | 41 -- .../AspNetCoreTraceInstrumentationOptions.cs | 115 ---- .../AssemblyInfo.cs | 10 - .../CHANGELOG.md | 638 ------------------ .../GrpcTagHelper.cs | 76 --- .../AspNetCoreInstrumentationEventSource.cs | 94 --- .../Implementation/HttpInListener.cs | 397 ----------- .../Implementation/HttpInMetricsListener.cs | 128 ---- .../Implementation/HttpTagHelper.cs | 33 - .../Implementation/TelemetryHelper.cs | 28 - ...elemetry.Instrumentation.AspNetCore.csproj | 46 -- .../README.md | 331 --------- .../StatusCanonicalCode.cs | 135 ---- 21 files changed, 2309 deletions(-) delete mode 100644 src/OpenTelemetry.Instrumentation.AspNetCore/.publicApi/PublicAPI.Shipped.txt delete mode 100644 src/OpenTelemetry.Instrumentation.AspNetCore/.publicApi/PublicAPI.Unshipped.txt delete mode 100644 src/OpenTelemetry.Instrumentation.AspNetCore/.publicApi/netstandard2.0/PublicAPI.Shipped.txt delete mode 100644 src/OpenTelemetry.Instrumentation.AspNetCore/.publicApi/netstandard2.0/PublicAPI.Unshipped.txt delete mode 100644 src/OpenTelemetry.Instrumentation.AspNetCore/AspNetCoreInstrumentation.cs delete mode 100644 src/OpenTelemetry.Instrumentation.AspNetCore/AspNetCoreInstrumentationMeterProviderBuilderExtensions.cs delete mode 100644 src/OpenTelemetry.Instrumentation.AspNetCore/AspNetCoreInstrumentationTracerProviderBuilderExtensions.cs delete mode 100644 src/OpenTelemetry.Instrumentation.AspNetCore/AspNetCoreMetrics.cs delete mode 100644 src/OpenTelemetry.Instrumentation.AspNetCore/AspNetCoreTraceInstrumentationOptions.cs delete mode 100644 src/OpenTelemetry.Instrumentation.AspNetCore/AssemblyInfo.cs delete mode 100644 src/OpenTelemetry.Instrumentation.AspNetCore/CHANGELOG.md delete mode 100644 src/OpenTelemetry.Instrumentation.AspNetCore/GrpcTagHelper.cs delete mode 100644 src/OpenTelemetry.Instrumentation.AspNetCore/Implementation/AspNetCoreInstrumentationEventSource.cs delete mode 100644 src/OpenTelemetry.Instrumentation.AspNetCore/Implementation/HttpInListener.cs delete mode 100644 src/OpenTelemetry.Instrumentation.AspNetCore/Implementation/HttpInMetricsListener.cs delete mode 100644 src/OpenTelemetry.Instrumentation.AspNetCore/Implementation/HttpTagHelper.cs delete mode 100644 src/OpenTelemetry.Instrumentation.AspNetCore/Implementation/TelemetryHelper.cs delete mode 100644 src/OpenTelemetry.Instrumentation.AspNetCore/OpenTelemetry.Instrumentation.AspNetCore.csproj delete mode 100644 src/OpenTelemetry.Instrumentation.AspNetCore/README.md delete mode 100644 src/OpenTelemetry.Instrumentation.AspNetCore/StatusCanonicalCode.cs diff --git a/OpenTelemetry.sln b/OpenTelemetry.sln index d78c6c78015..1141fd2d434 100644 --- a/OpenTelemetry.sln +++ b/OpenTelemetry.sln @@ -54,8 +54,6 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "OpenTelemetry.Exporter.Zipk EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "OpenTelemetry.Api", "src\OpenTelemetry.Api\OpenTelemetry.Api.csproj", "{99F8A331-05E9-45A5-89BA-4C54E825E5B2}" EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "OpenTelemetry.Instrumentation.AspNetCore", "src\OpenTelemetry.Instrumentation.AspNetCore\OpenTelemetry.Instrumentation.AspNetCore.csproj", "{752D2182-A351-41D8-99EE-DD363D7D5B43}" -EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "testdata", "testdata", "{77C7929A-2EED-4AA6-8705-B5C443C8AA0F}" EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "examples", "examples", "{E359BB2B-9AEC-497D-B321-7DF2450C3B8E}" @@ -365,10 +363,6 @@ Global {99F8A331-05E9-45A5-89BA-4C54E825E5B2}.Debug|Any CPU.Build.0 = Debug|Any CPU {99F8A331-05E9-45A5-89BA-4C54E825E5B2}.Release|Any CPU.ActiveCfg = Release|Any CPU {99F8A331-05E9-45A5-89BA-4C54E825E5B2}.Release|Any CPU.Build.0 = Release|Any CPU - {752D2182-A351-41D8-99EE-DD363D7D5B43}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {752D2182-A351-41D8-99EE-DD363D7D5B43}.Debug|Any CPU.Build.0 = Debug|Any CPU - {752D2182-A351-41D8-99EE-DD363D7D5B43}.Release|Any CPU.ActiveCfg = Release|Any CPU - {752D2182-A351-41D8-99EE-DD363D7D5B43}.Release|Any CPU.Build.0 = Release|Any CPU {AAC408FE-40EF-4479-97D9-697F2C1A0B28}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {AAC408FE-40EF-4479-97D9-697F2C1A0B28}.Debug|Any CPU.Build.0 = Debug|Any CPU {AAC408FE-40EF-4479-97D9-697F2C1A0B28}.Release|Any CPU.ActiveCfg = Release|Any CPU diff --git a/src/OpenTelemetry.Instrumentation.AspNetCore/.publicApi/PublicAPI.Shipped.txt b/src/OpenTelemetry.Instrumentation.AspNetCore/.publicApi/PublicAPI.Shipped.txt deleted file mode 100644 index fc47928891a..00000000000 --- a/src/OpenTelemetry.Instrumentation.AspNetCore/.publicApi/PublicAPI.Shipped.txt +++ /dev/null @@ -1,18 +0,0 @@ -OpenTelemetry.Instrumentation.AspNetCore.AspNetCoreTraceInstrumentationOptions -OpenTelemetry.Instrumentation.AspNetCore.AspNetCoreTraceInstrumentationOptions.AspNetCoreTraceInstrumentationOptions() -> void -OpenTelemetry.Instrumentation.AspNetCore.AspNetCoreTraceInstrumentationOptions.EnrichWithException.get -> System.Action -OpenTelemetry.Instrumentation.AspNetCore.AspNetCoreTraceInstrumentationOptions.EnrichWithException.set -> void -OpenTelemetry.Instrumentation.AspNetCore.AspNetCoreTraceInstrumentationOptions.EnrichWithHttpRequest.get -> System.Action -OpenTelemetry.Instrumentation.AspNetCore.AspNetCoreTraceInstrumentationOptions.EnrichWithHttpRequest.set -> void -OpenTelemetry.Instrumentation.AspNetCore.AspNetCoreTraceInstrumentationOptions.EnrichWithHttpResponse.get -> System.Action -OpenTelemetry.Instrumentation.AspNetCore.AspNetCoreTraceInstrumentationOptions.EnrichWithHttpResponse.set -> void -OpenTelemetry.Instrumentation.AspNetCore.AspNetCoreTraceInstrumentationOptions.Filter.get -> System.Func -OpenTelemetry.Instrumentation.AspNetCore.AspNetCoreTraceInstrumentationOptions.Filter.set -> void -OpenTelemetry.Instrumentation.AspNetCore.AspNetCoreTraceInstrumentationOptions.RecordException.get -> bool -OpenTelemetry.Instrumentation.AspNetCore.AspNetCoreTraceInstrumentationOptions.RecordException.set -> void -OpenTelemetry.Metrics.AspNetCoreInstrumentationMeterProviderBuilderExtensions -OpenTelemetry.Trace.AspNetCoreInstrumentationTracerProviderBuilderExtensions -static OpenTelemetry.Metrics.AspNetCoreInstrumentationMeterProviderBuilderExtensions.AddAspNetCoreInstrumentation(this OpenTelemetry.Metrics.MeterProviderBuilder builder) -> OpenTelemetry.Metrics.MeterProviderBuilder -static OpenTelemetry.Trace.AspNetCoreInstrumentationTracerProviderBuilderExtensions.AddAspNetCoreInstrumentation(this OpenTelemetry.Trace.TracerProviderBuilder builder) -> OpenTelemetry.Trace.TracerProviderBuilder -static OpenTelemetry.Trace.AspNetCoreInstrumentationTracerProviderBuilderExtensions.AddAspNetCoreInstrumentation(this OpenTelemetry.Trace.TracerProviderBuilder builder, string name, System.Action configureAspNetCoreTraceInstrumentationOptions) -> OpenTelemetry.Trace.TracerProviderBuilder -static OpenTelemetry.Trace.AspNetCoreInstrumentationTracerProviderBuilderExtensions.AddAspNetCoreInstrumentation(this OpenTelemetry.Trace.TracerProviderBuilder builder, System.Action configureAspNetCoreTraceInstrumentationOptions) -> OpenTelemetry.Trace.TracerProviderBuilder diff --git a/src/OpenTelemetry.Instrumentation.AspNetCore/.publicApi/PublicAPI.Unshipped.txt b/src/OpenTelemetry.Instrumentation.AspNetCore/.publicApi/PublicAPI.Unshipped.txt deleted file mode 100644 index e69de29bb2d..00000000000 diff --git a/src/OpenTelemetry.Instrumentation.AspNetCore/.publicApi/netstandard2.0/PublicAPI.Shipped.txt b/src/OpenTelemetry.Instrumentation.AspNetCore/.publicApi/netstandard2.0/PublicAPI.Shipped.txt deleted file mode 100644 index e69de29bb2d..00000000000 diff --git a/src/OpenTelemetry.Instrumentation.AspNetCore/.publicApi/netstandard2.0/PublicAPI.Unshipped.txt b/src/OpenTelemetry.Instrumentation.AspNetCore/.publicApi/netstandard2.0/PublicAPI.Unshipped.txt deleted file mode 100644 index e69de29bb2d..00000000000 diff --git a/src/OpenTelemetry.Instrumentation.AspNetCore/AspNetCoreInstrumentation.cs b/src/OpenTelemetry.Instrumentation.AspNetCore/AspNetCoreInstrumentation.cs deleted file mode 100644 index d3096792624..00000000000 --- a/src/OpenTelemetry.Instrumentation.AspNetCore/AspNetCoreInstrumentation.cs +++ /dev/null @@ -1,38 +0,0 @@ -// Copyright The OpenTelemetry Authors -// SPDX-License-Identifier: Apache-2.0 - -using OpenTelemetry.Instrumentation.AspNetCore.Implementation; - -namespace OpenTelemetry.Instrumentation.AspNetCore; - -/// -/// Asp.Net Core Requests instrumentation. -/// -internal sealed class AspNetCoreInstrumentation : IDisposable -{ - private static readonly HashSet DiagnosticSourceEvents = new() - { - "Microsoft.AspNetCore.Hosting.HttpRequestIn", - "Microsoft.AspNetCore.Hosting.HttpRequestIn.Start", - "Microsoft.AspNetCore.Hosting.HttpRequestIn.Stop", - "Microsoft.AspNetCore.Diagnostics.UnhandledException", - "Microsoft.AspNetCore.Hosting.UnhandledException", - }; - - private readonly Func isEnabled = (eventName, _, _) - => DiagnosticSourceEvents.Contains(eventName); - - private readonly DiagnosticSourceSubscriber diagnosticSourceSubscriber; - - public AspNetCoreInstrumentation(HttpInListener httpInListener) - { - this.diagnosticSourceSubscriber = new DiagnosticSourceSubscriber(httpInListener, this.isEnabled, AspNetCoreInstrumentationEventSource.Log.UnknownErrorProcessingEvent); - this.diagnosticSourceSubscriber.Subscribe(); - } - - /// - public void Dispose() - { - this.diagnosticSourceSubscriber?.Dispose(); - } -} diff --git a/src/OpenTelemetry.Instrumentation.AspNetCore/AspNetCoreInstrumentationMeterProviderBuilderExtensions.cs b/src/OpenTelemetry.Instrumentation.AspNetCore/AspNetCoreInstrumentationMeterProviderBuilderExtensions.cs deleted file mode 100644 index 3da8c1de59d..00000000000 --- a/src/OpenTelemetry.Instrumentation.AspNetCore/AspNetCoreInstrumentationMeterProviderBuilderExtensions.cs +++ /dev/null @@ -1,52 +0,0 @@ -// Copyright The OpenTelemetry Authors -// SPDX-License-Identifier: Apache-2.0 - -#if !NET8_0_OR_GREATER -using OpenTelemetry.Instrumentation.AspNetCore; -using OpenTelemetry.Instrumentation.AspNetCore.Implementation; -#endif -using OpenTelemetry.Internal; - -namespace OpenTelemetry.Metrics; - -/// -/// Extension methods to simplify registering of ASP.NET Core request instrumentation. -/// -public static class AspNetCoreInstrumentationMeterProviderBuilderExtensions -{ - /// - /// Enables the incoming requests automatic data collection for ASP.NET Core. - /// - /// being configured. - /// The instance of to chain the calls. - public static MeterProviderBuilder AddAspNetCoreInstrumentation( - this MeterProviderBuilder builder) - { - Guard.ThrowIfNull(builder); - -#if NET8_0_OR_GREATER - return builder.ConfigureMeters(); -#else - // Note: Warm-up the status code and method mapping. - _ = TelemetryHelper.BoxedStatusCodes; - _ = RequestMethodHelper.KnownMethods; - - builder.AddMeter(HttpInMetricsListener.InstrumentationName); - - builder.AddInstrumentation(new AspNetCoreMetrics()); - - return builder; -#endif - } - - internal static MeterProviderBuilder ConfigureMeters(this MeterProviderBuilder builder) - { - return builder - .AddMeter("Microsoft.AspNetCore.Hosting") - .AddMeter("Microsoft.AspNetCore.Server.Kestrel") - .AddMeter("Microsoft.AspNetCore.Http.Connections") - .AddMeter("Microsoft.AspNetCore.Routing") - .AddMeter("Microsoft.AspNetCore.Diagnostics") - .AddMeter("Microsoft.AspNetCore.RateLimiting"); - } -} diff --git a/src/OpenTelemetry.Instrumentation.AspNetCore/AspNetCoreInstrumentationTracerProviderBuilderExtensions.cs b/src/OpenTelemetry.Instrumentation.AspNetCore/AspNetCoreInstrumentationTracerProviderBuilderExtensions.cs deleted file mode 100644 index 8ec35718ccd..00000000000 --- a/src/OpenTelemetry.Instrumentation.AspNetCore/AspNetCoreInstrumentationTracerProviderBuilderExtensions.cs +++ /dev/null @@ -1,123 +0,0 @@ -// Copyright The OpenTelemetry Authors -// SPDX-License-Identifier: Apache-2.0 - -using System.Diagnostics; -using Microsoft.Extensions.DependencyInjection; -using Microsoft.Extensions.Options; -using OpenTelemetry.Instrumentation.AspNetCore; -using OpenTelemetry.Instrumentation.AspNetCore.Implementation; -using OpenTelemetry.Internal; - -namespace OpenTelemetry.Trace; - -/// -/// Extension methods to simplify registering of ASP.NET Core request instrumentation. -/// -public static class AspNetCoreInstrumentationTracerProviderBuilderExtensions -{ - /// - /// Enables the incoming requests automatic data collection for ASP.NET Core. - /// - /// being configured. - /// The instance of to chain the calls. - public static TracerProviderBuilder AddAspNetCoreInstrumentation(this TracerProviderBuilder builder) - => AddAspNetCoreInstrumentation(builder, name: null, configureAspNetCoreTraceInstrumentationOptions: null); - - /// - /// Enables the incoming requests automatic data collection for ASP.NET Core. - /// - /// being configured. - /// Callback action for configuring . - /// The instance of to chain the calls. - public static TracerProviderBuilder AddAspNetCoreInstrumentation( - this TracerProviderBuilder builder, - Action configureAspNetCoreTraceInstrumentationOptions) - => AddAspNetCoreInstrumentation(builder, name: null, configureAspNetCoreTraceInstrumentationOptions); - - /// - /// Enables the incoming requests automatic data collection for ASP.NET Core. - /// - /// being configured. - /// Name which is used when retrieving options. - /// Callback action for configuring . - /// The instance of to chain the calls. - public static TracerProviderBuilder AddAspNetCoreInstrumentation( - this TracerProviderBuilder builder, - string name, - Action configureAspNetCoreTraceInstrumentationOptions) - { - Guard.ThrowIfNull(builder); - - // Note: Warm-up the status code and method mapping. - _ = TelemetryHelper.BoxedStatusCodes; - _ = RequestMethodHelper.KnownMethods; - - name ??= Options.DefaultName; - - builder.ConfigureServices(services => - { - if (configureAspNetCoreTraceInstrumentationOptions != null) - { - services.Configure(name, configureAspNetCoreTraceInstrumentationOptions); - } - - services.RegisterOptionsFactory(configuration => new AspNetCoreTraceInstrumentationOptions(configuration)); - }); - - if (builder is IDeferredTracerProviderBuilder deferredTracerProviderBuilder) - { - deferredTracerProviderBuilder.Configure((sp, builder) => - { - AddAspNetCoreInstrumentationSources(builder, sp); - }); - } - - return builder.AddInstrumentation(sp => - { - var options = sp.GetRequiredService>().Get(name); - - return new AspNetCoreInstrumentation( - new HttpInListener(options)); - }); - } - - // Note: This is used by unit tests. - internal static TracerProviderBuilder AddAspNetCoreInstrumentation( - this TracerProviderBuilder builder, - HttpInListener listener) - { - builder.AddAspNetCoreInstrumentationSources(); - - return builder.AddInstrumentation( - new AspNetCoreInstrumentation(listener)); - } - - private static void AddAspNetCoreInstrumentationSources( - this TracerProviderBuilder builder, - IServiceProvider serviceProvider = null) - { - // For .NET7.0 onwards activity will be created using activitySource. - // https://github.com/dotnet/aspnetcore/blob/bf3352f2422bf16fa3ca49021f0e31961ce525eb/src/Hosting/Hosting/src/Internal/HostingApplicationDiagnostics.cs#L327 - // For .NET6.0 and below, we will continue to use legacy way. - if (HttpInListener.Net7OrGreater) - { - // TODO: Check with .NET team to see if this can be prevented - // as this allows user to override the ActivitySource. - var activitySourceService = serviceProvider?.GetService(); - if (activitySourceService != null) - { - builder.AddSource(activitySourceService.Name); - } - else - { - // For users not using hosting package? - builder.AddSource(HttpInListener.AspNetCoreActivitySourceName); - } - } - else - { - builder.AddSource(HttpInListener.ActivitySourceName); - builder.AddLegacySource(HttpInListener.ActivityOperationName); // for the activities created by AspNetCore - } - } -} diff --git a/src/OpenTelemetry.Instrumentation.AspNetCore/AspNetCoreMetrics.cs b/src/OpenTelemetry.Instrumentation.AspNetCore/AspNetCoreMetrics.cs deleted file mode 100644 index a819d561a9e..00000000000 --- a/src/OpenTelemetry.Instrumentation.AspNetCore/AspNetCoreMetrics.cs +++ /dev/null @@ -1,41 +0,0 @@ -// Copyright The OpenTelemetry Authors -// SPDX-License-Identifier: Apache-2.0 - -#if !NET8_0_OR_GREATER -using OpenTelemetry.Instrumentation.AspNetCore.Implementation; - -namespace OpenTelemetry.Instrumentation.AspNetCore; - -/// -/// Asp.Net Core Requests instrumentation. -/// -internal sealed class AspNetCoreMetrics : IDisposable -{ - private static readonly HashSet DiagnosticSourceEvents = new() - { - "Microsoft.AspNetCore.Hosting.HttpRequestIn", - "Microsoft.AspNetCore.Hosting.HttpRequestIn.Start", - "Microsoft.AspNetCore.Hosting.HttpRequestIn.Stop", - "Microsoft.AspNetCore.Diagnostics.UnhandledException", - "Microsoft.AspNetCore.Hosting.UnhandledException", - }; - - private readonly Func isEnabled = (eventName, _, _) - => DiagnosticSourceEvents.Contains(eventName); - - private readonly DiagnosticSourceSubscriber diagnosticSourceSubscriber; - - internal AspNetCoreMetrics() - { - var metricsListener = new HttpInMetricsListener("Microsoft.AspNetCore"); - this.diagnosticSourceSubscriber = new DiagnosticSourceSubscriber(metricsListener, this.isEnabled, AspNetCoreInstrumentationEventSource.Log.UnknownErrorProcessingEvent); - this.diagnosticSourceSubscriber.Subscribe(); - } - - /// - public void Dispose() - { - this.diagnosticSourceSubscriber?.Dispose(); - } -} -#endif diff --git a/src/OpenTelemetry.Instrumentation.AspNetCore/AspNetCoreTraceInstrumentationOptions.cs b/src/OpenTelemetry.Instrumentation.AspNetCore/AspNetCoreTraceInstrumentationOptions.cs deleted file mode 100644 index f5ffb7962f8..00000000000 --- a/src/OpenTelemetry.Instrumentation.AspNetCore/AspNetCoreTraceInstrumentationOptions.cs +++ /dev/null @@ -1,115 +0,0 @@ -// Copyright The OpenTelemetry Authors -// SPDX-License-Identifier: Apache-2.0 - -using System.Diagnostics; -using Microsoft.AspNetCore.Http; -using Microsoft.Extensions.Configuration; -using OpenTelemetry.Instrumentation.AspNetCore.Implementation; - -namespace OpenTelemetry.Instrumentation.AspNetCore; - -/// -/// Options for requests instrumentation. -/// -public class AspNetCoreTraceInstrumentationOptions -{ - /// - /// Initializes a new instance of the class. - /// - public AspNetCoreTraceInstrumentationOptions() - : this(new ConfigurationBuilder().AddEnvironmentVariables().Build()) - { - } - - internal AspNetCoreTraceInstrumentationOptions(IConfiguration configuration) - { - Debug.Assert(configuration != null, "configuration was null"); - - if (configuration.TryGetBoolValue( - AspNetCoreInstrumentationEventSource.Log, - "OTEL_DOTNET_EXPERIMENTAL_ASPNETCORE_ENABLE_GRPC_INSTRUMENTATION", - out var enableGrpcInstrumentation)) - { - this.EnableGrpcAspNetCoreSupport = enableGrpcInstrumentation; - } - - if (configuration.TryGetBoolValue( - AspNetCoreInstrumentationEventSource.Log, - "OTEL_DOTNET_EXPERIMENTAL_ASPNETCORE_DISABLE_URL_QUERY_REDACTION", - out var disableUrlQueryRedaction)) - { - this.DisableUrlQueryRedaction = disableUrlQueryRedaction; - } - } - - /// - /// Gets or sets a filter function that determines whether or not to - /// collect telemetry on a per request basis. - /// - /// - /// Notes: - /// - /// The return value for the filter function is interpreted as: - /// - /// If filter returns , the request is - /// collected. - /// If filter returns or throws an - /// exception the request is NOT collected. - /// - /// - /// - public Func Filter { get; set; } - - /// - /// Gets or sets an action to enrich an Activity. - /// - /// - /// : the activity being enriched. - /// : the HttpRequest object from which additional information can be extracted to enrich the activity. - /// - public Action EnrichWithHttpRequest { get; set; } - - /// - /// Gets or sets an action to enrich an Activity. - /// - /// - /// : the activity being enriched. - /// : the HttpResponse object from which additional information can be extracted to enrich the activity. - /// - public Action EnrichWithHttpResponse { get; set; } - - /// - /// Gets or sets an action to enrich an Activity. - /// - /// - /// : the activity being enriched. - /// : the Exception object from which additional information can be extracted to enrich the activity. - /// - public Action EnrichWithException { get; set; } - - /// - /// Gets or sets a value indicating whether the exception will be recorded as ActivityEvent or not. - /// - /// - /// https://github.com/open-telemetry/semantic-conventions/blob/main/docs/exceptions/exceptions-spans.md. - /// - public bool RecordException { get; set; } - - /// - /// Gets or sets a value indicating whether RPC attributes are added to an Activity when using Grpc.AspNetCore. - /// - /// - /// https://github.com/open-telemetry/semantic-conventions/blob/main/docs/rpc/rpc-spans.md. - /// - internal bool EnableGrpcAspNetCoreSupport { get; set; } - - /// - /// Gets or sets a value indicating whether the url query value should be redacted or not. - /// - /// - /// The query parameter values are redacted with value set as Redacted. - /// e.g. `?key1=value1` is set as `?key1=Redacted`. - /// The redaction can be disabled by setting this property to . - /// - internal bool DisableUrlQueryRedaction { get; set; } -} diff --git a/src/OpenTelemetry.Instrumentation.AspNetCore/AssemblyInfo.cs b/src/OpenTelemetry.Instrumentation.AspNetCore/AssemblyInfo.cs deleted file mode 100644 index 2cd1a339fd4..00000000000 --- a/src/OpenTelemetry.Instrumentation.AspNetCore/AssemblyInfo.cs +++ /dev/null @@ -1,10 +0,0 @@ -// Copyright The OpenTelemetry Authors -// SPDX-License-Identifier: Apache-2.0 - -using System.Runtime.CompilerServices; - -#if SIGNED -[assembly: InternalsVisibleTo("OpenTelemetry.Instrumentation.AspNetCore.Tests, PublicKey=002400000480000094000000060200000024000052534131000400000100010051c1562a090fb0c9f391012a32198b5e5d9a60e9b80fa2d7b434c9e5ccb7259bd606e66f9660676afc6692b8cdc6793d190904551d2103b7b22fa636dcbb8208839785ba402ea08fc00c8f1500ccef28bbf599aa64ffb1e1d5dc1bf3420a3777badfe697856e9d52070a50c3ea5821c80bef17ca3acffa28f89dd413f096f898")] -#else -[assembly: InternalsVisibleTo("OpenTelemetry.Instrumentation.AspNetCore.Tests")] -#endif diff --git a/src/OpenTelemetry.Instrumentation.AspNetCore/CHANGELOG.md b/src/OpenTelemetry.Instrumentation.AspNetCore/CHANGELOG.md deleted file mode 100644 index d8f74ef6cc5..00000000000 --- a/src/OpenTelemetry.Instrumentation.AspNetCore/CHANGELOG.md +++ /dev/null @@ -1,638 +0,0 @@ -# Changelog - -## 1.8.1 - -Released 2024-Apr-12 - -* **Breaking Change**: Fixed tracing instrumentation so that by default any - values detected in the query string component of requests are replaced with - the text `Redacted` when building the `url.query` tag. For example, - `?key1=value1&key2=value2` becomes `?key1=Redacted&key2=Redacted`. You can - disable this redaction by setting the environment variable - `OTEL_DOTNET_EXPERIMENTAL_ASPNETCORE_DISABLE_URL_QUERY_REDACTION` to `true`. - ([#5532](https://github.com/open-telemetry/opentelemetry-dotnet/pull/5532)) - -## 1.8.0 - -Released 2024-Apr-04 - -* Fixed an issue for spans when `server.port` attribute was not set with - `server.address` when it has default values (`80` for `HTTP` and - `443` for `HTTPS` protocol). - ([#5419](https://github.com/open-telemetry/opentelemetry-dotnet/pull/5419)) - -* Fixed an issue where the `http.request.method_original` attribute was not set - on activity. Now, when `http.request.method` is set and the original method - is converted to its canonical form (e.g., `Get` is converted to `GET`), - the original value `Get` will be stored in `http.request.method_original`. - ([#5471](https://github.com/open-telemetry/opentelemetry-dotnet/pull/5471)) - -* Fixed the name of spans that have `http.request.method` attribute set to `_OTHER`. - The span name will be set as `HTTP {http.route}` as per the [specification](https://github.com/open-telemetry/semantic-conventions/blob/v1.24.0/docs/http/http-spans.md#name). - ([#5484](https://github.com/open-telemetry/opentelemetry-dotnet/pull/5484)) - -## 1.7.1 - -Released 2024-Feb-09 - -* Fixed issue - [#4466](https://github.com/open-telemetry/opentelemetry-dotnet/issues/4466) - where the activity instance returned by `Activity.Current` was different than - instance obtained from `IHttpActivityFeature.Activity`. - ([#5136](https://github.com/open-telemetry/opentelemetry-dotnet/pull/5136)) - -* Fixed an issue where the `http.route` attribute was not set on either the - `Activity` or `http.server.request.duration` metric generated from a - request when an exception handling middleware is invoked. One caveat is that - this fix does not address the problem for the `http.server.request.duration` - metric when running ASP.NET Core 8. ASP.NET Core 8 contains an equivalent fix - which should ship in version 8.0.2 - (see: [dotnet/aspnetcore#52652](https://github.com/dotnet/aspnetcore/pull/52652)). - ([#5135](https://github.com/open-telemetry/opentelemetry-dotnet/pull/5135)) - -* Fixes scenario when the `net6.0` target of this library is loaded into a - .NET 7+ process and the instrumentation does not behave as expected. This - is an unusual scenario that does not affect users consuming this package - normally. This fix is primarily to support the - [opentelemetry-dotnet-instrumentation](https://github.com/open-telemetry/opentelemetry-dotnet/pull/5252) - project. - ([#5252](https://github.com/open-telemetry/opentelemetry-dotnet/pull/5252)) - -## 1.7.0 - -Released 2023-Dec-13 - -## 1.6.0 - First stable release of this library - -Released 2023-Dec-13 - -* Re-introduced support for gRPC instrumentation as an opt-in experimental - feature. From now onwards, gRPC can be enabled by setting - `OTEL_DOTNET_EXPERIMENTAL_ASPNETCORE_ENABLE_GRPC_INSTRUMENTATION` flag to - `True`. `OTEL_DOTNET_EXPERIMENTAL_ASPNETCORE_ENABLE_GRPC_INSTRUMENTATION` can - be set as an environment variable or via IConfiguration. The change is - introduced in order to support stable release of `http` instrumentation. - Semantic conventions for RPC is still - [experimental](https://github.com/open-telemetry/semantic-conventions/tree/main/docs/rpc) - and hence the package will only support it as an opt-in experimental feature. - Note that the support was removed in `1.6.0-rc.1` version of the package and - versions released before `1.6.0-rc.1` had gRPC instrumentation enabled by - default. - ([#5130](https://github.com/open-telemetry/opentelemetry-dotnet/pull/5130)) - -## 1.6.0-rc.1 - -Released 2023-Dec-01 - -* Removed support for `OTEL_SEMCONV_STABILITY_OPT_IN` environment variable. The - library will now emit only the - [stable](https://github.com/open-telemetry/semantic-conventions/tree/v1.23.0/docs/http) - semantic conventions. - ([#5066](https://github.com/open-telemetry/opentelemetry-dotnet/pull/5066)) - -* Removed `netstandard2.1` target. - ([#5094](https://github.com/open-telemetry/opentelemetry-dotnet/pull/5094)) - -* Removed support for grpc instrumentation to unblock stable release of http - instrumentation. For details, see issue - [#5098](https://github.com/open-telemetry/opentelemetry-dotnet/issues/5098) - ([#5097](https://github.com/open-telemetry/opentelemetry-dotnet/pull/5097)) - -* **Breaking Change** : Renamed `AspNetCoreInstrumentationOptions` to - `AspNetCoreTraceInstrumentationOptions`. - ([#5108](https://github.com/open-telemetry/opentelemetry-dotnet/pull/5108)) - -## 1.6.0-beta.3 - -Released 2023-Nov-17 - -* Removed the Activity Status Description that was being set during - exceptions. Activity Status will continue to be reported as `Error`. - This is a **breaking change**. `EnrichWithException` can be leveraged - to restore this behavior. - ([#5025](https://github.com/open-telemetry/opentelemetry-dotnet/pull/5025)) - -* Updated `http.request.method` to match specification guidelines. - * For activity, if the method does not belong to one of the [known - values](https://github.com/open-telemetry/semantic-conventions/blob/v1.22.0/docs/http/http-spans.md#:~:text=http.request.method%20has%20the%20following%20list%20of%20well%2Dknown%20values) - then the request method will be set on an additional tag - `http.request.method.original` and `http.request.method` will be set to - `_OTHER`. - * For metrics, if the original method does not belong to one of the [known - values](https://github.com/open-telemetry/semantic-conventions/blob/v1.22.0/docs/http/http-spans.md#:~:text=http.request.method%20has%20the%20following%20list%20of%20well%2Dknown%20values) - then `http.request.method` on `http.server.request.duration` metric will be - set to `_OTHER` - - `http.request.method` is set on `http.server.request.duration` metric or - activity when `OTEL_SEMCONV_STABILITY_OPT_IN` environment variable is set to - `http` or `http/dup`. - ([#5001](https://github.com/open-telemetry/opentelemetry-dotnet/pull/5001)) - -* An additional attribute `error.type` will be added to activity and -`http.server.request.duration` metric when the request results in unhandled -exception. The attribute value will be set to full name of exception type. - - The attribute will only be added when `OTEL_SEMCONV_STABILITY_OPT_IN` - environment variable is set to `http` or `http/dup`. - ([#4986](https://github.com/open-telemetry/opentelemetry-dotnet/pull/4986)) - -* Fixed `network.protocol.version` attribute values to match the specification. - ([#5007](https://github.com/open-telemetry/opentelemetry-dotnet/pull/5007)) - -* Calls to `/metrics` will now be included in the `http.server.request.duration` - metric. This change may affect Prometheus pull scenario if the Prometheus - server sends request to the scraping endpoint that contains `/metrics` in - path. - ([#5044](https://github.com/open-telemetry/opentelemetry-dotnet/pull/5044)) - -* Fixes the `http.route` attribute for scenarios in which it was - previously missing or incorrect. Additionally, the `http.route` attribute - is now the same for both the metric and `Activity` emitted for a request. - Lastly, the `Activity.DisplayName` has been adjusted to have the format - `{http.request.method} {http.route}` to conform with [the specification](https://github.com/open-telemetry/semantic-conventions/blob/main/docs/http/http-spans.md#name). - There remain scenarios when using conventional routing or Razor pages where - `http.route` is still incorrect. See [#5056](https://github.com/open-telemetry/opentelemetry-dotnet/issues/5056) - and [#5057](https://github.com/open-telemetry/opentelemetry-dotnet/issues/5057) - for more details. - ([#5026](https://github.com/open-telemetry/opentelemetry-dotnet/pull/5026)) - -* Removed `network.protocol.name` from `http.server.request.duration` metric as - per spec. - ([#5049](https://github.com/open-telemetry/opentelemetry-dotnet/pull/5049)) - -## 1.6.0-beta.2 - -Released 2023-Oct-26 - -* Introduced a new metric, `http.server.request.duration` measured in seconds. - The OTel SDK (starting with version 1.6.0) - [applies custom histogram buckets](https://github.com/open-telemetry/opentelemetry-dotnet/pull/4820) - for this metric to comply with the - [Semantic Convention for Http Metrics](https://github.com/open-telemetry/semantic-conventions/blob/2bad9afad58fbd6b33cc683d1ad1f006e35e4a5d/docs/http/http-metrics.md). - This new metric is only available for users who opt-in to the new - semantic convention by configuring the `OTEL_SEMCONV_STABILITY_OPT_IN` - environment variable to either `http` (to emit only the new metric) or - `http/dup` (to emit both the new and old metrics). - ([#4802](https://github.com/open-telemetry/opentelemetry-dotnet/pull/4802)) - * New metric: `http.server.request.duration` - * Unit: `s` (seconds) - * Histogram Buckets: `0, 0.005, 0.01, 0.025, 0.05, 0.075, 0.1, 0.25, 0.5, - 0.75, 1, 2.5, 5, 7.5, 10` - * Old metric: `http.server.duration` - * Unit: `ms` (milliseconds) - * Histogram Buckets: `0, 5, 10, 25, 50, 75, 100, 250, 500, 750, 1000, 2500, - 5000, 7500, 10000` - - Note: the older `http.server.duration` metric and - `OTEL_SEMCONV_STABILITY_OPT_IN` environment variable will eventually be - removed after the HTTP semantic conventions are marked stable. - At which time this instrumentation can publish a stable release. Refer to - the specification for more information regarding the new HTTP semantic - conventions for both - [spans](https://github.com/open-telemetry/semantic-conventions/blob/2bad9afad58fbd6b33cc683d1ad1f006e35e4a5d/docs/http/http-spans.md) - and - [metrics](https://github.com/open-telemetry/semantic-conventions/blob/2bad9afad58fbd6b33cc683d1ad1f006e35e4a5d/docs/http/http-metrics.md). - -* Following metrics will now be enabled by default when targeting `.NET8.0` or - newer framework: - - * **Meter** : `Microsoft.AspNetCore.Hosting` - * `http.server.request.duration` - * `http.server.active_requests` - - * **Meter** : `Microsoft.AspNetCore.Server.Kestrel` - * `kestrel.active_connections` - * `kestrel.connection.duration` - * `kestrel.rejected_connections` - * `kestrel.queued_connections` - * `kestrel.queued_requests` - * `kestrel.upgraded_connections` - * `kestrel.tls_handshake.duration` - * `kestrel.active_tls_handshakes` - - * **Meter** : `Microsoft.AspNetCore.Http.Connections` - * `signalr.server.connection.duration` - * `signalr.server.active_connections` - - * **Meter** : `Microsoft.AspNetCore.Routing` - * `aspnetcore.routing.match_attempts` - - * **Meter** : `Microsoft.AspNetCore.Diagnostics` - * `aspnetcore.diagnostics.exceptions` - - * **Meter** : `Microsoft.AspNetCore.RateLimiting` - * `aspnetcore.rate_limiting.active_request_leases` - * `aspnetcore.rate_limiting.request_lease.duration` - * `aspnetcore.rate_limiting.queued_requests` - * `aspnetcore.rate_limiting.request.time_in_queue` - * `aspnetcore.rate_limiting.requests` - - For details about each individual metric check [ASP.NET Core - docs - page](https://learn.microsoft.com/dotnet/core/diagnostics/built-in-metrics-aspnetcore). - - **NOTES**: - * When targeting `.NET8.0` framework or newer, `http.server.request.duration` metric - will only follow - [v1.22.0](https://github.com/open-telemetry/semantic-conventions/blob/v1.22.0/docs/http/http-metrics.md#metric-httpclientrequestduration) - semantic conventions specification. Ability to switch behavior to older - conventions using `OTEL_SEMCONV_STABILITY_OPT_IN` environment variable is - not available. - * Users can opt-out of metrics that are not required using - [views](https://github.com/open-telemetry/opentelemetry-dotnet/tree/main/docs/metrics/customizing-the-sdk#drop-an-instrument). - - ([#4934](https://github.com/open-telemetry/opentelemetry-dotnet/pull/4934)) - -* Added `network.protocol.name` dimension to `http.server.request.duration` -metric. This change only affects users setting `OTEL_SEMCONV_STABILITY_OPT_IN` -to `http` or `http/dup`. -([#4934](https://github.com/open-telemetry/opentelemetry-dotnet/pull/4934)) - -* **Breaking**: Removed `Enrich` and `Filter` support for **metrics** - instrumentation. With this change, `AspNetCoreMetricsInstrumentationOptions` - is no longer available. - ([#4981](https://github.com/open-telemetry/opentelemetry-dotnet/pull/4981)) - - * `Enrich` migration: - - An enrichment API for the `http.server.request.duration` metric is available - inside AspNetCore for users targeting .NET 8.0 (or newer). For details see: - [Enrich the ASP.NET Core request - metric](https://learn.microsoft.com/aspnet/core/log-mon/metrics/metrics?view=aspnetcore-8.0#enrich-the-aspnet-core-request-metric). - - * `Filter` migration: - - There is no comparable filter mechanism currently available for any .NET - version. Please [share your - feedback](https://github.com/open-telemetry/opentelemetry-dotnet/issues/4982) - if you are impacted by this feature gap. - - > **Note** - > The [View API](https://github.com/open-telemetry/opentelemetry-dotnet/tree/main/docs/metrics/customizing-the-sdk#select-specific-tags) - may be used to drop dimensions. - -* Updated description for `http.server.request.duration` metrics to match spec - definition. - ([#4990](https://github.com/open-telemetry/opentelemetry-dotnet/pull/4990)) - -## 1.5.1-beta.1 - -Released 2023-Jul-20 - -* The new HTTP and network semantic conventions can be opted in to by setting - the `OTEL_SEMCONV_STABILITY_OPT_IN` environment variable. This allows for a - transition period for users to experiment with the new semantic conventions - and adapt as necessary. The environment variable supports the following - values: - * `http` - emit the new, frozen (proposed for stable) HTTP and networking - attributes, and stop emitting the old experimental HTTP and networking - attributes that the instrumentation emitted previously. - * `http/dup` - emit both the old and the frozen (proposed for stable) HTTP - and networking attributes, allowing for a more seamless transition. - * The default behavior (in the absence of one of these values) is to continue - emitting the same HTTP and network semantic conventions that were emitted in - `1.5.0-beta.1`. - * Note: this option will eventually be removed after the new HTTP and - network semantic conventions are marked stable. At which time this - instrumentation can receive a stable release, and the old HTTP and - network semantic conventions will no longer be supported. Refer to the - specification for more information regarding the new HTTP and network - semantic conventions for both - [spans](https://github.com/open-telemetry/semantic-conventions/blob/v1.21.0/docs/http/http-spans.md) - and - [metrics](https://github.com/open-telemetry/semantic-conventions/blob/v1.21.0/docs/http/http-metrics.md). - ([#4537](https://github.com/open-telemetry/opentelemetry-dotnet/pull/4537), - [#4606](https://github.com/open-telemetry/opentelemetry-dotnet/pull/4606), - [#4660](https://github.com/open-telemetry/opentelemetry-dotnet/pull/4660)) - -* Fixed an issue affecting NET 7.0+. If custom propagation is being used - and tags are added to an Activity during sampling then that Activity would be dropped. - ([#4637](https://github.com/open-telemetry/opentelemetry-dotnet/pull/4637)) - -## 1.5.0-beta.1 - -Released 2023-Jun-05 - -* Bumped the package version to `1.5.0-beta.1` to keep its major and minor - version in sync with that of the core packages. This would make it more - intuitive for users to figure out what version of core packages would work - with a given version of this package. The pre-release identifier has also been - changed from `rc` to `beta` as we believe this more accurately reflects the - status of this package. We believe the `rc` identifier will be more - appropriate as semantic conventions reach stability. - -* Fix issue where baggage gets cleared when the ASP.NET Core Activity - is stopped. The instrumentation no longer clears baggage. One problem - this caused was that it prevented Activity processors from accessing baggage - during their `OnEnd` call. -([#4274](https://github.com/open-telemetry/opentelemetry-dotnet/pull/4274)) - -* Added direct reference to `System.Text.Encodings.Web` with minimum version of -`4.7.2` due to [CVE-2021-26701](https://github.com/dotnet/runtime/issues/49377). -This impacts target frameworks `netstandard2.0` and `netstandard2.1` which has a -reference to `Microsoft.AspNetCore.Http.Abstractions` that depends on -`System.Text.Encodings.Web` >= 4.5.0. -([#4399](https://github.com/open-telemetry/opentelemetry-dotnet/pull/4399)) - -* Improve perf by avoiding boxing of common status codes values. - ([#4360](https://github.com/open-telemetry/opentelemetry-dotnet/pull/4360), - [#4363](https://github.com/open-telemetry/opentelemetry-dotnet/pull/4363)) - -## 1.0.0-rc9.14 - -Released 2023-Feb-24 - -* Updated OTel SDK dependency to 1.4.0 - -## 1.4.0-rc9.13 - -Released 2023-Feb-10 - -## 1.0.0-rc9.12 - -Released 2023-Feb-01 - -## 1.0.0-rc9.11 - -Released 2023-Jan-09 - -## 1.0.0-rc9.10 - -Released 2022-Dec-12 - -* **Users migrating from version `1.0.0-rc9.9` will see the following breaking - changes:** - * Updated `http.status_code` dimension type from string to int for - `http.server.duration` metric. - ([#3930](https://github.com/open-telemetry/opentelemetry-dotnet/pull/3930)) - * `http.host` will no longer be populated on `http.server.duration` metric. - `net.host.name` and `net.host.port` attributes will be populated instead. -([#3928](https://github.com/open-telemetry/opentelemetry-dotnet/pull/3928)) - - * The `http.server.duration` metric's `http.target` attribute is replaced with -`http.route` attribute. -([#3903](https://github.com/open-telemetry/opentelemetry-dotnet/pull/3903)) - - * `http.host` will no longer be populated on activity. `net.host.name` and - `net.host.port` attributes will be populated instead. - ([#3858](https://github.com/open-telemetry/opentelemetry-dotnet/pull/3858)) - -* Extension method `AddAspNetCoreInstrumentation` on `MeterProviderBuilder` now - supports `AspNetCoreMetricsInstrumentationOptions`. This option class exposes - configuration properties for metric filtering and tag enrichment. - ([#3948](https://github.com/open-telemetry/opentelemetry-dotnet/pull/3948), - [#3982](https://github.com/open-telemetry/opentelemetry-dotnet/pull/3982)) - -## 1.0.0-rc9.9 - -Released 2022-Nov-07 - -* **Breaking change** The `Enrich` callback option has been removed. - For better usability, it has been replaced by three separate options: - `EnrichWithHttpRequest`, `EnrichWithHttpResponse` and `EnrichWithException`. - Previously, the single `Enrich` callback required the consumer to detect - which event triggered the callback to be invoked (e.g., request start, - response end, or an exception) and then cast the object received to the - appropriate type: `HttpRequest`, `HttpResponse`, or `Exception`. The separate - callbacks make it clear what event triggers them and there is no longer the - need to cast the argument to the expected type. - ([#3749](https://github.com/open-telemetry/opentelemetry-dotnet/pull/3749)) - -* Added back `netstandard2.0` and `netstandard2.1` targets. -([#3755](https://github.com/open-telemetry/opentelemetry-dotnet/pull/3755)) - -## 1.0.0-rc9.8 - -Released 2022-Oct-17 - -## 1.0.0-rc9.7 - -Released 2022-Sep-29 - -* Performance improvement (Reduced memory allocation) - Updated DiagnosticSource -event subscription to specific set of events. -([#3519](https://github.com/open-telemetry/opentelemetry-dotnet/pull/3519)) - -* Added overloads which accept a name to the `TracerProviderBuilder` - `AddAspNetCoreInstrumentation` extension to allow for more fine-grained - options management - ([#3661](https://github.com/open-telemetry/opentelemetry-dotnet/pull/3661)) - -* Fix issue where when an application has an ExceptionFilter, the exception data - wouldn't be collected. - ([#3475](https://github.com/open-telemetry/opentelemetry-dotnet/pull/3475)) - -## 1.0.0-rc9.6 - -Released 2022-Aug-18 - -* Removed `netstandard2.0` and `netstandard2.1` targets. .NET 5 reached EOL - in May 2022 and .NET Core 3.1 reaches EOL in December 2022. End of support - dates for .NET are published - [here](https://dotnet.microsoft.com/download/dotnet). The - instrumentation for ASP.NET Core now requires .NET 6 or later. - ([#3567](https://github.com/open-telemetry/opentelemetry-dotnet/pull/3567)) - -* Fixed an issue where activity started within middleware was modified by - instrumentation library. - ([#3498](https://github.com/open-telemetry/opentelemetry-dotnet/pull/3498)) - -* Updated to use Activity native support from - `System.Diagnostics.DiagnosticSource` to set activity status. - ([#3118](https://github.com/open-telemetry/opentelemetry-dotnet/issues/3118)) - ([#3555](https://github.com/open-telemetry/opentelemetry-dotnet/pull/3555)) - -## 1.0.0-rc9.5 - -Released 2022-Aug-02 - -* Fix Remote IP Address - NULL reference exception. - ([#3481](https://github.com/open-telemetry/opentelemetry-dotnet/pull/3481)) -* Metrics instrumentation to correctly populate `http.flavor` tag. - (1.1 instead of HTTP/1.1 etc.) - ([#3379](https://github.com/open-telemetry/opentelemetry-dotnet/pull/3379)) -* Tracing instrumentation to populate `http.flavor` tag. - ([#3372](https://github.com/open-telemetry/opentelemetry-dotnet/pull/3372)) -* Tracing instrumentation to populate `http.scheme` tag. - ([#3392](https://github.com/open-telemetry/opentelemetry-dotnet/pull/3392)) - -## 1.0.0-rc9.4 - -Released 2022-Jun-03 - -* Added additional metric dimensions. - ([#3247](https://github.com/open-telemetry/opentelemetry-dotnet/pull/3247)) -* Removes net5.0 target as .NET 5.0 is going out - of support. The package keeps netstandard2.1 target, so it - can still be used with .NET5.0 apps. - ([#3147](https://github.com/open-telemetry/opentelemetry-dotnet/issues/3147)) - -## 1.0.0-rc9.3 - -Released 2022-Apr-15 - -## 1.0.0-rc9.2 - -Released 2022-Apr-12 - -## 1.0.0-rc9.1 - -Released 2022-Mar-30 - -* Fix: Http server span status is now unset for `400`-`499`. - ([#2904](https://github.com/open-telemetry/opentelemetry-dotnet/pull/2904)) -* Fix: drop direct reference of the `Microsoft.AspNetCore.Http.Features` from - net5 & net6 targets (already part of the FrameworkReference since the net5). - ([#2860](https://github.com/open-telemetry/opentelemetry-dotnet/pull/2860)) -* Reduce allocations calculating the http.url tag. - ([#2947](https://github.com/open-telemetry/opentelemetry-dotnet/pull/2947)) - -## 1.0.0-rc10 (broken. use 1.0.0-rc9.1 and newer) - -Released 2022-Mar-04 - -## 1.0.0-rc9 - -Released 2022-Feb-02 - -## 1.0.0-rc8 - -Released 2021-Oct-08 - -* Replaced `http.path` tag on activity with `http.target`. - ([#2266](https://github.com/open-telemetry/opentelemetry-dotnet/pull/2266)) - -## 1.0.0-rc7 - -Released 2021-Jul-12 - -## 1.0.0-rc6 - -Released 2021-Jun-25 - -## 1.0.0-rc5 - -Released 2021-Jun-09 - -* Fixes bug - [#1740](https://github.com/open-telemetry/opentelemetry-dotnet/issues/1740): - Instrumentation.AspNetCore for gRPC services omits ALL rpc.* attributes under - certain conditions - ([#1879](https://github.com/open-telemetry/opentelemetry-dotnet/pull/1879)) - -## 1.0.0-rc4 - -Released 2021-Apr-23 - -* When using OpenTelemetry.Extensions.Hosting you can now bind - `AspNetCoreInstrumentationOptions` from DI. - ([#1997](https://github.com/open-telemetry/opentelemetry-dotnet/pull/1997)) - -## 1.0.0-rc3 - -Released 2021-Mar-19 - -* Leverages added AddLegacySource API from OpenTelemetry SDK to trigger Samplers - and ActivityProcessors. Samplers, ActivityProcessor.OnStart will now get the - Activity before any enrichment done by the instrumentation. - ([#1836](https://github.com/open-telemetry/opentelemetry-dotnet/pull/1836)) -* Performance optimization by leveraging sampling decision and short circuiting - activity enrichment. `Filter` and `Enrich` are now only called if - `activity.IsAllDataRequested` is `true` - ([#1899](https://github.com/open-telemetry/opentelemetry-dotnet/pull/1899)) - -## 1.0.0-rc2 - -Released 2021-Jan-29 - -## 1.0.0-rc1.1 - -Released 2020-Nov-17 - -* AspNetCoreInstrumentation sets ActivitySource to activities created outside - ActivitySource. - ([#1515](https://github.com/open-telemetry/opentelemetry-dotnet/pull/1515/)) -* For gRPC invocations, leading forward slash is trimmed from span name in order - to conform to the specification. - ([#1551](https://github.com/open-telemetry/opentelemetry-dotnet/pull/1551)) - -## 0.8.0-beta.1 - -Released 2020-Nov-5 - -* Record `Exception` in AspNetCore instrumentation based on `RecordException` in - `AspNetCoreInstrumentationOptions` - ([#1408](https://github.com/open-telemetry/opentelemetry-dotnet/issues/1408)) -* Added configuration option `EnableGrpcAspNetCoreSupport` to enable or disable - support for adding OpenTelemetry RPC attributes when using - [Grpc.AspNetCore](https://www.nuget.org/packages/Grpc.AspNetCore/). This - option is enabled by default. - ([#1423](https://github.com/open-telemetry/opentelemetry-dotnet/pull/1423)) -* Renamed TextMapPropagator to TraceContextPropagator, CompositePropagator to - CompositeTextMapPropagator. IPropagator is renamed to TextMapPropagator and - changed from interface to abstract class. - ([#1427](https://github.com/open-telemetry/opentelemetry-dotnet/pull/1427)) -* Propagators.DefaultTextMapPropagator will be used as the default Propagator - ([#1427](https://github.com/open-telemetry/opentelemetry-dotnet/pull/1428)) -* Removed Propagator from Instrumentation Options. Instrumentation now always - respect the Propagator.DefaultTextMapPropagator. - ([#1448](https://github.com/open-telemetry/opentelemetry-dotnet/pull/1448)) - -## 0.7.0-beta.1 - -Released 2020-Oct-16 - -* Instrumentation no longer store raw objects like `HttpRequest` in - Activity.CustomProperty. To enrich activity, use the Enrich action on the - instrumentation. - ([#1261](https://github.com/open-telemetry/opentelemetry-dotnet/pull/1261)) -* Span Status is populated as per new spec - ([#1313](https://github.com/open-telemetry/opentelemetry-dotnet/pull/1313)) - -## 0.6.0-beta.1 - -Released 2020-Sep-15 - -* For gRPC invocations, the `grpc.method` and `grpc.status_code` attributes - added by the library are removed from the span. The information from these - attributes is contained in other attributes that follow the conventions of - OpenTelemetry. - ([#1260](https://github.com/open-telemetry/opentelemetry-dotnet/pull/1260)) - -## 0.5.0-beta.2 - -Released 2020-08-28 - -* Added Filter public API on AspNetCoreInstrumentationOptions to allow filtering - of instrumentation based on HttpContext. - -* Asp.Net Core Instrumentation automatically populates HttpRequest, HttpResponse - in Activity custom property - -* Changed the default propagation to support W3C Baggage - ([#1048](https://github.com/open-telemetry/opentelemetry-dotnet/pull/1048)) - * The default ITextFormat is now `CompositePropagator(TraceContextFormat, - BaggageFormat)`. Baggage sent via the [W3C - Baggage](https://github.com/w3c/baggage/blob/master/baggage/HTTP_HEADER_FORMAT.md) - header will now be parsed and set on incoming Http spans. -* Introduced support for Grpc.AspNetCore (#803). - * Attributes are added to gRPC invocations: `rpc.system`, `rpc.service`, - `rpc.method`. These attributes are added to an existing span generated by - the instrumentation. This is unlike the instrumentation for client-side gRPC - calls where one span is created for the gRPC call and a separate span is - created for the underlying HTTP call in the event both gRPC and HTTP - instrumentation are enabled. -* Renamed `ITextPropagator` to `IPropagator` - ([#1190](https://github.com/open-telemetry/opentelemetry-dotnet/pull/1190)) - -## 0.4.0-beta.2 - -Released 2020-07-24 - -* First beta release - -## 0.3.0-beta - -Released 2020-07-23 - -* Initial release diff --git a/src/OpenTelemetry.Instrumentation.AspNetCore/GrpcTagHelper.cs b/src/OpenTelemetry.Instrumentation.AspNetCore/GrpcTagHelper.cs deleted file mode 100644 index 836beb226a8..00000000000 --- a/src/OpenTelemetry.Instrumentation.AspNetCore/GrpcTagHelper.cs +++ /dev/null @@ -1,76 +0,0 @@ -// Copyright The OpenTelemetry Authors -// SPDX-License-Identifier: Apache-2.0 - -using System.Diagnostics; -using System.Text.RegularExpressions; -using OpenTelemetry.Trace; - -namespace OpenTelemetry.Instrumentation.GrpcNetClient; - -internal static class GrpcTagHelper -{ - public const string RpcSystemGrpc = "grpc"; - - // The Grpc.Net.Client library adds its own tags to the activity. - // These tags are used to source the tags added by the OpenTelemetry instrumentation. - public const string GrpcMethodTagName = "grpc.method"; - public const string GrpcStatusCodeTagName = "grpc.status_code"; - - private static readonly Regex GrpcMethodRegex = new(@"^/?(?.*)/(?.*)$", RegexOptions.Compiled); - - public static string GetGrpcMethodFromActivity(Activity activity) - { - return activity.GetTagValue(GrpcMethodTagName) as string; - } - - public static bool TryGetGrpcStatusCodeFromActivity(Activity activity, out int statusCode) - { - statusCode = -1; - var grpcStatusCodeTag = activity.GetTagValue(GrpcStatusCodeTagName); - if (grpcStatusCodeTag == null) - { - return false; - } - - return int.TryParse(grpcStatusCodeTag as string, out statusCode); - } - - public static bool TryParseRpcServiceAndRpcMethod(string grpcMethod, out string rpcService, out string rpcMethod) - { - var match = GrpcMethodRegex.Match(grpcMethod); - if (match.Success) - { - rpcService = match.Groups["service"].Value; - rpcMethod = match.Groups["method"].Value; - return true; - } - else - { - rpcService = string.Empty; - rpcMethod = string.Empty; - return false; - } - } - - /// - /// Helper method that populates span properties from RPC status code according - /// to https://github.com/open-telemetry/semantic-conventions/blob/main/docs/rpc/grpc.md#grpc-attributes. - /// - /// RPC status code. - /// Resolved span for the Grpc status code. - public static ActivityStatusCode ResolveSpanStatusForGrpcStatusCode(int statusCode) - { - var status = ActivityStatusCode.Error; - - if (typeof(StatusCanonicalCode).IsEnumDefined(statusCode)) - { - status = ((StatusCanonicalCode)statusCode) switch - { - StatusCanonicalCode.Ok => ActivityStatusCode.Unset, - _ => ActivityStatusCode.Error, - }; - } - - return status; - } -} diff --git a/src/OpenTelemetry.Instrumentation.AspNetCore/Implementation/AspNetCoreInstrumentationEventSource.cs b/src/OpenTelemetry.Instrumentation.AspNetCore/Implementation/AspNetCoreInstrumentationEventSource.cs deleted file mode 100644 index cafd0141d98..00000000000 --- a/src/OpenTelemetry.Instrumentation.AspNetCore/Implementation/AspNetCoreInstrumentationEventSource.cs +++ /dev/null @@ -1,94 +0,0 @@ -// Copyright The OpenTelemetry Authors -// SPDX-License-Identifier: Apache-2.0 - -#if NET6_0_OR_GREATER -using System.Diagnostics.CodeAnalysis; -#endif -using System.Diagnostics.Tracing; -using Microsoft.Extensions.Configuration; -using OpenTelemetry.Internal; - -namespace OpenTelemetry.Instrumentation.AspNetCore.Implementation; - -/// -/// EventSource events emitted from the project. -/// -[EventSource(Name = "OpenTelemetry-Instrumentation-AspNetCore")] -internal sealed class AspNetCoreInstrumentationEventSource : EventSource, IConfigurationExtensionsLogger -{ - public static AspNetCoreInstrumentationEventSource Log = new(); - - [NonEvent] - public void RequestFilterException(string handlerName, string eventName, string operationName, Exception ex) - { - if (this.IsEnabled(EventLevel.Error, EventKeywords.All)) - { - this.RequestFilterException(handlerName, eventName, operationName, ex.ToInvariantString()); - } - } - - [NonEvent] - public void EnrichmentException(string handlerName, string eventName, string operationName, Exception ex) - { - if (this.IsEnabled(EventLevel.Error, EventKeywords.All)) - { - this.EnrichmentException(handlerName, eventName, operationName, ex.ToInvariantString()); - } - } - - [NonEvent] - public void UnknownErrorProcessingEvent(string handlerName, string eventName, Exception ex) - { - if (this.IsEnabled(EventLevel.Error, EventKeywords.All)) - { - this.UnknownErrorProcessingEvent(handlerName, eventName, ex.ToInvariantString()); - } - } - - [Event(1, Message = "Payload is NULL, span will not be recorded. HandlerName: '{0}', EventName: '{1}', OperationName: '{2}'.", Level = EventLevel.Warning)] - public void NullPayload(string handlerName, string eventName, string operationName) - { - this.WriteEvent(1, handlerName, eventName, operationName); - } - - [Event(2, Message = "Request is filtered out. HandlerName: '{0}', EventName: '{1}', OperationName: '{2}'.", Level = EventLevel.Verbose)] - public void RequestIsFilteredOut(string handlerName, string eventName, string operationName) - { - this.WriteEvent(2, handlerName, eventName, operationName); - } - -#if NET6_0_OR_GREATER - [UnconditionalSuppressMessage("ReflectionAnalysis", "IL2026:RequiresUnreferencedCode", Justification = "Parameters to this method are primitive and are trimmer safe.")] -#endif - [Event(3, Message = "Filter threw exception, request will not be collected. HandlerName: '{0}', EventName: '{1}', OperationName: '{2}', Exception: {3}.", Level = EventLevel.Error)] - public void RequestFilterException(string handlerName, string eventName, string operationName, string exception) - { - this.WriteEvent(3, handlerName, eventName, operationName, exception); - } - -#if NET6_0_OR_GREATER - [UnconditionalSuppressMessage("ReflectionAnalysis", "IL2026:RequiresUnreferencedCode", Justification = "Parameters to this method are primitive and are trimmer safe.")] -#endif - [Event(4, Message = "Enrich threw exception. HandlerName: '{0}', EventName: '{1}', OperationName: '{2}', Exception: {3}.", Level = EventLevel.Warning)] - public void EnrichmentException(string handlerName, string eventName, string operationName, string exception) - { - this.WriteEvent(4, handlerName, eventName, operationName, exception); - } - - [Event(5, Message = "Unknown error processing event '{1}' from handler '{0}', Exception: {2}", Level = EventLevel.Error)] - public void UnknownErrorProcessingEvent(string handlerName, string eventName, string ex) - { - this.WriteEvent(5, handlerName, eventName, ex); - } - - [Event(6, Message = "Configuration key '{0}' has an invalid value: '{1}'", Level = EventLevel.Warning)] - public void InvalidConfigurationValue(string key, string value) - { - this.WriteEvent(6, key, value); - } - - void IConfigurationExtensionsLogger.LogInvalidConfigurationValue(string key, string value) - { - this.InvalidConfigurationValue(key, value); - } -} diff --git a/src/OpenTelemetry.Instrumentation.AspNetCore/Implementation/HttpInListener.cs b/src/OpenTelemetry.Instrumentation.AspNetCore/Implementation/HttpInListener.cs deleted file mode 100644 index 7b5942a1084..00000000000 --- a/src/OpenTelemetry.Instrumentation.AspNetCore/Implementation/HttpInListener.cs +++ /dev/null @@ -1,397 +0,0 @@ -// Copyright The OpenTelemetry Authors -// SPDX-License-Identifier: Apache-2.0 - -using System.Diagnostics; -#if NET6_0_OR_GREATER -using System.Diagnostics.CodeAnalysis; -#endif -using System.Reflection; -using System.Runtime.CompilerServices; -using Microsoft.AspNetCore.Http; -#if !NETSTANDARD -using Microsoft.AspNetCore.Diagnostics; -using Microsoft.AspNetCore.Routing; -#endif -using OpenTelemetry.Context.Propagation; -using OpenTelemetry.Instrumentation.GrpcNetClient; -using OpenTelemetry.Internal; -using OpenTelemetry.Trace; - -namespace OpenTelemetry.Instrumentation.AspNetCore.Implementation; - -internal class HttpInListener : ListenerHandler -{ - internal const string ActivityOperationName = "Microsoft.AspNetCore.Hosting.HttpRequestIn"; - internal const string OnStartEvent = "Microsoft.AspNetCore.Hosting.HttpRequestIn.Start"; - internal const string OnStopEvent = "Microsoft.AspNetCore.Hosting.HttpRequestIn.Stop"; - internal const string OnUnhandledHostingExceptionEvent = "Microsoft.AspNetCore.Hosting.UnhandledException"; - internal const string OnUnHandledDiagnosticsExceptionEvent = "Microsoft.AspNetCore.Diagnostics.UnhandledException"; - - // https://github.com/dotnet/aspnetcore/blob/8d6554e655b64da75b71e0e20d6db54a3ba8d2fb/src/Hosting/Hosting/src/GenericHost/GenericWebHostBuilder.cs#L85 - internal static readonly string AspNetCoreActivitySourceName = "Microsoft.AspNetCore"; - - internal static readonly AssemblyName AssemblyName = typeof(HttpInListener).Assembly.GetName(); - internal static readonly string ActivitySourceName = AssemblyName.Name; - internal static readonly Version Version = AssemblyName.Version; - internal static readonly ActivitySource ActivitySource = new(ActivitySourceName, Version.ToString()); - internal static readonly bool Net7OrGreater = Environment.Version.Major >= 7; - - private const string DiagnosticSourceName = "Microsoft.AspNetCore"; - - private static readonly Func> HttpRequestHeaderValuesGetter = (request, name) => - { - if (request.Headers.TryGetValue(name, out var value)) - { - // This causes allocation as the `StringValues` struct has to be casted to an `IEnumerable` object. - return value; - } - - return Enumerable.Empty(); - }; - - private static readonly PropertyFetcher ExceptionPropertyFetcher = new("Exception"); - - private readonly AspNetCoreTraceInstrumentationOptions options; - - public HttpInListener(AspNetCoreTraceInstrumentationOptions options) - : base(DiagnosticSourceName) - { - Guard.ThrowIfNull(options); - - this.options = options; - } - - public override void OnEventWritten(string name, object payload) - { - switch (name) - { - case OnStartEvent: - { - this.OnStartActivity(Activity.Current, payload); - } - - break; - case OnStopEvent: - { - this.OnStopActivity(Activity.Current, payload); - } - - break; - case OnUnhandledHostingExceptionEvent: - case OnUnHandledDiagnosticsExceptionEvent: - { - this.OnException(Activity.Current, payload); - } - - break; - } - } - - public void OnStartActivity(Activity activity, object payload) - { - // The overall flow of what AspNetCore library does is as below: - // Activity.Start() - // DiagnosticSource.WriteEvent("Start", payload) - // DiagnosticSource.WriteEvent("Stop", payload) - // Activity.Stop() - - // This method is in the WriteEvent("Start", payload) path. - // By this time, samplers have already run and - // activity.IsAllDataRequested populated accordingly. - - var context = payload as HttpContext; - if (context == null) - { - AspNetCoreInstrumentationEventSource.Log.NullPayload(nameof(HttpInListener), nameof(this.OnStartActivity), activity.OperationName); - return; - } - - // Ensure context extraction irrespective of sampling decision - var request = context.Request; - var textMapPropagator = Propagators.DefaultTextMapPropagator; - if (textMapPropagator is not TraceContextPropagator) - { - var ctx = textMapPropagator.Extract(default, request, HttpRequestHeaderValuesGetter); - if (ctx.ActivityContext.IsValid() - && !((ctx.ActivityContext.TraceId == activity.TraceId) - && (ctx.ActivityContext.SpanId == activity.ParentSpanId) - && (ctx.ActivityContext.TraceState == activity.TraceStateString))) - { - // Create a new activity with its parent set from the extracted context. - // This makes the new activity as a "sibling" of the activity created by - // Asp.Net Core. - Activity newOne; - if (Net7OrGreater) - { - // For NET7.0 onwards activity is created using ActivitySource so, - // we will use the source of the activity to create the new one. - newOne = activity.Source.CreateActivity(ActivityOperationName, ActivityKind.Server, ctx.ActivityContext); - } - else - { - newOne = new Activity(ActivityOperationName); - newOne.SetParentId(ctx.ActivityContext.TraceId, ctx.ActivityContext.SpanId, ctx.ActivityContext.TraceFlags); - } - - newOne.TraceStateString = ctx.ActivityContext.TraceState; - - newOne.SetTag("IsCreatedByInstrumentation", bool.TrueString); - - // Starting the new activity make it the Activity.Current one. - newOne.Start(); - - // Set IsAllDataRequested to false for the activity created by the framework to only export the sibling activity and not the framework activity - activity.IsAllDataRequested = false; - activity = newOne; - } - - Baggage.Current = ctx.Baggage; - } - - // enrich Activity from payload only if sampling decision - // is favorable. - if (activity.IsAllDataRequested) - { - try - { - if (this.options.Filter?.Invoke(context) == false) - { - AspNetCoreInstrumentationEventSource.Log.RequestIsFilteredOut(nameof(HttpInListener), nameof(this.OnStartActivity), activity.OperationName); - activity.IsAllDataRequested = false; - activity.ActivityTraceFlags &= ~ActivityTraceFlags.Recorded; - return; - } - } - catch (Exception ex) - { - AspNetCoreInstrumentationEventSource.Log.RequestFilterException(nameof(HttpInListener), nameof(this.OnStartActivity), activity.OperationName, ex); - activity.IsAllDataRequested = false; - activity.ActivityTraceFlags &= ~ActivityTraceFlags.Recorded; - return; - } - - if (!Net7OrGreater) - { - ActivityInstrumentationHelper.SetActivitySourceProperty(activity, ActivitySource); - ActivityInstrumentationHelper.SetKindProperty(activity, ActivityKind.Server); - } - - var path = (request.PathBase.HasValue || request.Path.HasValue) ? (request.PathBase + request.Path).ToString() : "/"; - RequestMethodHelper.SetActivityDisplayName(activity, request.Method); - - // see the spec https://github.com/open-telemetry/semantic-conventions/blob/v1.23.0/docs/http/http-spans.md - - if (request.Host.HasValue) - { - activity.SetTag(SemanticConventions.AttributeServerAddress, request.Host.Host); - - if (request.Host.Port.HasValue) - { - activity.SetTag(SemanticConventions.AttributeServerPort, request.Host.Port.Value); - } - } - - if (request.QueryString.HasValue) - { - if (this.options.DisableUrlQueryRedaction) - { - activity.SetTag(SemanticConventions.AttributeUrlQuery, request.QueryString.Value); - } - else - { - activity.SetTag(SemanticConventions.AttributeUrlQuery, RedactionHelper.GetRedactedQueryString(request.QueryString.Value)); - } - } - - RequestMethodHelper.SetHttpMethodTag(activity, request.Method); - - activity.SetTag(SemanticConventions.AttributeUrlScheme, request.Scheme); - activity.SetTag(SemanticConventions.AttributeUrlPath, path); - activity.SetTag(SemanticConventions.AttributeNetworkProtocolVersion, HttpTagHelper.GetFlavorTagValueFromProtocol(request.Protocol)); - - if (request.Headers.TryGetValue("User-Agent", out var values)) - { - var userAgent = values.Count > 0 ? values[0] : null; - if (!string.IsNullOrEmpty(userAgent)) - { - activity.SetTag(SemanticConventions.AttributeUserAgentOriginal, userAgent); - } - } - - try - { - this.options.EnrichWithHttpRequest?.Invoke(activity, request); - } - catch (Exception ex) - { - AspNetCoreInstrumentationEventSource.Log.EnrichmentException(nameof(HttpInListener), nameof(this.OnStartActivity), activity.OperationName, ex); - } - } - } - - public void OnStopActivity(Activity activity, object payload) - { - if (activity.IsAllDataRequested) - { - HttpContext context = payload as HttpContext; - if (context == null) - { - AspNetCoreInstrumentationEventSource.Log.NullPayload(nameof(HttpInListener), nameof(this.OnStopActivity), activity.OperationName); - return; - } - - var response = context.Response; - -#if !NETSTANDARD - var routePattern = (context.Features.Get()?.Endpoint as RouteEndpoint ?? - context.GetEndpoint() as RouteEndpoint)?.RoutePattern.RawText; - if (!string.IsNullOrEmpty(routePattern)) - { - RequestMethodHelper.SetActivityDisplayName(activity, context.Request.Method, routePattern); - activity.SetTag(SemanticConventions.AttributeHttpRoute, routePattern); - } -#endif - - activity.SetTag(SemanticConventions.AttributeHttpResponseStatusCode, TelemetryHelper.GetBoxedStatusCode(response.StatusCode)); - - if (this.options.EnableGrpcAspNetCoreSupport && TryGetGrpcMethod(activity, out var grpcMethod)) - { - AddGrpcAttributes(activity, grpcMethod, context); - } - - if (activity.Status == ActivityStatusCode.Unset) - { - activity.SetStatus(SpanHelper.ResolveSpanStatusForHttpStatusCode(activity.Kind, response.StatusCode)); - } - - try - { - this.options.EnrichWithHttpResponse?.Invoke(activity, response); - } - catch (Exception ex) - { - AspNetCoreInstrumentationEventSource.Log.EnrichmentException(nameof(HttpInListener), nameof(this.OnStopActivity), activity.OperationName, ex); - } - } - - object tagValue; - if (Net7OrGreater) - { - tagValue = activity.GetTagValue("IsCreatedByInstrumentation"); - } - else - { - _ = activity.TryCheckFirstTag("IsCreatedByInstrumentation", out tagValue); - } - - if (ReferenceEquals(tagValue, bool.TrueString)) - { - // If instrumentation started a new Activity, it must - // be stopped here. - activity.SetTag("IsCreatedByInstrumentation", null); - activity.Stop(); - - // After the activity.Stop() code, Activity.Current becomes null. - // If Asp.Net Core uses Activity.Current?.Stop() - it'll not stop the activity - // it created. - // Currently Asp.Net core does not use Activity.Current, instead it stores a - // reference to its activity, and calls .Stop on it. - - // TODO: Should we still restore Activity.Current here? - // If yes, then we need to store the asp.net core activity inside - // the one created by the instrumentation. - // And retrieve it here, and set it to Current. - } - } - - public void OnException(Activity activity, object payload) - { - if (activity.IsAllDataRequested) - { - // We need to use reflection here as the payload type is not a defined public type. - if (!TryFetchException(payload, out Exception exc)) - { - AspNetCoreInstrumentationEventSource.Log.NullPayload(nameof(HttpInListener), nameof(this.OnException), activity.OperationName); - return; - } - - activity.SetTag(SemanticConventions.AttributeErrorType, exc.GetType().FullName); - - if (this.options.RecordException) - { - activity.RecordException(exc); - } - - activity.SetStatus(ActivityStatusCode.Error); - - try - { - this.options.EnrichWithException?.Invoke(activity, exc); - } - catch (Exception ex) - { - AspNetCoreInstrumentationEventSource.Log.EnrichmentException(nameof(HttpInListener), nameof(this.OnException), activity.OperationName, ex); - } - } - - // See https://github.com/dotnet/aspnetcore/blob/690d78279e940d267669f825aa6627b0d731f64c/src/Hosting/Hosting/src/Internal/HostingApplicationDiagnostics.cs#L252 - // and https://github.com/dotnet/aspnetcore/blob/690d78279e940d267669f825aa6627b0d731f64c/src/Middleware/Diagnostics/src/DeveloperExceptionPage/DeveloperExceptionPageMiddlewareImpl.cs#L174 - // this makes sure that top-level properties on the payload object are always preserved. -#if NET6_0_OR_GREATER - [UnconditionalSuppressMessage("Trimming", "IL2026", Justification = "The event source guarantees that top level properties are preserved")] -#endif - static bool TryFetchException(object payload, out Exception exc) - => ExceptionPropertyFetcher.TryFetch(payload, out exc) && exc != null; - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - private static bool TryGetGrpcMethod(Activity activity, out string grpcMethod) - { - grpcMethod = GrpcTagHelper.GetGrpcMethodFromActivity(activity); - return !string.IsNullOrEmpty(grpcMethod); - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - private static void AddGrpcAttributes(Activity activity, string grpcMethod, HttpContext context) - { - // The RPC semantic conventions indicate the span name - // should not have a leading forward slash. - // https://github.com/open-telemetry/semantic-conventions/blob/main/docs/rpc/rpc-spans.md#span-name - activity.DisplayName = grpcMethod.TrimStart('/'); - - activity.SetTag(SemanticConventions.AttributeRpcSystem, GrpcTagHelper.RpcSystemGrpc); - - // see the spec https://github.com/open-telemetry/semantic-conventions/blob/v1.23.0/docs/rpc/rpc-spans.md - - if (context.Connection.RemoteIpAddress != null) - { - activity.SetTag(SemanticConventions.AttributeClientAddress, context.Connection.RemoteIpAddress.ToString()); - } - - activity.SetTag(SemanticConventions.AttributeClientPort, context.Connection.RemotePort); - - bool validConversion = GrpcTagHelper.TryGetGrpcStatusCodeFromActivity(activity, out int status); - if (validConversion) - { - activity.SetStatus(GrpcTagHelper.ResolveSpanStatusForGrpcStatusCode(status)); - } - - if (GrpcTagHelper.TryParseRpcServiceAndRpcMethod(grpcMethod, out var rpcService, out var rpcMethod)) - { - activity.SetTag(SemanticConventions.AttributeRpcService, rpcService); - activity.SetTag(SemanticConventions.AttributeRpcMethod, rpcMethod); - - // Remove the grpc.method tag added by the gRPC .NET library - activity.SetTag(GrpcTagHelper.GrpcMethodTagName, null); - - // Remove the grpc.status_code tag added by the gRPC .NET library - activity.SetTag(GrpcTagHelper.GrpcStatusCodeTagName, null); - - if (validConversion) - { - // setting rpc.grpc.status_code - activity.SetTag(SemanticConventions.AttributeRpcGrpcStatusCode, status); - } - } - } -} diff --git a/src/OpenTelemetry.Instrumentation.AspNetCore/Implementation/HttpInMetricsListener.cs b/src/OpenTelemetry.Instrumentation.AspNetCore/Implementation/HttpInMetricsListener.cs deleted file mode 100644 index e41cd5dc256..00000000000 --- a/src/OpenTelemetry.Instrumentation.AspNetCore/Implementation/HttpInMetricsListener.cs +++ /dev/null @@ -1,128 +0,0 @@ -// Copyright The OpenTelemetry Authors -// SPDX-License-Identifier: Apache-2.0 - -using System.Diagnostics; -using System.Diagnostics.Metrics; -using System.Reflection; -using Microsoft.AspNetCore.Http; -using OpenTelemetry.Internal; - -#if NET6_0_OR_GREATER -using System.Diagnostics.CodeAnalysis; -using Microsoft.AspNetCore.Diagnostics; -using Microsoft.AspNetCore.Routing; -#endif -using OpenTelemetry.Trace; - -namespace OpenTelemetry.Instrumentation.AspNetCore.Implementation; - -internal sealed class HttpInMetricsListener : ListenerHandler -{ - internal const string HttpServerRequestDurationMetricName = "http.server.request.duration"; - - internal const string OnUnhandledHostingExceptionEvent = "Microsoft.AspNetCore.Hosting.UnhandledException"; - internal const string OnUnhandledDiagnosticsExceptionEvent = "Microsoft.AspNetCore.Diagnostics.UnhandledException"; - - internal static readonly AssemblyName AssemblyName = typeof(HttpInListener).Assembly.GetName(); - internal static readonly string InstrumentationName = AssemblyName.Name; - internal static readonly string InstrumentationVersion = AssemblyName.Version.ToString(); - internal static readonly Meter Meter = new(InstrumentationName, InstrumentationVersion); - - private const string OnStopEvent = "Microsoft.AspNetCore.Hosting.HttpRequestIn.Stop"; - - private static readonly PropertyFetcher ExceptionPropertyFetcher = new("Exception"); - private static readonly PropertyFetcher HttpContextPropertyFetcher = new("HttpContext"); - private static readonly object ErrorTypeHttpContextItemsKey = new(); - - private static readonly Histogram HttpServerRequestDuration = Meter.CreateHistogram(HttpServerRequestDurationMetricName, "s", "Duration of HTTP server requests."); - - internal HttpInMetricsListener(string name) - : base(name) - { - } - - public static void OnExceptionEventWritten(string name, object payload) - { - // We need to use reflection here as the payload type is not a defined public type. - if (!TryFetchException(payload, out Exception exc) || !TryFetchHttpContext(payload, out HttpContext ctx)) - { - AspNetCoreInstrumentationEventSource.Log.NullPayload(nameof(HttpInMetricsListener), nameof(OnExceptionEventWritten), HttpServerRequestDurationMetricName); - return; - } - - ctx.Items.Add(ErrorTypeHttpContextItemsKey, exc.GetType().FullName); - - // See https://github.com/dotnet/aspnetcore/blob/690d78279e940d267669f825aa6627b0d731f64c/src/Hosting/Hosting/src/Internal/HostingApplicationDiagnostics.cs#L252 - // and https://github.com/dotnet/aspnetcore/blob/690d78279e940d267669f825aa6627b0d731f64c/src/Middleware/Diagnostics/src/DeveloperExceptionPage/DeveloperExceptionPageMiddlewareImpl.cs#L174 - // this makes sure that top-level properties on the payload object are always preserved. -#if NET6_0_OR_GREATER - [UnconditionalSuppressMessage("Trimming", "IL2026", Justification = "The ASP.NET Core framework guarantees that top level properties are preserved")] -#endif - static bool TryFetchException(object payload, out Exception exc) - => ExceptionPropertyFetcher.TryFetch(payload, out exc) && exc != null; -#if NET6_0_OR_GREATER - [UnconditionalSuppressMessage("Trimming", "IL2026", Justification = "The ASP.NET Core framework guarantees that top level properties are preserved")] -#endif - static bool TryFetchHttpContext(object payload, out HttpContext ctx) - => HttpContextPropertyFetcher.TryFetch(payload, out ctx) && ctx != null; - } - - public static void OnStopEventWritten(string name, object payload) - { - var context = payload as HttpContext; - if (context == null) - { - AspNetCoreInstrumentationEventSource.Log.NullPayload(nameof(HttpInMetricsListener), nameof(OnStopEventWritten), HttpServerRequestDurationMetricName); - return; - } - - TagList tags = default; - - // see the spec https://github.com/open-telemetry/semantic-conventions/blob/v1.21.0/docs/http/http-spans.md - tags.Add(new KeyValuePair(SemanticConventions.AttributeNetworkProtocolVersion, HttpTagHelper.GetFlavorTagValueFromProtocol(context.Request.Protocol))); - tags.Add(new KeyValuePair(SemanticConventions.AttributeUrlScheme, context.Request.Scheme)); - tags.Add(new KeyValuePair(SemanticConventions.AttributeHttpResponseStatusCode, TelemetryHelper.GetBoxedStatusCode(context.Response.StatusCode))); - - var httpMethod = RequestMethodHelper.GetNormalizedHttpMethod(context.Request.Method); - tags.Add(new KeyValuePair(SemanticConventions.AttributeHttpRequestMethod, httpMethod)); - -#if NET6_0_OR_GREATER - // Check the exception handler feature first in case the endpoint was overwritten - var route = (context.Features.Get()?.Endpoint as RouteEndpoint ?? - context.GetEndpoint() as RouteEndpoint)?.RoutePattern.RawText; - if (!string.IsNullOrEmpty(route)) - { - tags.Add(new KeyValuePair(SemanticConventions.AttributeHttpRoute, route)); - } -#endif - if (context.Items.TryGetValue(ErrorTypeHttpContextItemsKey, out var errorType)) - { - tags.Add(new KeyValuePair(SemanticConventions.AttributeErrorType, errorType)); - } - - // We are relying here on ASP.NET Core to set duration before writing the stop event. - // https://github.com/dotnet/aspnetcore/blob/d6fa351048617ae1c8b47493ba1abbe94c3a24cf/src/Hosting/Hosting/src/Internal/HostingApplicationDiagnostics.cs#L449 - // TODO: Follow up with .NET team if we can continue to rely on this behavior. - HttpServerRequestDuration.Record(Activity.Current.Duration.TotalSeconds, tags); - } - - public override void OnEventWritten(string name, object payload) - { - switch (name) - { - case OnUnhandledDiagnosticsExceptionEvent: - case OnUnhandledHostingExceptionEvent: - { - OnExceptionEventWritten(name, payload); - } - - break; - case OnStopEvent: - { - OnStopEventWritten(name, payload); - } - - break; - } - } -} diff --git a/src/OpenTelemetry.Instrumentation.AspNetCore/Implementation/HttpTagHelper.cs b/src/OpenTelemetry.Instrumentation.AspNetCore/Implementation/HttpTagHelper.cs deleted file mode 100644 index 90f37eba3c2..00000000000 --- a/src/OpenTelemetry.Instrumentation.AspNetCore/Implementation/HttpTagHelper.cs +++ /dev/null @@ -1,33 +0,0 @@ -// Copyright The OpenTelemetry Authors -// SPDX-License-Identifier: Apache-2.0 - -namespace OpenTelemetry.Instrumentation.AspNetCore.Implementation; - -/// -/// A collection of helper methods to be used when building Http activities. -/// -internal static class HttpTagHelper -{ - /// - /// Gets the OpenTelemetry standard version tag value for a span based on its protocol/>. - /// - /// . - /// Span flavor value. - public static string GetFlavorTagValueFromProtocol(string protocol) - { - switch (protocol) - { - case "HTTP/2": - return "2"; - - case "HTTP/3": - return "3"; - - case "HTTP/1.1": - return "1.1"; - - default: - return protocol; - } - } -} diff --git a/src/OpenTelemetry.Instrumentation.AspNetCore/Implementation/TelemetryHelper.cs b/src/OpenTelemetry.Instrumentation.AspNetCore/Implementation/TelemetryHelper.cs deleted file mode 100644 index 6f2e1fae8e4..00000000000 --- a/src/OpenTelemetry.Instrumentation.AspNetCore/Implementation/TelemetryHelper.cs +++ /dev/null @@ -1,28 +0,0 @@ -// Copyright The OpenTelemetry Authors -// SPDX-License-Identifier: Apache-2.0 - -namespace OpenTelemetry.Instrumentation.AspNetCore.Implementation; - -internal static class TelemetryHelper -{ - public static readonly object[] BoxedStatusCodes; - - static TelemetryHelper() - { - BoxedStatusCodes = new object[500]; - for (int i = 0, c = 100; i < BoxedStatusCodes.Length; i++, c++) - { - BoxedStatusCodes[i] = c; - } - } - - public static object GetBoxedStatusCode(int statusCode) - { - if (statusCode >= 100 && statusCode < 600) - { - return BoxedStatusCodes[statusCode - 100]; - } - - return statusCode; - } -} diff --git a/src/OpenTelemetry.Instrumentation.AspNetCore/OpenTelemetry.Instrumentation.AspNetCore.csproj b/src/OpenTelemetry.Instrumentation.AspNetCore/OpenTelemetry.Instrumentation.AspNetCore.csproj deleted file mode 100644 index 99d581df576..00000000000 --- a/src/OpenTelemetry.Instrumentation.AspNetCore/OpenTelemetry.Instrumentation.AspNetCore.csproj +++ /dev/null @@ -1,46 +0,0 @@ - - - - $(TargetFrameworksForAspNetCoreInstrumentation) - ASP.NET Core instrumentation for OpenTelemetry .NET - $(PackageTags);distributed-tracing;AspNetCore - Instrumentation.AspNetCore- - true - 1.8.1 - - - disable - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/src/OpenTelemetry.Instrumentation.AspNetCore/README.md b/src/OpenTelemetry.Instrumentation.AspNetCore/README.md deleted file mode 100644 index f8ef36ef2d8..00000000000 --- a/src/OpenTelemetry.Instrumentation.AspNetCore/README.md +++ /dev/null @@ -1,331 +0,0 @@ -# ASP.NET Core Instrumentation for OpenTelemetry .NET - -[![NuGet](https://img.shields.io/nuget/v/OpenTelemetry.Instrumentation.AspNetCore.svg)](https://www.nuget.org/packages/OpenTelemetry.Instrumentation.AspNetCore) -[![NuGet](https://img.shields.io/nuget/dt/OpenTelemetry.Instrumentation.AspNetCore.svg)](https://www.nuget.org/packages/OpenTelemetry.Instrumentation.AspNetCore) - -This is an [Instrumentation -Library](https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/glossary.md#instrumentation-library), -which instruments [ASP.NET Core](https://docs.microsoft.com/aspnet/core) and -collect metrics and traces about incoming web requests. This instrumentation -also collects traces from incoming gRPC requests using -[Grpc.AspNetCore](https://www.nuget.org/packages/Grpc.AspNetCore). -Instrumentation support for gRPC server requests is supported via an -[experimental](#experimental-support-for-grpc-requests) feature flag. - -This component is based on the -[v1.23](https://github.com/open-telemetry/semantic-conventions/tree/v1.23.0/docs/http) -of http semantic conventions. For details on the default set of attributes that -are added, checkout [Traces](#traces) and [Metrics](#metrics) sections below. - -## Steps to enable OpenTelemetry.Instrumentation.AspNetCore - -### Step 1: Install Package - -Add a reference to the -[`OpenTelemetry.Instrumentation.AspNetCore`](https://www.nuget.org/packages/OpenTelemetry.Instrumentation.AspNetCore) -package. Also, add any other instrumentations & exporters you will need. - -```shell -dotnet add package OpenTelemetry.Instrumentation.AspNetCore -``` - -### Step 2: Enable ASP.NET Core Instrumentation at application startup - -ASP.NET Core instrumentation must be enabled at application startup. This is -typically done in the `ConfigureServices` of your `Startup` class. Both examples -below enables OpenTelemetry by calling `AddOpenTelemetry()` on `IServiceCollection`. - This extension method requires adding the package -[`OpenTelemetry.Extensions.Hosting`](../OpenTelemetry.Extensions.Hosting/README.md) -to the application. This ensures instrumentations are disposed when the host -is shutdown. - -#### Traces - -The following example demonstrates adding ASP.NET Core instrumentation with the -extension method `WithTracing()` on `OpenTelemetryBuilder`. -then extension method `AddAspNetCoreInstrumentation()` on `TracerProviderBuilder` -to the application. This example also sets up the Console Exporter, -which requires adding the package [`OpenTelemetry.Exporter.Console`](../OpenTelemetry.Exporter.Console/README.md) -to the application. - -```csharp -using Microsoft.Extensions.DependencyInjection; -using OpenTelemetry.Trace; - -public void ConfigureServices(IServiceCollection services) -{ - services.AddOpenTelemetry() - .WithTracing(builder => builder - .AddAspNetCoreInstrumentation() - .AddConsoleExporter()); -} -``` - -Following list of attributes are added by default on activity. See -[http-spans](https://github.com/open-telemetry/semantic-conventions/tree/v1.23.0/docs/http/http-spans.md) -for more details about each individual attribute: - -* `error.type` -* `http.request.method` -* `http.request.method_original` -* `http.response.status_code` -* `http.route` -* `network.protocol.version` -* `user_agent.original` -* `server.address` -* `server.port` -* `url.path` -* `url.query` - By default, the values in the query component are replaced with - the text `Redacted`. For example, `?key1=value1&key2=value2` becomes - `?key1=Redacted&key2=Redacted`. You can disable this redaction by setting the - environment variable - `OTEL_DOTNET_EXPERIMENTAL_ASPNETCORE_DISABLE_URL_QUERY_REDACTION` to `true`. -* `url.scheme` - -[Enrich Api](#enrich) can be used if any additional attributes are -required on activity. - -#### Metrics - -The following example demonstrates adding ASP.NET Core instrumentation with the -extension method `WithMetrics()` on `OpenTelemetryBuilder` -then extension method `AddAspNetCoreInstrumentation()` on `MeterProviderBuilder` -to the application. This example also sets up the Console Exporter, -which requires adding the package [`OpenTelemetry.Exporter.Console`](../OpenTelemetry.Exporter.Console/README.md) -to the application. - -```csharp -using Microsoft.Extensions.DependencyInjection; -using OpenTelemetry.Metrics; - -public void ConfigureServices(IServiceCollection services) -{ - services.AddOpenTelemetry() - .WithMetrics(builder => builder - .AddAspNetCoreInstrumentation() - .AddConsoleExporter()); -} -``` - -Following list of attributes are added by default on -`http.server.request.duration` metric. See -[http-metrics](https://github.com/open-telemetry/semantic-conventions/tree/v1.23.0/docs/http/http-metrics.md) -for more details about each individual attribute. `.NET8.0` and above supports -additional metrics, see [list of metrics produced](#list-of-metrics-produced) for -more details. - -* `error.type` -* `http.response.status_code` -* `http.request.method` -* `http.route` -* `network.protocol.version` -* `url.scheme` - -#### List of metrics produced - -When the application targets `.NET6.0` or `.NET7.0`, the instrumentation emits -the following metric: - -| Name | Details | -|-----------------------------------|---------------------------------------------------------------------------------------------------------------------------------------------------------| -| `http.server.request.duration` | [Specification](https://github.com/open-telemetry/semantic-conventions/blob/release/v1.23.x/docs/http/http-metrics.md#metric-httpserverrequestduration) | - -Starting from `.NET8.0`, metrics instrumentation is natively implemented, and -the ASP.NET Core library has incorporated support for [built-in -metrics](https://learn.microsoft.com/dotnet/core/diagnostics/built-in-metrics-aspnetcore) -following the OpenTelemetry semantic conventions. The library includes additional -metrics beyond those defined in the -[specification](https://github.com/open-telemetry/semantic-conventions/blob/v1.23.0/docs/http/http-metrics.md), -covering additional scenarios for ASP.NET Core users. When the application -targets `.NET8.0` and newer versions, the instrumentation library automatically -enables all `built-in` metrics by default. - -Note that the `AddAspNetCoreInstrumentation()` extension simplifies the process -of enabling all built-in metrics via a single line of code. Alternatively, for -more granular control over emitted metrics, you can utilize the `AddMeter()` -extension on `MeterProviderBuilder` for meters listed in -[built-in-metrics-aspnetcore](https://learn.microsoft.com/dotnet/core/diagnostics/built-in-metrics-aspnetcore). -Using `AddMeter()` for metrics activation eliminates the need to take dependency -on the instrumentation library package and calling -`AddAspNetCoreInstrumentation()`. - -If you utilize `AddAspNetCoreInstrumentation()` and wish to exclude unnecessary -metrics, you can utilize -[Views](https://github.com/open-telemetry/opentelemetry-dotnet/tree/main/docs/metrics/customizing-the-sdk#drop-an-instrument) -to achieve this. - -> [!NOTE] -> There is no difference in features or emitted metrics when enabling metrics -using `AddMeter()` or `AddAspNetCoreInstrumentation()` on `.NET8.0` and newer -versions. - -> [!NOTE] -> The `http.server.request.duration` metric is emitted in `seconds` as per the -semantic convention. While the convention [recommends using custom histogram -buckets](https://github.com/open-telemetry/semantic-conventions/blob/release/v1.23.x/docs/http/http-metrics.md) -, this feature is not yet available via .NET Metrics API. A -[workaround](https://github.com/open-telemetry/opentelemetry-dotnet/pull/4820) -has been included in OTel SDK starting version `1.6.0` which applies recommended -buckets by default for `http.server.request.duration`. This applies to all -targeted frameworks. - -## Advanced configuration - -### Tracing - -This instrumentation can be configured to change the default behavior by using -`AspNetCoreTraceInstrumentationOptions`, which allows adding [`Filter`](#filter), -[`Enrich`](#enrich) as explained below. - -// TODO: This section could be refined. -When used with -[`OpenTelemetry.Extensions.Hosting`](../OpenTelemetry.Extensions.Hosting/README.md), -all configurations to `AspNetCoreTraceInstrumentationOptions` can be done in the -`ConfigureServices` -method of you applications `Startup` class as shown below. - -```csharp -// Configure -services.Configure(options => -{ - options.Filter = (httpContext) => - { - // only collect telemetry about HTTP GET requests - return httpContext.Request.Method.Equals("GET"); - }; -}); - -services.AddOpenTelemetry() - .WithTracing(builder => builder - .AddAspNetCoreInstrumentation() - .AddConsoleExporter()); -``` - -#### Filter - -This instrumentation by default collects all the incoming http requests. It -allows filtering of requests by using the `Filter` function in -`AspNetCoreTraceInstrumentationOptions`. This defines the condition for allowable -requests. The Filter receives the `HttpContext` of the incoming -request, and does not collect telemetry about the request if the Filter -returns false or throws exception. - -The following code snippet shows how to use `Filter` to only allow GET -requests. - -```csharp -services.AddOpenTelemetry() - .WithTracing(builder => builder - .AddAspNetCoreInstrumentation((options) => options.Filter = httpContext => - { - // only collect telemetry about HTTP GET requests - return httpContext.Request.Method.Equals("GET"); - }) - .AddConsoleExporter()); -``` - -It is important to note that this `Filter` option is specific to this -instrumentation. OpenTelemetry has a concept of a -[Sampler](https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/trace/sdk.md#sampling), -and the `Filter` option does the filtering *after* the Sampler is invoked. - -#### Enrich - -This instrumentation library provides `EnrichWithHttpRequest`, -`EnrichWithHttpResponse` and `EnrichWithException` options that can be used to -enrich the activity with additional information from the raw `HttpRequest`, -`HttpResponse` and `Exception` objects respectively. These actions are called -only when `activity.IsAllDataRequested` is `true`. It contains the activity -itself (which can be enriched) and the actual raw object. - -The following code snippet shows how to enrich the activity using all 3 -different options. - -```csharp -services.AddOpenTelemetry() - .WithTracing(builder => builder - .AddAspNetCoreInstrumentation(o => - { - o.EnrichWithHttpRequest = (activity, httpRequest) => - { - activity.SetTag("requestProtocol", httpRequest.Protocol); - }; - o.EnrichWithHttpResponse = (activity, httpResponse) => - { - activity.SetTag("responseLength", httpResponse.ContentLength); - }; - o.EnrichWithException = (activity, exception) => - { - activity.SetTag("exceptionType", exception.GetType().ToString()); - }; - })); -``` - -[Processor](../../docs/trace/extending-the-sdk/README.md#processor), -is the general extensibility point to add additional properties to any activity. -The `Enrich` option is specific to this instrumentation, and is provided to -get access to `HttpRequest` and `HttpResponse`. - -#### RecordException - -This instrumentation automatically sets Activity Status to Error if an unhandled -exception is thrown. Additionally, `RecordException` feature may be turned on, -to store the exception to the Activity itself as ActivityEvent. - -## Activity duration and http.server.request.duration metric calculation - -`Activity.Duration` and `http.server.request.duration` values represents the -time used to handle an inbound HTTP request as measured at the hosting layer of -ASP.NET Core. The time measurement starts once the underlying web host has: - -* Sufficiently parsed the HTTP request headers on the inbound network stream to - identify the new request. -* Initialized the context data structures such as the - [HttpContext](https://learn.microsoft.com/dotnet/api/microsoft.aspnetcore.http.httpcontext). - -The time ends when: - -* The ASP.NET Core handler pipeline is finished executing. -* All response data has been sent. -* The context data structures for the request are being disposed. - -## Experimental support for gRPC requests - -gRPC instrumentation can be enabled by setting -`OTEL_DOTNET_EXPERIMENTAL_ASPNETCORE_ENABLE_GRPC_INSTRUMENTATION` flag to -`True`. The flag can be set as an environment variable or via IConfiguration as -shown below. - -```csharp -var appBuilder = WebApplication.CreateBuilder(args); - -appBuilder.Configuration.AddInMemoryCollection( - new Dictionary - { - ["OTEL_DOTNET_EXPERIMENTAL_ASPNETCORE_ENABLE_GRPC_INSTRUMENTATION"] = "true", - }); - -appBuilder.Services.AddOpenTelemetry() - .WithTracing(tracing => tracing - .AddAspNetCoreInstrumentation()); -``` - - Semantic conventions for RPC are still - [experimental](https://github.com/open-telemetry/semantic-conventions/tree/main/docs/rpc) - and hence the instrumentation only offers it as an experimental feature. - -## Troubleshooting - -This component uses an -[EventSource](https://docs.microsoft.com/dotnet/api/system.diagnostics.tracing.eventsource) -with the name "OpenTelemetry-Instrumentation-AspNetCore" for its internal -logging. Please refer to [SDK -troubleshooting](../OpenTelemetry/README.md#troubleshooting) for instructions on -seeing these internal logs. - -## References - -* [Introduction to ASP.NET - Core](https://docs.microsoft.com/aspnet/core/introduction-to-aspnet-core) -* [gRPC services using ASP.NET Core](https://docs.microsoft.com/aspnet/core/grpc/aspnetcore) -* [OpenTelemetry Project](https://opentelemetry.io/) diff --git a/src/OpenTelemetry.Instrumentation.AspNetCore/StatusCanonicalCode.cs b/src/OpenTelemetry.Instrumentation.AspNetCore/StatusCanonicalCode.cs deleted file mode 100644 index 4ddb1ec16cc..00000000000 --- a/src/OpenTelemetry.Instrumentation.AspNetCore/StatusCanonicalCode.cs +++ /dev/null @@ -1,135 +0,0 @@ -// Copyright The OpenTelemetry Authors -// SPDX-License-Identifier: Apache-2.0 - -namespace OpenTelemetry.Instrumentation.GrpcNetClient; - -/// -/// Canonical result code of span execution. -/// -/// -/// This follows the standard GRPC codes. -/// https://github.com/grpc/grpc/blob/master/doc/statuscodes.md. -/// -internal enum StatusCanonicalCode -{ - /// - /// The operation completed successfully. - /// - Ok = 0, - - /// - /// The operation was cancelled (typically by the caller). - /// - Cancelled = 1, - - /// - /// Unknown error. An example of where this error may be returned is if a Status value received - /// from another address space belongs to an error-space that is not known in this address space. - /// Also errors raised by APIs that do not return enough error information may be converted to - /// this error. - /// - Unknown = 2, - - /// - /// Client specified an invalid argument. Note that this differs from FAILED_PRECONDITION. - /// INVALID_ARGUMENT indicates arguments that are problematic regardless of the state of the - /// system (e.g., a malformed file name). - /// - InvalidArgument = 3, - - /// - /// Deadline expired before operation could complete. For operations that change the state of the - /// system, this error may be returned even if the operation has completed successfully. For - /// example, a successful response from a server could have been delayed long enough for the - /// deadline to expire. - /// - DeadlineExceeded = 4, - - /// - /// Some requested entity (e.g., file or directory) was not found. - /// - NotFound = 5, - - /// - /// Some entity that we attempted to create (e.g., file or directory) already exists. - /// - AlreadyExists = 6, - - /// - /// The caller does not have permission to execute the specified operation. PERMISSION_DENIED - /// must not be used for rejections caused by exhausting some resource (use RESOURCE_EXHAUSTED - /// instead for those errors). PERMISSION_DENIED must not be used if the caller cannot be - /// identified (use UNAUTHENTICATED instead for those errors). - /// - PermissionDenied = 7, - - /// - /// Some resource has been exhausted, perhaps a per-user quota, or perhaps the entire file system - /// is out of space. - /// - ResourceExhausted = 8, - - /// - /// Operation was rejected because the system is not in a state required for the operation's - /// execution. For example, directory to be deleted may be non-empty, an rmdir operation is - /// applied to a non-directory, etc. - /// A litmus test that may help a service implementor in deciding between FAILED_PRECONDITION, - /// ABORTED, and UNAVAILABLE: (a) Use UNAVAILABLE if the client can retry just the failing call. - /// (b) Use ABORTED if the client should retry at a higher-level (e.g., restarting a - /// read-modify-write sequence). (c) Use FAILED_PRECONDITION if the client should not retry until - /// the system state has been explicitly fixed. E.g., if an "rmdir" fails because the directory - /// is non-empty, FAILED_PRECONDITION should be returned since the client should not retry unless - /// they have first fixed up the directory by deleting files from it. - /// - FailedPrecondition = 9, - - /// - /// The operation was aborted, typically due to a concurrency issue like sequencer check - /// failures, transaction aborts, etc. - /// - Aborted = 10, - - /// - /// Operation was attempted past the valid range. E.g., seeking or reading past end of file. - /// - /// Unlike INVALID_ARGUMENT, this error indicates a problem that may be fixed if the system - /// state changes. For example, a 32-bit file system will generate INVALID_ARGUMENT if asked to - /// read at an offset that is not in the range [0,2^32-1], but it will generate OUT_OF_RANGE if - /// asked to read from an offset past the current file size. - /// - /// There is a fair bit of overlap between FAILED_PRECONDITION and OUT_OF_RANGE. We recommend - /// using OUT_OF_RANGE (the more specific error) when it applies so that callers who are - /// iterating through a space can easily look for an OUT_OF_RANGE error to detect when they are - /// done. - /// - OutOfRange = 11, - - /// - /// Operation is not implemented or not supported/enabled in this service. - /// - Unimplemented = 12, - - /// - /// Internal errors. Means some invariants expected by underlying system has been broken. If you - /// see one of these errors, something is very broken. - /// - Internal = 13, - - /// - /// The service is currently unavailable. This is a most likely a transient condition and may be - /// corrected by retrying with a backoff. - /// - /// See litmus test above for deciding between FAILED_PRECONDITION, ABORTED, and UNAVAILABLE. - /// - Unavailable = 14, - - /// - /// Unrecoverable data loss or corruption. - /// - DataLoss = 15, - - /// - /// The request does not have valid authentication credentials for the operation. - /// - Unauthenticated = 16, -} From 6a02c4b42796b96b092757a8bd85697e193ae1bb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Piotr=20Kie=C5=82kowicz?= Date: Wed, 8 May 2024 10:38:39 +0200 Subject: [PATCH 06/15] Drop unused property --- build/Common.props | 1 - 1 file changed, 1 deletion(-) diff --git a/build/Common.props b/build/Common.props index 0950b6a25ce..dd2627f9831 100644 --- a/build/Common.props +++ b/build/Common.props @@ -23,7 +23,6 @@ net8.0;net6.0;netstandard2.0;$(NetFrameworkMinimumSupportedVersion) net8.0;net6.0;netstandard2.1;netstandard2.0;$(NetFrameworkMinimumSupportedVersion) - net8.0;net7.0;net6.0;netstandard2.0 net8.0;net6.0 From 7373ede06fe4f7ee61d8e3eec57383b0d0b4ddce Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Piotr=20Kie=C5=82kowicz?= Date: Wed, 8 May 2024 10:47:40 +0200 Subject: [PATCH 07/15] Cleanup docs --- README.md | 6 ++---- VERSIONING.md | 4 ++-- build/RELEASING.md | 17 ----------------- .../getting-started-aspnetcore/README.md | 4 ++-- docs/trace/README.md | 2 +- docs/trace/extending-the-sdk/README.md | 4 ++-- docs/trace/getting-started-aspnetcore/README.md | 4 ++-- src/OpenTelemetry.Api/README.md | 2 +- 8 files changed, 12 insertions(+), 31 deletions(-) diff --git a/README.md b/README.md index 050236cae38..8a2695da6d5 100644 --- a/README.md +++ b/README.md @@ -45,10 +45,8 @@ Here are the most commonly used components: * [OpenTelemetry .NET API](./src/OpenTelemetry.Api/README.md) * [OpenTelemetry .NET SDK](./src/OpenTelemetry/README.md) -Here are the [instrumentation -libraries](https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/glossary.md#instrumentation-library): - -* [ASP.NET Core](./src/OpenTelemetry.Instrumentation.AspNetCore/README.md) +[Instrumentation libraries](https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/glossary.md#instrumentation-library) +can be found in [contrib repository](https://github.com/open-telemetry/opentelemetry-dotnet-contrib). Here are the [exporter libraries](https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/glossary.md#exporter-library): diff --git a/VERSIONING.md b/VERSIONING.md index cb70a80cd57..4f5ebd5cbca 100644 --- a/VERSIONING.md +++ b/VERSIONING.md @@ -56,9 +56,9 @@ without corresponding changes here will result in build breaks - this helps catch any unintended changes to public API from being shipped accidentally. This also helps reviewers quickly understand if a given PR is proposing public API changes. For example, -[this](https://github.com/open-telemetry/opentelemetry-dotnet/tree/master/src/OpenTelemetry.Instrumentation.AspNetCore/.publicApi) +[this](https://github.com/open-telemetry/opentelemetry-dotnet/tree/master/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/.publicApi) shows the public APIs, per target framework for the -`OpenTelemetry.Instrumentation.AspNetCore` package. +`OpenTelemetry.Exporter.OpenTelemetryProtocol` package. APIs which are released as part of stable packages will be listed in the "Shipped.txt" file, and those APIs which are released as part of diff --git a/build/RELEASING.md b/build/RELEASING.md index fcc17ad3dfe..2d2bc81d2fc 100644 --- a/build/RELEASING.md +++ b/build/RELEASING.md @@ -38,14 +38,6 @@ * `OpenTelemetry.Shims.OpenTracing` - Defined by spec (stable but incomplete implementation) - * Everything else: Instrumentation packages have dedicated tags. Some - packages have released stable and some have not. These packages may - be released as `alpha`, `beta`, `rc`, or stable depending on the - stability of the semantic conventions used by the instrumentation. - - * Stable: - * `OpenTelemetry.Instrumentation.AspNetCore` (`Instrumentation.AspNetCore-`) - * As of the `1.9.0` release cycle instrumentation packages and core unstable packages always depend on the stable versions of core packages. Before releasing a non-core component ensure the @@ -92,15 +84,6 @@ git push origin coreunstable-1.9.0-beta.1 ``` - * If releasing a particular non-core component which has a dedicated - `MinverTagPrefix` (such as AspNetCore instrumentation), push the tag with - that particular prefix. For example: - - ```sh - git tag -a Instrumentation.AspNetCore-1.6.0 -m "1.6.0 of AspNetCore instrumentation library" - git push origin Instrumentation.AspNetCore-1.6.0 - ``` - Pushing the tag will kick off the [Build, pack, and publish to MyGet](https://github.com/open-telemetry/opentelemetry-dotnet/actions/workflows/publish-packages-1.0.yml) workflow. diff --git a/docs/metrics/getting-started-aspnetcore/README.md b/docs/metrics/getting-started-aspnetcore/README.md index 7f202931b90..cd6fca9e2cd 100644 --- a/docs/metrics/getting-started-aspnetcore/README.md +++ b/docs/metrics/getting-started-aspnetcore/README.md @@ -14,7 +14,7 @@ Install the [OpenTelemetry.Exporter.Console](../../../src/OpenTelemetry.Exporter.Console/README.md), [OpenTelemetry.Extensions.Hosting](../../../src/OpenTelemetry.Extensions.Hosting/README.md), and -[OpenTelemetry.Instrumentation.AspNetCore](../../../src/OpenTelemetry.Exporter.Console/README.md) +[OpenTelemetry.Instrumentation.AspNetCore](https://github.com/open-telemetry/opentelemetry-dotnet-contrib/tree/main/src/OpenTelemetry.Instrumentation.AspNetCore/README.md) packages: ```sh @@ -56,7 +56,7 @@ Congratulations! You are now collecting metrics using OpenTelemetry. What does the above program do? The program uses the -[OpenTelemetry.Instrumentation.AspNetCore](../../../src/OpenTelemetry.Instrumentation.AspNetCore/README.md) +[OpenTelemetry.Instrumentation.AspNetCore](https://github.com/open-telemetry/opentelemetry-dotnet-contrib/tree/main/src/OpenTelemetry.Instrumentation.AspNetCore/README.md) package to automatically create metrics for incoming ASP.NET Core requests, uses the [OpenTelemetry.Exporter.Console](../../../src/OpenTelemetry.Exporter.Console/README.md) diff --git a/docs/trace/README.md b/docs/trace/README.md index ceda0ef39a6..343f65c594f 100644 --- a/docs/trace/README.md +++ b/docs/trace/README.md @@ -163,7 +163,7 @@ create too many activities (eg: for each method call). In addition to being expensive, excessive activities can also make trace visualization harder. Instead of manually creating `Activity`, check if you can leverage instrumentation libraries, such as [ASP.NET -Core](../../src/OpenTelemetry.Instrumentation.AspNetCore/README.md), +Core](https://github.com/open-telemetry/opentelemetry-dotnet-contrib/tree/main/src/OpenTelemetry.Instrumentation.AspNetCore/README.md), [HttpClient](https://github.com/open-telemetry/opentelemetry-dotnet-contrib/tree/main/src/OpenTelemetry.Instrumentation.Http/README.md) which will not only create and populate `Activity` with tags(attributes), but also take care of propagating/restoring the context across process diff --git a/docs/trace/extending-the-sdk/README.md b/docs/trace/extending-the-sdk/README.md index b5b4cbddaa3..4f7380ee64b 100644 --- a/docs/trace/extending-the-sdk/README.md +++ b/docs/trace/extending-the-sdk/README.md @@ -110,7 +110,7 @@ the following instrumentation libraries. The individual docs for them describes the library they instrument, and steps for enabling them. * [ASP.NET - Core](../../../src/OpenTelemetry.Instrumentation.AspNetCore/README.md) + Core](https://github.com/open-telemetry/opentelemetry-dotnet-contrib/tree/main/src/OpenTelemetry.Instrumentation.AspNetCore/README.md) More community contributed instrumentations are available in [OpenTelemetry .NET Contrib](https://github.com/open-telemetry/opentelemetry-dotnet-contrib/tree/main/src). @@ -224,7 +224,7 @@ activities does not by default runs through the sampler, and will have their with it. Some common examples of such libraries include [ASP.NET -Core](../../../src/OpenTelemetry.Instrumentation.AspNetCore/README.md). +Core](https://github.com/open-telemetry/opentelemetry-dotnet-contrib/tree/main/src/OpenTelemetry.Instrumentation.AspNetCore/README.md). Instrumentation libraries for these are already provided in this repo. The [OpenTelemetry .NET Contrib](https://github.com/open-telemetry/opentelemetry-dotnet-contrib) diff --git a/docs/trace/getting-started-aspnetcore/README.md b/docs/trace/getting-started-aspnetcore/README.md index 982c6ff688a..a9b7adf0b3d 100644 --- a/docs/trace/getting-started-aspnetcore/README.md +++ b/docs/trace/getting-started-aspnetcore/README.md @@ -14,7 +14,7 @@ Install the [OpenTelemetry.Exporter.Console](../../../src/OpenTelemetry.Exporter.Console/README.md), [OpenTelemetry.Extensions.Hosting](../../../src/OpenTelemetry.Extensions.Hosting/README.md), and -[OpenTelemetry.Instrumentation.AspNetCore](../../../src/OpenTelemetry.Exporter.Console/README.md) +[OpenTelemetry.Instrumentation.AspNetCore](https://github.com/open-telemetry/opentelemetry-dotnet-contrib/tree/main/src/OpenTelemetry.Instrumentation.AspNetCore/README.md) packages: ```sh @@ -61,7 +61,7 @@ Congratulations! You are now collecting traces using OpenTelemetry. What does the above program do? The program uses the -[OpenTelemetry.Instrumentation.AspNetCore](../../../src/OpenTelemetry.Instrumentation.AspNetCore/README.md) +[OpenTelemetry.Instrumentation.AspNetCore](https://github.com/open-telemetry/opentelemetry-dotnet-contrib/tree/main/src/OpenTelemetry.Instrumentation.AspNetCore/README.md) package to automatically create traces for incoming ASP.NET Core requests and uses the [OpenTelemetry.Exporter.Console](../../../src/OpenTelemetry.Exporter.Console/README.md) diff --git a/src/OpenTelemetry.Api/README.md b/src/OpenTelemetry.Api/README.md index 12109dc060e..5fd769f4461 100644 --- a/src/OpenTelemetry.Api/README.md +++ b/src/OpenTelemetry.Api/README.md @@ -154,7 +154,7 @@ required only for the following scenarios: to inject and extract context data. Some of the most common libraries requiring this include [HttpClient](https://github.com/open-telemetry/opentelemetry-dotnet-contrib/tree/main/src/OpenTelemetry.Instrumentation.Http/README.md), - [ASP.NET Core](../OpenTelemetry.Instrumentation.AspNetCore/README.md). + [ASP.NET Core](https://github.com/open-telemetry/opentelemetry-dotnet-contrib/tree/main/src/OpenTelemetry.Instrumentation.AspNetCore/README.md). This or contrib repository already provides instrumentation for these common libraries. If your library is not built on top of these, and want to leverage propagators, follow the [Context propagation](#context-propagation) section. From 76a12bfc83422cc41eb53973bc83a8211fe91cba Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Piotr=20Kie=C5=82kowicz?= Date: Wed, 8 May 2024 10:51:24 +0200 Subject: [PATCH 08/15] Cleanup CI --- .github/workflows/ci.yml | 31 ------------------------------- 1 file changed, 31 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 5d678d26275..b1a0d80bac4 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -31,7 +31,6 @@ jobs: api-code: ['*/OpenTelemetry.Api*/**', '!**/*.md'] api-packages: ['src/OpenTelemetry.Api*/**', '!**/*.md'] instrumentation: ['*/OpenTelemetry.Instrumentation*/**', 'test/TestApp.AspNetCore/**', '!**/*.md'] - instrumentation-packages: ['src/OpenTelemetry.Instrumentation*/**', '!**/*.md'] sdk-code: ['src/OpenTelemetry/**', 'test/OpenTelemetry.Tests/**', '!**/*.md'] sdk-package: ['src/OpenTelemetry/**', '!**/*.md'] unstable-core-packages: ['src/OpenTelemetry.Exporter.Prometheus.AspNetCore/**', 'src/OpenTelemetry.Exporter.Prometheus.HttpListener/**', 'src/OpenTelemetry.Shims.OpenTracing/**', '!**/*.md'] @@ -75,34 +74,6 @@ jobs: project-build-commands: '-p:ExposeExperimentalFeatures=true' code-cov-name: 'Solution-Experimental' - # Build instrumentation libraries using stable packages released to NuGet - build-test-instrumentation-stable: - needs: detect-changes - if: | - contains(needs.detect-changes.outputs.changes, 'instrumentation-packages') - || contains(needs.detect-changes.outputs.changes, 'build') - || contains(needs.detect-changes.outputs.changes, 'shared') - uses: ./.github/workflows/Component.BuildTest.yml - with: - project-name: './build/InstrumentationLibraries.proj' - project-build-commands: '-p:RunningDotNetPack=true -p:ExposeExperimentalFeatures=false' - code-cov-name: 'Instrumentation-Stable' - tfm-list: '[ "net6.0", "net7.0", "net8.0" ]' - - # Build instrumentation libraries using stable packages released to NuGet - build-test-instrumentation-experimental: - needs: detect-changes - if: | - contains(needs.detect-changes.outputs.changes, 'instrumentation-packages') - || contains(needs.detect-changes.outputs.changes, 'build') - || contains(needs.detect-changes.outputs.changes, 'shared') - uses: ./.github/workflows/Component.BuildTest.yml - with: - project-name: './build/InstrumentationLibraries.proj' - project-build-commands: '-p:RunningDotNetPack=true -p:ExposeExperimentalFeatures=true' - code-cov-name: 'Instrumentation-Experimental' - tfm-list: '[ "net6.0", "net7.0", "net8.0" ]' - # Build unstable core libraries using stable packages released to NuGet build-test-unstable-core: needs: detect-changes @@ -194,8 +165,6 @@ jobs: lint-dotnet-format, build-test-solution-stable, build-test-solution-experimental, - build-test-instrumentation-stable, - build-test-instrumentation-experimental, build-test-unstable-core, otlp-integration-test, w3c-trace-context-integration-test, From f951349985a01af1a37becde33525a7e91cdf44f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Piotr=20Kie=C5=82kowicz?= Date: Wed, 8 May 2024 10:52:09 +0200 Subject: [PATCH 09/15] Drop InstrumentationLibraries.proj --- OpenTelemetry.sln | 1 - build/InstrumentationLibraries.proj | 18 ------------------ 2 files changed, 19 deletions(-) delete mode 100644 build/InstrumentationLibraries.proj diff --git a/OpenTelemetry.sln b/OpenTelemetry.sln index 1141fd2d434..7e46ad04d14 100644 --- a/OpenTelemetry.sln +++ b/OpenTelemetry.sln @@ -36,7 +36,6 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "build", "build", "{7CB2F02E build\docker-compose.net8.0.yml = build\docker-compose.net8.0.yml build\finalize-publicapi.ps1 = build\finalize-publicapi.ps1 build\GlobalAttrExclusions.txt = build\GlobalAttrExclusions.txt - build\InstrumentationLibraries.proj = build\InstrumentationLibraries.proj build\opentelemetry-icon-color.png = build\opentelemetry-icon-color.png build\OpenTelemetry.prod.loose.ruleset = build\OpenTelemetry.prod.loose.ruleset build\OpenTelemetry.prod.ruleset = build\OpenTelemetry.prod.ruleset diff --git a/build/InstrumentationLibraries.proj b/build/InstrumentationLibraries.proj deleted file mode 100644 index 4a2ee62aaa4..00000000000 --- a/build/InstrumentationLibraries.proj +++ /dev/null @@ -1,18 +0,0 @@ - - - - - - - - - - - - - - - - - - From f66d4dce3961b33736775a350ab550f21631e844 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Piotr=20Kie=C5=82kowicz?= Date: Wed, 8 May 2024 10:53:24 +0200 Subject: [PATCH 10/15] Cleanup Common.props --- build/Common.props | 10 +--------- 1 file changed, 1 insertion(+), 9 deletions(-) diff --git a/build/Common.props b/build/Common.props index dd2627f9831..26eec294ea8 100644 --- a/build/Common.props +++ b/build/Common.props @@ -57,15 +57,7 @@ - - - - - - - - - + From f0a4bb4a849e4322bc86dba739c0630f21e66c4c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Piotr=20Kie=C5=82kowicz?= Date: Wed, 8 May 2024 10:55:25 +0200 Subject: [PATCH 11/15] Drop SpanHelper --- OpenTelemetry.sln | 1 - build/Common.props | 1 - src/Shared/SpanHelper.cs | 31 ------------------------------- 3 files changed, 33 deletions(-) delete mode 100644 src/Shared/SpanHelper.cs diff --git a/OpenTelemetry.sln b/OpenTelemetry.sln index 7e46ad04d14..bee538778c0 100644 --- a/OpenTelemetry.sln +++ b/OpenTelemetry.sln @@ -258,7 +258,6 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Shared", "Shared", "{A49299 src\Shared\ResourceSemanticConventions.cs = src\Shared\ResourceSemanticConventions.cs src\Shared\SemanticConventions.cs = src\Shared\SemanticConventions.cs src\Shared\SpanAttributeConstants.cs = src\Shared\SpanAttributeConstants.cs - src\Shared\SpanHelper.cs = src\Shared\SpanHelper.cs src\Shared\StatusHelper.cs = src\Shared\StatusHelper.cs src\Shared\TagTransformer.cs = src\Shared\TagTransformer.cs src\Shared\TagTransformerJsonHelper.cs = src\Shared\TagTransformerJsonHelper.cs diff --git a/build/Common.props b/build/Common.props index 26eec294ea8..63d25bd9306 100644 --- a/build/Common.props +++ b/build/Common.props @@ -62,7 +62,6 @@ - diff --git a/src/Shared/SpanHelper.cs b/src/Shared/SpanHelper.cs deleted file mode 100644 index 8f5aabc4a31..00000000000 --- a/src/Shared/SpanHelper.cs +++ /dev/null @@ -1,31 +0,0 @@ -// Copyright The OpenTelemetry Authors -// SPDX-License-Identifier: Apache-2.0 - -using System.Diagnostics; - -namespace OpenTelemetry.Trace; - -/// -/// A collection of helper methods to be used when building spans. -/// -internal static class SpanHelper -{ - /// - /// Helper method that populates span properties from http status code according - /// to https://github.com/open-telemetry/semantic-conventions/blob/main/docs/http/http-spans.md#common-attributes. - /// - /// The span kind. - /// Http status code. - /// Resolved span for the Http status code. - public static ActivityStatusCode ResolveSpanStatusForHttpStatusCode(ActivityKind kind, int httpStatusCode) - { - var lowerBound = kind == ActivityKind.Client ? 400 : 500; - var upperBound = 599; - if (httpStatusCode >= lowerBound && httpStatusCode <= upperBound) - { - return ActivityStatusCode.Error; - } - - return ActivityStatusCode.Unset; - } -} From 86383ed7c8664fbe5b381969b479a7968188cc1c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Piotr=20Kie=C5=82kowicz?= Date: Wed, 8 May 2024 10:59:58 +0200 Subject: [PATCH 12/15] Drop RequestMethodHelper --- OpenTelemetry.sln | 1 - src/Shared/RequestMethodHelper.cs | 74 ------------------------------- 2 files changed, 75 deletions(-) delete mode 100644 src/Shared/RequestMethodHelper.cs diff --git a/OpenTelemetry.sln b/OpenTelemetry.sln index bee538778c0..99cc4dbd756 100644 --- a/OpenTelemetry.sln +++ b/OpenTelemetry.sln @@ -254,7 +254,6 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Shared", "Shared", "{A49299 src\Shared\PeriodicExportingMetricReaderHelper.cs = src\Shared\PeriodicExportingMetricReaderHelper.cs src\Shared\PooledList.cs = src\Shared\PooledList.cs src\Shared\RedactionHelper.cs = src\Shared\RedactionHelper.cs - src\Shared\RequestMethodHelper.cs = src\Shared\RequestMethodHelper.cs src\Shared\ResourceSemanticConventions.cs = src\Shared\ResourceSemanticConventions.cs src\Shared\SemanticConventions.cs = src\Shared\SemanticConventions.cs src\Shared\SpanAttributeConstants.cs = src\Shared\SpanAttributeConstants.cs diff --git a/src/Shared/RequestMethodHelper.cs b/src/Shared/RequestMethodHelper.cs deleted file mode 100644 index 0dfc7684230..00000000000 --- a/src/Shared/RequestMethodHelper.cs +++ /dev/null @@ -1,74 +0,0 @@ -// Copyright The OpenTelemetry Authors -// SPDX-License-Identifier: Apache-2.0 - -#nullable enable - -#if NET8_0_OR_GREATER -using System.Collections.Frozen; -#endif -using System.Diagnostics; -using OpenTelemetry.Trace; - -namespace OpenTelemetry.Internal; - -internal static class RequestMethodHelper -{ - // The value "_OTHER" is used for non-standard HTTP methods. - // https://github.com/open-telemetry/semantic-conventions/blob/v1.22.0/docs/http/http-spans.md#common-attributes - public const string OtherHttpMethod = "_OTHER"; - -#if NET8_0_OR_GREATER - internal static readonly FrozenDictionary KnownMethods; -#else - internal static readonly Dictionary KnownMethods; -#endif - - static RequestMethodHelper() - { - var knownMethodSet = new Dictionary(StringComparer.OrdinalIgnoreCase) - { - { "GET", "GET" }, - { "PUT", "PUT" }, - { "POST", "POST" }, - { "DELETE", "DELETE" }, - { "HEAD", "HEAD" }, - { "OPTIONS", "OPTIONS" }, - { "TRACE", "TRACE" }, - { "PATCH", "PATCH" }, - { "CONNECT", "CONNECT" }, - }; - - // KnownMethods ignores case. Use the value returned by the dictionary to have a consistent case. -#if NET8_0_OR_GREATER - KnownMethods = FrozenDictionary.ToFrozenDictionary(knownMethodSet, StringComparer.OrdinalIgnoreCase); -#else - KnownMethods = knownMethodSet; -#endif - } - - public static string GetNormalizedHttpMethod(string method) - { - return KnownMethods.TryGetValue(method, out var normalizedMethod) - ? normalizedMethod - : OtherHttpMethod; - } - - public static void SetHttpMethodTag(Activity activity, string originalHttpMethod) - { - var normalizedHttpMethod = GetNormalizedHttpMethod(originalHttpMethod); - activity.SetTag(SemanticConventions.AttributeHttpRequestMethod, normalizedHttpMethod); - - if (originalHttpMethod != normalizedHttpMethod) - { - activity.SetTag(SemanticConventions.AttributeHttpRequestMethodOriginal, originalHttpMethod); - } - } - - public static void SetActivityDisplayName(Activity activity, string method, string? httpRoute = null) - { - // https://github.com/open-telemetry/semantic-conventions/blob/v1.24.0/docs/http/http-spans.md#name - - var namePrefix = KnownMethods.TryGetValue(method, out var httpMethod) ? httpMethod : "HTTP"; - activity.DisplayName = string.IsNullOrEmpty(httpRoute) ? namePrefix : $"{namePrefix} {httpRoute}"; - } -} From 1a2e3e3b7769a240f3292db1c9331e59d7889c0c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Piotr=20Kie=C5=82kowicz?= Date: Wed, 8 May 2024 11:12:54 +0200 Subject: [PATCH 13/15] Drop unused shared files --- OpenTelemetry.sln | 3 - .../DiagnosticSourceListener.cs | 47 -------- .../DiagnosticSourceSubscriber.cs | 103 ------------------ .../ListenerHandler.cs | 40 ------- .../OpenTelemetry.Tests.csproj | 1 - 5 files changed, 194 deletions(-) delete mode 100644 src/Shared/DiagnosticSourceInstrumentation/DiagnosticSourceListener.cs delete mode 100644 src/Shared/DiagnosticSourceInstrumentation/DiagnosticSourceSubscriber.cs delete mode 100644 src/Shared/DiagnosticSourceInstrumentation/ListenerHandler.cs diff --git a/OpenTelemetry.sln b/OpenTelemetry.sln index 99cc4dbd756..5f0bbaeb084 100644 --- a/OpenTelemetry.sln +++ b/OpenTelemetry.sln @@ -265,9 +265,6 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Shared", "Shared", "{A49299 EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "DiagnosticSourceInstrumentation", "DiagnosticSourceInstrumentation", "{28F3EC79-660C-4659-8B73-F90DC1173316}" ProjectSection(SolutionItems) = preProject - src\Shared\DiagnosticSourceInstrumentation\DiagnosticSourceListener.cs = src\Shared\DiagnosticSourceInstrumentation\DiagnosticSourceListener.cs - src\Shared\DiagnosticSourceInstrumentation\DiagnosticSourceSubscriber.cs = src\Shared\DiagnosticSourceInstrumentation\DiagnosticSourceSubscriber.cs - src\Shared\DiagnosticSourceInstrumentation\ListenerHandler.cs = src\Shared\DiagnosticSourceInstrumentation\ListenerHandler.cs src\Shared\DiagnosticSourceInstrumentation\PropertyFetcher.cs = src\Shared\DiagnosticSourceInstrumentation\PropertyFetcher.cs EndProjectSection EndProject diff --git a/src/Shared/DiagnosticSourceInstrumentation/DiagnosticSourceListener.cs b/src/Shared/DiagnosticSourceInstrumentation/DiagnosticSourceListener.cs deleted file mode 100644 index 51838533a86..00000000000 --- a/src/Shared/DiagnosticSourceInstrumentation/DiagnosticSourceListener.cs +++ /dev/null @@ -1,47 +0,0 @@ -// Copyright The OpenTelemetry Authors -// SPDX-License-Identifier: Apache-2.0 - -using System.Diagnostics; -using OpenTelemetry.Internal; - -namespace OpenTelemetry.Instrumentation; - -internal sealed class DiagnosticSourceListener : IObserver> -{ - private readonly ListenerHandler handler; - - private readonly Action logUnknownException; - - public DiagnosticSourceListener(ListenerHandler handler, Action logUnknownException) - { - Guard.ThrowIfNull(handler); - - this.handler = handler; - this.logUnknownException = logUnknownException; - } - - public void OnCompleted() - { - } - - public void OnError(Exception error) - { - } - - public void OnNext(KeyValuePair value) - { - if (!this.handler.SupportsNullActivity && Activity.Current == null) - { - return; - } - - try - { - this.handler.OnEventWritten(value.Key, value.Value); - } - catch (Exception ex) - { - this.logUnknownException?.Invoke(this.handler?.SourceName, value.Key, ex); - } - } -} diff --git a/src/Shared/DiagnosticSourceInstrumentation/DiagnosticSourceSubscriber.cs b/src/Shared/DiagnosticSourceInstrumentation/DiagnosticSourceSubscriber.cs deleted file mode 100644 index 3071617f8a1..00000000000 --- a/src/Shared/DiagnosticSourceInstrumentation/DiagnosticSourceSubscriber.cs +++ /dev/null @@ -1,103 +0,0 @@ -// Copyright The OpenTelemetry Authors -// SPDX-License-Identifier: Apache-2.0 - -using System.Diagnostics; -using OpenTelemetry.Internal; - -namespace OpenTelemetry.Instrumentation; - -internal sealed class DiagnosticSourceSubscriber : IDisposable, IObserver -{ - private readonly List listenerSubscriptions; - private readonly Func handlerFactory; - private readonly Func diagnosticSourceFilter; - private readonly Func isEnabledFilter; - private readonly Action logUnknownException; - private long disposed; - private IDisposable allSourcesSubscription; - - public DiagnosticSourceSubscriber( - ListenerHandler handler, - Func isEnabledFilter, - Action logUnknownException) - : this(_ => handler, value => handler.SourceName == value.Name, isEnabledFilter, logUnknownException) - { - } - - public DiagnosticSourceSubscriber( - Func handlerFactory, - Func diagnosticSourceFilter, - Func isEnabledFilter, - Action logUnknownException) - { - Guard.ThrowIfNull(handlerFactory); - - this.listenerSubscriptions = new List(); - this.handlerFactory = handlerFactory; - this.diagnosticSourceFilter = diagnosticSourceFilter; - this.isEnabledFilter = isEnabledFilter; - this.logUnknownException = logUnknownException; - } - - public void Subscribe() - { - if (this.allSourcesSubscription == null) - { - this.allSourcesSubscription = DiagnosticListener.AllListeners.Subscribe(this); - } - } - - public void OnNext(DiagnosticListener value) - { - if ((Interlocked.Read(ref this.disposed) == 0) && - this.diagnosticSourceFilter(value)) - { - var handler = this.handlerFactory(value.Name); - var listener = new DiagnosticSourceListener(handler, this.logUnknownException); - var subscription = this.isEnabledFilter == null ? - value.Subscribe(listener) : - value.Subscribe(listener, this.isEnabledFilter); - - lock (this.listenerSubscriptions) - { - this.listenerSubscriptions.Add(subscription); - } - } - } - - public void OnCompleted() - { - } - - public void OnError(Exception error) - { - } - - /// - public void Dispose() - { - this.Dispose(true); - GC.SuppressFinalize(this); - } - - private void Dispose(bool disposing) - { - if (Interlocked.CompareExchange(ref this.disposed, 1, 0) == 1) - { - return; - } - - lock (this.listenerSubscriptions) - { - foreach (var listenerSubscription in this.listenerSubscriptions) - { - listenerSubscription?.Dispose(); - } - - this.listenerSubscriptions.Clear(); - } - - this.allSourcesSubscription?.Dispose(); - this.allSourcesSubscription = null; - } -} diff --git a/src/Shared/DiagnosticSourceInstrumentation/ListenerHandler.cs b/src/Shared/DiagnosticSourceInstrumentation/ListenerHandler.cs deleted file mode 100644 index 98c55107a67..00000000000 --- a/src/Shared/DiagnosticSourceInstrumentation/ListenerHandler.cs +++ /dev/null @@ -1,40 +0,0 @@ -// Copyright The OpenTelemetry Authors -// SPDX-License-Identifier: Apache-2.0 - -using System.Diagnostics; - -namespace OpenTelemetry.Instrumentation; - -/// -/// ListenerHandler base class. -/// -internal abstract class ListenerHandler -{ - /// - /// Initializes a new instance of the class. - /// - /// The name of the . - public ListenerHandler(string sourceName) - { - this.SourceName = sourceName; - } - - /// - /// Gets the name of the . - /// - public string SourceName { get; } - - /// - /// Gets a value indicating whether the supports NULL . - /// - public virtual bool SupportsNullActivity { get; } - - /// - /// Method called for an event which does not have 'Start', 'Stop' or 'Exception' as suffix. - /// - /// Custom name. - /// An object that represent the value being passed as a payload for the event. - public virtual void OnEventWritten(string name, object payload) - { - } -} diff --git a/test/OpenTelemetry.Tests/OpenTelemetry.Tests.csproj b/test/OpenTelemetry.Tests/OpenTelemetry.Tests.csproj index 6d2b3619bd6..87c8a444837 100644 --- a/test/OpenTelemetry.Tests/OpenTelemetry.Tests.csproj +++ b/test/OpenTelemetry.Tests/OpenTelemetry.Tests.csproj @@ -20,7 +20,6 @@ - From 350d5c0797f1f92ded26290cfd2942a9ccd24465 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Piotr=20Kie=C5=82kowicz?= Date: Wed, 8 May 2024 11:21:07 +0200 Subject: [PATCH 14/15] fix Cleanup AOT test package --- .../OpenTelemetry.AotCompatibility.TestApp.csproj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/OpenTelemetry.AotCompatibility.TestApp/OpenTelemetry.AotCompatibility.TestApp.csproj b/test/OpenTelemetry.AotCompatibility.TestApp/OpenTelemetry.AotCompatibility.TestApp.csproj index 4c53546e3bb..4cf2627eb3d 100644 --- a/test/OpenTelemetry.AotCompatibility.TestApp/OpenTelemetry.AotCompatibility.TestApp.csproj +++ b/test/OpenTelemetry.AotCompatibility.TestApp/OpenTelemetry.AotCompatibility.TestApp.csproj @@ -18,11 +18,11 @@ + - From 1b0a697dade713a01b2c1c7f0fdd0090a68ad596 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Piotr=20Kie=C5=82kowicz?= Date: Wed, 8 May 2024 18:16:12 +0200 Subject: [PATCH 15/15] PR feedback, update RELEASING --- build/RELEASING.md | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/build/RELEASING.md b/build/RELEASING.md index 2d2bc81d2fc..5185af401de 100644 --- a/build/RELEASING.md +++ b/build/RELEASING.md @@ -135,16 +135,11 @@ `Ready for review` and then merge it once the build passes (this requires the packages be available on NuGet). -15. If a new stable version of a package with a dedicated `MinVerTagPrefix` was - released (typically instrumentation packages) open a PR to update - `PackageValidationBaselineVersion` in the project file to reflect the stable - version which was just released. - -16. If a new stable version of the core packages was released, open an issue in +15. If a new stable version of the core packages was released, open an issue in the [opentelemetry-dotnet-contrib](https://github.com/open-telemetry/opentelemetry-dotnet-contrib) repo to notify maintainers to begin upgrading dependencies. -17. Post an announcement in the [Slack +16. Post an announcement in the [Slack channel](https://cloud-native.slack.com/archives/C01N3BC2W7Q). Note any big or interesting new features as part of the announcement.