From 8a991e109bbbc556a3451c3538415811848a4cc8 Mon Sep 17 00:00:00 2001 From: Vishwesh Bankwar Date: Fri, 27 Oct 2023 12:26:20 -0700 Subject: [PATCH 01/12] Set http.request.method as per spec --- .../Implementation/HttpInListener.cs | 11 ++- .../Implementation/HttpInMetricsListener.cs | 3 +- .../Implementation/TelemetryHelper.cs | 52 ++++++++++++ src/Shared/SemanticConventions.cs | 1 + .../BasicTests.cs | 75 +++++++++++++++++ .../MetricTests.cs | 84 +++++++++++++++++++ 6 files changed, 224 insertions(+), 2 deletions(-) diff --git a/src/OpenTelemetry.Instrumentation.AspNetCore/Implementation/HttpInListener.cs b/src/OpenTelemetry.Instrumentation.AspNetCore/Implementation/HttpInListener.cs index 3b25d781ee2..21e348b76a8 100644 --- a/src/OpenTelemetry.Instrumentation.AspNetCore/Implementation/HttpInListener.cs +++ b/src/OpenTelemetry.Instrumentation.AspNetCore/Implementation/HttpInListener.cs @@ -252,7 +252,16 @@ public void OnStartActivity(Activity activity, object payload) activity.SetTag(SemanticConventions.AttributeUrlQuery, request.QueryString.Value); } - activity.SetTag(SemanticConventions.AttributeHttpRequestMethod, request.Method); + if (TelemetryHelper.TryResolveHttpMethod(request.Method, out var httpMethod)) + { + activity.SetTag(SemanticConventions.AttributeHttpRequestMethod, httpMethod); + } + else + { + activity.SetTag(SemanticConventions.AttributeHttpRequestMethod, httpMethod); + activity.SetTag(SemanticConventions.AttributeHttpRequestMethodOriginal, request.Method); + } + activity.SetTag(SemanticConventions.AttributeUrlScheme, request.Scheme); activity.SetTag(SemanticConventions.AttributeUrlPath, path); activity.SetTag(SemanticConventions.AttributeNetworkProtocolVersion, HttpTagHelper.GetFlavorTagValueFromProtocol(request.Protocol)); diff --git a/src/OpenTelemetry.Instrumentation.AspNetCore/Implementation/HttpInMetricsListener.cs b/src/OpenTelemetry.Instrumentation.AspNetCore/Implementation/HttpInMetricsListener.cs index 07221945d6c..210e0de6745 100644 --- a/src/OpenTelemetry.Instrumentation.AspNetCore/Implementation/HttpInMetricsListener.cs +++ b/src/OpenTelemetry.Instrumentation.AspNetCore/Implementation/HttpInMetricsListener.cs @@ -150,8 +150,9 @@ public void OnEventWritten_New(string name, object payload) tags.Add(new KeyValuePair(SemanticConventions.AttributeNetworkProtocolName, NetworkProtocolName)); 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.AttributeHttpRequestMethod, context.Request.Method)); tags.Add(new KeyValuePair(SemanticConventions.AttributeHttpResponseStatusCode, TelemetryHelper.GetBoxedStatusCode(context.Response.StatusCode))); + TelemetryHelper.TryResolveHttpMethod(context.Request.Method, out var httpMethod); + tags.Add(new KeyValuePair(SemanticConventions.AttributeHttpRequestMethod, httpMethod)); #if NET6_0_OR_GREATER var route = (context.GetEndpoint() as RouteEndpoint)?.RoutePattern.RawText; diff --git a/src/OpenTelemetry.Instrumentation.AspNetCore/Implementation/TelemetryHelper.cs b/src/OpenTelemetry.Instrumentation.AspNetCore/Implementation/TelemetryHelper.cs index 73d84269e82..2df46c0400b 100644 --- a/src/OpenTelemetry.Instrumentation.AspNetCore/Implementation/TelemetryHelper.cs +++ b/src/OpenTelemetry.Instrumentation.AspNetCore/Implementation/TelemetryHelper.cs @@ -14,12 +14,49 @@ // limitations under the License. // +using Microsoft.AspNetCore.Http; +#if NET8_0_OR_GREATER +using System.Collections.Frozen; +using System.Collections.Generic; +#endif + namespace OpenTelemetry.Instrumentation.AspNetCore.Implementation; internal static class TelemetryHelper { public static readonly object[] BoxedStatusCodes; +#if NET8_0_OR_GREATER + internal static readonly FrozenDictionary KnownMethods = FrozenDictionary.ToFrozenDictionary( + new[] + { + KeyValuePair.Create(HttpMethods.Connect, HttpMethods.Connect), + KeyValuePair.Create(HttpMethods.Delete, HttpMethods.Delete), + KeyValuePair.Create(HttpMethods.Get, HttpMethods.Get), + KeyValuePair.Create(HttpMethods.Head, HttpMethods.Head), + KeyValuePair.Create(HttpMethods.Options, HttpMethods.Options), + KeyValuePair.Create(HttpMethods.Patch, HttpMethods.Patch), + KeyValuePair.Create(HttpMethods.Post, HttpMethods.Post), + KeyValuePair.Create(HttpMethods.Put, HttpMethods.Put), + KeyValuePair.Create(HttpMethods.Trace, HttpMethods.Trace) + }, + StringComparer.OrdinalIgnoreCase); +#else + internal static readonly Dictionary KnownMethods = new Dictionary(StringComparer.OrdinalIgnoreCase) + { + { HttpMethods.Connect, HttpMethods.Connect }, + { HttpMethods.Delete, HttpMethods.Delete }, + { HttpMethods.Get, HttpMethods.Get }, + { HttpMethods.Head, HttpMethods.Head }, + { HttpMethods.Options, HttpMethods.Options }, + { HttpMethods.Patch, HttpMethods.Patch }, + { HttpMethods.Post, HttpMethods.Post }, + { HttpMethods.Put, HttpMethods.Put }, + { HttpMethods.Trace, HttpMethods.Trace }, + }; + +#endif + static TelemetryHelper() { BoxedStatusCodes = new object[500]; @@ -38,4 +75,19 @@ public static object GetBoxedStatusCode(int statusCode) return statusCode; } + + public static bool TryResolveHttpMethod(string method, out string resolvedMethod) + { + if (KnownMethods.TryGetValue(method, out var result)) + { + // KnownMethods ignores case. Use the value returned by the dictionary to have a consistent case. + resolvedMethod = result; + return true; + } + + // Set to default "_OTHER" as per spec. + // https://github.com/open-telemetry/semantic-conventions/blob/v1.22.0/docs/http/http-spans.md#common-attributes + resolvedMethod = "_OTHER"; + return false; + } } diff --git a/src/Shared/SemanticConventions.cs b/src/Shared/SemanticConventions.cs index d48f7d6bca1..26c016b3e1f 100644 --- a/src/Shared/SemanticConventions.cs +++ b/src/Shared/SemanticConventions.cs @@ -129,4 +129,5 @@ internal static class SemanticConventions public const string AttributeUrlScheme = "url.scheme"; // replaces: "http.scheme" (AttributeHttpScheme) public const string AttributeUrlQuery = "url.query"; public const string AttributeUserAgentOriginal = "user_agent.original"; // replaces: "http.user_agent" (AttributeHttpUserAgent) + public const string AttributeHttpRequestMethodOriginal = "http.request.method_original"; } diff --git a/test/OpenTelemetry.Instrumentation.AspNetCore.Tests/BasicTests.cs b/test/OpenTelemetry.Instrumentation.AspNetCore.Tests/BasicTests.cs index 87a70d3bfbd..322eae5a15b 100644 --- a/test/OpenTelemetry.Instrumentation.AspNetCore.Tests/BasicTests.cs +++ b/test/OpenTelemetry.Instrumentation.AspNetCore.Tests/BasicTests.cs @@ -21,6 +21,7 @@ using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Mvc.Testing; using Microsoft.AspNetCore.TestHost; +using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Logging; using Moq; @@ -32,6 +33,8 @@ using TestApp.AspNetCore.Filters; using Xunit; +using static OpenTelemetry.Internal.HttpSemanticConventionHelper; + namespace OpenTelemetry.Instrumentation.AspNetCore.Tests; // See https://github.com/aspnet/Docs/tree/master/aspnetcore/test/integration-tests/samples/2.x/IntegrationTestsSample @@ -647,6 +650,78 @@ void ConfigureTestServices(IServiceCollection services) Assert.Equal("api/Values/{id}", aspnetcoreframeworkactivity.DisplayName); } + [Theory] + [InlineData("CONNECT")] + [InlineData("DELETE")] + [InlineData("GET")] + [InlineData("PUT")] + [InlineData("HEAD")] + [InlineData("OPTIONS")] + [InlineData("PATCH")] + [InlineData("Get")] + [InlineData("POST")] + [InlineData("TRACE")] + [InlineData("CUSTOM")] + public async Task HttpRequestMethodIsSetAsPerSpec(string method) + { + var exportedItems = new List(); + + var configuration = new ConfigurationBuilder() + .AddInMemoryCollection(new Dictionary { [SemanticConventionOptInKeyName] = "http" }) + .Build(); + + void ConfigureTestServices(IServiceCollection services) + { + this.tracerProvider = Sdk.CreateTracerProviderBuilder() + .ConfigureServices(services => services.AddSingleton(configuration)) + .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(method); + + try + { + using var response = await client.SendAsync(message).ConfigureAwait(false); + response.EnsureSuccessStatusCode(); + } + catch + { + // ignore error. + } + + WaitForActivityExport(exportedItems, 1); + + Assert.Single(exportedItems); + + var activity = exportedItems[0]; + + Assert.Contains(activity.TagObjects, t => t.Key == SemanticConventions.AttributeHttpRequestMethod); + + if (TelemetryHelper.KnownMethods.TryGetValue(method, out var val)) + { + Assert.Equal(val, activity.GetTagValue(SemanticConventions.AttributeHttpRequestMethod) as string); + Assert.DoesNotContain(activity.TagObjects, t => t.Key == SemanticConventions.AttributeHttpRequestMethodOriginal); + } + else + { + Assert.Equal("_OTHER", activity.GetTagValue(SemanticConventions.AttributeHttpRequestMethod) as string); + Assert.Equal(method, activity.GetTagValue(SemanticConventions.AttributeHttpRequestMethodOriginal) as string); + } + } + [Fact] public async Task ActivitiesStartedInMiddlewareBySettingHostActivityToNullShouldNotBeUpdated() { diff --git a/test/OpenTelemetry.Instrumentation.AspNetCore.Tests/MetricTests.cs b/test/OpenTelemetry.Instrumentation.AspNetCore.Tests/MetricTests.cs index ff11b5e1225..605a0792593 100644 --- a/test/OpenTelemetry.Instrumentation.AspNetCore.Tests/MetricTests.cs +++ b/test/OpenTelemetry.Instrumentation.AspNetCore.Tests/MetricTests.cs @@ -29,6 +29,7 @@ using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Logging; +using OpenTelemetry.Instrumentation.AspNetCore.Implementation; using OpenTelemetry.Metrics; using OpenTelemetry.Trace; using Xunit; @@ -237,6 +238,89 @@ public async Task RequestMetricIsCaptured_New() expectedTagsCount: 6); } + [Theory] + [InlineData("CONNECT")] + [InlineData("DELETE")] + [InlineData("GET")] + [InlineData("GeT")] + [InlineData("PUT")] + [InlineData("HEAD")] + [InlineData("OPTIONS")] + [InlineData("PATCH")] + [InlineData("POST")] + [InlineData("TRACE")] + [InlineData("CUSTOM")] + public async Task HttpRequestMethodIsCapturedAsPerSpec(string method) + { + var configuration = new ConfigurationBuilder() + .AddInMemoryCollection(new Dictionary { [SemanticConventionOptInKeyName] = "http" }) + .Build(); + + var metricItems = new List(); + + this.meterProvider = Sdk.CreateMeterProviderBuilder() + .ConfigureServices(services => services.AddSingleton(configuration)) + .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(method); + + try + { + using var response = await client.SendAsync(message).ConfigureAwait(false); + } + 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)).ConfigureAwait(false); + + 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; + } + + if (TelemetryHelper.KnownMethods.TryGetValue(method, out var val)) + { + Assert.Contains(attributes, kvp => kvp.Key == SemanticConventions.AttributeHttpRequestMethod && kvp.Value.ToString() == method.ToUpper()); + } + else + { + Assert.Contains(attributes, kvp => kvp.Key == SemanticConventions.AttributeHttpRequestMethod && kvp.Value.ToString() == "_OTHER"); + } + + Assert.DoesNotContain(attributes, t => t.Key == SemanticConventions.AttributeHttpRequestMethodOriginal); + } + #if !NET8_0_OR_GREATER [Fact] public async Task RequestMetricIsCaptured_Old() From 20c3b095fb2b5e005866879504dbcdaf324efcc2 Mon Sep 17 00:00:00 2001 From: Vishwesh Bankwar Date: Fri, 27 Oct 2023 12:32:05 -0700 Subject: [PATCH 02/12] fix build --- .../Implementation/TelemetryHelper.cs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/OpenTelemetry.Instrumentation.AspNetCore/Implementation/TelemetryHelper.cs b/src/OpenTelemetry.Instrumentation.AspNetCore/Implementation/TelemetryHelper.cs index 2df46c0400b..351345eda43 100644 --- a/src/OpenTelemetry.Instrumentation.AspNetCore/Implementation/TelemetryHelper.cs +++ b/src/OpenTelemetry.Instrumentation.AspNetCore/Implementation/TelemetryHelper.cs @@ -17,7 +17,6 @@ using Microsoft.AspNetCore.Http; #if NET8_0_OR_GREATER using System.Collections.Frozen; -using System.Collections.Generic; #endif namespace OpenTelemetry.Instrumentation.AspNetCore.Implementation; @@ -38,7 +37,7 @@ internal static class TelemetryHelper KeyValuePair.Create(HttpMethods.Patch, HttpMethods.Patch), KeyValuePair.Create(HttpMethods.Post, HttpMethods.Post), KeyValuePair.Create(HttpMethods.Put, HttpMethods.Put), - KeyValuePair.Create(HttpMethods.Trace, HttpMethods.Trace) + KeyValuePair.Create(HttpMethods.Trace, HttpMethods.Trace), }, StringComparer.OrdinalIgnoreCase); #else From f70c4fdae57ee2fabd37eda6a114505ecf12cc80 Mon Sep 17 00:00:00 2001 From: Vishwesh Bankwar Date: Fri, 27 Oct 2023 13:33:04 -0700 Subject: [PATCH 03/12] add changelog --- .../CHANGELOG.md | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/src/OpenTelemetry.Instrumentation.AspNetCore/CHANGELOG.md b/src/OpenTelemetry.Instrumentation.AspNetCore/CHANGELOG.md index eb65dfb92c8..07468db4700 100644 --- a/src/OpenTelemetry.Instrumentation.AspNetCore/CHANGELOG.md +++ b/src/OpenTelemetry.Instrumentation.AspNetCore/CHANGELOG.md @@ -2,6 +2,22 @@ ## Unreleased +* Updated `http.request.method` to match specification guidelines. + * For activity, request method will be set on an additional tag + `http.request.method.original` will be added 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) + and `http.request.method` will be set to `_OTHER`. + * For metrics, `http.request.method` on `http.server.request.duration` metric + will be set to `_OTHER` 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) + + `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] + ## 1.6.0-beta.2 Released 2023-Oct-26 From 06e0c5dde847c88299128805649fd579977b8a06 Mon Sep 17 00:00:00 2001 From: Vishwesh Bankwar Date: Fri, 27 Oct 2023 13:33:55 -0700 Subject: [PATCH 04/12] fix link --- src/OpenTelemetry.Instrumentation.AspNetCore/CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/OpenTelemetry.Instrumentation.AspNetCore/CHANGELOG.md b/src/OpenTelemetry.Instrumentation.AspNetCore/CHANGELOG.md index 07468db4700..b5135b742cc 100644 --- a/src/OpenTelemetry.Instrumentation.AspNetCore/CHANGELOG.md +++ b/src/OpenTelemetry.Instrumentation.AspNetCore/CHANGELOG.md @@ -16,7 +16,7 @@ `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] + ([#5001](https://github.com/open-telemetry/opentelemetry-dotnet/pull/5001)) ## 1.6.0-beta.2 From a11a48126ee951e636a5238bc4b7a73bbcc67427 Mon Sep 17 00:00:00 2001 From: Vishwesh Bankwar Date: Mon, 30 Oct 2023 14:51:53 -0700 Subject: [PATCH 05/12] fix changelog --- src/OpenTelemetry.Instrumentation.AspNetCore/CHANGELOG.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/OpenTelemetry.Instrumentation.AspNetCore/CHANGELOG.md b/src/OpenTelemetry.Instrumentation.AspNetCore/CHANGELOG.md index b5135b742cc..f18a5497c9c 100644 --- a/src/OpenTelemetry.Instrumentation.AspNetCore/CHANGELOG.md +++ b/src/OpenTelemetry.Instrumentation.AspNetCore/CHANGELOG.md @@ -4,8 +4,8 @@ * Updated `http.request.method` to match specification guidelines. * For activity, request method will be set on an additional tag - `http.request.method.original` will be added if the method does not belong - to one of the [known + `http.request.method.original` 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) and `http.request.method` will be set to `_OTHER`. * For metrics, `http.request.method` on `http.server.request.duration` metric From bf22886f7045fe7f72e15d5b65699bf6cd8a2dec Mon Sep 17 00:00:00 2001 From: Vishwesh Bankwar Date: Mon, 30 Oct 2023 15:20:15 -0700 Subject: [PATCH 06/12] refactor --- OpenTelemetry.sln | 1 + .../Implementation/HttpInListener.cs | 2 +- .../Implementation/HttpInMetricsListener.cs | 4 +- .../Implementation/TelemetryHelper.cs | 51 ------------ ...elemetry.Instrumentation.AspNetCore.csproj | 1 + src/Shared/RequestMethodHelper.cs | 78 +++++++++++++++++++ .../BasicTests.cs | 3 +- .../MetricTests.cs | 3 +- 8 files changed, 88 insertions(+), 55 deletions(-) create mode 100644 src/Shared/RequestMethodHelper.cs diff --git a/OpenTelemetry.sln b/OpenTelemetry.sln index 922ba6b2d20..e4908525cef 100644 --- a/OpenTelemetry.sln +++ b/OpenTelemetry.sln @@ -269,6 +269,7 @@ 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\ResourceSemanticConventions.cs = src\Shared\ResourceSemanticConventions.cs + src\Shared\RequestMethodHelper.cs = src\Shared\RequestMethodHelper.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 diff --git a/src/OpenTelemetry.Instrumentation.AspNetCore/Implementation/HttpInListener.cs b/src/OpenTelemetry.Instrumentation.AspNetCore/Implementation/HttpInListener.cs index 21e348b76a8..b074fb5c254 100644 --- a/src/OpenTelemetry.Instrumentation.AspNetCore/Implementation/HttpInListener.cs +++ b/src/OpenTelemetry.Instrumentation.AspNetCore/Implementation/HttpInListener.cs @@ -252,7 +252,7 @@ public void OnStartActivity(Activity activity, object payload) activity.SetTag(SemanticConventions.AttributeUrlQuery, request.QueryString.Value); } - if (TelemetryHelper.TryResolveHttpMethod(request.Method, out var httpMethod)) + if (RequestMethodHelper.TryResolveHttpMethod(request.Method, out var httpMethod)) { activity.SetTag(SemanticConventions.AttributeHttpRequestMethod, httpMethod); } diff --git a/src/OpenTelemetry.Instrumentation.AspNetCore/Implementation/HttpInMetricsListener.cs b/src/OpenTelemetry.Instrumentation.AspNetCore/Implementation/HttpInMetricsListener.cs index 210e0de6745..987fac68e57 100644 --- a/src/OpenTelemetry.Instrumentation.AspNetCore/Implementation/HttpInMetricsListener.cs +++ b/src/OpenTelemetry.Instrumentation.AspNetCore/Implementation/HttpInMetricsListener.cs @@ -18,6 +18,8 @@ using System.Diagnostics; using System.Diagnostics.Metrics; using Microsoft.AspNetCore.Http; +using OpenTelemetry.Internal; + #if NET6_0_OR_GREATER using Microsoft.AspNetCore.Routing; #endif @@ -151,7 +153,7 @@ public void OnEventWritten_New(string name, object payload) 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))); - TelemetryHelper.TryResolveHttpMethod(context.Request.Method, out var httpMethod); + RequestMethodHelper.TryResolveHttpMethod(context.Request.Method, out var httpMethod); tags.Add(new KeyValuePair(SemanticConventions.AttributeHttpRequestMethod, httpMethod)); #if NET6_0_OR_GREATER diff --git a/src/OpenTelemetry.Instrumentation.AspNetCore/Implementation/TelemetryHelper.cs b/src/OpenTelemetry.Instrumentation.AspNetCore/Implementation/TelemetryHelper.cs index 351345eda43..73d84269e82 100644 --- a/src/OpenTelemetry.Instrumentation.AspNetCore/Implementation/TelemetryHelper.cs +++ b/src/OpenTelemetry.Instrumentation.AspNetCore/Implementation/TelemetryHelper.cs @@ -14,48 +14,12 @@ // limitations under the License. // -using Microsoft.AspNetCore.Http; -#if NET8_0_OR_GREATER -using System.Collections.Frozen; -#endif - namespace OpenTelemetry.Instrumentation.AspNetCore.Implementation; internal static class TelemetryHelper { public static readonly object[] BoxedStatusCodes; -#if NET8_0_OR_GREATER - internal static readonly FrozenDictionary KnownMethods = FrozenDictionary.ToFrozenDictionary( - new[] - { - KeyValuePair.Create(HttpMethods.Connect, HttpMethods.Connect), - KeyValuePair.Create(HttpMethods.Delete, HttpMethods.Delete), - KeyValuePair.Create(HttpMethods.Get, HttpMethods.Get), - KeyValuePair.Create(HttpMethods.Head, HttpMethods.Head), - KeyValuePair.Create(HttpMethods.Options, HttpMethods.Options), - KeyValuePair.Create(HttpMethods.Patch, HttpMethods.Patch), - KeyValuePair.Create(HttpMethods.Post, HttpMethods.Post), - KeyValuePair.Create(HttpMethods.Put, HttpMethods.Put), - KeyValuePair.Create(HttpMethods.Trace, HttpMethods.Trace), - }, - StringComparer.OrdinalIgnoreCase); -#else - internal static readonly Dictionary KnownMethods = new Dictionary(StringComparer.OrdinalIgnoreCase) - { - { HttpMethods.Connect, HttpMethods.Connect }, - { HttpMethods.Delete, HttpMethods.Delete }, - { HttpMethods.Get, HttpMethods.Get }, - { HttpMethods.Head, HttpMethods.Head }, - { HttpMethods.Options, HttpMethods.Options }, - { HttpMethods.Patch, HttpMethods.Patch }, - { HttpMethods.Post, HttpMethods.Post }, - { HttpMethods.Put, HttpMethods.Put }, - { HttpMethods.Trace, HttpMethods.Trace }, - }; - -#endif - static TelemetryHelper() { BoxedStatusCodes = new object[500]; @@ -74,19 +38,4 @@ public static object GetBoxedStatusCode(int statusCode) return statusCode; } - - public static bool TryResolveHttpMethod(string method, out string resolvedMethod) - { - if (KnownMethods.TryGetValue(method, out var result)) - { - // KnownMethods ignores case. Use the value returned by the dictionary to have a consistent case. - resolvedMethod = result; - return true; - } - - // Set to default "_OTHER" as per spec. - // https://github.com/open-telemetry/semantic-conventions/blob/v1.22.0/docs/http/http-spans.md#common-attributes - resolvedMethod = "_OTHER"; - return false; - } } diff --git a/src/OpenTelemetry.Instrumentation.AspNetCore/OpenTelemetry.Instrumentation.AspNetCore.csproj b/src/OpenTelemetry.Instrumentation.AspNetCore/OpenTelemetry.Instrumentation.AspNetCore.csproj index 840652e39c4..19c3a53f73b 100644 --- a/src/OpenTelemetry.Instrumentation.AspNetCore/OpenTelemetry.Instrumentation.AspNetCore.csproj +++ b/src/OpenTelemetry.Instrumentation.AspNetCore/OpenTelemetry.Instrumentation.AspNetCore.csproj @@ -14,6 +14,7 @@ + diff --git a/src/Shared/RequestMethodHelper.cs b/src/Shared/RequestMethodHelper.cs new file mode 100644 index 00000000000..b04954ab5cd --- /dev/null +++ b/src/Shared/RequestMethodHelper.cs @@ -0,0 +1,78 @@ +// +// Copyright The OpenTelemetry Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +#if NET8_0_OR_GREATER +using System.Collections.Frozen; +#endif + +namespace OpenTelemetry.Internal; + +internal static class RequestMethodHelper +{ +#if NET8_0_OR_GREATER + internal static readonly FrozenDictionary KnownMethods; +#else + internal static readonly Dictionary KnownMethods; +#endif + + static RequestMethodHelper() + { +#if NET8_0_OR_GREATER + KnownMethods = FrozenDictionary.ToFrozenDictionary( + new[] + { + KeyValuePair.Create("GET", "GET"), + KeyValuePair.Create("PUT", "PUT"), + KeyValuePair.Create("POST", "POST"), + KeyValuePair.Create("DELETE", "DELETE"), + KeyValuePair.Create("HEAD", "HEAD"), + KeyValuePair.Create("OPTIONS", "OPTIONS"), + KeyValuePair.Create("TRACE", "TRACE"), + KeyValuePair.Create("PATCH", "PATCH"), + KeyValuePair.Create("CONNECT", "CONNECT"), + }, + StringComparer.OrdinalIgnoreCase); +#else + KnownMethods = new Dictionary(StringComparer.OrdinalIgnoreCase) + { + { "GET", "GET" }, + { "PUT", "PUT" }, + { "POST", "POST" }, + { "DELETE", "DELETE" }, + { "HEAD", "HEAD" }, + { "OPTIONS", "OPTIONS" }, + { "TRACE", "TRACE" }, + { "PATCH", "PATCH" }, + { "CONNECT", "CONNECT" }, + }; +#endif + } + + public static bool TryResolveHttpMethod(string method, out string resolvedMethod) + { + if (KnownMethods.TryGetValue(method, out var result)) + { + // KnownMethods ignores case. Use the value returned by the dictionary to have a consistent case. + resolvedMethod = result; + return true; + } + + // Set to default "_OTHER" as per spec. + // https://github.com/open-telemetry/semantic-conventions/blob/v1.22.0/docs/http/http-spans.md#common-attributes + resolvedMethod = "_OTHER"; + return false; + } +} diff --git a/test/OpenTelemetry.Instrumentation.AspNetCore.Tests/BasicTests.cs b/test/OpenTelemetry.Instrumentation.AspNetCore.Tests/BasicTests.cs index 322eae5a15b..f7f60085d98 100644 --- a/test/OpenTelemetry.Instrumentation.AspNetCore.Tests/BasicTests.cs +++ b/test/OpenTelemetry.Instrumentation.AspNetCore.Tests/BasicTests.cs @@ -27,6 +27,7 @@ using Moq; using OpenTelemetry.Context.Propagation; using OpenTelemetry.Instrumentation.AspNetCore.Implementation; +using OpenTelemetry.Internal; using OpenTelemetry.Tests; using OpenTelemetry.Trace; using TestApp.AspNetCore; @@ -710,7 +711,7 @@ void ConfigureTestServices(IServiceCollection services) Assert.Contains(activity.TagObjects, t => t.Key == SemanticConventions.AttributeHttpRequestMethod); - if (TelemetryHelper.KnownMethods.TryGetValue(method, out var val)) + if (RequestMethodHelper.KnownMethods.TryGetValue(method, out var val)) { Assert.Equal(val, activity.GetTagValue(SemanticConventions.AttributeHttpRequestMethod) as string); Assert.DoesNotContain(activity.TagObjects, t => t.Key == SemanticConventions.AttributeHttpRequestMethodOriginal); diff --git a/test/OpenTelemetry.Instrumentation.AspNetCore.Tests/MetricTests.cs b/test/OpenTelemetry.Instrumentation.AspNetCore.Tests/MetricTests.cs index 605a0792593..ec7dc47b4fb 100644 --- a/test/OpenTelemetry.Instrumentation.AspNetCore.Tests/MetricTests.cs +++ b/test/OpenTelemetry.Instrumentation.AspNetCore.Tests/MetricTests.cs @@ -30,6 +30,7 @@ using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Logging; using OpenTelemetry.Instrumentation.AspNetCore.Implementation; +using OpenTelemetry.Internal; using OpenTelemetry.Metrics; using OpenTelemetry.Trace; using Xunit; @@ -309,7 +310,7 @@ public async Task HttpRequestMethodIsCapturedAsPerSpec(string method) attributes[tag.Key] = tag.Value; } - if (TelemetryHelper.KnownMethods.TryGetValue(method, out var val)) + if (RequestMethodHelper.KnownMethods.TryGetValue(method, out var val)) { Assert.Contains(attributes, kvp => kvp.Key == SemanticConventions.AttributeHttpRequestMethod && kvp.Value.ToString() == method.ToUpper()); } From b1f168cf7f019dba7742fd2ef41e7d69ac63c475 Mon Sep 17 00:00:00 2001 From: Vishwesh Bankwar Date: Mon, 30 Oct 2023 15:21:04 -0700 Subject: [PATCH 07/12] rmv using --- .../MetricTests.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/test/OpenTelemetry.Instrumentation.AspNetCore.Tests/MetricTests.cs b/test/OpenTelemetry.Instrumentation.AspNetCore.Tests/MetricTests.cs index ec7dc47b4fb..5c53e864583 100644 --- a/test/OpenTelemetry.Instrumentation.AspNetCore.Tests/MetricTests.cs +++ b/test/OpenTelemetry.Instrumentation.AspNetCore.Tests/MetricTests.cs @@ -29,7 +29,6 @@ using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Logging; -using OpenTelemetry.Instrumentation.AspNetCore.Implementation; using OpenTelemetry.Internal; using OpenTelemetry.Metrics; using OpenTelemetry.Trace; From 2cd0715cbbbe8d753f6c06ee9e8c364e3ae58da3 Mon Sep 17 00:00:00 2001 From: Vishwesh Bankwar Date: Mon, 30 Oct 2023 15:22:54 -0700 Subject: [PATCH 08/12] init --- .../MeterProviderBuilderExtensions.cs | 3 ++- .../TracerProviderBuilderExtensions.cs | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/src/OpenTelemetry.Instrumentation.AspNetCore/MeterProviderBuilderExtensions.cs b/src/OpenTelemetry.Instrumentation.AspNetCore/MeterProviderBuilderExtensions.cs index 71f00d1b5bf..6118642b2ed 100644 --- a/src/OpenTelemetry.Instrumentation.AspNetCore/MeterProviderBuilderExtensions.cs +++ b/src/OpenTelemetry.Instrumentation.AspNetCore/MeterProviderBuilderExtensions.cs @@ -42,8 +42,9 @@ public static MeterProviderBuilder AddAspNetCoreInstrumentation( #if NET8_0_OR_GREATER return builder.ConfigureMeters(); #else - // Note: Warm-up the status code mapping. + // Note: Warm-up the status code and method mapping. _ = TelemetryHelper.BoxedStatusCodes; + _ = RequestMethodHelper.KnownMethods; builder.ConfigureServices(services => { diff --git a/src/OpenTelemetry.Instrumentation.AspNetCore/TracerProviderBuilderExtensions.cs b/src/OpenTelemetry.Instrumentation.AspNetCore/TracerProviderBuilderExtensions.cs index dae87bf4991..b4749b699f7 100644 --- a/src/OpenTelemetry.Instrumentation.AspNetCore/TracerProviderBuilderExtensions.cs +++ b/src/OpenTelemetry.Instrumentation.AspNetCore/TracerProviderBuilderExtensions.cs @@ -63,8 +63,9 @@ public static TracerProviderBuilder AddAspNetCoreInstrumentation( { Guard.ThrowIfNull(builder); - // Note: Warm-up the status code mapping. + // Note: Warm-up the status code and method mapping. _ = TelemetryHelper.BoxedStatusCodes; + _ = RequestMethodHelper.KnownMethods; name ??= Options.DefaultName; From a387d1eb90b72885b77e820c3d9586c17fee4b81 Mon Sep 17 00:00:00 2001 From: Vishwesh Bankwar Date: Mon, 30 Oct 2023 16:30:21 -0700 Subject: [PATCH 09/12] reword --- .../CHANGELOG.md | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/OpenTelemetry.Instrumentation.AspNetCore/CHANGELOG.md b/src/OpenTelemetry.Instrumentation.AspNetCore/CHANGELOG.md index f18a5497c9c..24c14b260bd 100644 --- a/src/OpenTelemetry.Instrumentation.AspNetCore/CHANGELOG.md +++ b/src/OpenTelemetry.Instrumentation.AspNetCore/CHANGELOG.md @@ -3,11 +3,11 @@ ## Unreleased * Updated `http.request.method` to match specification guidelines. - * For activity, request method will be set on an additional tag - `http.request.method.original` 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) - and `http.request.method` will be set to `_OTHER`. + * 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, `http.request.method` on `http.server.request.duration` metric will be set to `_OTHER` if the original method does not belong to one of the [known From fe96c9f1ac68a47ae7570eec1a1862a2461a4080 Mon Sep 17 00:00:00 2001 From: Vishwesh Bankwar Date: Mon, 30 Oct 2023 16:48:10 -0700 Subject: [PATCH 10/12] reword --- src/OpenTelemetry.Instrumentation.AspNetCore/CHANGELOG.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/OpenTelemetry.Instrumentation.AspNetCore/CHANGELOG.md b/src/OpenTelemetry.Instrumentation.AspNetCore/CHANGELOG.md index 24c14b260bd..e44043d3b56 100644 --- a/src/OpenTelemetry.Instrumentation.AspNetCore/CHANGELOG.md +++ b/src/OpenTelemetry.Instrumentation.AspNetCore/CHANGELOG.md @@ -8,10 +8,10 @@ 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, `http.request.method` on `http.server.request.duration` metric - will be set to `_OTHER` 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) + * 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 From 6feef4b9a9295c8f2d862bea72396a38533b9538 Mon Sep 17 00:00:00 2001 From: Vishwesh Bankwar Date: Mon, 30 Oct 2023 16:51:24 -0700 Subject: [PATCH 11/12] fix space --- .../CHANGELOG.md | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/OpenTelemetry.Instrumentation.AspNetCore/CHANGELOG.md b/src/OpenTelemetry.Instrumentation.AspNetCore/CHANGELOG.md index e44043d3b56..72f9147b46e 100644 --- a/src/OpenTelemetry.Instrumentation.AspNetCore/CHANGELOG.md +++ b/src/OpenTelemetry.Instrumentation.AspNetCore/CHANGELOG.md @@ -4,14 +4,14 @@ * 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 + 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` + 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 From c4f79da6e197cd05fa077d78265507944792d4ae Mon Sep 17 00:00:00 2001 From: Vishwesh Bankwar Date: Tue, 31 Oct 2023 08:45:52 -0700 Subject: [PATCH 12/12] address feedback --- src/Shared/RequestMethodHelper.cs | 3 +- .../BasicTests.cs | 35 +++++++++--------- .../MetricTests.cs | 36 ++++++++----------- 3 files changed, 32 insertions(+), 42 deletions(-) diff --git a/src/Shared/RequestMethodHelper.cs b/src/Shared/RequestMethodHelper.cs index b04954ab5cd..b4404bde770 100644 --- a/src/Shared/RequestMethodHelper.cs +++ b/src/Shared/RequestMethodHelper.cs @@ -63,10 +63,9 @@ static RequestMethodHelper() public static bool TryResolveHttpMethod(string method, out string resolvedMethod) { - if (KnownMethods.TryGetValue(method, out var result)) + if (KnownMethods.TryGetValue(method, out resolvedMethod)) { // KnownMethods ignores case. Use the value returned by the dictionary to have a consistent case. - resolvedMethod = result; return true; } diff --git a/test/OpenTelemetry.Instrumentation.AspNetCore.Tests/BasicTests.cs b/test/OpenTelemetry.Instrumentation.AspNetCore.Tests/BasicTests.cs index f7f60085d98..93ef8753772 100644 --- a/test/OpenTelemetry.Instrumentation.AspNetCore.Tests/BasicTests.cs +++ b/test/OpenTelemetry.Instrumentation.AspNetCore.Tests/BasicTests.cs @@ -27,7 +27,6 @@ using Moq; using OpenTelemetry.Context.Propagation; using OpenTelemetry.Instrumentation.AspNetCore.Implementation; -using OpenTelemetry.Internal; using OpenTelemetry.Tests; using OpenTelemetry.Trace; using TestApp.AspNetCore; @@ -652,18 +651,18 @@ void ConfigureTestServices(IServiceCollection services) } [Theory] - [InlineData("CONNECT")] - [InlineData("DELETE")] - [InlineData("GET")] - [InlineData("PUT")] - [InlineData("HEAD")] - [InlineData("OPTIONS")] - [InlineData("PATCH")] - [InlineData("Get")] - [InlineData("POST")] - [InlineData("TRACE")] - [InlineData("CUSTOM")] - public async Task HttpRequestMethodIsSetAsPerSpec(string method) + [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 HttpRequestMethodIsSetAsPerSpec(string originalMethod, string expectedMethod) { var exportedItems = new List(); @@ -691,7 +690,7 @@ void ConfigureTestServices(IServiceCollection services) var message = new HttpRequestMessage(); - message.Method = new HttpMethod(method); + message.Method = new HttpMethod(originalMethod); try { @@ -711,16 +710,16 @@ void ConfigureTestServices(IServiceCollection services) Assert.Contains(activity.TagObjects, t => t.Key == SemanticConventions.AttributeHttpRequestMethod); - if (RequestMethodHelper.KnownMethods.TryGetValue(method, out var val)) + if (originalMethod.Equals(expectedMethod, StringComparison.OrdinalIgnoreCase)) { - Assert.Equal(val, activity.GetTagValue(SemanticConventions.AttributeHttpRequestMethod) as string); Assert.DoesNotContain(activity.TagObjects, t => t.Key == SemanticConventions.AttributeHttpRequestMethodOriginal); } else { - Assert.Equal("_OTHER", activity.GetTagValue(SemanticConventions.AttributeHttpRequestMethod) as string); - Assert.Equal(method, activity.GetTagValue(SemanticConventions.AttributeHttpRequestMethodOriginal) as string); + Assert.Equal(originalMethod, activity.GetTagValue(SemanticConventions.AttributeHttpRequestMethodOriginal) as string); } + + Assert.Equal(expectedMethod, activity.GetTagValue(SemanticConventions.AttributeHttpRequestMethod) as string); } [Fact] diff --git a/test/OpenTelemetry.Instrumentation.AspNetCore.Tests/MetricTests.cs b/test/OpenTelemetry.Instrumentation.AspNetCore.Tests/MetricTests.cs index 5c53e864583..d6f72df7c62 100644 --- a/test/OpenTelemetry.Instrumentation.AspNetCore.Tests/MetricTests.cs +++ b/test/OpenTelemetry.Instrumentation.AspNetCore.Tests/MetricTests.cs @@ -29,7 +29,6 @@ using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Logging; -using OpenTelemetry.Internal; using OpenTelemetry.Metrics; using OpenTelemetry.Trace; using Xunit; @@ -239,18 +238,18 @@ public async Task RequestMetricIsCaptured_New() } [Theory] - [InlineData("CONNECT")] - [InlineData("DELETE")] - [InlineData("GET")] - [InlineData("GeT")] - [InlineData("PUT")] - [InlineData("HEAD")] - [InlineData("OPTIONS")] - [InlineData("PATCH")] - [InlineData("POST")] - [InlineData("TRACE")] - [InlineData("CUSTOM")] - public async Task HttpRequestMethodIsCapturedAsPerSpec(string method) + [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 configuration = new ConfigurationBuilder() .AddInMemoryCollection(new Dictionary { [SemanticConventionOptInKeyName] = "http" }) @@ -272,7 +271,7 @@ public async Task HttpRequestMethodIsCapturedAsPerSpec(string method) .CreateClient(); var message = new HttpRequestMessage(); - message.Method = new HttpMethod(method); + message.Method = new HttpMethod(originalMethod); try { @@ -309,14 +308,7 @@ public async Task HttpRequestMethodIsCapturedAsPerSpec(string method) attributes[tag.Key] = tag.Value; } - if (RequestMethodHelper.KnownMethods.TryGetValue(method, out var val)) - { - Assert.Contains(attributes, kvp => kvp.Key == SemanticConventions.AttributeHttpRequestMethod && kvp.Value.ToString() == method.ToUpper()); - } - else - { - Assert.Contains(attributes, kvp => kvp.Key == SemanticConventions.AttributeHttpRequestMethod && kvp.Value.ToString() == "_OTHER"); - } + Assert.Contains(attributes, kvp => kvp.Key == SemanticConventions.AttributeHttpRequestMethod && kvp.Value.ToString() == expectedMethod); Assert.DoesNotContain(attributes, t => t.Key == SemanticConventions.AttributeHttpRequestMethodOriginal); }