Skip to content

NET9 HttpClient isn't following the OTel spec for some attributesΒ #109847

@TimothyMothra

Description

@TimothyMothra

Description

This is a follow up to #104251.
I'm working on the OpenTelemetry.Instrumentation.Http library, which should not overwrite attributes set by NET9's runtime: open-telemetry/opentelemetry-dotnet-contrib#2314

I found two examples where runtime isn't following the OTel spec:

  1. URL Fragement Identifier
    OTel Spec (link): "[4]: For network calls, URL usually has scheme://host[:port][path][?query][#fragment] format, where the fragment is not transmitted over HTTP, but if it is known, it SHOULD be included nevertheless."

    Runtime is NOT recording the Fragment in url.full.

  2. HTTP Method
    OTel Spec (link): "[1]: HTTP method names are case-sensitive and http.request.method attribute value MUST match a known HTTP method name exactly. Instrumentations for specific web frameworks that consider HTTP methods to be case insensitive, SHOULD populate a canonical equivalent. Tracing instrumentations that do so, MUST also set http.request.method_original to the original value."

    Runtime is treating the HTTP Method as case-insensitive and not setting http.request.method_original (ie: "Get" vs "GET")

Reproduction Steps

CSPROJ

<Project Sdk="Microsoft.NET.Sdk">

  <PropertyGroup>
    <OutputType>Exe</OutputType>
    <TargetFramework>net9.0</TargetFramework>
    <ImplicitUsings>enable</ImplicitUsings>
    <Nullable>enable</Nullable>
  </PropertyGroup>

  <ItemGroup>
    <PackageReference Include="OpenTelemetry" Version="1.10.0" />
    <PackageReference Include="OpenTelemetry.Exporter.Console" Version="1.10.0" />
    <PackageReference Include="OpenTelemetry.Instrumentation.Http" Version="1.9.0" />
  </ItemGroup>

</Project>

PROGRAM.CS

namespace ConsoleApp4
{
    using OpenTelemetry;
    using OpenTelemetry.Trace;

    internal class Program
    {
        static async Task Main(string[] args)
        {
            using var tracerProvider = Sdk.CreateTracerProviderBuilder()
                .AddSource("System.Net.Http")       // Use this to test native NET9 instrumentation
                //.AddHttpClientInstrumentation()   // Use this to test OpenTelemetry.Instrumentation.Http
                .AddConsoleExporter()
                .Build();

            using var client = new HttpClient();
            var requestMessage = new HttpRequestMessage(new HttpMethod("Get"), "https://en.wikipedia.org/wiki/.NET#History");
            var response = await client.SendAsync(requestMessage);

            Console.WriteLine($"Response status code: {response.StatusCode}");

            tracerProvider.Dispose();
        }
    }
}

Expected behavior

Using the OpenTelemetry.Instrumentation.Http instrumentation:

Activity.TraceId:            eefbe518c63a5e46a2ce0ce7a5174f8f
Activity.SpanId:             e3c2ddba04b97629
Activity.TraceFlags:         Recorded
Activity.DisplayName:        GET
Activity.Kind:               Client
Activity.StartTime:          2024-11-14T21:08:44.4819233Z
Activity.Duration:           00:00:00.2718997
Activity.Tags:
    http.request.method: GET          <-- Shows 2, Recording the standard "GET" (all caps)
    server.address: en.wikipedia.org
    server.port: 443
    url.full: https://en.wikipedia.org/wiki/.NET#History          <-- Shows 1, Recording the "#History" fragment
    http.request.method_original: Get          <-- Shows 2, Recording the original "Get"
    http.response.status_code: 200
    network.protocol.version: 1.1
Instrumentation scope (ActivitySource):
    Name: System.Net.Http
Resource associated with Activity:
    telemetry.sdk.name: opentelemetry
    telemetry.sdk.language: dotnet
    telemetry.sdk.version: 1.10.0
    service.name: unknown_service:ConsoleApp4

Response status code: OK

Actual behavior

Using the NET9 native instrumentation:

Note that "http.request.method_original: Get" is missing.

Activity.TraceId:            46cd716242195da4f72318d6a5eb20f9
Activity.SpanId:             cf84fa8a9856eb57
Activity.TraceFlags:         Recorded
Activity.DisplayName:        GET
Activity.Kind:               Client
Activity.StartTime:          2024-11-14T21:10:49.3686726Z
Activity.Duration:           00:00:00.4011271
Activity.Tags:
    http.request.method: GET          <-- Shows 2, Recording the standard "GET". This is correct.
    server.address: en.wikipedia.org
    server.port: 443
    url.full: https://en.wikipedia.org/wiki/.NET          <-- Shows 1, the "#History" fragment is missing
    http.response.status_code: 200
    network.protocol.version: 1.1
Instrumentation scope (ActivitySource):
    Name: System.Net.Http
Resource associated with Activity:
    telemetry.sdk.name: opentelemetry
    telemetry.sdk.language: dotnet
    telemetry.sdk.version: 1.10.0
    service.name: unknown_service:ConsoleApp4

Response status code: OK

Regression?

No response

Known Workarounds

No response

Configuration

No response

Other information

No response

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions