Skip to content

Commit dfdbf01

Browse files
authored
[Instrumentation.AspNetCore, Instrumentation.Http] Fix http.request.method_original attribute (#5471)
1 parent af57de2 commit dfdbf01

File tree

5 files changed

+66
-49
lines changed

5 files changed

+66
-49
lines changed

src/OpenTelemetry.Instrumentation.AspNetCore/CHANGELOG.md

+6
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,12 @@
77
`443` for `HTTPS` protocol).
88
([#5419](https://github.com/open-telemetry/opentelemetry-dotnet/pull/5419))
99

10+
* Fixed an issue where the `http.request.method_original` attribute was not set
11+
on activity. Now, when `http.request.method` is set and the original method
12+
is converted to its canonical form (e.g., `Get` is converted to `GET`),
13+
the original value `Get` will be stored in `http.request.method_original`.
14+
([#5471](https://github.com/open-telemetry/opentelemetry-dotnet/pull/5471))
15+
1016
## 1.7.1
1117

1218
Released 2024-Feb-09

src/OpenTelemetry.Instrumentation.Http/CHANGELOG.md

+9
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,15 @@
77
`443` for `HTTPS` protocol).
88
([#5419](https://github.com/open-telemetry/opentelemetry-dotnet/pull/5419))
99

10+
* Fixed an issue where the `http.request.method_original` attribute was not set
11+
on activity. Now, when `http.request.method` is set and the original method
12+
is converted to its canonical form (e.g., `Get` is converted to `GET`),
13+
the original value `Get` will be stored in `http.request.method_original`.
14+
The attribute is not set on .NET Framework for non canonical form of `CONNECT`,
15+
`GET`, `HEAD`, `PUT`, and `POST`. HTTP Client is converting these values
16+
to canonical form.
17+
([#5471](https://github.com/open-telemetry/opentelemetry-dotnet/pull/5471))
18+
1019
## 1.7.1
1120

1221
Released 2024-Feb-09

src/Shared/RequestMethodHelper.cs

+6-8
Original file line numberDiff line numberDiff line change
@@ -51,16 +51,14 @@ public static string GetNormalizedHttpMethod(string method)
5151
: OtherHttpMethod;
5252
}
5353

54-
public static void SetHttpMethodTag(Activity activity, string method)
54+
public static void SetHttpMethodTag(Activity activity, string originalHttpMethod)
5555
{
56-
if (KnownMethods.TryGetValue(method, out var normalizedMethod))
57-
{
58-
activity?.SetTag(SemanticConventions.AttributeHttpRequestMethod, normalizedMethod);
59-
}
60-
else
56+
var normalizedHttpMethod = GetNormalizedHttpMethod(originalHttpMethod);
57+
activity.SetTag(SemanticConventions.AttributeHttpRequestMethod, normalizedHttpMethod);
58+
59+
if (originalHttpMethod != normalizedHttpMethod)
6160
{
62-
activity?.SetTag(SemanticConventions.AttributeHttpRequestMethod, OtherHttpMethod);
63-
activity?.SetTag(SemanticConventions.AttributeHttpRequestMethodOriginal, method);
61+
activity.SetTag(SemanticConventions.AttributeHttpRequestMethodOriginal, originalHttpMethod);
6462
}
6563
}
6664

test/OpenTelemetry.Instrumentation.AspNetCore.Tests/BasicTests.cs

+14-24
Original file line numberDiff line numberDiff line change
@@ -629,18 +629,18 @@ void ConfigureTestServices(IServiceCollection services)
629629
}
630630

631631
[Theory]
632-
[InlineData("CONNECT", "CONNECT")]
633-
[InlineData("DELETE", "DELETE")]
634-
[InlineData("GET", "GET")]
635-
[InlineData("PUT", "PUT")]
636-
[InlineData("HEAD", "HEAD")]
637-
[InlineData("OPTIONS", "OPTIONS")]
638-
[InlineData("PATCH", "PATCH")]
639-
[InlineData("Get", "GET")]
640-
[InlineData("POST", "POST")]
641-
[InlineData("TRACE", "TRACE")]
642-
[InlineData("CUSTOM", "_OTHER")]
643-
public async Task HttpRequestMethodIsSetAsPerSpec(string originalMethod, string expectedMethod)
632+
[InlineData("CONNECT", "CONNECT", null)]
633+
[InlineData("DELETE", "DELETE", null)]
634+
[InlineData("GET", "GET", null)]
635+
[InlineData("PUT", "PUT", null)]
636+
[InlineData("HEAD", "HEAD", null)]
637+
[InlineData("OPTIONS", "OPTIONS", null)]
638+
[InlineData("PATCH", "PATCH", null)]
639+
[InlineData("Get", "GET", "Get")]
640+
[InlineData("POST", "POST", null)]
641+
[InlineData("TRACE", "TRACE", null)]
642+
[InlineData("CUSTOM", "_OTHER", "CUSTOM")]
643+
public async Task HttpRequestMethodIsSetAsPerSpec(string originalMethod, string expectedMethod, string expectedOriginalMethod)
644644
{
645645
var exportedItems = new List<Activity>();
646646

@@ -681,18 +681,8 @@ void ConfigureTestServices(IServiceCollection services)
681681

682682
var activity = exportedItems[0];
683683

684-
Assert.Contains(activity.TagObjects, t => t.Key == SemanticConventions.AttributeHttpRequestMethod);
685-
686-
if (originalMethod.Equals(expectedMethod, StringComparison.OrdinalIgnoreCase))
687-
{
688-
Assert.DoesNotContain(activity.TagObjects, t => t.Key == SemanticConventions.AttributeHttpRequestMethodOriginal);
689-
}
690-
else
691-
{
692-
Assert.Equal(originalMethod, activity.GetTagValue(SemanticConventions.AttributeHttpRequestMethodOriginal) as string);
693-
}
694-
695-
Assert.Equal(expectedMethod, activity.GetTagValue(SemanticConventions.AttributeHttpRequestMethod) as string);
684+
Assert.Equal(expectedMethod, activity.GetTagValue(SemanticConventions.AttributeHttpRequestMethod));
685+
Assert.Equal(expectedOriginalMethod, activity.GetTagValue(SemanticConventions.AttributeHttpRequestMethodOriginal));
696686
}
697687

698688
[Fact]

test/OpenTelemetry.Instrumentation.Http.Tests/HttpClientTests.Basic.cs

+31-17
Original file line numberDiff line numberDiff line change
@@ -356,18 +356,34 @@ public async Task ExportsSpansCreatedForRetries()
356356
}
357357

358358
[Theory]
359-
[InlineData("CONNECT", "CONNECT")]
360-
[InlineData("DELETE", "DELETE")]
361-
[InlineData("GET", "GET")]
362-
[InlineData("PUT", "PUT")]
363-
[InlineData("HEAD", "HEAD")]
364-
[InlineData("OPTIONS", "OPTIONS")]
365-
[InlineData("PATCH", "PATCH")]
366-
[InlineData("Get", "GET")]
367-
[InlineData("POST", "POST")]
368-
[InlineData("TRACE", "TRACE")]
369-
[InlineData("CUSTOM", "_OTHER")]
370-
public async Task HttpRequestMethodIsSetOnActivityAsPerSpec(string originalMethod, string expectedMethod)
359+
[InlineData("CONNECT", "CONNECT", null)]
360+
[InlineData("DELETE", "DELETE", null)]
361+
[InlineData("GET", "GET", null)]
362+
[InlineData("PUT", "PUT", null)]
363+
[InlineData("HEAD", "HEAD", null)]
364+
[InlineData("OPTIONS", "OPTIONS", null)]
365+
[InlineData("PATCH", "PATCH", null)]
366+
[InlineData("POST", "POST", null)]
367+
[InlineData("TRACE", "TRACE", null)]
368+
[InlineData("Delete", "DELETE", "Delete")]
369+
#if NETFRAMEWORK
370+
[InlineData("Connect", "CONNECT", null)]// HTTP Client converts Connect to its canonical form (Connect). Expected original method is null.
371+
[InlineData("Get", "GET", null)] // HTTP Client converts Get to its canonical form (GET). Expected original method is null.
372+
[InlineData("Put", "PUT", null)] // HTTP Client converts Put to its canonical form (PUT). Expected original method is null.
373+
[InlineData("Head", "HEAD", null)] // HTTP Client converts Head to its canonical form (HEAD). Expected original method is null.
374+
[InlineData("Post", "POST", null)] // HTTP Client converts Post to its canonical form (POST). Expected original method is null.
375+
#else
376+
[InlineData("Connect", "CONNECT", "Connect")]
377+
[InlineData("Get", "GET", "Get")]
378+
[InlineData("Put", "PUT", "Put")]
379+
[InlineData("Head", "HEAD", "Head")]
380+
[InlineData("Post", "POST", "Post")]
381+
#endif
382+
[InlineData("Options", "OPTIONS", "Options")]
383+
[InlineData("Patch", "PATCH", "Patch")]
384+
[InlineData("Trace", "TRACE", "Trace")]
385+
[InlineData("CUSTOM", "_OTHER", "CUSTOM")]
386+
public async Task HttpRequestMethodIsSetOnActivityAsPerSpec(string originalMethod, string expectedMethod, string expectedOriginalMethod)
371387
{
372388
var exportedItems = new List<Activity>();
373389
using var request = new HttpRequestMessage
@@ -396,20 +412,17 @@ public async Task HttpRequestMethodIsSetOnActivityAsPerSpec(string originalMetho
396412

397413
var activity = exportedItems[0];
398414

399-
Assert.Contains(activity.TagObjects, t => t.Key == SemanticConventions.AttributeHttpRequestMethod);
400-
401415
if (originalMethod.Equals(expectedMethod, StringComparison.OrdinalIgnoreCase))
402416
{
403417
Assert.Equal(expectedMethod, activity.DisplayName);
404-
Assert.DoesNotContain(activity.TagObjects, t => t.Key == SemanticConventions.AttributeHttpRequestMethodOriginal);
405418
}
406419
else
407420
{
408421
Assert.Equal("HTTP", activity.DisplayName);
409-
Assert.Equal(originalMethod, activity.GetTagValue(SemanticConventions.AttributeHttpRequestMethodOriginal) as string);
410422
}
411423

412-
Assert.Equal(expectedMethod, activity.GetTagValue(SemanticConventions.AttributeHttpRequestMethod) as string);
424+
Assert.Equal(expectedMethod, activity.GetTagValue(SemanticConventions.AttributeHttpRequestMethod));
425+
Assert.Equal(expectedOriginalMethod, activity.GetTagValue(SemanticConventions.AttributeHttpRequestMethodOriginal));
413426
}
414427

415428
[Theory]
@@ -423,6 +436,7 @@ public async Task HttpRequestMethodIsSetOnActivityAsPerSpec(string originalMetho
423436
[InlineData("Get", "GET")]
424437
[InlineData("POST", "POST")]
425438
[InlineData("TRACE", "TRACE")]
439+
[InlineData("Trace", "TRACE")]
426440
[InlineData("CUSTOM", "_OTHER")]
427441
public async Task HttpRequestMethodIsSetonRequestDurationMetricAsPerSpec(string originalMethod, string expectedMethod)
428442
{

0 commit comments

Comments
 (0)