From 506b4fd057588d298c7a3076bb6de742a981c3bf Mon Sep 17 00:00:00 2001 From: Zach Montoya Date: Thu, 30 Jul 2026 15:49:31 -0700 Subject: [PATCH 1/9] Implement OpenTelemetry tags for HTTP client spans: - Add "http.response.status_code" and "url.full" as the OTel Name for the HttpTags first-class HttpMethod and Host properties - Introduce HttpRequestMethodOriginal and ServerPort properties on HttpTags to set OpenTelemetry span attributes "http.request.method_original" and "server.port" - Add one central `HttpSemanticConventions.SetHttpClientRequestValues `method that updtes a span and its HttpTags object with all of the logic for required and some recommended span attributes - Call the above `HttpSemanticConventions.SetHttpClientRequestValues` from `ScopeFactory.CreateInactiveOutboundHttpSpan` and the remoting client `HttpProcessAndSendIntegration` --- .../Client/HttpProcessAndSendIntegration.cs | 8 + .../Datadog.Trace/ClrProfiler/ScopeFactory.cs | 22 +- .../TagListGenerator/HttpTags.g.cs | 94 ++++++++- .../TagListGenerator/HttpTags.g.cs | 94 ++++++++- .../TagListGenerator/HttpTags.g.cs | 94 ++++++++- .../TagListGenerator/HttpTags.g.cs | 94 ++++++++- .../OpenTelemetry/HttpSemanticConventions.cs | 141 +++++++++++++ tracer/src/Datadog.Trace/Tagging/HttpTags.cs | 28 ++- tracer/src/Datadog.Trace/Tags.cs | 22 ++ .../Util/Http/HttpRequestUtils.cs | 38 ++++ .../ClrProfiler/ScopeFactoryTests.cs | 198 ++++++++++++++++++ .../HttpSemanticConventionsTests.cs | 67 ++++++ .../Tagging/TagsListTests.cs | 66 ++++++ .../Util/Http/HttpRequestUtilsTests.cs | 51 +++++ 14 files changed, 983 insertions(+), 34 deletions(-) create mode 100644 tracer/src/Datadog.Trace/OpenTelemetry/HttpSemanticConventions.cs create mode 100644 tracer/test/Datadog.Trace.Tests/ClrProfiler/ScopeFactoryTests.cs create mode 100644 tracer/test/Datadog.Trace.Tests/OpenTelemetry/HttpSemanticConventionsTests.cs diff --git a/tracer/src/Datadog.Trace/ClrProfiler/AutoInstrumentation/Remoting/Client/HttpProcessAndSendIntegration.cs b/tracer/src/Datadog.Trace/ClrProfiler/AutoInstrumentation/Remoting/Client/HttpProcessAndSendIntegration.cs index 8e50fa4ed1c7..3b35d87210de 100644 --- a/tracer/src/Datadog.Trace/ClrProfiler/AutoInstrumentation/Remoting/Client/HttpProcessAndSendIntegration.cs +++ b/tracer/src/Datadog.Trace/ClrProfiler/AutoInstrumentation/Remoting/Client/HttpProcessAndSendIntegration.cs @@ -14,6 +14,7 @@ using System.Runtime.Remoting.Channels; using System.Runtime.Remoting.Messaging; using Datadog.Trace.ClrProfiler.CallTarget; +using Datadog.Trace.OpenTelemetry; using Datadog.Trace.Tagging; using Datadog.Trace.Util; using Datadog.Trace.Util.Http; @@ -72,6 +73,13 @@ internal static CallTargetReturn OnMethodEnd(TTarget if (state.Scope?.Span is Span span && span.Tags is HttpTags httpTags && returnValue is HttpWebRequest request) { var requestUri = request.RequestUri; + + if (span.OpenTelemetrySemanticsEnabled) + { + HttpSemanticConventions.SetHttpClientRequestValues(span, httpTags, request.Method, requestUri, Tracer.Instance.TracerManager.QueryStringManager); + return new CallTargetReturn(returnValue); + } + var requestMethod = request.Method.ToUpperInvariant(); if (requestUri != null) diff --git a/tracer/src/Datadog.Trace/ClrProfiler/ScopeFactory.cs b/tracer/src/Datadog.Trace/ClrProfiler/ScopeFactory.cs index b470e6e1dfea..9afb33ef4e5c 100644 --- a/tracer/src/Datadog.Trace/ClrProfiler/ScopeFactory.cs +++ b/tracer/src/Datadog.Trace/ClrProfiler/ScopeFactory.cs @@ -8,6 +8,7 @@ using Datadog.Trace.Configuration; using Datadog.Trace.Configuration.Schema; using Datadog.Trace.Logging; +using Datadog.Trace.OpenTelemetry; using Datadog.Trace.Tagging; using Datadog.Trace.Util; using Datadog.Trace.Util.Http; @@ -109,8 +110,6 @@ internal static Span CreateInactiveOutboundHttpSpan( return null; } - string resourceUrl = requestUri != null ? UriHelpers.CleanUri(requestUri, removeScheme: true, tryRemoveIds: true) : null; - var operationName = tracer.CurrentTraceSettings.Schema.Client.GetOperationNameForProtocol(ClientSchema.Protocol.Http); var (serviceName, serviceNameSource) = tracer.CurrentTraceSettings.Schema.Client.GetServiceNameMetadata(ClientSchema.Component.Http); tags = tracer.CurrentTraceSettings.Schema.Client.CreateHttpTags(); @@ -118,13 +117,22 @@ internal static Span CreateInactiveOutboundHttpSpan( span = tracer.StartSpan(operationName, tags, serviceName: serviceName, serviceNameSource: serviceNameSource, traceId: traceId, spanId: spanId, startTime: startTime, addToTraceContext: addToTraceContext); span.Type = SpanTypes.Http; - span.ResourceName = $"{httpMethod} {resourceUrl}"; - tags.HttpMethod = httpMethod?.ToUpperInvariant(); - if (requestUri is not null) + if (span.OpenTelemetrySemanticsEnabled) + { + HttpSemanticConventions.SetHttpClientRequestValues(span, tags, httpMethod, requestUri, tracer.TracerManager.QueryStringManager); + } + else { - tags.HttpUrl = HttpRequestUtils.GetUrl(requestUri, tracer.TracerManager.QueryStringManager); - tags.Host = HttpRequestUtils.GetNormalizedHost(requestUri.Host); + string resourceUrl = requestUri != null ? UriHelpers.CleanUri(requestUri, removeScheme: true, tryRemoveIds: true) : null; + span.ResourceName = $"{httpMethod} {resourceUrl}"; + + tags.HttpMethod = httpMethod?.ToUpperInvariant(); + if (requestUri is not null) + { + tags.HttpUrl = HttpRequestUtils.GetUrl(requestUri, tracer.TracerManager.QueryStringManager); + tags.Host = HttpRequestUtils.GetNormalizedHost(requestUri.Host); + } } tags.InstrumentationName = IntegrationRegistry.GetName(integrationId); diff --git a/tracer/src/Datadog.Trace/Generated/net461/Datadog.Trace.SourceGenerators/TagListGenerator/HttpTags.g.cs b/tracer/src/Datadog.Trace/Generated/net461/Datadog.Trace.SourceGenerators/TagListGenerator/HttpTags.g.cs index 7406088379c3..e41164e84181 100644 --- a/tracer/src/Datadog.Trace/Generated/net461/Datadog.Trace.SourceGenerators/TagListGenerator/HttpTags.g.cs +++ b/tracer/src/Datadog.Trace/Generated/net461/Datadog.Trace.SourceGenerators/TagListGenerator/HttpTags.g.cs @@ -23,9 +23,18 @@ partial class HttpTags // HttpMethodBytes = MessagePack.Serialize("http.method"); private static ReadOnlySpan HttpMethodBytes => [171, 104, 116, 116, 112, 46, 109, 101, 116, 104, 111, 100]; + // HttpMethodOTelBytes = MessagePack.Serialize("http.request.method"); + private static ReadOnlySpan HttpMethodOTelBytes => [179, 104, 116, 116, 112, 46, 114, 101, 113, 117, 101, 115, 116, 46, 109, 101, 116, 104, 111, 100]; + + // HttpRequestMethodOriginalBytes = MessagePack.Serialize("http.request.method_original"); + private static ReadOnlySpan HttpRequestMethodOriginalBytes => [188, 104, 116, 116, 112, 46, 114, 101, 113, 117, 101, 115, 116, 46, 109, 101, 116, 104, 111, 100, 95, 111, 114, 105, 103, 105, 110, 97, 108]; + // HttpUrlBytes = MessagePack.Serialize("http.url"); private static ReadOnlySpan HttpUrlBytes => [168, 104, 116, 116, 112, 46, 117, 114, 108]; + // HttpUrlOTelBytes = MessagePack.Serialize("url.full"); + private static ReadOnlySpan HttpUrlOTelBytes => [168, 117, 114, 108, 46, 102, 117, 108, 108]; + // HttpClientHandlerTypeBytes = MessagePack.Serialize("http-client-handler-type"); private static ReadOnlySpan HttpClientHandlerTypeBytes => [184, 104, 116, 116, 112, 45, 99, 108, 105, 101, 110, 116, 45, 104, 97, 110, 100, 108, 101, 114, 45, 116, 121, 112, 101]; @@ -38,6 +47,12 @@ partial class HttpTags // HostBytes = MessagePack.Serialize("out.host"); private static ReadOnlySpan HostBytes => [168, 111, 117, 116, 46, 104, 111, 115, 116]; + // HostOTelBytes = MessagePack.Serialize("server.address"); + private static ReadOnlySpan HostOTelBytes => [174, 115, 101, 114, 118, 101, 114, 46, 97, 100, 100, 114, 101, 115, 115]; + + // ServerPortBytes = MessagePack.Serialize("server.port"); + private static ReadOnlySpan ServerPortBytes => [171, 115, 101, 114, 118, 101, 114, 46, 112, 111, 114, 116]; + public override string? GetTag(string key) { return key switch @@ -45,11 +60,16 @@ partial class HttpTags "span.kind" => SpanKind, "component" => InstrumentationName, "http.method" => HttpMethod, + "http.request.method" => HttpMethod, + "http.request.method_original" => HttpRequestMethodOriginal, "http.url" => HttpUrl, + "url.full" => HttpUrl, "http-client-handler-type" => HttpClientHandlerType, "http.status_code" => HttpStatusCode is null ? null : Datadog.Trace.Util.IntStringCache.ToInvariantString(HttpStatusCode.Value), "http.response.status_code" => HttpStatusCode is null ? null : Datadog.Trace.Util.IntStringCache.ToInvariantString(HttpStatusCode.Value), "out.host" => Host, + "server.address" => Host, + "server.port" => ServerPort is null ? null : Datadog.Trace.Util.IntStringCache.ToInvariantString(ServerPort.Value), _ => base.GetTag(key), }; } @@ -61,10 +81,15 @@ public override void SetTag(string key, string? value) case "component": InstrumentationName = value; break; - case "http.method": + case "http.method": + case "http.request.method": HttpMethod = value; break; - case "http.url": + case "http.request.method_original": + HttpRequestMethodOriginal = value; + break; + case "http.url": + case "url.full": HttpUrl = value; break; case "http-client-handler-type": @@ -82,8 +107,20 @@ public override void SetTag(string key, string? value) } break; - case "out.host": + case "out.host": + case "server.address": Host = value; + break; + case "server.port": + if (int.TryParse(value, System.Globalization.NumberStyles.Integer, System.Globalization.CultureInfo.InvariantCulture, out var parsedServerPort)) + { + ServerPort = parsedServerPort; + } + else + { + ServerPort = null; + } + break; case "span.kind": Logger.Value.Warning("Attempted to set readonly tag {TagName} on {TagType}. Ignoring.", key, nameof(HttpTags)); @@ -108,12 +145,31 @@ public override void EnumerateTags(ref TProcessor processor, bool op if (HttpMethod is not null) { - processor.Process(new TagItem("http.method", HttpMethod, HttpMethodBytes)); + if (openTelemetrySemanticsEnabled) + { + processor.Process(new TagItem("http.request.method", HttpMethod, HttpMethodOTelBytes)); + } + else + { + processor.Process(new TagItem("http.method", HttpMethod, HttpMethodBytes)); + } + } + + if (HttpRequestMethodOriginal is not null) + { + processor.Process(new TagItem("http.request.method_original", HttpRequestMethodOriginal, HttpRequestMethodOriginalBytes)); } if (HttpUrl is not null) { - processor.Process(new TagItem("http.url", HttpUrl, HttpUrlBytes)); + if (openTelemetrySemanticsEnabled) + { + processor.Process(new TagItem("url.full", HttpUrl, HttpUrlOTelBytes)); + } + else + { + processor.Process(new TagItem("http.url", HttpUrl, HttpUrlBytes)); + } } if (HttpClientHandlerType is not null) @@ -135,7 +191,19 @@ public override void EnumerateTags(ref TProcessor processor, bool op if (Host is not null) { - processor.Process(new TagItem("out.host", Host, HostBytes)); + if (openTelemetrySemanticsEnabled) + { + processor.Process(new TagItem("server.address", Host, HostOTelBytes)); + } + else + { + processor.Process(new TagItem("out.host", Host, HostBytes)); + } + } + + if (ServerPort is not null) + { + processor.Process(new TagItem("server.port", ServerPort.Value, ServerPortBytes)); } base.EnumerateTags(ref processor, openTelemetrySemanticsEnabled); @@ -164,6 +232,13 @@ protected override void WriteAdditionalTags(System.Text.StringBuilder sb) .Append(','); } + if (HttpRequestMethodOriginal is not null) + { + sb.Append("http.request.method_original (tag):") + .Append(HttpRequestMethodOriginal) + .Append(','); + } + if (HttpUrl is not null) { sb.Append("http.url (tag):") @@ -192,6 +267,13 @@ protected override void WriteAdditionalTags(System.Text.StringBuilder sb) .Append(','); } + if (ServerPort is not null) + { + sb.Append("server.port (tag):") + .Append(ServerPort.Value.ToString(System.Globalization.CultureInfo.InvariantCulture)) + .Append(','); + } + base.WriteAdditionalTags(sb); } } diff --git a/tracer/src/Datadog.Trace/Generated/net6.0/Datadog.Trace.SourceGenerators/TagListGenerator/HttpTags.g.cs b/tracer/src/Datadog.Trace/Generated/net6.0/Datadog.Trace.SourceGenerators/TagListGenerator/HttpTags.g.cs index 7406088379c3..e41164e84181 100644 --- a/tracer/src/Datadog.Trace/Generated/net6.0/Datadog.Trace.SourceGenerators/TagListGenerator/HttpTags.g.cs +++ b/tracer/src/Datadog.Trace/Generated/net6.0/Datadog.Trace.SourceGenerators/TagListGenerator/HttpTags.g.cs @@ -23,9 +23,18 @@ partial class HttpTags // HttpMethodBytes = MessagePack.Serialize("http.method"); private static ReadOnlySpan HttpMethodBytes => [171, 104, 116, 116, 112, 46, 109, 101, 116, 104, 111, 100]; + // HttpMethodOTelBytes = MessagePack.Serialize("http.request.method"); + private static ReadOnlySpan HttpMethodOTelBytes => [179, 104, 116, 116, 112, 46, 114, 101, 113, 117, 101, 115, 116, 46, 109, 101, 116, 104, 111, 100]; + + // HttpRequestMethodOriginalBytes = MessagePack.Serialize("http.request.method_original"); + private static ReadOnlySpan HttpRequestMethodOriginalBytes => [188, 104, 116, 116, 112, 46, 114, 101, 113, 117, 101, 115, 116, 46, 109, 101, 116, 104, 111, 100, 95, 111, 114, 105, 103, 105, 110, 97, 108]; + // HttpUrlBytes = MessagePack.Serialize("http.url"); private static ReadOnlySpan HttpUrlBytes => [168, 104, 116, 116, 112, 46, 117, 114, 108]; + // HttpUrlOTelBytes = MessagePack.Serialize("url.full"); + private static ReadOnlySpan HttpUrlOTelBytes => [168, 117, 114, 108, 46, 102, 117, 108, 108]; + // HttpClientHandlerTypeBytes = MessagePack.Serialize("http-client-handler-type"); private static ReadOnlySpan HttpClientHandlerTypeBytes => [184, 104, 116, 116, 112, 45, 99, 108, 105, 101, 110, 116, 45, 104, 97, 110, 100, 108, 101, 114, 45, 116, 121, 112, 101]; @@ -38,6 +47,12 @@ partial class HttpTags // HostBytes = MessagePack.Serialize("out.host"); private static ReadOnlySpan HostBytes => [168, 111, 117, 116, 46, 104, 111, 115, 116]; + // HostOTelBytes = MessagePack.Serialize("server.address"); + private static ReadOnlySpan HostOTelBytes => [174, 115, 101, 114, 118, 101, 114, 46, 97, 100, 100, 114, 101, 115, 115]; + + // ServerPortBytes = MessagePack.Serialize("server.port"); + private static ReadOnlySpan ServerPortBytes => [171, 115, 101, 114, 118, 101, 114, 46, 112, 111, 114, 116]; + public override string? GetTag(string key) { return key switch @@ -45,11 +60,16 @@ partial class HttpTags "span.kind" => SpanKind, "component" => InstrumentationName, "http.method" => HttpMethod, + "http.request.method" => HttpMethod, + "http.request.method_original" => HttpRequestMethodOriginal, "http.url" => HttpUrl, + "url.full" => HttpUrl, "http-client-handler-type" => HttpClientHandlerType, "http.status_code" => HttpStatusCode is null ? null : Datadog.Trace.Util.IntStringCache.ToInvariantString(HttpStatusCode.Value), "http.response.status_code" => HttpStatusCode is null ? null : Datadog.Trace.Util.IntStringCache.ToInvariantString(HttpStatusCode.Value), "out.host" => Host, + "server.address" => Host, + "server.port" => ServerPort is null ? null : Datadog.Trace.Util.IntStringCache.ToInvariantString(ServerPort.Value), _ => base.GetTag(key), }; } @@ -61,10 +81,15 @@ public override void SetTag(string key, string? value) case "component": InstrumentationName = value; break; - case "http.method": + case "http.method": + case "http.request.method": HttpMethod = value; break; - case "http.url": + case "http.request.method_original": + HttpRequestMethodOriginal = value; + break; + case "http.url": + case "url.full": HttpUrl = value; break; case "http-client-handler-type": @@ -82,8 +107,20 @@ public override void SetTag(string key, string? value) } break; - case "out.host": + case "out.host": + case "server.address": Host = value; + break; + case "server.port": + if (int.TryParse(value, System.Globalization.NumberStyles.Integer, System.Globalization.CultureInfo.InvariantCulture, out var parsedServerPort)) + { + ServerPort = parsedServerPort; + } + else + { + ServerPort = null; + } + break; case "span.kind": Logger.Value.Warning("Attempted to set readonly tag {TagName} on {TagType}. Ignoring.", key, nameof(HttpTags)); @@ -108,12 +145,31 @@ public override void EnumerateTags(ref TProcessor processor, bool op if (HttpMethod is not null) { - processor.Process(new TagItem("http.method", HttpMethod, HttpMethodBytes)); + if (openTelemetrySemanticsEnabled) + { + processor.Process(new TagItem("http.request.method", HttpMethod, HttpMethodOTelBytes)); + } + else + { + processor.Process(new TagItem("http.method", HttpMethod, HttpMethodBytes)); + } + } + + if (HttpRequestMethodOriginal is not null) + { + processor.Process(new TagItem("http.request.method_original", HttpRequestMethodOriginal, HttpRequestMethodOriginalBytes)); } if (HttpUrl is not null) { - processor.Process(new TagItem("http.url", HttpUrl, HttpUrlBytes)); + if (openTelemetrySemanticsEnabled) + { + processor.Process(new TagItem("url.full", HttpUrl, HttpUrlOTelBytes)); + } + else + { + processor.Process(new TagItem("http.url", HttpUrl, HttpUrlBytes)); + } } if (HttpClientHandlerType is not null) @@ -135,7 +191,19 @@ public override void EnumerateTags(ref TProcessor processor, bool op if (Host is not null) { - processor.Process(new TagItem("out.host", Host, HostBytes)); + if (openTelemetrySemanticsEnabled) + { + processor.Process(new TagItem("server.address", Host, HostOTelBytes)); + } + else + { + processor.Process(new TagItem("out.host", Host, HostBytes)); + } + } + + if (ServerPort is not null) + { + processor.Process(new TagItem("server.port", ServerPort.Value, ServerPortBytes)); } base.EnumerateTags(ref processor, openTelemetrySemanticsEnabled); @@ -164,6 +232,13 @@ protected override void WriteAdditionalTags(System.Text.StringBuilder sb) .Append(','); } + if (HttpRequestMethodOriginal is not null) + { + sb.Append("http.request.method_original (tag):") + .Append(HttpRequestMethodOriginal) + .Append(','); + } + if (HttpUrl is not null) { sb.Append("http.url (tag):") @@ -192,6 +267,13 @@ protected override void WriteAdditionalTags(System.Text.StringBuilder sb) .Append(','); } + if (ServerPort is not null) + { + sb.Append("server.port (tag):") + .Append(ServerPort.Value.ToString(System.Globalization.CultureInfo.InvariantCulture)) + .Append(','); + } + base.WriteAdditionalTags(sb); } } diff --git a/tracer/src/Datadog.Trace/Generated/netcoreapp3.1/Datadog.Trace.SourceGenerators/TagListGenerator/HttpTags.g.cs b/tracer/src/Datadog.Trace/Generated/netcoreapp3.1/Datadog.Trace.SourceGenerators/TagListGenerator/HttpTags.g.cs index 7406088379c3..e41164e84181 100644 --- a/tracer/src/Datadog.Trace/Generated/netcoreapp3.1/Datadog.Trace.SourceGenerators/TagListGenerator/HttpTags.g.cs +++ b/tracer/src/Datadog.Trace/Generated/netcoreapp3.1/Datadog.Trace.SourceGenerators/TagListGenerator/HttpTags.g.cs @@ -23,9 +23,18 @@ partial class HttpTags // HttpMethodBytes = MessagePack.Serialize("http.method"); private static ReadOnlySpan HttpMethodBytes => [171, 104, 116, 116, 112, 46, 109, 101, 116, 104, 111, 100]; + // HttpMethodOTelBytes = MessagePack.Serialize("http.request.method"); + private static ReadOnlySpan HttpMethodOTelBytes => [179, 104, 116, 116, 112, 46, 114, 101, 113, 117, 101, 115, 116, 46, 109, 101, 116, 104, 111, 100]; + + // HttpRequestMethodOriginalBytes = MessagePack.Serialize("http.request.method_original"); + private static ReadOnlySpan HttpRequestMethodOriginalBytes => [188, 104, 116, 116, 112, 46, 114, 101, 113, 117, 101, 115, 116, 46, 109, 101, 116, 104, 111, 100, 95, 111, 114, 105, 103, 105, 110, 97, 108]; + // HttpUrlBytes = MessagePack.Serialize("http.url"); private static ReadOnlySpan HttpUrlBytes => [168, 104, 116, 116, 112, 46, 117, 114, 108]; + // HttpUrlOTelBytes = MessagePack.Serialize("url.full"); + private static ReadOnlySpan HttpUrlOTelBytes => [168, 117, 114, 108, 46, 102, 117, 108, 108]; + // HttpClientHandlerTypeBytes = MessagePack.Serialize("http-client-handler-type"); private static ReadOnlySpan HttpClientHandlerTypeBytes => [184, 104, 116, 116, 112, 45, 99, 108, 105, 101, 110, 116, 45, 104, 97, 110, 100, 108, 101, 114, 45, 116, 121, 112, 101]; @@ -38,6 +47,12 @@ partial class HttpTags // HostBytes = MessagePack.Serialize("out.host"); private static ReadOnlySpan HostBytes => [168, 111, 117, 116, 46, 104, 111, 115, 116]; + // HostOTelBytes = MessagePack.Serialize("server.address"); + private static ReadOnlySpan HostOTelBytes => [174, 115, 101, 114, 118, 101, 114, 46, 97, 100, 100, 114, 101, 115, 115]; + + // ServerPortBytes = MessagePack.Serialize("server.port"); + private static ReadOnlySpan ServerPortBytes => [171, 115, 101, 114, 118, 101, 114, 46, 112, 111, 114, 116]; + public override string? GetTag(string key) { return key switch @@ -45,11 +60,16 @@ partial class HttpTags "span.kind" => SpanKind, "component" => InstrumentationName, "http.method" => HttpMethod, + "http.request.method" => HttpMethod, + "http.request.method_original" => HttpRequestMethodOriginal, "http.url" => HttpUrl, + "url.full" => HttpUrl, "http-client-handler-type" => HttpClientHandlerType, "http.status_code" => HttpStatusCode is null ? null : Datadog.Trace.Util.IntStringCache.ToInvariantString(HttpStatusCode.Value), "http.response.status_code" => HttpStatusCode is null ? null : Datadog.Trace.Util.IntStringCache.ToInvariantString(HttpStatusCode.Value), "out.host" => Host, + "server.address" => Host, + "server.port" => ServerPort is null ? null : Datadog.Trace.Util.IntStringCache.ToInvariantString(ServerPort.Value), _ => base.GetTag(key), }; } @@ -61,10 +81,15 @@ public override void SetTag(string key, string? value) case "component": InstrumentationName = value; break; - case "http.method": + case "http.method": + case "http.request.method": HttpMethod = value; break; - case "http.url": + case "http.request.method_original": + HttpRequestMethodOriginal = value; + break; + case "http.url": + case "url.full": HttpUrl = value; break; case "http-client-handler-type": @@ -82,8 +107,20 @@ public override void SetTag(string key, string? value) } break; - case "out.host": + case "out.host": + case "server.address": Host = value; + break; + case "server.port": + if (int.TryParse(value, System.Globalization.NumberStyles.Integer, System.Globalization.CultureInfo.InvariantCulture, out var parsedServerPort)) + { + ServerPort = parsedServerPort; + } + else + { + ServerPort = null; + } + break; case "span.kind": Logger.Value.Warning("Attempted to set readonly tag {TagName} on {TagType}. Ignoring.", key, nameof(HttpTags)); @@ -108,12 +145,31 @@ public override void EnumerateTags(ref TProcessor processor, bool op if (HttpMethod is not null) { - processor.Process(new TagItem("http.method", HttpMethod, HttpMethodBytes)); + if (openTelemetrySemanticsEnabled) + { + processor.Process(new TagItem("http.request.method", HttpMethod, HttpMethodOTelBytes)); + } + else + { + processor.Process(new TagItem("http.method", HttpMethod, HttpMethodBytes)); + } + } + + if (HttpRequestMethodOriginal is not null) + { + processor.Process(new TagItem("http.request.method_original", HttpRequestMethodOriginal, HttpRequestMethodOriginalBytes)); } if (HttpUrl is not null) { - processor.Process(new TagItem("http.url", HttpUrl, HttpUrlBytes)); + if (openTelemetrySemanticsEnabled) + { + processor.Process(new TagItem("url.full", HttpUrl, HttpUrlOTelBytes)); + } + else + { + processor.Process(new TagItem("http.url", HttpUrl, HttpUrlBytes)); + } } if (HttpClientHandlerType is not null) @@ -135,7 +191,19 @@ public override void EnumerateTags(ref TProcessor processor, bool op if (Host is not null) { - processor.Process(new TagItem("out.host", Host, HostBytes)); + if (openTelemetrySemanticsEnabled) + { + processor.Process(new TagItem("server.address", Host, HostOTelBytes)); + } + else + { + processor.Process(new TagItem("out.host", Host, HostBytes)); + } + } + + if (ServerPort is not null) + { + processor.Process(new TagItem("server.port", ServerPort.Value, ServerPortBytes)); } base.EnumerateTags(ref processor, openTelemetrySemanticsEnabled); @@ -164,6 +232,13 @@ protected override void WriteAdditionalTags(System.Text.StringBuilder sb) .Append(','); } + if (HttpRequestMethodOriginal is not null) + { + sb.Append("http.request.method_original (tag):") + .Append(HttpRequestMethodOriginal) + .Append(','); + } + if (HttpUrl is not null) { sb.Append("http.url (tag):") @@ -192,6 +267,13 @@ protected override void WriteAdditionalTags(System.Text.StringBuilder sb) .Append(','); } + if (ServerPort is not null) + { + sb.Append("server.port (tag):") + .Append(ServerPort.Value.ToString(System.Globalization.CultureInfo.InvariantCulture)) + .Append(','); + } + base.WriteAdditionalTags(sb); } } diff --git a/tracer/src/Datadog.Trace/Generated/netstandard2.0/Datadog.Trace.SourceGenerators/TagListGenerator/HttpTags.g.cs b/tracer/src/Datadog.Trace/Generated/netstandard2.0/Datadog.Trace.SourceGenerators/TagListGenerator/HttpTags.g.cs index 7406088379c3..e41164e84181 100644 --- a/tracer/src/Datadog.Trace/Generated/netstandard2.0/Datadog.Trace.SourceGenerators/TagListGenerator/HttpTags.g.cs +++ b/tracer/src/Datadog.Trace/Generated/netstandard2.0/Datadog.Trace.SourceGenerators/TagListGenerator/HttpTags.g.cs @@ -23,9 +23,18 @@ partial class HttpTags // HttpMethodBytes = MessagePack.Serialize("http.method"); private static ReadOnlySpan HttpMethodBytes => [171, 104, 116, 116, 112, 46, 109, 101, 116, 104, 111, 100]; + // HttpMethodOTelBytes = MessagePack.Serialize("http.request.method"); + private static ReadOnlySpan HttpMethodOTelBytes => [179, 104, 116, 116, 112, 46, 114, 101, 113, 117, 101, 115, 116, 46, 109, 101, 116, 104, 111, 100]; + + // HttpRequestMethodOriginalBytes = MessagePack.Serialize("http.request.method_original"); + private static ReadOnlySpan HttpRequestMethodOriginalBytes => [188, 104, 116, 116, 112, 46, 114, 101, 113, 117, 101, 115, 116, 46, 109, 101, 116, 104, 111, 100, 95, 111, 114, 105, 103, 105, 110, 97, 108]; + // HttpUrlBytes = MessagePack.Serialize("http.url"); private static ReadOnlySpan HttpUrlBytes => [168, 104, 116, 116, 112, 46, 117, 114, 108]; + // HttpUrlOTelBytes = MessagePack.Serialize("url.full"); + private static ReadOnlySpan HttpUrlOTelBytes => [168, 117, 114, 108, 46, 102, 117, 108, 108]; + // HttpClientHandlerTypeBytes = MessagePack.Serialize("http-client-handler-type"); private static ReadOnlySpan HttpClientHandlerTypeBytes => [184, 104, 116, 116, 112, 45, 99, 108, 105, 101, 110, 116, 45, 104, 97, 110, 100, 108, 101, 114, 45, 116, 121, 112, 101]; @@ -38,6 +47,12 @@ partial class HttpTags // HostBytes = MessagePack.Serialize("out.host"); private static ReadOnlySpan HostBytes => [168, 111, 117, 116, 46, 104, 111, 115, 116]; + // HostOTelBytes = MessagePack.Serialize("server.address"); + private static ReadOnlySpan HostOTelBytes => [174, 115, 101, 114, 118, 101, 114, 46, 97, 100, 100, 114, 101, 115, 115]; + + // ServerPortBytes = MessagePack.Serialize("server.port"); + private static ReadOnlySpan ServerPortBytes => [171, 115, 101, 114, 118, 101, 114, 46, 112, 111, 114, 116]; + public override string? GetTag(string key) { return key switch @@ -45,11 +60,16 @@ partial class HttpTags "span.kind" => SpanKind, "component" => InstrumentationName, "http.method" => HttpMethod, + "http.request.method" => HttpMethod, + "http.request.method_original" => HttpRequestMethodOriginal, "http.url" => HttpUrl, + "url.full" => HttpUrl, "http-client-handler-type" => HttpClientHandlerType, "http.status_code" => HttpStatusCode is null ? null : Datadog.Trace.Util.IntStringCache.ToInvariantString(HttpStatusCode.Value), "http.response.status_code" => HttpStatusCode is null ? null : Datadog.Trace.Util.IntStringCache.ToInvariantString(HttpStatusCode.Value), "out.host" => Host, + "server.address" => Host, + "server.port" => ServerPort is null ? null : Datadog.Trace.Util.IntStringCache.ToInvariantString(ServerPort.Value), _ => base.GetTag(key), }; } @@ -61,10 +81,15 @@ public override void SetTag(string key, string? value) case "component": InstrumentationName = value; break; - case "http.method": + case "http.method": + case "http.request.method": HttpMethod = value; break; - case "http.url": + case "http.request.method_original": + HttpRequestMethodOriginal = value; + break; + case "http.url": + case "url.full": HttpUrl = value; break; case "http-client-handler-type": @@ -82,8 +107,20 @@ public override void SetTag(string key, string? value) } break; - case "out.host": + case "out.host": + case "server.address": Host = value; + break; + case "server.port": + if (int.TryParse(value, System.Globalization.NumberStyles.Integer, System.Globalization.CultureInfo.InvariantCulture, out var parsedServerPort)) + { + ServerPort = parsedServerPort; + } + else + { + ServerPort = null; + } + break; case "span.kind": Logger.Value.Warning("Attempted to set readonly tag {TagName} on {TagType}. Ignoring.", key, nameof(HttpTags)); @@ -108,12 +145,31 @@ public override void EnumerateTags(ref TProcessor processor, bool op if (HttpMethod is not null) { - processor.Process(new TagItem("http.method", HttpMethod, HttpMethodBytes)); + if (openTelemetrySemanticsEnabled) + { + processor.Process(new TagItem("http.request.method", HttpMethod, HttpMethodOTelBytes)); + } + else + { + processor.Process(new TagItem("http.method", HttpMethod, HttpMethodBytes)); + } + } + + if (HttpRequestMethodOriginal is not null) + { + processor.Process(new TagItem("http.request.method_original", HttpRequestMethodOriginal, HttpRequestMethodOriginalBytes)); } if (HttpUrl is not null) { - processor.Process(new TagItem("http.url", HttpUrl, HttpUrlBytes)); + if (openTelemetrySemanticsEnabled) + { + processor.Process(new TagItem("url.full", HttpUrl, HttpUrlOTelBytes)); + } + else + { + processor.Process(new TagItem("http.url", HttpUrl, HttpUrlBytes)); + } } if (HttpClientHandlerType is not null) @@ -135,7 +191,19 @@ public override void EnumerateTags(ref TProcessor processor, bool op if (Host is not null) { - processor.Process(new TagItem("out.host", Host, HostBytes)); + if (openTelemetrySemanticsEnabled) + { + processor.Process(new TagItem("server.address", Host, HostOTelBytes)); + } + else + { + processor.Process(new TagItem("out.host", Host, HostBytes)); + } + } + + if (ServerPort is not null) + { + processor.Process(new TagItem("server.port", ServerPort.Value, ServerPortBytes)); } base.EnumerateTags(ref processor, openTelemetrySemanticsEnabled); @@ -164,6 +232,13 @@ protected override void WriteAdditionalTags(System.Text.StringBuilder sb) .Append(','); } + if (HttpRequestMethodOriginal is not null) + { + sb.Append("http.request.method_original (tag):") + .Append(HttpRequestMethodOriginal) + .Append(','); + } + if (HttpUrl is not null) { sb.Append("http.url (tag):") @@ -192,6 +267,13 @@ protected override void WriteAdditionalTags(System.Text.StringBuilder sb) .Append(','); } + if (ServerPort is not null) + { + sb.Append("server.port (tag):") + .Append(ServerPort.Value.ToString(System.Globalization.CultureInfo.InvariantCulture)) + .Append(','); + } + base.WriteAdditionalTags(sb); } } diff --git a/tracer/src/Datadog.Trace/OpenTelemetry/HttpSemanticConventions.cs b/tracer/src/Datadog.Trace/OpenTelemetry/HttpSemanticConventions.cs new file mode 100644 index 000000000000..3c4b9cf23111 --- /dev/null +++ b/tracer/src/Datadog.Trace/OpenTelemetry/HttpSemanticConventions.cs @@ -0,0 +1,141 @@ +// +// Unless explicitly stated otherwise all files in this repository are licensed under the Apache 2 License. +// This product includes software developed at Datadog (https://www.datadoghq.com/). Copyright 2017 Datadog, Inc. +// + +#nullable enable + +using System; +using System.Collections.Generic; +using Datadog.Trace.Tagging; +using Datadog.Trace.Util; +using Datadog.Trace.Util.Http; + +namespace Datadog.Trace.OpenTelemetry +{ + /// + /// Shapes the values required by the + /// OpenTelemetry HTTP + /// semantic conventions and stores them on the corresponding ITags properties. + /// Only used when OpenTelemetry semantics are enabled. + /// + internal static class HttpSemanticConventions + { + /// + /// The value reported in "http.request.method" when the request method is not one of the + /// . + /// + internal const string OtherRequestMethod = "_OTHER"; + + /// + /// The span name used when the request method is not one of the . + /// + internal const string UnknownMethodSpanName = "HTTP"; + + /// + /// Maps a method name to its canonical form, ignoring case. Contains the methods defined in + /// RFC 9110, plus + /// PATCH and QUERY. Any other method is reported as . + /// + private static readonly Dictionary CanonicalRequestMethods = + new(StringComparer.OrdinalIgnoreCase) + { + { "CONNECT", "CONNECT" }, + { "DELETE", "DELETE" }, + { "GET", "GET" }, + { "HEAD", "HEAD" }, + { "OPTIONS", "OPTIONS" }, + { "PATCH", "PATCH" }, + { "POST", "POST" }, + { "PUT", "PUT" }, + { "QUERY", "QUERY" }, + { "TRACE", "TRACE" }, + }; + + /// + /// Sets the span name and the request tags of an HTTP client span, using the OpenTelemetry + /// HTTP semantic conventions. + /// + /// The HTTP client span + /// The tags of + /// The HTTP method of the request, as provided by the instrumented library + /// The absolute URI of the request, if known + /// Used to truncate and obfuscate the query string + internal static void SetHttpClientRequestValues(Span span, HttpTags tags, string? httpMethod, Uri? requestUri, QueryStringManager? queryStringManager) + { + var requestMethod = NormalizeRequestMethod(httpMethod); + + tags.HttpMethod = requestMethod; + + // "http.request.method_original" is only set when it differs from "http.request.method", + // which happens when the method is unknown, or was not already in its canonical form. + // Always assigned, as some integrations call this more than once for the same span. + tags.HttpRequestMethodOriginal = + !StringUtil.IsNullOrEmpty(httpMethod) && !string.Equals(httpMethod, requestMethod, StringComparison.Ordinal) + ? httpMethod + : null; + + // The span name is "{method} {target}", but there is no low-cardinality target available + // for HTTP client spans until we support "url.template", so we only use the method. + // Note that we must not fall back to using the URI path as the target. + span.ResourceName = GetSpanName(requestMethod); + + if (requestUri is not null) + { + tags.HttpUrl = HttpRequestUtils.GetUrlFull(requestUri, queryStringManager); + tags.Host = HttpRequestUtils.GetNormalizedHost(requestUri.Host); + + // Uri.Port is the default port for the scheme when the URL doesn't specify one, + // which is what we want to report in "server.port" + tags.ServerPort = requestUri.Port; + } + } + + /// + /// Gets the value to report in "http.request.method": the canonical form of + /// , or if it is not one of + /// the . + /// + internal static string NormalizeRequestMethod(string? httpMethod) + { + if (StringUtil.IsNullOrEmpty(httpMethod)) + { + return OtherRequestMethod; + } + + // Fast path: the method is already in its canonical form, which is the common case. + // Kept in sync with CanonicalRequestMethods, but written as a switch because an ordinal + // match is measurably cheaper than the case-insensitive hash the dictionary has to compute. + switch (httpMethod) + { + case "CONNECT": + case "DELETE": + case "GET": + case "HEAD": + case "OPTIONS": + case "PATCH": + case "POST": + case "PUT": + case "QUERY": + case "TRACE": + return httpMethod; + } + + // HTTP methods are case-sensitive, but the libraries we instrument are not always, + // so treat a case-insensitive match as the canonical method. The original value is + // reported separately in "http.request.method_original". + return CanonicalRequestMethods.TryGetValue(httpMethod, out var canonicalMethod) + ? canonicalMethod + : OtherRequestMethod; + } + + /// + /// Gets the span name for a request with the provided "http.request.method" value, when no + /// low-cardinality target is available. + /// + internal static string GetSpanName(string requestMethod) + => string.Equals(requestMethod, OtherRequestMethod, StringComparison.Ordinal) + ? UnknownMethodSpanName + : requestMethod; + } +} diff --git a/tracer/src/Datadog.Trace/Tagging/HttpTags.cs b/tracer/src/Datadog.Trace/Tagging/HttpTags.cs index 095304bee5ca..644c03d4a8ea 100644 --- a/tracer/src/Datadog.Trace/Tagging/HttpTags.cs +++ b/tracer/src/Datadog.Trace/Tagging/HttpTags.cs @@ -18,10 +18,22 @@ internal partial class HttpTags : InstrumentationTags, IHasStatusCode [Tag(Trace.Tags.InstrumentationName)] public string InstrumentationName { get; set; } - [Tag(Trace.Tags.HttpMethod)] + [Tag(Trace.Tags.HttpMethod, OTelName = Trace.Tags.HttpRequestMethod)] public string HttpMethod { get; set; } - [Tag(Trace.Tags.HttpUrl)] + /// + /// Gets or sets the original HTTP method, when it differs from the value reported + /// in . This is an OpenTelemetry-only concept, so it is only + /// set when OpenTelemetry semantics are enabled. + /// + [Tag(Trace.Tags.HttpRequestMethodOriginal)] + public string HttpRequestMethodOriginal { get; set; } + + /// + /// Gets or sets the request URL. Serialized as "http.url" with Datadog semantics + /// and as "url.full" with OpenTelemetry semantics. + /// + [Tag(Trace.Tags.HttpUrl, OTelName = Trace.Tags.UrlFull)] public string HttpUrl { get; set; } [Tag(HttpClientHandlerTypeKey)] @@ -30,8 +42,16 @@ internal partial class HttpTags : InstrumentationTags, IHasStatusCode [Tag(Trace.Tags.HttpStatusCode, OtelName = Trace.Tags.HttpResponseStatusCode)] public int? HttpStatusCode { get; set; } - [Tag(Trace.Tags.OutHost)] + [Tag(Trace.Tags.OutHost, OTelName = Trace.Tags.ServerAddress)] public string Host { get; set; } + + /// + /// Gets or sets the port of the remote server. We have never reported a port for + /// HTTP client spans with Datadog semantics, so this is only set when OpenTelemetry + /// semantics are enabled. + /// + [Tag(Trace.Tags.ServerPort)] + public int? ServerPort { get; set; } } internal sealed partial class HttpV1Tags : HttpTags @@ -55,6 +75,8 @@ public string PeerServiceSource { get { + // Do not update this when OpenTelemetry semantics are enabled + // since OpenTelemetry semantics supercedes V1Tags return _peerServiceOverride is not null ? "peer.service" : "out.host"; diff --git a/tracer/src/Datadog.Trace/Tags.cs b/tracer/src/Datadog.Trace/Tags.cs index 36425e4372ca..93ab0e56df77 100644 --- a/tracer/src/Datadog.Trace/Tags.cs +++ b/tracer/src/Datadog.Trace/Tags.cs @@ -56,11 +56,28 @@ public static partial class Tags /// public const string HttpUrl = "http.url"; + /// + /// The OpenTelemetry semantic convention absolute URL of an HTTP request + /// + public const string UrlFull = "url.full"; + /// /// The method of an HTTP request /// public const string HttpMethod = "http.method"; + /// + /// The OpenTelemetry semantic convention method of an HTTP request. The method is reported in its canonical uppercase form. + /// Unrecognized methods are reported as _OTHER. + /// + public const string HttpRequestMethod = "http.request.method"; + + /// + /// The original method of an HTTP request. Only set when it differs (case-sensitive) from + /// the value reported in . + /// + public const string HttpRequestMethodOriginal = "http.request.method_original"; + /// /// The host of an HTTP request /// @@ -155,6 +172,11 @@ public static partial class Tags /// public const string ServerAddress = "server.address"; + /// + /// The server port for the remote service. + /// + public const string ServerPort = "server.port"; + /// /// The size of the message. /// diff --git a/tracer/src/Datadog.Trace/Util/Http/HttpRequestUtils.cs b/tracer/src/Datadog.Trace/Util/Http/HttpRequestUtils.cs index 9e3aa34101a8..cece1e601002 100644 --- a/tracer/src/Datadog.Trace/Util/Http/HttpRequestUtils.cs +++ b/tracer/src/Datadog.Trace/Util/Http/HttpRequestUtils.cs @@ -14,6 +14,10 @@ internal static class HttpRequestUtils { private const string NoHostSpecified = "UNKNOWN_HOST"; + // These include the '@' delimiter so they can be inserted into a URL as-is + private const string RedactedUserName = "REDACTED@"; + private const string RedactedUserNameAndPassword = "REDACTED:REDACTED@"; + #if NET6_0_OR_GREATER // In .NET 6+, we could theoretically bypass a bunch of allocations by using the GetComponents() method which is heavily // optimized. Unfortunately, in .NET FX and < .NET 6, this approach allocates a _lot_ more. And what's more @@ -92,6 +96,40 @@ internal static string GetUrl(Uri uri, QueryStringManager? queryStringManager = } #endif + /// + /// Gets the absolute URL reported in the OpenTelemetry "url.full" attribute. This is the + /// same value as , except that credentials + /// passed in the URL are redacted rather than dropped, as required by the OpenTelemetry + /// HTTP semantic conventions. + /// + /// The absolute URI of the request + /// Used to truncate and obfuscate the query string + internal static string GetUrlFull(Uri uri, QueryStringManager? queryStringManager = null) + { + var userInfo = uri.UserInfo; + var url = GetUrl(uri, queryStringManager); + + if (StringUtil.IsNullOrEmpty(userInfo)) + { + return url; + } + + // GetUrl() never includes the user info, so the redacted form has to be inserted at the + // start of the authority. A scheme cannot contain '/', so the first "//" always delimits + // it. If it somehow isn't there, return the URL that already has no credentials in it + // rather than building something malformed. + var schemeDelimiterIndex = url.IndexOf("//", StringComparison.Ordinal); + if (schemeDelimiterIndex < 0) + { + return url; + } + + // We must never report the credentials, but the URL should still show that they were there + return url.Insert( + schemeDelimiterIndex + 2, + userInfo.IndexOf(':') >= 0 ? RedactedUserNameAndPassword : RedactedUserName); + } + internal static string GetUrl(string scheme, string host, int? port, string pathBase, string path, string queryString, QueryStringManager? queryStringManager = null) { if (queryStringManager != null) diff --git a/tracer/test/Datadog.Trace.Tests/ClrProfiler/ScopeFactoryTests.cs b/tracer/test/Datadog.Trace.Tests/ClrProfiler/ScopeFactoryTests.cs new file mode 100644 index 000000000000..1a46a0251891 --- /dev/null +++ b/tracer/test/Datadog.Trace.Tests/ClrProfiler/ScopeFactoryTests.cs @@ -0,0 +1,198 @@ +// +// Unless explicitly stated otherwise all files in this repository are licensed under the Apache 2 License. +// This product includes software developed at Datadog (https://www.datadoghq.com/). Copyright 2017 Datadog, Inc. +// + +using System; +using System.Collections.Generic; +using System.Collections.Specialized; +using System.Globalization; +using System.Linq; +using System.Threading.Tasks; +using Datadog.Trace.ClrProfiler; +using Datadog.Trace.Configuration; +using Datadog.Trace.Processors; +using Datadog.Trace.Tagging; +using Datadog.Trace.TestHelpers.TestTracer; +using FluentAssertions; +using Xunit; + +namespace Datadog.Trace.Tests.ClrProfiler; + +public class ScopeFactoryTests +{ + private static readonly Uri RequestUri = new("http://localhost:8080/api/users?q=1"); + + [Fact] + public async Task CreateOutboundHttpScope_WithDatadogSemantics_UsesDatadogNamesAndValues() + { + await using var tracer = CreateTracer(otelSemanticsEnabled: false); + using var parent = StartParentScope(tracer); + + using var scope = ScopeFactory.CreateOutboundHttpScope(tracer, "GET", RequestUri, IntegrationId.HttpMessageHandler, out var tags); + + scope.Span.ResourceName.Should().Be("GET localhost:8080/api/users"); + scope.Span.Type.Should().Be(SpanTypes.Http); + + tags.HttpMethod.Should().Be("GET"); + tags.HttpUrl.Should().Be("http://localhost:8080/api/users?q=1"); + tags.Host.Should().Be("localhost"); + + // OpenTelemetry-only concepts are not populated + tags.ServerPort.Should().BeNull(); + tags.HttpRequestMethodOriginal.Should().BeNull(); + + var serializedTags = GetSerializedTags(scope.Span); + serializedTags.Should().Contain( + [ + new KeyValuePair(Tags.HttpMethod, "GET"), + new KeyValuePair(Tags.HttpUrl, "http://localhost:8080/api/users?q=1"), + new KeyValuePair(Tags.OutHost, "localhost"), + ]); + serializedTags.Keys.Should().NotContain([Tags.HttpRequestMethod, Tags.UrlFull, Tags.ServerAddress, Tags.ServerPort]); + } + + [Fact] + public async Task CreateOutboundHttpScope_WithOpenTelemetrySemantics_UsesOpenTelemetryNamesAndValues() + { + await using var tracer = CreateTracer(otelSemanticsEnabled: true); + using var parent = StartParentScope(tracer); + + using var scope = ScopeFactory.CreateOutboundHttpScope(tracer, "GET", RequestUri, IntegrationId.HttpMessageHandler, out var tags); + + // there is no low-cardinality target available for HTTP client spans, so the name is just the method + scope.Span.ResourceName.Should().Be("GET"); + scope.Span.Type.Should().Be(SpanTypes.Http); + + tags.HttpMethod.Should().Be("GET"); + tags.HttpUrl.Should().Be("http://localhost:8080/api/users?q=1"); + tags.Host.Should().Be("localhost"); + tags.ServerPort.Should().Be(8080); + tags.HttpRequestMethodOriginal.Should().BeNull(); + + var serializedTags = GetSerializedTags(scope.Span); + serializedTags.Should().Contain( + [ + new KeyValuePair(Tags.HttpRequestMethod, "GET"), + new KeyValuePair(Tags.UrlFull, "http://localhost:8080/api/users?q=1"), + new KeyValuePair(Tags.ServerAddress, "localhost"), + new KeyValuePair(Tags.ServerPort, "8080"), + ]); + serializedTags.Keys.Should().NotContain([Tags.HttpMethod, Tags.HttpUrl, Tags.OutHost, Tags.HttpRequestMethodOriginal]); + } + + [Fact] + public async Task CreateOutboundHttpScope_WithOpenTelemetrySemantics_UsesTheDefaultPortWhenNotSpecified() + { + await using var tracer = CreateTracer(otelSemanticsEnabled: true); + using var parent = StartParentScope(tracer); + + using var scope = ScopeFactory.CreateOutboundHttpScope(tracer, "GET", new Uri("https://example.com/api"), IntegrationId.HttpMessageHandler, out var tags); + + tags.ServerPort.Should().Be(443); + tags.HttpUrl.Should().Be("https://example.com/api"); + } + + [Theory] + // known methods are reported in their canonical form, with the original value when it differs + [InlineData("GET", "GET", null, "GET")] + [InlineData("get", "GET", "get", "GET")] + [InlineData("Patch", "PATCH", "Patch", "PATCH")] + + // unknown methods are reported as _OTHER, and the span is named HTTP + [InlineData("FOO", "_OTHER", "FOO", "HTTP")] + [InlineData(null, "_OTHER", null, "HTTP")] + public async Task CreateOutboundHttpScope_WithOpenTelemetrySemantics_NormalizesTheRequestMethod( + string httpMethod, string expectedMethod, string expectedOriginalMethod, string expectedSpanName) + { + await using var tracer = CreateTracer(otelSemanticsEnabled: true); + using var parent = StartParentScope(tracer); + + using var scope = ScopeFactory.CreateOutboundHttpScope(tracer, httpMethod, RequestUri, IntegrationId.HttpMessageHandler, out var tags); + + scope.Span.ResourceName.Should().Be(expectedSpanName); + tags.HttpMethod.Should().Be(expectedMethod); + tags.HttpRequestMethodOriginal.Should().Be(expectedOriginalMethod); + } + + [Fact] + public async Task CreateOutboundHttpScope_WithOpenTelemetrySemantics_RedactsCredentialsInTheUrl() + { + await using var tracer = CreateTracer(otelSemanticsEnabled: true); + using var parent = StartParentScope(tracer); + + using var scope = ScopeFactory.CreateOutboundHttpScope(tracer, "GET", new Uri("http://user:pass@localhost/api"), IntegrationId.HttpMessageHandler, out var tags); + + tags.HttpUrl.Should().Be("http://REDACTED:REDACTED@localhost/api"); + } + + // peer.service is calculated from the same backing property as the host tag, so it keeps working + // when the tag is renamed to "server.address". The reported source stays "out.host" either way: + // it is a Datadog-only attribute, and OpenTelemetry semantics supersede the V1 schema. + [Theory] + [InlineData(false)] + [InlineData(true)] + public async Task CreateOutboundHttpScope_CalculatesPeerServiceFromTheHostTag(bool otelSemanticsEnabled) + { + await using var tracer = CreateTracer(otelSemanticsEnabled, schemaVersion: "v1"); + using var parent = StartParentScope(tracer); + + using var scope = ScopeFactory.CreateOutboundHttpScope(tracer, "GET", RequestUri, IntegrationId.HttpMessageHandler, out var tags); + + tags.Should().BeOfType(); + tags.GetTag(Tags.PeerService).Should().Be("localhost"); + tags.GetTag(Tags.PeerServiceSource).Should().Be(Tags.OutHost); + } + + private static Scope StartParentScope(Tracer tracer) + { + // Azure Functions support installs a PlatformStrategy.ShouldSkipClientSpan callback that + // skips parentless client spans. It is a mutable static, so another test in this assembly + // may already have installed it: always create the client span inside an active scope. + return tracer.StartActiveInternal("parent"); + } + + private static ScopedTracer CreateTracer(bool otelSemanticsEnabled, string schemaVersion = null) + { + var collection = new NameValueCollection + { + { ConfigurationKeys.OpenTelemetry.OtelSemanticsEnabled, otelSemanticsEnabled ? "true" : "false" }, + }; + + if (schemaVersion is not null) + { + collection.Add(ConfigurationKeys.MetadataSchemaVersion, schemaVersion); + } + + var settings = new TracerSettings(new NameValueConfigurationSource(collection)); + return TracerHelper.CreateWithFakeAgent(settings); + } + + private static Dictionary GetSerializedTags(Span span) + { + var result = new Dictionary(); + var processor = new TagCollectorProcessor(result); + span.Tags.EnumerateTags(ref processor, span.OpenTelemetrySemanticsEnabled); + return result; + } + + private readonly struct TagCollectorProcessor : IItemProcessor, IItemProcessor + { + private readonly Dictionary _items; + + public TagCollectorProcessor(Dictionary items) + { + _items = items; + } + + public void Process(TagItem item) + { + _items[item.Key] = item.Value; + } + + public void Process(TagItem item) + { + _items[item.Key] = item.Value.ToString(CultureInfo.InvariantCulture); + } + } +} diff --git a/tracer/test/Datadog.Trace.Tests/OpenTelemetry/HttpSemanticConventionsTests.cs b/tracer/test/Datadog.Trace.Tests/OpenTelemetry/HttpSemanticConventionsTests.cs new file mode 100644 index 000000000000..fbd9a1d34cf5 --- /dev/null +++ b/tracer/test/Datadog.Trace.Tests/OpenTelemetry/HttpSemanticConventionsTests.cs @@ -0,0 +1,67 @@ +// +// Unless explicitly stated otherwise all files in this repository are licensed under the Apache 2 License. +// This product includes software developed at Datadog (https://www.datadoghq.com/). Copyright 2017 Datadog, Inc. +// + +using Datadog.Trace.OpenTelemetry; +using FluentAssertions; +using Xunit; + +namespace Datadog.Trace.Tests.OpenTelemetry; + +public class HttpSemanticConventionsTests +{ + public static TheoryData AllKnownMethods() => + new() { "CONNECT", "DELETE", "GET", "HEAD", "OPTIONS", "PATCH", "POST", "PUT", "QUERY", "TRACE" }; + + [Theory] + // The RFC 9110 methods, plus PATCH and QUERY + [InlineData("CONNECT", "CONNECT")] + [InlineData("DELETE", "DELETE")] + [InlineData("GET", "GET")] + [InlineData("HEAD", "HEAD")] + [InlineData("OPTIONS", "OPTIONS")] + [InlineData("PATCH", "PATCH")] + [InlineData("POST", "POST")] + [InlineData("PUT", "PUT")] + [InlineData("QUERY", "QUERY")] + [InlineData("TRACE", "TRACE")] + + // Known methods are converted to their canonical form + [InlineData("get", "GET")] + [InlineData("Post", "POST")] + [InlineData("pAtCh", "PATCH")] + + // Anything else is _OTHER + [InlineData("FOO", "_OTHER")] + [InlineData("GETS", "_OTHER")] + [InlineData("GE", "_OTHER")] + [InlineData("_OTHER", "_OTHER")] + [InlineData(" GET", "_OTHER")] + [InlineData("", "_OTHER")] + [InlineData(null, "_OTHER")] + public void NormalizeRequestMethod_ReturnsKnownMethodOrOther(string httpMethod, string expected) + { + HttpSemanticConventions.NormalizeRequestMethod(httpMethod).Should().Be(expected); + } + + [Theory] + [InlineData("GET", "GET")] + [InlineData("POST", "POST")] + [InlineData("_OTHER", "HTTP")] + public void GetSpanName_UsesHttpForUnknownMethods(string requestMethod, string expected) + { + HttpSemanticConventions.GetSpanName(requestMethod).Should().Be(expected); + } + + // NormalizeRequestMethod holds the known methods twice: an ordinal switch for the fast path, + // and a case-insensitive dictionary for the fallback. Exercising every method in both its + // canonical and its lower-case form covers both, so the two cannot drift apart unnoticed. + [Theory] + [MemberData(nameof(AllKnownMethods))] + public void NormalizeRequestMethod_TakesTheSameDecisionOnBothPaths(string canonicalMethod) + { + HttpSemanticConventions.NormalizeRequestMethod(canonicalMethod).Should().Be(canonicalMethod); + HttpSemanticConventions.NormalizeRequestMethod(canonicalMethod.ToLowerInvariant()).Should().Be(canonicalMethod); + } +} diff --git a/tracer/test/Datadog.Trace.Tests/Tagging/TagsListTests.cs b/tracer/test/Datadog.Trace.Tests/Tagging/TagsListTests.cs index 4357163523f3..d144b9cca4a9 100644 --- a/tracer/test/Datadog.Trace.Tests/Tagging/TagsListTests.cs +++ b/tracer/test/Datadog.Trace.Tests/Tagging/TagsListTests.cs @@ -190,6 +190,72 @@ public void StronglyTypedStatusCodeAliasCanBeClearedByEitherName() tags.GetTag(Tags.HttpResponseStatusCode).Should().BeNull(); } + [Theory] + [InlineData(false, Tags.HttpMethod, Tags.HttpUrl, Tags.OutHost)] + [InlineData(true, Tags.HttpRequestMethod, Tags.UrlFull, Tags.ServerAddress)] + public void HttpClientTagAliasesEnumerateSelectedNames(bool openTelemetrySemanticsEnabled, string methodKey, string urlKey, string hostKey) + { + const string url = "http://localhost/api"; + var tags = new HttpTags { HttpMethod = "GET", HttpUrl = url, Host = "localhost" }; + + var snapshot = GetTagsSnapshot(tags, openTelemetrySemanticsEnabled); + + snapshot.Should().Contain( + [ + new KeyValuePair(methodKey, "GET"), + new KeyValuePair(urlKey, url), + new KeyValuePair(hostKey, "localhost"), + ]); + + // the aliases are mutually exclusive, so only one name is reported for each concept + var aliases = new[] { Tags.HttpMethod, Tags.HttpRequestMethod, Tags.HttpUrl, Tags.UrlFull, Tags.OutHost, Tags.ServerAddress }; + snapshot.Select(x => x.Key) + .Where(aliases.Contains) + .Should() + .BeEquivalentTo(new[] { methodKey, urlKey, hostKey }); + } + + [Fact] + public void HttpClientTagAliasesCanBeReadAndWrittenByEitherName() + { + var tags = new HttpTags(); + + tags.SetTag(Tags.HttpMethod, "GET"); + tags.GetTag(Tags.HttpRequestMethod).Should().Be("GET"); + tags.SetTag(Tags.HttpRequestMethod, "POST"); + tags.GetTag(Tags.HttpMethod).Should().Be("POST"); + tags.HttpMethod.Should().Be("POST"); + + tags.SetTag(Tags.HttpUrl, "http://localhost/1"); + tags.GetTag(Tags.UrlFull).Should().Be("http://localhost/1"); + tags.SetTag(Tags.UrlFull, "http://localhost/2"); + tags.GetTag(Tags.HttpUrl).Should().Be("http://localhost/2"); + tags.HttpUrl.Should().Be("http://localhost/2"); + + tags.SetTag(Tags.OutHost, "host1"); + tags.GetTag(Tags.ServerAddress).Should().Be("host1"); + tags.SetTag(Tags.ServerAddress, "host2"); + tags.GetTag(Tags.OutHost).Should().Be("host2"); + tags.Host.Should().Be("host2"); + } + + [Fact] + public void ServerPortIsOnlyReportedWhenSet() + { + var tags = new HttpTags(); + + GetTagsSnapshot(tags, openTelemetrySemanticsEnabled: true) + .Select(x => x.Key) + .Should() + .NotContain(Tags.ServerPort); + + tags.ServerPort = 8080; + + GetTagsSnapshot(tags, openTelemetrySemanticsEnabled: true) + .Should() + .Contain(new KeyValuePair(Tags.ServerPort, "8080")); + } + [Fact] public void GetTag_GetMetric_ReturnUpdatedValues() { diff --git a/tracer/test/Datadog.Trace.Tests/Util/Http/HttpRequestUtilsTests.cs b/tracer/test/Datadog.Trace.Tests/Util/Http/HttpRequestUtilsTests.cs index 00b9755c5fc8..5f63036cecff 100644 --- a/tracer/test/Datadog.Trace.Tests/Util/Http/HttpRequestUtilsTests.cs +++ b/tracer/test/Datadog.Trace.Tests/Util/Http/HttpRequestUtilsTests.cs @@ -200,6 +200,57 @@ public void GetUrl_ShouldFormatCorrectly(string url, bool useQueryManager, bool result.Should().Be(expected); } + [Theory] + // No credentials, so the value is identical to GetUrl() + [InlineData("http://localhost/path", false, "http://localhost/path")] + [InlineData("https://example.com/api/users", false, "https://example.com/api/users")] + [InlineData("http://localhost:8080/test", false, "http://localhost:8080/test")] + [InlineData("http://localhost/path?key=value", false, "http://localhost/path")] + [InlineData("http://localhost/path?key=value", true, "http://localhost/path?key=value")] + + // Credentials are redacted instead of dropped + [InlineData("http://user:pass@localhost/path", false, "http://REDACTED:REDACTED@localhost/path")] + [InlineData("https://user:pass@example.com/api/users", false, "https://REDACTED:REDACTED@example.com/api/users")] + [InlineData("http://user:pass@localhost:8080/path", false, "http://REDACTED:REDACTED@localhost:8080/path")] + [InlineData("http://user:pass@localhost", false, "http://REDACTED:REDACTED@localhost/")] + + // A username without a password is redacted without inventing a password + [InlineData("http://user@localhost/path", false, "http://REDACTED@localhost/path")] + + // The query string is still truncated and obfuscated + [InlineData("http://user:pass@localhost/path?q=1", false, "http://REDACTED:REDACTED@localhost/path")] + [InlineData("http://user:pass@localhost/path?q=1", true, "http://REDACTED:REDACTED@localhost/path?q=1")] + [InlineData("http://user:pass@localhost/login?password=secret123", true, "http://REDACTED:REDACTED@localhost/login?")] + + // The rows below pin KNOWN DEVIATIONS from the OpenTelemetry semantic conventions for + // "url.full" (https://opentelemetry.io/docs/specs/semconv/registry/attributes/url/), so that + // they are recorded rather than mistaken for compliance. Update them if the behaviour is fixed. + // + // Deviation 1: query scrubbing uses DD_TRACE_OBFUSCATION_QUERY_STRING_REGEXP, which replaces the + // match with "" and does not preserve the key (see the "password" row above), whereas + // the spec asks for "sig=REDACTED" with the key preserved. Its default key list also differs + // from ours: of the five keys the spec redacts by default, "sig" and "X-Amz-Credential" are not + // matched at all, so they are reported verbatim. That part of the spec is Development stability, + // and it allows the default list to be fully overridden, which is what our regex effectively does. + [InlineData("http://localhost/path?sig=abc123", true, "http://localhost/path?sig=abc123")] + [InlineData("http://localhost/path?color=blue&sig=abc123", true, "http://localhost/path?color=blue&sig=abc123")] + + // Deviation 2: a known fragment is dropped, whereas the spec says that although it is not + // transmitted over HTTP it SHOULD be included when known. Asserted on both code paths, because + // only the credential path builds the URL by hand instead of delegating to GetUrl(). + [InlineData("http://localhost/path?q=1#SemConv", true, "http://localhost/path?q=1")] + [InlineData("http://user:pass@localhost/path?q=1#SemConv", true, "http://REDACTED:REDACTED@localhost/path?q=1")] + public void GetUrlFull_ShouldRedactCredentials(string url, bool useQueryManager, string expected) + { + var uri = new Uri(url); + var queryStringManager = useQueryManager + ? new QueryStringManager(reportQueryString: true, timeout: 30_000, maxSizeBeforeObfuscation: 50, pattern: TracerSettingsConstants.DefaultObfuscationQueryStringRegex) + : null; + + var result = HttpRequestUtils.GetUrlFull(uri, queryStringManager); + result.Should().Be(expected); + } + [Theory] [InlineData("http://localhost/path", "http://localhost/path")] [InlineData("https://example.com/api/users?id=123", "https://example.com/api/users")] From bc08228674c1cb2009b4d6b3f3c3b15a5f546365 Mon Sep 17 00:00:00 2001 From: Zach Montoya Date: Thu, 30 Jul 2026 17:28:36 -0700 Subject: [PATCH 2/9] Fix tracer settings so OpenTelemetry semantics overrides schema configuration by using v0 schema --- .../Configuration/TracerSettings.cs | 10 +++++++++ .../ClrProfiler/ScopeFactoryTests.cs | 21 ++++++++++--------- .../Configuration/TracerSettingsTests.cs | 17 +++++++++++++++ 3 files changed, 38 insertions(+), 10 deletions(-) diff --git a/tracer/src/Datadog.Trace/Configuration/TracerSettings.cs b/tracer/src/Datadog.Trace/Configuration/TracerSettings.cs index 81cea58b0535..b6604cb47c12 100644 --- a/tracer/src/Datadog.Trace/Configuration/TracerSettings.cs +++ b/tracer/src/Datadog.Trace/Configuration/TracerSettings.cs @@ -818,6 +818,16 @@ not null when string.Equals(value, "otlp", StringComparison.OrdinalIgnoreCase) = .WithKeys(ConfigurationKeys.OpenTelemetry.OtelSemanticsEnabled) .AsBool(defaultValue: false); + if (OtelSemanticsEnabled && MetadataSchemaVersion != SchemaVersion.V0) + { + // OpenTelemetry semantics mode already fully replaces Datadog attribute naming and values, + // so the V1 schema's Datadog-only attributes (e.g. peer.service) must not be layered on top. + Log.Warning( + $"{ConfigurationKeys.MetadataSchemaVersion} is set to a version other than v0, but {ConfigurationKeys.OpenTelemetry.OtelSemanticsEnabled} is enabled. Using v0 instead."); + MetadataSchemaVersion = SchemaVersion.V0; + telemetry.Record(ConfigurationKeys.MetadataSchemaVersion, "v0", recordValue: true, ConfigurationOrigins.Calculated); + } + var disabledActivitySources = config.WithKeys(ConfigurationKeys.DisabledActivitySources).AsString(); DisabledActivitySources = !string.IsNullOrEmpty(disabledActivitySources) ? TrimSplitString(disabledActivitySources, commaSeparator) : []; diff --git a/tracer/test/Datadog.Trace.Tests/ClrProfiler/ScopeFactoryTests.cs b/tracer/test/Datadog.Trace.Tests/ClrProfiler/ScopeFactoryTests.cs index 1a46a0251891..d28574da9cfb 100644 --- a/tracer/test/Datadog.Trace.Tests/ClrProfiler/ScopeFactoryTests.cs +++ b/tracer/test/Datadog.Trace.Tests/ClrProfiler/ScopeFactoryTests.cs @@ -126,22 +126,23 @@ public async Task CreateOutboundHttpScope_WithOpenTelemetrySemantics_RedactsCred tags.HttpUrl.Should().Be("http://REDACTED:REDACTED@localhost/api"); } - // peer.service is calculated from the same backing property as the host tag, so it keeps working - // when the tag is renamed to "server.address". The reported source stays "out.host" either way: - // it is a Datadog-only attribute, and OpenTelemetry semantics supersede the V1 schema. + // DD_TRACE_OTEL_SEMANTICS_ENABLED forces the effective metadata schema version to v0 (see + // TracerSettings), because OpenTelemetry semantics already fully replace Datadog attribute naming + // and values, so the V1 schema's Datadog-only attributes (e.g. peer.service) must not coexist with them. [Theory] - [InlineData(false)] - [InlineData(true)] - public async Task CreateOutboundHttpScope_CalculatesPeerServiceFromTheHostTag(bool otelSemanticsEnabled) + [InlineData("v1")] + [InlineData("v0")] + [InlineData(null)] + public async Task CreateOutboundHttpScope_WithOpenTelemetrySemantics_NeverUsesV1SchemaTags(string requestedSchemaVersion) { - await using var tracer = CreateTracer(otelSemanticsEnabled, schemaVersion: "v1"); + await using var tracer = CreateTracer(otelSemanticsEnabled: true, schemaVersion: requestedSchemaVersion); using var parent = StartParentScope(tracer); using var scope = ScopeFactory.CreateOutboundHttpScope(tracer, "GET", RequestUri, IntegrationId.HttpMessageHandler, out var tags); - tags.Should().BeOfType(); - tags.GetTag(Tags.PeerService).Should().Be("localhost"); - tags.GetTag(Tags.PeerServiceSource).Should().Be(Tags.OutHost); + tags.Should().BeOfType(); + tags.GetTag(Tags.PeerService).Should().BeNull(); + tags.GetTag(Tags.PeerServiceSource).Should().BeNull(); } private static Scope StartParentScope(Tracer tracer) diff --git a/tracer/test/Datadog.Trace.Tests/Configuration/TracerSettingsTests.cs b/tracer/test/Datadog.Trace.Tests/Configuration/TracerSettingsTests.cs index 080a0e9aabc1..97560eaec6f5 100644 --- a/tracer/test/Datadog.Trace.Tests/Configuration/TracerSettingsTests.cs +++ b/tracer/test/Datadog.Trace.Tests/Configuration/TracerSettingsTests.cs @@ -108,6 +108,23 @@ public void MetadataSchemaVersion(string value, object expected) settings.MetadataSchemaVersion.Should().Be((SchemaVersion)expected); } + [Theory] + [InlineData("v1", true, SchemaVersion.V0)] + [InlineData("V1", true, SchemaVersion.V0)] + [InlineData("v0", true, SchemaVersion.V0)] + [InlineData(null, true, SchemaVersion.V0)] + [InlineData("v1", false, SchemaVersion.V1)] + [InlineData(null, false, SchemaVersion.V0)] + public void MetadataSchemaVersion_IsForcedToV0WhenOtelSemanticsEnabled(string schemaVersion, bool otelSemanticsEnabled, object expected) + { + var source = CreateConfigurationSource( + (ConfigurationKeys.MetadataSchemaVersion, schemaVersion), + (ConfigurationKeys.OpenTelemetry.OtelSemanticsEnabled, otelSemanticsEnabled ? "true" : "false")); + var settings = new TracerSettings(source); + + settings.MetadataSchemaVersion.Should().Be((SchemaVersion)expected); + } + [Theory] [InlineData("key1:value1,key2:value2", new[] { "key1:value1", "key2:value2" })] [InlineData("key1 :value1,invalid,key2: value2", new[] { "key1:value1", "key2:value2" })] From ee5c864faebba2139d3449c0a17c20ac21933527 Mon Sep 17 00:00:00 2001 From: Zach Montoya Date: Thu, 30 Jul 2026 17:29:47 -0700 Subject: [PATCH 3/9] Add snapshot tests for Samples.WebRequest to test the new behavior with DD_TRACE_OTEL_SEMANTICS_ENABLED emitting DD MsgPack --- .../WebRequestTests.cs | 30 +- .../SpanMetadataAPI.cs | 1 + .../SpanMetadataOTelRules.cs | 43 + .../SpanTagAssertion.cs | 1 + .../WebRequestTests_otel.verified.txt | 3131 +++++++++++++++++ 5 files changed, 3200 insertions(+), 6 deletions(-) create mode 100644 tracer/test/Datadog.Trace.TestHelpers/SpanMetadataOTelRules.cs create mode 100644 tracer/test/snapshots/WebRequestTests_otel.verified.txt diff --git a/tracer/test/Datadog.Trace.ClrProfiler.IntegrationTests/WebRequestTests.cs b/tracer/test/Datadog.Trace.ClrProfiler.IntegrationTests/WebRequestTests.cs index a2665b2442dc..895bc16e0f4f 100644 --- a/tracer/test/Datadog.Trace.ClrProfiler.IntegrationTests/WebRequestTests.cs +++ b/tracer/test/Datadog.Trace.ClrProfiler.IntegrationTests/WebRequestTests.cs @@ -5,6 +5,7 @@ using System.Globalization; using System.Linq; +using System.Text.RegularExpressions; using System.Threading.Tasks; using Datadog.Trace.ClrProfiler.IntegrationTests.Helpers; using Datadog.Trace.Configuration; @@ -34,13 +35,25 @@ public WebRequestTests(ITestOutputHelper output) [Trait("Category", "EndToEnd")] [Trait("RunOnWindows", "True")] [Trait("SupportsInstrumentationVerification", "True")] - public Task SubmitsTracesV0() => RunTest("v0"); + public Task SubmitsTracesV0() => RunTest(metadataSchemaVersion: "v0", openTelemetrySemanticsEnabled: false); [SkippableFact] [Trait("Category", "EndToEnd")] [Trait("RunOnWindows", "True")] [Trait("SupportsInstrumentationVerification", "True")] - public Task SubmitsTracesV1() => RunTest("v1"); + public Task SubmitsTracesV1() => RunTest(metadataSchemaVersion: "v1", openTelemetrySemanticsEnabled: false); + + [SkippableFact] + [Trait("Category", "EndToEnd")] + [Trait("RunOnWindows", "True")] + [Trait("SupportsInstrumentationVerification", "True")] + public Task SubmitsTracesV0WithOpenTelemetrySemantics() => RunTest(metadataSchemaVersion: "v0", openTelemetrySemanticsEnabled: true); + + [SkippableFact] + [Trait("Category", "EndToEnd")] + [Trait("RunOnWindows", "True")] + [Trait("SupportsInstrumentationVerification", "True")] + public Task SubmitsTracesV1WithOpenTelemetrySemantics() => RunTest(metadataSchemaVersion: "v1", openTelemetrySemanticsEnabled: true); [SkippableFact] [Trait("Category", "EndToEnd")] @@ -71,7 +84,7 @@ public async Task TracingDisabled_DoesNotSubmitsTraces() } } - private async Task RunTest(string metadataSchemaVersion) + private async Task RunTest(string metadataSchemaVersion, bool openTelemetrySemanticsEnabled) { SetInstrumentationVerification(); @@ -81,7 +94,8 @@ private async Task RunTest(string metadataSchemaVersion) Output.WriteLine($"Assigning port {httpPort} for the httpPort."); SetEnvironmentVariable("DD_TRACE_SPAN_ATTRIBUTE_SCHEMA", metadataSchemaVersion); - var isExternalSpan = metadataSchemaVersion == "v0"; + SetEnvironmentVariable("DD_TRACE_OTEL_SEMANTICS_ENABLED", openTelemetrySemanticsEnabled.ToString()); + var isExternalSpan = metadataSchemaVersion == "v0" || openTelemetrySemanticsEnabled; // For OpenTelemetry Semantics enabled, we are unilaterally setting the metadata schema to v0 var clientSpanServiceName = isExternalSpan ? $"{EnvironmentHelper.FullSampleName}-http-client" : EnvironmentHelper.FullSampleName; using var telemetry = this.ConfigureTelemetry(); @@ -112,7 +126,10 @@ private async Task RunTest(string metadataSchemaVersion) // different TFMs use different underlying handlers, which we don't really care about for the snapshots settings.AddSimpleScrubber("System.Net.Http.HttpClientHandler", "System.Net.Http.SocketsHttpHandler"); #endif + settings.AddRegexScrubber(new Regex("\"time_unix_nano\":\\d+"), "\"time_unix_nano\":"); + settings.AddRegexScrubber(new Regex("server.port: \\d+"), "server.port: 8080"); var suffix = EnvironmentHelper.IsCoreClr() ? string.Empty : "_netfx"; + var schema = openTelemetrySemanticsEnabled ? "otel" : metadataSchemaVersion; await VerifyHelper.VerifySpans( allSpans, settings, @@ -122,11 +139,12 @@ await VerifyHelper.VerifySpans( .ThenBy(x => x.Tags.TryGetValue("http.url", out var url) ? url : string.Empty) .ThenBy(x => x.Start) .ThenBy(x => x.Duration)) - .UseFileName($"{nameof(WebRequestTests)}{suffix}_{metadataSchemaVersion}"); + .DisableRequireUniquePrefix() + .UseFileName($"{nameof(WebRequestTests)}{suffix}_{schema}"); allSpans.Should().OnlyHaveUniqueItems(s => new { s.SpanId, s.TraceId }); var httpSpans = allSpans.Where(s => s.Type == SpanTypes.Http).ToList(); - ValidateIntegrationSpans(httpSpans, metadataSchemaVersion, expectedServiceName: clientSpanServiceName, isExternalSpan); + ValidateIntegrationSpans(httpSpans, schema, expectedServiceName: clientSpanServiceName, isExternalSpan); await telemetry.AssertIntegrationEnabledAsync(IntegrationId.WebRequest); VerifyInstrumentation(processResult.Process); diff --git a/tracer/test/Datadog.Trace.TestHelpers/SpanMetadataAPI.cs b/tracer/test/Datadog.Trace.TestHelpers/SpanMetadataAPI.cs index 1d6750146749..8fc91b14ce55 100644 --- a/tracer/test/Datadog.Trace.TestHelpers/SpanMetadataAPI.cs +++ b/tracer/test/Datadog.Trace.TestHelpers/SpanMetadataAPI.cs @@ -389,6 +389,7 @@ public static Result IsWcf(this MockSpan span, string metadataSchemaVersion, ISe public static Result IsWebRequest(this MockSpan span, string metadataSchemaVersion) => metadataSchemaVersion switch { + "otel" => span.IsHttpClientRequestOTel(), "v1" => span.IsWebRequestV1(), _ => span.IsWebRequestV0(), }; diff --git a/tracer/test/Datadog.Trace.TestHelpers/SpanMetadataOTelRules.cs b/tracer/test/Datadog.Trace.TestHelpers/SpanMetadataOTelRules.cs new file mode 100644 index 000000000000..4b17a6efc26c --- /dev/null +++ b/tracer/test/Datadog.Trace.TestHelpers/SpanMetadataOTelRules.cs @@ -0,0 +1,43 @@ +// +// Unless explicitly stated otherwise all files in this repository are licensed under the Apache 2 License. +// This product includes software developed at Datadog (https://www.datadoghq.com/). Copyright 2017 Datadog, Inc. +// + +using System.Collections.Generic; +using static Datadog.Trace.TestHelpers.SpanMetadataRulesHelpers; + +namespace Datadog.Trace.TestHelpers +{ +#pragma warning disable SA1601 // Partial elements should be documented + internal static class SpanMetadataOTelRules + { + // See: https://opentelemetry.io/docs/specs/semconv/http/http-spans/ + public static Result IsHttpClientRequestOTel(this MockSpan span) => Result.FromSpan(span) + .Properties(s => s + .Matches(Name, "http.request") + .Matches(Type, "http")) + .Tags(s => s + // Required + .IsPresent("http.request.method") + .IsPresent("server.address") + .IsPresent("url.full") + // Conditionally required + .IsOptional("error.type") + .IsOptional("http.request.method_original") + .IsOptional("http.response.status_code") + .IsOptional("network.protocol.name") + .IsOptional("server.port") + // Recommended + .IsOptional("http.request.resend_count") + .IsOptional("network.peer.address") + .IsOptional("network.peer.port") + .IsOptional("network.protocol.version") + // DD Only + .IsPresent("component") + .IsOptional("http-client-handler-type") + .IsOptional("_dd.base_service") + .IsOptional("_dd.tags.process") + .IsOptional("_dd.svc_src") + .Matches("span.kind", "client")); + } +} diff --git a/tracer/test/Datadog.Trace.TestHelpers/SpanTagAssertion.cs b/tracer/test/Datadog.Trace.TestHelpers/SpanTagAssertion.cs index a1a6d11e3943..9ba71e81f492 100644 --- a/tracer/test/Datadog.Trace.TestHelpers/SpanTagAssertion.cs +++ b/tracer/test/Datadog.Trace.TestHelpers/SpanTagAssertion.cs @@ -26,6 +26,7 @@ public static void DefaultTagAssertions(SpanTagAssertion s) => s .IsOptional("runtime-id") // TODO: Make runtime-id required on all spans, per our span attributes push .IsOptional("language") // TODO: Make language required on all spans, per our span attributes push .IsOptional("version") + .IsOptional("events") .IsOptional("_dd.p.dm") // "decision maker", but contains the sampling mechanism .IsOptional("_dd.p.tid") // contains the upper 64 bits of a 128-bit trace id .IsOptional("_dd.p.ksr") // Knuth sampling rate, propagated tag added by the tracer diff --git a/tracer/test/snapshots/WebRequestTests_otel.verified.txt b/tracer/test/snapshots/WebRequestTests_otel.verified.txt new file mode 100644 index 000000000000..67ee7d7e7fbc --- /dev/null +++ b/tracer/test/snapshots/WebRequestTests_otel.verified.txt @@ -0,0 +1,3131 @@ +[ + { + TraceId: Id_1, + SpanId: Id_2, + Name: http.request, + Resource: POST, + Service: Samples.WebRequest-http-client, + Type: http, + Tags: { + component: WebRequest, + env: integration_tests, + http.request.method: POST, + http.response.status_code: 200, + language: dotnet, + runtime-id: Guid_1, + server.address: localhost, + server.port: 8080, + span.kind: client, + url.full: http://localhost:00000/Guid_2/?GetRequestStream, + _dd.base_service: Samples.WebRequest, + _dd.svc_src: http-client + }, + Metrics: { + process_id: 0, + _dd.top_level: 1.0, + _dd.tracer_kr: 1.0, + _sampling_priority_v1: 1.0 + } + }, + { + TraceId: Id_3, + SpanId: Id_4, + Name: http.request, + Resource: POST, + Service: Samples.WebRequest-http-client, + Type: http, + Tags: { + component: WebRequest, + env: integration_tests, + http.request.method: POST, + http.response.status_code: 200, + language: dotnet, + runtime-id: Guid_1, + server.address: localhost, + server.port: 8080, + span.kind: client, + url.full: http://localhost:00000/Guid_2/?BeginGetRequestStream, + _dd.base_service: Samples.WebRequest, + _dd.svc_src: http-client + }, + Metrics: { + process_id: 0, + _dd.top_level: 1.0, + _dd.tracer_kr: 1.0, + _sampling_priority_v1: 1.0 + } + }, + { + TraceId: Id_5, + SpanId: Id_6, + Name: internal, + Resource: WebClient, + Service: Samples.WebRequest, + Type: custom, + Tags: { + env: integration_tests, + language: dotnet, + otel.status_code: STATUS_CODE_UNSET, + runtime-id: Guid_1, + span.kind: internal, + version: 1.0.0 + }, + Metrics: { + process_id: 0, + _dd.top_level: 1.0, + _dd.tracer_kr: 1.0, + _sampling_priority_v1: 1.0 + } + }, + { + TraceId: Id_5, + SpanId: Id_7, + Name: internal, + Resource: DownloadData, + Service: Samples.WebRequest, + Type: custom, + ParentId: Id_6, + Tags: { + env: integration_tests, + language: dotnet, + otel.status_code: STATUS_CODE_UNSET, + span.kind: internal, + version: 1.0.0 + } + }, + { + TraceId: Id_5, + SpanId: Id_8, + Name: internal, + Resource: DownloadDataAsync, + Service: Samples.WebRequest, + Type: custom, + ParentId: Id_6, + Tags: { + env: integration_tests, + language: dotnet, + otel.status_code: STATUS_CODE_UNSET, + span.kind: internal, + version: 1.0.0 + } + }, + { + TraceId: Id_5, + SpanId: Id_9, + Name: internal, + Resource: DownloadDataTaskAsync, + Service: Samples.WebRequest, + Type: custom, + ParentId: Id_6, + Tags: { + env: integration_tests, + language: dotnet, + otel.status_code: STATUS_CODE_UNSET, + span.kind: internal, + version: 1.0.0 + } + }, + { + TraceId: Id_5, + SpanId: Id_10, + Name: internal, + Resource: DownloadFile, + Service: Samples.WebRequest, + Type: custom, + ParentId: Id_6, + Tags: { + env: integration_tests, + language: dotnet, + otel.status_code: STATUS_CODE_UNSET, + span.kind: internal, + version: 1.0.0 + } + }, + { + TraceId: Id_5, + SpanId: Id_11, + Name: internal, + Resource: DownloadFileAsync, + Service: Samples.WebRequest, + Type: custom, + ParentId: Id_6, + Tags: { + env: integration_tests, + language: dotnet, + otel.status_code: STATUS_CODE_UNSET, + span.kind: internal, + version: 1.0.0 + } + }, + { + TraceId: Id_5, + SpanId: Id_12, + Name: internal, + Resource: DownloadFileTaskAsync, + Service: Samples.WebRequest, + Type: custom, + ParentId: Id_6, + Tags: { + env: integration_tests, + language: dotnet, + otel.status_code: STATUS_CODE_UNSET, + span.kind: internal, + version: 1.0.0 + } + }, + { + TraceId: Id_5, + SpanId: Id_13, + Name: internal, + Resource: DownloadString, + Service: Samples.WebRequest, + Type: custom, + ParentId: Id_6, + Tags: { + env: integration_tests, + language: dotnet, + otel.status_code: STATUS_CODE_UNSET, + span.kind: internal, + version: 1.0.0 + } + }, + { + TraceId: Id_5, + SpanId: Id_14, + Name: internal, + Resource: DownloadStringAsync, + Service: Samples.WebRequest, + Type: custom, + ParentId: Id_6, + Tags: { + env: integration_tests, + language: dotnet, + otel.status_code: STATUS_CODE_UNSET, + span.kind: internal, + version: 1.0.0 + } + }, + { + TraceId: Id_5, + SpanId: Id_15, + Name: internal, + Resource: DownloadStringTaskAsync, + Service: Samples.WebRequest, + Type: custom, + ParentId: Id_6, + Tags: { + env: integration_tests, + language: dotnet, + otel.status_code: STATUS_CODE_UNSET, + span.kind: internal, + version: 1.0.0 + } + }, + { + TraceId: Id_5, + SpanId: Id_16, + Name: internal, + Resource: OpenRead, + Service: Samples.WebRequest, + Type: custom, + ParentId: Id_6, + Tags: { + env: integration_tests, + language: dotnet, + otel.status_code: STATUS_CODE_UNSET, + span.kind: internal, + version: 1.0.0 + } + }, + { + TraceId: Id_5, + SpanId: Id_17, + Name: internal, + Resource: OpenReadAsync, + Service: Samples.WebRequest, + Type: custom, + ParentId: Id_6, + Tags: { + env: integration_tests, + language: dotnet, + otel.status_code: STATUS_CODE_UNSET, + span.kind: internal, + version: 1.0.0 + } + }, + { + TraceId: Id_5, + SpanId: Id_18, + Name: internal, + Resource: OpenReadTaskAsync, + Service: Samples.WebRequest, + Type: custom, + ParentId: Id_6, + Tags: { + env: integration_tests, + language: dotnet, + otel.status_code: STATUS_CODE_UNSET, + span.kind: internal, + version: 1.0.0 + } + }, + { + TraceId: Id_5, + SpanId: Id_19, + Name: internal, + Resource: UploadData, + Service: Samples.WebRequest, + Type: custom, + ParentId: Id_6, + Tags: { + env: integration_tests, + language: dotnet, + otel.status_code: STATUS_CODE_UNSET, + span.kind: internal, + version: 1.0.0 + } + }, + { + TraceId: Id_5, + SpanId: Id_20, + Name: internal, + Resource: UploadDataAsync, + Service: Samples.WebRequest, + Type: custom, + ParentId: Id_6, + Tags: { + env: integration_tests, + language: dotnet, + otel.status_code: STATUS_CODE_UNSET, + span.kind: internal, + version: 1.0.0 + } + }, + { + TraceId: Id_5, + SpanId: Id_21, + Name: internal, + Resource: UploadDataTaskAsync, + Service: Samples.WebRequest, + Type: custom, + ParentId: Id_6, + Tags: { + env: integration_tests, + language: dotnet, + otel.status_code: STATUS_CODE_UNSET, + span.kind: internal, + version: 1.0.0 + } + }, + { + TraceId: Id_5, + SpanId: Id_22, + Name: internal, + Resource: UploadFile, + Service: Samples.WebRequest, + Type: custom, + ParentId: Id_6, + Tags: { + env: integration_tests, + language: dotnet, + otel.status_code: STATUS_CODE_UNSET, + span.kind: internal, + version: 1.0.0 + } + }, + { + TraceId: Id_5, + SpanId: Id_23, + Name: internal, + Resource: UploadFileAsync, + Service: Samples.WebRequest, + Type: custom, + ParentId: Id_6, + Tags: { + env: integration_tests, + language: dotnet, + otel.status_code: STATUS_CODE_UNSET, + span.kind: internal, + version: 1.0.0 + } + }, + { + TraceId: Id_5, + SpanId: Id_24, + Name: internal, + Resource: UploadFileTaskAsync, + Service: Samples.WebRequest, + Type: custom, + ParentId: Id_6, + Tags: { + env: integration_tests, + language: dotnet, + otel.status_code: STATUS_CODE_UNSET, + span.kind: internal, + version: 1.0.0 + } + }, + { + TraceId: Id_5, + SpanId: Id_25, + Name: internal, + Resource: UploadString, + Service: Samples.WebRequest, + Type: custom, + ParentId: Id_6, + Tags: { + env: integration_tests, + language: dotnet, + otel.status_code: STATUS_CODE_UNSET, + span.kind: internal, + version: 1.0.0 + } + }, + { + TraceId: Id_5, + SpanId: Id_26, + Name: internal, + Resource: UploadStringAsync, + Service: Samples.WebRequest, + Type: custom, + ParentId: Id_6, + Tags: { + env: integration_tests, + language: dotnet, + otel.status_code: STATUS_CODE_UNSET, + span.kind: internal, + version: 1.0.0 + } + }, + { + TraceId: Id_5, + SpanId: Id_27, + Name: internal, + Resource: UploadStringTaskAsync, + Service: Samples.WebRequest, + Type: custom, + ParentId: Id_6, + Tags: { + env: integration_tests, + language: dotnet, + otel.status_code: STATUS_CODE_UNSET, + span.kind: internal, + version: 1.0.0 + } + }, + { + TraceId: Id_5, + SpanId: Id_28, + Name: internal, + Resource: UploadValues, + Service: Samples.WebRequest, + Type: custom, + ParentId: Id_6, + Tags: { + env: integration_tests, + language: dotnet, + otel.status_code: STATUS_CODE_UNSET, + span.kind: internal, + version: 1.0.0 + } + }, + { + TraceId: Id_5, + SpanId: Id_29, + Name: internal, + Resource: UploadValuesAsync, + Service: Samples.WebRequest, + Type: custom, + ParentId: Id_6, + Tags: { + env: integration_tests, + language: dotnet, + otel.status_code: STATUS_CODE_UNSET, + span.kind: internal, + version: 1.0.0 + } + }, + { + TraceId: Id_5, + SpanId: Id_30, + Name: internal, + Resource: UploadValuesTaskAsync, + Service: Samples.WebRequest, + Type: custom, + ParentId: Id_6, + Tags: { + env: integration_tests, + language: dotnet, + otel.status_code: STATUS_CODE_UNSET, + span.kind: internal, + version: 1.0.0 + } + }, + { + TraceId: Id_5, + SpanId: Id_31, + Name: http.request, + Resource: GET, + Service: Samples.WebRequest-http-client, + Type: http, + ParentId: Id_7, + Tags: { + component: WebRequest, + env: integration_tests, + http.request.method: GET, + http.response.status_code: 200, + language: dotnet, + runtime-id: Guid_1, + server.address: localhost, + server.port: 8080, + span.kind: client, + url.full: http://localhost:00000/Guid_2/?DownloadData, + _dd.base_service: Samples.WebRequest, + _dd.svc_src: http-client + }, + Metrics: { + _dd.top_level: 1.0 + } + }, + { + TraceId: Id_5, + SpanId: Id_32, + Name: http.request, + Resource: GET, + Service: Samples.WebRequest-http-client, + Type: http, + ParentId: Id_7, + Tags: { + component: WebRequest, + env: integration_tests, + http.request.method: GET, + http.response.status_code: 200, + language: dotnet, + runtime-id: Guid_1, + server.address: localhost, + server.port: 8080, + span.kind: client, + url.full: http://localhost:00000/Guid_2/?DownloadData2, + _dd.base_service: Samples.WebRequest, + _dd.svc_src: http-client + }, + Metrics: { + _dd.top_level: 1.0 + } + }, + { + TraceId: Id_5, + SpanId: Id_33, + Name: http.request, + Resource: GET, + Service: Samples.WebRequest-http-client, + Type: http, + ParentId: Id_8, + Tags: { + component: HttpMessageHandler, + env: integration_tests, + http-client-handler-type: System.Net.Http.SocketsHttpHandler, + http.request.method: GET, + http.response.status_code: 200, + language: dotnet, + runtime-id: Guid_1, + server.address: localhost, + server.port: 8080, + span.kind: client, + url.full: http://localhost:00000/Guid_2/?DownloadDataAsync, + _dd.base_service: Samples.WebRequest, + _dd.svc_src: http-client + }, + Metrics: { + _dd.top_level: 1.0 + } + }, + { + TraceId: Id_5, + SpanId: Id_34, + Name: http.request, + Resource: GET, + Service: Samples.WebRequest-http-client, + Type: http, + ParentId: Id_8, + Tags: { + component: HttpMessageHandler, + env: integration_tests, + http-client-handler-type: System.Net.Http.SocketsHttpHandler, + http.request.method: GET, + http.response.status_code: 200, + language: dotnet, + runtime-id: Guid_1, + server.address: localhost, + server.port: 8080, + span.kind: client, + url.full: http://localhost:00000/Guid_2/?DownloadDataAsync2, + _dd.base_service: Samples.WebRequest, + _dd.svc_src: http-client + }, + Metrics: { + _dd.top_level: 1.0 + } + }, + { + TraceId: Id_5, + SpanId: Id_35, + Name: http.request, + Resource: GET, + Service: Samples.WebRequest-http-client, + Type: http, + ParentId: Id_9, + Tags: { + component: HttpMessageHandler, + env: integration_tests, + http-client-handler-type: System.Net.Http.SocketsHttpHandler, + http.request.method: GET, + http.response.status_code: 200, + language: dotnet, + runtime-id: Guid_1, + server.address: localhost, + server.port: 8080, + span.kind: client, + url.full: http://localhost:00000/Guid_2/?DownloadDataTaskAsync, + _dd.base_service: Samples.WebRequest, + _dd.svc_src: http-client + }, + Metrics: { + _dd.top_level: 1.0 + } + }, + { + TraceId: Id_5, + SpanId: Id_36, + Name: http.request, + Resource: GET, + Service: Samples.WebRequest-http-client, + Type: http, + ParentId: Id_9, + Tags: { + component: HttpMessageHandler, + env: integration_tests, + http-client-handler-type: System.Net.Http.SocketsHttpHandler, + http.request.method: GET, + http.response.status_code: 200, + language: dotnet, + runtime-id: Guid_1, + server.address: localhost, + server.port: 8080, + span.kind: client, + url.full: http://localhost:00000/Guid_2/?DownloadDataTaskAsync2, + _dd.base_service: Samples.WebRequest, + _dd.svc_src: http-client + }, + Metrics: { + _dd.top_level: 1.0 + } + }, + { + TraceId: Id_5, + SpanId: Id_37, + Name: http.request, + Resource: GET, + Service: Samples.WebRequest-http-client, + Type: http, + ParentId: Id_10, + Tags: { + component: WebRequest, + env: integration_tests, + http.request.method: GET, + http.response.status_code: 200, + language: dotnet, + runtime-id: Guid_1, + server.address: localhost, + server.port: 8080, + span.kind: client, + url.full: http://localhost:00000/Guid_2/?DownloadFile, + _dd.base_service: Samples.WebRequest, + _dd.svc_src: http-client + }, + Metrics: { + _dd.top_level: 1.0 + } + }, + { + TraceId: Id_5, + SpanId: Id_38, + Name: http.request, + Resource: GET, + Service: Samples.WebRequest-http-client, + Type: http, + ParentId: Id_10, + Tags: { + component: WebRequest, + env: integration_tests, + http.request.method: GET, + http.response.status_code: 200, + language: dotnet, + runtime-id: Guid_1, + server.address: localhost, + server.port: 8080, + span.kind: client, + url.full: http://localhost:00000/Guid_2/?DownloadFile2, + _dd.base_service: Samples.WebRequest, + _dd.svc_src: http-client + }, + Metrics: { + _dd.top_level: 1.0 + } + }, + { + TraceId: Id_5, + SpanId: Id_39, + Name: http.request, + Resource: GET, + Service: Samples.WebRequest-http-client, + Type: http, + ParentId: Id_11, + Tags: { + component: HttpMessageHandler, + env: integration_tests, + http-client-handler-type: System.Net.Http.SocketsHttpHandler, + http.request.method: GET, + http.response.status_code: 200, + language: dotnet, + runtime-id: Guid_1, + server.address: localhost, + server.port: 8080, + span.kind: client, + url.full: http://localhost:00000/Guid_2/?DownloadFileAsync, + _dd.base_service: Samples.WebRequest, + _dd.svc_src: http-client + }, + Metrics: { + _dd.top_level: 1.0 + } + }, + { + TraceId: Id_5, + SpanId: Id_40, + Name: http.request, + Resource: GET, + Service: Samples.WebRequest-http-client, + Type: http, + ParentId: Id_11, + Tags: { + component: HttpMessageHandler, + env: integration_tests, + http-client-handler-type: System.Net.Http.SocketsHttpHandler, + http.request.method: GET, + http.response.status_code: 200, + language: dotnet, + runtime-id: Guid_1, + server.address: localhost, + server.port: 8080, + span.kind: client, + url.full: http://localhost:00000/Guid_2/?DownloadFileAsync2, + _dd.base_service: Samples.WebRequest, + _dd.svc_src: http-client + }, + Metrics: { + _dd.top_level: 1.0 + } + }, + { + TraceId: Id_5, + SpanId: Id_41, + Name: http.request, + Resource: GET, + Service: Samples.WebRequest-http-client, + Type: http, + ParentId: Id_12, + Tags: { + component: HttpMessageHandler, + env: integration_tests, + http-client-handler-type: System.Net.Http.SocketsHttpHandler, + http.request.method: GET, + http.response.status_code: 200, + language: dotnet, + runtime-id: Guid_1, + server.address: localhost, + server.port: 8080, + span.kind: client, + url.full: http://localhost:00000/Guid_2/?DownloadFileTaskAsync, + _dd.base_service: Samples.WebRequest, + _dd.svc_src: http-client + }, + Metrics: { + _dd.top_level: 1.0 + } + }, + { + TraceId: Id_5, + SpanId: Id_42, + Name: http.request, + Resource: GET, + Service: Samples.WebRequest-http-client, + Type: http, + ParentId: Id_12, + Tags: { + component: HttpMessageHandler, + env: integration_tests, + http-client-handler-type: System.Net.Http.SocketsHttpHandler, + http.request.method: GET, + http.response.status_code: 200, + language: dotnet, + runtime-id: Guid_1, + server.address: localhost, + server.port: 8080, + span.kind: client, + url.full: http://localhost:00000/Guid_2/?DownloadFileTaskAsync2, + _dd.base_service: Samples.WebRequest, + _dd.svc_src: http-client + }, + Metrics: { + _dd.top_level: 1.0 + } + }, + { + TraceId: Id_5, + SpanId: Id_43, + Name: http.request, + Resource: GET, + Service: Samples.WebRequest-http-client, + Type: http, + ParentId: Id_13, + Tags: { + component: WebRequest, + env: integration_tests, + http.request.method: GET, + http.response.status_code: 200, + language: dotnet, + runtime-id: Guid_1, + server.address: localhost, + server.port: 8080, + span.kind: client, + url.full: http://localhost:00000/Guid_2/?DownloadString, + _dd.base_service: Samples.WebRequest, + _dd.svc_src: http-client + }, + Metrics: { + _dd.top_level: 1.0 + } + }, + { + TraceId: Id_5, + SpanId: Id_44, + Name: http.request, + Resource: GET, + Service: Samples.WebRequest-http-client, + Type: http, + ParentId: Id_13, + Tags: { + component: WebRequest, + env: integration_tests, + http.request.method: GET, + http.response.status_code: 200, + language: dotnet, + runtime-id: Guid_1, + server.address: localhost, + server.port: 8080, + span.kind: client, + url.full: http://localhost:00000/Guid_2/?DownloadString2, + _dd.base_service: Samples.WebRequest, + _dd.svc_src: http-client + }, + Metrics: { + _dd.top_level: 1.0 + } + }, + { + TraceId: Id_5, + SpanId: Id_45, + Name: http.request, + Resource: GET, + Service: Samples.WebRequest-http-client, + Type: http, + ParentId: Id_14, + Tags: { + component: HttpMessageHandler, + env: integration_tests, + http-client-handler-type: System.Net.Http.SocketsHttpHandler, + http.request.method: GET, + http.response.status_code: 200, + language: dotnet, + runtime-id: Guid_1, + server.address: localhost, + server.port: 8080, + span.kind: client, + url.full: http://localhost:00000/Guid_2/?DownloadStringAsync, + _dd.base_service: Samples.WebRequest, + _dd.svc_src: http-client + }, + Metrics: { + _dd.top_level: 1.0 + } + }, + { + TraceId: Id_5, + SpanId: Id_46, + Name: http.request, + Resource: GET, + Service: Samples.WebRequest-http-client, + Type: http, + ParentId: Id_14, + Tags: { + component: HttpMessageHandler, + env: integration_tests, + http-client-handler-type: System.Net.Http.SocketsHttpHandler, + http.request.method: GET, + http.response.status_code: 200, + language: dotnet, + runtime-id: Guid_1, + server.address: localhost, + server.port: 8080, + span.kind: client, + url.full: http://localhost:00000/Guid_2/?DownloadStringAsync2, + _dd.base_service: Samples.WebRequest, + _dd.svc_src: http-client + }, + Metrics: { + _dd.top_level: 1.0 + } + }, + { + TraceId: Id_5, + SpanId: Id_47, + Name: http.request, + Resource: GET, + Service: Samples.WebRequest-http-client, + Type: http, + ParentId: Id_15, + Tags: { + component: HttpMessageHandler, + env: integration_tests, + http-client-handler-type: System.Net.Http.SocketsHttpHandler, + http.request.method: GET, + http.response.status_code: 200, + language: dotnet, + runtime-id: Guid_1, + server.address: localhost, + server.port: 8080, + span.kind: client, + url.full: http://localhost:00000/Guid_2/?DownloadStringTaskAsync, + _dd.base_service: Samples.WebRequest, + _dd.svc_src: http-client + }, + Metrics: { + _dd.top_level: 1.0 + } + }, + { + TraceId: Id_5, + SpanId: Id_48, + Name: http.request, + Resource: GET, + Service: Samples.WebRequest-http-client, + Type: http, + ParentId: Id_15, + Tags: { + component: HttpMessageHandler, + env: integration_tests, + http-client-handler-type: System.Net.Http.SocketsHttpHandler, + http.request.method: GET, + http.response.status_code: 200, + language: dotnet, + runtime-id: Guid_1, + server.address: localhost, + server.port: 8080, + span.kind: client, + url.full: http://localhost:00000/Guid_2/?DownloadStringTaskAsync2, + _dd.base_service: Samples.WebRequest, + _dd.svc_src: http-client + }, + Metrics: { + _dd.top_level: 1.0 + } + }, + { + TraceId: Id_5, + SpanId: Id_49, + Name: http.request, + Resource: GET, + Service: Samples.WebRequest-http-client, + Type: http, + ParentId: Id_16, + Tags: { + component: WebRequest, + env: integration_tests, + http.request.method: GET, + http.response.status_code: 200, + language: dotnet, + runtime-id: Guid_1, + server.address: localhost, + server.port: 8080, + span.kind: client, + url.full: http://localhost:00000/Guid_2/?OpenRead, + _dd.base_service: Samples.WebRequest, + _dd.svc_src: http-client + }, + Metrics: { + _dd.top_level: 1.0 + } + }, + { + TraceId: Id_5, + SpanId: Id_50, + Name: http.request, + Resource: GET, + Service: Samples.WebRequest-http-client, + Type: http, + ParentId: Id_16, + Tags: { + component: WebRequest, + env: integration_tests, + http.request.method: GET, + http.response.status_code: 200, + language: dotnet, + runtime-id: Guid_1, + server.address: localhost, + server.port: 8080, + span.kind: client, + url.full: http://localhost:00000/Guid_2/?OpenRead2, + _dd.base_service: Samples.WebRequest, + _dd.svc_src: http-client + }, + Metrics: { + _dd.top_level: 1.0 + } + }, + { + TraceId: Id_5, + SpanId: Id_51, + Name: http.request, + Resource: GET, + Service: Samples.WebRequest-http-client, + Type: http, + ParentId: Id_17, + Tags: { + component: HttpMessageHandler, + env: integration_tests, + http-client-handler-type: System.Net.Http.SocketsHttpHandler, + http.request.method: GET, + http.response.status_code: 200, + language: dotnet, + runtime-id: Guid_1, + server.address: localhost, + server.port: 8080, + span.kind: client, + url.full: http://localhost:00000/Guid_2/?OpenReadAsync, + _dd.base_service: Samples.WebRequest, + _dd.svc_src: http-client + }, + Metrics: { + _dd.top_level: 1.0 + } + }, + { + TraceId: Id_5, + SpanId: Id_52, + Name: http.request, + Resource: GET, + Service: Samples.WebRequest-http-client, + Type: http, + ParentId: Id_17, + Tags: { + component: HttpMessageHandler, + env: integration_tests, + http-client-handler-type: System.Net.Http.SocketsHttpHandler, + http.request.method: GET, + http.response.status_code: 200, + language: dotnet, + runtime-id: Guid_1, + server.address: localhost, + server.port: 8080, + span.kind: client, + url.full: http://localhost:00000/Guid_2/?OpenReadAsync2, + _dd.base_service: Samples.WebRequest, + _dd.svc_src: http-client + }, + Metrics: { + _dd.top_level: 1.0 + } + }, + { + TraceId: Id_5, + SpanId: Id_53, + Name: http.request, + Resource: GET, + Service: Samples.WebRequest-http-client, + Type: http, + ParentId: Id_18, + Tags: { + component: HttpMessageHandler, + env: integration_tests, + http-client-handler-type: System.Net.Http.SocketsHttpHandler, + http.request.method: GET, + http.response.status_code: 200, + language: dotnet, + runtime-id: Guid_1, + server.address: localhost, + server.port: 8080, + span.kind: client, + url.full: http://localhost:00000/Guid_2/?OpenReadTaskAsync, + _dd.base_service: Samples.WebRequest, + _dd.svc_src: http-client + }, + Metrics: { + _dd.top_level: 1.0 + } + }, + { + TraceId: Id_5, + SpanId: Id_54, + Name: http.request, + Resource: GET, + Service: Samples.WebRequest-http-client, + Type: http, + ParentId: Id_18, + Tags: { + component: HttpMessageHandler, + env: integration_tests, + http-client-handler-type: System.Net.Http.SocketsHttpHandler, + http.request.method: GET, + http.response.status_code: 200, + language: dotnet, + runtime-id: Guid_1, + server.address: localhost, + server.port: 8080, + span.kind: client, + url.full: http://localhost:00000/Guid_2/?OpenReadTaskAsync2, + _dd.base_service: Samples.WebRequest, + _dd.svc_src: http-client + }, + Metrics: { + _dd.top_level: 1.0 + } + }, + { + TraceId: Id_5, + SpanId: Id_55, + Name: http.request, + Resource: POST, + Service: Samples.WebRequest-http-client, + Type: http, + ParentId: Id_19, + Tags: { + component: WebRequest, + env: integration_tests, + http.request.method: POST, + http.response.status_code: 200, + language: dotnet, + runtime-id: Guid_1, + server.address: localhost, + server.port: 8080, + span.kind: client, + url.full: http://localhost:00000/Guid_2/?UploadData, + _dd.base_service: Samples.WebRequest, + _dd.svc_src: http-client + }, + Metrics: { + _dd.top_level: 1.0 + } + }, + { + TraceId: Id_5, + SpanId: Id_56, + Name: http.request, + Resource: POST, + Service: Samples.WebRequest-http-client, + Type: http, + ParentId: Id_19, + Tags: { + component: WebRequest, + env: integration_tests, + http.request.method: POST, + http.response.status_code: 200, + language: dotnet, + runtime-id: Guid_1, + server.address: localhost, + server.port: 8080, + span.kind: client, + url.full: http://localhost:00000/Guid_2/?UploadData2, + _dd.base_service: Samples.WebRequest, + _dd.svc_src: http-client + }, + Metrics: { + _dd.top_level: 1.0 + } + }, + { + TraceId: Id_5, + SpanId: Id_57, + Name: http.request, + Resource: POST, + Service: Samples.WebRequest-http-client, + Type: http, + ParentId: Id_19, + Tags: { + component: WebRequest, + env: integration_tests, + http.request.method: POST, + http.response.status_code: 200, + language: dotnet, + runtime-id: Guid_1, + server.address: localhost, + server.port: 8080, + span.kind: client, + url.full: http://localhost:00000/Guid_2/?UploadData3, + _dd.base_service: Samples.WebRequest, + _dd.svc_src: http-client + }, + Metrics: { + _dd.top_level: 1.0 + } + }, + { + TraceId: Id_5, + SpanId: Id_58, + Name: http.request, + Resource: POST, + Service: Samples.WebRequest-http-client, + Type: http, + ParentId: Id_19, + Tags: { + component: WebRequest, + env: integration_tests, + http.request.method: POST, + http.response.status_code: 200, + language: dotnet, + runtime-id: Guid_1, + server.address: localhost, + server.port: 8080, + span.kind: client, + url.full: http://localhost:00000/Guid_2/?UploadData4, + _dd.base_service: Samples.WebRequest, + _dd.svc_src: http-client + }, + Metrics: { + _dd.top_level: 1.0 + } + }, + { + TraceId: Id_5, + SpanId: Id_59, + Name: http.request, + Resource: POST, + Service: Samples.WebRequest-http-client, + Type: http, + ParentId: Id_20, + Tags: { + component: HttpMessageHandler, + env: integration_tests, + http-client-handler-type: System.Net.Http.SocketsHttpHandler, + http.request.method: POST, + http.response.status_code: 200, + language: dotnet, + runtime-id: Guid_1, + server.address: localhost, + server.port: 8080, + span.kind: client, + url.full: http://localhost:00000/Guid_2/?UploadDataAsync, + _dd.base_service: Samples.WebRequest, + _dd.svc_src: http-client + }, + Metrics: { + _dd.top_level: 1.0 + } + }, + { + TraceId: Id_5, + SpanId: Id_60, + Name: http.request, + Resource: POST, + Service: Samples.WebRequest-http-client, + Type: http, + ParentId: Id_20, + Tags: { + component: HttpMessageHandler, + env: integration_tests, + http-client-handler-type: System.Net.Http.SocketsHttpHandler, + http.request.method: POST, + http.response.status_code: 200, + language: dotnet, + runtime-id: Guid_1, + server.address: localhost, + server.port: 8080, + span.kind: client, + url.full: http://localhost:00000/Guid_2/?UploadDataAsync2, + _dd.base_service: Samples.WebRequest, + _dd.svc_src: http-client + }, + Metrics: { + _dd.top_level: 1.0 + } + }, + { + TraceId: Id_5, + SpanId: Id_61, + Name: http.request, + Resource: POST, + Service: Samples.WebRequest-http-client, + Type: http, + ParentId: Id_20, + Tags: { + component: HttpMessageHandler, + env: integration_tests, + http-client-handler-type: System.Net.Http.SocketsHttpHandler, + http.request.method: POST, + http.response.status_code: 200, + language: dotnet, + runtime-id: Guid_1, + server.address: localhost, + server.port: 8080, + span.kind: client, + url.full: http://localhost:00000/Guid_2/?UploadDataAsync3, + _dd.base_service: Samples.WebRequest, + _dd.svc_src: http-client + }, + Metrics: { + _dd.top_level: 1.0 + } + }, + { + TraceId: Id_5, + SpanId: Id_62, + Name: http.request, + Resource: POST, + Service: Samples.WebRequest-http-client, + Type: http, + ParentId: Id_21, + Tags: { + component: HttpMessageHandler, + env: integration_tests, + http-client-handler-type: System.Net.Http.SocketsHttpHandler, + http.request.method: POST, + http.response.status_code: 200, + language: dotnet, + runtime-id: Guid_1, + server.address: localhost, + server.port: 8080, + span.kind: client, + url.full: http://localhost:00000/Guid_2/?UploadDataTaskAsync, + _dd.base_service: Samples.WebRequest, + _dd.svc_src: http-client + }, + Metrics: { + _dd.top_level: 1.0 + } + }, + { + TraceId: Id_5, + SpanId: Id_63, + Name: http.request, + Resource: POST, + Service: Samples.WebRequest-http-client, + Type: http, + ParentId: Id_21, + Tags: { + component: HttpMessageHandler, + env: integration_tests, + http-client-handler-type: System.Net.Http.SocketsHttpHandler, + http.request.method: POST, + http.response.status_code: 200, + language: dotnet, + runtime-id: Guid_1, + server.address: localhost, + server.port: 8080, + span.kind: client, + url.full: http://localhost:00000/Guid_2/?UploadDataTaskAsync2, + _dd.base_service: Samples.WebRequest, + _dd.svc_src: http-client + }, + Metrics: { + _dd.top_level: 1.0 + } + }, + { + TraceId: Id_5, + SpanId: Id_64, + Name: http.request, + Resource: POST, + Service: Samples.WebRequest-http-client, + Type: http, + ParentId: Id_21, + Tags: { + component: HttpMessageHandler, + env: integration_tests, + http-client-handler-type: System.Net.Http.SocketsHttpHandler, + http.request.method: POST, + http.response.status_code: 200, + language: dotnet, + runtime-id: Guid_1, + server.address: localhost, + server.port: 8080, + span.kind: client, + url.full: http://localhost:00000/Guid_2/?UploadDataTaskAsync3, + _dd.base_service: Samples.WebRequest, + _dd.svc_src: http-client + }, + Metrics: { + _dd.top_level: 1.0 + } + }, + { + TraceId: Id_5, + SpanId: Id_65, + Name: http.request, + Resource: POST, + Service: Samples.WebRequest-http-client, + Type: http, + ParentId: Id_21, + Tags: { + component: HttpMessageHandler, + env: integration_tests, + http-client-handler-type: System.Net.Http.SocketsHttpHandler, + http.request.method: POST, + http.response.status_code: 200, + language: dotnet, + runtime-id: Guid_1, + server.address: localhost, + server.port: 8080, + span.kind: client, + url.full: http://localhost:00000/Guid_2/?UploadDataTaskAsync4, + _dd.base_service: Samples.WebRequest, + _dd.svc_src: http-client + }, + Metrics: { + _dd.top_level: 1.0 + } + }, + { + TraceId: Id_5, + SpanId: Id_66, + Name: http.request, + Resource: POST, + Service: Samples.WebRequest-http-client, + Type: http, + ParentId: Id_22, + Tags: { + component: WebRequest, + env: integration_tests, + http.request.method: POST, + http.response.status_code: 200, + language: dotnet, + runtime-id: Guid_1, + server.address: localhost, + server.port: 8080, + span.kind: client, + url.full: http://localhost:00000/Guid_2/?UploadFile, + _dd.base_service: Samples.WebRequest, + _dd.svc_src: http-client + }, + Metrics: { + _dd.top_level: 1.0 + } + }, + { + TraceId: Id_5, + SpanId: Id_67, + Name: http.request, + Resource: POST, + Service: Samples.WebRequest-http-client, + Type: http, + ParentId: Id_22, + Tags: { + component: WebRequest, + env: integration_tests, + http.request.method: POST, + http.response.status_code: 200, + language: dotnet, + runtime-id: Guid_1, + server.address: localhost, + server.port: 8080, + span.kind: client, + url.full: http://localhost:00000/Guid_2/?UploadFile2, + _dd.base_service: Samples.WebRequest, + _dd.svc_src: http-client + }, + Metrics: { + _dd.top_level: 1.0 + } + }, + { + TraceId: Id_5, + SpanId: Id_68, + Name: http.request, + Resource: POST, + Service: Samples.WebRequest-http-client, + Type: http, + ParentId: Id_22, + Tags: { + component: WebRequest, + env: integration_tests, + http.request.method: POST, + http.response.status_code: 200, + language: dotnet, + runtime-id: Guid_1, + server.address: localhost, + server.port: 8080, + span.kind: client, + url.full: http://localhost:00000/Guid_2/?UploadFile3, + _dd.base_service: Samples.WebRequest, + _dd.svc_src: http-client + }, + Metrics: { + _dd.top_level: 1.0 + } + }, + { + TraceId: Id_5, + SpanId: Id_69, + Name: http.request, + Resource: POST, + Service: Samples.WebRequest-http-client, + Type: http, + ParentId: Id_22, + Tags: { + component: WebRequest, + env: integration_tests, + http.request.method: POST, + http.response.status_code: 200, + language: dotnet, + runtime-id: Guid_1, + server.address: localhost, + server.port: 8080, + span.kind: client, + url.full: http://localhost:00000/Guid_2/?UploadFile4, + _dd.base_service: Samples.WebRequest, + _dd.svc_src: http-client + }, + Metrics: { + _dd.top_level: 1.0 + } + }, + { + TraceId: Id_5, + SpanId: Id_70, + Name: http.request, + Resource: POST, + Service: Samples.WebRequest-http-client, + Type: http, + ParentId: Id_23, + Tags: { + component: HttpMessageHandler, + env: integration_tests, + http-client-handler-type: System.Net.Http.SocketsHttpHandler, + http.request.method: POST, + http.response.status_code: 200, + language: dotnet, + runtime-id: Guid_1, + server.address: localhost, + server.port: 8080, + span.kind: client, + url.full: http://localhost:00000/Guid_2/?UploadFileAsync, + _dd.base_service: Samples.WebRequest, + _dd.svc_src: http-client + }, + Metrics: { + _dd.top_level: 1.0 + } + }, + { + TraceId: Id_5, + SpanId: Id_71, + Name: http.request, + Resource: POST, + Service: Samples.WebRequest-http-client, + Type: http, + ParentId: Id_23, + Tags: { + component: HttpMessageHandler, + env: integration_tests, + http-client-handler-type: System.Net.Http.SocketsHttpHandler, + http.request.method: POST, + http.response.status_code: 200, + language: dotnet, + runtime-id: Guid_1, + server.address: localhost, + server.port: 8080, + span.kind: client, + url.full: http://localhost:00000/Guid_2/?UploadFileAsync2, + _dd.base_service: Samples.WebRequest, + _dd.svc_src: http-client + }, + Metrics: { + _dd.top_level: 1.0 + } + }, + { + TraceId: Id_5, + SpanId: Id_72, + Name: http.request, + Resource: POST, + Service: Samples.WebRequest-http-client, + Type: http, + ParentId: Id_23, + Tags: { + component: HttpMessageHandler, + env: integration_tests, + http-client-handler-type: System.Net.Http.SocketsHttpHandler, + http.request.method: POST, + http.response.status_code: 200, + language: dotnet, + runtime-id: Guid_1, + server.address: localhost, + server.port: 8080, + span.kind: client, + url.full: http://localhost:00000/Guid_2/?UploadFileAsync3, + _dd.base_service: Samples.WebRequest, + _dd.svc_src: http-client + }, + Metrics: { + _dd.top_level: 1.0 + } + }, + { + TraceId: Id_5, + SpanId: Id_73, + Name: http.request, + Resource: POST, + Service: Samples.WebRequest-http-client, + Type: http, + ParentId: Id_24, + Tags: { + component: HttpMessageHandler, + env: integration_tests, + http-client-handler-type: System.Net.Http.SocketsHttpHandler, + http.request.method: POST, + http.response.status_code: 200, + language: dotnet, + runtime-id: Guid_1, + server.address: localhost, + server.port: 8080, + span.kind: client, + url.full: http://localhost:00000/Guid_2/?UploadFileTaskAsync, + _dd.base_service: Samples.WebRequest, + _dd.svc_src: http-client + }, + Metrics: { + _dd.top_level: 1.0 + } + }, + { + TraceId: Id_5, + SpanId: Id_74, + Name: http.request, + Resource: POST, + Service: Samples.WebRequest-http-client, + Type: http, + ParentId: Id_24, + Tags: { + component: HttpMessageHandler, + env: integration_tests, + http-client-handler-type: System.Net.Http.SocketsHttpHandler, + http.request.method: POST, + http.response.status_code: 200, + language: dotnet, + runtime-id: Guid_1, + server.address: localhost, + server.port: 8080, + span.kind: client, + url.full: http://localhost:00000/Guid_2/?UploadFileTaskAsync2, + _dd.base_service: Samples.WebRequest, + _dd.svc_src: http-client + }, + Metrics: { + _dd.top_level: 1.0 + } + }, + { + TraceId: Id_5, + SpanId: Id_75, + Name: http.request, + Resource: POST, + Service: Samples.WebRequest-http-client, + Type: http, + ParentId: Id_24, + Tags: { + component: HttpMessageHandler, + env: integration_tests, + http-client-handler-type: System.Net.Http.SocketsHttpHandler, + http.request.method: POST, + http.response.status_code: 200, + language: dotnet, + runtime-id: Guid_1, + server.address: localhost, + server.port: 8080, + span.kind: client, + url.full: http://localhost:00000/Guid_2/?UploadFileTaskAsync3, + _dd.base_service: Samples.WebRequest, + _dd.svc_src: http-client + }, + Metrics: { + _dd.top_level: 1.0 + } + }, + { + TraceId: Id_5, + SpanId: Id_76, + Name: http.request, + Resource: POST, + Service: Samples.WebRequest-http-client, + Type: http, + ParentId: Id_24, + Tags: { + component: HttpMessageHandler, + env: integration_tests, + http-client-handler-type: System.Net.Http.SocketsHttpHandler, + http.request.method: POST, + http.response.status_code: 200, + language: dotnet, + runtime-id: Guid_1, + server.address: localhost, + server.port: 8080, + span.kind: client, + url.full: http://localhost:00000/Guid_2/?UploadFileTaskAsync4, + _dd.base_service: Samples.WebRequest, + _dd.svc_src: http-client + }, + Metrics: { + _dd.top_level: 1.0 + } + }, + { + TraceId: Id_5, + SpanId: Id_77, + Name: http.request, + Resource: POST, + Service: Samples.WebRequest-http-client, + Type: http, + ParentId: Id_25, + Tags: { + component: WebRequest, + env: integration_tests, + http.request.method: POST, + http.response.status_code: 200, + language: dotnet, + runtime-id: Guid_1, + server.address: localhost, + server.port: 8080, + span.kind: client, + url.full: http://localhost:00000/Guid_2/?UploadString, + _dd.base_service: Samples.WebRequest, + _dd.svc_src: http-client + }, + Metrics: { + _dd.top_level: 1.0 + } + }, + { + TraceId: Id_5, + SpanId: Id_78, + Name: http.request, + Resource: POST, + Service: Samples.WebRequest-http-client, + Type: http, + ParentId: Id_25, + Tags: { + component: WebRequest, + env: integration_tests, + http.request.method: POST, + http.response.status_code: 200, + language: dotnet, + runtime-id: Guid_1, + server.address: localhost, + server.port: 8080, + span.kind: client, + url.full: http://localhost:00000/Guid_2/?UploadString2, + _dd.base_service: Samples.WebRequest, + _dd.svc_src: http-client + }, + Metrics: { + _dd.top_level: 1.0 + } + }, + { + TraceId: Id_5, + SpanId: Id_79, + Name: http.request, + Resource: POST, + Service: Samples.WebRequest-http-client, + Type: http, + ParentId: Id_25, + Tags: { + component: WebRequest, + env: integration_tests, + http.request.method: POST, + http.response.status_code: 200, + language: dotnet, + runtime-id: Guid_1, + server.address: localhost, + server.port: 8080, + span.kind: client, + url.full: http://localhost:00000/Guid_2/?UploadString3, + _dd.base_service: Samples.WebRequest, + _dd.svc_src: http-client + }, + Metrics: { + _dd.top_level: 1.0 + } + }, + { + TraceId: Id_5, + SpanId: Id_80, + Name: http.request, + Resource: POST, + Service: Samples.WebRequest-http-client, + Type: http, + ParentId: Id_25, + Tags: { + component: WebRequest, + env: integration_tests, + http.request.method: POST, + http.response.status_code: 200, + language: dotnet, + runtime-id: Guid_1, + server.address: localhost, + server.port: 8080, + span.kind: client, + url.full: http://localhost:00000/Guid_2/?UploadString4, + _dd.base_service: Samples.WebRequest, + _dd.svc_src: http-client + }, + Metrics: { + _dd.top_level: 1.0 + } + }, + { + TraceId: Id_5, + SpanId: Id_81, + Name: http.request, + Resource: POST, + Service: Samples.WebRequest-http-client, + Type: http, + ParentId: Id_26, + Tags: { + component: HttpMessageHandler, + env: integration_tests, + http-client-handler-type: System.Net.Http.SocketsHttpHandler, + http.request.method: POST, + http.response.status_code: 200, + language: dotnet, + runtime-id: Guid_1, + server.address: localhost, + server.port: 8080, + span.kind: client, + url.full: http://localhost:00000/Guid_2/?UploadStringAsync, + _dd.base_service: Samples.WebRequest, + _dd.svc_src: http-client + }, + Metrics: { + _dd.top_level: 1.0 + } + }, + { + TraceId: Id_5, + SpanId: Id_82, + Name: http.request, + Resource: POST, + Service: Samples.WebRequest-http-client, + Type: http, + ParentId: Id_26, + Tags: { + component: HttpMessageHandler, + env: integration_tests, + http-client-handler-type: System.Net.Http.SocketsHttpHandler, + http.request.method: POST, + http.response.status_code: 200, + language: dotnet, + runtime-id: Guid_1, + server.address: localhost, + server.port: 8080, + span.kind: client, + url.full: http://localhost:00000/Guid_2/?UploadStringAsync2, + _dd.base_service: Samples.WebRequest, + _dd.svc_src: http-client + }, + Metrics: { + _dd.top_level: 1.0 + } + }, + { + TraceId: Id_5, + SpanId: Id_83, + Name: http.request, + Resource: POST, + Service: Samples.WebRequest-http-client, + Type: http, + ParentId: Id_26, + Tags: { + component: HttpMessageHandler, + env: integration_tests, + http-client-handler-type: System.Net.Http.SocketsHttpHandler, + http.request.method: POST, + http.response.status_code: 200, + language: dotnet, + runtime-id: Guid_1, + server.address: localhost, + server.port: 8080, + span.kind: client, + url.full: http://localhost:00000/Guid_2/?UploadStringAsync3, + _dd.base_service: Samples.WebRequest, + _dd.svc_src: http-client + }, + Metrics: { + _dd.top_level: 1.0 + } + }, + { + TraceId: Id_5, + SpanId: Id_84, + Name: http.request, + Resource: POST, + Service: Samples.WebRequest-http-client, + Type: http, + ParentId: Id_27, + Tags: { + component: HttpMessageHandler, + env: integration_tests, + http-client-handler-type: System.Net.Http.SocketsHttpHandler, + http.request.method: POST, + http.response.status_code: 200, + language: dotnet, + runtime-id: Guid_1, + server.address: localhost, + server.port: 8080, + span.kind: client, + url.full: http://localhost:00000/Guid_2/?UploadStringTaskAsync, + _dd.base_service: Samples.WebRequest, + _dd.svc_src: http-client + }, + Metrics: { + _dd.top_level: 1.0 + } + }, + { + TraceId: Id_5, + SpanId: Id_85, + Name: http.request, + Resource: POST, + Service: Samples.WebRequest-http-client, + Type: http, + ParentId: Id_27, + Tags: { + component: HttpMessageHandler, + env: integration_tests, + http-client-handler-type: System.Net.Http.SocketsHttpHandler, + http.request.method: POST, + http.response.status_code: 200, + language: dotnet, + runtime-id: Guid_1, + server.address: localhost, + server.port: 8080, + span.kind: client, + url.full: http://localhost:00000/Guid_2/?UploadStringTaskAsync2, + _dd.base_service: Samples.WebRequest, + _dd.svc_src: http-client + }, + Metrics: { + _dd.top_level: 1.0 + } + }, + { + TraceId: Id_5, + SpanId: Id_86, + Name: http.request, + Resource: POST, + Service: Samples.WebRequest-http-client, + Type: http, + ParentId: Id_27, + Tags: { + component: HttpMessageHandler, + env: integration_tests, + http-client-handler-type: System.Net.Http.SocketsHttpHandler, + http.request.method: POST, + http.response.status_code: 200, + language: dotnet, + runtime-id: Guid_1, + server.address: localhost, + server.port: 8080, + span.kind: client, + url.full: http://localhost:00000/Guid_2/?UploadStringTaskAsync3, + _dd.base_service: Samples.WebRequest, + _dd.svc_src: http-client + }, + Metrics: { + _dd.top_level: 1.0 + } + }, + { + TraceId: Id_5, + SpanId: Id_87, + Name: http.request, + Resource: POST, + Service: Samples.WebRequest-http-client, + Type: http, + ParentId: Id_27, + Tags: { + component: HttpMessageHandler, + env: integration_tests, + http-client-handler-type: System.Net.Http.SocketsHttpHandler, + http.request.method: POST, + http.response.status_code: 200, + language: dotnet, + runtime-id: Guid_1, + server.address: localhost, + server.port: 8080, + span.kind: client, + url.full: http://localhost:00000/Guid_2/?UploadStringTaskAsync4, + _dd.base_service: Samples.WebRequest, + _dd.svc_src: http-client + }, + Metrics: { + _dd.top_level: 1.0 + } + }, + { + TraceId: Id_5, + SpanId: Id_88, + Name: http.request, + Resource: POST, + Service: Samples.WebRequest-http-client, + Type: http, + ParentId: Id_28, + Tags: { + component: WebRequest, + env: integration_tests, + http.request.method: POST, + http.response.status_code: 200, + language: dotnet, + runtime-id: Guid_1, + server.address: localhost, + server.port: 8080, + span.kind: client, + url.full: http://localhost:00000/Guid_2/?UploadValues, + _dd.base_service: Samples.WebRequest, + _dd.svc_src: http-client + }, + Metrics: { + _dd.top_level: 1.0 + } + }, + { + TraceId: Id_5, + SpanId: Id_89, + Name: http.request, + Resource: POST, + Service: Samples.WebRequest-http-client, + Type: http, + ParentId: Id_28, + Tags: { + component: WebRequest, + env: integration_tests, + http.request.method: POST, + http.response.status_code: 200, + language: dotnet, + runtime-id: Guid_1, + server.address: localhost, + server.port: 8080, + span.kind: client, + url.full: http://localhost:00000/Guid_2/?UploadValues2, + _dd.base_service: Samples.WebRequest, + _dd.svc_src: http-client + }, + Metrics: { + _dd.top_level: 1.0 + } + }, + { + TraceId: Id_5, + SpanId: Id_90, + Name: http.request, + Resource: POST, + Service: Samples.WebRequest-http-client, + Type: http, + ParentId: Id_28, + Tags: { + component: WebRequest, + env: integration_tests, + http.request.method: POST, + http.response.status_code: 200, + language: dotnet, + runtime-id: Guid_1, + server.address: localhost, + server.port: 8080, + span.kind: client, + url.full: http://localhost:00000/Guid_2/?UploadValues3, + _dd.base_service: Samples.WebRequest, + _dd.svc_src: http-client + }, + Metrics: { + _dd.top_level: 1.0 + } + }, + { + TraceId: Id_5, + SpanId: Id_91, + Name: http.request, + Resource: POST, + Service: Samples.WebRequest-http-client, + Type: http, + ParentId: Id_28, + Tags: { + component: WebRequest, + env: integration_tests, + http.request.method: POST, + http.response.status_code: 200, + language: dotnet, + runtime-id: Guid_1, + server.address: localhost, + server.port: 8080, + span.kind: client, + url.full: http://localhost:00000/Guid_2/?UploadValues4, + _dd.base_service: Samples.WebRequest, + _dd.svc_src: http-client + }, + Metrics: { + _dd.top_level: 1.0 + } + }, + { + TraceId: Id_5, + SpanId: Id_92, + Name: http.request, + Resource: POST, + Service: Samples.WebRequest-http-client, + Type: http, + ParentId: Id_29, + Tags: { + component: HttpMessageHandler, + env: integration_tests, + http-client-handler-type: System.Net.Http.SocketsHttpHandler, + http.request.method: POST, + http.response.status_code: 200, + language: dotnet, + runtime-id: Guid_1, + server.address: localhost, + server.port: 8080, + span.kind: client, + url.full: http://localhost:00000/Guid_2/?UploadValuesAsync, + _dd.base_service: Samples.WebRequest, + _dd.svc_src: http-client + }, + Metrics: { + _dd.top_level: 1.0 + } + }, + { + TraceId: Id_5, + SpanId: Id_93, + Name: http.request, + Resource: POST, + Service: Samples.WebRequest-http-client, + Type: http, + ParentId: Id_29, + Tags: { + component: HttpMessageHandler, + env: integration_tests, + http-client-handler-type: System.Net.Http.SocketsHttpHandler, + http.request.method: POST, + http.response.status_code: 200, + language: dotnet, + runtime-id: Guid_1, + server.address: localhost, + server.port: 8080, + span.kind: client, + url.full: http://localhost:00000/Guid_2/?UploadValuesAsync2, + _dd.base_service: Samples.WebRequest, + _dd.svc_src: http-client + }, + Metrics: { + _dd.top_level: 1.0 + } + }, + { + TraceId: Id_5, + SpanId: Id_94, + Name: http.request, + Resource: POST, + Service: Samples.WebRequest-http-client, + Type: http, + ParentId: Id_29, + Tags: { + component: HttpMessageHandler, + env: integration_tests, + http-client-handler-type: System.Net.Http.SocketsHttpHandler, + http.request.method: POST, + http.response.status_code: 200, + language: dotnet, + runtime-id: Guid_1, + server.address: localhost, + server.port: 8080, + span.kind: client, + url.full: http://localhost:00000/Guid_2/?UploadValuesAsync3, + _dd.base_service: Samples.WebRequest, + _dd.svc_src: http-client + }, + Metrics: { + _dd.top_level: 1.0 + } + }, + { + TraceId: Id_5, + SpanId: Id_95, + Name: http.request, + Resource: POST, + Service: Samples.WebRequest-http-client, + Type: http, + ParentId: Id_30, + Tags: { + component: HttpMessageHandler, + env: integration_tests, + http-client-handler-type: System.Net.Http.SocketsHttpHandler, + http.request.method: POST, + http.response.status_code: 200, + language: dotnet, + runtime-id: Guid_1, + server.address: localhost, + server.port: 8080, + span.kind: client, + url.full: http://localhost:00000/Guid_2/?UploadValuesTaskAsync, + _dd.base_service: Samples.WebRequest, + _dd.svc_src: http-client + }, + Metrics: { + _dd.top_level: 1.0 + } + }, + { + TraceId: Id_5, + SpanId: Id_96, + Name: http.request, + Resource: POST, + Service: Samples.WebRequest-http-client, + Type: http, + ParentId: Id_30, + Tags: { + component: HttpMessageHandler, + env: integration_tests, + http-client-handler-type: System.Net.Http.SocketsHttpHandler, + http.request.method: POST, + http.response.status_code: 200, + language: dotnet, + runtime-id: Guid_1, + server.address: localhost, + server.port: 8080, + span.kind: client, + url.full: http://localhost:00000/Guid_2/?UploadValuesTaskAsync2, + _dd.base_service: Samples.WebRequest, + _dd.svc_src: http-client + }, + Metrics: { + _dd.top_level: 1.0 + } + }, + { + TraceId: Id_5, + SpanId: Id_97, + Name: http.request, + Resource: POST, + Service: Samples.WebRequest-http-client, + Type: http, + ParentId: Id_30, + Tags: { + component: HttpMessageHandler, + env: integration_tests, + http-client-handler-type: System.Net.Http.SocketsHttpHandler, + http.request.method: POST, + http.response.status_code: 200, + language: dotnet, + runtime-id: Guid_1, + server.address: localhost, + server.port: 8080, + span.kind: client, + url.full: http://localhost:00000/Guid_2/?UploadValuesTaskAsync3, + _dd.base_service: Samples.WebRequest, + _dd.svc_src: http-client + }, + Metrics: { + _dd.top_level: 1.0 + } + }, + { + TraceId: Id_5, + SpanId: Id_98, + Name: http.request, + Resource: POST, + Service: Samples.WebRequest-http-client, + Type: http, + ParentId: Id_30, + Tags: { + component: HttpMessageHandler, + env: integration_tests, + http-client-handler-type: System.Net.Http.SocketsHttpHandler, + http.request.method: POST, + http.response.status_code: 200, + language: dotnet, + runtime-id: Guid_1, + server.address: localhost, + server.port: 8080, + span.kind: client, + url.full: http://localhost:00000/Guid_2/?UploadValuesTaskAsync4, + _dd.base_service: Samples.WebRequest, + _dd.svc_src: http-client + }, + Metrics: { + _dd.top_level: 1.0 + } + }, + { + TraceId: Id_99, + SpanId: Id_100, + Name: internal, + Resource: WebRequest, + Service: Samples.WebRequest, + Type: custom, + Tags: { + env: integration_tests, + language: dotnet, + otel.status_code: STATUS_CODE_UNSET, + runtime-id: Guid_1, + span.kind: internal, + version: 1.0.0 + }, + Metrics: { + process_id: 0, + _dd.top_level: 1.0, + _dd.tracer_kr: 1.0, + _sampling_priority_v1: 1.0 + } + }, + { + TraceId: Id_99, + SpanId: Id_101, + Name: internal, + Resource: GetResponse, + Service: Samples.WebRequest, + Type: custom, + ParentId: Id_100, + Tags: { + env: integration_tests, + language: dotnet, + otel.status_code: STATUS_CODE_UNSET, + span.kind: internal, + version: 1.0.0 + } + }, + { + TraceId: Id_99, + SpanId: Id_102, + Name: internal, + Resource: GetResponseWithDistributedTracingHeaders, + Service: Samples.WebRequest, + Type: custom, + ParentId: Id_100, + Tags: { + env: integration_tests, + language: dotnet, + otel.status_code: STATUS_CODE_UNSET, + span.kind: internal, + version: 1.0.0 + } + }, + { + TraceId: Id_99, + SpanId: Id_103, + Name: internal, + Resource: GetResponseNotFound, + Service: Samples.WebRequest, + Type: custom, + ParentId: Id_100, + Tags: { + env: integration_tests, + language: dotnet, + otel.status_code: STATUS_CODE_UNSET, + span.kind: internal, + version: 1.0.0 + } + }, + { + TraceId: Id_99, + SpanId: Id_104, + Name: internal, + Resource: GetResponseTeapot, + Service: Samples.WebRequest, + Type: custom, + ParentId: Id_100, + Tags: { + env: integration_tests, + language: dotnet, + otel.status_code: STATUS_CODE_UNSET, + span.kind: internal, + version: 1.0.0 + } + }, + { + TraceId: Id_99, + SpanId: Id_105, + Name: internal, + Resource: GetResponseAsyncWithDistributedTracingHeaders, + Service: Samples.WebRequest, + Type: custom, + ParentId: Id_100, + Tags: { + env: integration_tests, + language: dotnet, + otel.status_code: STATUS_CODE_UNSET, + span.kind: internal, + version: 1.0.0 + } + }, + { + TraceId: Id_99, + SpanId: Id_106, + Name: internal, + Resource: GetResponseAsync, + Service: Samples.WebRequest, + Type: custom, + ParentId: Id_100, + Tags: { + env: integration_tests, + language: dotnet, + otel.status_code: STATUS_CODE_UNSET, + span.kind: internal, + version: 1.0.0 + } + }, + { + TraceId: Id_99, + SpanId: Id_107, + Name: internal, + Resource: GetResponseAsyncNotFound, + Service: Samples.WebRequest, + Type: custom, + ParentId: Id_100, + Tags: { + env: integration_tests, + language: dotnet, + otel.status_code: STATUS_CODE_UNSET, + span.kind: internal, + version: 1.0.0 + } + }, + { + TraceId: Id_99, + SpanId: Id_108, + Name: internal, + Resource: GetResponseAsyncTeapot, + Service: Samples.WebRequest, + Type: custom, + ParentId: Id_100, + Tags: { + env: integration_tests, + language: dotnet, + otel.status_code: STATUS_CODE_UNSET, + span.kind: internal, + version: 1.0.0 + } + }, + { + TraceId: Id_99, + SpanId: Id_109, + Name: internal, + Resource: GetRequestStream, + Service: Samples.WebRequest, + Type: custom, + ParentId: Id_100, + Tags: { + env: integration_tests, + language: dotnet, + otel.status_code: STATUS_CODE_UNSET, + span.kind: internal, + version: 1.0.0 + } + }, + { + TraceId: Id_99, + SpanId: Id_110, + Name: internal, + Resource: GetRequestStreamWithDistributedTracingHeaders, + Service: Samples.WebRequest, + Type: custom, + ParentId: Id_100, + Tags: { + env: integration_tests, + language: dotnet, + otel.status_code: STATUS_CODE_UNSET, + span.kind: internal, + version: 1.0.0 + } + }, + { + TraceId: Id_99, + SpanId: Id_111, + Name: internal, + Resource: BeginGetRequestStream, + Service: Samples.WebRequest, + Type: custom, + ParentId: Id_100, + Tags: { + env: integration_tests, + language: dotnet, + otel.status_code: STATUS_CODE_UNSET, + span.kind: internal, + version: 1.0.0 + } + }, + { + TraceId: Id_99, + SpanId: Id_112, + Name: internal, + Resource: BeginGetRequestStreamWithDistributedTracingHeaders, + Service: Samples.WebRequest, + Type: custom, + ParentId: Id_100, + Tags: { + env: integration_tests, + language: dotnet, + otel.status_code: STATUS_CODE_UNSET, + span.kind: internal, + version: 1.0.0 + } + }, + { + TraceId: Id_99, + SpanId: Id_113, + Name: internal, + Resource: BeginGetResponse, + Service: Samples.WebRequest, + Type: custom, + ParentId: Id_100, + Tags: { + env: integration_tests, + language: dotnet, + otel.status_code: STATUS_CODE_UNSET, + span.kind: internal, + version: 1.0.0 + } + }, + { + TraceId: Id_99, + SpanId: Id_114, + Name: internal, + Resource: BeginGetResponseWithDistributedTracingHeaders, + Service: Samples.WebRequest, + Type: custom, + ParentId: Id_100, + Tags: { + env: integration_tests, + language: dotnet, + otel.status_code: STATUS_CODE_UNSET, + span.kind: internal, + version: 1.0.0 + } + }, + { + TraceId: Id_99, + SpanId: Id_115, + Name: internal, + Resource: BeginGetResponseNotFound, + Service: Samples.WebRequest, + Type: custom, + ParentId: Id_100, + Tags: { + env: integration_tests, + language: dotnet, + otel.status_code: STATUS_CODE_UNSET, + span.kind: internal, + version: 1.0.0 + } + }, + { + TraceId: Id_99, + SpanId: Id_116, + Name: internal, + Resource: BeginGetResponseTeapot, + Service: Samples.WebRequest, + Type: custom, + ParentId: Id_100, + Tags: { + env: integration_tests, + language: dotnet, + otel.status_code: STATUS_CODE_UNSET, + span.kind: internal, + version: 1.0.0 + } + }, + { + TraceId: Id_99, + SpanId: Id_117, + Name: internal, + Resource: BeginGetResponse TaskFactoryFromAsync, + Service: Samples.WebRequest, + Type: custom, + ParentId: Id_100, + Tags: { + env: integration_tests, + language: dotnet, + otel.status_code: STATUS_CODE_UNSET, + span.kind: internal, + version: 1.0.0 + } + }, + { + TraceId: Id_99, + SpanId: Id_118, + Name: http.request, + Resource: GET, + Service: Samples.WebRequest-http-client, + Type: http, + ParentId: Id_101, + Tags: { + component: WebRequest, + env: integration_tests, + http.request.method: GET, + http.response.status_code: 200, + language: dotnet, + runtime-id: Guid_1, + server.address: localhost, + server.port: 8080, + span.kind: client, + url.full: http://localhost:00000/Guid_2/?GetResponse, + _dd.base_service: Samples.WebRequest, + _dd.svc_src: http-client + }, + Metrics: { + _dd.top_level: 1.0 + } + }, + { + TraceId: Id_99, + SpanId: Id_119, + Name: http.request, + Resource: GET, + Service: Samples.WebRequest-http-client, + Type: http, + ParentId: Id_101, + Tags: { + component: WebRequest, + env: integration_tests, + http.request.method: GET, + http.response.status_code: 200, + language: dotnet, + runtime-id: Guid_1, + server.address: localhost, + server.port: 8080, + span.kind: client, + url.full: http://localhost:00000/Guid_2/?GetResponse_NoBuffering, + _dd.base_service: Samples.WebRequest, + _dd.svc_src: http-client + }, + Metrics: { + _dd.top_level: 1.0 + } + }, + { + TraceId: Id_99, + SpanId: Id_120, + Name: http.request, + Resource: GET, + Service: Samples.WebRequest-http-client, + Type: http, + ParentId: Id_102, + Tags: { + component: WebRequest, + env: integration_tests, + http.request.method: GET, + http.response.status_code: 200, + language: dotnet, + runtime-id: Guid_1, + server.address: localhost, + server.port: 8080, + span.kind: client, + url.full: http://localhost:00000/Guid_2/?GetResponse, + _dd.base_service: Samples.WebRequest, + _dd.svc_src: http-client + }, + Metrics: { + _dd.top_level: 1.0 + } + }, + { + TraceId: Id_99, + SpanId: Id_121, + Name: http.request, + Resource: GET, + Service: Samples.WebRequest-http-client, + Type: http, + ParentId: Id_103, + Tags: { + component: WebRequest, + env: integration_tests, + events: [{"name":"exception","time_unix_nano":,"attributes":{"exception.type":"System.Net.WebException","exception.message":"The remote server returned an error: (404) Not Found.","exception.stacktrace":"System.Net.WebException: The remote server returned an error: (404) Not Found.\n at System.Net.HttpWebRequest.GetResponse()\n at Samples.WebRequest.RequestHelpers.SendWebRequestRequests(Boolean tracingDisabled, String url, String requestContent) in {SolutionDirectory}tracer/test/test-applications/integrations/Samples.WebRequest/RequestHelpers.cs:line 377"}}], + http.request.method: GET, + http.response.status_code: 404, + language: dotnet, + runtime-id: Guid_1, + server.address: localhost, + server.port: 8080, + span.kind: client, + url.full: http://localhost:00000/Guid_2/?GetResponseNotFound, + _dd.base_service: Samples.WebRequest, + _dd.svc_src: http-client + }, + Metrics: { + _dd.top_level: 1.0 + } + }, + { + TraceId: Id_99, + SpanId: Id_122, + Name: http.request, + Resource: GET, + Service: Samples.WebRequest-http-client, + Type: http, + ParentId: Id_104, + Error: 1, + Tags: { + component: WebRequest, + env: integration_tests, + error.msg: The HTTP response has status code 418., + events: [{"name":"exception","time_unix_nano":,"attributes":{"exception.type":"System.Net.WebException","exception.message":"The remote server returned an error: (418) .","exception.stacktrace":"System.Net.WebException: The remote server returned an error: (418) .\n at System.Net.HttpWebRequest.GetResponse()\n at Samples.WebRequest.RequestHelpers.SendWebRequestRequests(Boolean tracingDisabled, String url, String requestContent) in {SolutionDirectory}tracer/test/test-applications/integrations/Samples.WebRequest/RequestHelpers.cs:line 400"}}], + http.request.method: GET, + http.response.status_code: 418, + language: dotnet, + runtime-id: Guid_1, + server.address: localhost, + server.port: 8080, + span.kind: client, + url.full: http://localhost:00000/Guid_2/?GetResponseTeapot, + _dd.base_service: Samples.WebRequest, + _dd.svc_src: http-client + }, + Metrics: { + _dd.top_level: 1.0 + } + }, + { + TraceId: Id_99, + SpanId: Id_123, + Name: http.request, + Resource: GET, + Service: Samples.WebRequest-http-client, + Type: http, + ParentId: Id_105, + Tags: { + component: WebRequest, + env: integration_tests, + http.request.method: GET, + http.response.status_code: 200, + language: dotnet, + runtime-id: Guid_1, + server.address: localhost, + server.port: 8080, + span.kind: client, + url.full: http://localhost:00000/Guid_2/?GetResponseAsync, + _dd.base_service: Samples.WebRequest, + _dd.svc_src: http-client + }, + Metrics: { + _dd.top_level: 1.0 + } + }, + { + TraceId: Id_99, + SpanId: Id_124, + Name: http.request, + Resource: GET, + Service: Samples.WebRequest-http-client, + Type: http, + ParentId: Id_106, + Tags: { + component: WebRequest, + env: integration_tests, + http.request.method: GET, + http.response.status_code: 200, + language: dotnet, + runtime-id: Guid_1, + server.address: localhost, + server.port: 8080, + span.kind: client, + url.full: http://localhost:00000/Guid_2/?GetResponseAsync, + _dd.base_service: Samples.WebRequest, + _dd.svc_src: http-client + }, + Metrics: { + _dd.top_level: 1.0 + } + }, + { + TraceId: Id_99, + SpanId: Id_125, + Name: http.request, + Resource: GET, + Service: Samples.WebRequest-http-client, + Type: http, + ParentId: Id_107, + Tags: { + component: WebRequest, + env: integration_tests, + events: [{"name":"exception","time_unix_nano":,"attributes":{"exception.type":"System.Net.WebException","exception.message":"The remote server returned an error: (404) Not Found.","exception.stacktrace":"System.Net.WebException: The remote server returned an error: (404) Not Found.\n at System.Net.HttpWebRequest.EndGetResponse(IAsyncResult asyncResult)\n at System.Threading.Tasks.TaskFactory`1.FromAsyncCoreLogic(IAsyncResult iar, Func`2 endFunction, Action`1 endAction, Task`1 promise, Boolean requiresSynchronization)\n--- End of stack trace from previous location ---\n at System.Net.WebRequest.GetResponseAsync()"}}], + http.request.method: GET, + http.response.status_code: 404, + language: dotnet, + runtime-id: Guid_1, + server.address: localhost, + server.port: 8080, + span.kind: client, + url.full: http://localhost:00000/Guid_2/?GetResponseAsyncNotFound, + _dd.base_service: Samples.WebRequest, + _dd.svc_src: http-client + }, + Metrics: { + _dd.top_level: 1.0 + } + }, + { + TraceId: Id_99, + SpanId: Id_126, + Name: http.request, + Resource: GET, + Service: Samples.WebRequest-http-client, + Type: http, + ParentId: Id_108, + Error: 1, + Tags: { + component: WebRequest, + env: integration_tests, + error.msg: The HTTP response has status code 418., + events: [{"name":"exception","time_unix_nano":,"attributes":{"exception.type":"System.Net.WebException","exception.message":"The remote server returned an error: (418) .","exception.stacktrace":"System.Net.WebException: The remote server returned an error: (418) .\n at System.Net.HttpWebRequest.EndGetResponse(IAsyncResult asyncResult)\n at System.Threading.Tasks.TaskFactory`1.FromAsyncCoreLogic(IAsyncResult iar, Func`2 endFunction, Action`1 endAction, Task`1 promise, Boolean requiresSynchronization)\n--- End of stack trace from previous location ---\n at System.Net.WebRequest.GetResponseAsync()"}}], + http.request.method: GET, + http.response.status_code: 418, + language: dotnet, + runtime-id: Guid_1, + server.address: localhost, + server.port: 8080, + span.kind: client, + url.full: http://localhost:00000/Guid_2/?GetResponseAsyncTeapot, + _dd.base_service: Samples.WebRequest, + _dd.svc_src: http-client + }, + Metrics: { + _dd.top_level: 1.0 + } + }, + { + TraceId: Id_99, + SpanId: Id_127, + Name: http.request, + Resource: POST, + Service: Samples.WebRequest-http-client, + Type: http, + ParentId: Id_109, + Tags: { + component: WebRequest, + env: integration_tests, + http.request.method: POST, + http.response.status_code: 200, + language: dotnet, + runtime-id: Guid_1, + server.address: localhost, + server.port: 8080, + span.kind: client, + url.full: http://localhost:00000/Guid_2/?GetRequestStream, + _dd.base_service: Samples.WebRequest, + _dd.svc_src: http-client + }, + Metrics: { + _dd.top_level: 1.0 + } + }, + { + TraceId: Id_99, + SpanId: Id_128, + Name: http.request, + Resource: POST, + Service: Samples.WebRequest-http-client, + Type: http, + ParentId: Id_109, + Tags: { + component: WebRequest, + env: integration_tests, + http.request.method: POST, + http.response.status_code: 200, + language: dotnet, + runtime-id: Guid_1, + server.address: localhost, + server.port: 8080, + span.kind: client, + url.full: http://localhost:00000/Guid_2/?GetRequestStream_NoBuffering, + _dd.base_service: Samples.WebRequest, + _dd.svc_src: http-client + }, + Metrics: { + _dd.top_level: 1.0 + } + }, + { + TraceId: Id_99, + SpanId: Id_129, + Name: http.request, + Resource: POST, + Service: Samples.WebRequest-http-client, + Type: http, + ParentId: Id_110, + Tags: { + component: WebRequest, + env: integration_tests, + http.request.method: POST, + http.response.status_code: 200, + language: dotnet, + runtime-id: Guid_1, + server.address: localhost, + server.port: 8080, + span.kind: client, + url.full: http://localhost:00000/Guid_2/?GetRequestStream, + _dd.base_service: Samples.WebRequest, + _dd.svc_src: http-client + }, + Metrics: { + _dd.top_level: 1.0 + } + }, + { + TraceId: Id_99, + SpanId: Id_130, + Name: http.request, + Resource: POST, + Service: Samples.WebRequest-http-client, + Type: http, + ParentId: Id_111, + Tags: { + component: WebRequest, + env: integration_tests, + http.request.method: POST, + http.response.status_code: 200, + language: dotnet, + runtime-id: Guid_1, + server.address: localhost, + server.port: 8080, + span.kind: client, + url.full: http://localhost:00000/Guid_2/?BeginGetRequestStream, + _dd.base_service: Samples.WebRequest, + _dd.svc_src: http-client + }, + Metrics: { + _dd.top_level: 1.0 + } + }, + { + TraceId: Id_99, + SpanId: Id_131, + Name: http.request, + Resource: POST, + Service: Samples.WebRequest-http-client, + Type: http, + ParentId: Id_111, + Tags: { + component: WebRequest, + env: integration_tests, + http.request.method: POST, + http.response.status_code: 200, + language: dotnet, + runtime-id: Guid_1, + server.address: localhost, + server.port: 8080, + span.kind: client, + url.full: http://localhost:00000/Guid_2/?BeginGetRequestStream_NoBuffering, + _dd.base_service: Samples.WebRequest, + _dd.svc_src: http-client + }, + Metrics: { + _dd.top_level: 1.0 + } + }, + { + TraceId: Id_99, + SpanId: Id_132, + Name: http.request, + Resource: POST, + Service: Samples.WebRequest-http-client, + Type: http, + ParentId: Id_112, + Tags: { + component: WebRequest, + env: integration_tests, + http.request.method: POST, + http.response.status_code: 200, + language: dotnet, + runtime-id: Guid_1, + server.address: localhost, + server.port: 8080, + span.kind: client, + url.full: http://localhost:00000/Guid_2/?BeginGetRequestStream, + _dd.base_service: Samples.WebRequest, + _dd.svc_src: http-client + }, + Metrics: { + _dd.top_level: 1.0 + } + }, + { + TraceId: Id_99, + SpanId: Id_133, + Name: http.request, + Resource: POST, + Service: Samples.WebRequest-http-client, + Type: http, + ParentId: Id_113, + Tags: { + component: HttpMessageHandler, + env: integration_tests, + http-client-handler-type: System.Net.Http.SocketsHttpHandler, + http.request.method: POST, + http.response.status_code: 200, + language: dotnet, + runtime-id: Guid_1, + server.address: localhost, + server.port: 8080, + span.kind: client, + url.full: http://localhost:00000/Guid_2/?BeginGetResponseAsync, + _dd.base_service: Samples.WebRequest, + _dd.svc_src: http-client + }, + Metrics: { + _dd.top_level: 1.0 + } + }, + { + TraceId: Id_99, + SpanId: Id_134, + Name: http.request, + Resource: POST, + Service: Samples.WebRequest-http-client, + Type: http, + ParentId: Id_113, + Tags: { + component: WebRequest, + env: integration_tests, + http.request.method: POST, + http.response.status_code: 200, + language: dotnet, + runtime-id: Guid_1, + server.address: localhost, + server.port: 8080, + span.kind: client, + url.full: http://localhost:00000/Guid_2/?BeginGetResponseAsync_NoBuffering, + _dd.base_service: Samples.WebRequest, + _dd.svc_src: http-client + }, + Metrics: { + _dd.top_level: 1.0 + } + }, + { + TraceId: Id_99, + SpanId: Id_135, + Name: http.request, + Resource: POST, + Service: Samples.WebRequest-http-client, + Type: http, + ParentId: Id_114, + Tags: { + component: HttpMessageHandler, + env: integration_tests, + http-client-handler-type: System.Net.Http.SocketsHttpHandler, + http.request.method: POST, + http.response.status_code: 200, + language: dotnet, + runtime-id: Guid_1, + server.address: localhost, + server.port: 8080, + span.kind: client, + url.full: http://localhost:00000/Guid_2/?BeginGetResponseAsync, + _dd.base_service: Samples.WebRequest, + _dd.svc_src: http-client + }, + Metrics: { + _dd.top_level: 1.0 + } + }, + { + TraceId: Id_99, + SpanId: Id_136, + Name: http.request, + Resource: POST, + Service: Samples.WebRequest-http-client, + Type: http, + ParentId: Id_115, + Tags: { + component: HttpMessageHandler, + env: integration_tests, + http-client-handler-type: System.Net.Http.SocketsHttpHandler, + http.request.method: POST, + http.response.status_code: 404, + language: dotnet, + runtime-id: Guid_1, + server.address: localhost, + server.port: 8080, + span.kind: client, + url.full: http://localhost:00000/Guid_2/?BeginGetResponseNotFoundAsync, + _dd.base_service: Samples.WebRequest, + _dd.svc_src: http-client + }, + Metrics: { + _dd.top_level: 1.0 + } + }, + { + TraceId: Id_99, + SpanId: Id_137, + Name: http.request, + Resource: POST, + Service: Samples.WebRequest-http-client, + Type: http, + ParentId: Id_116, + Error: 1, + Tags: { + component: HttpMessageHandler, + env: integration_tests, + error.msg: The HTTP response has status code 418., + http-client-handler-type: System.Net.Http.SocketsHttpHandler, + http.request.method: POST, + http.response.status_code: 418, + language: dotnet, + runtime-id: Guid_1, + server.address: localhost, + server.port: 8080, + span.kind: client, + url.full: http://localhost:00000/Guid_2/?BeginGetResponseTeapotAsync, + _dd.base_service: Samples.WebRequest, + _dd.svc_src: http-client + }, + Metrics: { + _dd.top_level: 1.0 + } + }, + { + TraceId: Id_99, + SpanId: Id_138, + Name: http.request, + Resource: GET, + Service: Samples.WebRequest-http-client, + Type: http, + ParentId: Id_117, + Tags: { + component: HttpMessageHandler, + env: integration_tests, + http-client-handler-type: System.Net.Http.SocketsHttpHandler, + http.request.method: GET, + http.response.status_code: 200, + language: dotnet, + runtime-id: Guid_1, + server.address: localhost, + server.port: 8080, + span.kind: client, + url.full: http://localhost:00000/Guid_2/?TaskFactoryFromAsync, + _dd.base_service: Samples.WebRequest, + _dd.svc_src: http-client + }, + Metrics: { + _dd.top_level: 1.0 + } + } +] \ No newline at end of file From a9d7b7302b69b1863c2bcbb95d49f23d211b506e Mon Sep 17 00:00:00 2001 From: Zach Montoya Date: Thu, 30 Jul 2026 17:52:38 -0700 Subject: [PATCH 4/9] Add design spec for WebRequest OTLP snapshot tests Co-Authored-By: Claude Opus 5 (1M context) --- ...-07-30-webrequest-otlp-snapshots-design.md | 231 ++++++++++++++++++ 1 file changed, 231 insertions(+) create mode 100644 docs/superpowers/specs/2026-07-30-webrequest-otlp-snapshots-design.md diff --git a/docs/superpowers/specs/2026-07-30-webrequest-otlp-snapshots-design.md b/docs/superpowers/specs/2026-07-30-webrequest-otlp-snapshots-design.md new file mode 100644 index 000000000000..b70ff5a222e3 --- /dev/null +++ b/docs/superpowers/specs/2026-07-30-webrequest-otlp-snapshots-design.md @@ -0,0 +1,231 @@ +# OTLP snapshot tests for `Samples.WebRequest` + +**Date:** 2026-07-30 +**Branch:** `otel-httpclient` + +## Goal + +`WebRequestTests` currently snapshots the Datadog msgpack payload produced by +`Samples.WebRequest` (~134 spans) in three configurations: `v0`, `v1`, and +`otel` (`DD_TRACE_OTEL_SEMANTICS_ENABLED=true`). None of them exercise the OTLP +export path, so the HTTP semantic-convention attributes added on this branch +(`http.request.method`, `http.response.status_code`, `url.full`, +`server.address`, `server.port`, `http.request.method_original`) are never +verified as they appear on the wire in OTLP. + +Add OTLP snapshot coverage for the same sample, following the pattern +established by `OpenTelemetrySdkTests.SubmitsOtlpTraces`. + +Non-goal: changing, removing, or regenerating any existing msgpack snapshot. + +## Scope + +| In scope | Out of scope | +| --- | --- | +| New `SubmitsOtlpTraces` theory on `WebRequestTests` | Changing the existing `SubmitsTracesV0/V1(...)` tests | +| Extracting OTLP normalization into a shared helper | Changing `OpenTelemetrySdkTests`' observable behavior or its snapshots | +| Two new `.verified.txt` snapshots | gRPC protocol coverage (unsupported by the DD SDK trace exporter) | +| | `DD_AGENT_HOST` fallback coverage (already covered by `OpenTelemetrySdkTests`) | + +## Test matrix + +Four test cases, two snapshot files: + +| `protocol` | `openTelemetrySemanticsEnabled` | Snapshot | +| --- | --- | --- | +| `http/json` | `false` | `WebRequestTests.SubmitsOtlpTraces_DD` | +| `http/protobuf` | `false` | `WebRequestTests.SubmitsOtlpTraces_DD` | +| `http/json` | `true` | `WebRequestTests.SubmitsOtlpTraces_DD_OtelSemantics` | +| `http/protobuf` | `true` | `WebRequestTests.SubmitsOtlpTraces_DD_OtelSemantics` | + +The two protocols share a snapshot: the test-agent renders an http/protobuf +payload as JSON with snake_case field names and string-form enum values, and the +existing `ProtobufToJsonFieldNameMappings` / `ProtobufToJsonEnumMappings` tables +scrub that into the http/json shape. This is the same arrangement +`OpenTelemetrySdkTests.SubmitsOtlpTraces` uses. + +gRPC is excluded deliberately: `ExporterSettings` maps only `HttpProtobuf` and +`HttpJson` to an OTLP traces encoding and falls back to Datadog v0.4 otherwise. + +The metadata schema is pinned to `v0` for both cases. With +`DD_TRACE_OTEL_SEMANTICS_ENABLED=true` the tracer already forces v0, so pinning +it keeps the semantics-off baseline directly comparable. + +## Test method + +```csharp +[SkippableTheory] +[Trait("Category", "EndToEnd")] +[Trait("RequiresDockerDependency", "true")] +[Trait("DockerGroup", "1")] +[InlineData("http/json", false)] +[InlineData("http/json", true)] +[InlineData("http/protobuf", false)] +[InlineData("http/protobuf", true)] +public async Task SubmitsOtlpTraces(string protocol, bool openTelemetrySemanticsEnabled) +``` + +### Trait placement + +`RequiresDockerDependency` and `DockerGroup` go on the **method**, not the class. +CI partitions the integration-test run with +`(RequiresDockerDependency=true)` / `(RequiresDockerDependency!=true)` +(`tracer/build/_build/Build.Steps.cs`) and then further by +`DockerGroup=$(dockerGroup)` (`.azure-pipelines/ultimate-pipeline.yml`). A +class-level trait would pull the existing non-docker `WebRequestTests` into the +docker job. A docker test with no `DockerGroup` trait runs in neither group, so +the trait is required, not optional. `test-agent` is a dependency of both +`StartDependencies.Group1` and `Group2`, so group 1 is an arbitrary but valid +choice matching `OpenTelemetrySdkTests`. + +No `RunOnWindows` trait — the OTLP tests in `OpenTelemetrySdkTests` omit it too, +so these run on Linux only in CI. Consequence: no `_netfx` snapshot variant. + +### Environment + +``` +OTEL_TRACES_EXPORTER = otlp +OTEL_EXPORTER_OTLP_PROTOCOL = +OTEL_EXPORTER_OTLP_ENDPOINT = http://:4318 +DD_TRACE_OTEL_SEMANTICS_ENABLED = +DD_TRACE_SPAN_ATTRIBUTE_SCHEMA = v0 +``` + +`TEST_AGENT_HOST` falls back to `127.0.0.1` when unset, matching +`SubmitsOtlpTraces`. The port is always 4318 (http/json and http/protobuf both +use the HTTP endpoint). + +`DD_TRACE_HTTP_CLIENT_ERROR_STATUSES=410-499` and `SetServiceVersion("1.0.0")` +are inherited from the existing constructor and stay as-is. + +### Flow + +1. `ClearTestAgentSession(testAgentHost)` — with retries, so a not-yet-ready + test-agent doesn't fail the test. +2. Allocate `httpPort` via `TcpPortProvider.GetOpenPort()` for the sample's + `HttpListener`, as the existing tests do. +3. Construct `MockTracerAgent` and `RunSampleAndWaitForExit(agent, arguments: $"Port={httpPort}")`. + The mock agent is still needed: telemetry does not travel over OTLP, so + `telemetry.AssertIntegrationEnabledAsync(IntegrationId.WebRequest)` and + `VerifyInstrumentation(processResult.Process)` continue to work unchanged. + Only trace payloads divert to the test-agent. +4. `WaitForTestAgentData("http://:4318/test/session/traces")` — polls, + because the tracer flushes during shutdown. +5. Normalize (below), then `Verifier.Verify(finalJson, settings)` with + `.UseFileName(...)` and `.DisableRequireUniquePrefix()`. + +## Shared helper + +New `OtlpSnapshotHelper` in `Datadog.Trace.ClrProfiler.IntegrationTests`, holding +what is currently private to `OpenTelemetrySdkTests`: + +- `ProtobufToJsonFieldNameMappings` / `ProtobufToJsonEnumMappings` tables and + `AddProtobufToJsonScrubbers(settings)` +- `ClearTestAgentSession(host, maxRetries, delayMs)` +- `WaitForTestAgentData(url, timeoutSeconds, pollIntervalMs)` +- Resource-attribute normalization (`telemetry.sdk.version`, + `telemetry.sdk.name`, `git.commit.sha`) +- Per-span normalization: base64→hex conversion with the existing + `_traceIdRegex` / `_spanIdRegex` assertions and monotonic-timestamp + assertions, followed by flattening to placeholders +- Merging every request into a single `resource_spans` entry after asserting + the resource attributes and instrumentation scope are identical across + requests + +`OpenTelemetrySdkTests` is rewired to call the helper. **Its snapshots must stay +byte-identical**; verify by re-running its OTLP tests and confirming no diff. + +The one behavioral risk in the extraction is span ordering: `OpenTelemetrySdkTests` +sorts by `name` only. The helper therefore takes an **optional sort-key selector +defaulting to name-only**, preserving current behavior, and `WebRequestTests` +passes the composite key described below. + +## Determinism + +Four sources of instability, each handled explicitly. + +### 1. Span ordering + +Under OTLP the span `name` is `Span.ResourceName` (see +`OtlpTracesJsonSerializer`), which for this sample is mostly `POST`/`GET` — +name-only sorting is nowhere near deterministic across 134 spans. + +Sort by: `name` → the `url.full` attribute value (empty string when absent) → +the span's own normalized JSON text. + +IDs and timestamps are normalized *before* sorting, which makes the third key +total: any two spans that still tie are byte-identical, so their relative order +cannot change the output. The first two keys exist only to make the snapshot +readable. + +### 2. Dynamic listener port + +`VerifyHelper.SpanScrubbers` already rewrites `localhost:\d+` → `localhost:00000` +and `127.0.0.1:\d+` → `localhost:00000`, which covers `url.full`. +`ScrubInlineGuids` covers the per-run GUID in the request path. + +`server.port` is a separate attribute carrying the raw port number, and it is +not a text match for those regexes. Normalize it via a JToken lookup on +`key == 'server.port'`, setting the value to a fixed `8080` — mirroring the +`server.port: \d+` regex scrubber the msgpack test already uses. + +### 3. TFM differences + +The existing msgpack test handles two TFM-dependent differences that apply +equally to the OTLP payload: + +- 49 spans carry `http-client-handler-type`. On .NET Core the test scrubs + `System.Net.Http.HttpClientHandler` → `System.Net.Http.SocketsHttpHandler`. +- On .NET 9+, the `?BeginGetResponseAsync_NoBuffering` request produces a + `WebRequest` span instead of an `HttpClient` span. The existing test patches + that single span's `component` to `HttpMessageHandler` and adds + `http-client-handler-type = System.Net.Http.SocketsHttpHandler`. + +Both must be replicated on the OTLP JSON (the handler-type as a simple string +scrubber; the .NET 9 fixup as a JToken edit locating the span by its `url.full` +attribute suffix). Without them, .NET 9 needs its own snapshot pair. + +### 4. IDs and timestamps + +Flattened to fixed placeholders (`normalized-trace-id`, `normalized-span-id`, +`normalized-parent-span-id`, `"0"` for start/end times), matching +`OpenTelemetrySdkTests`. This erases parent→child structure from the snapshot; +that structure is already asserted by the existing msgpack snapshots, and the +OTLP snapshot's job is to verify attributes and span shape on the wire. + +The hex-format and monotonic-timestamp assertions run *before* flattening, so +the real values are still validated. + +## Assertions beyond the snapshot + +Carried over from the existing `RunTest`: + +- `telemetry.AssertIntegrationEnabledAsync(IntegrationId.WebRequest)` +- `VerifyInstrumentation(processResult.Process)` (via `SetInstrumentationVerification()`) +- `tracesRequests.Should().NotBeNullOrEmpty()` + +`ValidateIntegrationSpans` is **not** applicable: it operates on `MockSpan`, +which is the msgpack representation. The OTLP payload has no `MockSpan` +equivalent, and the snapshot covers the same ground. + +## Known trade-off: snapshot size + +OTLP JSON is far more verbose than the Verify span format — roughly six lines +per attribute versus one. The existing `WebRequestTests_otel.verified.txt` is +3,130 lines for this same data; each OTLP snapshot is expected to land around +10–12k lines, for two files. Accepted in exchange for full-fidelity coverage; +the alternative (filtering to HTTP-client spans only) was considered and +rejected. + +## Verification plan + +1. `docker compose up -d test-agent` locally (macOS, Docker confirmed running; + `artifacts/monitoring-home` is already built). +2. Run the four new cases to generate the two `.verified.txt` files; inspect + them for leaked ports, GUIDs, timestamps, or machine-specific paths. +3. Re-run each case a second time to confirm the snapshots are stable + (ordering, merged-request handling). +4. Re-run `OpenTelemetrySdkTests.SubmitsOtlpTraces` and confirm its snapshots + are unchanged after the helper extraction. +5. Confirm the existing `SubmitsTracesV0/V1` msgpack tests still pass and their + snapshots are untouched. From 7ecd53110be8670048afae181fffee9d69fe4b2f Mon Sep 17 00:00:00 2001 From: Zach Montoya Date: Thu, 30 Jul 2026 18:03:12 -0700 Subject: [PATCH 5/9] Add implementation plan for WebRequest OTLP snapshot tests Co-Authored-By: Claude Opus 5 (1M context) --- .../2026-07-30-webrequest-otlp-snapshots.md | 966 ++++++++++++++++++ ...-07-30-webrequest-otlp-snapshots-design.md | 21 + 2 files changed, 987 insertions(+) create mode 100644 docs/superpowers/plans/2026-07-30-webrequest-otlp-snapshots.md diff --git a/docs/superpowers/plans/2026-07-30-webrequest-otlp-snapshots.md b/docs/superpowers/plans/2026-07-30-webrequest-otlp-snapshots.md new file mode 100644 index 000000000000..6b36f5eba638 --- /dev/null +++ b/docs/superpowers/plans/2026-07-30-webrequest-otlp-snapshots.md @@ -0,0 +1,966 @@ +# WebRequest OTLP Snapshot Tests Implementation Plan + +> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking. + +**Goal:** Add OTLP snapshot coverage for `Samples.WebRequest` so the HTTP semantic-convention attributes added on this branch are verified as they appear on the wire in OTLP, not just in Datadog msgpack. + +**Architecture:** Extract the OTLP payload-normalization logic currently private to `OpenTelemetrySdkTests` into a shared `OtlpSnapshotHelper`, then add a four-case `SubmitsOtlpTraces` theory to `WebRequestTests` that exports through the `test-agent` container and snapshots the normalized JSON. `OpenTelemetrySdkTests` must keep producing byte-identical snapshots throughout. + +**Tech Stack:** xUnit (`SkippableTheory`), Verify/VerifyXunit snapshots, FluentAssertions, `Datadog.Trace.Vendors.Newtonsoft.Json.Linq`, `ddapm-test-agent` docker container. + +**Spec:** `docs/superpowers/specs/2026-07-30-webrequest-otlp-snapshots-design.md` + +## Global Constraints + +- **Never regenerate or modify an existing snapshot.** `WebRequestTests_v0`, `WebRequestTests_v1`, `WebRequestTests_otel`, `WebRequestTests_netfx_*`, and every `OpenTelemetrySdkTests.*` snapshot must remain byte-identical. If one changes, the change is a bug — revert and rethink. +- Copyright header on every new file, matching the repo's exact format (see any existing file under `tracer/test/`). +- Follow `.editorconfig` and `tracer/stylecop.json`. Use `is not null` over `!= null`. Add `using` directives rather than fully-qualified type names. +- Use `Datadog.Trace.Vendors.Newtonsoft.Json` / `.Linq` — **not** `Newtonsoft.Json`. This is what `OpenTelemetrySdkTests` uses and the only JSON library referenced by the test project. +- The new helper files use `#nullable enable`, but the code being moved into them came from a file that does not. Expect nullability warnings on the copied bodies (`CS8602` on `span[key].ToString()`, `CS8600` on `JToken previousResourceAttributes = null`). Resolve them with `?` on locals and the `!` null-forgiving operator — both are compile-time only and cannot change behavior. Do **not** restructure the copied logic to satisfy the compiler. +- `OtlpFieldNames` is passed **by value**, never as an `in`/`ref` parameter. Lambdas cannot capture `in` parameters, and several call sites close over it. +- The test-agent OTLP HTTP endpoint is always port **4318** for both `http/json` and `http/protobuf`. Host comes from `TEST_AGENT_HOST`, defaulting to `127.0.0.1`. +- gRPC is out of scope: `ExporterSettings` only maps `HttpProtobuf`/`HttpJson` to an OTLP traces encoding and silently falls back to Datadog v0.4 otherwise. + +## Prerequisites + +Start the test-agent before running anything in Task 1, 2, or 4: + +```bash +docker compose up -d test-agent +curl -sf http://127.0.0.1:4318/test/session/clear && echo OK +``` + +## File Structure + +| File | Responsibility | +| --- | --- | +| `tracer/test/Datadog.Trace.ClrProfiler.IntegrationTests/Helpers/OtlpFieldNames.cs` (create) | Maps a protocol to the OTLP field-name casing the test-agent renders (`resourceSpans` vs `resource_spans`, etc.) | +| `tracer/test/Datadog.Trace.ClrProfiler.IntegrationTests/Helpers/OtlpSnapshotHelper.cs` (create) | Test-agent session I/O, protobuf→json scrubbers, OTLP payload normalization, request merging, attribute lookup | +| `tracer/test/Datadog.Trace.ClrProfiler.IntegrationTests/Helpers/TestAgentOtlpCollection.cs` (create) | xUnit collection that serializes every class sharing the test-agent OTLP session | +| `tracer/test/Datadog.Trace.ClrProfiler.IntegrationTests/OpenTelemetrySdkTests.cs` (modify) | Loses its private OTLP plumbing; delegates to the helper. Behavior unchanged. | +| `tracer/test/Datadog.Trace.ClrProfiler.IntegrationTests/WebRequestTests.cs` (modify) | Gains `SubmitsOtlpTraces` plus WebRequest-specific normalization | +| `tracer/test/snapshots/WebRequestTests.SubmitsOtlpTraces_DD.verified.txt` (create) | Snapshot, semantics off | +| `tracer/test/snapshots/WebRequestTests.SubmitsOtlpTraces_DD_OtelSemantics.verified.txt` (create) | Snapshot, semantics on | + +--- + +### Task 1: Extract test-agent I/O and protocol scrubbers + +Pure code motion. Everything moved here is currently private to `OpenTelemetrySdkTests` and used verbatim by its traces, metrics, and logs tests. + +**Files:** +- Create: `tracer/test/Datadog.Trace.ClrProfiler.IntegrationTests/Helpers/OtlpFieldNames.cs` +- Create: `tracer/test/Datadog.Trace.ClrProfiler.IntegrationTests/Helpers/OtlpSnapshotHelper.cs` +- Modify: `tracer/test/Datadog.Trace.ClrProfiler.IntegrationTests/OpenTelemetrySdkTests.cs` + +**Interfaces:** +- Consumes: nothing +- Produces: + - `OtlpFieldNames.For(bool isJson) -> OtlpFieldNames` with `string` properties `ResourceSpans`, `ScopeSpans`, `StringValue`, `IntValue`, `TraceId`, `SpanId`, `ParentSpanId`, `StartTimeUnixNano`, `EndTimeUnixNano`, `TimeUnixNano`, and `bool IsJson` + - `OtlpSnapshotHelper.ClearTestAgentSessionAsync(string testAgentHost, int maxRetries = 5, int delayMs = 1000) -> Task` + - `OtlpSnapshotHelper.WaitForTestAgentDataAsync(string url, int timeoutSeconds = 60, int pollIntervalMs = 500) -> Task` + - `OtlpSnapshotHelper.AddProtobufToJsonScrubbers(VerifySettings settings) -> void` + +- [ ] **Step 1: Create `OtlpFieldNames.cs`** + +```csharp +// +// Unless explicitly stated otherwise all files in this repository are licensed under the Apache 2 License. +// This product includes software developed at Datadog (https://www.datadoghq.com/). Copyright 2017 Datadog, Inc. +// + +#nullable enable + +namespace Datadog.Trace.ClrProfiler.IntegrationTests.Helpers +{ + /// + /// The test agent renders an OTLP http/json payload with camelCase field names and an + /// http/protobuf payload with snake_case field names. This maps a protocol to the casing + /// used when walking the rendered JSON. + /// + internal readonly struct OtlpFieldNames + { + private OtlpFieldNames(bool isJson) + { + IsJson = isJson; + } + + public bool IsJson { get; } + + public string ResourceSpans => IsJson ? "resourceSpans" : "resource_spans"; + + public string ScopeSpans => IsJson ? "scopeSpans" : "scope_spans"; + + public string StringValue => IsJson ? "stringValue" : "string_value"; + + public string IntValue => IsJson ? "intValue" : "int_value"; + + public string TraceId => IsJson ? "traceId" : "trace_id"; + + public string SpanId => IsJson ? "spanId" : "span_id"; + + public string ParentSpanId => IsJson ? "parentSpanId" : "parent_span_id"; + + public string StartTimeUnixNano => IsJson ? "startTimeUnixNano" : "start_time_unix_nano"; + + public string EndTimeUnixNano => IsJson ? "endTimeUnixNano" : "end_time_unix_nano"; + + public string TimeUnixNano => IsJson ? "timeUnixNano" : "time_unix_nano"; + + public static OtlpFieldNames For(bool isJson) => new(isJson); + } +} +``` + +- [ ] **Step 2: Create `OtlpSnapshotHelper.cs` with the moved I/O and scrubbers** + +The two mapping tables and all three method bodies are copied verbatim from `OpenTelemetrySdkTests` — do not re-derive them. + +```csharp +// +// Unless explicitly stated otherwise all files in this repository are licensed under the Apache 2 License. +// This product includes software developed at Datadog (https://www.datadoghq.com/). Copyright 2017 Datadog, Inc. +// + +#nullable enable + +using System; +using System.Net.Http; +using System.Threading.Tasks; +using Datadog.Trace.Vendors.Newtonsoft.Json.Linq; +using VerifyTests; + +namespace Datadog.Trace.ClrProfiler.IntegrationTests.Helpers +{ + /// + /// Shared plumbing for tests that snapshot OTLP payloads captured by the ddapm test agent. + /// + internal static class OtlpSnapshotHelper + { + // Single source of truth for translating an OTLP http/protobuf payload (rendered as JSON + // by the test agent with snake_case field names and string-form enum values) to the + // OTLP http/json shape (camelCase field names, integer enum values). When a new OTLP + // field or enum reaches the serializer, add the mapping here. + private static readonly (string From, string To)[] ProtobufToJsonFieldNameMappings = + { + ("\"resource_spans\"", "\"resourceSpans\""), + ("\"scope_spans\"", "\"scopeSpans\""), + ("\"trace_id\"", "\"traceId\""), + ("\"span_id\"", "\"spanId\""), + ("\"parent_span_id\"", "\"parentSpanId\""), + ("\"start_time_unix_nano\"", "\"startTimeUnixNano\""), + ("\"end_time_unix_nano\"", "\"endTimeUnixNano\""), + ("\"time_unix_nano\"", "\"timeUnixNano\""), + ("\"string_value\"", "\"stringValue\""), + ("\"double_value\"", "\"doubleValue\""), + ("\"int_value\"", "\"intValue\""), + ("\"bool_value\"", "\"boolValue\""), + ("\"array_value\"", "\"arrayValue\""), + }; + + private static readonly (string From, string To)[] ProtobufToJsonEnumMappings = + { + ("\"kind\": \"SPAN_KIND_INTERNAL\"", "\"kind\": 1"), + ("\"kind\": \"SPAN_KIND_SERVER\"", "\"kind\": 2"), + ("\"kind\": \"SPAN_KIND_CLIENT\"", "\"kind\": 3"), + ("\"kind\": \"SPAN_KIND_PRODUCER\"", "\"kind\": 4"), + ("\"kind\": \"SPAN_KIND_CONSUMER\"", "\"kind\": 5"), + ("\"code\": \"STATUS_CODE_UNSET\"", "\"code\": 0"), + ("\"code\": \"STATUS_CODE_OK\"", "\"code\": 1"), + ("\"code\": \"STATUS_CODE_ERROR\"", "\"code\": 2"), + }; + + public static void AddProtobufToJsonScrubbers(VerifySettings settings) + { + foreach (var (from, to) in ProtobufToJsonFieldNameMappings) + { + settings.AddSimpleScrubber(from, to); + } + + foreach (var (from, to) in ProtobufToJsonEnumMappings) + { + settings.AddSimpleScrubber(from, to); + } + } + + /// + /// Clears the test-agent session, retrying if the agent is not yet ready. + /// Ensures the OTLP HTTP endpoint is accepting connections before tests proceed. + /// + public static async Task ClearTestAgentSessionAsync(string testAgentHost, int maxRetries = 5, int delayMs = 1000) + { + using var httpClient = new HttpClient { Timeout = TimeSpan.FromSeconds(5) }; + var url = $"http://{testAgentHost}:4318/test/session/clear"; + + for (var attempt = 1; attempt <= maxRetries; attempt++) + { + try + { + var response = await httpClient.GetAsync(url); + response.EnsureSuccessStatusCode(); + return; + } + catch (Exception) when (attempt < maxRetries) + { + await Task.Delay(delayMs); + } + } + + // Final attempt -- let it throw if it fails + var finalResponse = await httpClient.GetAsync(url); + finalResponse.EnsureSuccessStatusCode(); + } + + /// + /// Polls the test-agent for data until non-empty results are returned or timeout is reached. + /// The sample app exports data during shutdown, so there can be a brief delay + /// between process exit and data appearing in the test-agent. The timeout is generous + /// because first-time gRPC connections (TCP+HTTP/2+TLS handshake) plus tracer shutdown + /// flushing can stack up on slower CI runners. + /// + public static async Task WaitForTestAgentDataAsync(string url, int timeoutSeconds = 60, int pollIntervalMs = 500) + { + using var httpClient = new HttpClient { Timeout = TimeSpan.FromSeconds(10) }; + var deadline = DateTime.UtcNow.AddSeconds(timeoutSeconds); + + while (DateTime.UtcNow < deadline) + { + var response = await httpClient.GetAsync(url); + response.EnsureSuccessStatusCode(); + + var json = await response.Content.ReadAsStringAsync(); + var data = JToken.Parse(json); + + if (data.HasValues) + { + return data; + } + + await Task.Delay(pollIntervalMs); + } + + // Final attempt -- return whatever we get so the caller's assertion shows the actual value + var finalResponse = await httpClient.GetAsync(url); + finalResponse.EnsureSuccessStatusCode(); + var finalJson = await finalResponse.Content.ReadAsStringAsync(); + return JToken.Parse(finalJson); + } + } +} +``` + +`AddSimpleScrubber` is an extension method on `VerifySettings` defined in `tracer/test/Datadog.Trace.TestHelpers.SharedSource/VerifyHelper.cs:202`, so `OtlpSnapshotHelper.cs` also needs `using Datadog.Trace.TestHelpers;`. + +- [ ] **Step 3: Delete the moved members from `OpenTelemetrySdkTests.cs`** + +Delete `ProtobufToJsonFieldNameMappings` (lines ~83-98), `ProtobufToJsonEnumMappings` (~100-110), `AddProtobufToJsonScrubbers` (~979-990), `ClearTestAgentSession` (~892-914), and `WaitForTestAgentData` (~923-949). + +- [ ] **Step 4: Update the call sites in `OpenTelemetrySdkTests.cs`** + +There are five call sites. Replace each: + +| Old | New | +| --- | --- | +| `await ClearTestAgentSession(testAgentHost);` | `await OtlpSnapshotHelper.ClearTestAgentSessionAsync(testAgentHost);` | +| `await WaitForTestAgentData(...)` | `await OtlpSnapshotHelper.WaitForTestAgentDataAsync(...)` | +| `AddProtobufToJsonScrubbers(settings);` | `OtlpSnapshotHelper.AddProtobufToJsonScrubbers(settings);` | + +Add `using Datadog.Trace.ClrProfiler.IntegrationTests.Helpers;` to the file's using block. + +- [ ] **Step 5: Build** + +Run: `dotnet build tracer/test/Datadog.Trace.ClrProfiler.IntegrationTests/Datadog.Trace.ClrProfiler.IntegrationTests.csproj -f net10.0` +Expected: build succeeds with no new warnings. `System.Net.Http` and `System.Text.RegularExpressions` usings in `OpenTelemetrySdkTests.cs` may now be unused — if the analyzer flags them, remove only the ones it flags (`Regex` is still used by the `_versionRegex` fields, so do not blanket-remove). + +- [ ] **Step 6: Smoke-test one OTLP case** + +Run: +```bash +dotnet test tracer/test/Datadog.Trace.ClrProfiler.IntegrationTests/Datadog.Trace.ClrProfiler.IntegrationTests.csproj \ + -f net10.0 --no-build \ + --filter "FullyQualifiedName~OpenTelemetrySdkTests.SubmitsOtlpTraces" +``` +Expected: PASS. If the harness cannot locate the monitoring home, fall back to the documented Nuke path: +```bash +./tracer/build.sh BuildAndRunIntegrationTests --framework net10.0 \ + --filter "Datadog.Trace.ClrProfiler.IntegrationTests.OpenTelemetrySdkTests.SubmitsOtlpTraces" \ + --SampleName "Samples.OpenTelemetrySdk" +``` + +- [ ] **Step 7: Confirm no snapshot drifted** + +Run: `git status --porcelain tracer/test/snapshots/` +Expected: **empty output**. Any modified or new `.received.txt` file means the extraction changed behavior — stop and fix before continuing. + +- [ ] **Step 8: Commit** + +```bash +git add tracer/test/Datadog.Trace.ClrProfiler.IntegrationTests/Helpers/OtlpFieldNames.cs \ + tracer/test/Datadog.Trace.ClrProfiler.IntegrationTests/Helpers/OtlpSnapshotHelper.cs \ + tracer/test/Datadog.Trace.ClrProfiler.IntegrationTests/OpenTelemetrySdkTests.cs +git commit -m "test: extract OTLP test-agent helpers from OpenTelemetrySdkTests" +``` + +--- + +### Task 2: Extract OTLP payload normalization and request merging + +**Files:** +- Modify: `tracer/test/Datadog.Trace.ClrProfiler.IntegrationTests/Helpers/OtlpSnapshotHelper.cs` +- Modify: `tracer/test/Datadog.Trace.ClrProfiler.IntegrationTests/OpenTelemetrySdkTests.cs:340-535` + +**Interfaces:** +- Consumes: `OtlpFieldNames`, `OtlpSnapshotHelper` from Task 1 +- Produces: + - `OtlpSnapshotHelper.NormalizeResourceAttributes(JToken tracesRequests, OtlpFieldNames names) -> void` + - `OtlpSnapshotHelper.NormalizeSpans(JToken tracesRequests, OtlpFieldNames names, long applicationStartTimeUnixNano) -> void` + - `OtlpSnapshotHelper.MergeDatadogRequests(JToken tracesRequests, OtlpFieldNames names, Func, IEnumerable>? sortSpans = null) -> JToken` + - `OtlpSnapshotHelper.SortSpansPerScope(JToken tracesRequests, OtlpFieldNames names) -> void` + - `OtlpSnapshotHelper.GetAttributeStringValue(JToken span, OtlpFieldNames names, params string[] keys) -> string?` + - `OtlpSnapshotHelper.SetAttributeStringValue(JToken span, OtlpFieldNames names, string key, string value) -> void` + - `OtlpSnapshotHelper.SortSpanAttributes(JToken tracesRequests) -> void` + +**Critical:** `MergeDatadogRequests`'s default sort must stay `OrderBy(s => s["name"]!.ToString())` with the default string comparer — exactly what `OpenTelemetrySdkTests` does today. Do not "improve" it to `StringComparer.Ordinal` here or its snapshots will reorder. + +- [ ] **Step 1: Add the normalization methods to `OtlpSnapshotHelper`** + +Bodies are lifted verbatim from `OpenTelemetrySdkTests.SubmitsOtlpTraces`, with the local `*Key` variables replaced by `names.*`. + +```csharp + public static void NormalizeResourceAttributes(JToken tracesRequests, OtlpFieldNames names) + { + var stringValueKey = names.StringValue; + + foreach (var attribute in tracesRequests.SelectTokens("$..resource.attributes[?(@.key == 'telemetry.sdk.version')]")) + { + attribute["value"]![stringValueKey] = "sdk-version"; + } + + foreach (var attribute in tracesRequests.SelectTokens("$..resource.attributes[?(@.key == 'telemetry.sdk.name')]")) + { + attribute["value"]![stringValueKey] = "sdk-name"; + } + + foreach (var attribute in tracesRequests.SelectTokens("$..resource.attributes[?(@.key == 'git.commit.sha')]")) + { + attribute["value"]![stringValueKey] = "normalized-git-commit-sha"; + } + } + + public static void NormalizeSpans(JToken tracesRequests, OtlpFieldNames names, long applicationStartTimeUnixNano) + { + var isJson = names.IsJson; + var stringValueKey = names.StringValue; + var traceIdKey = names.TraceId; + var spanIdKey = names.SpanId; + var parentSpanIdKey = names.ParentSpanId; + var startTimeUnixNanoKey = names.StartTimeUnixNano; + var endTimeUnixNanoKey = names.EndTimeUnixNano; + var timeUnixNanoKey = names.TimeUnixNano; + + foreach (var span in tracesRequests.SelectTokens("$..spans[*]")) + { + // Parse unstable information from the span + string traceIdData = isJson ? span[traceIdKey].ToString() + : ToTraceId(Convert.FromBase64String(span[traceIdKey].ToString())); + string spanIdData = isJson ? span[spanIdKey].ToString() + : ToSpanId(Convert.FromBase64String(span[spanIdKey].ToString())); + var spanStartTimeUnixNano = long.Parse(span[startTimeUnixNanoKey].ToString()); + var spanEndTimeUnixNano = long.Parse(span[endTimeUnixNanoKey].ToString()); + + // Add strong assertions on unstable span information + spanStartTimeUnixNano.Should().BeGreaterThanOrEqualTo(applicationStartTimeUnixNano); + spanEndTimeUnixNano.Should().BeGreaterThanOrEqualTo(spanStartTimeUnixNano); + traceIdData.Should().MatchRegex(TraceIdRegex); + spanIdData.Should().MatchRegex(SpanIdRegex); + if (span[parentSpanIdKey] != null) + { + string parentSpanIdData = isJson ? span[parentSpanIdKey]?.ToString() + : ToSpanId(Convert.FromBase64String(span[parentSpanIdKey].ToString())); + parentSpanIdData.Should().MatchRegex(SpanIdRegex); + } + + // Normalize the unstable span information for our snapshots + span[startTimeUnixNanoKey] = "0"; + span[endTimeUnixNanoKey] = "0"; + span[traceIdKey] = "normalized-trace-id"; + span[spanIdKey] = "normalized-span-id"; + if (span[parentSpanIdKey] != null) + { + span[parentSpanIdKey] = "normalized-parent-span-id"; + } + + // Our JSON and Protobuf OTLP exporters differ in serialization behavior when there are no attributes. + // Standardize them here by removing an empty array + if (span["attributes"] is JArray attributes && attributes.Count == 0) + { + ((JObject)span).Remove("attributes"); + } + } + + foreach (var attribute in tracesRequests.SelectTokens("$..spans[*].attributes[?(@.key == 'otel.trace_id')]")) + { + attribute["value"]![stringValueKey] = "normalized-otel-trace-id"; + } + + foreach (var link in tracesRequests.SelectTokens("$..links[*]")) + { + if (isJson) + { + link[traceIdKey].ToString().Should().MatchRegex(TraceIdRegex); + link[spanIdKey].ToString().Should().MatchRegex(SpanIdRegex); + } + + link[traceIdKey] = "normalized-trace-id"; + link[spanIdKey] = "normalized-span-id"; + } + + foreach (var @event in tracesRequests.SelectTokens("$..events[*]")) + { + ((JObject)@event).Remove(timeUnixNanoKey); + ((JObject)@event).AddFirst(new JProperty(timeUnixNanoKey, "0")); + } + } + + private static string ToHexString(byte[] bytes, int length) + { + bytes.Length.Should().Be(length); + + var traceId = new byte[length * 2]; + for (int i = 0; i < length; i++) + { + traceId[2 * i] = (byte)(bytes[i] >> 4); // high 4 bits + traceId[(2 * i) + 1] = (byte)(bytes[i] & 0x0F); // low 4 bits + } + + // Convert each nibble (0-15) to its hex character + var result = new char[length * 2]; + for (int i = 0; i < length * 2; i++) + { + result[i] = (char)(traceId[i] < 10 ? '0' + traceId[i] : 'a' + traceId[i] - 10); + } + + return new string(result); + } + + private static string ToTraceId(byte[] bytes) => ToHexString(bytes, 16); + + private static string ToSpanId(byte[] bytes) => ToHexString(bytes, 8); +``` + +Add these static fields alongside the mapping tables (moved from `OpenTelemetrySdkTests`' instance fields `_traceIdRegex` / `_spanIdRegex`): + +```csharp + private static readonly Regex TraceIdRegex = new(@"^([a-fA-F0-9]{32})$"); + private static readonly Regex SpanIdRegex = new(@"^([a-fA-F0-9]{16})$"); +``` + +New usings for this file: `System.Collections.Generic`, `System.Linq`, `System.Text.RegularExpressions`, `Datadog.Trace.Vendors.Newtonsoft.Json`, `FluentAssertions`. + +The original `foreach (var link ...)` had a commented-out `else` branch for the protobuf case. It is dead code that was never enabled — drop the comment block and keep the `if (isJson)` guard, as written above. + +- [ ] **Step 2: Add merge, sort, and attribute helpers to `OtlpSnapshotHelper`** + +```csharp + /// + /// Collapses every captured request into the first one. Asserts that each request carries + /// identical resource attributes and a single instrumentation scope first, which holds for + /// the Datadog SDK because it emits one application-level resource and does not yet track + /// per-library scopes. + /// + public static JToken MergeDatadogRequests( + JToken tracesRequests, + OtlpFieldNames names, + Func, IEnumerable>? sortSpans = null) + { + var resourceSpansKey = names.ResourceSpans; + var scopeSpansKey = names.ScopeSpans; + + // First, for the DD SDK, assert that the resource attributes for all requests are identical + // This is analogous to DD_SERVICE, DD_VERSION, DD_ENV, etc. that define + // metadata for the telemetry at an application and host level. + JToken previousResourceAttributes = null; + foreach (var tracesRequest in tracesRequests) + { + tracesRequest[resourceSpansKey].Should().HaveCount(1); + var resourceAttributes = tracesRequest[resourceSpansKey][0]["resource"]["attributes"]; + + if (previousResourceAttributes == null) + { + previousResourceAttributes = resourceAttributes; + } + else + { + JToken.DeepEquals(previousResourceAttributes, resourceAttributes).Should().BeTrue(); + previousResourceAttributes = resourceAttributes; + } + } + + // Next, assert that we only have a singular InstrumentationScope in each request. + // In OpenTelemetry, an InstrumentationScope is a way to group spans by the library that produced them. + // We should be respecting this for each library/ActivitySource, but right now the DD SDK doesn't + // keep track of that information, so consolidate them into one single, empty InstrumentationScope. + // TODO: Properly track spans per instrumentation scope. + JArray firstSpans = null; + foreach (var tracesRequest in tracesRequests) + { + tracesRequest[resourceSpansKey][0][scopeSpansKey].Should().HaveCount(1); + var spans = tracesRequest[resourceSpansKey][0][scopeSpansKey][0]["spans"] as JArray; + + if (firstSpans == null) + { + firstSpans = spans; + } + else + { + foreach (var span in spans) + { + firstSpans.Add(span); + } + } + } + + // Now re-order and trim down to one single request + // This means the output is not a true 1:1 mapping of the input spans, but it's good enough for now + // and will make the results stable. + sortSpans ??= spans => spans.OrderBy(s => s["name"]!.ToString()); + var sortedSpans = new JArray(sortSpans(firstSpans)); + tracesRequests[0][resourceSpansKey][0][scopeSpansKey][0]["spans"] = sortedSpans; + return tracesRequests[0]; + } + + /// + /// Sorts spans by name within each scope, leaving the request structure intact. Used when the + /// payload comes from a real OTel SDK, which emits genuinely distinct scopes. + /// + public static void SortSpansPerScope(JToken tracesRequests, OtlpFieldNames names) + { + foreach (var scopeSpan in tracesRequests.SelectTokens($"$..{names.ScopeSpans}[*]")) + { + if (scopeSpan["spans"] is JArray spansArray) + { + var sorted = new JArray(spansArray.OrderBy(s => s["name"]?.ToString())); + scopeSpan["spans"] = sorted; + } + } + } + + /// + /// Returns the string value of the first attribute matching any of , + /// or null when the span carries none of them. Accepts several keys because a tag's name + /// changes with the semantic conventions in play (for example http.url vs url.full). + /// + public static string? GetAttributeStringValue(JToken span, OtlpFieldNames names, params string[] keys) + { + if (span["attributes"] is not JArray attributes) + { + return null; + } + + foreach (var key in keys) + { + foreach (var attribute in attributes) + { + if (attribute["key"]?.ToString() == key) + { + return attribute["value"]?[names.StringValue]?.ToString(); + } + } + } + + return null; + } + + /// + /// Sets a string attribute on a span, appending it when absent. + /// + public static void SetAttributeStringValue(JToken span, OtlpFieldNames names, string key, string value) + { + if (span["attributes"] is not JArray attributes) + { + attributes = new JArray(); + ((JObject)span)["attributes"] = attributes; + } + + foreach (var attribute in attributes) + { + if (attribute["key"]?.ToString() == key) + { + attribute["value"] = new JObject { [names.StringValue] = value }; + return; + } + } + + attributes.Add(new JObject + { + ["key"] = key, + ["value"] = new JObject { [names.StringValue] = value }, + }); + } + + /// + /// Sorts every span's attribute array by key. Attribute order otherwise follows tag + /// enumeration order, which is not guaranteed stable across runtimes. + /// + public static void SortSpanAttributes(JToken tracesRequests) + { + foreach (var span in tracesRequests.SelectTokens("$..spans[*]")) + { + if (span["attributes"] is JArray attributes) + { + ((JObject)span)["attributes"] = new JArray( + attributes.OrderBy(a => a["key"]?.ToString() ?? string.Empty, StringComparer.Ordinal)); + } + } + } +``` + +- [ ] **Step 3: Replace the inlined logic in `OpenTelemetrySdkTests.SubmitsOtlpTraces`** + +Delete everything from `// Normalize the data in resource attributes and spans` (the block of `*Key` local variables) through the end of the `else` branch that assigns `finalJson`, and replace with: + +```csharp + var names = OtlpFieldNames.For(isJson); + OtlpSnapshotHelper.NormalizeResourceAttributes(tracesRequests, names); + OtlpSnapshotHelper.NormalizeSpans(tracesRequests, names, applicationStartTimeUnixNano); + + string finalJson; + if (datadogTracesEnabled.Equals("true")) + { + finalJson = OtlpSnapshotHelper.MergeDatadogRequests(tracesRequests, names) + .ToString(Formatting.Indented); + } + else + { + OtlpSnapshotHelper.SortSpansPerScope(tracesRequests, names); + finalJson = tracesRequests.ToString(Formatting.Indented); + } +``` + +Then delete the now-unused `_traceIdRegex` and `_spanIdRegex` instance fields. + +- [ ] **Step 4: Build** + +Run: `dotnet build tracer/test/Datadog.Trace.ClrProfiler.IntegrationTests/Datadog.Trace.ClrProfiler.IntegrationTests.csproj -f net10.0` +Expected: build succeeds. Remove any using directives the analyzer now reports as unused. + +- [ ] **Step 5: Run the full OTLP traces theory** + +Run: +```bash +dotnet test tracer/test/Datadog.Trace.ClrProfiler.IntegrationTests/Datadog.Trace.ClrProfiler.IntegrationTests.csproj \ + -f net10.0 --no-build \ + --filter "FullyQualifiedName~OpenTelemetrySdkTests.SubmitsOtlpTraces" +``` +Expected: all cases PASS. This covers both the http/json and http/protobuf paths and both the merged (Datadog) and per-scope (OTel SDK) branches. + +- [ ] **Step 6: Confirm no snapshot drifted** + +Run: `git status --porcelain tracer/test/snapshots/` +Expected: **empty output**. + +- [ ] **Step 7: Commit** + +```bash +git add tracer/test/Datadog.Trace.ClrProfiler.IntegrationTests/Helpers/OtlpSnapshotHelper.cs \ + tracer/test/Datadog.Trace.ClrProfiler.IntegrationTests/OpenTelemetrySdkTests.cs +git commit -m "test: extract OTLP payload normalization into OtlpSnapshotHelper" +``` + +--- + +### Task 3: Serialize test-agent OTLP consumers into one xUnit collection + +xUnit runs distinct collections in parallel, and this project sets no `CollectionBehavior`. `ClearTestAgentSessionAsync` wipes the shared test-agent session globally, so once `WebRequestTests` also uses it, a clear from one class can delete another class's in-flight traces. Today `OpenTelemetrySdkTests` is safe only because all its tests live in one implicit per-class collection. + +In CI this costs almost nothing: the non-docker job filters `OpenTelemetrySdkTests` out entirely (`RequiresDockerDependency!=true`), and the docker job filters out `WebRequestTests`' msgpack tests, so the only serialization that actually happens is between OTLP tests — which is the intent. + +**Files:** +- Create: `tracer/test/Datadog.Trace.ClrProfiler.IntegrationTests/Helpers/TestAgentOtlpCollection.cs` +- Modify: `tracer/test/Datadog.Trace.ClrProfiler.IntegrationTests/OpenTelemetrySdkTests.cs:28` +- Modify: `tracer/test/Datadog.Trace.ClrProfiler.IntegrationTests/WebRequestTests.cs:21-22` + +**Interfaces:** +- Consumes: nothing +- Produces: collection name `TestAgentOtlpCollection` for use in `[Collection(nameof(TestAgentOtlpCollection))]` + +- [ ] **Step 1: Create the collection definition** + +```csharp +// +// Unless explicitly stated otherwise all files in this repository are licensed under the Apache 2 License. +// This product includes software developed at Datadog (https://www.datadoghq.com/). Copyright 2017 Datadog, Inc. +// + +using Xunit; + +namespace Datadog.Trace.ClrProfiler.IntegrationTests.Helpers +{ + /// + /// Serializes every test class that reads from the shared ddapm test-agent OTLP session. + /// Those tests call /test/session/clear, which wipes the session for everyone, so they must + /// not run concurrently with each other. + /// + [CollectionDefinition(nameof(TestAgentOtlpCollection), DisableParallelization = true)] + public class TestAgentOtlpCollection + { + } +} +``` + +- [ ] **Step 2: Move `WebRequestTests` into the shared collection** + +Replace lines 21-22 of `WebRequestTests.cs`: + +```csharp + [CollectionDefinition(nameof(WebRequestTests), DisableParallelization = true)] + [Collection(nameof(WebRequestTests))] +``` + +with: + +```csharp + [Collection(nameof(TestAgentOtlpCollection))] +``` + +The existing collection contained only this one class and existed purely to disable parallelization, which the new collection also does. + +- [ ] **Step 3: Add `OpenTelemetrySdkTests` to the shared collection** + +Add `[Collection(nameof(TestAgentOtlpCollection))]` to the attribute list on the class (alongside the existing `[UsesVerify]`). + +- [ ] **Step 4: Verify test discovery is unchanged** + +Run: +```bash +dotnet test tracer/test/Datadog.Trace.ClrProfiler.IntegrationTests/Datadog.Trace.ClrProfiler.IntegrationTests.csproj \ + -f net10.0 --list-tests --filter "FullyQualifiedName~WebRequestTests|FullyQualifiedName~OpenTelemetrySdkTests" \ + | grep -c "WebRequestTests\|OpenTelemetrySdkTests" +``` +Expected: a non-zero count, and no discovery errors. A class in two collections is a runtime error, so a clean listing confirms the attributes are correct. + +- [ ] **Step 5: Commit** + +```bash +git add tracer/test/Datadog.Trace.ClrProfiler.IntegrationTests/Helpers/TestAgentOtlpCollection.cs \ + tracer/test/Datadog.Trace.ClrProfiler.IntegrationTests/OpenTelemetrySdkTests.cs \ + tracer/test/Datadog.Trace.ClrProfiler.IntegrationTests/WebRequestTests.cs +git commit -m "test: serialize test-agent OTLP consumers into a shared xunit collection" +``` + +--- + +### Task 4: Add `WebRequestTests.SubmitsOtlpTraces` and generate the snapshots + +**Files:** +- Modify: `tracer/test/Datadog.Trace.ClrProfiler.IntegrationTests/WebRequestTests.cs` +- Create: `tracer/test/snapshots/WebRequestTests.SubmitsOtlpTraces_DD.verified.txt` +- Create: `tracer/test/snapshots/WebRequestTests.SubmitsOtlpTraces_DD_OtelSemantics.verified.txt` + +**Interfaces:** +- Consumes: everything produced by Tasks 1-3 +- Produces: nothing downstream + +- [ ] **Step 1: Write the test method (it will fail — no snapshot exists yet)** + +Add to `WebRequestTests`, after `SubmitsTracesV1WithOpenTelemetrySemantics`: + +```csharp + [SkippableTheory] + [Trait("Category", "EndToEnd")] + [Trait("RequiresDockerDependency", "true")] + [Trait("DockerGroup", "1")] + [InlineData("http/json", false)] + [InlineData("http/json", true)] + [InlineData("http/protobuf", false)] + [InlineData("http/protobuf", true)] + public async Task SubmitsOtlpTraces(string protocol, bool openTelemetrySemanticsEnabled) + { + SetInstrumentationVerification(); + + var isJson = protocol == "http/json"; + var names = OtlpFieldNames.For(isJson); + var testAgentHost = Environment.GetEnvironmentVariable("TEST_AGENT_HOST") ?? "127.0.0.1"; + + await OtlpSnapshotHelper.ClearTestAgentSessionAsync(testAgentHost); + + var httpPort = TcpPortProvider.GetOpenPort(); + Output.WriteLine($"Assigning port {httpPort} for the httpPort."); + + // OpenTelemetry semantics unilaterally force the v0 schema, so pin v0 for the + // semantics-off case too and keep the two snapshots directly comparable. + SetEnvironmentVariable("DD_TRACE_SPAN_ATTRIBUTE_SCHEMA", "v0"); + SetEnvironmentVariable("DD_TRACE_OTEL_SEMANTICS_ENABLED", openTelemetrySemanticsEnabled.ToString()); + + // OTEL_TRACES_EXPORTER=otlp is what makes the Datadog SDK emit OTLP instead of msgpack + SetEnvironmentVariable("OTEL_TRACES_EXPORTER", "otlp"); + SetEnvironmentVariable("OTEL_EXPORTER_OTLP_PROTOCOL", protocol); + SetEnvironmentVariable("OTEL_EXPORTER_OTLP_ENDPOINT", $"http://{testAgentHost}:4318"); + + var applicationStartTimeUnixNano = DateTimeOffset.UtcNow.ToUnixTimeNanoseconds(); + + // Traces go to the test-agent over OTLP, but telemetry still goes to the mock agent + using var telemetry = this.ConfigureTelemetry(); + using var agent = EnvironmentHelper.GetMockAgent(); + using ProcessResult processResult = await RunSampleAndWaitForExit(agent, arguments: $"Port={httpPort}"); + + var tracesRequests = await OtlpSnapshotHelper.WaitForTestAgentDataAsync($"http://{testAgentHost}:4318/test/session/traces"); + tracesRequests.Should().NotBeNullOrEmpty(); + + OtlpSnapshotHelper.NormalizeResourceAttributes(tracesRequests, names); + OtlpSnapshotHelper.NormalizeSpans(tracesRequests, names, applicationStartTimeUnixNano); + NormalizeWebRequestSpans(tracesRequests, names); + OtlpSnapshotHelper.SortSpanAttributes(tracesRequests); + + // Sort by name, then by the request URL, then by the span's own normalized JSON. + // IDs and timestamps are already normalized, so the last key is total: any two spans + // that still tie are byte-identical and their order cannot affect the snapshot. + var merged = OtlpSnapshotHelper.MergeDatadogRequests( + tracesRequests, + names, + spans => spans.OrderBy(s => s["name"]?.ToString() ?? string.Empty, StringComparer.Ordinal) + .ThenBy(s => OtlpSnapshotHelper.GetAttributeStringValue(s, names, "url.full", "http.url") ?? string.Empty, StringComparer.Ordinal) + .ThenBy(s => s.ToString(Formatting.None), StringComparer.Ordinal)); + + var finalJson = merged.ToString(Formatting.Indented); + + var settings = VerifyHelper.GetSpanVerifierSettings(); +#if NETCOREAPP + // different TFMs use different underlying handlers, which we don't really care about for the snapshots + settings.AddSimpleScrubber("System.Net.Http.HttpClientHandler", "System.Net.Http.SocketsHttpHandler"); +#endif + if (!isJson) + { + OtlpSnapshotHelper.AddProtobufToJsonScrubbers(settings); + } + + var suffix = openTelemetrySemanticsEnabled ? "_OtelSemantics" : string.Empty; + await Verifier.Verify(finalJson, settings) + .UseFileName($"{nameof(WebRequestTests)}.{nameof(SubmitsOtlpTraces)}_DD{suffix}") + .DisableRequireUniquePrefix(); + + await telemetry.AssertIntegrationEnabledAsync(IntegrationId.WebRequest); + VerifyInstrumentation(processResult.Process); + } +``` + +New usings for `WebRequestTests.cs`: `System`, `Datadog.Trace.ClrProfiler.IntegrationTests.Helpers` (already present), `Datadog.Trace.ExtensionMethods` (for `ToUnixTimeNanoseconds`), `Datadog.Trace.Vendors.Newtonsoft.Json` (for `Formatting`), `Datadog.Trace.Vendors.Newtonsoft.Json.Linq` (for `JToken`/`JObject`/`JTokenType`). + +- [ ] **Step 2: Add the WebRequest-specific normalization** + +Add as a private method on `WebRequestTests`: + +```csharp + /// + /// Normalizes the parts of the OTLP payload that are specific to this sample: the randomly + /// assigned listener port, and the one span whose shape changed on .NET 9. + /// + private void NormalizeWebRequestSpans(JToken tracesRequests, OtlpFieldNames names) + { + // The sample's HttpListener binds a random port each run. url.full is covered by + // VerifyHelper's localhost: scrubber, but server.port carries the bare number. + foreach (var attribute in tracesRequests.SelectTokens("$..spans[*].attributes[?(@.key == 'server.port')]")) + { + if (attribute["value"] is JObject value) + { + foreach (var property in value.Properties()) + { + // Preserve the value kind (stringValue vs intValue) so http/json and + // http/protobuf still render identically after scrubbing. + property.Value = property.Value.Type == JTokenType.String ? (JToken)"8080" : (JToken)8080; + } + } + } + +#if NET9_0_OR_GREATER + // .NET 9.0 changed the behaviour when AllowWriteStreamBuffering=false + // The net result is that we end up creating a "WebRequest" span instead + // of an "HttpClient" span in one of the cases. Rather than creating a whole + // separate set of snapshots for .NET 9+, just "fixing" that one span instead. + var rogueSpan = tracesRequests + .SelectTokens("$..spans[*]") + .SingleOrDefault(s => OtlpSnapshotHelper.GetAttributeStringValue(s, names, "url.full", "http.url") + ?.EndsWith("?BeginGetResponseAsync_NoBuffering") == true); + + // it should never be null, but fall through to fail the snapshots for easier debuggability if it is + if (rogueSpan is not null) + { + Output.WriteLine("Updating span with HttpClient tags"); + OtlpSnapshotHelper.SetAttributeStringValue(rogueSpan, names, "component", "HttpMessageHandler"); // previously "WebRequest" + OtlpSnapshotHelper.SetAttributeStringValue(rogueSpan, names, "http-client-handler-type", "System.Net.Http.SocketsHttpHandler"); // previously not set + } +#endif + } +``` + +`SortSpanAttributes` runs *after* this method in Step 1 precisely so the appended `http-client-handler-type` lands in key order rather than at the end of the array. + +- [ ] **Step 3: Build and run one case to watch it fail** + +Run: +```bash +dotnet build tracer/test/Datadog.Trace.ClrProfiler.IntegrationTests/Datadog.Trace.ClrProfiler.IntegrationTests.csproj -f net10.0 +dotnet test tracer/test/Datadog.Trace.ClrProfiler.IntegrationTests/Datadog.Trace.ClrProfiler.IntegrationTests.csproj \ + -f net10.0 --no-build \ + --filter "FullyQualifiedName~WebRequestTests.SubmitsOtlpTraces" +``` +Expected: FAIL — Verify reports a new `.received.txt` with no matching `.verified.txt`. Any *other* failure (no traces returned, assertion on trace ID format, `SingleOrDefault` throwing on multiple matches) is a real bug: fix it before accepting anything. + +- [ ] **Step 4: Inspect the received snapshots before accepting them** + +Run: +```bash +ls tracer/test/snapshots/WebRequestTests.SubmitsOtlpTraces*.received.txt +grep -nE '"(stringValue|string_value)": "[^"]*(:[0-9]{4,5})' tracer/test/snapshots/WebRequestTests.SubmitsOtlpTraces_DD.received.txt | head +grep -n "server.port" -A 3 tracer/test/snapshots/WebRequestTests.SubmitsOtlpTraces_DD.received.txt | head -8 +``` +Expected: exactly two `.received.txt` files. No raw port numbers, GUIDs, absolute paths, hostnames, or non-zero `timeUnixNano` values anywhere. `server.port` renders as `8080`. Confirm `url.full`/`http.url` shows `localhost:00000`. + +Also confirm the two files genuinely differ in the expected way — the `_OtelSemantics` one should carry `http.request.method`, `url.full`, `server.address`, `server.port`, `http.response.status_code`; the other should carry the v0 Datadog tag names: + +```bash +diff <(grep -oE '"key": "[^"]+"' tracer/test/snapshots/WebRequestTests.SubmitsOtlpTraces_DD.received.txt | sort -u) \ + <(grep -oE '"key": "[^"]+"' tracer/test/snapshots/WebRequestTests.SubmitsOtlpTraces_DD_OtelSemantics.received.txt | sort -u) +``` + +- [ ] **Step 5: Accept the snapshots** + +```bash +for f in tracer/test/snapshots/WebRequestTests.SubmitsOtlpTraces*.received.txt; do + mv "$f" "${f%.received.txt}.verified.txt" +done +``` + +- [ ] **Step 6: Re-run twice to prove stability** + +Run the Step 3 test command twice in a row. +Expected: PASS both times, and `git status --porcelain tracer/test/snapshots/` shows only the two new `.verified.txt` files as untracked — no `.received.txt` files. A `.received.txt` appearing here means the output is not deterministic; the sort key or a normalization step is incomplete. + +- [ ] **Step 7: Regression-check the existing msgpack tests** + +Run: +```bash +dotnet test tracer/test/Datadog.Trace.ClrProfiler.IntegrationTests/Datadog.Trace.ClrProfiler.IntegrationTests.csproj \ + -f net10.0 --no-build \ + --filter "FullyQualifiedName~WebRequestTests.SubmitsTraces|FullyQualifiedName~WebRequestTests.TracingDisabled" +git status --porcelain tracer/test/snapshots/ +``` +Expected: all PASS, and `git status` lists only the two new untracked `.verified.txt` files. `WebRequestTests_v0/_v1/_otel` must be unmodified. + +- [ ] **Step 8: Commit** + +```bash +git add tracer/test/Datadog.Trace.ClrProfiler.IntegrationTests/WebRequestTests.cs \ + tracer/test/snapshots/WebRequestTests.SubmitsOtlpTraces_DD.verified.txt \ + tracer/test/snapshots/WebRequestTests.SubmitsOtlpTraces_DD_OtelSemantics.verified.txt +git commit -m "test: add OTLP snapshot tests for Samples.WebRequest" +``` + +--- + +## Final verification + +- [ ] `git status --porcelain tracer/test/snapshots/` is clean apart from the two intended new files. +- [ ] `git diff --stat HEAD~4 -- tracer/test/snapshots/` shows **only** additions of the two new snapshots — zero modifications to existing ones. +- [ ] `dotnet build tracer/test/Datadog.Trace.ClrProfiler.IntegrationTests/Datadog.Trace.ClrProfiler.IntegrationTests.csproj -f net10.0` is warning-clean. +- [ ] `docker compose stop test-agent` when finished. diff --git a/docs/superpowers/specs/2026-07-30-webrequest-otlp-snapshots-design.md b/docs/superpowers/specs/2026-07-30-webrequest-otlp-snapshots-design.md index b70ff5a222e3..c7455972d7f2 100644 --- a/docs/superpowers/specs/2026-07-30-webrequest-otlp-snapshots-design.md +++ b/docs/superpowers/specs/2026-07-30-webrequest-otlp-snapshots-design.md @@ -140,6 +140,27 @@ sorts by `name` only. The helper therefore takes an **optional sort-key selector defaulting to name-only**, preserving current behavior, and `WebRequestTests` passes the composite key described below. +## Test isolation + +`ClearTestAgentSession` clears the test-agent session **globally**. xUnit runs +distinct collections in parallel and this project declares no +`CollectionBehavior`, so once a second class starts clearing the session, a +clear from one class can delete another class's in-flight traces. + +Today `OpenTelemetrySdkTests` is safe only by accident: all of its OTLP tests +share one implicit per-class collection. Adding OTLP tests to `WebRequestTests` +breaks that. + +Fix: a shared `TestAgentOtlpCollection` with `DisableParallelization = true`, +applied to both classes. `WebRequestTests`' existing single-class +`CollectionDefinition` is replaced by it — that collection existed only to +disable parallelization, which the shared one also does. + +The CI cost is close to zero. The non-docker job filters `OpenTelemetrySdkTests` +out entirely via `RequiresDockerDependency!=true`, and the docker job filters out +`WebRequestTests`' msgpack tests, so the only work actually serialized is OTLP +tests against each other — which is the point. + ## Determinism Four sources of instability, each handled explicitly. From 14a47732812aa41d4964acf50709f5dcea2bf0e1 Mon Sep 17 00:00:00 2001 From: Zach Montoya Date: Thu, 30 Jul 2026 18:11:16 -0700 Subject: [PATCH 6/9] test: serialize test-agent OTLP consumers into a shared xunit collection Co-Authored-By: Claude Opus 5 (1M context) --- .../Helpers/TestAgentOtlpCollection.cs | 19 +++++++++++++++++++ .../OpenTelemetrySdkTests.cs | 1 + .../WebRequestTests.cs | 3 +-- 3 files changed, 21 insertions(+), 2 deletions(-) create mode 100644 tracer/test/Datadog.Trace.ClrProfiler.IntegrationTests/Helpers/TestAgentOtlpCollection.cs diff --git a/tracer/test/Datadog.Trace.ClrProfiler.IntegrationTests/Helpers/TestAgentOtlpCollection.cs b/tracer/test/Datadog.Trace.ClrProfiler.IntegrationTests/Helpers/TestAgentOtlpCollection.cs new file mode 100644 index 000000000000..f0636cd965ca --- /dev/null +++ b/tracer/test/Datadog.Trace.ClrProfiler.IntegrationTests/Helpers/TestAgentOtlpCollection.cs @@ -0,0 +1,19 @@ +// +// Unless explicitly stated otherwise all files in this repository are licensed under the Apache 2 License. +// This product includes software developed at Datadog (https://www.datadoghq.com/). Copyright 2017 Datadog, Inc. +// + +using Xunit; + +namespace Datadog.Trace.ClrProfiler.IntegrationTests.Helpers +{ + /// + /// Serializes every test class that reads from the shared ddapm test-agent OTLP session. + /// Those tests call /test/session/clear, which wipes the session for everyone, so they must + /// not run concurrently with each other. + /// + [CollectionDefinition(nameof(TestAgentOtlpCollection), DisableParallelization = true)] + public class TestAgentOtlpCollection + { + } +} diff --git a/tracer/test/Datadog.Trace.ClrProfiler.IntegrationTests/OpenTelemetrySdkTests.cs b/tracer/test/Datadog.Trace.ClrProfiler.IntegrationTests/OpenTelemetrySdkTests.cs index e274cac22fc8..da7cd1c17482 100644 --- a/tracer/test/Datadog.Trace.ClrProfiler.IntegrationTests/OpenTelemetrySdkTests.cs +++ b/tracer/test/Datadog.Trace.ClrProfiler.IntegrationTests/OpenTelemetrySdkTests.cs @@ -25,6 +25,7 @@ namespace Datadog.Trace.ClrProfiler.IntegrationTests [Trait("RequiresDockerDependency", "true")] [Trait("DockerGroup", "1")] [UsesVerify] + [Collection(nameof(TestAgentOtlpCollection))] public class OpenTelemetrySdkTests : TracingIntegrationTest { private static readonly string CustomServiceName = "CustomServiceName"; diff --git a/tracer/test/Datadog.Trace.ClrProfiler.IntegrationTests/WebRequestTests.cs b/tracer/test/Datadog.Trace.ClrProfiler.IntegrationTests/WebRequestTests.cs index 895bc16e0f4f..4081a4c30b13 100644 --- a/tracer/test/Datadog.Trace.ClrProfiler.IntegrationTests/WebRequestTests.cs +++ b/tracer/test/Datadog.Trace.ClrProfiler.IntegrationTests/WebRequestTests.cs @@ -18,8 +18,7 @@ namespace Datadog.Trace.ClrProfiler.IntegrationTests { [UsesVerify] - [CollectionDefinition(nameof(WebRequestTests), DisableParallelization = true)] - [Collection(nameof(WebRequestTests))] + [Collection(nameof(TestAgentOtlpCollection))] public class WebRequestTests : TracingIntegrationTest { public WebRequestTests(ITestOutputHelper output) From 013eef1d67135de4f5db4006be26ffc73831510e Mon Sep 17 00:00:00 2001 From: Zach Montoya Date: Thu, 30 Jul 2026 18:18:44 -0700 Subject: [PATCH 7/9] test: add OTLP snapshot tests for Samples.WebRequest Co-Authored-By: Claude Opus 5 (1M context) --- .../WebRequestTests.cs | 124 + ...estTests.SubmitsOtlpTraces_DD.verified.txt | 10000 ++++++++++++++++ ...tsOtlpTraces_DD_OtelSemantics.verified.txt | 4300 +++++++ 3 files changed, 14424 insertions(+) create mode 100644 tracer/test/snapshots/WebRequestTests.SubmitsOtlpTraces_DD.verified.txt create mode 100644 tracer/test/snapshots/WebRequestTests.SubmitsOtlpTraces_DD_OtelSemantics.verified.txt diff --git a/tracer/test/Datadog.Trace.ClrProfiler.IntegrationTests/WebRequestTests.cs b/tracer/test/Datadog.Trace.ClrProfiler.IntegrationTests/WebRequestTests.cs index 4081a4c30b13..3b4e99ad566d 100644 --- a/tracer/test/Datadog.Trace.ClrProfiler.IntegrationTests/WebRequestTests.cs +++ b/tracer/test/Datadog.Trace.ClrProfiler.IntegrationTests/WebRequestTests.cs @@ -3,13 +3,17 @@ // This product includes software developed at Datadog (https://www.datadoghq.com/). Copyright 2017 Datadog, Inc. // +using System; using System.Globalization; using System.Linq; using System.Text.RegularExpressions; using System.Threading.Tasks; using Datadog.Trace.ClrProfiler.IntegrationTests.Helpers; using Datadog.Trace.Configuration; +using Datadog.Trace.ExtensionMethods; using Datadog.Trace.TestHelpers; +using Datadog.Trace.Vendors.Newtonsoft.Json; +using Datadog.Trace.Vendors.Newtonsoft.Json.Linq; using FluentAssertions; using VerifyXunit; using Xunit; @@ -54,6 +58,83 @@ public WebRequestTests(ITestOutputHelper output) [Trait("SupportsInstrumentationVerification", "True")] public Task SubmitsTracesV1WithOpenTelemetrySemantics() => RunTest(metadataSchemaVersion: "v1", openTelemetrySemanticsEnabled: true); + [SkippableTheory] + [Trait("Category", "EndToEnd")] + [Trait("RequiresDockerDependency", "true")] + [Trait("DockerGroup", "1")] + [InlineData("http/json", false)] + [InlineData("http/json", true)] + [InlineData("http/protobuf", false)] + [InlineData("http/protobuf", true)] + public async Task SubmitsOtlpTraces(string protocol, bool openTelemetrySemanticsEnabled) + { + SetInstrumentationVerification(); + + var isJson = protocol == "http/json"; + var names = OtlpFieldNames.For(isJson); + var testAgentHost = Environment.GetEnvironmentVariable("TEST_AGENT_HOST") ?? "127.0.0.1"; + + await OtlpSnapshotHelper.ClearTestAgentSessionAsync(testAgentHost); + + int httpPort = TcpPortProvider.GetOpenPort(); + Output.WriteLine($"Assigning port {httpPort} for the httpPort."); + + // OpenTelemetry semantics unilaterally force the v0 schema, so pin v0 for the + // semantics-off case too and keep the two snapshots directly comparable. + SetEnvironmentVariable("DD_TRACE_SPAN_ATTRIBUTE_SCHEMA", "v0"); + SetEnvironmentVariable("DD_TRACE_OTEL_SEMANTICS_ENABLED", openTelemetrySemanticsEnabled.ToString()); + + // OTEL_TRACES_EXPORTER=otlp is what makes the Datadog SDK emit OTLP instead of msgpack + SetEnvironmentVariable("OTEL_TRACES_EXPORTER", "otlp"); + SetEnvironmentVariable("OTEL_EXPORTER_OTLP_PROTOCOL", protocol); + SetEnvironmentVariable("OTEL_EXPORTER_OTLP_ENDPOINT", $"http://{testAgentHost}:4318"); + + var applicationStartTimeUnixNano = DateTimeOffset.UtcNow.ToUnixTimeNanoseconds(); + + // Traces go to the test-agent over OTLP, but telemetry still goes to the mock agent + using var telemetry = this.ConfigureTelemetry(); + using var agent = EnvironmentHelper.GetMockAgent(); + using ProcessResult processResult = await RunSampleAndWaitForExit(agent, arguments: $"Port={httpPort}"); + + var tracesRequests = await OtlpSnapshotHelper.WaitForTestAgentDataAsync($"http://{testAgentHost}:4318/test/session/traces"); + tracesRequests.Should().NotBeNullOrEmpty(); + + OtlpSnapshotHelper.NormalizeResourceAttributes(tracesRequests, names); + OtlpSnapshotHelper.NormalizeSpans(tracesRequests, names, applicationStartTimeUnixNano); + NormalizeWebRequestSpans(tracesRequests, names); + OtlpSnapshotHelper.SortSpanAttributes(tracesRequests); + + // Sort by name, then by the request URL, then by the span's own normalized JSON. + // Ids and timestamps are already normalized, so the last key is total: any two spans + // that still tie are byte-identical and their order cannot affect the snapshot. + var merged = OtlpSnapshotHelper.MergeDatadogRequests( + tracesRequests, + names, + spans => spans.OrderBy(s => s["name"]?.ToString() ?? string.Empty, StringComparer.Ordinal) + .ThenBy(s => OtlpSnapshotHelper.GetAttributeStringValue(s, names, "url.full", "http.url") ?? string.Empty, StringComparer.Ordinal) + .ThenBy(s => s.ToString(Formatting.None), StringComparer.Ordinal)); + + var finalJson = merged.ToString(Formatting.Indented); + + var settings = VerifyHelper.GetSpanVerifierSettings(); +#if NETCOREAPP + // different TFMs use different underlying handlers, which we don't really care about for the snapshots + settings.AddSimpleScrubber("System.Net.Http.HttpClientHandler", "System.Net.Http.SocketsHttpHandler"); +#endif + if (!isJson) + { + OtlpSnapshotHelper.AddProtobufToJsonScrubbers(settings); + } + + var suffix = openTelemetrySemanticsEnabled ? "_OtelSemantics" : string.Empty; + await Verifier.Verify(finalJson, settings) + .UseFileName($"{nameof(WebRequestTests)}.{nameof(SubmitsOtlpTraces)}_DD{suffix}") + .DisableRequireUniquePrefix(); + + await telemetry.AssertIntegrationEnabledAsync(IntegrationId.WebRequest); + VerifyInstrumentation(processResult.Process); + } + [SkippableFact] [Trait("Category", "EndToEnd")] [Trait("RunOnWindows", "True")] @@ -83,6 +164,49 @@ public async Task TracingDisabled_DoesNotSubmitsTraces() } } + /// + /// Normalizes the parts of the OTLP payload that are specific to this sample: the randomly + /// assigned listener port, and the one span whose shape changed on .NET 9. + /// + /// The captured OTLP requests. + /// The field-name casing to use. + private void NormalizeWebRequestSpans(JToken tracesRequests, OtlpFieldNames names) + { + // The sample's HttpListener binds a random port each run. url.full is covered by + // VerifyHelper's localhost: scrubber, but server.port carries the bare number. + foreach (var attribute in tracesRequests.SelectTokens("$..spans[*].attributes[?(@.key == 'server.port')]")) + { + if (attribute["value"] is JObject value) + { + foreach (var property in value.Properties()) + { + // Preserve the value kind (stringValue vs intValue) so http/json and + // http/protobuf still render identically after scrubbing. + property.Value = property.Value.Type == JTokenType.String ? (JToken)"8080" : (JToken)8080; + } + } + } + +#if NET9_0_OR_GREATER + // .NET 9.0 changed the behaviour when AllowWriteStreamBuffering=false + // The net result is that we end up creating a "WebRequest" span instead + // of an "HttpClient" span in one of the cases. Rather than creating a whole + // separate set of snapshots for .NET 9+, just "fixing" that one span instead. + var rogueSpan = tracesRequests + .SelectTokens("$..spans[*]") + .SingleOrDefault(s => OtlpSnapshotHelper.GetAttributeStringValue(s, names, "url.full", "http.url") + ?.EndsWith("?BeginGetResponseAsync_NoBuffering") == true); + + // it should never be null, but fall through to fail the snapshots for easier debuggability if it is + if (rogueSpan is not null) + { + Output.WriteLine("Updating span with HttpClient tags"); + OtlpSnapshotHelper.SetAttributeStringValue(rogueSpan, names, "component", "HttpMessageHandler"); // previously "WebRequest" + OtlpSnapshotHelper.SetAttributeStringValue(rogueSpan, names, "http-client-handler-type", "System.Net.Http.SocketsHttpHandler"); // previously not set + } +#endif + } + private async Task RunTest(string metadataSchemaVersion, bool openTelemetrySemanticsEnabled) { SetInstrumentationVerification(); diff --git a/tracer/test/snapshots/WebRequestTests.SubmitsOtlpTraces_DD.verified.txt b/tracer/test/snapshots/WebRequestTests.SubmitsOtlpTraces_DD.verified.txt new file mode 100644 index 000000000000..9c6327a341af --- /dev/null +++ b/tracer/test/snapshots/WebRequestTests.SubmitsOtlpTraces_DD.verified.txt @@ -0,0 +1,10000 @@ +{ + "resourceSpans": [ + { + "resource": { + "attributes": [ + { + "key": "service.name", + "value": { + "stringValue": "Samples.WebRequest" + } + }, + { + "key": "service.version", + "value": { + "stringValue": "1.0.0" + } + }, + { + "key": "deployment.environment.name", + "value": { + "stringValue": "integration_tests" + } + }, + { + "key": "telemetry.sdk.name", + "value": { + "stringValue": "sdk-name" + } + }, + { + "key": "telemetry.sdk.language", + "value": { + "stringValue": "dotnet" + } + }, + { + "key": "telemetry.sdk.version", + "value": { + "stringValue": "sdk-version" + } + }, + { + "key": "git.commit.sha", + "value": { + "stringValue": "normalized-git-commit-sha" + } + }, + { + "key": "git.repository_url", + "value": { + "stringValue": "https://github.com/DataDog/dd-trace-dotnet" + } + }, + { + "key": "runtime-id", + "value": { + "stringValue": "Guid_1" + } + } + ] + }, + "scopeSpans": [ + { + "spans": [ + { + "traceId": "normalized-trace-id", + "spanId": "normalized-span-id", + "parentSpanId": "normalized-parent-span-id", + "name": "BeginGetRequestStream", + "kind": 1, + "startTimeUnixNano": "0", + "endTimeUnixNano": "0", + "attributes": [ + { + "key": "operation.name", + "value": { + "stringValue": "internal" + } + }, + { + "key": "otel.library.name", + "value": { + "stringValue": "Samples.WebRequest" + } + }, + { + "key": "otel.status_code", + "value": { + "stringValue": "STATUS_CODE_UNSET" + } + }, + { + "key": "otel.trace_id", + "value": { + "stringValue": "normalized-otel-trace-id" + } + }, + { + "key": "resource.name", + "value": { + "stringValue": "BeginGetRequestStream" + } + }, + { + "key": "service.name", + "value": { + "stringValue": "samples.webrequest" + } + }, + { + "key": "span.kind", + "value": { + "stringValue": "internal" + } + }, + { + "key": "span.type", + "value": { + "stringValue": "custom" + } + } + ] + }, + { + "traceId": "normalized-trace-id", + "spanId": "normalized-span-id", + "parentSpanId": "normalized-parent-span-id", + "name": "BeginGetRequestStreamWithDistributedTracingHeaders", + "kind": 1, + "startTimeUnixNano": "0", + "endTimeUnixNano": "0", + "attributes": [ + { + "key": "operation.name", + "value": { + "stringValue": "internal" + } + }, + { + "key": "otel.library.name", + "value": { + "stringValue": "Samples.WebRequest" + } + }, + { + "key": "otel.status_code", + "value": { + "stringValue": "STATUS_CODE_UNSET" + } + }, + { + "key": "otel.trace_id", + "value": { + "stringValue": "normalized-otel-trace-id" + } + }, + { + "key": "resource.name", + "value": { + "stringValue": "BeginGetRequestStreamWithDistributedTracingHeaders" + } + }, + { + "key": "service.name", + "value": { + "stringValue": "samples.webrequest" + } + }, + { + "key": "span.kind", + "value": { + "stringValue": "internal" + } + }, + { + "key": "span.type", + "value": { + "stringValue": "custom" + } + } + ] + }, + { + "traceId": "normalized-trace-id", + "spanId": "normalized-span-id", + "parentSpanId": "normalized-parent-span-id", + "name": "BeginGetResponse", + "kind": 1, + "startTimeUnixNano": "0", + "endTimeUnixNano": "0", + "attributes": [ + { + "key": "operation.name", + "value": { + "stringValue": "internal" + } + }, + { + "key": "otel.library.name", + "value": { + "stringValue": "Samples.WebRequest" + } + }, + { + "key": "otel.status_code", + "value": { + "stringValue": "STATUS_CODE_UNSET" + } + }, + { + "key": "otel.trace_id", + "value": { + "stringValue": "normalized-otel-trace-id" + } + }, + { + "key": "resource.name", + "value": { + "stringValue": "BeginGetResponse" + } + }, + { + "key": "service.name", + "value": { + "stringValue": "samples.webrequest" + } + }, + { + "key": "span.kind", + "value": { + "stringValue": "internal" + } + }, + { + "key": "span.type", + "value": { + "stringValue": "custom" + } + } + ] + }, + { + "traceId": "normalized-trace-id", + "spanId": "normalized-span-id", + "parentSpanId": "normalized-parent-span-id", + "name": "BeginGetResponse TaskFactoryFromAsync", + "kind": 1, + "startTimeUnixNano": "0", + "endTimeUnixNano": "0", + "attributes": [ + { + "key": "operation.name", + "value": { + "stringValue": "internal" + } + }, + { + "key": "otel.library.name", + "value": { + "stringValue": "Samples.WebRequest" + } + }, + { + "key": "otel.status_code", + "value": { + "stringValue": "STATUS_CODE_UNSET" + } + }, + { + "key": "otel.trace_id", + "value": { + "stringValue": "normalized-otel-trace-id" + } + }, + { + "key": "resource.name", + "value": { + "stringValue": "BeginGetResponse TaskFactoryFromAsync" + } + }, + { + "key": "service.name", + "value": { + "stringValue": "samples.webrequest" + } + }, + { + "key": "span.kind", + "value": { + "stringValue": "internal" + } + }, + { + "key": "span.type", + "value": { + "stringValue": "custom" + } + } + ] + }, + { + "traceId": "normalized-trace-id", + "spanId": "normalized-span-id", + "parentSpanId": "normalized-parent-span-id", + "name": "BeginGetResponseNotFound", + "kind": 1, + "startTimeUnixNano": "0", + "endTimeUnixNano": "0", + "attributes": [ + { + "key": "operation.name", + "value": { + "stringValue": "internal" + } + }, + { + "key": "otel.library.name", + "value": { + "stringValue": "Samples.WebRequest" + } + }, + { + "key": "otel.status_code", + "value": { + "stringValue": "STATUS_CODE_UNSET" + } + }, + { + "key": "otel.trace_id", + "value": { + "stringValue": "normalized-otel-trace-id" + } + }, + { + "key": "resource.name", + "value": { + "stringValue": "BeginGetResponseNotFound" + } + }, + { + "key": "service.name", + "value": { + "stringValue": "samples.webrequest" + } + }, + { + "key": "span.kind", + "value": { + "stringValue": "internal" + } + }, + { + "key": "span.type", + "value": { + "stringValue": "custom" + } + } + ] + }, + { + "traceId": "normalized-trace-id", + "spanId": "normalized-span-id", + "parentSpanId": "normalized-parent-span-id", + "name": "BeginGetResponseTeapot", + "kind": 1, + "startTimeUnixNano": "0", + "endTimeUnixNano": "0", + "attributes": [ + { + "key": "operation.name", + "value": { + "stringValue": "internal" + } + }, + { + "key": "otel.library.name", + "value": { + "stringValue": "Samples.WebRequest" + } + }, + { + "key": "otel.status_code", + "value": { + "stringValue": "STATUS_CODE_UNSET" + } + }, + { + "key": "otel.trace_id", + "value": { + "stringValue": "normalized-otel-trace-id" + } + }, + { + "key": "resource.name", + "value": { + "stringValue": "BeginGetResponseTeapot" + } + }, + { + "key": "service.name", + "value": { + "stringValue": "samples.webrequest" + } + }, + { + "key": "span.kind", + "value": { + "stringValue": "internal" + } + }, + { + "key": "span.type", + "value": { + "stringValue": "custom" + } + } + ] + }, + { + "traceId": "normalized-trace-id", + "spanId": "normalized-span-id", + "parentSpanId": "normalized-parent-span-id", + "name": "BeginGetResponseWithDistributedTracingHeaders", + "kind": 1, + "startTimeUnixNano": "0", + "endTimeUnixNano": "0", + "attributes": [ + { + "key": "operation.name", + "value": { + "stringValue": "internal" + } + }, + { + "key": "otel.library.name", + "value": { + "stringValue": "Samples.WebRequest" + } + }, + { + "key": "otel.status_code", + "value": { + "stringValue": "STATUS_CODE_UNSET" + } + }, + { + "key": "otel.trace_id", + "value": { + "stringValue": "normalized-otel-trace-id" + } + }, + { + "key": "resource.name", + "value": { + "stringValue": "BeginGetResponseWithDistributedTracingHeaders" + } + }, + { + "key": "service.name", + "value": { + "stringValue": "samples.webrequest" + } + }, + { + "key": "span.kind", + "value": { + "stringValue": "internal" + } + }, + { + "key": "span.type", + "value": { + "stringValue": "custom" + } + } + ] + }, + { + "traceId": "normalized-trace-id", + "spanId": "normalized-span-id", + "parentSpanId": "normalized-parent-span-id", + "name": "DownloadData", + "kind": 1, + "startTimeUnixNano": "0", + "endTimeUnixNano": "0", + "attributes": [ + { + "key": "operation.name", + "value": { + "stringValue": "internal" + } + }, + { + "key": "otel.library.name", + "value": { + "stringValue": "Samples.WebRequest" + } + }, + { + "key": "otel.status_code", + "value": { + "stringValue": "STATUS_CODE_UNSET" + } + }, + { + "key": "otel.trace_id", + "value": { + "stringValue": "normalized-otel-trace-id" + } + }, + { + "key": "resource.name", + "value": { + "stringValue": "DownloadData" + } + }, + { + "key": "service.name", + "value": { + "stringValue": "samples.webrequest" + } + }, + { + "key": "span.kind", + "value": { + "stringValue": "internal" + } + }, + { + "key": "span.type", + "value": { + "stringValue": "custom" + } + } + ] + }, + { + "traceId": "normalized-trace-id", + "spanId": "normalized-span-id", + "parentSpanId": "normalized-parent-span-id", + "name": "DownloadDataAsync", + "kind": 1, + "startTimeUnixNano": "0", + "endTimeUnixNano": "0", + "attributes": [ + { + "key": "operation.name", + "value": { + "stringValue": "internal" + } + }, + { + "key": "otel.library.name", + "value": { + "stringValue": "Samples.WebRequest" + } + }, + { + "key": "otel.status_code", + "value": { + "stringValue": "STATUS_CODE_UNSET" + } + }, + { + "key": "otel.trace_id", + "value": { + "stringValue": "normalized-otel-trace-id" + } + }, + { + "key": "resource.name", + "value": { + "stringValue": "DownloadDataAsync" + } + }, + { + "key": "service.name", + "value": { + "stringValue": "samples.webrequest" + } + }, + { + "key": "span.kind", + "value": { + "stringValue": "internal" + } + }, + { + "key": "span.type", + "value": { + "stringValue": "custom" + } + } + ] + }, + { + "traceId": "normalized-trace-id", + "spanId": "normalized-span-id", + "parentSpanId": "normalized-parent-span-id", + "name": "DownloadDataTaskAsync", + "kind": 1, + "startTimeUnixNano": "0", + "endTimeUnixNano": "0", + "attributes": [ + { + "key": "operation.name", + "value": { + "stringValue": "internal" + } + }, + { + "key": "otel.library.name", + "value": { + "stringValue": "Samples.WebRequest" + } + }, + { + "key": "otel.status_code", + "value": { + "stringValue": "STATUS_CODE_UNSET" + } + }, + { + "key": "otel.trace_id", + "value": { + "stringValue": "normalized-otel-trace-id" + } + }, + { + "key": "resource.name", + "value": { + "stringValue": "DownloadDataTaskAsync" + } + }, + { + "key": "service.name", + "value": { + "stringValue": "samples.webrequest" + } + }, + { + "key": "span.kind", + "value": { + "stringValue": "internal" + } + }, + { + "key": "span.type", + "value": { + "stringValue": "custom" + } + } + ] + }, + { + "traceId": "normalized-trace-id", + "spanId": "normalized-span-id", + "parentSpanId": "normalized-parent-span-id", + "name": "DownloadFile", + "kind": 1, + "startTimeUnixNano": "0", + "endTimeUnixNano": "0", + "attributes": [ + { + "key": "operation.name", + "value": { + "stringValue": "internal" + } + }, + { + "key": "otel.library.name", + "value": { + "stringValue": "Samples.WebRequest" + } + }, + { + "key": "otel.status_code", + "value": { + "stringValue": "STATUS_CODE_UNSET" + } + }, + { + "key": "otel.trace_id", + "value": { + "stringValue": "normalized-otel-trace-id" + } + }, + { + "key": "resource.name", + "value": { + "stringValue": "DownloadFile" + } + }, + { + "key": "service.name", + "value": { + "stringValue": "samples.webrequest" + } + }, + { + "key": "span.kind", + "value": { + "stringValue": "internal" + } + }, + { + "key": "span.type", + "value": { + "stringValue": "custom" + } + } + ] + }, + { + "traceId": "normalized-trace-id", + "spanId": "normalized-span-id", + "parentSpanId": "normalized-parent-span-id", + "name": "DownloadFileAsync", + "kind": 1, + "startTimeUnixNano": "0", + "endTimeUnixNano": "0", + "attributes": [ + { + "key": "operation.name", + "value": { + "stringValue": "internal" + } + }, + { + "key": "otel.library.name", + "value": { + "stringValue": "Samples.WebRequest" + } + }, + { + "key": "otel.status_code", + "value": { + "stringValue": "STATUS_CODE_UNSET" + } + }, + { + "key": "otel.trace_id", + "value": { + "stringValue": "normalized-otel-trace-id" + } + }, + { + "key": "resource.name", + "value": { + "stringValue": "DownloadFileAsync" + } + }, + { + "key": "service.name", + "value": { + "stringValue": "samples.webrequest" + } + }, + { + "key": "span.kind", + "value": { + "stringValue": "internal" + } + }, + { + "key": "span.type", + "value": { + "stringValue": "custom" + } + } + ] + }, + { + "traceId": "normalized-trace-id", + "spanId": "normalized-span-id", + "parentSpanId": "normalized-parent-span-id", + "name": "DownloadFileTaskAsync", + "kind": 1, + "startTimeUnixNano": "0", + "endTimeUnixNano": "0", + "attributes": [ + { + "key": "operation.name", + "value": { + "stringValue": "internal" + } + }, + { + "key": "otel.library.name", + "value": { + "stringValue": "Samples.WebRequest" + } + }, + { + "key": "otel.status_code", + "value": { + "stringValue": "STATUS_CODE_UNSET" + } + }, + { + "key": "otel.trace_id", + "value": { + "stringValue": "normalized-otel-trace-id" + } + }, + { + "key": "resource.name", + "value": { + "stringValue": "DownloadFileTaskAsync" + } + }, + { + "key": "service.name", + "value": { + "stringValue": "samples.webrequest" + } + }, + { + "key": "span.kind", + "value": { + "stringValue": "internal" + } + }, + { + "key": "span.type", + "value": { + "stringValue": "custom" + } + } + ] + }, + { + "traceId": "normalized-trace-id", + "spanId": "normalized-span-id", + "parentSpanId": "normalized-parent-span-id", + "name": "DownloadString", + "kind": 1, + "startTimeUnixNano": "0", + "endTimeUnixNano": "0", + "attributes": [ + { + "key": "operation.name", + "value": { + "stringValue": "internal" + } + }, + { + "key": "otel.library.name", + "value": { + "stringValue": "Samples.WebRequest" + } + }, + { + "key": "otel.status_code", + "value": { + "stringValue": "STATUS_CODE_UNSET" + } + }, + { + "key": "otel.trace_id", + "value": { + "stringValue": "normalized-otel-trace-id" + } + }, + { + "key": "resource.name", + "value": { + "stringValue": "DownloadString" + } + }, + { + "key": "service.name", + "value": { + "stringValue": "samples.webrequest" + } + }, + { + "key": "span.kind", + "value": { + "stringValue": "internal" + } + }, + { + "key": "span.type", + "value": { + "stringValue": "custom" + } + } + ] + }, + { + "traceId": "normalized-trace-id", + "spanId": "normalized-span-id", + "parentSpanId": "normalized-parent-span-id", + "name": "DownloadStringAsync", + "kind": 1, + "startTimeUnixNano": "0", + "endTimeUnixNano": "0", + "attributes": [ + { + "key": "operation.name", + "value": { + "stringValue": "internal" + } + }, + { + "key": "otel.library.name", + "value": { + "stringValue": "Samples.WebRequest" + } + }, + { + "key": "otel.status_code", + "value": { + "stringValue": "STATUS_CODE_UNSET" + } + }, + { + "key": "otel.trace_id", + "value": { + "stringValue": "normalized-otel-trace-id" + } + }, + { + "key": "resource.name", + "value": { + "stringValue": "DownloadStringAsync" + } + }, + { + "key": "service.name", + "value": { + "stringValue": "samples.webrequest" + } + }, + { + "key": "span.kind", + "value": { + "stringValue": "internal" + } + }, + { + "key": "span.type", + "value": { + "stringValue": "custom" + } + } + ] + }, + { + "traceId": "normalized-trace-id", + "spanId": "normalized-span-id", + "parentSpanId": "normalized-parent-span-id", + "name": "DownloadStringTaskAsync", + "kind": 1, + "startTimeUnixNano": "0", + "endTimeUnixNano": "0", + "attributes": [ + { + "key": "operation.name", + "value": { + "stringValue": "internal" + } + }, + { + "key": "otel.library.name", + "value": { + "stringValue": "Samples.WebRequest" + } + }, + { + "key": "otel.status_code", + "value": { + "stringValue": "STATUS_CODE_UNSET" + } + }, + { + "key": "otel.trace_id", + "value": { + "stringValue": "normalized-otel-trace-id" + } + }, + { + "key": "resource.name", + "value": { + "stringValue": "DownloadStringTaskAsync" + } + }, + { + "key": "service.name", + "value": { + "stringValue": "samples.webrequest" + } + }, + { + "key": "span.kind", + "value": { + "stringValue": "internal" + } + }, + { + "key": "span.type", + "value": { + "stringValue": "custom" + } + } + ] + }, + { + "traceId": "normalized-trace-id", + "spanId": "normalized-span-id", + "parentSpanId": "normalized-parent-span-id", + "name": "GET localhost:00000/?/", + "kind": 3, + "startTimeUnixNano": "0", + "endTimeUnixNano": "0", + "attributes": [ + { + "key": "component", + "value": { + "stringValue": "WebRequest" + } + }, + { + "key": "http.method", + "value": { + "stringValue": "GET" + } + }, + { + "key": "http.status_code", + "value": { + "intValue": "200" + } + }, + { + "key": "http.url", + "value": { + "stringValue": "http://localhost:00000/Guid_2/?DownloadData" + } + }, + { + "key": "operation.name", + "value": { + "stringValue": "http.request" + } + }, + { + "key": "out.host", + "value": { + "stringValue": "localhost" + } + }, + { + "key": "resource.name", + "value": { + "stringValue": "GET localhost:00000/?/" + } + }, + { + "key": "runtime-id", + "value": { + "stringValue": "Guid_1" + } + }, + { + "key": "service.name", + "value": { + "stringValue": "samples.webrequest-http-client" + } + }, + { + "key": "span.kind", + "value": { + "stringValue": "client" + } + }, + { + "key": "span.type", + "value": { + "stringValue": "http" + } + } + ] + }, + { + "traceId": "normalized-trace-id", + "spanId": "normalized-span-id", + "parentSpanId": "normalized-parent-span-id", + "name": "GET localhost:00000/?/", + "kind": 3, + "startTimeUnixNano": "0", + "endTimeUnixNano": "0", + "attributes": [ + { + "key": "component", + "value": { + "stringValue": "WebRequest" + } + }, + { + "key": "http.method", + "value": { + "stringValue": "GET" + } + }, + { + "key": "http.status_code", + "value": { + "intValue": "200" + } + }, + { + "key": "http.url", + "value": { + "stringValue": "http://localhost:00000/Guid_2/?DownloadData2" + } + }, + { + "key": "operation.name", + "value": { + "stringValue": "http.request" + } + }, + { + "key": "out.host", + "value": { + "stringValue": "localhost" + } + }, + { + "key": "resource.name", + "value": { + "stringValue": "GET localhost:00000/?/" + } + }, + { + "key": "runtime-id", + "value": { + "stringValue": "Guid_1" + } + }, + { + "key": "service.name", + "value": { + "stringValue": "samples.webrequest-http-client" + } + }, + { + "key": "span.kind", + "value": { + "stringValue": "client" + } + }, + { + "key": "span.type", + "value": { + "stringValue": "http" + } + } + ] + }, + { + "traceId": "normalized-trace-id", + "spanId": "normalized-span-id", + "parentSpanId": "normalized-parent-span-id", + "name": "GET localhost:00000/?/", + "kind": 3, + "startTimeUnixNano": "0", + "endTimeUnixNano": "0", + "attributes": [ + { + "key": "component", + "value": { + "stringValue": "HttpMessageHandler" + } + }, + { + "key": "http-client-handler-type", + "value": { + "stringValue": "System.Net.Http.SocketsHttpHandler" + } + }, + { + "key": "http.method", + "value": { + "stringValue": "GET" + } + }, + { + "key": "http.status_code", + "value": { + "intValue": "200" + } + }, + { + "key": "http.url", + "value": { + "stringValue": "http://localhost:00000/Guid_2/?DownloadDataAsync" + } + }, + { + "key": "operation.name", + "value": { + "stringValue": "http.request" + } + }, + { + "key": "out.host", + "value": { + "stringValue": "localhost" + } + }, + { + "key": "resource.name", + "value": { + "stringValue": "GET localhost:00000/?/" + } + }, + { + "key": "runtime-id", + "value": { + "stringValue": "Guid_1" + } + }, + { + "key": "service.name", + "value": { + "stringValue": "samples.webrequest-http-client" + } + }, + { + "key": "span.kind", + "value": { + "stringValue": "client" + } + }, + { + "key": "span.type", + "value": { + "stringValue": "http" + } + } + ] + }, + { + "traceId": "normalized-trace-id", + "spanId": "normalized-span-id", + "parentSpanId": "normalized-parent-span-id", + "name": "GET localhost:00000/?/", + "kind": 3, + "startTimeUnixNano": "0", + "endTimeUnixNano": "0", + "attributes": [ + { + "key": "component", + "value": { + "stringValue": "HttpMessageHandler" + } + }, + { + "key": "http-client-handler-type", + "value": { + "stringValue": "System.Net.Http.SocketsHttpHandler" + } + }, + { + "key": "http.method", + "value": { + "stringValue": "GET" + } + }, + { + "key": "http.status_code", + "value": { + "intValue": "200" + } + }, + { + "key": "http.url", + "value": { + "stringValue": "http://localhost:00000/Guid_2/?DownloadDataAsync2" + } + }, + { + "key": "operation.name", + "value": { + "stringValue": "http.request" + } + }, + { + "key": "out.host", + "value": { + "stringValue": "localhost" + } + }, + { + "key": "resource.name", + "value": { + "stringValue": "GET localhost:00000/?/" + } + }, + { + "key": "runtime-id", + "value": { + "stringValue": "Guid_1" + } + }, + { + "key": "service.name", + "value": { + "stringValue": "samples.webrequest-http-client" + } + }, + { + "key": "span.kind", + "value": { + "stringValue": "client" + } + }, + { + "key": "span.type", + "value": { + "stringValue": "http" + } + } + ] + }, + { + "traceId": "normalized-trace-id", + "spanId": "normalized-span-id", + "parentSpanId": "normalized-parent-span-id", + "name": "GET localhost:00000/?/", + "kind": 3, + "startTimeUnixNano": "0", + "endTimeUnixNano": "0", + "attributes": [ + { + "key": "component", + "value": { + "stringValue": "HttpMessageHandler" + } + }, + { + "key": "http-client-handler-type", + "value": { + "stringValue": "System.Net.Http.SocketsHttpHandler" + } + }, + { + "key": "http.method", + "value": { + "stringValue": "GET" + } + }, + { + "key": "http.status_code", + "value": { + "intValue": "200" + } + }, + { + "key": "http.url", + "value": { + "stringValue": "http://localhost:00000/Guid_2/?DownloadDataTaskAsync" + } + }, + { + "key": "operation.name", + "value": { + "stringValue": "http.request" + } + }, + { + "key": "out.host", + "value": { + "stringValue": "localhost" + } + }, + { + "key": "resource.name", + "value": { + "stringValue": "GET localhost:00000/?/" + } + }, + { + "key": "runtime-id", + "value": { + "stringValue": "Guid_1" + } + }, + { + "key": "service.name", + "value": { + "stringValue": "samples.webrequest-http-client" + } + }, + { + "key": "span.kind", + "value": { + "stringValue": "client" + } + }, + { + "key": "span.type", + "value": { + "stringValue": "http" + } + } + ] + }, + { + "traceId": "normalized-trace-id", + "spanId": "normalized-span-id", + "parentSpanId": "normalized-parent-span-id", + "name": "GET localhost:00000/?/", + "kind": 3, + "startTimeUnixNano": "0", + "endTimeUnixNano": "0", + "attributes": [ + { + "key": "component", + "value": { + "stringValue": "HttpMessageHandler" + } + }, + { + "key": "http-client-handler-type", + "value": { + "stringValue": "System.Net.Http.SocketsHttpHandler" + } + }, + { + "key": "http.method", + "value": { + "stringValue": "GET" + } + }, + { + "key": "http.status_code", + "value": { + "intValue": "200" + } + }, + { + "key": "http.url", + "value": { + "stringValue": "http://localhost:00000/Guid_2/?DownloadDataTaskAsync2" + } + }, + { + "key": "operation.name", + "value": { + "stringValue": "http.request" + } + }, + { + "key": "out.host", + "value": { + "stringValue": "localhost" + } + }, + { + "key": "resource.name", + "value": { + "stringValue": "GET localhost:00000/?/" + } + }, + { + "key": "runtime-id", + "value": { + "stringValue": "Guid_1" + } + }, + { + "key": "service.name", + "value": { + "stringValue": "samples.webrequest-http-client" + } + }, + { + "key": "span.kind", + "value": { + "stringValue": "client" + } + }, + { + "key": "span.type", + "value": { + "stringValue": "http" + } + } + ] + }, + { + "traceId": "normalized-trace-id", + "spanId": "normalized-span-id", + "parentSpanId": "normalized-parent-span-id", + "name": "GET localhost:00000/?/", + "kind": 3, + "startTimeUnixNano": "0", + "endTimeUnixNano": "0", + "attributes": [ + { + "key": "component", + "value": { + "stringValue": "WebRequest" + } + }, + { + "key": "http.method", + "value": { + "stringValue": "GET" + } + }, + { + "key": "http.status_code", + "value": { + "intValue": "200" + } + }, + { + "key": "http.url", + "value": { + "stringValue": "http://localhost:00000/Guid_2/?DownloadFile" + } + }, + { + "key": "operation.name", + "value": { + "stringValue": "http.request" + } + }, + { + "key": "out.host", + "value": { + "stringValue": "localhost" + } + }, + { + "key": "resource.name", + "value": { + "stringValue": "GET localhost:00000/?/" + } + }, + { + "key": "runtime-id", + "value": { + "stringValue": "Guid_1" + } + }, + { + "key": "service.name", + "value": { + "stringValue": "samples.webrequest-http-client" + } + }, + { + "key": "span.kind", + "value": { + "stringValue": "client" + } + }, + { + "key": "span.type", + "value": { + "stringValue": "http" + } + } + ] + }, + { + "traceId": "normalized-trace-id", + "spanId": "normalized-span-id", + "parentSpanId": "normalized-parent-span-id", + "name": "GET localhost:00000/?/", + "kind": 3, + "startTimeUnixNano": "0", + "endTimeUnixNano": "0", + "attributes": [ + { + "key": "component", + "value": { + "stringValue": "WebRequest" + } + }, + { + "key": "http.method", + "value": { + "stringValue": "GET" + } + }, + { + "key": "http.status_code", + "value": { + "intValue": "200" + } + }, + { + "key": "http.url", + "value": { + "stringValue": "http://localhost:00000/Guid_2/?DownloadFile2" + } + }, + { + "key": "operation.name", + "value": { + "stringValue": "http.request" + } + }, + { + "key": "out.host", + "value": { + "stringValue": "localhost" + } + }, + { + "key": "resource.name", + "value": { + "stringValue": "GET localhost:00000/?/" + } + }, + { + "key": "runtime-id", + "value": { + "stringValue": "Guid_1" + } + }, + { + "key": "service.name", + "value": { + "stringValue": "samples.webrequest-http-client" + } + }, + { + "key": "span.kind", + "value": { + "stringValue": "client" + } + }, + { + "key": "span.type", + "value": { + "stringValue": "http" + } + } + ] + }, + { + "traceId": "normalized-trace-id", + "spanId": "normalized-span-id", + "parentSpanId": "normalized-parent-span-id", + "name": "GET localhost:00000/?/", + "kind": 3, + "startTimeUnixNano": "0", + "endTimeUnixNano": "0", + "attributes": [ + { + "key": "component", + "value": { + "stringValue": "HttpMessageHandler" + } + }, + { + "key": "http-client-handler-type", + "value": { + "stringValue": "System.Net.Http.SocketsHttpHandler" + } + }, + { + "key": "http.method", + "value": { + "stringValue": "GET" + } + }, + { + "key": "http.status_code", + "value": { + "intValue": "200" + } + }, + { + "key": "http.url", + "value": { + "stringValue": "http://localhost:00000/Guid_2/?DownloadFileAsync" + } + }, + { + "key": "operation.name", + "value": { + "stringValue": "http.request" + } + }, + { + "key": "out.host", + "value": { + "stringValue": "localhost" + } + }, + { + "key": "resource.name", + "value": { + "stringValue": "GET localhost:00000/?/" + } + }, + { + "key": "runtime-id", + "value": { + "stringValue": "Guid_1" + } + }, + { + "key": "service.name", + "value": { + "stringValue": "samples.webrequest-http-client" + } + }, + { + "key": "span.kind", + "value": { + "stringValue": "client" + } + }, + { + "key": "span.type", + "value": { + "stringValue": "http" + } + } + ] + }, + { + "traceId": "normalized-trace-id", + "spanId": "normalized-span-id", + "parentSpanId": "normalized-parent-span-id", + "name": "GET localhost:00000/?/", + "kind": 3, + "startTimeUnixNano": "0", + "endTimeUnixNano": "0", + "attributes": [ + { + "key": "component", + "value": { + "stringValue": "HttpMessageHandler" + } + }, + { + "key": "http-client-handler-type", + "value": { + "stringValue": "System.Net.Http.SocketsHttpHandler" + } + }, + { + "key": "http.method", + "value": { + "stringValue": "GET" + } + }, + { + "key": "http.status_code", + "value": { + "intValue": "200" + } + }, + { + "key": "http.url", + "value": { + "stringValue": "http://localhost:00000/Guid_2/?DownloadFileAsync2" + } + }, + { + "key": "operation.name", + "value": { + "stringValue": "http.request" + } + }, + { + "key": "out.host", + "value": { + "stringValue": "localhost" + } + }, + { + "key": "resource.name", + "value": { + "stringValue": "GET localhost:00000/?/" + } + }, + { + "key": "runtime-id", + "value": { + "stringValue": "Guid_1" + } + }, + { + "key": "service.name", + "value": { + "stringValue": "samples.webrequest-http-client" + } + }, + { + "key": "span.kind", + "value": { + "stringValue": "client" + } + }, + { + "key": "span.type", + "value": { + "stringValue": "http" + } + } + ] + }, + { + "traceId": "normalized-trace-id", + "spanId": "normalized-span-id", + "parentSpanId": "normalized-parent-span-id", + "name": "GET localhost:00000/?/", + "kind": 3, + "startTimeUnixNano": "0", + "endTimeUnixNano": "0", + "attributes": [ + { + "key": "component", + "value": { + "stringValue": "HttpMessageHandler" + } + }, + { + "key": "http-client-handler-type", + "value": { + "stringValue": "System.Net.Http.SocketsHttpHandler" + } + }, + { + "key": "http.method", + "value": { + "stringValue": "GET" + } + }, + { + "key": "http.status_code", + "value": { + "intValue": "200" + } + }, + { + "key": "http.url", + "value": { + "stringValue": "http://localhost:00000/Guid_2/?DownloadFileTaskAsync" + } + }, + { + "key": "operation.name", + "value": { + "stringValue": "http.request" + } + }, + { + "key": "out.host", + "value": { + "stringValue": "localhost" + } + }, + { + "key": "resource.name", + "value": { + "stringValue": "GET localhost:00000/?/" + } + }, + { + "key": "runtime-id", + "value": { + "stringValue": "Guid_1" + } + }, + { + "key": "service.name", + "value": { + "stringValue": "samples.webrequest-http-client" + } + }, + { + "key": "span.kind", + "value": { + "stringValue": "client" + } + }, + { + "key": "span.type", + "value": { + "stringValue": "http" + } + } + ] + }, + { + "traceId": "normalized-trace-id", + "spanId": "normalized-span-id", + "parentSpanId": "normalized-parent-span-id", + "name": "GET localhost:00000/?/", + "kind": 3, + "startTimeUnixNano": "0", + "endTimeUnixNano": "0", + "attributes": [ + { + "key": "component", + "value": { + "stringValue": "HttpMessageHandler" + } + }, + { + "key": "http-client-handler-type", + "value": { + "stringValue": "System.Net.Http.SocketsHttpHandler" + } + }, + { + "key": "http.method", + "value": { + "stringValue": "GET" + } + }, + { + "key": "http.status_code", + "value": { + "intValue": "200" + } + }, + { + "key": "http.url", + "value": { + "stringValue": "http://localhost:00000/Guid_2/?DownloadFileTaskAsync2" + } + }, + { + "key": "operation.name", + "value": { + "stringValue": "http.request" + } + }, + { + "key": "out.host", + "value": { + "stringValue": "localhost" + } + }, + { + "key": "resource.name", + "value": { + "stringValue": "GET localhost:00000/?/" + } + }, + { + "key": "runtime-id", + "value": { + "stringValue": "Guid_1" + } + }, + { + "key": "service.name", + "value": { + "stringValue": "samples.webrequest-http-client" + } + }, + { + "key": "span.kind", + "value": { + "stringValue": "client" + } + }, + { + "key": "span.type", + "value": { + "stringValue": "http" + } + } + ] + }, + { + "traceId": "normalized-trace-id", + "spanId": "normalized-span-id", + "parentSpanId": "normalized-parent-span-id", + "name": "GET localhost:00000/?/", + "kind": 3, + "startTimeUnixNano": "0", + "endTimeUnixNano": "0", + "attributes": [ + { + "key": "component", + "value": { + "stringValue": "WebRequest" + } + }, + { + "key": "http.method", + "value": { + "stringValue": "GET" + } + }, + { + "key": "http.status_code", + "value": { + "intValue": "200" + } + }, + { + "key": "http.url", + "value": { + "stringValue": "http://localhost:00000/Guid_2/?DownloadString" + } + }, + { + "key": "operation.name", + "value": { + "stringValue": "http.request" + } + }, + { + "key": "out.host", + "value": { + "stringValue": "localhost" + } + }, + { + "key": "resource.name", + "value": { + "stringValue": "GET localhost:00000/?/" + } + }, + { + "key": "runtime-id", + "value": { + "stringValue": "Guid_1" + } + }, + { + "key": "service.name", + "value": { + "stringValue": "samples.webrequest-http-client" + } + }, + { + "key": "span.kind", + "value": { + "stringValue": "client" + } + }, + { + "key": "span.type", + "value": { + "stringValue": "http" + } + } + ] + }, + { + "traceId": "normalized-trace-id", + "spanId": "normalized-span-id", + "parentSpanId": "normalized-parent-span-id", + "name": "GET localhost:00000/?/", + "kind": 3, + "startTimeUnixNano": "0", + "endTimeUnixNano": "0", + "attributes": [ + { + "key": "component", + "value": { + "stringValue": "WebRequest" + } + }, + { + "key": "http.method", + "value": { + "stringValue": "GET" + } + }, + { + "key": "http.status_code", + "value": { + "intValue": "200" + } + }, + { + "key": "http.url", + "value": { + "stringValue": "http://localhost:00000/Guid_2/?DownloadString2" + } + }, + { + "key": "operation.name", + "value": { + "stringValue": "http.request" + } + }, + { + "key": "out.host", + "value": { + "stringValue": "localhost" + } + }, + { + "key": "resource.name", + "value": { + "stringValue": "GET localhost:00000/?/" + } + }, + { + "key": "runtime-id", + "value": { + "stringValue": "Guid_1" + } + }, + { + "key": "service.name", + "value": { + "stringValue": "samples.webrequest-http-client" + } + }, + { + "key": "span.kind", + "value": { + "stringValue": "client" + } + }, + { + "key": "span.type", + "value": { + "stringValue": "http" + } + } + ] + }, + { + "traceId": "normalized-trace-id", + "spanId": "normalized-span-id", + "parentSpanId": "normalized-parent-span-id", + "name": "GET localhost:00000/?/", + "kind": 3, + "startTimeUnixNano": "0", + "endTimeUnixNano": "0", + "attributes": [ + { + "key": "component", + "value": { + "stringValue": "HttpMessageHandler" + } + }, + { + "key": "http-client-handler-type", + "value": { + "stringValue": "System.Net.Http.SocketsHttpHandler" + } + }, + { + "key": "http.method", + "value": { + "stringValue": "GET" + } + }, + { + "key": "http.status_code", + "value": { + "intValue": "200" + } + }, + { + "key": "http.url", + "value": { + "stringValue": "http://localhost:00000/Guid_2/?DownloadStringAsync" + } + }, + { + "key": "operation.name", + "value": { + "stringValue": "http.request" + } + }, + { + "key": "out.host", + "value": { + "stringValue": "localhost" + } + }, + { + "key": "resource.name", + "value": { + "stringValue": "GET localhost:00000/?/" + } + }, + { + "key": "runtime-id", + "value": { + "stringValue": "Guid_1" + } + }, + { + "key": "service.name", + "value": { + "stringValue": "samples.webrequest-http-client" + } + }, + { + "key": "span.kind", + "value": { + "stringValue": "client" + } + }, + { + "key": "span.type", + "value": { + "stringValue": "http" + } + } + ] + }, + { + "traceId": "normalized-trace-id", + "spanId": "normalized-span-id", + "parentSpanId": "normalized-parent-span-id", + "name": "GET localhost:00000/?/", + "kind": 3, + "startTimeUnixNano": "0", + "endTimeUnixNano": "0", + "attributes": [ + { + "key": "component", + "value": { + "stringValue": "HttpMessageHandler" + } + }, + { + "key": "http-client-handler-type", + "value": { + "stringValue": "System.Net.Http.SocketsHttpHandler" + } + }, + { + "key": "http.method", + "value": { + "stringValue": "GET" + } + }, + { + "key": "http.status_code", + "value": { + "intValue": "200" + } + }, + { + "key": "http.url", + "value": { + "stringValue": "http://localhost:00000/Guid_2/?DownloadStringAsync2" + } + }, + { + "key": "operation.name", + "value": { + "stringValue": "http.request" + } + }, + { + "key": "out.host", + "value": { + "stringValue": "localhost" + } + }, + { + "key": "resource.name", + "value": { + "stringValue": "GET localhost:00000/?/" + } + }, + { + "key": "runtime-id", + "value": { + "stringValue": "Guid_1" + } + }, + { + "key": "service.name", + "value": { + "stringValue": "samples.webrequest-http-client" + } + }, + { + "key": "span.kind", + "value": { + "stringValue": "client" + } + }, + { + "key": "span.type", + "value": { + "stringValue": "http" + } + } + ] + }, + { + "traceId": "normalized-trace-id", + "spanId": "normalized-span-id", + "parentSpanId": "normalized-parent-span-id", + "name": "GET localhost:00000/?/", + "kind": 3, + "startTimeUnixNano": "0", + "endTimeUnixNano": "0", + "attributes": [ + { + "key": "component", + "value": { + "stringValue": "HttpMessageHandler" + } + }, + { + "key": "http-client-handler-type", + "value": { + "stringValue": "System.Net.Http.SocketsHttpHandler" + } + }, + { + "key": "http.method", + "value": { + "stringValue": "GET" + } + }, + { + "key": "http.status_code", + "value": { + "intValue": "200" + } + }, + { + "key": "http.url", + "value": { + "stringValue": "http://localhost:00000/Guid_2/?DownloadStringTaskAsync" + } + }, + { + "key": "operation.name", + "value": { + "stringValue": "http.request" + } + }, + { + "key": "out.host", + "value": { + "stringValue": "localhost" + } + }, + { + "key": "resource.name", + "value": { + "stringValue": "GET localhost:00000/?/" + } + }, + { + "key": "runtime-id", + "value": { + "stringValue": "Guid_1" + } + }, + { + "key": "service.name", + "value": { + "stringValue": "samples.webrequest-http-client" + } + }, + { + "key": "span.kind", + "value": { + "stringValue": "client" + } + }, + { + "key": "span.type", + "value": { + "stringValue": "http" + } + } + ] + }, + { + "traceId": "normalized-trace-id", + "spanId": "normalized-span-id", + "parentSpanId": "normalized-parent-span-id", + "name": "GET localhost:00000/?/", + "kind": 3, + "startTimeUnixNano": "0", + "endTimeUnixNano": "0", + "attributes": [ + { + "key": "component", + "value": { + "stringValue": "HttpMessageHandler" + } + }, + { + "key": "http-client-handler-type", + "value": { + "stringValue": "System.Net.Http.SocketsHttpHandler" + } + }, + { + "key": "http.method", + "value": { + "stringValue": "GET" + } + }, + { + "key": "http.status_code", + "value": { + "intValue": "200" + } + }, + { + "key": "http.url", + "value": { + "stringValue": "http://localhost:00000/Guid_2/?DownloadStringTaskAsync2" + } + }, + { + "key": "operation.name", + "value": { + "stringValue": "http.request" + } + }, + { + "key": "out.host", + "value": { + "stringValue": "localhost" + } + }, + { + "key": "resource.name", + "value": { + "stringValue": "GET localhost:00000/?/" + } + }, + { + "key": "runtime-id", + "value": { + "stringValue": "Guid_1" + } + }, + { + "key": "service.name", + "value": { + "stringValue": "samples.webrequest-http-client" + } + }, + { + "key": "span.kind", + "value": { + "stringValue": "client" + } + }, + { + "key": "span.type", + "value": { + "stringValue": "http" + } + } + ] + }, + { + "traceId": "normalized-trace-id", + "spanId": "normalized-span-id", + "parentSpanId": "normalized-parent-span-id", + "name": "GET localhost:00000/?/", + "kind": 3, + "startTimeUnixNano": "0", + "endTimeUnixNano": "0", + "attributes": [ + { + "key": "component", + "value": { + "stringValue": "WebRequest" + } + }, + { + "key": "http.method", + "value": { + "stringValue": "GET" + } + }, + { + "key": "http.status_code", + "value": { + "intValue": "200" + } + }, + { + "key": "http.url", + "value": { + "stringValue": "http://localhost:00000/Guid_2/?GetResponse" + } + }, + { + "key": "operation.name", + "value": { + "stringValue": "http.request" + } + }, + { + "key": "out.host", + "value": { + "stringValue": "localhost" + } + }, + { + "key": "resource.name", + "value": { + "stringValue": "GET localhost:00000/?/" + } + }, + { + "key": "runtime-id", + "value": { + "stringValue": "Guid_1" + } + }, + { + "key": "service.name", + "value": { + "stringValue": "samples.webrequest-http-client" + } + }, + { + "key": "span.kind", + "value": { + "stringValue": "client" + } + }, + { + "key": "span.type", + "value": { + "stringValue": "http" + } + } + ] + }, + { + "traceId": "normalized-trace-id", + "spanId": "normalized-span-id", + "parentSpanId": "normalized-parent-span-id", + "name": "GET localhost:00000/?/", + "kind": 3, + "startTimeUnixNano": "0", + "endTimeUnixNano": "0", + "attributes": [ + { + "key": "component", + "value": { + "stringValue": "WebRequest" + } + }, + { + "key": "http.method", + "value": { + "stringValue": "GET" + } + }, + { + "key": "http.status_code", + "value": { + "intValue": "200" + } + }, + { + "key": "http.url", + "value": { + "stringValue": "http://localhost:00000/Guid_2/?GetResponse" + } + }, + { + "key": "operation.name", + "value": { + "stringValue": "http.request" + } + }, + { + "key": "out.host", + "value": { + "stringValue": "localhost" + } + }, + { + "key": "resource.name", + "value": { + "stringValue": "GET localhost:00000/?/" + } + }, + { + "key": "runtime-id", + "value": { + "stringValue": "Guid_1" + } + }, + { + "key": "service.name", + "value": { + "stringValue": "samples.webrequest-http-client" + } + }, + { + "key": "span.kind", + "value": { + "stringValue": "client" + } + }, + { + "key": "span.type", + "value": { + "stringValue": "http" + } + } + ] + }, + { + "traceId": "normalized-trace-id", + "spanId": "normalized-span-id", + "parentSpanId": "normalized-parent-span-id", + "name": "GET localhost:00000/?/", + "kind": 3, + "startTimeUnixNano": "0", + "endTimeUnixNano": "0", + "attributes": [ + { + "key": "component", + "value": { + "stringValue": "WebRequest" + } + }, + { + "key": "http.method", + "value": { + "stringValue": "GET" + } + }, + { + "key": "http.status_code", + "value": { + "intValue": "200" + } + }, + { + "key": "http.url", + "value": { + "stringValue": "http://localhost:00000/Guid_2/?GetResponseAsync" + } + }, + { + "key": "operation.name", + "value": { + "stringValue": "http.request" + } + }, + { + "key": "out.host", + "value": { + "stringValue": "localhost" + } + }, + { + "key": "resource.name", + "value": { + "stringValue": "GET localhost:00000/?/" + } + }, + { + "key": "runtime-id", + "value": { + "stringValue": "Guid_1" + } + }, + { + "key": "service.name", + "value": { + "stringValue": "samples.webrequest-http-client" + } + }, + { + "key": "span.kind", + "value": { + "stringValue": "client" + } + }, + { + "key": "span.type", + "value": { + "stringValue": "http" + } + } + ] + }, + { + "traceId": "normalized-trace-id", + "spanId": "normalized-span-id", + "parentSpanId": "normalized-parent-span-id", + "name": "GET localhost:00000/?/", + "kind": 3, + "startTimeUnixNano": "0", + "endTimeUnixNano": "0", + "attributes": [ + { + "key": "component", + "value": { + "stringValue": "WebRequest" + } + }, + { + "key": "http.method", + "value": { + "stringValue": "GET" + } + }, + { + "key": "http.status_code", + "value": { + "intValue": "200" + } + }, + { + "key": "http.url", + "value": { + "stringValue": "http://localhost:00000/Guid_2/?GetResponseAsync" + } + }, + { + "key": "operation.name", + "value": { + "stringValue": "http.request" + } + }, + { + "key": "out.host", + "value": { + "stringValue": "localhost" + } + }, + { + "key": "resource.name", + "value": { + "stringValue": "GET localhost:00000/?/" + } + }, + { + "key": "runtime-id", + "value": { + "stringValue": "Guid_1" + } + }, + { + "key": "service.name", + "value": { + "stringValue": "samples.webrequest-http-client" + } + }, + { + "key": "span.kind", + "value": { + "stringValue": "client" + } + }, + { + "key": "span.type", + "value": { + "stringValue": "http" + } + } + ] + }, + { + "traceId": "normalized-trace-id", + "spanId": "normalized-span-id", + "parentSpanId": "normalized-parent-span-id", + "name": "GET localhost:00000/?/", + "kind": 3, + "startTimeUnixNano": "0", + "endTimeUnixNano": "0", + "attributes": [ + { + "key": "component", + "value": { + "stringValue": "WebRequest" + } + }, + { + "key": "error.msg", + "value": { + "stringValue": "The remote server returned an error: (404) Not Found." + } + }, + { + "key": "error.stack", + "value": { + "stringValue": "System.Net.WebException: The remote server returned an error: (404) Not Found.\n at System.Net.HttpWebRequest.EndGetResponse(IAsyncResult asyncResult)\n at System.Threading.Tasks.TaskFactory`1.FromAsyncCoreLogic(IAsyncResult iar, Func`2 endFunction, Action`1 endAction, Task`1 promise, Boolean requiresSynchronization)\n--- End of stack trace from previous location ---\n at System.Net.WebRequest.GetResponseAsync()" + } + }, + { + "key": "error.type", + "value": { + "stringValue": "System.Net.WebException" + } + }, + { + "key": "http.method", + "value": { + "stringValue": "GET" + } + }, + { + "key": "http.status_code", + "value": { + "intValue": "404" + } + }, + { + "key": "http.url", + "value": { + "stringValue": "http://localhost:00000/Guid_2/?GetResponseAsyncNotFound" + } + }, + { + "key": "operation.name", + "value": { + "stringValue": "http.request" + } + }, + { + "key": "out.host", + "value": { + "stringValue": "localhost" + } + }, + { + "key": "resource.name", + "value": { + "stringValue": "GET localhost:00000/?/" + } + }, + { + "key": "runtime-id", + "value": { + "stringValue": "Guid_1" + } + }, + { + "key": "service.name", + "value": { + "stringValue": "samples.webrequest-http-client" + } + }, + { + "key": "span.kind", + "value": { + "stringValue": "client" + } + }, + { + "key": "span.type", + "value": { + "stringValue": "http" + } + } + ] + }, + { + "traceId": "normalized-trace-id", + "spanId": "normalized-span-id", + "parentSpanId": "normalized-parent-span-id", + "name": "GET localhost:00000/?/", + "kind": 3, + "startTimeUnixNano": "0", + "endTimeUnixNano": "0", + "attributes": [ + { + "key": "component", + "value": { + "stringValue": "WebRequest" + } + }, + { + "key": "error.msg", + "value": { + "stringValue": "The remote server returned an error: (418) ." + } + }, + { + "key": "error.stack", + "value": { + "stringValue": "System.Net.WebException: The remote server returned an error: (418) .\n at System.Net.HttpWebRequest.EndGetResponse(IAsyncResult asyncResult)\n at System.Threading.Tasks.TaskFactory`1.FromAsyncCoreLogic(IAsyncResult iar, Func`2 endFunction, Action`1 endAction, Task`1 promise, Boolean requiresSynchronization)\n--- End of stack trace from previous location ---\n at System.Net.WebRequest.GetResponseAsync()" + } + }, + { + "key": "error.type", + "value": { + "stringValue": "System.Net.WebException" + } + }, + { + "key": "http.method", + "value": { + "stringValue": "GET" + } + }, + { + "key": "http.status_code", + "value": { + "intValue": "418" + } + }, + { + "key": "http.url", + "value": { + "stringValue": "http://localhost:00000/Guid_2/?GetResponseAsyncTeapot" + } + }, + { + "key": "operation.name", + "value": { + "stringValue": "http.request" + } + }, + { + "key": "out.host", + "value": { + "stringValue": "localhost" + } + }, + { + "key": "resource.name", + "value": { + "stringValue": "GET localhost:00000/?/" + } + }, + { + "key": "runtime-id", + "value": { + "stringValue": "Guid_1" + } + }, + { + "key": "service.name", + "value": { + "stringValue": "samples.webrequest-http-client" + } + }, + { + "key": "span.kind", + "value": { + "stringValue": "client" + } + }, + { + "key": "span.type", + "value": { + "stringValue": "http" + } + } + ] + }, + { + "traceId": "normalized-trace-id", + "spanId": "normalized-span-id", + "parentSpanId": "normalized-parent-span-id", + "name": "GET localhost:00000/?/", + "kind": 3, + "startTimeUnixNano": "0", + "endTimeUnixNano": "0", + "attributes": [ + { + "key": "component", + "value": { + "stringValue": "WebRequest" + } + }, + { + "key": "error.msg", + "value": { + "stringValue": "The remote server returned an error: (404) Not Found." + } + }, + { + "key": "error.stack", + "value": { + "stringValue": "System.Net.WebException: The remote server returned an error: (404) Not Found.\n at System.Net.HttpWebRequest.GetResponse()\n at Samples.WebRequest.RequestHelpers.SendWebRequestRequests(Boolean tracingDisabled, String url, String requestContent) in {SolutionDirectory}tracer/test/test-applications/integrations/Samples.WebRequest/RequestHelpers.cs:line 377" + } + }, + { + "key": "error.type", + "value": { + "stringValue": "System.Net.WebException" + } + }, + { + "key": "http.method", + "value": { + "stringValue": "GET" + } + }, + { + "key": "http.status_code", + "value": { + "intValue": "404" + } + }, + { + "key": "http.url", + "value": { + "stringValue": "http://localhost:00000/Guid_2/?GetResponseNotFound" + } + }, + { + "key": "operation.name", + "value": { + "stringValue": "http.request" + } + }, + { + "key": "out.host", + "value": { + "stringValue": "localhost" + } + }, + { + "key": "resource.name", + "value": { + "stringValue": "GET localhost:00000/?/" + } + }, + { + "key": "runtime-id", + "value": { + "stringValue": "Guid_1" + } + }, + { + "key": "service.name", + "value": { + "stringValue": "samples.webrequest-http-client" + } + }, + { + "key": "span.kind", + "value": { + "stringValue": "client" + } + }, + { + "key": "span.type", + "value": { + "stringValue": "http" + } + } + ] + }, + { + "traceId": "normalized-trace-id", + "spanId": "normalized-span-id", + "parentSpanId": "normalized-parent-span-id", + "name": "GET localhost:00000/?/", + "kind": 3, + "startTimeUnixNano": "0", + "endTimeUnixNano": "0", + "attributes": [ + { + "key": "component", + "value": { + "stringValue": "WebRequest" + } + }, + { + "key": "error.msg", + "value": { + "stringValue": "The remote server returned an error: (418) ." + } + }, + { + "key": "error.stack", + "value": { + "stringValue": "System.Net.WebException: The remote server returned an error: (418) .\n at System.Net.HttpWebRequest.GetResponse()\n at Samples.WebRequest.RequestHelpers.SendWebRequestRequests(Boolean tracingDisabled, String url, String requestContent) in {SolutionDirectory}tracer/test/test-applications/integrations/Samples.WebRequest/RequestHelpers.cs:line 400" + } + }, + { + "key": "error.type", + "value": { + "stringValue": "System.Net.WebException" + } + }, + { + "key": "http.method", + "value": { + "stringValue": "GET" + } + }, + { + "key": "http.status_code", + "value": { + "intValue": "418" + } + }, + { + "key": "http.url", + "value": { + "stringValue": "http://localhost:00000/Guid_2/?GetResponseTeapot" + } + }, + { + "key": "operation.name", + "value": { + "stringValue": "http.request" + } + }, + { + "key": "out.host", + "value": { + "stringValue": "localhost" + } + }, + { + "key": "resource.name", + "value": { + "stringValue": "GET localhost:00000/?/" + } + }, + { + "key": "runtime-id", + "value": { + "stringValue": "Guid_1" + } + }, + { + "key": "service.name", + "value": { + "stringValue": "samples.webrequest-http-client" + } + }, + { + "key": "span.kind", + "value": { + "stringValue": "client" + } + }, + { + "key": "span.type", + "value": { + "stringValue": "http" + } + } + ] + }, + { + "traceId": "normalized-trace-id", + "spanId": "normalized-span-id", + "parentSpanId": "normalized-parent-span-id", + "name": "GET localhost:00000/?/", + "kind": 3, + "startTimeUnixNano": "0", + "endTimeUnixNano": "0", + "attributes": [ + { + "key": "component", + "value": { + "stringValue": "WebRequest" + } + }, + { + "key": "http.method", + "value": { + "stringValue": "GET" + } + }, + { + "key": "http.status_code", + "value": { + "intValue": "200" + } + }, + { + "key": "http.url", + "value": { + "stringValue": "http://localhost:00000/Guid_2/?GetResponse_NoBuffering" + } + }, + { + "key": "operation.name", + "value": { + "stringValue": "http.request" + } + }, + { + "key": "out.host", + "value": { + "stringValue": "localhost" + } + }, + { + "key": "resource.name", + "value": { + "stringValue": "GET localhost:00000/?/" + } + }, + { + "key": "runtime-id", + "value": { + "stringValue": "Guid_1" + } + }, + { + "key": "service.name", + "value": { + "stringValue": "samples.webrequest-http-client" + } + }, + { + "key": "span.kind", + "value": { + "stringValue": "client" + } + }, + { + "key": "span.type", + "value": { + "stringValue": "http" + } + } + ] + }, + { + "traceId": "normalized-trace-id", + "spanId": "normalized-span-id", + "parentSpanId": "normalized-parent-span-id", + "name": "GET localhost:00000/?/", + "kind": 3, + "startTimeUnixNano": "0", + "endTimeUnixNano": "0", + "attributes": [ + { + "key": "component", + "value": { + "stringValue": "WebRequest" + } + }, + { + "key": "http.method", + "value": { + "stringValue": "GET" + } + }, + { + "key": "http.status_code", + "value": { + "intValue": "200" + } + }, + { + "key": "http.url", + "value": { + "stringValue": "http://localhost:00000/Guid_2/?OpenRead" + } + }, + { + "key": "operation.name", + "value": { + "stringValue": "http.request" + } + }, + { + "key": "out.host", + "value": { + "stringValue": "localhost" + } + }, + { + "key": "resource.name", + "value": { + "stringValue": "GET localhost:00000/?/" + } + }, + { + "key": "runtime-id", + "value": { + "stringValue": "Guid_1" + } + }, + { + "key": "service.name", + "value": { + "stringValue": "samples.webrequest-http-client" + } + }, + { + "key": "span.kind", + "value": { + "stringValue": "client" + } + }, + { + "key": "span.type", + "value": { + "stringValue": "http" + } + } + ] + }, + { + "traceId": "normalized-trace-id", + "spanId": "normalized-span-id", + "parentSpanId": "normalized-parent-span-id", + "name": "GET localhost:00000/?/", + "kind": 3, + "startTimeUnixNano": "0", + "endTimeUnixNano": "0", + "attributes": [ + { + "key": "component", + "value": { + "stringValue": "WebRequest" + } + }, + { + "key": "http.method", + "value": { + "stringValue": "GET" + } + }, + { + "key": "http.status_code", + "value": { + "intValue": "200" + } + }, + { + "key": "http.url", + "value": { + "stringValue": "http://localhost:00000/Guid_2/?OpenRead2" + } + }, + { + "key": "operation.name", + "value": { + "stringValue": "http.request" + } + }, + { + "key": "out.host", + "value": { + "stringValue": "localhost" + } + }, + { + "key": "resource.name", + "value": { + "stringValue": "GET localhost:00000/?/" + } + }, + { + "key": "runtime-id", + "value": { + "stringValue": "Guid_1" + } + }, + { + "key": "service.name", + "value": { + "stringValue": "samples.webrequest-http-client" + } + }, + { + "key": "span.kind", + "value": { + "stringValue": "client" + } + }, + { + "key": "span.type", + "value": { + "stringValue": "http" + } + } + ] + }, + { + "traceId": "normalized-trace-id", + "spanId": "normalized-span-id", + "parentSpanId": "normalized-parent-span-id", + "name": "GET localhost:00000/?/", + "kind": 3, + "startTimeUnixNano": "0", + "endTimeUnixNano": "0", + "attributes": [ + { + "key": "component", + "value": { + "stringValue": "HttpMessageHandler" + } + }, + { + "key": "http-client-handler-type", + "value": { + "stringValue": "System.Net.Http.SocketsHttpHandler" + } + }, + { + "key": "http.method", + "value": { + "stringValue": "GET" + } + }, + { + "key": "http.status_code", + "value": { + "intValue": "200" + } + }, + { + "key": "http.url", + "value": { + "stringValue": "http://localhost:00000/Guid_2/?OpenReadAsync" + } + }, + { + "key": "operation.name", + "value": { + "stringValue": "http.request" + } + }, + { + "key": "out.host", + "value": { + "stringValue": "localhost" + } + }, + { + "key": "resource.name", + "value": { + "stringValue": "GET localhost:00000/?/" + } + }, + { + "key": "runtime-id", + "value": { + "stringValue": "Guid_1" + } + }, + { + "key": "service.name", + "value": { + "stringValue": "samples.webrequest-http-client" + } + }, + { + "key": "span.kind", + "value": { + "stringValue": "client" + } + }, + { + "key": "span.type", + "value": { + "stringValue": "http" + } + } + ] + }, + { + "traceId": "normalized-trace-id", + "spanId": "normalized-span-id", + "parentSpanId": "normalized-parent-span-id", + "name": "GET localhost:00000/?/", + "kind": 3, + "startTimeUnixNano": "0", + "endTimeUnixNano": "0", + "attributes": [ + { + "key": "component", + "value": { + "stringValue": "HttpMessageHandler" + } + }, + { + "key": "http-client-handler-type", + "value": { + "stringValue": "System.Net.Http.SocketsHttpHandler" + } + }, + { + "key": "http.method", + "value": { + "stringValue": "GET" + } + }, + { + "key": "http.status_code", + "value": { + "intValue": "200" + } + }, + { + "key": "http.url", + "value": { + "stringValue": "http://localhost:00000/Guid_2/?OpenReadAsync2" + } + }, + { + "key": "operation.name", + "value": { + "stringValue": "http.request" + } + }, + { + "key": "out.host", + "value": { + "stringValue": "localhost" + } + }, + { + "key": "resource.name", + "value": { + "stringValue": "GET localhost:00000/?/" + } + }, + { + "key": "runtime-id", + "value": { + "stringValue": "Guid_1" + } + }, + { + "key": "service.name", + "value": { + "stringValue": "samples.webrequest-http-client" + } + }, + { + "key": "span.kind", + "value": { + "stringValue": "client" + } + }, + { + "key": "span.type", + "value": { + "stringValue": "http" + } + } + ] + }, + { + "traceId": "normalized-trace-id", + "spanId": "normalized-span-id", + "parentSpanId": "normalized-parent-span-id", + "name": "GET localhost:00000/?/", + "kind": 3, + "startTimeUnixNano": "0", + "endTimeUnixNano": "0", + "attributes": [ + { + "key": "component", + "value": { + "stringValue": "HttpMessageHandler" + } + }, + { + "key": "http-client-handler-type", + "value": { + "stringValue": "System.Net.Http.SocketsHttpHandler" + } + }, + { + "key": "http.method", + "value": { + "stringValue": "GET" + } + }, + { + "key": "http.status_code", + "value": { + "intValue": "200" + } + }, + { + "key": "http.url", + "value": { + "stringValue": "http://localhost:00000/Guid_2/?OpenReadTaskAsync" + } + }, + { + "key": "operation.name", + "value": { + "stringValue": "http.request" + } + }, + { + "key": "out.host", + "value": { + "stringValue": "localhost" + } + }, + { + "key": "resource.name", + "value": { + "stringValue": "GET localhost:00000/?/" + } + }, + { + "key": "runtime-id", + "value": { + "stringValue": "Guid_1" + } + }, + { + "key": "service.name", + "value": { + "stringValue": "samples.webrequest-http-client" + } + }, + { + "key": "span.kind", + "value": { + "stringValue": "client" + } + }, + { + "key": "span.type", + "value": { + "stringValue": "http" + } + } + ] + }, + { + "traceId": "normalized-trace-id", + "spanId": "normalized-span-id", + "parentSpanId": "normalized-parent-span-id", + "name": "GET localhost:00000/?/", + "kind": 3, + "startTimeUnixNano": "0", + "endTimeUnixNano": "0", + "attributes": [ + { + "key": "component", + "value": { + "stringValue": "HttpMessageHandler" + } + }, + { + "key": "http-client-handler-type", + "value": { + "stringValue": "System.Net.Http.SocketsHttpHandler" + } + }, + { + "key": "http.method", + "value": { + "stringValue": "GET" + } + }, + { + "key": "http.status_code", + "value": { + "intValue": "200" + } + }, + { + "key": "http.url", + "value": { + "stringValue": "http://localhost:00000/Guid_2/?OpenReadTaskAsync2" + } + }, + { + "key": "operation.name", + "value": { + "stringValue": "http.request" + } + }, + { + "key": "out.host", + "value": { + "stringValue": "localhost" + } + }, + { + "key": "resource.name", + "value": { + "stringValue": "GET localhost:00000/?/" + } + }, + { + "key": "runtime-id", + "value": { + "stringValue": "Guid_1" + } + }, + { + "key": "service.name", + "value": { + "stringValue": "samples.webrequest-http-client" + } + }, + { + "key": "span.kind", + "value": { + "stringValue": "client" + } + }, + { + "key": "span.type", + "value": { + "stringValue": "http" + } + } + ] + }, + { + "traceId": "normalized-trace-id", + "spanId": "normalized-span-id", + "parentSpanId": "normalized-parent-span-id", + "name": "GET localhost:00000/?/", + "kind": 3, + "startTimeUnixNano": "0", + "endTimeUnixNano": "0", + "attributes": [ + { + "key": "component", + "value": { + "stringValue": "HttpMessageHandler" + } + }, + { + "key": "http-client-handler-type", + "value": { + "stringValue": "System.Net.Http.SocketsHttpHandler" + } + }, + { + "key": "http.method", + "value": { + "stringValue": "GET" + } + }, + { + "key": "http.status_code", + "value": { + "intValue": "200" + } + }, + { + "key": "http.url", + "value": { + "stringValue": "http://localhost:00000/Guid_2/?TaskFactoryFromAsync" + } + }, + { + "key": "operation.name", + "value": { + "stringValue": "http.request" + } + }, + { + "key": "out.host", + "value": { + "stringValue": "localhost" + } + }, + { + "key": "resource.name", + "value": { + "stringValue": "GET localhost:00000/?/" + } + }, + { + "key": "runtime-id", + "value": { + "stringValue": "Guid_1" + } + }, + { + "key": "service.name", + "value": { + "stringValue": "samples.webrequest-http-client" + } + }, + { + "key": "span.kind", + "value": { + "stringValue": "client" + } + }, + { + "key": "span.type", + "value": { + "stringValue": "http" + } + } + ] + }, + { + "traceId": "normalized-trace-id", + "spanId": "normalized-span-id", + "parentSpanId": "normalized-parent-span-id", + "name": "GetRequestStream", + "kind": 1, + "startTimeUnixNano": "0", + "endTimeUnixNano": "0", + "attributes": [ + { + "key": "operation.name", + "value": { + "stringValue": "internal" + } + }, + { + "key": "otel.library.name", + "value": { + "stringValue": "Samples.WebRequest" + } + }, + { + "key": "otel.status_code", + "value": { + "stringValue": "STATUS_CODE_UNSET" + } + }, + { + "key": "otel.trace_id", + "value": { + "stringValue": "normalized-otel-trace-id" + } + }, + { + "key": "resource.name", + "value": { + "stringValue": "GetRequestStream" + } + }, + { + "key": "service.name", + "value": { + "stringValue": "samples.webrequest" + } + }, + { + "key": "span.kind", + "value": { + "stringValue": "internal" + } + }, + { + "key": "span.type", + "value": { + "stringValue": "custom" + } + } + ] + }, + { + "traceId": "normalized-trace-id", + "spanId": "normalized-span-id", + "parentSpanId": "normalized-parent-span-id", + "name": "GetRequestStreamWithDistributedTracingHeaders", + "kind": 1, + "startTimeUnixNano": "0", + "endTimeUnixNano": "0", + "attributes": [ + { + "key": "operation.name", + "value": { + "stringValue": "internal" + } + }, + { + "key": "otel.library.name", + "value": { + "stringValue": "Samples.WebRequest" + } + }, + { + "key": "otel.status_code", + "value": { + "stringValue": "STATUS_CODE_UNSET" + } + }, + { + "key": "otel.trace_id", + "value": { + "stringValue": "normalized-otel-trace-id" + } + }, + { + "key": "resource.name", + "value": { + "stringValue": "GetRequestStreamWithDistributedTracingHeaders" + } + }, + { + "key": "service.name", + "value": { + "stringValue": "samples.webrequest" + } + }, + { + "key": "span.kind", + "value": { + "stringValue": "internal" + } + }, + { + "key": "span.type", + "value": { + "stringValue": "custom" + } + } + ] + }, + { + "traceId": "normalized-trace-id", + "spanId": "normalized-span-id", + "parentSpanId": "normalized-parent-span-id", + "name": "GetResponse", + "kind": 1, + "startTimeUnixNano": "0", + "endTimeUnixNano": "0", + "attributes": [ + { + "key": "operation.name", + "value": { + "stringValue": "internal" + } + }, + { + "key": "otel.library.name", + "value": { + "stringValue": "Samples.WebRequest" + } + }, + { + "key": "otel.status_code", + "value": { + "stringValue": "STATUS_CODE_UNSET" + } + }, + { + "key": "otel.trace_id", + "value": { + "stringValue": "normalized-otel-trace-id" + } + }, + { + "key": "resource.name", + "value": { + "stringValue": "GetResponse" + } + }, + { + "key": "service.name", + "value": { + "stringValue": "samples.webrequest" + } + }, + { + "key": "span.kind", + "value": { + "stringValue": "internal" + } + }, + { + "key": "span.type", + "value": { + "stringValue": "custom" + } + } + ] + }, + { + "traceId": "normalized-trace-id", + "spanId": "normalized-span-id", + "parentSpanId": "normalized-parent-span-id", + "name": "GetResponseAsync", + "kind": 1, + "startTimeUnixNano": "0", + "endTimeUnixNano": "0", + "attributes": [ + { + "key": "operation.name", + "value": { + "stringValue": "internal" + } + }, + { + "key": "otel.library.name", + "value": { + "stringValue": "Samples.WebRequest" + } + }, + { + "key": "otel.status_code", + "value": { + "stringValue": "STATUS_CODE_UNSET" + } + }, + { + "key": "otel.trace_id", + "value": { + "stringValue": "normalized-otel-trace-id" + } + }, + { + "key": "resource.name", + "value": { + "stringValue": "GetResponseAsync" + } + }, + { + "key": "service.name", + "value": { + "stringValue": "samples.webrequest" + } + }, + { + "key": "span.kind", + "value": { + "stringValue": "internal" + } + }, + { + "key": "span.type", + "value": { + "stringValue": "custom" + } + } + ] + }, + { + "traceId": "normalized-trace-id", + "spanId": "normalized-span-id", + "parentSpanId": "normalized-parent-span-id", + "name": "GetResponseAsyncNotFound", + "kind": 1, + "startTimeUnixNano": "0", + "endTimeUnixNano": "0", + "attributes": [ + { + "key": "operation.name", + "value": { + "stringValue": "internal" + } + }, + { + "key": "otel.library.name", + "value": { + "stringValue": "Samples.WebRequest" + } + }, + { + "key": "otel.status_code", + "value": { + "stringValue": "STATUS_CODE_UNSET" + } + }, + { + "key": "otel.trace_id", + "value": { + "stringValue": "normalized-otel-trace-id" + } + }, + { + "key": "resource.name", + "value": { + "stringValue": "GetResponseAsyncNotFound" + } + }, + { + "key": "service.name", + "value": { + "stringValue": "samples.webrequest" + } + }, + { + "key": "span.kind", + "value": { + "stringValue": "internal" + } + }, + { + "key": "span.type", + "value": { + "stringValue": "custom" + } + } + ] + }, + { + "traceId": "normalized-trace-id", + "spanId": "normalized-span-id", + "parentSpanId": "normalized-parent-span-id", + "name": "GetResponseAsyncTeapot", + "kind": 1, + "startTimeUnixNano": "0", + "endTimeUnixNano": "0", + "attributes": [ + { + "key": "operation.name", + "value": { + "stringValue": "internal" + } + }, + { + "key": "otel.library.name", + "value": { + "stringValue": "Samples.WebRequest" + } + }, + { + "key": "otel.status_code", + "value": { + "stringValue": "STATUS_CODE_UNSET" + } + }, + { + "key": "otel.trace_id", + "value": { + "stringValue": "normalized-otel-trace-id" + } + }, + { + "key": "resource.name", + "value": { + "stringValue": "GetResponseAsyncTeapot" + } + }, + { + "key": "service.name", + "value": { + "stringValue": "samples.webrequest" + } + }, + { + "key": "span.kind", + "value": { + "stringValue": "internal" + } + }, + { + "key": "span.type", + "value": { + "stringValue": "custom" + } + } + ] + }, + { + "traceId": "normalized-trace-id", + "spanId": "normalized-span-id", + "parentSpanId": "normalized-parent-span-id", + "name": "GetResponseAsyncWithDistributedTracingHeaders", + "kind": 1, + "startTimeUnixNano": "0", + "endTimeUnixNano": "0", + "attributes": [ + { + "key": "operation.name", + "value": { + "stringValue": "internal" + } + }, + { + "key": "otel.library.name", + "value": { + "stringValue": "Samples.WebRequest" + } + }, + { + "key": "otel.status_code", + "value": { + "stringValue": "STATUS_CODE_UNSET" + } + }, + { + "key": "otel.trace_id", + "value": { + "stringValue": "normalized-otel-trace-id" + } + }, + { + "key": "resource.name", + "value": { + "stringValue": "GetResponseAsyncWithDistributedTracingHeaders" + } + }, + { + "key": "service.name", + "value": { + "stringValue": "samples.webrequest" + } + }, + { + "key": "span.kind", + "value": { + "stringValue": "internal" + } + }, + { + "key": "span.type", + "value": { + "stringValue": "custom" + } + } + ] + }, + { + "traceId": "normalized-trace-id", + "spanId": "normalized-span-id", + "parentSpanId": "normalized-parent-span-id", + "name": "GetResponseNotFound", + "kind": 1, + "startTimeUnixNano": "0", + "endTimeUnixNano": "0", + "attributes": [ + { + "key": "operation.name", + "value": { + "stringValue": "internal" + } + }, + { + "key": "otel.library.name", + "value": { + "stringValue": "Samples.WebRequest" + } + }, + { + "key": "otel.status_code", + "value": { + "stringValue": "STATUS_CODE_UNSET" + } + }, + { + "key": "otel.trace_id", + "value": { + "stringValue": "normalized-otel-trace-id" + } + }, + { + "key": "resource.name", + "value": { + "stringValue": "GetResponseNotFound" + } + }, + { + "key": "service.name", + "value": { + "stringValue": "samples.webrequest" + } + }, + { + "key": "span.kind", + "value": { + "stringValue": "internal" + } + }, + { + "key": "span.type", + "value": { + "stringValue": "custom" + } + } + ] + }, + { + "traceId": "normalized-trace-id", + "spanId": "normalized-span-id", + "parentSpanId": "normalized-parent-span-id", + "name": "GetResponseTeapot", + "kind": 1, + "startTimeUnixNano": "0", + "endTimeUnixNano": "0", + "attributes": [ + { + "key": "operation.name", + "value": { + "stringValue": "internal" + } + }, + { + "key": "otel.library.name", + "value": { + "stringValue": "Samples.WebRequest" + } + }, + { + "key": "otel.status_code", + "value": { + "stringValue": "STATUS_CODE_UNSET" + } + }, + { + "key": "otel.trace_id", + "value": { + "stringValue": "normalized-otel-trace-id" + } + }, + { + "key": "resource.name", + "value": { + "stringValue": "GetResponseTeapot" + } + }, + { + "key": "service.name", + "value": { + "stringValue": "samples.webrequest" + } + }, + { + "key": "span.kind", + "value": { + "stringValue": "internal" + } + }, + { + "key": "span.type", + "value": { + "stringValue": "custom" + } + } + ] + }, + { + "traceId": "normalized-trace-id", + "spanId": "normalized-span-id", + "parentSpanId": "normalized-parent-span-id", + "name": "GetResponseWithDistributedTracingHeaders", + "kind": 1, + "startTimeUnixNano": "0", + "endTimeUnixNano": "0", + "attributes": [ + { + "key": "operation.name", + "value": { + "stringValue": "internal" + } + }, + { + "key": "otel.library.name", + "value": { + "stringValue": "Samples.WebRequest" + } + }, + { + "key": "otel.status_code", + "value": { + "stringValue": "STATUS_CODE_UNSET" + } + }, + { + "key": "otel.trace_id", + "value": { + "stringValue": "normalized-otel-trace-id" + } + }, + { + "key": "resource.name", + "value": { + "stringValue": "GetResponseWithDistributedTracingHeaders" + } + }, + { + "key": "service.name", + "value": { + "stringValue": "samples.webrequest" + } + }, + { + "key": "span.kind", + "value": { + "stringValue": "internal" + } + }, + { + "key": "span.type", + "value": { + "stringValue": "custom" + } + } + ] + }, + { + "traceId": "normalized-trace-id", + "spanId": "normalized-span-id", + "parentSpanId": "normalized-parent-span-id", + "name": "OpenRead", + "kind": 1, + "startTimeUnixNano": "0", + "endTimeUnixNano": "0", + "attributes": [ + { + "key": "operation.name", + "value": { + "stringValue": "internal" + } + }, + { + "key": "otel.library.name", + "value": { + "stringValue": "Samples.WebRequest" + } + }, + { + "key": "otel.status_code", + "value": { + "stringValue": "STATUS_CODE_UNSET" + } + }, + { + "key": "otel.trace_id", + "value": { + "stringValue": "normalized-otel-trace-id" + } + }, + { + "key": "resource.name", + "value": { + "stringValue": "OpenRead" + } + }, + { + "key": "service.name", + "value": { + "stringValue": "samples.webrequest" + } + }, + { + "key": "span.kind", + "value": { + "stringValue": "internal" + } + }, + { + "key": "span.type", + "value": { + "stringValue": "custom" + } + } + ] + }, + { + "traceId": "normalized-trace-id", + "spanId": "normalized-span-id", + "parentSpanId": "normalized-parent-span-id", + "name": "OpenReadAsync", + "kind": 1, + "startTimeUnixNano": "0", + "endTimeUnixNano": "0", + "attributes": [ + { + "key": "operation.name", + "value": { + "stringValue": "internal" + } + }, + { + "key": "otel.library.name", + "value": { + "stringValue": "Samples.WebRequest" + } + }, + { + "key": "otel.status_code", + "value": { + "stringValue": "STATUS_CODE_UNSET" + } + }, + { + "key": "otel.trace_id", + "value": { + "stringValue": "normalized-otel-trace-id" + } + }, + { + "key": "resource.name", + "value": { + "stringValue": "OpenReadAsync" + } + }, + { + "key": "service.name", + "value": { + "stringValue": "samples.webrequest" + } + }, + { + "key": "span.kind", + "value": { + "stringValue": "internal" + } + }, + { + "key": "span.type", + "value": { + "stringValue": "custom" + } + } + ] + }, + { + "traceId": "normalized-trace-id", + "spanId": "normalized-span-id", + "parentSpanId": "normalized-parent-span-id", + "name": "OpenReadTaskAsync", + "kind": 1, + "startTimeUnixNano": "0", + "endTimeUnixNano": "0", + "attributes": [ + { + "key": "operation.name", + "value": { + "stringValue": "internal" + } + }, + { + "key": "otel.library.name", + "value": { + "stringValue": "Samples.WebRequest" + } + }, + { + "key": "otel.status_code", + "value": { + "stringValue": "STATUS_CODE_UNSET" + } + }, + { + "key": "otel.trace_id", + "value": { + "stringValue": "normalized-otel-trace-id" + } + }, + { + "key": "resource.name", + "value": { + "stringValue": "OpenReadTaskAsync" + } + }, + { + "key": "service.name", + "value": { + "stringValue": "samples.webrequest" + } + }, + { + "key": "span.kind", + "value": { + "stringValue": "internal" + } + }, + { + "key": "span.type", + "value": { + "stringValue": "custom" + } + } + ] + }, + { + "traceId": "normalized-trace-id", + "spanId": "normalized-span-id", + "name": "POST localhost:00000/?/", + "kind": 3, + "startTimeUnixNano": "0", + "endTimeUnixNano": "0", + "attributes": [ + { + "key": "component", + "value": { + "stringValue": "WebRequest" + } + }, + { + "key": "http.method", + "value": { + "stringValue": "POST" + } + }, + { + "key": "http.status_code", + "value": { + "intValue": "200" + } + }, + { + "key": "http.url", + "value": { + "stringValue": "http://localhost:00000/Guid_2/?BeginGetRequestStream" + } + }, + { + "key": "operation.name", + "value": { + "stringValue": "http.request" + } + }, + { + "key": "out.host", + "value": { + "stringValue": "localhost" + } + }, + { + "key": "resource.name", + "value": { + "stringValue": "POST localhost:00000/?/" + } + }, + { + "key": "runtime-id", + "value": { + "stringValue": "Guid_1" + } + }, + { + "key": "service.name", + "value": { + "stringValue": "samples.webrequest-http-client" + } + }, + { + "key": "span.kind", + "value": { + "stringValue": "client" + } + }, + { + "key": "span.type", + "value": { + "stringValue": "http" + } + } + ] + }, + { + "traceId": "normalized-trace-id", + "spanId": "normalized-span-id", + "parentSpanId": "normalized-parent-span-id", + "name": "POST localhost:00000/?/", + "kind": 3, + "startTimeUnixNano": "0", + "endTimeUnixNano": "0", + "attributes": [ + { + "key": "component", + "value": { + "stringValue": "WebRequest" + } + }, + { + "key": "http.method", + "value": { + "stringValue": "POST" + } + }, + { + "key": "http.status_code", + "value": { + "intValue": "200" + } + }, + { + "key": "http.url", + "value": { + "stringValue": "http://localhost:00000/Guid_2/?BeginGetRequestStream" + } + }, + { + "key": "operation.name", + "value": { + "stringValue": "http.request" + } + }, + { + "key": "out.host", + "value": { + "stringValue": "localhost" + } + }, + { + "key": "resource.name", + "value": { + "stringValue": "POST localhost:00000/?/" + } + }, + { + "key": "runtime-id", + "value": { + "stringValue": "Guid_1" + } + }, + { + "key": "service.name", + "value": { + "stringValue": "samples.webrequest-http-client" + } + }, + { + "key": "span.kind", + "value": { + "stringValue": "client" + } + }, + { + "key": "span.type", + "value": { + "stringValue": "http" + } + } + ] + }, + { + "traceId": "normalized-trace-id", + "spanId": "normalized-span-id", + "parentSpanId": "normalized-parent-span-id", + "name": "POST localhost:00000/?/", + "kind": 3, + "startTimeUnixNano": "0", + "endTimeUnixNano": "0", + "attributes": [ + { + "key": "component", + "value": { + "stringValue": "WebRequest" + } + }, + { + "key": "http.method", + "value": { + "stringValue": "POST" + } + }, + { + "key": "http.status_code", + "value": { + "intValue": "200" + } + }, + { + "key": "http.url", + "value": { + "stringValue": "http://localhost:00000/Guid_2/?BeginGetRequestStream" + } + }, + { + "key": "operation.name", + "value": { + "stringValue": "http.request" + } + }, + { + "key": "out.host", + "value": { + "stringValue": "localhost" + } + }, + { + "key": "resource.name", + "value": { + "stringValue": "POST localhost:00000/?/" + } + }, + { + "key": "runtime-id", + "value": { + "stringValue": "Guid_1" + } + }, + { + "key": "service.name", + "value": { + "stringValue": "samples.webrequest-http-client" + } + }, + { + "key": "span.kind", + "value": { + "stringValue": "client" + } + }, + { + "key": "span.type", + "value": { + "stringValue": "http" + } + } + ] + }, + { + "traceId": "normalized-trace-id", + "spanId": "normalized-span-id", + "parentSpanId": "normalized-parent-span-id", + "name": "POST localhost:00000/?/", + "kind": 3, + "startTimeUnixNano": "0", + "endTimeUnixNano": "0", + "attributes": [ + { + "key": "component", + "value": { + "stringValue": "WebRequest" + } + }, + { + "key": "http.method", + "value": { + "stringValue": "POST" + } + }, + { + "key": "http.status_code", + "value": { + "intValue": "200" + } + }, + { + "key": "http.url", + "value": { + "stringValue": "http://localhost:00000/Guid_2/?BeginGetRequestStream_NoBuffering" + } + }, + { + "key": "operation.name", + "value": { + "stringValue": "http.request" + } + }, + { + "key": "out.host", + "value": { + "stringValue": "localhost" + } + }, + { + "key": "resource.name", + "value": { + "stringValue": "POST localhost:00000/?/" + } + }, + { + "key": "runtime-id", + "value": { + "stringValue": "Guid_1" + } + }, + { + "key": "service.name", + "value": { + "stringValue": "samples.webrequest-http-client" + } + }, + { + "key": "span.kind", + "value": { + "stringValue": "client" + } + }, + { + "key": "span.type", + "value": { + "stringValue": "http" + } + } + ] + }, + { + "traceId": "normalized-trace-id", + "spanId": "normalized-span-id", + "parentSpanId": "normalized-parent-span-id", + "name": "POST localhost:00000/?/", + "kind": 3, + "startTimeUnixNano": "0", + "endTimeUnixNano": "0", + "attributes": [ + { + "key": "component", + "value": { + "stringValue": "HttpMessageHandler" + } + }, + { + "key": "http-client-handler-type", + "value": { + "stringValue": "System.Net.Http.SocketsHttpHandler" + } + }, + { + "key": "http.method", + "value": { + "stringValue": "POST" + } + }, + { + "key": "http.status_code", + "value": { + "intValue": "200" + } + }, + { + "key": "http.url", + "value": { + "stringValue": "http://localhost:00000/Guid_2/?BeginGetResponseAsync" + } + }, + { + "key": "operation.name", + "value": { + "stringValue": "http.request" + } + }, + { + "key": "out.host", + "value": { + "stringValue": "localhost" + } + }, + { + "key": "resource.name", + "value": { + "stringValue": "POST localhost:00000/?/" + } + }, + { + "key": "runtime-id", + "value": { + "stringValue": "Guid_1" + } + }, + { + "key": "service.name", + "value": { + "stringValue": "samples.webrequest-http-client" + } + }, + { + "key": "span.kind", + "value": { + "stringValue": "client" + } + }, + { + "key": "span.type", + "value": { + "stringValue": "http" + } + } + ] + }, + { + "traceId": "normalized-trace-id", + "spanId": "normalized-span-id", + "parentSpanId": "normalized-parent-span-id", + "name": "POST localhost:00000/?/", + "kind": 3, + "startTimeUnixNano": "0", + "endTimeUnixNano": "0", + "attributes": [ + { + "key": "component", + "value": { + "stringValue": "HttpMessageHandler" + } + }, + { + "key": "http-client-handler-type", + "value": { + "stringValue": "System.Net.Http.SocketsHttpHandler" + } + }, + { + "key": "http.method", + "value": { + "stringValue": "POST" + } + }, + { + "key": "http.status_code", + "value": { + "intValue": "200" + } + }, + { + "key": "http.url", + "value": { + "stringValue": "http://localhost:00000/Guid_2/?BeginGetResponseAsync" + } + }, + { + "key": "operation.name", + "value": { + "stringValue": "http.request" + } + }, + { + "key": "out.host", + "value": { + "stringValue": "localhost" + } + }, + { + "key": "resource.name", + "value": { + "stringValue": "POST localhost:00000/?/" + } + }, + { + "key": "runtime-id", + "value": { + "stringValue": "Guid_1" + } + }, + { + "key": "service.name", + "value": { + "stringValue": "samples.webrequest-http-client" + } + }, + { + "key": "span.kind", + "value": { + "stringValue": "client" + } + }, + { + "key": "span.type", + "value": { + "stringValue": "http" + } + } + ] + }, + { + "traceId": "normalized-trace-id", + "spanId": "normalized-span-id", + "parentSpanId": "normalized-parent-span-id", + "name": "POST localhost:00000/?/", + "kind": 3, + "startTimeUnixNano": "0", + "endTimeUnixNano": "0", + "attributes": [ + { + "key": "component", + "value": { + "stringValue": "HttpMessageHandler" + } + }, + { + "key": "http-client-handler-type", + "value": { + "stringValue": "System.Net.Http.SocketsHttpHandler" + } + }, + { + "key": "http.method", + "value": { + "stringValue": "POST" + } + }, + { + "key": "http.status_code", + "value": { + "intValue": "200" + } + }, + { + "key": "http.url", + "value": { + "stringValue": "http://localhost:00000/Guid_2/?BeginGetResponseAsync_NoBuffering" + } + }, + { + "key": "operation.name", + "value": { + "stringValue": "http.request" + } + }, + { + "key": "out.host", + "value": { + "stringValue": "localhost" + } + }, + { + "key": "resource.name", + "value": { + "stringValue": "POST localhost:00000/?/" + } + }, + { + "key": "runtime-id", + "value": { + "stringValue": "Guid_1" + } + }, + { + "key": "service.name", + "value": { + "stringValue": "samples.webrequest-http-client" + } + }, + { + "key": "span.kind", + "value": { + "stringValue": "client" + } + }, + { + "key": "span.type", + "value": { + "stringValue": "http" + } + } + ] + }, + { + "traceId": "normalized-trace-id", + "spanId": "normalized-span-id", + "parentSpanId": "normalized-parent-span-id", + "name": "POST localhost:00000/?/", + "kind": 3, + "startTimeUnixNano": "0", + "endTimeUnixNano": "0", + "attributes": [ + { + "key": "component", + "value": { + "stringValue": "HttpMessageHandler" + } + }, + { + "key": "http-client-handler-type", + "value": { + "stringValue": "System.Net.Http.SocketsHttpHandler" + } + }, + { + "key": "http.method", + "value": { + "stringValue": "POST" + } + }, + { + "key": "http.status_code", + "value": { + "intValue": "404" + } + }, + { + "key": "http.url", + "value": { + "stringValue": "http://localhost:00000/Guid_2/?BeginGetResponseNotFoundAsync" + } + }, + { + "key": "operation.name", + "value": { + "stringValue": "http.request" + } + }, + { + "key": "out.host", + "value": { + "stringValue": "localhost" + } + }, + { + "key": "resource.name", + "value": { + "stringValue": "POST localhost:00000/?/" + } + }, + { + "key": "runtime-id", + "value": { + "stringValue": "Guid_1" + } + }, + { + "key": "service.name", + "value": { + "stringValue": "samples.webrequest-http-client" + } + }, + { + "key": "span.kind", + "value": { + "stringValue": "client" + } + }, + { + "key": "span.type", + "value": { + "stringValue": "http" + } + } + ] + }, + { + "traceId": "normalized-trace-id", + "spanId": "normalized-span-id", + "parentSpanId": "normalized-parent-span-id", + "name": "POST localhost:00000/?/", + "kind": 3, + "startTimeUnixNano": "0", + "endTimeUnixNano": "0", + "attributes": [ + { + "key": "component", + "value": { + "stringValue": "HttpMessageHandler" + } + }, + { + "key": "error.msg", + "value": { + "stringValue": "The HTTP response has status code 418." + } + }, + { + "key": "http-client-handler-type", + "value": { + "stringValue": "System.Net.Http.SocketsHttpHandler" + } + }, + { + "key": "http.method", + "value": { + "stringValue": "POST" + } + }, + { + "key": "http.status_code", + "value": { + "intValue": "418" + } + }, + { + "key": "http.url", + "value": { + "stringValue": "http://localhost:00000/Guid_2/?BeginGetResponseTeapotAsync" + } + }, + { + "key": "operation.name", + "value": { + "stringValue": "http.request" + } + }, + { + "key": "out.host", + "value": { + "stringValue": "localhost" + } + }, + { + "key": "resource.name", + "value": { + "stringValue": "POST localhost:00000/?/" + } + }, + { + "key": "runtime-id", + "value": { + "stringValue": "Guid_1" + } + }, + { + "key": "service.name", + "value": { + "stringValue": "samples.webrequest-http-client" + } + }, + { + "key": "span.kind", + "value": { + "stringValue": "client" + } + }, + { + "key": "span.type", + "value": { + "stringValue": "http" + } + } + ] + }, + { + "traceId": "normalized-trace-id", + "spanId": "normalized-span-id", + "name": "POST localhost:00000/?/", + "kind": 3, + "startTimeUnixNano": "0", + "endTimeUnixNano": "0", + "attributes": [ + { + "key": "component", + "value": { + "stringValue": "WebRequest" + } + }, + { + "key": "http.method", + "value": { + "stringValue": "POST" + } + }, + { + "key": "http.status_code", + "value": { + "intValue": "200" + } + }, + { + "key": "http.url", + "value": { + "stringValue": "http://localhost:00000/Guid_2/?GetRequestStream" + } + }, + { + "key": "operation.name", + "value": { + "stringValue": "http.request" + } + }, + { + "key": "out.host", + "value": { + "stringValue": "localhost" + } + }, + { + "key": "resource.name", + "value": { + "stringValue": "POST localhost:00000/?/" + } + }, + { + "key": "runtime-id", + "value": { + "stringValue": "Guid_1" + } + }, + { + "key": "service.name", + "value": { + "stringValue": "samples.webrequest-http-client" + } + }, + { + "key": "span.kind", + "value": { + "stringValue": "client" + } + }, + { + "key": "span.type", + "value": { + "stringValue": "http" + } + } + ] + }, + { + "traceId": "normalized-trace-id", + "spanId": "normalized-span-id", + "parentSpanId": "normalized-parent-span-id", + "name": "POST localhost:00000/?/", + "kind": 3, + "startTimeUnixNano": "0", + "endTimeUnixNano": "0", + "attributes": [ + { + "key": "component", + "value": { + "stringValue": "WebRequest" + } + }, + { + "key": "http.method", + "value": { + "stringValue": "POST" + } + }, + { + "key": "http.status_code", + "value": { + "intValue": "200" + } + }, + { + "key": "http.url", + "value": { + "stringValue": "http://localhost:00000/Guid_2/?GetRequestStream" + } + }, + { + "key": "operation.name", + "value": { + "stringValue": "http.request" + } + }, + { + "key": "out.host", + "value": { + "stringValue": "localhost" + } + }, + { + "key": "resource.name", + "value": { + "stringValue": "POST localhost:00000/?/" + } + }, + { + "key": "runtime-id", + "value": { + "stringValue": "Guid_1" + } + }, + { + "key": "service.name", + "value": { + "stringValue": "samples.webrequest-http-client" + } + }, + { + "key": "span.kind", + "value": { + "stringValue": "client" + } + }, + { + "key": "span.type", + "value": { + "stringValue": "http" + } + } + ] + }, + { + "traceId": "normalized-trace-id", + "spanId": "normalized-span-id", + "parentSpanId": "normalized-parent-span-id", + "name": "POST localhost:00000/?/", + "kind": 3, + "startTimeUnixNano": "0", + "endTimeUnixNano": "0", + "attributes": [ + { + "key": "component", + "value": { + "stringValue": "WebRequest" + } + }, + { + "key": "http.method", + "value": { + "stringValue": "POST" + } + }, + { + "key": "http.status_code", + "value": { + "intValue": "200" + } + }, + { + "key": "http.url", + "value": { + "stringValue": "http://localhost:00000/Guid_2/?GetRequestStream" + } + }, + { + "key": "operation.name", + "value": { + "stringValue": "http.request" + } + }, + { + "key": "out.host", + "value": { + "stringValue": "localhost" + } + }, + { + "key": "resource.name", + "value": { + "stringValue": "POST localhost:00000/?/" + } + }, + { + "key": "runtime-id", + "value": { + "stringValue": "Guid_1" + } + }, + { + "key": "service.name", + "value": { + "stringValue": "samples.webrequest-http-client" + } + }, + { + "key": "span.kind", + "value": { + "stringValue": "client" + } + }, + { + "key": "span.type", + "value": { + "stringValue": "http" + } + } + ] + }, + { + "traceId": "normalized-trace-id", + "spanId": "normalized-span-id", + "parentSpanId": "normalized-parent-span-id", + "name": "POST localhost:00000/?/", + "kind": 3, + "startTimeUnixNano": "0", + "endTimeUnixNano": "0", + "attributes": [ + { + "key": "component", + "value": { + "stringValue": "WebRequest" + } + }, + { + "key": "http.method", + "value": { + "stringValue": "POST" + } + }, + { + "key": "http.status_code", + "value": { + "intValue": "200" + } + }, + { + "key": "http.url", + "value": { + "stringValue": "http://localhost:00000/Guid_2/?GetRequestStream_NoBuffering" + } + }, + { + "key": "operation.name", + "value": { + "stringValue": "http.request" + } + }, + { + "key": "out.host", + "value": { + "stringValue": "localhost" + } + }, + { + "key": "resource.name", + "value": { + "stringValue": "POST localhost:00000/?/" + } + }, + { + "key": "runtime-id", + "value": { + "stringValue": "Guid_1" + } + }, + { + "key": "service.name", + "value": { + "stringValue": "samples.webrequest-http-client" + } + }, + { + "key": "span.kind", + "value": { + "stringValue": "client" + } + }, + { + "key": "span.type", + "value": { + "stringValue": "http" + } + } + ] + }, + { + "traceId": "normalized-trace-id", + "spanId": "normalized-span-id", + "parentSpanId": "normalized-parent-span-id", + "name": "POST localhost:00000/?/", + "kind": 3, + "startTimeUnixNano": "0", + "endTimeUnixNano": "0", + "attributes": [ + { + "key": "component", + "value": { + "stringValue": "WebRequest" + } + }, + { + "key": "http.method", + "value": { + "stringValue": "POST" + } + }, + { + "key": "http.status_code", + "value": { + "intValue": "200" + } + }, + { + "key": "http.url", + "value": { + "stringValue": "http://localhost:00000/Guid_2/?UploadData" + } + }, + { + "key": "operation.name", + "value": { + "stringValue": "http.request" + } + }, + { + "key": "out.host", + "value": { + "stringValue": "localhost" + } + }, + { + "key": "resource.name", + "value": { + "stringValue": "POST localhost:00000/?/" + } + }, + { + "key": "runtime-id", + "value": { + "stringValue": "Guid_1" + } + }, + { + "key": "service.name", + "value": { + "stringValue": "samples.webrequest-http-client" + } + }, + { + "key": "span.kind", + "value": { + "stringValue": "client" + } + }, + { + "key": "span.type", + "value": { + "stringValue": "http" + } + } + ] + }, + { + "traceId": "normalized-trace-id", + "spanId": "normalized-span-id", + "parentSpanId": "normalized-parent-span-id", + "name": "POST localhost:00000/?/", + "kind": 3, + "startTimeUnixNano": "0", + "endTimeUnixNano": "0", + "attributes": [ + { + "key": "component", + "value": { + "stringValue": "WebRequest" + } + }, + { + "key": "http.method", + "value": { + "stringValue": "POST" + } + }, + { + "key": "http.status_code", + "value": { + "intValue": "200" + } + }, + { + "key": "http.url", + "value": { + "stringValue": "http://localhost:00000/Guid_2/?UploadData2" + } + }, + { + "key": "operation.name", + "value": { + "stringValue": "http.request" + } + }, + { + "key": "out.host", + "value": { + "stringValue": "localhost" + } + }, + { + "key": "resource.name", + "value": { + "stringValue": "POST localhost:00000/?/" + } + }, + { + "key": "runtime-id", + "value": { + "stringValue": "Guid_1" + } + }, + { + "key": "service.name", + "value": { + "stringValue": "samples.webrequest-http-client" + } + }, + { + "key": "span.kind", + "value": { + "stringValue": "client" + } + }, + { + "key": "span.type", + "value": { + "stringValue": "http" + } + } + ] + }, + { + "traceId": "normalized-trace-id", + "spanId": "normalized-span-id", + "parentSpanId": "normalized-parent-span-id", + "name": "POST localhost:00000/?/", + "kind": 3, + "startTimeUnixNano": "0", + "endTimeUnixNano": "0", + "attributes": [ + { + "key": "component", + "value": { + "stringValue": "WebRequest" + } + }, + { + "key": "http.method", + "value": { + "stringValue": "POST" + } + }, + { + "key": "http.status_code", + "value": { + "intValue": "200" + } + }, + { + "key": "http.url", + "value": { + "stringValue": "http://localhost:00000/Guid_2/?UploadData3" + } + }, + { + "key": "operation.name", + "value": { + "stringValue": "http.request" + } + }, + { + "key": "out.host", + "value": { + "stringValue": "localhost" + } + }, + { + "key": "resource.name", + "value": { + "stringValue": "POST localhost:00000/?/" + } + }, + { + "key": "runtime-id", + "value": { + "stringValue": "Guid_1" + } + }, + { + "key": "service.name", + "value": { + "stringValue": "samples.webrequest-http-client" + } + }, + { + "key": "span.kind", + "value": { + "stringValue": "client" + } + }, + { + "key": "span.type", + "value": { + "stringValue": "http" + } + } + ] + }, + { + "traceId": "normalized-trace-id", + "spanId": "normalized-span-id", + "parentSpanId": "normalized-parent-span-id", + "name": "POST localhost:00000/?/", + "kind": 3, + "startTimeUnixNano": "0", + "endTimeUnixNano": "0", + "attributes": [ + { + "key": "component", + "value": { + "stringValue": "WebRequest" + } + }, + { + "key": "http.method", + "value": { + "stringValue": "POST" + } + }, + { + "key": "http.status_code", + "value": { + "intValue": "200" + } + }, + { + "key": "http.url", + "value": { + "stringValue": "http://localhost:00000/Guid_2/?UploadData4" + } + }, + { + "key": "operation.name", + "value": { + "stringValue": "http.request" + } + }, + { + "key": "out.host", + "value": { + "stringValue": "localhost" + } + }, + { + "key": "resource.name", + "value": { + "stringValue": "POST localhost:00000/?/" + } + }, + { + "key": "runtime-id", + "value": { + "stringValue": "Guid_1" + } + }, + { + "key": "service.name", + "value": { + "stringValue": "samples.webrequest-http-client" + } + }, + { + "key": "span.kind", + "value": { + "stringValue": "client" + } + }, + { + "key": "span.type", + "value": { + "stringValue": "http" + } + } + ] + }, + { + "traceId": "normalized-trace-id", + "spanId": "normalized-span-id", + "parentSpanId": "normalized-parent-span-id", + "name": "POST localhost:00000/?/", + "kind": 3, + "startTimeUnixNano": "0", + "endTimeUnixNano": "0", + "attributes": [ + { + "key": "component", + "value": { + "stringValue": "HttpMessageHandler" + } + }, + { + "key": "http-client-handler-type", + "value": { + "stringValue": "System.Net.Http.SocketsHttpHandler" + } + }, + { + "key": "http.method", + "value": { + "stringValue": "POST" + } + }, + { + "key": "http.status_code", + "value": { + "intValue": "200" + } + }, + { + "key": "http.url", + "value": { + "stringValue": "http://localhost:00000/Guid_2/?UploadDataAsync" + } + }, + { + "key": "operation.name", + "value": { + "stringValue": "http.request" + } + }, + { + "key": "out.host", + "value": { + "stringValue": "localhost" + } + }, + { + "key": "resource.name", + "value": { + "stringValue": "POST localhost:00000/?/" + } + }, + { + "key": "runtime-id", + "value": { + "stringValue": "Guid_1" + } + }, + { + "key": "service.name", + "value": { + "stringValue": "samples.webrequest-http-client" + } + }, + { + "key": "span.kind", + "value": { + "stringValue": "client" + } + }, + { + "key": "span.type", + "value": { + "stringValue": "http" + } + } + ] + }, + { + "traceId": "normalized-trace-id", + "spanId": "normalized-span-id", + "parentSpanId": "normalized-parent-span-id", + "name": "POST localhost:00000/?/", + "kind": 3, + "startTimeUnixNano": "0", + "endTimeUnixNano": "0", + "attributes": [ + { + "key": "component", + "value": { + "stringValue": "HttpMessageHandler" + } + }, + { + "key": "http-client-handler-type", + "value": { + "stringValue": "System.Net.Http.SocketsHttpHandler" + } + }, + { + "key": "http.method", + "value": { + "stringValue": "POST" + } + }, + { + "key": "http.status_code", + "value": { + "intValue": "200" + } + }, + { + "key": "http.url", + "value": { + "stringValue": "http://localhost:00000/Guid_2/?UploadDataAsync2" + } + }, + { + "key": "operation.name", + "value": { + "stringValue": "http.request" + } + }, + { + "key": "out.host", + "value": { + "stringValue": "localhost" + } + }, + { + "key": "resource.name", + "value": { + "stringValue": "POST localhost:00000/?/" + } + }, + { + "key": "runtime-id", + "value": { + "stringValue": "Guid_1" + } + }, + { + "key": "service.name", + "value": { + "stringValue": "samples.webrequest-http-client" + } + }, + { + "key": "span.kind", + "value": { + "stringValue": "client" + } + }, + { + "key": "span.type", + "value": { + "stringValue": "http" + } + } + ] + }, + { + "traceId": "normalized-trace-id", + "spanId": "normalized-span-id", + "parentSpanId": "normalized-parent-span-id", + "name": "POST localhost:00000/?/", + "kind": 3, + "startTimeUnixNano": "0", + "endTimeUnixNano": "0", + "attributes": [ + { + "key": "component", + "value": { + "stringValue": "HttpMessageHandler" + } + }, + { + "key": "http-client-handler-type", + "value": { + "stringValue": "System.Net.Http.SocketsHttpHandler" + } + }, + { + "key": "http.method", + "value": { + "stringValue": "POST" + } + }, + { + "key": "http.status_code", + "value": { + "intValue": "200" + } + }, + { + "key": "http.url", + "value": { + "stringValue": "http://localhost:00000/Guid_2/?UploadDataAsync3" + } + }, + { + "key": "operation.name", + "value": { + "stringValue": "http.request" + } + }, + { + "key": "out.host", + "value": { + "stringValue": "localhost" + } + }, + { + "key": "resource.name", + "value": { + "stringValue": "POST localhost:00000/?/" + } + }, + { + "key": "runtime-id", + "value": { + "stringValue": "Guid_1" + } + }, + { + "key": "service.name", + "value": { + "stringValue": "samples.webrequest-http-client" + } + }, + { + "key": "span.kind", + "value": { + "stringValue": "client" + } + }, + { + "key": "span.type", + "value": { + "stringValue": "http" + } + } + ] + }, + { + "traceId": "normalized-trace-id", + "spanId": "normalized-span-id", + "parentSpanId": "normalized-parent-span-id", + "name": "POST localhost:00000/?/", + "kind": 3, + "startTimeUnixNano": "0", + "endTimeUnixNano": "0", + "attributes": [ + { + "key": "component", + "value": { + "stringValue": "HttpMessageHandler" + } + }, + { + "key": "http-client-handler-type", + "value": { + "stringValue": "System.Net.Http.SocketsHttpHandler" + } + }, + { + "key": "http.method", + "value": { + "stringValue": "POST" + } + }, + { + "key": "http.status_code", + "value": { + "intValue": "200" + } + }, + { + "key": "http.url", + "value": { + "stringValue": "http://localhost:00000/Guid_2/?UploadDataTaskAsync" + } + }, + { + "key": "operation.name", + "value": { + "stringValue": "http.request" + } + }, + { + "key": "out.host", + "value": { + "stringValue": "localhost" + } + }, + { + "key": "resource.name", + "value": { + "stringValue": "POST localhost:00000/?/" + } + }, + { + "key": "runtime-id", + "value": { + "stringValue": "Guid_1" + } + }, + { + "key": "service.name", + "value": { + "stringValue": "samples.webrequest-http-client" + } + }, + { + "key": "span.kind", + "value": { + "stringValue": "client" + } + }, + { + "key": "span.type", + "value": { + "stringValue": "http" + } + } + ] + }, + { + "traceId": "normalized-trace-id", + "spanId": "normalized-span-id", + "parentSpanId": "normalized-parent-span-id", + "name": "POST localhost:00000/?/", + "kind": 3, + "startTimeUnixNano": "0", + "endTimeUnixNano": "0", + "attributes": [ + { + "key": "component", + "value": { + "stringValue": "HttpMessageHandler" + } + }, + { + "key": "http-client-handler-type", + "value": { + "stringValue": "System.Net.Http.SocketsHttpHandler" + } + }, + { + "key": "http.method", + "value": { + "stringValue": "POST" + } + }, + { + "key": "http.status_code", + "value": { + "intValue": "200" + } + }, + { + "key": "http.url", + "value": { + "stringValue": "http://localhost:00000/Guid_2/?UploadDataTaskAsync2" + } + }, + { + "key": "operation.name", + "value": { + "stringValue": "http.request" + } + }, + { + "key": "out.host", + "value": { + "stringValue": "localhost" + } + }, + { + "key": "resource.name", + "value": { + "stringValue": "POST localhost:00000/?/" + } + }, + { + "key": "runtime-id", + "value": { + "stringValue": "Guid_1" + } + }, + { + "key": "service.name", + "value": { + "stringValue": "samples.webrequest-http-client" + } + }, + { + "key": "span.kind", + "value": { + "stringValue": "client" + } + }, + { + "key": "span.type", + "value": { + "stringValue": "http" + } + } + ] + }, + { + "traceId": "normalized-trace-id", + "spanId": "normalized-span-id", + "parentSpanId": "normalized-parent-span-id", + "name": "POST localhost:00000/?/", + "kind": 3, + "startTimeUnixNano": "0", + "endTimeUnixNano": "0", + "attributes": [ + { + "key": "component", + "value": { + "stringValue": "HttpMessageHandler" + } + }, + { + "key": "http-client-handler-type", + "value": { + "stringValue": "System.Net.Http.SocketsHttpHandler" + } + }, + { + "key": "http.method", + "value": { + "stringValue": "POST" + } + }, + { + "key": "http.status_code", + "value": { + "intValue": "200" + } + }, + { + "key": "http.url", + "value": { + "stringValue": "http://localhost:00000/Guid_2/?UploadDataTaskAsync3" + } + }, + { + "key": "operation.name", + "value": { + "stringValue": "http.request" + } + }, + { + "key": "out.host", + "value": { + "stringValue": "localhost" + } + }, + { + "key": "resource.name", + "value": { + "stringValue": "POST localhost:00000/?/" + } + }, + { + "key": "runtime-id", + "value": { + "stringValue": "Guid_1" + } + }, + { + "key": "service.name", + "value": { + "stringValue": "samples.webrequest-http-client" + } + }, + { + "key": "span.kind", + "value": { + "stringValue": "client" + } + }, + { + "key": "span.type", + "value": { + "stringValue": "http" + } + } + ] + }, + { + "traceId": "normalized-trace-id", + "spanId": "normalized-span-id", + "parentSpanId": "normalized-parent-span-id", + "name": "POST localhost:00000/?/", + "kind": 3, + "startTimeUnixNano": "0", + "endTimeUnixNano": "0", + "attributes": [ + { + "key": "component", + "value": { + "stringValue": "HttpMessageHandler" + } + }, + { + "key": "http-client-handler-type", + "value": { + "stringValue": "System.Net.Http.SocketsHttpHandler" + } + }, + { + "key": "http.method", + "value": { + "stringValue": "POST" + } + }, + { + "key": "http.status_code", + "value": { + "intValue": "200" + } + }, + { + "key": "http.url", + "value": { + "stringValue": "http://localhost:00000/Guid_2/?UploadDataTaskAsync4" + } + }, + { + "key": "operation.name", + "value": { + "stringValue": "http.request" + } + }, + { + "key": "out.host", + "value": { + "stringValue": "localhost" + } + }, + { + "key": "resource.name", + "value": { + "stringValue": "POST localhost:00000/?/" + } + }, + { + "key": "runtime-id", + "value": { + "stringValue": "Guid_1" + } + }, + { + "key": "service.name", + "value": { + "stringValue": "samples.webrequest-http-client" + } + }, + { + "key": "span.kind", + "value": { + "stringValue": "client" + } + }, + { + "key": "span.type", + "value": { + "stringValue": "http" + } + } + ] + }, + { + "traceId": "normalized-trace-id", + "spanId": "normalized-span-id", + "parentSpanId": "normalized-parent-span-id", + "name": "POST localhost:00000/?/", + "kind": 3, + "startTimeUnixNano": "0", + "endTimeUnixNano": "0", + "attributes": [ + { + "key": "component", + "value": { + "stringValue": "WebRequest" + } + }, + { + "key": "http.method", + "value": { + "stringValue": "POST" + } + }, + { + "key": "http.status_code", + "value": { + "intValue": "200" + } + }, + { + "key": "http.url", + "value": { + "stringValue": "http://localhost:00000/Guid_2/?UploadFile" + } + }, + { + "key": "operation.name", + "value": { + "stringValue": "http.request" + } + }, + { + "key": "out.host", + "value": { + "stringValue": "localhost" + } + }, + { + "key": "resource.name", + "value": { + "stringValue": "POST localhost:00000/?/" + } + }, + { + "key": "runtime-id", + "value": { + "stringValue": "Guid_1" + } + }, + { + "key": "service.name", + "value": { + "stringValue": "samples.webrequest-http-client" + } + }, + { + "key": "span.kind", + "value": { + "stringValue": "client" + } + }, + { + "key": "span.type", + "value": { + "stringValue": "http" + } + } + ] + }, + { + "traceId": "normalized-trace-id", + "spanId": "normalized-span-id", + "parentSpanId": "normalized-parent-span-id", + "name": "POST localhost:00000/?/", + "kind": 3, + "startTimeUnixNano": "0", + "endTimeUnixNano": "0", + "attributes": [ + { + "key": "component", + "value": { + "stringValue": "WebRequest" + } + }, + { + "key": "http.method", + "value": { + "stringValue": "POST" + } + }, + { + "key": "http.status_code", + "value": { + "intValue": "200" + } + }, + { + "key": "http.url", + "value": { + "stringValue": "http://localhost:00000/Guid_2/?UploadFile2" + } + }, + { + "key": "operation.name", + "value": { + "stringValue": "http.request" + } + }, + { + "key": "out.host", + "value": { + "stringValue": "localhost" + } + }, + { + "key": "resource.name", + "value": { + "stringValue": "POST localhost:00000/?/" + } + }, + { + "key": "runtime-id", + "value": { + "stringValue": "Guid_1" + } + }, + { + "key": "service.name", + "value": { + "stringValue": "samples.webrequest-http-client" + } + }, + { + "key": "span.kind", + "value": { + "stringValue": "client" + } + }, + { + "key": "span.type", + "value": { + "stringValue": "http" + } + } + ] + }, + { + "traceId": "normalized-trace-id", + "spanId": "normalized-span-id", + "parentSpanId": "normalized-parent-span-id", + "name": "POST localhost:00000/?/", + "kind": 3, + "startTimeUnixNano": "0", + "endTimeUnixNano": "0", + "attributes": [ + { + "key": "component", + "value": { + "stringValue": "WebRequest" + } + }, + { + "key": "http.method", + "value": { + "stringValue": "POST" + } + }, + { + "key": "http.status_code", + "value": { + "intValue": "200" + } + }, + { + "key": "http.url", + "value": { + "stringValue": "http://localhost:00000/Guid_2/?UploadFile3" + } + }, + { + "key": "operation.name", + "value": { + "stringValue": "http.request" + } + }, + { + "key": "out.host", + "value": { + "stringValue": "localhost" + } + }, + { + "key": "resource.name", + "value": { + "stringValue": "POST localhost:00000/?/" + } + }, + { + "key": "runtime-id", + "value": { + "stringValue": "Guid_1" + } + }, + { + "key": "service.name", + "value": { + "stringValue": "samples.webrequest-http-client" + } + }, + { + "key": "span.kind", + "value": { + "stringValue": "client" + } + }, + { + "key": "span.type", + "value": { + "stringValue": "http" + } + } + ] + }, + { + "traceId": "normalized-trace-id", + "spanId": "normalized-span-id", + "parentSpanId": "normalized-parent-span-id", + "name": "POST localhost:00000/?/", + "kind": 3, + "startTimeUnixNano": "0", + "endTimeUnixNano": "0", + "attributes": [ + { + "key": "component", + "value": { + "stringValue": "WebRequest" + } + }, + { + "key": "http.method", + "value": { + "stringValue": "POST" + } + }, + { + "key": "http.status_code", + "value": { + "intValue": "200" + } + }, + { + "key": "http.url", + "value": { + "stringValue": "http://localhost:00000/Guid_2/?UploadFile4" + } + }, + { + "key": "operation.name", + "value": { + "stringValue": "http.request" + } + }, + { + "key": "out.host", + "value": { + "stringValue": "localhost" + } + }, + { + "key": "resource.name", + "value": { + "stringValue": "POST localhost:00000/?/" + } + }, + { + "key": "runtime-id", + "value": { + "stringValue": "Guid_1" + } + }, + { + "key": "service.name", + "value": { + "stringValue": "samples.webrequest-http-client" + } + }, + { + "key": "span.kind", + "value": { + "stringValue": "client" + } + }, + { + "key": "span.type", + "value": { + "stringValue": "http" + } + } + ] + }, + { + "traceId": "normalized-trace-id", + "spanId": "normalized-span-id", + "parentSpanId": "normalized-parent-span-id", + "name": "POST localhost:00000/?/", + "kind": 3, + "startTimeUnixNano": "0", + "endTimeUnixNano": "0", + "attributes": [ + { + "key": "component", + "value": { + "stringValue": "HttpMessageHandler" + } + }, + { + "key": "http-client-handler-type", + "value": { + "stringValue": "System.Net.Http.SocketsHttpHandler" + } + }, + { + "key": "http.method", + "value": { + "stringValue": "POST" + } + }, + { + "key": "http.status_code", + "value": { + "intValue": "200" + } + }, + { + "key": "http.url", + "value": { + "stringValue": "http://localhost:00000/Guid_2/?UploadFileAsync" + } + }, + { + "key": "operation.name", + "value": { + "stringValue": "http.request" + } + }, + { + "key": "out.host", + "value": { + "stringValue": "localhost" + } + }, + { + "key": "resource.name", + "value": { + "stringValue": "POST localhost:00000/?/" + } + }, + { + "key": "runtime-id", + "value": { + "stringValue": "Guid_1" + } + }, + { + "key": "service.name", + "value": { + "stringValue": "samples.webrequest-http-client" + } + }, + { + "key": "span.kind", + "value": { + "stringValue": "client" + } + }, + { + "key": "span.type", + "value": { + "stringValue": "http" + } + } + ] + }, + { + "traceId": "normalized-trace-id", + "spanId": "normalized-span-id", + "parentSpanId": "normalized-parent-span-id", + "name": "POST localhost:00000/?/", + "kind": 3, + "startTimeUnixNano": "0", + "endTimeUnixNano": "0", + "attributes": [ + { + "key": "component", + "value": { + "stringValue": "HttpMessageHandler" + } + }, + { + "key": "http-client-handler-type", + "value": { + "stringValue": "System.Net.Http.SocketsHttpHandler" + } + }, + { + "key": "http.method", + "value": { + "stringValue": "POST" + } + }, + { + "key": "http.status_code", + "value": { + "intValue": "200" + } + }, + { + "key": "http.url", + "value": { + "stringValue": "http://localhost:00000/Guid_2/?UploadFileAsync2" + } + }, + { + "key": "operation.name", + "value": { + "stringValue": "http.request" + } + }, + { + "key": "out.host", + "value": { + "stringValue": "localhost" + } + }, + { + "key": "resource.name", + "value": { + "stringValue": "POST localhost:00000/?/" + } + }, + { + "key": "runtime-id", + "value": { + "stringValue": "Guid_1" + } + }, + { + "key": "service.name", + "value": { + "stringValue": "samples.webrequest-http-client" + } + }, + { + "key": "span.kind", + "value": { + "stringValue": "client" + } + }, + { + "key": "span.type", + "value": { + "stringValue": "http" + } + } + ] + }, + { + "traceId": "normalized-trace-id", + "spanId": "normalized-span-id", + "parentSpanId": "normalized-parent-span-id", + "name": "POST localhost:00000/?/", + "kind": 3, + "startTimeUnixNano": "0", + "endTimeUnixNano": "0", + "attributes": [ + { + "key": "component", + "value": { + "stringValue": "HttpMessageHandler" + } + }, + { + "key": "http-client-handler-type", + "value": { + "stringValue": "System.Net.Http.SocketsHttpHandler" + } + }, + { + "key": "http.method", + "value": { + "stringValue": "POST" + } + }, + { + "key": "http.status_code", + "value": { + "intValue": "200" + } + }, + { + "key": "http.url", + "value": { + "stringValue": "http://localhost:00000/Guid_2/?UploadFileAsync3" + } + }, + { + "key": "operation.name", + "value": { + "stringValue": "http.request" + } + }, + { + "key": "out.host", + "value": { + "stringValue": "localhost" + } + }, + { + "key": "resource.name", + "value": { + "stringValue": "POST localhost:00000/?/" + } + }, + { + "key": "runtime-id", + "value": { + "stringValue": "Guid_1" + } + }, + { + "key": "service.name", + "value": { + "stringValue": "samples.webrequest-http-client" + } + }, + { + "key": "span.kind", + "value": { + "stringValue": "client" + } + }, + { + "key": "span.type", + "value": { + "stringValue": "http" + } + } + ] + }, + { + "traceId": "normalized-trace-id", + "spanId": "normalized-span-id", + "parentSpanId": "normalized-parent-span-id", + "name": "POST localhost:00000/?/", + "kind": 3, + "startTimeUnixNano": "0", + "endTimeUnixNano": "0", + "attributes": [ + { + "key": "component", + "value": { + "stringValue": "HttpMessageHandler" + } + }, + { + "key": "http-client-handler-type", + "value": { + "stringValue": "System.Net.Http.SocketsHttpHandler" + } + }, + { + "key": "http.method", + "value": { + "stringValue": "POST" + } + }, + { + "key": "http.status_code", + "value": { + "intValue": "200" + } + }, + { + "key": "http.url", + "value": { + "stringValue": "http://localhost:00000/Guid_2/?UploadFileTaskAsync" + } + }, + { + "key": "operation.name", + "value": { + "stringValue": "http.request" + } + }, + { + "key": "out.host", + "value": { + "stringValue": "localhost" + } + }, + { + "key": "resource.name", + "value": { + "stringValue": "POST localhost:00000/?/" + } + }, + { + "key": "runtime-id", + "value": { + "stringValue": "Guid_1" + } + }, + { + "key": "service.name", + "value": { + "stringValue": "samples.webrequest-http-client" + } + }, + { + "key": "span.kind", + "value": { + "stringValue": "client" + } + }, + { + "key": "span.type", + "value": { + "stringValue": "http" + } + } + ] + }, + { + "traceId": "normalized-trace-id", + "spanId": "normalized-span-id", + "parentSpanId": "normalized-parent-span-id", + "name": "POST localhost:00000/?/", + "kind": 3, + "startTimeUnixNano": "0", + "endTimeUnixNano": "0", + "attributes": [ + { + "key": "component", + "value": { + "stringValue": "HttpMessageHandler" + } + }, + { + "key": "http-client-handler-type", + "value": { + "stringValue": "System.Net.Http.SocketsHttpHandler" + } + }, + { + "key": "http.method", + "value": { + "stringValue": "POST" + } + }, + { + "key": "http.status_code", + "value": { + "intValue": "200" + } + }, + { + "key": "http.url", + "value": { + "stringValue": "http://localhost:00000/Guid_2/?UploadFileTaskAsync2" + } + }, + { + "key": "operation.name", + "value": { + "stringValue": "http.request" + } + }, + { + "key": "out.host", + "value": { + "stringValue": "localhost" + } + }, + { + "key": "resource.name", + "value": { + "stringValue": "POST localhost:00000/?/" + } + }, + { + "key": "runtime-id", + "value": { + "stringValue": "Guid_1" + } + }, + { + "key": "service.name", + "value": { + "stringValue": "samples.webrequest-http-client" + } + }, + { + "key": "span.kind", + "value": { + "stringValue": "client" + } + }, + { + "key": "span.type", + "value": { + "stringValue": "http" + } + } + ] + }, + { + "traceId": "normalized-trace-id", + "spanId": "normalized-span-id", + "parentSpanId": "normalized-parent-span-id", + "name": "POST localhost:00000/?/", + "kind": 3, + "startTimeUnixNano": "0", + "endTimeUnixNano": "0", + "attributes": [ + { + "key": "component", + "value": { + "stringValue": "HttpMessageHandler" + } + }, + { + "key": "http-client-handler-type", + "value": { + "stringValue": "System.Net.Http.SocketsHttpHandler" + } + }, + { + "key": "http.method", + "value": { + "stringValue": "POST" + } + }, + { + "key": "http.status_code", + "value": { + "intValue": "200" + } + }, + { + "key": "http.url", + "value": { + "stringValue": "http://localhost:00000/Guid_2/?UploadFileTaskAsync3" + } + }, + { + "key": "operation.name", + "value": { + "stringValue": "http.request" + } + }, + { + "key": "out.host", + "value": { + "stringValue": "localhost" + } + }, + { + "key": "resource.name", + "value": { + "stringValue": "POST localhost:00000/?/" + } + }, + { + "key": "runtime-id", + "value": { + "stringValue": "Guid_1" + } + }, + { + "key": "service.name", + "value": { + "stringValue": "samples.webrequest-http-client" + } + }, + { + "key": "span.kind", + "value": { + "stringValue": "client" + } + }, + { + "key": "span.type", + "value": { + "stringValue": "http" + } + } + ] + }, + { + "traceId": "normalized-trace-id", + "spanId": "normalized-span-id", + "parentSpanId": "normalized-parent-span-id", + "name": "POST localhost:00000/?/", + "kind": 3, + "startTimeUnixNano": "0", + "endTimeUnixNano": "0", + "attributes": [ + { + "key": "component", + "value": { + "stringValue": "HttpMessageHandler" + } + }, + { + "key": "http-client-handler-type", + "value": { + "stringValue": "System.Net.Http.SocketsHttpHandler" + } + }, + { + "key": "http.method", + "value": { + "stringValue": "POST" + } + }, + { + "key": "http.status_code", + "value": { + "intValue": "200" + } + }, + { + "key": "http.url", + "value": { + "stringValue": "http://localhost:00000/Guid_2/?UploadFileTaskAsync4" + } + }, + { + "key": "operation.name", + "value": { + "stringValue": "http.request" + } + }, + { + "key": "out.host", + "value": { + "stringValue": "localhost" + } + }, + { + "key": "resource.name", + "value": { + "stringValue": "POST localhost:00000/?/" + } + }, + { + "key": "runtime-id", + "value": { + "stringValue": "Guid_1" + } + }, + { + "key": "service.name", + "value": { + "stringValue": "samples.webrequest-http-client" + } + }, + { + "key": "span.kind", + "value": { + "stringValue": "client" + } + }, + { + "key": "span.type", + "value": { + "stringValue": "http" + } + } + ] + }, + { + "traceId": "normalized-trace-id", + "spanId": "normalized-span-id", + "parentSpanId": "normalized-parent-span-id", + "name": "POST localhost:00000/?/", + "kind": 3, + "startTimeUnixNano": "0", + "endTimeUnixNano": "0", + "attributes": [ + { + "key": "component", + "value": { + "stringValue": "WebRequest" + } + }, + { + "key": "http.method", + "value": { + "stringValue": "POST" + } + }, + { + "key": "http.status_code", + "value": { + "intValue": "200" + } + }, + { + "key": "http.url", + "value": { + "stringValue": "http://localhost:00000/Guid_2/?UploadString" + } + }, + { + "key": "operation.name", + "value": { + "stringValue": "http.request" + } + }, + { + "key": "out.host", + "value": { + "stringValue": "localhost" + } + }, + { + "key": "resource.name", + "value": { + "stringValue": "POST localhost:00000/?/" + } + }, + { + "key": "runtime-id", + "value": { + "stringValue": "Guid_1" + } + }, + { + "key": "service.name", + "value": { + "stringValue": "samples.webrequest-http-client" + } + }, + { + "key": "span.kind", + "value": { + "stringValue": "client" + } + }, + { + "key": "span.type", + "value": { + "stringValue": "http" + } + } + ] + }, + { + "traceId": "normalized-trace-id", + "spanId": "normalized-span-id", + "parentSpanId": "normalized-parent-span-id", + "name": "POST localhost:00000/?/", + "kind": 3, + "startTimeUnixNano": "0", + "endTimeUnixNano": "0", + "attributes": [ + { + "key": "component", + "value": { + "stringValue": "WebRequest" + } + }, + { + "key": "http.method", + "value": { + "stringValue": "POST" + } + }, + { + "key": "http.status_code", + "value": { + "intValue": "200" + } + }, + { + "key": "http.url", + "value": { + "stringValue": "http://localhost:00000/Guid_2/?UploadString2" + } + }, + { + "key": "operation.name", + "value": { + "stringValue": "http.request" + } + }, + { + "key": "out.host", + "value": { + "stringValue": "localhost" + } + }, + { + "key": "resource.name", + "value": { + "stringValue": "POST localhost:00000/?/" + } + }, + { + "key": "runtime-id", + "value": { + "stringValue": "Guid_1" + } + }, + { + "key": "service.name", + "value": { + "stringValue": "samples.webrequest-http-client" + } + }, + { + "key": "span.kind", + "value": { + "stringValue": "client" + } + }, + { + "key": "span.type", + "value": { + "stringValue": "http" + } + } + ] + }, + { + "traceId": "normalized-trace-id", + "spanId": "normalized-span-id", + "parentSpanId": "normalized-parent-span-id", + "name": "POST localhost:00000/?/", + "kind": 3, + "startTimeUnixNano": "0", + "endTimeUnixNano": "0", + "attributes": [ + { + "key": "component", + "value": { + "stringValue": "WebRequest" + } + }, + { + "key": "http.method", + "value": { + "stringValue": "POST" + } + }, + { + "key": "http.status_code", + "value": { + "intValue": "200" + } + }, + { + "key": "http.url", + "value": { + "stringValue": "http://localhost:00000/Guid_2/?UploadString3" + } + }, + { + "key": "operation.name", + "value": { + "stringValue": "http.request" + } + }, + { + "key": "out.host", + "value": { + "stringValue": "localhost" + } + }, + { + "key": "resource.name", + "value": { + "stringValue": "POST localhost:00000/?/" + } + }, + { + "key": "runtime-id", + "value": { + "stringValue": "Guid_1" + } + }, + { + "key": "service.name", + "value": { + "stringValue": "samples.webrequest-http-client" + } + }, + { + "key": "span.kind", + "value": { + "stringValue": "client" + } + }, + { + "key": "span.type", + "value": { + "stringValue": "http" + } + } + ] + }, + { + "traceId": "normalized-trace-id", + "spanId": "normalized-span-id", + "parentSpanId": "normalized-parent-span-id", + "name": "POST localhost:00000/?/", + "kind": 3, + "startTimeUnixNano": "0", + "endTimeUnixNano": "0", + "attributes": [ + { + "key": "component", + "value": { + "stringValue": "WebRequest" + } + }, + { + "key": "http.method", + "value": { + "stringValue": "POST" + } + }, + { + "key": "http.status_code", + "value": { + "intValue": "200" + } + }, + { + "key": "http.url", + "value": { + "stringValue": "http://localhost:00000/Guid_2/?UploadString4" + } + }, + { + "key": "operation.name", + "value": { + "stringValue": "http.request" + } + }, + { + "key": "out.host", + "value": { + "stringValue": "localhost" + } + }, + { + "key": "resource.name", + "value": { + "stringValue": "POST localhost:00000/?/" + } + }, + { + "key": "runtime-id", + "value": { + "stringValue": "Guid_1" + } + }, + { + "key": "service.name", + "value": { + "stringValue": "samples.webrequest-http-client" + } + }, + { + "key": "span.kind", + "value": { + "stringValue": "client" + } + }, + { + "key": "span.type", + "value": { + "stringValue": "http" + } + } + ] + }, + { + "traceId": "normalized-trace-id", + "spanId": "normalized-span-id", + "parentSpanId": "normalized-parent-span-id", + "name": "POST localhost:00000/?/", + "kind": 3, + "startTimeUnixNano": "0", + "endTimeUnixNano": "0", + "attributes": [ + { + "key": "component", + "value": { + "stringValue": "HttpMessageHandler" + } + }, + { + "key": "http-client-handler-type", + "value": { + "stringValue": "System.Net.Http.SocketsHttpHandler" + } + }, + { + "key": "http.method", + "value": { + "stringValue": "POST" + } + }, + { + "key": "http.status_code", + "value": { + "intValue": "200" + } + }, + { + "key": "http.url", + "value": { + "stringValue": "http://localhost:00000/Guid_2/?UploadStringAsync" + } + }, + { + "key": "operation.name", + "value": { + "stringValue": "http.request" + } + }, + { + "key": "out.host", + "value": { + "stringValue": "localhost" + } + }, + { + "key": "resource.name", + "value": { + "stringValue": "POST localhost:00000/?/" + } + }, + { + "key": "runtime-id", + "value": { + "stringValue": "Guid_1" + } + }, + { + "key": "service.name", + "value": { + "stringValue": "samples.webrequest-http-client" + } + }, + { + "key": "span.kind", + "value": { + "stringValue": "client" + } + }, + { + "key": "span.type", + "value": { + "stringValue": "http" + } + } + ] + }, + { + "traceId": "normalized-trace-id", + "spanId": "normalized-span-id", + "parentSpanId": "normalized-parent-span-id", + "name": "POST localhost:00000/?/", + "kind": 3, + "startTimeUnixNano": "0", + "endTimeUnixNano": "0", + "attributes": [ + { + "key": "component", + "value": { + "stringValue": "HttpMessageHandler" + } + }, + { + "key": "http-client-handler-type", + "value": { + "stringValue": "System.Net.Http.SocketsHttpHandler" + } + }, + { + "key": "http.method", + "value": { + "stringValue": "POST" + } + }, + { + "key": "http.status_code", + "value": { + "intValue": "200" + } + }, + { + "key": "http.url", + "value": { + "stringValue": "http://localhost:00000/Guid_2/?UploadStringAsync2" + } + }, + { + "key": "operation.name", + "value": { + "stringValue": "http.request" + } + }, + { + "key": "out.host", + "value": { + "stringValue": "localhost" + } + }, + { + "key": "resource.name", + "value": { + "stringValue": "POST localhost:00000/?/" + } + }, + { + "key": "runtime-id", + "value": { + "stringValue": "Guid_1" + } + }, + { + "key": "service.name", + "value": { + "stringValue": "samples.webrequest-http-client" + } + }, + { + "key": "span.kind", + "value": { + "stringValue": "client" + } + }, + { + "key": "span.type", + "value": { + "stringValue": "http" + } + } + ] + }, + { + "traceId": "normalized-trace-id", + "spanId": "normalized-span-id", + "parentSpanId": "normalized-parent-span-id", + "name": "POST localhost:00000/?/", + "kind": 3, + "startTimeUnixNano": "0", + "endTimeUnixNano": "0", + "attributes": [ + { + "key": "component", + "value": { + "stringValue": "HttpMessageHandler" + } + }, + { + "key": "http-client-handler-type", + "value": { + "stringValue": "System.Net.Http.SocketsHttpHandler" + } + }, + { + "key": "http.method", + "value": { + "stringValue": "POST" + } + }, + { + "key": "http.status_code", + "value": { + "intValue": "200" + } + }, + { + "key": "http.url", + "value": { + "stringValue": "http://localhost:00000/Guid_2/?UploadStringAsync3" + } + }, + { + "key": "operation.name", + "value": { + "stringValue": "http.request" + } + }, + { + "key": "out.host", + "value": { + "stringValue": "localhost" + } + }, + { + "key": "resource.name", + "value": { + "stringValue": "POST localhost:00000/?/" + } + }, + { + "key": "runtime-id", + "value": { + "stringValue": "Guid_1" + } + }, + { + "key": "service.name", + "value": { + "stringValue": "samples.webrequest-http-client" + } + }, + { + "key": "span.kind", + "value": { + "stringValue": "client" + } + }, + { + "key": "span.type", + "value": { + "stringValue": "http" + } + } + ] + }, + { + "traceId": "normalized-trace-id", + "spanId": "normalized-span-id", + "parentSpanId": "normalized-parent-span-id", + "name": "POST localhost:00000/?/", + "kind": 3, + "startTimeUnixNano": "0", + "endTimeUnixNano": "0", + "attributes": [ + { + "key": "component", + "value": { + "stringValue": "HttpMessageHandler" + } + }, + { + "key": "http-client-handler-type", + "value": { + "stringValue": "System.Net.Http.SocketsHttpHandler" + } + }, + { + "key": "http.method", + "value": { + "stringValue": "POST" + } + }, + { + "key": "http.status_code", + "value": { + "intValue": "200" + } + }, + { + "key": "http.url", + "value": { + "stringValue": "http://localhost:00000/Guid_2/?UploadStringTaskAsync" + } + }, + { + "key": "operation.name", + "value": { + "stringValue": "http.request" + } + }, + { + "key": "out.host", + "value": { + "stringValue": "localhost" + } + }, + { + "key": "resource.name", + "value": { + "stringValue": "POST localhost:00000/?/" + } + }, + { + "key": "runtime-id", + "value": { + "stringValue": "Guid_1" + } + }, + { + "key": "service.name", + "value": { + "stringValue": "samples.webrequest-http-client" + } + }, + { + "key": "span.kind", + "value": { + "stringValue": "client" + } + }, + { + "key": "span.type", + "value": { + "stringValue": "http" + } + } + ] + }, + { + "traceId": "normalized-trace-id", + "spanId": "normalized-span-id", + "parentSpanId": "normalized-parent-span-id", + "name": "POST localhost:00000/?/", + "kind": 3, + "startTimeUnixNano": "0", + "endTimeUnixNano": "0", + "attributes": [ + { + "key": "component", + "value": { + "stringValue": "HttpMessageHandler" + } + }, + { + "key": "http-client-handler-type", + "value": { + "stringValue": "System.Net.Http.SocketsHttpHandler" + } + }, + { + "key": "http.method", + "value": { + "stringValue": "POST" + } + }, + { + "key": "http.status_code", + "value": { + "intValue": "200" + } + }, + { + "key": "http.url", + "value": { + "stringValue": "http://localhost:00000/Guid_2/?UploadStringTaskAsync2" + } + }, + { + "key": "operation.name", + "value": { + "stringValue": "http.request" + } + }, + { + "key": "out.host", + "value": { + "stringValue": "localhost" + } + }, + { + "key": "resource.name", + "value": { + "stringValue": "POST localhost:00000/?/" + } + }, + { + "key": "runtime-id", + "value": { + "stringValue": "Guid_1" + } + }, + { + "key": "service.name", + "value": { + "stringValue": "samples.webrequest-http-client" + } + }, + { + "key": "span.kind", + "value": { + "stringValue": "client" + } + }, + { + "key": "span.type", + "value": { + "stringValue": "http" + } + } + ] + }, + { + "traceId": "normalized-trace-id", + "spanId": "normalized-span-id", + "parentSpanId": "normalized-parent-span-id", + "name": "POST localhost:00000/?/", + "kind": 3, + "startTimeUnixNano": "0", + "endTimeUnixNano": "0", + "attributes": [ + { + "key": "component", + "value": { + "stringValue": "HttpMessageHandler" + } + }, + { + "key": "http-client-handler-type", + "value": { + "stringValue": "System.Net.Http.SocketsHttpHandler" + } + }, + { + "key": "http.method", + "value": { + "stringValue": "POST" + } + }, + { + "key": "http.status_code", + "value": { + "intValue": "200" + } + }, + { + "key": "http.url", + "value": { + "stringValue": "http://localhost:00000/Guid_2/?UploadStringTaskAsync3" + } + }, + { + "key": "operation.name", + "value": { + "stringValue": "http.request" + } + }, + { + "key": "out.host", + "value": { + "stringValue": "localhost" + } + }, + { + "key": "resource.name", + "value": { + "stringValue": "POST localhost:00000/?/" + } + }, + { + "key": "runtime-id", + "value": { + "stringValue": "Guid_1" + } + }, + { + "key": "service.name", + "value": { + "stringValue": "samples.webrequest-http-client" + } + }, + { + "key": "span.kind", + "value": { + "stringValue": "client" + } + }, + { + "key": "span.type", + "value": { + "stringValue": "http" + } + } + ] + }, + { + "traceId": "normalized-trace-id", + "spanId": "normalized-span-id", + "parentSpanId": "normalized-parent-span-id", + "name": "POST localhost:00000/?/", + "kind": 3, + "startTimeUnixNano": "0", + "endTimeUnixNano": "0", + "attributes": [ + { + "key": "component", + "value": { + "stringValue": "HttpMessageHandler" + } + }, + { + "key": "http-client-handler-type", + "value": { + "stringValue": "System.Net.Http.SocketsHttpHandler" + } + }, + { + "key": "http.method", + "value": { + "stringValue": "POST" + } + }, + { + "key": "http.status_code", + "value": { + "intValue": "200" + } + }, + { + "key": "http.url", + "value": { + "stringValue": "http://localhost:00000/Guid_2/?UploadStringTaskAsync4" + } + }, + { + "key": "operation.name", + "value": { + "stringValue": "http.request" + } + }, + { + "key": "out.host", + "value": { + "stringValue": "localhost" + } + }, + { + "key": "resource.name", + "value": { + "stringValue": "POST localhost:00000/?/" + } + }, + { + "key": "runtime-id", + "value": { + "stringValue": "Guid_1" + } + }, + { + "key": "service.name", + "value": { + "stringValue": "samples.webrequest-http-client" + } + }, + { + "key": "span.kind", + "value": { + "stringValue": "client" + } + }, + { + "key": "span.type", + "value": { + "stringValue": "http" + } + } + ] + }, + { + "traceId": "normalized-trace-id", + "spanId": "normalized-span-id", + "parentSpanId": "normalized-parent-span-id", + "name": "POST localhost:00000/?/", + "kind": 3, + "startTimeUnixNano": "0", + "endTimeUnixNano": "0", + "attributes": [ + { + "key": "component", + "value": { + "stringValue": "WebRequest" + } + }, + { + "key": "http.method", + "value": { + "stringValue": "POST" + } + }, + { + "key": "http.status_code", + "value": { + "intValue": "200" + } + }, + { + "key": "http.url", + "value": { + "stringValue": "http://localhost:00000/Guid_2/?UploadValues" + } + }, + { + "key": "operation.name", + "value": { + "stringValue": "http.request" + } + }, + { + "key": "out.host", + "value": { + "stringValue": "localhost" + } + }, + { + "key": "resource.name", + "value": { + "stringValue": "POST localhost:00000/?/" + } + }, + { + "key": "runtime-id", + "value": { + "stringValue": "Guid_1" + } + }, + { + "key": "service.name", + "value": { + "stringValue": "samples.webrequest-http-client" + } + }, + { + "key": "span.kind", + "value": { + "stringValue": "client" + } + }, + { + "key": "span.type", + "value": { + "stringValue": "http" + } + } + ] + }, + { + "traceId": "normalized-trace-id", + "spanId": "normalized-span-id", + "parentSpanId": "normalized-parent-span-id", + "name": "POST localhost:00000/?/", + "kind": 3, + "startTimeUnixNano": "0", + "endTimeUnixNano": "0", + "attributes": [ + { + "key": "component", + "value": { + "stringValue": "WebRequest" + } + }, + { + "key": "http.method", + "value": { + "stringValue": "POST" + } + }, + { + "key": "http.status_code", + "value": { + "intValue": "200" + } + }, + { + "key": "http.url", + "value": { + "stringValue": "http://localhost:00000/Guid_2/?UploadValues2" + } + }, + { + "key": "operation.name", + "value": { + "stringValue": "http.request" + } + }, + { + "key": "out.host", + "value": { + "stringValue": "localhost" + } + }, + { + "key": "resource.name", + "value": { + "stringValue": "POST localhost:00000/?/" + } + }, + { + "key": "runtime-id", + "value": { + "stringValue": "Guid_1" + } + }, + { + "key": "service.name", + "value": { + "stringValue": "samples.webrequest-http-client" + } + }, + { + "key": "span.kind", + "value": { + "stringValue": "client" + } + }, + { + "key": "span.type", + "value": { + "stringValue": "http" + } + } + ] + }, + { + "traceId": "normalized-trace-id", + "spanId": "normalized-span-id", + "parentSpanId": "normalized-parent-span-id", + "name": "POST localhost:00000/?/", + "kind": 3, + "startTimeUnixNano": "0", + "endTimeUnixNano": "0", + "attributes": [ + { + "key": "component", + "value": { + "stringValue": "WebRequest" + } + }, + { + "key": "http.method", + "value": { + "stringValue": "POST" + } + }, + { + "key": "http.status_code", + "value": { + "intValue": "200" + } + }, + { + "key": "http.url", + "value": { + "stringValue": "http://localhost:00000/Guid_2/?UploadValues3" + } + }, + { + "key": "operation.name", + "value": { + "stringValue": "http.request" + } + }, + { + "key": "out.host", + "value": { + "stringValue": "localhost" + } + }, + { + "key": "resource.name", + "value": { + "stringValue": "POST localhost:00000/?/" + } + }, + { + "key": "runtime-id", + "value": { + "stringValue": "Guid_1" + } + }, + { + "key": "service.name", + "value": { + "stringValue": "samples.webrequest-http-client" + } + }, + { + "key": "span.kind", + "value": { + "stringValue": "client" + } + }, + { + "key": "span.type", + "value": { + "stringValue": "http" + } + } + ] + }, + { + "traceId": "normalized-trace-id", + "spanId": "normalized-span-id", + "parentSpanId": "normalized-parent-span-id", + "name": "POST localhost:00000/?/", + "kind": 3, + "startTimeUnixNano": "0", + "endTimeUnixNano": "0", + "attributes": [ + { + "key": "component", + "value": { + "stringValue": "WebRequest" + } + }, + { + "key": "http.method", + "value": { + "stringValue": "POST" + } + }, + { + "key": "http.status_code", + "value": { + "intValue": "200" + } + }, + { + "key": "http.url", + "value": { + "stringValue": "http://localhost:00000/Guid_2/?UploadValues4" + } + }, + { + "key": "operation.name", + "value": { + "stringValue": "http.request" + } + }, + { + "key": "out.host", + "value": { + "stringValue": "localhost" + } + }, + { + "key": "resource.name", + "value": { + "stringValue": "POST localhost:00000/?/" + } + }, + { + "key": "runtime-id", + "value": { + "stringValue": "Guid_1" + } + }, + { + "key": "service.name", + "value": { + "stringValue": "samples.webrequest-http-client" + } + }, + { + "key": "span.kind", + "value": { + "stringValue": "client" + } + }, + { + "key": "span.type", + "value": { + "stringValue": "http" + } + } + ] + }, + { + "traceId": "normalized-trace-id", + "spanId": "normalized-span-id", + "parentSpanId": "normalized-parent-span-id", + "name": "POST localhost:00000/?/", + "kind": 3, + "startTimeUnixNano": "0", + "endTimeUnixNano": "0", + "attributes": [ + { + "key": "component", + "value": { + "stringValue": "HttpMessageHandler" + } + }, + { + "key": "http-client-handler-type", + "value": { + "stringValue": "System.Net.Http.SocketsHttpHandler" + } + }, + { + "key": "http.method", + "value": { + "stringValue": "POST" + } + }, + { + "key": "http.status_code", + "value": { + "intValue": "200" + } + }, + { + "key": "http.url", + "value": { + "stringValue": "http://localhost:00000/Guid_2/?UploadValuesAsync" + } + }, + { + "key": "operation.name", + "value": { + "stringValue": "http.request" + } + }, + { + "key": "out.host", + "value": { + "stringValue": "localhost" + } + }, + { + "key": "resource.name", + "value": { + "stringValue": "POST localhost:00000/?/" + } + }, + { + "key": "runtime-id", + "value": { + "stringValue": "Guid_1" + } + }, + { + "key": "service.name", + "value": { + "stringValue": "samples.webrequest-http-client" + } + }, + { + "key": "span.kind", + "value": { + "stringValue": "client" + } + }, + { + "key": "span.type", + "value": { + "stringValue": "http" + } + } + ] + }, + { + "traceId": "normalized-trace-id", + "spanId": "normalized-span-id", + "parentSpanId": "normalized-parent-span-id", + "name": "POST localhost:00000/?/", + "kind": 3, + "startTimeUnixNano": "0", + "endTimeUnixNano": "0", + "attributes": [ + { + "key": "component", + "value": { + "stringValue": "HttpMessageHandler" + } + }, + { + "key": "http-client-handler-type", + "value": { + "stringValue": "System.Net.Http.SocketsHttpHandler" + } + }, + { + "key": "http.method", + "value": { + "stringValue": "POST" + } + }, + { + "key": "http.status_code", + "value": { + "intValue": "200" + } + }, + { + "key": "http.url", + "value": { + "stringValue": "http://localhost:00000/Guid_2/?UploadValuesAsync2" + } + }, + { + "key": "operation.name", + "value": { + "stringValue": "http.request" + } + }, + { + "key": "out.host", + "value": { + "stringValue": "localhost" + } + }, + { + "key": "resource.name", + "value": { + "stringValue": "POST localhost:00000/?/" + } + }, + { + "key": "runtime-id", + "value": { + "stringValue": "Guid_1" + } + }, + { + "key": "service.name", + "value": { + "stringValue": "samples.webrequest-http-client" + } + }, + { + "key": "span.kind", + "value": { + "stringValue": "client" + } + }, + { + "key": "span.type", + "value": { + "stringValue": "http" + } + } + ] + }, + { + "traceId": "normalized-trace-id", + "spanId": "normalized-span-id", + "parentSpanId": "normalized-parent-span-id", + "name": "POST localhost:00000/?/", + "kind": 3, + "startTimeUnixNano": "0", + "endTimeUnixNano": "0", + "attributes": [ + { + "key": "component", + "value": { + "stringValue": "HttpMessageHandler" + } + }, + { + "key": "http-client-handler-type", + "value": { + "stringValue": "System.Net.Http.SocketsHttpHandler" + } + }, + { + "key": "http.method", + "value": { + "stringValue": "POST" + } + }, + { + "key": "http.status_code", + "value": { + "intValue": "200" + } + }, + { + "key": "http.url", + "value": { + "stringValue": "http://localhost:00000/Guid_2/?UploadValuesAsync3" + } + }, + { + "key": "operation.name", + "value": { + "stringValue": "http.request" + } + }, + { + "key": "out.host", + "value": { + "stringValue": "localhost" + } + }, + { + "key": "resource.name", + "value": { + "stringValue": "POST localhost:00000/?/" + } + }, + { + "key": "runtime-id", + "value": { + "stringValue": "Guid_1" + } + }, + { + "key": "service.name", + "value": { + "stringValue": "samples.webrequest-http-client" + } + }, + { + "key": "span.kind", + "value": { + "stringValue": "client" + } + }, + { + "key": "span.type", + "value": { + "stringValue": "http" + } + } + ] + }, + { + "traceId": "normalized-trace-id", + "spanId": "normalized-span-id", + "parentSpanId": "normalized-parent-span-id", + "name": "POST localhost:00000/?/", + "kind": 3, + "startTimeUnixNano": "0", + "endTimeUnixNano": "0", + "attributes": [ + { + "key": "component", + "value": { + "stringValue": "HttpMessageHandler" + } + }, + { + "key": "http-client-handler-type", + "value": { + "stringValue": "System.Net.Http.SocketsHttpHandler" + } + }, + { + "key": "http.method", + "value": { + "stringValue": "POST" + } + }, + { + "key": "http.status_code", + "value": { + "intValue": "200" + } + }, + { + "key": "http.url", + "value": { + "stringValue": "http://localhost:00000/Guid_2/?UploadValuesTaskAsync" + } + }, + { + "key": "operation.name", + "value": { + "stringValue": "http.request" + } + }, + { + "key": "out.host", + "value": { + "stringValue": "localhost" + } + }, + { + "key": "resource.name", + "value": { + "stringValue": "POST localhost:00000/?/" + } + }, + { + "key": "runtime-id", + "value": { + "stringValue": "Guid_1" + } + }, + { + "key": "service.name", + "value": { + "stringValue": "samples.webrequest-http-client" + } + }, + { + "key": "span.kind", + "value": { + "stringValue": "client" + } + }, + { + "key": "span.type", + "value": { + "stringValue": "http" + } + } + ] + }, + { + "traceId": "normalized-trace-id", + "spanId": "normalized-span-id", + "parentSpanId": "normalized-parent-span-id", + "name": "POST localhost:00000/?/", + "kind": 3, + "startTimeUnixNano": "0", + "endTimeUnixNano": "0", + "attributes": [ + { + "key": "component", + "value": { + "stringValue": "HttpMessageHandler" + } + }, + { + "key": "http-client-handler-type", + "value": { + "stringValue": "System.Net.Http.SocketsHttpHandler" + } + }, + { + "key": "http.method", + "value": { + "stringValue": "POST" + } + }, + { + "key": "http.status_code", + "value": { + "intValue": "200" + } + }, + { + "key": "http.url", + "value": { + "stringValue": "http://localhost:00000/Guid_2/?UploadValuesTaskAsync2" + } + }, + { + "key": "operation.name", + "value": { + "stringValue": "http.request" + } + }, + { + "key": "out.host", + "value": { + "stringValue": "localhost" + } + }, + { + "key": "resource.name", + "value": { + "stringValue": "POST localhost:00000/?/" + } + }, + { + "key": "runtime-id", + "value": { + "stringValue": "Guid_1" + } + }, + { + "key": "service.name", + "value": { + "stringValue": "samples.webrequest-http-client" + } + }, + { + "key": "span.kind", + "value": { + "stringValue": "client" + } + }, + { + "key": "span.type", + "value": { + "stringValue": "http" + } + } + ] + }, + { + "traceId": "normalized-trace-id", + "spanId": "normalized-span-id", + "parentSpanId": "normalized-parent-span-id", + "name": "POST localhost:00000/?/", + "kind": 3, + "startTimeUnixNano": "0", + "endTimeUnixNano": "0", + "attributes": [ + { + "key": "component", + "value": { + "stringValue": "HttpMessageHandler" + } + }, + { + "key": "http-client-handler-type", + "value": { + "stringValue": "System.Net.Http.SocketsHttpHandler" + } + }, + { + "key": "http.method", + "value": { + "stringValue": "POST" + } + }, + { + "key": "http.status_code", + "value": { + "intValue": "200" + } + }, + { + "key": "http.url", + "value": { + "stringValue": "http://localhost:00000/Guid_2/?UploadValuesTaskAsync3" + } + }, + { + "key": "operation.name", + "value": { + "stringValue": "http.request" + } + }, + { + "key": "out.host", + "value": { + "stringValue": "localhost" + } + }, + { + "key": "resource.name", + "value": { + "stringValue": "POST localhost:00000/?/" + } + }, + { + "key": "runtime-id", + "value": { + "stringValue": "Guid_1" + } + }, + { + "key": "service.name", + "value": { + "stringValue": "samples.webrequest-http-client" + } + }, + { + "key": "span.kind", + "value": { + "stringValue": "client" + } + }, + { + "key": "span.type", + "value": { + "stringValue": "http" + } + } + ] + }, + { + "traceId": "normalized-trace-id", + "spanId": "normalized-span-id", + "parentSpanId": "normalized-parent-span-id", + "name": "POST localhost:00000/?/", + "kind": 3, + "startTimeUnixNano": "0", + "endTimeUnixNano": "0", + "attributes": [ + { + "key": "component", + "value": { + "stringValue": "HttpMessageHandler" + } + }, + { + "key": "http-client-handler-type", + "value": { + "stringValue": "System.Net.Http.SocketsHttpHandler" + } + }, + { + "key": "http.method", + "value": { + "stringValue": "POST" + } + }, + { + "key": "http.status_code", + "value": { + "intValue": "200" + } + }, + { + "key": "http.url", + "value": { + "stringValue": "http://localhost:00000/Guid_2/?UploadValuesTaskAsync4" + } + }, + { + "key": "operation.name", + "value": { + "stringValue": "http.request" + } + }, + { + "key": "out.host", + "value": { + "stringValue": "localhost" + } + }, + { + "key": "resource.name", + "value": { + "stringValue": "POST localhost:00000/?/" + } + }, + { + "key": "runtime-id", + "value": { + "stringValue": "Guid_1" + } + }, + { + "key": "service.name", + "value": { + "stringValue": "samples.webrequest-http-client" + } + }, + { + "key": "span.kind", + "value": { + "stringValue": "client" + } + }, + { + "key": "span.type", + "value": { + "stringValue": "http" + } + } + ] + }, + { + "traceId": "normalized-trace-id", + "spanId": "normalized-span-id", + "parentSpanId": "normalized-parent-span-id", + "name": "UploadData", + "kind": 1, + "startTimeUnixNano": "0", + "endTimeUnixNano": "0", + "attributes": [ + { + "key": "operation.name", + "value": { + "stringValue": "internal" + } + }, + { + "key": "otel.library.name", + "value": { + "stringValue": "Samples.WebRequest" + } + }, + { + "key": "otel.status_code", + "value": { + "stringValue": "STATUS_CODE_UNSET" + } + }, + { + "key": "otel.trace_id", + "value": { + "stringValue": "normalized-otel-trace-id" + } + }, + { + "key": "resource.name", + "value": { + "stringValue": "UploadData" + } + }, + { + "key": "service.name", + "value": { + "stringValue": "samples.webrequest" + } + }, + { + "key": "span.kind", + "value": { + "stringValue": "internal" + } + }, + { + "key": "span.type", + "value": { + "stringValue": "custom" + } + } + ] + }, + { + "traceId": "normalized-trace-id", + "spanId": "normalized-span-id", + "parentSpanId": "normalized-parent-span-id", + "name": "UploadDataAsync", + "kind": 1, + "startTimeUnixNano": "0", + "endTimeUnixNano": "0", + "attributes": [ + { + "key": "operation.name", + "value": { + "stringValue": "internal" + } + }, + { + "key": "otel.library.name", + "value": { + "stringValue": "Samples.WebRequest" + } + }, + { + "key": "otel.status_code", + "value": { + "stringValue": "STATUS_CODE_UNSET" + } + }, + { + "key": "otel.trace_id", + "value": { + "stringValue": "normalized-otel-trace-id" + } + }, + { + "key": "resource.name", + "value": { + "stringValue": "UploadDataAsync" + } + }, + { + "key": "service.name", + "value": { + "stringValue": "samples.webrequest" + } + }, + { + "key": "span.kind", + "value": { + "stringValue": "internal" + } + }, + { + "key": "span.type", + "value": { + "stringValue": "custom" + } + } + ] + }, + { + "traceId": "normalized-trace-id", + "spanId": "normalized-span-id", + "parentSpanId": "normalized-parent-span-id", + "name": "UploadDataTaskAsync", + "kind": 1, + "startTimeUnixNano": "0", + "endTimeUnixNano": "0", + "attributes": [ + { + "key": "operation.name", + "value": { + "stringValue": "internal" + } + }, + { + "key": "otel.library.name", + "value": { + "stringValue": "Samples.WebRequest" + } + }, + { + "key": "otel.status_code", + "value": { + "stringValue": "STATUS_CODE_UNSET" + } + }, + { + "key": "otel.trace_id", + "value": { + "stringValue": "normalized-otel-trace-id" + } + }, + { + "key": "resource.name", + "value": { + "stringValue": "UploadDataTaskAsync" + } + }, + { + "key": "service.name", + "value": { + "stringValue": "samples.webrequest" + } + }, + { + "key": "span.kind", + "value": { + "stringValue": "internal" + } + }, + { + "key": "span.type", + "value": { + "stringValue": "custom" + } + } + ] + }, + { + "traceId": "normalized-trace-id", + "spanId": "normalized-span-id", + "parentSpanId": "normalized-parent-span-id", + "name": "UploadFile", + "kind": 1, + "startTimeUnixNano": "0", + "endTimeUnixNano": "0", + "attributes": [ + { + "key": "operation.name", + "value": { + "stringValue": "internal" + } + }, + { + "key": "otel.library.name", + "value": { + "stringValue": "Samples.WebRequest" + } + }, + { + "key": "otel.status_code", + "value": { + "stringValue": "STATUS_CODE_UNSET" + } + }, + { + "key": "otel.trace_id", + "value": { + "stringValue": "normalized-otel-trace-id" + } + }, + { + "key": "resource.name", + "value": { + "stringValue": "UploadFile" + } + }, + { + "key": "service.name", + "value": { + "stringValue": "samples.webrequest" + } + }, + { + "key": "span.kind", + "value": { + "stringValue": "internal" + } + }, + { + "key": "span.type", + "value": { + "stringValue": "custom" + } + } + ] + }, + { + "traceId": "normalized-trace-id", + "spanId": "normalized-span-id", + "parentSpanId": "normalized-parent-span-id", + "name": "UploadFileAsync", + "kind": 1, + "startTimeUnixNano": "0", + "endTimeUnixNano": "0", + "attributes": [ + { + "key": "operation.name", + "value": { + "stringValue": "internal" + } + }, + { + "key": "otel.library.name", + "value": { + "stringValue": "Samples.WebRequest" + } + }, + { + "key": "otel.status_code", + "value": { + "stringValue": "STATUS_CODE_UNSET" + } + }, + { + "key": "otel.trace_id", + "value": { + "stringValue": "normalized-otel-trace-id" + } + }, + { + "key": "resource.name", + "value": { + "stringValue": "UploadFileAsync" + } + }, + { + "key": "service.name", + "value": { + "stringValue": "samples.webrequest" + } + }, + { + "key": "span.kind", + "value": { + "stringValue": "internal" + } + }, + { + "key": "span.type", + "value": { + "stringValue": "custom" + } + } + ] + }, + { + "traceId": "normalized-trace-id", + "spanId": "normalized-span-id", + "parentSpanId": "normalized-parent-span-id", + "name": "UploadFileTaskAsync", + "kind": 1, + "startTimeUnixNano": "0", + "endTimeUnixNano": "0", + "attributes": [ + { + "key": "operation.name", + "value": { + "stringValue": "internal" + } + }, + { + "key": "otel.library.name", + "value": { + "stringValue": "Samples.WebRequest" + } + }, + { + "key": "otel.status_code", + "value": { + "stringValue": "STATUS_CODE_UNSET" + } + }, + { + "key": "otel.trace_id", + "value": { + "stringValue": "normalized-otel-trace-id" + } + }, + { + "key": "resource.name", + "value": { + "stringValue": "UploadFileTaskAsync" + } + }, + { + "key": "service.name", + "value": { + "stringValue": "samples.webrequest" + } + }, + { + "key": "span.kind", + "value": { + "stringValue": "internal" + } + }, + { + "key": "span.type", + "value": { + "stringValue": "custom" + } + } + ] + }, + { + "traceId": "normalized-trace-id", + "spanId": "normalized-span-id", + "parentSpanId": "normalized-parent-span-id", + "name": "UploadString", + "kind": 1, + "startTimeUnixNano": "0", + "endTimeUnixNano": "0", + "attributes": [ + { + "key": "operation.name", + "value": { + "stringValue": "internal" + } + }, + { + "key": "otel.library.name", + "value": { + "stringValue": "Samples.WebRequest" + } + }, + { + "key": "otel.status_code", + "value": { + "stringValue": "STATUS_CODE_UNSET" + } + }, + { + "key": "otel.trace_id", + "value": { + "stringValue": "normalized-otel-trace-id" + } + }, + { + "key": "resource.name", + "value": { + "stringValue": "UploadString" + } + }, + { + "key": "service.name", + "value": { + "stringValue": "samples.webrequest" + } + }, + { + "key": "span.kind", + "value": { + "stringValue": "internal" + } + }, + { + "key": "span.type", + "value": { + "stringValue": "custom" + } + } + ] + }, + { + "traceId": "normalized-trace-id", + "spanId": "normalized-span-id", + "parentSpanId": "normalized-parent-span-id", + "name": "UploadStringAsync", + "kind": 1, + "startTimeUnixNano": "0", + "endTimeUnixNano": "0", + "attributes": [ + { + "key": "operation.name", + "value": { + "stringValue": "internal" + } + }, + { + "key": "otel.library.name", + "value": { + "stringValue": "Samples.WebRequest" + } + }, + { + "key": "otel.status_code", + "value": { + "stringValue": "STATUS_CODE_UNSET" + } + }, + { + "key": "otel.trace_id", + "value": { + "stringValue": "normalized-otel-trace-id" + } + }, + { + "key": "resource.name", + "value": { + "stringValue": "UploadStringAsync" + } + }, + { + "key": "service.name", + "value": { + "stringValue": "samples.webrequest" + } + }, + { + "key": "span.kind", + "value": { + "stringValue": "internal" + } + }, + { + "key": "span.type", + "value": { + "stringValue": "custom" + } + } + ] + }, + { + "traceId": "normalized-trace-id", + "spanId": "normalized-span-id", + "parentSpanId": "normalized-parent-span-id", + "name": "UploadStringTaskAsync", + "kind": 1, + "startTimeUnixNano": "0", + "endTimeUnixNano": "0", + "attributes": [ + { + "key": "operation.name", + "value": { + "stringValue": "internal" + } + }, + { + "key": "otel.library.name", + "value": { + "stringValue": "Samples.WebRequest" + } + }, + { + "key": "otel.status_code", + "value": { + "stringValue": "STATUS_CODE_UNSET" + } + }, + { + "key": "otel.trace_id", + "value": { + "stringValue": "normalized-otel-trace-id" + } + }, + { + "key": "resource.name", + "value": { + "stringValue": "UploadStringTaskAsync" + } + }, + { + "key": "service.name", + "value": { + "stringValue": "samples.webrequest" + } + }, + { + "key": "span.kind", + "value": { + "stringValue": "internal" + } + }, + { + "key": "span.type", + "value": { + "stringValue": "custom" + } + } + ] + }, + { + "traceId": "normalized-trace-id", + "spanId": "normalized-span-id", + "parentSpanId": "normalized-parent-span-id", + "name": "UploadValues", + "kind": 1, + "startTimeUnixNano": "0", + "endTimeUnixNano": "0", + "attributes": [ + { + "key": "operation.name", + "value": { + "stringValue": "internal" + } + }, + { + "key": "otel.library.name", + "value": { + "stringValue": "Samples.WebRequest" + } + }, + { + "key": "otel.status_code", + "value": { + "stringValue": "STATUS_CODE_UNSET" + } + }, + { + "key": "otel.trace_id", + "value": { + "stringValue": "normalized-otel-trace-id" + } + }, + { + "key": "resource.name", + "value": { + "stringValue": "UploadValues" + } + }, + { + "key": "service.name", + "value": { + "stringValue": "samples.webrequest" + } + }, + { + "key": "span.kind", + "value": { + "stringValue": "internal" + } + }, + { + "key": "span.type", + "value": { + "stringValue": "custom" + } + } + ] + }, + { + "traceId": "normalized-trace-id", + "spanId": "normalized-span-id", + "parentSpanId": "normalized-parent-span-id", + "name": "UploadValuesAsync", + "kind": 1, + "startTimeUnixNano": "0", + "endTimeUnixNano": "0", + "attributes": [ + { + "key": "operation.name", + "value": { + "stringValue": "internal" + } + }, + { + "key": "otel.library.name", + "value": { + "stringValue": "Samples.WebRequest" + } + }, + { + "key": "otel.status_code", + "value": { + "stringValue": "STATUS_CODE_UNSET" + } + }, + { + "key": "otel.trace_id", + "value": { + "stringValue": "normalized-otel-trace-id" + } + }, + { + "key": "resource.name", + "value": { + "stringValue": "UploadValuesAsync" + } + }, + { + "key": "service.name", + "value": { + "stringValue": "samples.webrequest" + } + }, + { + "key": "span.kind", + "value": { + "stringValue": "internal" + } + }, + { + "key": "span.type", + "value": { + "stringValue": "custom" + } + } + ] + }, + { + "traceId": "normalized-trace-id", + "spanId": "normalized-span-id", + "parentSpanId": "normalized-parent-span-id", + "name": "UploadValuesTaskAsync", + "kind": 1, + "startTimeUnixNano": "0", + "endTimeUnixNano": "0", + "attributes": [ + { + "key": "operation.name", + "value": { + "stringValue": "internal" + } + }, + { + "key": "otel.library.name", + "value": { + "stringValue": "Samples.WebRequest" + } + }, + { + "key": "otel.status_code", + "value": { + "stringValue": "STATUS_CODE_UNSET" + } + }, + { + "key": "otel.trace_id", + "value": { + "stringValue": "normalized-otel-trace-id" + } + }, + { + "key": "resource.name", + "value": { + "stringValue": "UploadValuesTaskAsync" + } + }, + { + "key": "service.name", + "value": { + "stringValue": "samples.webrequest" + } + }, + { + "key": "span.kind", + "value": { + "stringValue": "internal" + } + }, + { + "key": "span.type", + "value": { + "stringValue": "custom" + } + } + ] + }, + { + "traceId": "normalized-trace-id", + "spanId": "normalized-span-id", + "name": "WebClient", + "kind": 1, + "startTimeUnixNano": "0", + "endTimeUnixNano": "0", + "attributes": [ + { + "key": "operation.name", + "value": { + "stringValue": "internal" + } + }, + { + "key": "otel.library.name", + "value": { + "stringValue": "Samples.WebRequest" + } + }, + { + "key": "otel.status_code", + "value": { + "stringValue": "STATUS_CODE_UNSET" + } + }, + { + "key": "otel.trace_id", + "value": { + "stringValue": "normalized-otel-trace-id" + } + }, + { + "key": "resource.name", + "value": { + "stringValue": "WebClient" + } + }, + { + "key": "runtime-id", + "value": { + "stringValue": "Guid_1" + } + }, + { + "key": "service.name", + "value": { + "stringValue": "samples.webrequest" + } + }, + { + "key": "span.kind", + "value": { + "stringValue": "internal" + } + }, + { + "key": "span.type", + "value": { + "stringValue": "custom" + } + } + ] + }, + { + "traceId": "normalized-trace-id", + "spanId": "normalized-span-id", + "name": "WebRequest", + "kind": 1, + "startTimeUnixNano": "0", + "endTimeUnixNano": "0", + "attributes": [ + { + "key": "operation.name", + "value": { + "stringValue": "internal" + } + }, + { + "key": "otel.library.name", + "value": { + "stringValue": "Samples.WebRequest" + } + }, + { + "key": "otel.status_code", + "value": { + "stringValue": "STATUS_CODE_UNSET" + } + }, + { + "key": "otel.trace_id", + "value": { + "stringValue": "normalized-otel-trace-id" + } + }, + { + "key": "resource.name", + "value": { + "stringValue": "WebRequest" + } + }, + { + "key": "runtime-id", + "value": { + "stringValue": "Guid_1" + } + }, + { + "key": "service.name", + "value": { + "stringValue": "samples.webrequest" + } + }, + { + "key": "span.kind", + "value": { + "stringValue": "internal" + } + }, + { + "key": "span.type", + "value": { + "stringValue": "custom" + } + } + ] + } + ] + } + ] + } + ] +} \ No newline at end of file diff --git a/tracer/test/snapshots/WebRequestTests.SubmitsOtlpTraces_DD_OtelSemantics.verified.txt b/tracer/test/snapshots/WebRequestTests.SubmitsOtlpTraces_DD_OtelSemantics.verified.txt new file mode 100644 index 000000000000..8b76239ed5d6 --- /dev/null +++ b/tracer/test/snapshots/WebRequestTests.SubmitsOtlpTraces_DD_OtelSemantics.verified.txt @@ -0,0 +1,4300 @@ +{ + "resourceSpans": [ + { + "resource": { + "attributes": [ + { + "key": "service.name", + "value": { + "stringValue": "Samples.WebRequest" + } + }, + { + "key": "service.version", + "value": { + "stringValue": "1.0.0" + } + }, + { + "key": "deployment.environment.name", + "value": { + "stringValue": "integration_tests" + } + }, + { + "key": "telemetry.sdk.name", + "value": { + "stringValue": "sdk-name" + } + }, + { + "key": "telemetry.sdk.language", + "value": { + "stringValue": "dotnet" + } + }, + { + "key": "telemetry.sdk.version", + "value": { + "stringValue": "sdk-version" + } + }, + { + "key": "git.commit.sha", + "value": { + "stringValue": "normalized-git-commit-sha" + } + }, + { + "key": "git.repository_url", + "value": { + "stringValue": "https://github.com/DataDog/dd-trace-dotnet" + } + }, + { + "key": "runtime-id", + "value": { + "stringValue": "Guid_1" + } + } + ] + }, + "scopeSpans": [ + { + "spans": [ + { + "traceId": "normalized-trace-id", + "spanId": "normalized-span-id", + "parentSpanId": "normalized-parent-span-id", + "name": "BeginGetRequestStream", + "kind": 1, + "startTimeUnixNano": "0", + "endTimeUnixNano": "0" + }, + { + "traceId": "normalized-trace-id", + "spanId": "normalized-span-id", + "parentSpanId": "normalized-parent-span-id", + "name": "BeginGetRequestStreamWithDistributedTracingHeaders", + "kind": 1, + "startTimeUnixNano": "0", + "endTimeUnixNano": "0" + }, + { + "traceId": "normalized-trace-id", + "spanId": "normalized-span-id", + "parentSpanId": "normalized-parent-span-id", + "name": "BeginGetResponse", + "kind": 1, + "startTimeUnixNano": "0", + "endTimeUnixNano": "0" + }, + { + "traceId": "normalized-trace-id", + "spanId": "normalized-span-id", + "parentSpanId": "normalized-parent-span-id", + "name": "BeginGetResponse TaskFactoryFromAsync", + "kind": 1, + "startTimeUnixNano": "0", + "endTimeUnixNano": "0" + }, + { + "traceId": "normalized-trace-id", + "spanId": "normalized-span-id", + "parentSpanId": "normalized-parent-span-id", + "name": "BeginGetResponseNotFound", + "kind": 1, + "startTimeUnixNano": "0", + "endTimeUnixNano": "0" + }, + { + "traceId": "normalized-trace-id", + "spanId": "normalized-span-id", + "parentSpanId": "normalized-parent-span-id", + "name": "BeginGetResponseTeapot", + "kind": 1, + "startTimeUnixNano": "0", + "endTimeUnixNano": "0" + }, + { + "traceId": "normalized-trace-id", + "spanId": "normalized-span-id", + "parentSpanId": "normalized-parent-span-id", + "name": "BeginGetResponseWithDistributedTracingHeaders", + "kind": 1, + "startTimeUnixNano": "0", + "endTimeUnixNano": "0" + }, + { + "traceId": "normalized-trace-id", + "spanId": "normalized-span-id", + "parentSpanId": "normalized-parent-span-id", + "name": "DownloadData", + "kind": 1, + "startTimeUnixNano": "0", + "endTimeUnixNano": "0" + }, + { + "traceId": "normalized-trace-id", + "spanId": "normalized-span-id", + "parentSpanId": "normalized-parent-span-id", + "name": "DownloadDataAsync", + "kind": 1, + "startTimeUnixNano": "0", + "endTimeUnixNano": "0" + }, + { + "traceId": "normalized-trace-id", + "spanId": "normalized-span-id", + "parentSpanId": "normalized-parent-span-id", + "name": "DownloadDataTaskAsync", + "kind": 1, + "startTimeUnixNano": "0", + "endTimeUnixNano": "0" + }, + { + "traceId": "normalized-trace-id", + "spanId": "normalized-span-id", + "parentSpanId": "normalized-parent-span-id", + "name": "DownloadFile", + "kind": 1, + "startTimeUnixNano": "0", + "endTimeUnixNano": "0" + }, + { + "traceId": "normalized-trace-id", + "spanId": "normalized-span-id", + "parentSpanId": "normalized-parent-span-id", + "name": "DownloadFileAsync", + "kind": 1, + "startTimeUnixNano": "0", + "endTimeUnixNano": "0" + }, + { + "traceId": "normalized-trace-id", + "spanId": "normalized-span-id", + "parentSpanId": "normalized-parent-span-id", + "name": "DownloadFileTaskAsync", + "kind": 1, + "startTimeUnixNano": "0", + "endTimeUnixNano": "0" + }, + { + "traceId": "normalized-trace-id", + "spanId": "normalized-span-id", + "parentSpanId": "normalized-parent-span-id", + "name": "DownloadString", + "kind": 1, + "startTimeUnixNano": "0", + "endTimeUnixNano": "0" + }, + { + "traceId": "normalized-trace-id", + "spanId": "normalized-span-id", + "parentSpanId": "normalized-parent-span-id", + "name": "DownloadStringAsync", + "kind": 1, + "startTimeUnixNano": "0", + "endTimeUnixNano": "0" + }, + { + "traceId": "normalized-trace-id", + "spanId": "normalized-span-id", + "parentSpanId": "normalized-parent-span-id", + "name": "DownloadStringTaskAsync", + "kind": 1, + "startTimeUnixNano": "0", + "endTimeUnixNano": "0" + }, + { + "traceId": "normalized-trace-id", + "spanId": "normalized-span-id", + "parentSpanId": "normalized-parent-span-id", + "name": "GET", + "kind": 3, + "startTimeUnixNano": "0", + "endTimeUnixNano": "0", + "attributes": [ + { + "key": "http.request.method", + "value": { + "stringValue": "GET" + } + }, + { + "key": "http.response.status_code", + "value": { + "intValue": "200" + } + }, + { + "key": "server.address", + "value": { + "stringValue": "localhost" + } + }, + { + "key": "server.port", + "value": { + "intValue": "8080" + } + }, + { + "key": "url.full", + "value": { + "stringValue": "http://localhost:00000/Guid_2/?DownloadData" + } + } + ] + }, + { + "traceId": "normalized-trace-id", + "spanId": "normalized-span-id", + "parentSpanId": "normalized-parent-span-id", + "name": "GET", + "kind": 3, + "startTimeUnixNano": "0", + "endTimeUnixNano": "0", + "attributes": [ + { + "key": "http.request.method", + "value": { + "stringValue": "GET" + } + }, + { + "key": "http.response.status_code", + "value": { + "intValue": "200" + } + }, + { + "key": "server.address", + "value": { + "stringValue": "localhost" + } + }, + { + "key": "server.port", + "value": { + "intValue": "8080" + } + }, + { + "key": "url.full", + "value": { + "stringValue": "http://localhost:00000/Guid_2/?DownloadData2" + } + } + ] + }, + { + "traceId": "normalized-trace-id", + "spanId": "normalized-span-id", + "parentSpanId": "normalized-parent-span-id", + "name": "GET", + "kind": 3, + "startTimeUnixNano": "0", + "endTimeUnixNano": "0", + "attributes": [ + { + "key": "http.request.method", + "value": { + "stringValue": "GET" + } + }, + { + "key": "http.response.status_code", + "value": { + "intValue": "200" + } + }, + { + "key": "server.address", + "value": { + "stringValue": "localhost" + } + }, + { + "key": "server.port", + "value": { + "intValue": "8080" + } + }, + { + "key": "url.full", + "value": { + "stringValue": "http://localhost:00000/Guid_2/?DownloadDataAsync" + } + } + ] + }, + { + "traceId": "normalized-trace-id", + "spanId": "normalized-span-id", + "parentSpanId": "normalized-parent-span-id", + "name": "GET", + "kind": 3, + "startTimeUnixNano": "0", + "endTimeUnixNano": "0", + "attributes": [ + { + "key": "http.request.method", + "value": { + "stringValue": "GET" + } + }, + { + "key": "http.response.status_code", + "value": { + "intValue": "200" + } + }, + { + "key": "server.address", + "value": { + "stringValue": "localhost" + } + }, + { + "key": "server.port", + "value": { + "intValue": "8080" + } + }, + { + "key": "url.full", + "value": { + "stringValue": "http://localhost:00000/Guid_2/?DownloadDataAsync2" + } + } + ] + }, + { + "traceId": "normalized-trace-id", + "spanId": "normalized-span-id", + "parentSpanId": "normalized-parent-span-id", + "name": "GET", + "kind": 3, + "startTimeUnixNano": "0", + "endTimeUnixNano": "0", + "attributes": [ + { + "key": "http.request.method", + "value": { + "stringValue": "GET" + } + }, + { + "key": "http.response.status_code", + "value": { + "intValue": "200" + } + }, + { + "key": "server.address", + "value": { + "stringValue": "localhost" + } + }, + { + "key": "server.port", + "value": { + "intValue": "8080" + } + }, + { + "key": "url.full", + "value": { + "stringValue": "http://localhost:00000/Guid_2/?DownloadDataTaskAsync" + } + } + ] + }, + { + "traceId": "normalized-trace-id", + "spanId": "normalized-span-id", + "parentSpanId": "normalized-parent-span-id", + "name": "GET", + "kind": 3, + "startTimeUnixNano": "0", + "endTimeUnixNano": "0", + "attributes": [ + { + "key": "http.request.method", + "value": { + "stringValue": "GET" + } + }, + { + "key": "http.response.status_code", + "value": { + "intValue": "200" + } + }, + { + "key": "server.address", + "value": { + "stringValue": "localhost" + } + }, + { + "key": "server.port", + "value": { + "intValue": "8080" + } + }, + { + "key": "url.full", + "value": { + "stringValue": "http://localhost:00000/Guid_2/?DownloadDataTaskAsync2" + } + } + ] + }, + { + "traceId": "normalized-trace-id", + "spanId": "normalized-span-id", + "parentSpanId": "normalized-parent-span-id", + "name": "GET", + "kind": 3, + "startTimeUnixNano": "0", + "endTimeUnixNano": "0", + "attributes": [ + { + "key": "http.request.method", + "value": { + "stringValue": "GET" + } + }, + { + "key": "http.response.status_code", + "value": { + "intValue": "200" + } + }, + { + "key": "server.address", + "value": { + "stringValue": "localhost" + } + }, + { + "key": "server.port", + "value": { + "intValue": "8080" + } + }, + { + "key": "url.full", + "value": { + "stringValue": "http://localhost:00000/Guid_2/?DownloadFile" + } + } + ] + }, + { + "traceId": "normalized-trace-id", + "spanId": "normalized-span-id", + "parentSpanId": "normalized-parent-span-id", + "name": "GET", + "kind": 3, + "startTimeUnixNano": "0", + "endTimeUnixNano": "0", + "attributes": [ + { + "key": "http.request.method", + "value": { + "stringValue": "GET" + } + }, + { + "key": "http.response.status_code", + "value": { + "intValue": "200" + } + }, + { + "key": "server.address", + "value": { + "stringValue": "localhost" + } + }, + { + "key": "server.port", + "value": { + "intValue": "8080" + } + }, + { + "key": "url.full", + "value": { + "stringValue": "http://localhost:00000/Guid_2/?DownloadFile2" + } + } + ] + }, + { + "traceId": "normalized-trace-id", + "spanId": "normalized-span-id", + "parentSpanId": "normalized-parent-span-id", + "name": "GET", + "kind": 3, + "startTimeUnixNano": "0", + "endTimeUnixNano": "0", + "attributes": [ + { + "key": "http.request.method", + "value": { + "stringValue": "GET" + } + }, + { + "key": "http.response.status_code", + "value": { + "intValue": "200" + } + }, + { + "key": "server.address", + "value": { + "stringValue": "localhost" + } + }, + { + "key": "server.port", + "value": { + "intValue": "8080" + } + }, + { + "key": "url.full", + "value": { + "stringValue": "http://localhost:00000/Guid_2/?DownloadFileAsync" + } + } + ] + }, + { + "traceId": "normalized-trace-id", + "spanId": "normalized-span-id", + "parentSpanId": "normalized-parent-span-id", + "name": "GET", + "kind": 3, + "startTimeUnixNano": "0", + "endTimeUnixNano": "0", + "attributes": [ + { + "key": "http.request.method", + "value": { + "stringValue": "GET" + } + }, + { + "key": "http.response.status_code", + "value": { + "intValue": "200" + } + }, + { + "key": "server.address", + "value": { + "stringValue": "localhost" + } + }, + { + "key": "server.port", + "value": { + "intValue": "8080" + } + }, + { + "key": "url.full", + "value": { + "stringValue": "http://localhost:00000/Guid_2/?DownloadFileAsync2" + } + } + ] + }, + { + "traceId": "normalized-trace-id", + "spanId": "normalized-span-id", + "parentSpanId": "normalized-parent-span-id", + "name": "GET", + "kind": 3, + "startTimeUnixNano": "0", + "endTimeUnixNano": "0", + "attributes": [ + { + "key": "http.request.method", + "value": { + "stringValue": "GET" + } + }, + { + "key": "http.response.status_code", + "value": { + "intValue": "200" + } + }, + { + "key": "server.address", + "value": { + "stringValue": "localhost" + } + }, + { + "key": "server.port", + "value": { + "intValue": "8080" + } + }, + { + "key": "url.full", + "value": { + "stringValue": "http://localhost:00000/Guid_2/?DownloadFileTaskAsync" + } + } + ] + }, + { + "traceId": "normalized-trace-id", + "spanId": "normalized-span-id", + "parentSpanId": "normalized-parent-span-id", + "name": "GET", + "kind": 3, + "startTimeUnixNano": "0", + "endTimeUnixNano": "0", + "attributes": [ + { + "key": "http.request.method", + "value": { + "stringValue": "GET" + } + }, + { + "key": "http.response.status_code", + "value": { + "intValue": "200" + } + }, + { + "key": "server.address", + "value": { + "stringValue": "localhost" + } + }, + { + "key": "server.port", + "value": { + "intValue": "8080" + } + }, + { + "key": "url.full", + "value": { + "stringValue": "http://localhost:00000/Guid_2/?DownloadFileTaskAsync2" + } + } + ] + }, + { + "traceId": "normalized-trace-id", + "spanId": "normalized-span-id", + "parentSpanId": "normalized-parent-span-id", + "name": "GET", + "kind": 3, + "startTimeUnixNano": "0", + "endTimeUnixNano": "0", + "attributes": [ + { + "key": "http.request.method", + "value": { + "stringValue": "GET" + } + }, + { + "key": "http.response.status_code", + "value": { + "intValue": "200" + } + }, + { + "key": "server.address", + "value": { + "stringValue": "localhost" + } + }, + { + "key": "server.port", + "value": { + "intValue": "8080" + } + }, + { + "key": "url.full", + "value": { + "stringValue": "http://localhost:00000/Guid_2/?DownloadString" + } + } + ] + }, + { + "traceId": "normalized-trace-id", + "spanId": "normalized-span-id", + "parentSpanId": "normalized-parent-span-id", + "name": "GET", + "kind": 3, + "startTimeUnixNano": "0", + "endTimeUnixNano": "0", + "attributes": [ + { + "key": "http.request.method", + "value": { + "stringValue": "GET" + } + }, + { + "key": "http.response.status_code", + "value": { + "intValue": "200" + } + }, + { + "key": "server.address", + "value": { + "stringValue": "localhost" + } + }, + { + "key": "server.port", + "value": { + "intValue": "8080" + } + }, + { + "key": "url.full", + "value": { + "stringValue": "http://localhost:00000/Guid_2/?DownloadString2" + } + } + ] + }, + { + "traceId": "normalized-trace-id", + "spanId": "normalized-span-id", + "parentSpanId": "normalized-parent-span-id", + "name": "GET", + "kind": 3, + "startTimeUnixNano": "0", + "endTimeUnixNano": "0", + "attributes": [ + { + "key": "http.request.method", + "value": { + "stringValue": "GET" + } + }, + { + "key": "http.response.status_code", + "value": { + "intValue": "200" + } + }, + { + "key": "server.address", + "value": { + "stringValue": "localhost" + } + }, + { + "key": "server.port", + "value": { + "intValue": "8080" + } + }, + { + "key": "url.full", + "value": { + "stringValue": "http://localhost:00000/Guid_2/?DownloadStringAsync" + } + } + ] + }, + { + "traceId": "normalized-trace-id", + "spanId": "normalized-span-id", + "parentSpanId": "normalized-parent-span-id", + "name": "GET", + "kind": 3, + "startTimeUnixNano": "0", + "endTimeUnixNano": "0", + "attributes": [ + { + "key": "http.request.method", + "value": { + "stringValue": "GET" + } + }, + { + "key": "http.response.status_code", + "value": { + "intValue": "200" + } + }, + { + "key": "server.address", + "value": { + "stringValue": "localhost" + } + }, + { + "key": "server.port", + "value": { + "intValue": "8080" + } + }, + { + "key": "url.full", + "value": { + "stringValue": "http://localhost:00000/Guid_2/?DownloadStringAsync2" + } + } + ] + }, + { + "traceId": "normalized-trace-id", + "spanId": "normalized-span-id", + "parentSpanId": "normalized-parent-span-id", + "name": "GET", + "kind": 3, + "startTimeUnixNano": "0", + "endTimeUnixNano": "0", + "attributes": [ + { + "key": "http.request.method", + "value": { + "stringValue": "GET" + } + }, + { + "key": "http.response.status_code", + "value": { + "intValue": "200" + } + }, + { + "key": "server.address", + "value": { + "stringValue": "localhost" + } + }, + { + "key": "server.port", + "value": { + "intValue": "8080" + } + }, + { + "key": "url.full", + "value": { + "stringValue": "http://localhost:00000/Guid_2/?DownloadStringTaskAsync" + } + } + ] + }, + { + "traceId": "normalized-trace-id", + "spanId": "normalized-span-id", + "parentSpanId": "normalized-parent-span-id", + "name": "GET", + "kind": 3, + "startTimeUnixNano": "0", + "endTimeUnixNano": "0", + "attributes": [ + { + "key": "http.request.method", + "value": { + "stringValue": "GET" + } + }, + { + "key": "http.response.status_code", + "value": { + "intValue": "200" + } + }, + { + "key": "server.address", + "value": { + "stringValue": "localhost" + } + }, + { + "key": "server.port", + "value": { + "intValue": "8080" + } + }, + { + "key": "url.full", + "value": { + "stringValue": "http://localhost:00000/Guid_2/?DownloadStringTaskAsync2" + } + } + ] + }, + { + "traceId": "normalized-trace-id", + "spanId": "normalized-span-id", + "parentSpanId": "normalized-parent-span-id", + "name": "GET", + "kind": 3, + "startTimeUnixNano": "0", + "endTimeUnixNano": "0", + "attributes": [ + { + "key": "http.request.method", + "value": { + "stringValue": "GET" + } + }, + { + "key": "http.response.status_code", + "value": { + "intValue": "200" + } + }, + { + "key": "server.address", + "value": { + "stringValue": "localhost" + } + }, + { + "key": "server.port", + "value": { + "intValue": "8080" + } + }, + { + "key": "url.full", + "value": { + "stringValue": "http://localhost:00000/Guid_2/?GetResponse" + } + } + ] + }, + { + "traceId": "normalized-trace-id", + "spanId": "normalized-span-id", + "parentSpanId": "normalized-parent-span-id", + "name": "GET", + "kind": 3, + "startTimeUnixNano": "0", + "endTimeUnixNano": "0", + "attributes": [ + { + "key": "http.request.method", + "value": { + "stringValue": "GET" + } + }, + { + "key": "http.response.status_code", + "value": { + "intValue": "200" + } + }, + { + "key": "server.address", + "value": { + "stringValue": "localhost" + } + }, + { + "key": "server.port", + "value": { + "intValue": "8080" + } + }, + { + "key": "url.full", + "value": { + "stringValue": "http://localhost:00000/Guid_2/?GetResponse" + } + } + ] + }, + { + "traceId": "normalized-trace-id", + "spanId": "normalized-span-id", + "parentSpanId": "normalized-parent-span-id", + "name": "GET", + "kind": 3, + "startTimeUnixNano": "0", + "endTimeUnixNano": "0", + "attributes": [ + { + "key": "http.request.method", + "value": { + "stringValue": "GET" + } + }, + { + "key": "http.response.status_code", + "value": { + "intValue": "200" + } + }, + { + "key": "server.address", + "value": { + "stringValue": "localhost" + } + }, + { + "key": "server.port", + "value": { + "intValue": "8080" + } + }, + { + "key": "url.full", + "value": { + "stringValue": "http://localhost:00000/Guid_2/?GetResponseAsync" + } + } + ] + }, + { + "traceId": "normalized-trace-id", + "spanId": "normalized-span-id", + "parentSpanId": "normalized-parent-span-id", + "name": "GET", + "kind": 3, + "startTimeUnixNano": "0", + "endTimeUnixNano": "0", + "attributes": [ + { + "key": "http.request.method", + "value": { + "stringValue": "GET" + } + }, + { + "key": "http.response.status_code", + "value": { + "intValue": "200" + } + }, + { + "key": "server.address", + "value": { + "stringValue": "localhost" + } + }, + { + "key": "server.port", + "value": { + "intValue": "8080" + } + }, + { + "key": "url.full", + "value": { + "stringValue": "http://localhost:00000/Guid_2/?GetResponseAsync" + } + } + ] + }, + { + "traceId": "normalized-trace-id", + "spanId": "normalized-span-id", + "parentSpanId": "normalized-parent-span-id", + "name": "GET", + "kind": 3, + "startTimeUnixNano": "0", + "endTimeUnixNano": "0", + "attributes": [ + { + "key": "http.request.method", + "value": { + "stringValue": "GET" + } + }, + { + "key": "http.response.status_code", + "value": { + "intValue": "404" + } + }, + { + "key": "server.address", + "value": { + "stringValue": "localhost" + } + }, + { + "key": "server.port", + "value": { + "intValue": "8080" + } + }, + { + "key": "url.full", + "value": { + "stringValue": "http://localhost:00000/Guid_2/?GetResponseAsyncNotFound" + } + } + ], + "events": [ + { + "timeUnixNano": "0", + "name": "exception", + "attributes": [ + { + "key": "exception.type", + "value": { + "stringValue": "System.Net.WebException" + } + }, + { + "key": "exception.message", + "value": { + "stringValue": "The remote server returned an error: (404) Not Found." + } + }, + { + "key": "exception.stacktrace", + "value": { + "stringValue": "System.Net.WebException: The remote server returned an error: (404) Not Found.\n at System.Net.HttpWebRequest.EndGetResponse(IAsyncResult asyncResult)\n at System.Threading.Tasks.TaskFactory`1.FromAsyncCoreLogic(IAsyncResult iar, Func`2 endFunction, Action`1 endAction, Task`1 promise, Boolean requiresSynchronization)\n--- End of stack trace from previous location ---\n at System.Net.WebRequest.GetResponseAsync()" + } + } + ] + } + ] + }, + { + "traceId": "normalized-trace-id", + "spanId": "normalized-span-id", + "parentSpanId": "normalized-parent-span-id", + "name": "GET", + "kind": 3, + "startTimeUnixNano": "0", + "endTimeUnixNano": "0", + "attributes": [ + { + "key": "http.request.method", + "value": { + "stringValue": "GET" + } + }, + { + "key": "http.response.status_code", + "value": { + "intValue": "418" + } + }, + { + "key": "server.address", + "value": { + "stringValue": "localhost" + } + }, + { + "key": "server.port", + "value": { + "intValue": "8080" + } + }, + { + "key": "url.full", + "value": { + "stringValue": "http://localhost:00000/Guid_2/?GetResponseAsyncTeapot" + } + } + ], + "events": [ + { + "timeUnixNano": "0", + "name": "exception", + "attributes": [ + { + "key": "exception.type", + "value": { + "stringValue": "System.Net.WebException" + } + }, + { + "key": "exception.message", + "value": { + "stringValue": "The remote server returned an error: (418) ." + } + }, + { + "key": "exception.stacktrace", + "value": { + "stringValue": "System.Net.WebException: The remote server returned an error: (418) .\n at System.Net.HttpWebRequest.EndGetResponse(IAsyncResult asyncResult)\n at System.Threading.Tasks.TaskFactory`1.FromAsyncCoreLogic(IAsyncResult iar, Func`2 endFunction, Action`1 endAction, Task`1 promise, Boolean requiresSynchronization)\n--- End of stack trace from previous location ---\n at System.Net.WebRequest.GetResponseAsync()" + } + } + ] + } + ] + }, + { + "traceId": "normalized-trace-id", + "spanId": "normalized-span-id", + "parentSpanId": "normalized-parent-span-id", + "name": "GET", + "kind": 3, + "startTimeUnixNano": "0", + "endTimeUnixNano": "0", + "attributes": [ + { + "key": "http.request.method", + "value": { + "stringValue": "GET" + } + }, + { + "key": "http.response.status_code", + "value": { + "intValue": "404" + } + }, + { + "key": "server.address", + "value": { + "stringValue": "localhost" + } + }, + { + "key": "server.port", + "value": { + "intValue": "8080" + } + }, + { + "key": "url.full", + "value": { + "stringValue": "http://localhost:00000/Guid_2/?GetResponseNotFound" + } + } + ], + "events": [ + { + "timeUnixNano": "0", + "name": "exception", + "attributes": [ + { + "key": "exception.type", + "value": { + "stringValue": "System.Net.WebException" + } + }, + { + "key": "exception.message", + "value": { + "stringValue": "The remote server returned an error: (404) Not Found." + } + }, + { + "key": "exception.stacktrace", + "value": { + "stringValue": "System.Net.WebException: The remote server returned an error: (404) Not Found.\n at System.Net.HttpWebRequest.GetResponse()\n at Samples.WebRequest.RequestHelpers.SendWebRequestRequests(Boolean tracingDisabled, String url, String requestContent) in {SolutionDirectory}tracer/test/test-applications/integrations/Samples.WebRequest/RequestHelpers.cs:line 377" + } + } + ] + } + ] + }, + { + "traceId": "normalized-trace-id", + "spanId": "normalized-span-id", + "parentSpanId": "normalized-parent-span-id", + "name": "GET", + "kind": 3, + "startTimeUnixNano": "0", + "endTimeUnixNano": "0", + "attributes": [ + { + "key": "http.request.method", + "value": { + "stringValue": "GET" + } + }, + { + "key": "http.response.status_code", + "value": { + "intValue": "418" + } + }, + { + "key": "server.address", + "value": { + "stringValue": "localhost" + } + }, + { + "key": "server.port", + "value": { + "intValue": "8080" + } + }, + { + "key": "url.full", + "value": { + "stringValue": "http://localhost:00000/Guid_2/?GetResponseTeapot" + } + } + ], + "events": [ + { + "timeUnixNano": "0", + "name": "exception", + "attributes": [ + { + "key": "exception.type", + "value": { + "stringValue": "System.Net.WebException" + } + }, + { + "key": "exception.message", + "value": { + "stringValue": "The remote server returned an error: (418) ." + } + }, + { + "key": "exception.stacktrace", + "value": { + "stringValue": "System.Net.WebException: The remote server returned an error: (418) .\n at System.Net.HttpWebRequest.GetResponse()\n at Samples.WebRequest.RequestHelpers.SendWebRequestRequests(Boolean tracingDisabled, String url, String requestContent) in {SolutionDirectory}tracer/test/test-applications/integrations/Samples.WebRequest/RequestHelpers.cs:line 400" + } + } + ] + } + ] + }, + { + "traceId": "normalized-trace-id", + "spanId": "normalized-span-id", + "parentSpanId": "normalized-parent-span-id", + "name": "GET", + "kind": 3, + "startTimeUnixNano": "0", + "endTimeUnixNano": "0", + "attributes": [ + { + "key": "http.request.method", + "value": { + "stringValue": "GET" + } + }, + { + "key": "http.response.status_code", + "value": { + "intValue": "200" + } + }, + { + "key": "server.address", + "value": { + "stringValue": "localhost" + } + }, + { + "key": "server.port", + "value": { + "intValue": "8080" + } + }, + { + "key": "url.full", + "value": { + "stringValue": "http://localhost:00000/Guid_2/?GetResponse_NoBuffering" + } + } + ] + }, + { + "traceId": "normalized-trace-id", + "spanId": "normalized-span-id", + "parentSpanId": "normalized-parent-span-id", + "name": "GET", + "kind": 3, + "startTimeUnixNano": "0", + "endTimeUnixNano": "0", + "attributes": [ + { + "key": "http.request.method", + "value": { + "stringValue": "GET" + } + }, + { + "key": "http.response.status_code", + "value": { + "intValue": "200" + } + }, + { + "key": "server.address", + "value": { + "stringValue": "localhost" + } + }, + { + "key": "server.port", + "value": { + "intValue": "8080" + } + }, + { + "key": "url.full", + "value": { + "stringValue": "http://localhost:00000/Guid_2/?OpenRead" + } + } + ] + }, + { + "traceId": "normalized-trace-id", + "spanId": "normalized-span-id", + "parentSpanId": "normalized-parent-span-id", + "name": "GET", + "kind": 3, + "startTimeUnixNano": "0", + "endTimeUnixNano": "0", + "attributes": [ + { + "key": "http.request.method", + "value": { + "stringValue": "GET" + } + }, + { + "key": "http.response.status_code", + "value": { + "intValue": "200" + } + }, + { + "key": "server.address", + "value": { + "stringValue": "localhost" + } + }, + { + "key": "server.port", + "value": { + "intValue": "8080" + } + }, + { + "key": "url.full", + "value": { + "stringValue": "http://localhost:00000/Guid_2/?OpenRead2" + } + } + ] + }, + { + "traceId": "normalized-trace-id", + "spanId": "normalized-span-id", + "parentSpanId": "normalized-parent-span-id", + "name": "GET", + "kind": 3, + "startTimeUnixNano": "0", + "endTimeUnixNano": "0", + "attributes": [ + { + "key": "http.request.method", + "value": { + "stringValue": "GET" + } + }, + { + "key": "http.response.status_code", + "value": { + "intValue": "200" + } + }, + { + "key": "server.address", + "value": { + "stringValue": "localhost" + } + }, + { + "key": "server.port", + "value": { + "intValue": "8080" + } + }, + { + "key": "url.full", + "value": { + "stringValue": "http://localhost:00000/Guid_2/?OpenReadAsync" + } + } + ] + }, + { + "traceId": "normalized-trace-id", + "spanId": "normalized-span-id", + "parentSpanId": "normalized-parent-span-id", + "name": "GET", + "kind": 3, + "startTimeUnixNano": "0", + "endTimeUnixNano": "0", + "attributes": [ + { + "key": "http.request.method", + "value": { + "stringValue": "GET" + } + }, + { + "key": "http.response.status_code", + "value": { + "intValue": "200" + } + }, + { + "key": "server.address", + "value": { + "stringValue": "localhost" + } + }, + { + "key": "server.port", + "value": { + "intValue": "8080" + } + }, + { + "key": "url.full", + "value": { + "stringValue": "http://localhost:00000/Guid_2/?OpenReadAsync2" + } + } + ] + }, + { + "traceId": "normalized-trace-id", + "spanId": "normalized-span-id", + "parentSpanId": "normalized-parent-span-id", + "name": "GET", + "kind": 3, + "startTimeUnixNano": "0", + "endTimeUnixNano": "0", + "attributes": [ + { + "key": "http.request.method", + "value": { + "stringValue": "GET" + } + }, + { + "key": "http.response.status_code", + "value": { + "intValue": "200" + } + }, + { + "key": "server.address", + "value": { + "stringValue": "localhost" + } + }, + { + "key": "server.port", + "value": { + "intValue": "8080" + } + }, + { + "key": "url.full", + "value": { + "stringValue": "http://localhost:00000/Guid_2/?OpenReadTaskAsync" + } + } + ] + }, + { + "traceId": "normalized-trace-id", + "spanId": "normalized-span-id", + "parentSpanId": "normalized-parent-span-id", + "name": "GET", + "kind": 3, + "startTimeUnixNano": "0", + "endTimeUnixNano": "0", + "attributes": [ + { + "key": "http.request.method", + "value": { + "stringValue": "GET" + } + }, + { + "key": "http.response.status_code", + "value": { + "intValue": "200" + } + }, + { + "key": "server.address", + "value": { + "stringValue": "localhost" + } + }, + { + "key": "server.port", + "value": { + "intValue": "8080" + } + }, + { + "key": "url.full", + "value": { + "stringValue": "http://localhost:00000/Guid_2/?OpenReadTaskAsync2" + } + } + ] + }, + { + "traceId": "normalized-trace-id", + "spanId": "normalized-span-id", + "parentSpanId": "normalized-parent-span-id", + "name": "GET", + "kind": 3, + "startTimeUnixNano": "0", + "endTimeUnixNano": "0", + "attributes": [ + { + "key": "http.request.method", + "value": { + "stringValue": "GET" + } + }, + { + "key": "http.response.status_code", + "value": { + "intValue": "200" + } + }, + { + "key": "server.address", + "value": { + "stringValue": "localhost" + } + }, + { + "key": "server.port", + "value": { + "intValue": "8080" + } + }, + { + "key": "url.full", + "value": { + "stringValue": "http://localhost:00000/Guid_2/?TaskFactoryFromAsync" + } + } + ] + }, + { + "traceId": "normalized-trace-id", + "spanId": "normalized-span-id", + "parentSpanId": "normalized-parent-span-id", + "name": "GetRequestStream", + "kind": 1, + "startTimeUnixNano": "0", + "endTimeUnixNano": "0" + }, + { + "traceId": "normalized-trace-id", + "spanId": "normalized-span-id", + "parentSpanId": "normalized-parent-span-id", + "name": "GetRequestStreamWithDistributedTracingHeaders", + "kind": 1, + "startTimeUnixNano": "0", + "endTimeUnixNano": "0" + }, + { + "traceId": "normalized-trace-id", + "spanId": "normalized-span-id", + "parentSpanId": "normalized-parent-span-id", + "name": "GetResponse", + "kind": 1, + "startTimeUnixNano": "0", + "endTimeUnixNano": "0" + }, + { + "traceId": "normalized-trace-id", + "spanId": "normalized-span-id", + "parentSpanId": "normalized-parent-span-id", + "name": "GetResponseAsync", + "kind": 1, + "startTimeUnixNano": "0", + "endTimeUnixNano": "0" + }, + { + "traceId": "normalized-trace-id", + "spanId": "normalized-span-id", + "parentSpanId": "normalized-parent-span-id", + "name": "GetResponseAsyncNotFound", + "kind": 1, + "startTimeUnixNano": "0", + "endTimeUnixNano": "0" + }, + { + "traceId": "normalized-trace-id", + "spanId": "normalized-span-id", + "parentSpanId": "normalized-parent-span-id", + "name": "GetResponseAsyncTeapot", + "kind": 1, + "startTimeUnixNano": "0", + "endTimeUnixNano": "0" + }, + { + "traceId": "normalized-trace-id", + "spanId": "normalized-span-id", + "parentSpanId": "normalized-parent-span-id", + "name": "GetResponseAsyncWithDistributedTracingHeaders", + "kind": 1, + "startTimeUnixNano": "0", + "endTimeUnixNano": "0" + }, + { + "traceId": "normalized-trace-id", + "spanId": "normalized-span-id", + "parentSpanId": "normalized-parent-span-id", + "name": "GetResponseNotFound", + "kind": 1, + "startTimeUnixNano": "0", + "endTimeUnixNano": "0" + }, + { + "traceId": "normalized-trace-id", + "spanId": "normalized-span-id", + "parentSpanId": "normalized-parent-span-id", + "name": "GetResponseTeapot", + "kind": 1, + "startTimeUnixNano": "0", + "endTimeUnixNano": "0" + }, + { + "traceId": "normalized-trace-id", + "spanId": "normalized-span-id", + "parentSpanId": "normalized-parent-span-id", + "name": "GetResponseWithDistributedTracingHeaders", + "kind": 1, + "startTimeUnixNano": "0", + "endTimeUnixNano": "0" + }, + { + "traceId": "normalized-trace-id", + "spanId": "normalized-span-id", + "parentSpanId": "normalized-parent-span-id", + "name": "OpenRead", + "kind": 1, + "startTimeUnixNano": "0", + "endTimeUnixNano": "0" + }, + { + "traceId": "normalized-trace-id", + "spanId": "normalized-span-id", + "parentSpanId": "normalized-parent-span-id", + "name": "OpenReadAsync", + "kind": 1, + "startTimeUnixNano": "0", + "endTimeUnixNano": "0" + }, + { + "traceId": "normalized-trace-id", + "spanId": "normalized-span-id", + "parentSpanId": "normalized-parent-span-id", + "name": "OpenReadTaskAsync", + "kind": 1, + "startTimeUnixNano": "0", + "endTimeUnixNano": "0" + }, + { + "traceId": "normalized-trace-id", + "spanId": "normalized-span-id", + "name": "POST", + "kind": 3, + "startTimeUnixNano": "0", + "endTimeUnixNano": "0", + "attributes": [ + { + "key": "http.request.method", + "value": { + "stringValue": "POST" + } + }, + { + "key": "http.response.status_code", + "value": { + "intValue": "200" + } + }, + { + "key": "server.address", + "value": { + "stringValue": "localhost" + } + }, + { + "key": "server.port", + "value": { + "intValue": "8080" + } + }, + { + "key": "url.full", + "value": { + "stringValue": "http://localhost:00000/Guid_2/?BeginGetRequestStream" + } + } + ] + }, + { + "traceId": "normalized-trace-id", + "spanId": "normalized-span-id", + "parentSpanId": "normalized-parent-span-id", + "name": "POST", + "kind": 3, + "startTimeUnixNano": "0", + "endTimeUnixNano": "0", + "attributes": [ + { + "key": "http.request.method", + "value": { + "stringValue": "POST" + } + }, + { + "key": "http.response.status_code", + "value": { + "intValue": "200" + } + }, + { + "key": "server.address", + "value": { + "stringValue": "localhost" + } + }, + { + "key": "server.port", + "value": { + "intValue": "8080" + } + }, + { + "key": "url.full", + "value": { + "stringValue": "http://localhost:00000/Guid_2/?BeginGetRequestStream" + } + } + ] + }, + { + "traceId": "normalized-trace-id", + "spanId": "normalized-span-id", + "parentSpanId": "normalized-parent-span-id", + "name": "POST", + "kind": 3, + "startTimeUnixNano": "0", + "endTimeUnixNano": "0", + "attributes": [ + { + "key": "http.request.method", + "value": { + "stringValue": "POST" + } + }, + { + "key": "http.response.status_code", + "value": { + "intValue": "200" + } + }, + { + "key": "server.address", + "value": { + "stringValue": "localhost" + } + }, + { + "key": "server.port", + "value": { + "intValue": "8080" + } + }, + { + "key": "url.full", + "value": { + "stringValue": "http://localhost:00000/Guid_2/?BeginGetRequestStream" + } + } + ] + }, + { + "traceId": "normalized-trace-id", + "spanId": "normalized-span-id", + "parentSpanId": "normalized-parent-span-id", + "name": "POST", + "kind": 3, + "startTimeUnixNano": "0", + "endTimeUnixNano": "0", + "attributes": [ + { + "key": "http.request.method", + "value": { + "stringValue": "POST" + } + }, + { + "key": "http.response.status_code", + "value": { + "intValue": "200" + } + }, + { + "key": "server.address", + "value": { + "stringValue": "localhost" + } + }, + { + "key": "server.port", + "value": { + "intValue": "8080" + } + }, + { + "key": "url.full", + "value": { + "stringValue": "http://localhost:00000/Guid_2/?BeginGetRequestStream_NoBuffering" + } + } + ] + }, + { + "traceId": "normalized-trace-id", + "spanId": "normalized-span-id", + "parentSpanId": "normalized-parent-span-id", + "name": "POST", + "kind": 3, + "startTimeUnixNano": "0", + "endTimeUnixNano": "0", + "attributes": [ + { + "key": "http.request.method", + "value": { + "stringValue": "POST" + } + }, + { + "key": "http.response.status_code", + "value": { + "intValue": "200" + } + }, + { + "key": "server.address", + "value": { + "stringValue": "localhost" + } + }, + { + "key": "server.port", + "value": { + "intValue": "8080" + } + }, + { + "key": "url.full", + "value": { + "stringValue": "http://localhost:00000/Guid_2/?BeginGetResponseAsync" + } + } + ] + }, + { + "traceId": "normalized-trace-id", + "spanId": "normalized-span-id", + "parentSpanId": "normalized-parent-span-id", + "name": "POST", + "kind": 3, + "startTimeUnixNano": "0", + "endTimeUnixNano": "0", + "attributes": [ + { + "key": "http.request.method", + "value": { + "stringValue": "POST" + } + }, + { + "key": "http.response.status_code", + "value": { + "intValue": "200" + } + }, + { + "key": "server.address", + "value": { + "stringValue": "localhost" + } + }, + { + "key": "server.port", + "value": { + "intValue": "8080" + } + }, + { + "key": "url.full", + "value": { + "stringValue": "http://localhost:00000/Guid_2/?BeginGetResponseAsync" + } + } + ] + }, + { + "traceId": "normalized-trace-id", + "spanId": "normalized-span-id", + "parentSpanId": "normalized-parent-span-id", + "name": "POST", + "kind": 3, + "startTimeUnixNano": "0", + "endTimeUnixNano": "0", + "attributes": [ + { + "key": "component", + "value": { + "stringValue": "HttpMessageHandler" + } + }, + { + "key": "http-client-handler-type", + "value": { + "stringValue": "System.Net.Http.SocketsHttpHandler" + } + }, + { + "key": "http.request.method", + "value": { + "stringValue": "POST" + } + }, + { + "key": "http.response.status_code", + "value": { + "intValue": "200" + } + }, + { + "key": "server.address", + "value": { + "stringValue": "localhost" + } + }, + { + "key": "server.port", + "value": { + "intValue": "8080" + } + }, + { + "key": "url.full", + "value": { + "stringValue": "http://localhost:00000/Guid_2/?BeginGetResponseAsync_NoBuffering" + } + } + ] + }, + { + "traceId": "normalized-trace-id", + "spanId": "normalized-span-id", + "parentSpanId": "normalized-parent-span-id", + "name": "POST", + "kind": 3, + "startTimeUnixNano": "0", + "endTimeUnixNano": "0", + "attributes": [ + { + "key": "http.request.method", + "value": { + "stringValue": "POST" + } + }, + { + "key": "http.response.status_code", + "value": { + "intValue": "404" + } + }, + { + "key": "server.address", + "value": { + "stringValue": "localhost" + } + }, + { + "key": "server.port", + "value": { + "intValue": "8080" + } + }, + { + "key": "url.full", + "value": { + "stringValue": "http://localhost:00000/Guid_2/?BeginGetResponseNotFoundAsync" + } + } + ] + }, + { + "traceId": "normalized-trace-id", + "spanId": "normalized-span-id", + "parentSpanId": "normalized-parent-span-id", + "name": "POST", + "kind": 3, + "startTimeUnixNano": "0", + "endTimeUnixNano": "0", + "attributes": [ + { + "key": "http.request.method", + "value": { + "stringValue": "POST" + } + }, + { + "key": "http.response.status_code", + "value": { + "intValue": "418" + } + }, + { + "key": "server.address", + "value": { + "stringValue": "localhost" + } + }, + { + "key": "server.port", + "value": { + "intValue": "8080" + } + }, + { + "key": "url.full", + "value": { + "stringValue": "http://localhost:00000/Guid_2/?BeginGetResponseTeapotAsync" + } + } + ] + }, + { + "traceId": "normalized-trace-id", + "spanId": "normalized-span-id", + "name": "POST", + "kind": 3, + "startTimeUnixNano": "0", + "endTimeUnixNano": "0", + "attributes": [ + { + "key": "http.request.method", + "value": { + "stringValue": "POST" + } + }, + { + "key": "http.response.status_code", + "value": { + "intValue": "200" + } + }, + { + "key": "server.address", + "value": { + "stringValue": "localhost" + } + }, + { + "key": "server.port", + "value": { + "intValue": "8080" + } + }, + { + "key": "url.full", + "value": { + "stringValue": "http://localhost:00000/Guid_2/?GetRequestStream" + } + } + ] + }, + { + "traceId": "normalized-trace-id", + "spanId": "normalized-span-id", + "parentSpanId": "normalized-parent-span-id", + "name": "POST", + "kind": 3, + "startTimeUnixNano": "0", + "endTimeUnixNano": "0", + "attributes": [ + { + "key": "http.request.method", + "value": { + "stringValue": "POST" + } + }, + { + "key": "http.response.status_code", + "value": { + "intValue": "200" + } + }, + { + "key": "server.address", + "value": { + "stringValue": "localhost" + } + }, + { + "key": "server.port", + "value": { + "intValue": "8080" + } + }, + { + "key": "url.full", + "value": { + "stringValue": "http://localhost:00000/Guid_2/?GetRequestStream" + } + } + ] + }, + { + "traceId": "normalized-trace-id", + "spanId": "normalized-span-id", + "parentSpanId": "normalized-parent-span-id", + "name": "POST", + "kind": 3, + "startTimeUnixNano": "0", + "endTimeUnixNano": "0", + "attributes": [ + { + "key": "http.request.method", + "value": { + "stringValue": "POST" + } + }, + { + "key": "http.response.status_code", + "value": { + "intValue": "200" + } + }, + { + "key": "server.address", + "value": { + "stringValue": "localhost" + } + }, + { + "key": "server.port", + "value": { + "intValue": "8080" + } + }, + { + "key": "url.full", + "value": { + "stringValue": "http://localhost:00000/Guid_2/?GetRequestStream" + } + } + ] + }, + { + "traceId": "normalized-trace-id", + "spanId": "normalized-span-id", + "parentSpanId": "normalized-parent-span-id", + "name": "POST", + "kind": 3, + "startTimeUnixNano": "0", + "endTimeUnixNano": "0", + "attributes": [ + { + "key": "http.request.method", + "value": { + "stringValue": "POST" + } + }, + { + "key": "http.response.status_code", + "value": { + "intValue": "200" + } + }, + { + "key": "server.address", + "value": { + "stringValue": "localhost" + } + }, + { + "key": "server.port", + "value": { + "intValue": "8080" + } + }, + { + "key": "url.full", + "value": { + "stringValue": "http://localhost:00000/Guid_2/?GetRequestStream_NoBuffering" + } + } + ] + }, + { + "traceId": "normalized-trace-id", + "spanId": "normalized-span-id", + "parentSpanId": "normalized-parent-span-id", + "name": "POST", + "kind": 3, + "startTimeUnixNano": "0", + "endTimeUnixNano": "0", + "attributes": [ + { + "key": "http.request.method", + "value": { + "stringValue": "POST" + } + }, + { + "key": "http.response.status_code", + "value": { + "intValue": "200" + } + }, + { + "key": "server.address", + "value": { + "stringValue": "localhost" + } + }, + { + "key": "server.port", + "value": { + "intValue": "8080" + } + }, + { + "key": "url.full", + "value": { + "stringValue": "http://localhost:00000/Guid_2/?UploadData" + } + } + ] + }, + { + "traceId": "normalized-trace-id", + "spanId": "normalized-span-id", + "parentSpanId": "normalized-parent-span-id", + "name": "POST", + "kind": 3, + "startTimeUnixNano": "0", + "endTimeUnixNano": "0", + "attributes": [ + { + "key": "http.request.method", + "value": { + "stringValue": "POST" + } + }, + { + "key": "http.response.status_code", + "value": { + "intValue": "200" + } + }, + { + "key": "server.address", + "value": { + "stringValue": "localhost" + } + }, + { + "key": "server.port", + "value": { + "intValue": "8080" + } + }, + { + "key": "url.full", + "value": { + "stringValue": "http://localhost:00000/Guid_2/?UploadData2" + } + } + ] + }, + { + "traceId": "normalized-trace-id", + "spanId": "normalized-span-id", + "parentSpanId": "normalized-parent-span-id", + "name": "POST", + "kind": 3, + "startTimeUnixNano": "0", + "endTimeUnixNano": "0", + "attributes": [ + { + "key": "http.request.method", + "value": { + "stringValue": "POST" + } + }, + { + "key": "http.response.status_code", + "value": { + "intValue": "200" + } + }, + { + "key": "server.address", + "value": { + "stringValue": "localhost" + } + }, + { + "key": "server.port", + "value": { + "intValue": "8080" + } + }, + { + "key": "url.full", + "value": { + "stringValue": "http://localhost:00000/Guid_2/?UploadData3" + } + } + ] + }, + { + "traceId": "normalized-trace-id", + "spanId": "normalized-span-id", + "parentSpanId": "normalized-parent-span-id", + "name": "POST", + "kind": 3, + "startTimeUnixNano": "0", + "endTimeUnixNano": "0", + "attributes": [ + { + "key": "http.request.method", + "value": { + "stringValue": "POST" + } + }, + { + "key": "http.response.status_code", + "value": { + "intValue": "200" + } + }, + { + "key": "server.address", + "value": { + "stringValue": "localhost" + } + }, + { + "key": "server.port", + "value": { + "intValue": "8080" + } + }, + { + "key": "url.full", + "value": { + "stringValue": "http://localhost:00000/Guid_2/?UploadData4" + } + } + ] + }, + { + "traceId": "normalized-trace-id", + "spanId": "normalized-span-id", + "parentSpanId": "normalized-parent-span-id", + "name": "POST", + "kind": 3, + "startTimeUnixNano": "0", + "endTimeUnixNano": "0", + "attributes": [ + { + "key": "http.request.method", + "value": { + "stringValue": "POST" + } + }, + { + "key": "http.response.status_code", + "value": { + "intValue": "200" + } + }, + { + "key": "server.address", + "value": { + "stringValue": "localhost" + } + }, + { + "key": "server.port", + "value": { + "intValue": "8080" + } + }, + { + "key": "url.full", + "value": { + "stringValue": "http://localhost:00000/Guid_2/?UploadDataAsync" + } + } + ] + }, + { + "traceId": "normalized-trace-id", + "spanId": "normalized-span-id", + "parentSpanId": "normalized-parent-span-id", + "name": "POST", + "kind": 3, + "startTimeUnixNano": "0", + "endTimeUnixNano": "0", + "attributes": [ + { + "key": "http.request.method", + "value": { + "stringValue": "POST" + } + }, + { + "key": "http.response.status_code", + "value": { + "intValue": "200" + } + }, + { + "key": "server.address", + "value": { + "stringValue": "localhost" + } + }, + { + "key": "server.port", + "value": { + "intValue": "8080" + } + }, + { + "key": "url.full", + "value": { + "stringValue": "http://localhost:00000/Guid_2/?UploadDataAsync2" + } + } + ] + }, + { + "traceId": "normalized-trace-id", + "spanId": "normalized-span-id", + "parentSpanId": "normalized-parent-span-id", + "name": "POST", + "kind": 3, + "startTimeUnixNano": "0", + "endTimeUnixNano": "0", + "attributes": [ + { + "key": "http.request.method", + "value": { + "stringValue": "POST" + } + }, + { + "key": "http.response.status_code", + "value": { + "intValue": "200" + } + }, + { + "key": "server.address", + "value": { + "stringValue": "localhost" + } + }, + { + "key": "server.port", + "value": { + "intValue": "8080" + } + }, + { + "key": "url.full", + "value": { + "stringValue": "http://localhost:00000/Guid_2/?UploadDataAsync3" + } + } + ] + }, + { + "traceId": "normalized-trace-id", + "spanId": "normalized-span-id", + "parentSpanId": "normalized-parent-span-id", + "name": "POST", + "kind": 3, + "startTimeUnixNano": "0", + "endTimeUnixNano": "0", + "attributes": [ + { + "key": "http.request.method", + "value": { + "stringValue": "POST" + } + }, + { + "key": "http.response.status_code", + "value": { + "intValue": "200" + } + }, + { + "key": "server.address", + "value": { + "stringValue": "localhost" + } + }, + { + "key": "server.port", + "value": { + "intValue": "8080" + } + }, + { + "key": "url.full", + "value": { + "stringValue": "http://localhost:00000/Guid_2/?UploadDataTaskAsync" + } + } + ] + }, + { + "traceId": "normalized-trace-id", + "spanId": "normalized-span-id", + "parentSpanId": "normalized-parent-span-id", + "name": "POST", + "kind": 3, + "startTimeUnixNano": "0", + "endTimeUnixNano": "0", + "attributes": [ + { + "key": "http.request.method", + "value": { + "stringValue": "POST" + } + }, + { + "key": "http.response.status_code", + "value": { + "intValue": "200" + } + }, + { + "key": "server.address", + "value": { + "stringValue": "localhost" + } + }, + { + "key": "server.port", + "value": { + "intValue": "8080" + } + }, + { + "key": "url.full", + "value": { + "stringValue": "http://localhost:00000/Guid_2/?UploadDataTaskAsync2" + } + } + ] + }, + { + "traceId": "normalized-trace-id", + "spanId": "normalized-span-id", + "parentSpanId": "normalized-parent-span-id", + "name": "POST", + "kind": 3, + "startTimeUnixNano": "0", + "endTimeUnixNano": "0", + "attributes": [ + { + "key": "http.request.method", + "value": { + "stringValue": "POST" + } + }, + { + "key": "http.response.status_code", + "value": { + "intValue": "200" + } + }, + { + "key": "server.address", + "value": { + "stringValue": "localhost" + } + }, + { + "key": "server.port", + "value": { + "intValue": "8080" + } + }, + { + "key": "url.full", + "value": { + "stringValue": "http://localhost:00000/Guid_2/?UploadDataTaskAsync3" + } + } + ] + }, + { + "traceId": "normalized-trace-id", + "spanId": "normalized-span-id", + "parentSpanId": "normalized-parent-span-id", + "name": "POST", + "kind": 3, + "startTimeUnixNano": "0", + "endTimeUnixNano": "0", + "attributes": [ + { + "key": "http.request.method", + "value": { + "stringValue": "POST" + } + }, + { + "key": "http.response.status_code", + "value": { + "intValue": "200" + } + }, + { + "key": "server.address", + "value": { + "stringValue": "localhost" + } + }, + { + "key": "server.port", + "value": { + "intValue": "8080" + } + }, + { + "key": "url.full", + "value": { + "stringValue": "http://localhost:00000/Guid_2/?UploadDataTaskAsync4" + } + } + ] + }, + { + "traceId": "normalized-trace-id", + "spanId": "normalized-span-id", + "parentSpanId": "normalized-parent-span-id", + "name": "POST", + "kind": 3, + "startTimeUnixNano": "0", + "endTimeUnixNano": "0", + "attributes": [ + { + "key": "http.request.method", + "value": { + "stringValue": "POST" + } + }, + { + "key": "http.response.status_code", + "value": { + "intValue": "200" + } + }, + { + "key": "server.address", + "value": { + "stringValue": "localhost" + } + }, + { + "key": "server.port", + "value": { + "intValue": "8080" + } + }, + { + "key": "url.full", + "value": { + "stringValue": "http://localhost:00000/Guid_2/?UploadFile" + } + } + ] + }, + { + "traceId": "normalized-trace-id", + "spanId": "normalized-span-id", + "parentSpanId": "normalized-parent-span-id", + "name": "POST", + "kind": 3, + "startTimeUnixNano": "0", + "endTimeUnixNano": "0", + "attributes": [ + { + "key": "http.request.method", + "value": { + "stringValue": "POST" + } + }, + { + "key": "http.response.status_code", + "value": { + "intValue": "200" + } + }, + { + "key": "server.address", + "value": { + "stringValue": "localhost" + } + }, + { + "key": "server.port", + "value": { + "intValue": "8080" + } + }, + { + "key": "url.full", + "value": { + "stringValue": "http://localhost:00000/Guid_2/?UploadFile2" + } + } + ] + }, + { + "traceId": "normalized-trace-id", + "spanId": "normalized-span-id", + "parentSpanId": "normalized-parent-span-id", + "name": "POST", + "kind": 3, + "startTimeUnixNano": "0", + "endTimeUnixNano": "0", + "attributes": [ + { + "key": "http.request.method", + "value": { + "stringValue": "POST" + } + }, + { + "key": "http.response.status_code", + "value": { + "intValue": "200" + } + }, + { + "key": "server.address", + "value": { + "stringValue": "localhost" + } + }, + { + "key": "server.port", + "value": { + "intValue": "8080" + } + }, + { + "key": "url.full", + "value": { + "stringValue": "http://localhost:00000/Guid_2/?UploadFile3" + } + } + ] + }, + { + "traceId": "normalized-trace-id", + "spanId": "normalized-span-id", + "parentSpanId": "normalized-parent-span-id", + "name": "POST", + "kind": 3, + "startTimeUnixNano": "0", + "endTimeUnixNano": "0", + "attributes": [ + { + "key": "http.request.method", + "value": { + "stringValue": "POST" + } + }, + { + "key": "http.response.status_code", + "value": { + "intValue": "200" + } + }, + { + "key": "server.address", + "value": { + "stringValue": "localhost" + } + }, + { + "key": "server.port", + "value": { + "intValue": "8080" + } + }, + { + "key": "url.full", + "value": { + "stringValue": "http://localhost:00000/Guid_2/?UploadFile4" + } + } + ] + }, + { + "traceId": "normalized-trace-id", + "spanId": "normalized-span-id", + "parentSpanId": "normalized-parent-span-id", + "name": "POST", + "kind": 3, + "startTimeUnixNano": "0", + "endTimeUnixNano": "0", + "attributes": [ + { + "key": "http.request.method", + "value": { + "stringValue": "POST" + } + }, + { + "key": "http.response.status_code", + "value": { + "intValue": "200" + } + }, + { + "key": "server.address", + "value": { + "stringValue": "localhost" + } + }, + { + "key": "server.port", + "value": { + "intValue": "8080" + } + }, + { + "key": "url.full", + "value": { + "stringValue": "http://localhost:00000/Guid_2/?UploadFileAsync" + } + } + ] + }, + { + "traceId": "normalized-trace-id", + "spanId": "normalized-span-id", + "parentSpanId": "normalized-parent-span-id", + "name": "POST", + "kind": 3, + "startTimeUnixNano": "0", + "endTimeUnixNano": "0", + "attributes": [ + { + "key": "http.request.method", + "value": { + "stringValue": "POST" + } + }, + { + "key": "http.response.status_code", + "value": { + "intValue": "200" + } + }, + { + "key": "server.address", + "value": { + "stringValue": "localhost" + } + }, + { + "key": "server.port", + "value": { + "intValue": "8080" + } + }, + { + "key": "url.full", + "value": { + "stringValue": "http://localhost:00000/Guid_2/?UploadFileAsync2" + } + } + ] + }, + { + "traceId": "normalized-trace-id", + "spanId": "normalized-span-id", + "parentSpanId": "normalized-parent-span-id", + "name": "POST", + "kind": 3, + "startTimeUnixNano": "0", + "endTimeUnixNano": "0", + "attributes": [ + { + "key": "http.request.method", + "value": { + "stringValue": "POST" + } + }, + { + "key": "http.response.status_code", + "value": { + "intValue": "200" + } + }, + { + "key": "server.address", + "value": { + "stringValue": "localhost" + } + }, + { + "key": "server.port", + "value": { + "intValue": "8080" + } + }, + { + "key": "url.full", + "value": { + "stringValue": "http://localhost:00000/Guid_2/?UploadFileAsync3" + } + } + ] + }, + { + "traceId": "normalized-trace-id", + "spanId": "normalized-span-id", + "parentSpanId": "normalized-parent-span-id", + "name": "POST", + "kind": 3, + "startTimeUnixNano": "0", + "endTimeUnixNano": "0", + "attributes": [ + { + "key": "http.request.method", + "value": { + "stringValue": "POST" + } + }, + { + "key": "http.response.status_code", + "value": { + "intValue": "200" + } + }, + { + "key": "server.address", + "value": { + "stringValue": "localhost" + } + }, + { + "key": "server.port", + "value": { + "intValue": "8080" + } + }, + { + "key": "url.full", + "value": { + "stringValue": "http://localhost:00000/Guid_2/?UploadFileTaskAsync" + } + } + ] + }, + { + "traceId": "normalized-trace-id", + "spanId": "normalized-span-id", + "parentSpanId": "normalized-parent-span-id", + "name": "POST", + "kind": 3, + "startTimeUnixNano": "0", + "endTimeUnixNano": "0", + "attributes": [ + { + "key": "http.request.method", + "value": { + "stringValue": "POST" + } + }, + { + "key": "http.response.status_code", + "value": { + "intValue": "200" + } + }, + { + "key": "server.address", + "value": { + "stringValue": "localhost" + } + }, + { + "key": "server.port", + "value": { + "intValue": "8080" + } + }, + { + "key": "url.full", + "value": { + "stringValue": "http://localhost:00000/Guid_2/?UploadFileTaskAsync2" + } + } + ] + }, + { + "traceId": "normalized-trace-id", + "spanId": "normalized-span-id", + "parentSpanId": "normalized-parent-span-id", + "name": "POST", + "kind": 3, + "startTimeUnixNano": "0", + "endTimeUnixNano": "0", + "attributes": [ + { + "key": "http.request.method", + "value": { + "stringValue": "POST" + } + }, + { + "key": "http.response.status_code", + "value": { + "intValue": "200" + } + }, + { + "key": "server.address", + "value": { + "stringValue": "localhost" + } + }, + { + "key": "server.port", + "value": { + "intValue": "8080" + } + }, + { + "key": "url.full", + "value": { + "stringValue": "http://localhost:00000/Guid_2/?UploadFileTaskAsync3" + } + } + ] + }, + { + "traceId": "normalized-trace-id", + "spanId": "normalized-span-id", + "parentSpanId": "normalized-parent-span-id", + "name": "POST", + "kind": 3, + "startTimeUnixNano": "0", + "endTimeUnixNano": "0", + "attributes": [ + { + "key": "http.request.method", + "value": { + "stringValue": "POST" + } + }, + { + "key": "http.response.status_code", + "value": { + "intValue": "200" + } + }, + { + "key": "server.address", + "value": { + "stringValue": "localhost" + } + }, + { + "key": "server.port", + "value": { + "intValue": "8080" + } + }, + { + "key": "url.full", + "value": { + "stringValue": "http://localhost:00000/Guid_2/?UploadFileTaskAsync4" + } + } + ] + }, + { + "traceId": "normalized-trace-id", + "spanId": "normalized-span-id", + "parentSpanId": "normalized-parent-span-id", + "name": "POST", + "kind": 3, + "startTimeUnixNano": "0", + "endTimeUnixNano": "0", + "attributes": [ + { + "key": "http.request.method", + "value": { + "stringValue": "POST" + } + }, + { + "key": "http.response.status_code", + "value": { + "intValue": "200" + } + }, + { + "key": "server.address", + "value": { + "stringValue": "localhost" + } + }, + { + "key": "server.port", + "value": { + "intValue": "8080" + } + }, + { + "key": "url.full", + "value": { + "stringValue": "http://localhost:00000/Guid_2/?UploadString" + } + } + ] + }, + { + "traceId": "normalized-trace-id", + "spanId": "normalized-span-id", + "parentSpanId": "normalized-parent-span-id", + "name": "POST", + "kind": 3, + "startTimeUnixNano": "0", + "endTimeUnixNano": "0", + "attributes": [ + { + "key": "http.request.method", + "value": { + "stringValue": "POST" + } + }, + { + "key": "http.response.status_code", + "value": { + "intValue": "200" + } + }, + { + "key": "server.address", + "value": { + "stringValue": "localhost" + } + }, + { + "key": "server.port", + "value": { + "intValue": "8080" + } + }, + { + "key": "url.full", + "value": { + "stringValue": "http://localhost:00000/Guid_2/?UploadString2" + } + } + ] + }, + { + "traceId": "normalized-trace-id", + "spanId": "normalized-span-id", + "parentSpanId": "normalized-parent-span-id", + "name": "POST", + "kind": 3, + "startTimeUnixNano": "0", + "endTimeUnixNano": "0", + "attributes": [ + { + "key": "http.request.method", + "value": { + "stringValue": "POST" + } + }, + { + "key": "http.response.status_code", + "value": { + "intValue": "200" + } + }, + { + "key": "server.address", + "value": { + "stringValue": "localhost" + } + }, + { + "key": "server.port", + "value": { + "intValue": "8080" + } + }, + { + "key": "url.full", + "value": { + "stringValue": "http://localhost:00000/Guid_2/?UploadString3" + } + } + ] + }, + { + "traceId": "normalized-trace-id", + "spanId": "normalized-span-id", + "parentSpanId": "normalized-parent-span-id", + "name": "POST", + "kind": 3, + "startTimeUnixNano": "0", + "endTimeUnixNano": "0", + "attributes": [ + { + "key": "http.request.method", + "value": { + "stringValue": "POST" + } + }, + { + "key": "http.response.status_code", + "value": { + "intValue": "200" + } + }, + { + "key": "server.address", + "value": { + "stringValue": "localhost" + } + }, + { + "key": "server.port", + "value": { + "intValue": "8080" + } + }, + { + "key": "url.full", + "value": { + "stringValue": "http://localhost:00000/Guid_2/?UploadString4" + } + } + ] + }, + { + "traceId": "normalized-trace-id", + "spanId": "normalized-span-id", + "parentSpanId": "normalized-parent-span-id", + "name": "POST", + "kind": 3, + "startTimeUnixNano": "0", + "endTimeUnixNano": "0", + "attributes": [ + { + "key": "http.request.method", + "value": { + "stringValue": "POST" + } + }, + { + "key": "http.response.status_code", + "value": { + "intValue": "200" + } + }, + { + "key": "server.address", + "value": { + "stringValue": "localhost" + } + }, + { + "key": "server.port", + "value": { + "intValue": "8080" + } + }, + { + "key": "url.full", + "value": { + "stringValue": "http://localhost:00000/Guid_2/?UploadStringAsync" + } + } + ] + }, + { + "traceId": "normalized-trace-id", + "spanId": "normalized-span-id", + "parentSpanId": "normalized-parent-span-id", + "name": "POST", + "kind": 3, + "startTimeUnixNano": "0", + "endTimeUnixNano": "0", + "attributes": [ + { + "key": "http.request.method", + "value": { + "stringValue": "POST" + } + }, + { + "key": "http.response.status_code", + "value": { + "intValue": "200" + } + }, + { + "key": "server.address", + "value": { + "stringValue": "localhost" + } + }, + { + "key": "server.port", + "value": { + "intValue": "8080" + } + }, + { + "key": "url.full", + "value": { + "stringValue": "http://localhost:00000/Guid_2/?UploadStringAsync2" + } + } + ] + }, + { + "traceId": "normalized-trace-id", + "spanId": "normalized-span-id", + "parentSpanId": "normalized-parent-span-id", + "name": "POST", + "kind": 3, + "startTimeUnixNano": "0", + "endTimeUnixNano": "0", + "attributes": [ + { + "key": "http.request.method", + "value": { + "stringValue": "POST" + } + }, + { + "key": "http.response.status_code", + "value": { + "intValue": "200" + } + }, + { + "key": "server.address", + "value": { + "stringValue": "localhost" + } + }, + { + "key": "server.port", + "value": { + "intValue": "8080" + } + }, + { + "key": "url.full", + "value": { + "stringValue": "http://localhost:00000/Guid_2/?UploadStringAsync3" + } + } + ] + }, + { + "traceId": "normalized-trace-id", + "spanId": "normalized-span-id", + "parentSpanId": "normalized-parent-span-id", + "name": "POST", + "kind": 3, + "startTimeUnixNano": "0", + "endTimeUnixNano": "0", + "attributes": [ + { + "key": "http.request.method", + "value": { + "stringValue": "POST" + } + }, + { + "key": "http.response.status_code", + "value": { + "intValue": "200" + } + }, + { + "key": "server.address", + "value": { + "stringValue": "localhost" + } + }, + { + "key": "server.port", + "value": { + "intValue": "8080" + } + }, + { + "key": "url.full", + "value": { + "stringValue": "http://localhost:00000/Guid_2/?UploadStringTaskAsync" + } + } + ] + }, + { + "traceId": "normalized-trace-id", + "spanId": "normalized-span-id", + "parentSpanId": "normalized-parent-span-id", + "name": "POST", + "kind": 3, + "startTimeUnixNano": "0", + "endTimeUnixNano": "0", + "attributes": [ + { + "key": "http.request.method", + "value": { + "stringValue": "POST" + } + }, + { + "key": "http.response.status_code", + "value": { + "intValue": "200" + } + }, + { + "key": "server.address", + "value": { + "stringValue": "localhost" + } + }, + { + "key": "server.port", + "value": { + "intValue": "8080" + } + }, + { + "key": "url.full", + "value": { + "stringValue": "http://localhost:00000/Guid_2/?UploadStringTaskAsync2" + } + } + ] + }, + { + "traceId": "normalized-trace-id", + "spanId": "normalized-span-id", + "parentSpanId": "normalized-parent-span-id", + "name": "POST", + "kind": 3, + "startTimeUnixNano": "0", + "endTimeUnixNano": "0", + "attributes": [ + { + "key": "http.request.method", + "value": { + "stringValue": "POST" + } + }, + { + "key": "http.response.status_code", + "value": { + "intValue": "200" + } + }, + { + "key": "server.address", + "value": { + "stringValue": "localhost" + } + }, + { + "key": "server.port", + "value": { + "intValue": "8080" + } + }, + { + "key": "url.full", + "value": { + "stringValue": "http://localhost:00000/Guid_2/?UploadStringTaskAsync3" + } + } + ] + }, + { + "traceId": "normalized-trace-id", + "spanId": "normalized-span-id", + "parentSpanId": "normalized-parent-span-id", + "name": "POST", + "kind": 3, + "startTimeUnixNano": "0", + "endTimeUnixNano": "0", + "attributes": [ + { + "key": "http.request.method", + "value": { + "stringValue": "POST" + } + }, + { + "key": "http.response.status_code", + "value": { + "intValue": "200" + } + }, + { + "key": "server.address", + "value": { + "stringValue": "localhost" + } + }, + { + "key": "server.port", + "value": { + "intValue": "8080" + } + }, + { + "key": "url.full", + "value": { + "stringValue": "http://localhost:00000/Guid_2/?UploadStringTaskAsync4" + } + } + ] + }, + { + "traceId": "normalized-trace-id", + "spanId": "normalized-span-id", + "parentSpanId": "normalized-parent-span-id", + "name": "POST", + "kind": 3, + "startTimeUnixNano": "0", + "endTimeUnixNano": "0", + "attributes": [ + { + "key": "http.request.method", + "value": { + "stringValue": "POST" + } + }, + { + "key": "http.response.status_code", + "value": { + "intValue": "200" + } + }, + { + "key": "server.address", + "value": { + "stringValue": "localhost" + } + }, + { + "key": "server.port", + "value": { + "intValue": "8080" + } + }, + { + "key": "url.full", + "value": { + "stringValue": "http://localhost:00000/Guid_2/?UploadValues" + } + } + ] + }, + { + "traceId": "normalized-trace-id", + "spanId": "normalized-span-id", + "parentSpanId": "normalized-parent-span-id", + "name": "POST", + "kind": 3, + "startTimeUnixNano": "0", + "endTimeUnixNano": "0", + "attributes": [ + { + "key": "http.request.method", + "value": { + "stringValue": "POST" + } + }, + { + "key": "http.response.status_code", + "value": { + "intValue": "200" + } + }, + { + "key": "server.address", + "value": { + "stringValue": "localhost" + } + }, + { + "key": "server.port", + "value": { + "intValue": "8080" + } + }, + { + "key": "url.full", + "value": { + "stringValue": "http://localhost:00000/Guid_2/?UploadValues2" + } + } + ] + }, + { + "traceId": "normalized-trace-id", + "spanId": "normalized-span-id", + "parentSpanId": "normalized-parent-span-id", + "name": "POST", + "kind": 3, + "startTimeUnixNano": "0", + "endTimeUnixNano": "0", + "attributes": [ + { + "key": "http.request.method", + "value": { + "stringValue": "POST" + } + }, + { + "key": "http.response.status_code", + "value": { + "intValue": "200" + } + }, + { + "key": "server.address", + "value": { + "stringValue": "localhost" + } + }, + { + "key": "server.port", + "value": { + "intValue": "8080" + } + }, + { + "key": "url.full", + "value": { + "stringValue": "http://localhost:00000/Guid_2/?UploadValues3" + } + } + ] + }, + { + "traceId": "normalized-trace-id", + "spanId": "normalized-span-id", + "parentSpanId": "normalized-parent-span-id", + "name": "POST", + "kind": 3, + "startTimeUnixNano": "0", + "endTimeUnixNano": "0", + "attributes": [ + { + "key": "http.request.method", + "value": { + "stringValue": "POST" + } + }, + { + "key": "http.response.status_code", + "value": { + "intValue": "200" + } + }, + { + "key": "server.address", + "value": { + "stringValue": "localhost" + } + }, + { + "key": "server.port", + "value": { + "intValue": "8080" + } + }, + { + "key": "url.full", + "value": { + "stringValue": "http://localhost:00000/Guid_2/?UploadValues4" + } + } + ] + }, + { + "traceId": "normalized-trace-id", + "spanId": "normalized-span-id", + "parentSpanId": "normalized-parent-span-id", + "name": "POST", + "kind": 3, + "startTimeUnixNano": "0", + "endTimeUnixNano": "0", + "attributes": [ + { + "key": "http.request.method", + "value": { + "stringValue": "POST" + } + }, + { + "key": "http.response.status_code", + "value": { + "intValue": "200" + } + }, + { + "key": "server.address", + "value": { + "stringValue": "localhost" + } + }, + { + "key": "server.port", + "value": { + "intValue": "8080" + } + }, + { + "key": "url.full", + "value": { + "stringValue": "http://localhost:00000/Guid_2/?UploadValuesAsync" + } + } + ] + }, + { + "traceId": "normalized-trace-id", + "spanId": "normalized-span-id", + "parentSpanId": "normalized-parent-span-id", + "name": "POST", + "kind": 3, + "startTimeUnixNano": "0", + "endTimeUnixNano": "0", + "attributes": [ + { + "key": "http.request.method", + "value": { + "stringValue": "POST" + } + }, + { + "key": "http.response.status_code", + "value": { + "intValue": "200" + } + }, + { + "key": "server.address", + "value": { + "stringValue": "localhost" + } + }, + { + "key": "server.port", + "value": { + "intValue": "8080" + } + }, + { + "key": "url.full", + "value": { + "stringValue": "http://localhost:00000/Guid_2/?UploadValuesAsync2" + } + } + ] + }, + { + "traceId": "normalized-trace-id", + "spanId": "normalized-span-id", + "parentSpanId": "normalized-parent-span-id", + "name": "POST", + "kind": 3, + "startTimeUnixNano": "0", + "endTimeUnixNano": "0", + "attributes": [ + { + "key": "http.request.method", + "value": { + "stringValue": "POST" + } + }, + { + "key": "http.response.status_code", + "value": { + "intValue": "200" + } + }, + { + "key": "server.address", + "value": { + "stringValue": "localhost" + } + }, + { + "key": "server.port", + "value": { + "intValue": "8080" + } + }, + { + "key": "url.full", + "value": { + "stringValue": "http://localhost:00000/Guid_2/?UploadValuesAsync3" + } + } + ] + }, + { + "traceId": "normalized-trace-id", + "spanId": "normalized-span-id", + "parentSpanId": "normalized-parent-span-id", + "name": "POST", + "kind": 3, + "startTimeUnixNano": "0", + "endTimeUnixNano": "0", + "attributes": [ + { + "key": "http.request.method", + "value": { + "stringValue": "POST" + } + }, + { + "key": "http.response.status_code", + "value": { + "intValue": "200" + } + }, + { + "key": "server.address", + "value": { + "stringValue": "localhost" + } + }, + { + "key": "server.port", + "value": { + "intValue": "8080" + } + }, + { + "key": "url.full", + "value": { + "stringValue": "http://localhost:00000/Guid_2/?UploadValuesTaskAsync" + } + } + ] + }, + { + "traceId": "normalized-trace-id", + "spanId": "normalized-span-id", + "parentSpanId": "normalized-parent-span-id", + "name": "POST", + "kind": 3, + "startTimeUnixNano": "0", + "endTimeUnixNano": "0", + "attributes": [ + { + "key": "http.request.method", + "value": { + "stringValue": "POST" + } + }, + { + "key": "http.response.status_code", + "value": { + "intValue": "200" + } + }, + { + "key": "server.address", + "value": { + "stringValue": "localhost" + } + }, + { + "key": "server.port", + "value": { + "intValue": "8080" + } + }, + { + "key": "url.full", + "value": { + "stringValue": "http://localhost:00000/Guid_2/?UploadValuesTaskAsync2" + } + } + ] + }, + { + "traceId": "normalized-trace-id", + "spanId": "normalized-span-id", + "parentSpanId": "normalized-parent-span-id", + "name": "POST", + "kind": 3, + "startTimeUnixNano": "0", + "endTimeUnixNano": "0", + "attributes": [ + { + "key": "http.request.method", + "value": { + "stringValue": "POST" + } + }, + { + "key": "http.response.status_code", + "value": { + "intValue": "200" + } + }, + { + "key": "server.address", + "value": { + "stringValue": "localhost" + } + }, + { + "key": "server.port", + "value": { + "intValue": "8080" + } + }, + { + "key": "url.full", + "value": { + "stringValue": "http://localhost:00000/Guid_2/?UploadValuesTaskAsync3" + } + } + ] + }, + { + "traceId": "normalized-trace-id", + "spanId": "normalized-span-id", + "parentSpanId": "normalized-parent-span-id", + "name": "POST", + "kind": 3, + "startTimeUnixNano": "0", + "endTimeUnixNano": "0", + "attributes": [ + { + "key": "http.request.method", + "value": { + "stringValue": "POST" + } + }, + { + "key": "http.response.status_code", + "value": { + "intValue": "200" + } + }, + { + "key": "server.address", + "value": { + "stringValue": "localhost" + } + }, + { + "key": "server.port", + "value": { + "intValue": "8080" + } + }, + { + "key": "url.full", + "value": { + "stringValue": "http://localhost:00000/Guid_2/?UploadValuesTaskAsync4" + } + } + ] + }, + { + "traceId": "normalized-trace-id", + "spanId": "normalized-span-id", + "parentSpanId": "normalized-parent-span-id", + "name": "UploadData", + "kind": 1, + "startTimeUnixNano": "0", + "endTimeUnixNano": "0" + }, + { + "traceId": "normalized-trace-id", + "spanId": "normalized-span-id", + "parentSpanId": "normalized-parent-span-id", + "name": "UploadDataAsync", + "kind": 1, + "startTimeUnixNano": "0", + "endTimeUnixNano": "0" + }, + { + "traceId": "normalized-trace-id", + "spanId": "normalized-span-id", + "parentSpanId": "normalized-parent-span-id", + "name": "UploadDataTaskAsync", + "kind": 1, + "startTimeUnixNano": "0", + "endTimeUnixNano": "0" + }, + { + "traceId": "normalized-trace-id", + "spanId": "normalized-span-id", + "parentSpanId": "normalized-parent-span-id", + "name": "UploadFile", + "kind": 1, + "startTimeUnixNano": "0", + "endTimeUnixNano": "0" + }, + { + "traceId": "normalized-trace-id", + "spanId": "normalized-span-id", + "parentSpanId": "normalized-parent-span-id", + "name": "UploadFileAsync", + "kind": 1, + "startTimeUnixNano": "0", + "endTimeUnixNano": "0" + }, + { + "traceId": "normalized-trace-id", + "spanId": "normalized-span-id", + "parentSpanId": "normalized-parent-span-id", + "name": "UploadFileTaskAsync", + "kind": 1, + "startTimeUnixNano": "0", + "endTimeUnixNano": "0" + }, + { + "traceId": "normalized-trace-id", + "spanId": "normalized-span-id", + "parentSpanId": "normalized-parent-span-id", + "name": "UploadString", + "kind": 1, + "startTimeUnixNano": "0", + "endTimeUnixNano": "0" + }, + { + "traceId": "normalized-trace-id", + "spanId": "normalized-span-id", + "parentSpanId": "normalized-parent-span-id", + "name": "UploadStringAsync", + "kind": 1, + "startTimeUnixNano": "0", + "endTimeUnixNano": "0" + }, + { + "traceId": "normalized-trace-id", + "spanId": "normalized-span-id", + "parentSpanId": "normalized-parent-span-id", + "name": "UploadStringTaskAsync", + "kind": 1, + "startTimeUnixNano": "0", + "endTimeUnixNano": "0" + }, + { + "traceId": "normalized-trace-id", + "spanId": "normalized-span-id", + "parentSpanId": "normalized-parent-span-id", + "name": "UploadValues", + "kind": 1, + "startTimeUnixNano": "0", + "endTimeUnixNano": "0" + }, + { + "traceId": "normalized-trace-id", + "spanId": "normalized-span-id", + "parentSpanId": "normalized-parent-span-id", + "name": "UploadValuesAsync", + "kind": 1, + "startTimeUnixNano": "0", + "endTimeUnixNano": "0" + }, + { + "traceId": "normalized-trace-id", + "spanId": "normalized-span-id", + "parentSpanId": "normalized-parent-span-id", + "name": "UploadValuesTaskAsync", + "kind": 1, + "startTimeUnixNano": "0", + "endTimeUnixNano": "0" + }, + { + "traceId": "normalized-trace-id", + "spanId": "normalized-span-id", + "name": "WebClient", + "kind": 1, + "startTimeUnixNano": "0", + "endTimeUnixNano": "0" + }, + { + "traceId": "normalized-trace-id", + "spanId": "normalized-span-id", + "name": "WebRequest", + "kind": 1, + "startTimeUnixNano": "0", + "endTimeUnixNano": "0" + } + ] + } + ] + } + ] +} \ No newline at end of file From 97a5898ccaa0cae74c13b2d47d97c40aa7c7f413 Mon Sep 17 00:00:00 2001 From: Zach Montoya Date: Fri, 31 Jul 2026 14:21:25 -0700 Subject: [PATCH 8/9] Fix the new properties to use the OtelName casing --- .../TagListGenerator/HttpTags.g.cs | 18 +++++++++--------- .../TagListGenerator/HttpTags.g.cs | 18 +++++++++--------- .../TagListGenerator/HttpTags.g.cs | 18 +++++++++--------- .../TagListGenerator/HttpTags.g.cs | 18 +++++++++--------- tracer/src/Datadog.Trace/Tagging/HttpTags.cs | 6 +++--- 5 files changed, 39 insertions(+), 39 deletions(-) diff --git a/tracer/src/Datadog.Trace/Generated/net461/Datadog.Trace.SourceGenerators/TagListGenerator/HttpTags.g.cs b/tracer/src/Datadog.Trace/Generated/net461/Datadog.Trace.SourceGenerators/TagListGenerator/HttpTags.g.cs index e41164e84181..70953f44cb59 100644 --- a/tracer/src/Datadog.Trace/Generated/net461/Datadog.Trace.SourceGenerators/TagListGenerator/HttpTags.g.cs +++ b/tracer/src/Datadog.Trace/Generated/net461/Datadog.Trace.SourceGenerators/TagListGenerator/HttpTags.g.cs @@ -23,8 +23,8 @@ partial class HttpTags // HttpMethodBytes = MessagePack.Serialize("http.method"); private static ReadOnlySpan HttpMethodBytes => [171, 104, 116, 116, 112, 46, 109, 101, 116, 104, 111, 100]; - // HttpMethodOTelBytes = MessagePack.Serialize("http.request.method"); - private static ReadOnlySpan HttpMethodOTelBytes => [179, 104, 116, 116, 112, 46, 114, 101, 113, 117, 101, 115, 116, 46, 109, 101, 116, 104, 111, 100]; + // HttpMethodOtelBytes = MessagePack.Serialize("http.request.method"); + private static ReadOnlySpan HttpMethodOtelBytes => [179, 104, 116, 116, 112, 46, 114, 101, 113, 117, 101, 115, 116, 46, 109, 101, 116, 104, 111, 100]; // HttpRequestMethodOriginalBytes = MessagePack.Serialize("http.request.method_original"); private static ReadOnlySpan HttpRequestMethodOriginalBytes => [188, 104, 116, 116, 112, 46, 114, 101, 113, 117, 101, 115, 116, 46, 109, 101, 116, 104, 111, 100, 95, 111, 114, 105, 103, 105, 110, 97, 108]; @@ -32,8 +32,8 @@ partial class HttpTags // HttpUrlBytes = MessagePack.Serialize("http.url"); private static ReadOnlySpan HttpUrlBytes => [168, 104, 116, 116, 112, 46, 117, 114, 108]; - // HttpUrlOTelBytes = MessagePack.Serialize("url.full"); - private static ReadOnlySpan HttpUrlOTelBytes => [168, 117, 114, 108, 46, 102, 117, 108, 108]; + // HttpUrlOtelBytes = MessagePack.Serialize("url.full"); + private static ReadOnlySpan HttpUrlOtelBytes => [168, 117, 114, 108, 46, 102, 117, 108, 108]; // HttpClientHandlerTypeBytes = MessagePack.Serialize("http-client-handler-type"); private static ReadOnlySpan HttpClientHandlerTypeBytes => [184, 104, 116, 116, 112, 45, 99, 108, 105, 101, 110, 116, 45, 104, 97, 110, 100, 108, 101, 114, 45, 116, 121, 112, 101]; @@ -47,8 +47,8 @@ partial class HttpTags // HostBytes = MessagePack.Serialize("out.host"); private static ReadOnlySpan HostBytes => [168, 111, 117, 116, 46, 104, 111, 115, 116]; - // HostOTelBytes = MessagePack.Serialize("server.address"); - private static ReadOnlySpan HostOTelBytes => [174, 115, 101, 114, 118, 101, 114, 46, 97, 100, 100, 114, 101, 115, 115]; + // HostOtelBytes = MessagePack.Serialize("server.address"); + private static ReadOnlySpan HostOtelBytes => [174, 115, 101, 114, 118, 101, 114, 46, 97, 100, 100, 114, 101, 115, 115]; // ServerPortBytes = MessagePack.Serialize("server.port"); private static ReadOnlySpan ServerPortBytes => [171, 115, 101, 114, 118, 101, 114, 46, 112, 111, 114, 116]; @@ -147,7 +147,7 @@ public override void EnumerateTags(ref TProcessor processor, bool op { if (openTelemetrySemanticsEnabled) { - processor.Process(new TagItem("http.request.method", HttpMethod, HttpMethodOTelBytes)); + processor.Process(new TagItem("http.request.method", HttpMethod, HttpMethodOtelBytes)); } else { @@ -164,7 +164,7 @@ public override void EnumerateTags(ref TProcessor processor, bool op { if (openTelemetrySemanticsEnabled) { - processor.Process(new TagItem("url.full", HttpUrl, HttpUrlOTelBytes)); + processor.Process(new TagItem("url.full", HttpUrl, HttpUrlOtelBytes)); } else { @@ -193,7 +193,7 @@ public override void EnumerateTags(ref TProcessor processor, bool op { if (openTelemetrySemanticsEnabled) { - processor.Process(new TagItem("server.address", Host, HostOTelBytes)); + processor.Process(new TagItem("server.address", Host, HostOtelBytes)); } else { diff --git a/tracer/src/Datadog.Trace/Generated/net6.0/Datadog.Trace.SourceGenerators/TagListGenerator/HttpTags.g.cs b/tracer/src/Datadog.Trace/Generated/net6.0/Datadog.Trace.SourceGenerators/TagListGenerator/HttpTags.g.cs index e41164e84181..70953f44cb59 100644 --- a/tracer/src/Datadog.Trace/Generated/net6.0/Datadog.Trace.SourceGenerators/TagListGenerator/HttpTags.g.cs +++ b/tracer/src/Datadog.Trace/Generated/net6.0/Datadog.Trace.SourceGenerators/TagListGenerator/HttpTags.g.cs @@ -23,8 +23,8 @@ partial class HttpTags // HttpMethodBytes = MessagePack.Serialize("http.method"); private static ReadOnlySpan HttpMethodBytes => [171, 104, 116, 116, 112, 46, 109, 101, 116, 104, 111, 100]; - // HttpMethodOTelBytes = MessagePack.Serialize("http.request.method"); - private static ReadOnlySpan HttpMethodOTelBytes => [179, 104, 116, 116, 112, 46, 114, 101, 113, 117, 101, 115, 116, 46, 109, 101, 116, 104, 111, 100]; + // HttpMethodOtelBytes = MessagePack.Serialize("http.request.method"); + private static ReadOnlySpan HttpMethodOtelBytes => [179, 104, 116, 116, 112, 46, 114, 101, 113, 117, 101, 115, 116, 46, 109, 101, 116, 104, 111, 100]; // HttpRequestMethodOriginalBytes = MessagePack.Serialize("http.request.method_original"); private static ReadOnlySpan HttpRequestMethodOriginalBytes => [188, 104, 116, 116, 112, 46, 114, 101, 113, 117, 101, 115, 116, 46, 109, 101, 116, 104, 111, 100, 95, 111, 114, 105, 103, 105, 110, 97, 108]; @@ -32,8 +32,8 @@ partial class HttpTags // HttpUrlBytes = MessagePack.Serialize("http.url"); private static ReadOnlySpan HttpUrlBytes => [168, 104, 116, 116, 112, 46, 117, 114, 108]; - // HttpUrlOTelBytes = MessagePack.Serialize("url.full"); - private static ReadOnlySpan HttpUrlOTelBytes => [168, 117, 114, 108, 46, 102, 117, 108, 108]; + // HttpUrlOtelBytes = MessagePack.Serialize("url.full"); + private static ReadOnlySpan HttpUrlOtelBytes => [168, 117, 114, 108, 46, 102, 117, 108, 108]; // HttpClientHandlerTypeBytes = MessagePack.Serialize("http-client-handler-type"); private static ReadOnlySpan HttpClientHandlerTypeBytes => [184, 104, 116, 116, 112, 45, 99, 108, 105, 101, 110, 116, 45, 104, 97, 110, 100, 108, 101, 114, 45, 116, 121, 112, 101]; @@ -47,8 +47,8 @@ partial class HttpTags // HostBytes = MessagePack.Serialize("out.host"); private static ReadOnlySpan HostBytes => [168, 111, 117, 116, 46, 104, 111, 115, 116]; - // HostOTelBytes = MessagePack.Serialize("server.address"); - private static ReadOnlySpan HostOTelBytes => [174, 115, 101, 114, 118, 101, 114, 46, 97, 100, 100, 114, 101, 115, 115]; + // HostOtelBytes = MessagePack.Serialize("server.address"); + private static ReadOnlySpan HostOtelBytes => [174, 115, 101, 114, 118, 101, 114, 46, 97, 100, 100, 114, 101, 115, 115]; // ServerPortBytes = MessagePack.Serialize("server.port"); private static ReadOnlySpan ServerPortBytes => [171, 115, 101, 114, 118, 101, 114, 46, 112, 111, 114, 116]; @@ -147,7 +147,7 @@ public override void EnumerateTags(ref TProcessor processor, bool op { if (openTelemetrySemanticsEnabled) { - processor.Process(new TagItem("http.request.method", HttpMethod, HttpMethodOTelBytes)); + processor.Process(new TagItem("http.request.method", HttpMethod, HttpMethodOtelBytes)); } else { @@ -164,7 +164,7 @@ public override void EnumerateTags(ref TProcessor processor, bool op { if (openTelemetrySemanticsEnabled) { - processor.Process(new TagItem("url.full", HttpUrl, HttpUrlOTelBytes)); + processor.Process(new TagItem("url.full", HttpUrl, HttpUrlOtelBytes)); } else { @@ -193,7 +193,7 @@ public override void EnumerateTags(ref TProcessor processor, bool op { if (openTelemetrySemanticsEnabled) { - processor.Process(new TagItem("server.address", Host, HostOTelBytes)); + processor.Process(new TagItem("server.address", Host, HostOtelBytes)); } else { diff --git a/tracer/src/Datadog.Trace/Generated/netcoreapp3.1/Datadog.Trace.SourceGenerators/TagListGenerator/HttpTags.g.cs b/tracer/src/Datadog.Trace/Generated/netcoreapp3.1/Datadog.Trace.SourceGenerators/TagListGenerator/HttpTags.g.cs index e41164e84181..70953f44cb59 100644 --- a/tracer/src/Datadog.Trace/Generated/netcoreapp3.1/Datadog.Trace.SourceGenerators/TagListGenerator/HttpTags.g.cs +++ b/tracer/src/Datadog.Trace/Generated/netcoreapp3.1/Datadog.Trace.SourceGenerators/TagListGenerator/HttpTags.g.cs @@ -23,8 +23,8 @@ partial class HttpTags // HttpMethodBytes = MessagePack.Serialize("http.method"); private static ReadOnlySpan HttpMethodBytes => [171, 104, 116, 116, 112, 46, 109, 101, 116, 104, 111, 100]; - // HttpMethodOTelBytes = MessagePack.Serialize("http.request.method"); - private static ReadOnlySpan HttpMethodOTelBytes => [179, 104, 116, 116, 112, 46, 114, 101, 113, 117, 101, 115, 116, 46, 109, 101, 116, 104, 111, 100]; + // HttpMethodOtelBytes = MessagePack.Serialize("http.request.method"); + private static ReadOnlySpan HttpMethodOtelBytes => [179, 104, 116, 116, 112, 46, 114, 101, 113, 117, 101, 115, 116, 46, 109, 101, 116, 104, 111, 100]; // HttpRequestMethodOriginalBytes = MessagePack.Serialize("http.request.method_original"); private static ReadOnlySpan HttpRequestMethodOriginalBytes => [188, 104, 116, 116, 112, 46, 114, 101, 113, 117, 101, 115, 116, 46, 109, 101, 116, 104, 111, 100, 95, 111, 114, 105, 103, 105, 110, 97, 108]; @@ -32,8 +32,8 @@ partial class HttpTags // HttpUrlBytes = MessagePack.Serialize("http.url"); private static ReadOnlySpan HttpUrlBytes => [168, 104, 116, 116, 112, 46, 117, 114, 108]; - // HttpUrlOTelBytes = MessagePack.Serialize("url.full"); - private static ReadOnlySpan HttpUrlOTelBytes => [168, 117, 114, 108, 46, 102, 117, 108, 108]; + // HttpUrlOtelBytes = MessagePack.Serialize("url.full"); + private static ReadOnlySpan HttpUrlOtelBytes => [168, 117, 114, 108, 46, 102, 117, 108, 108]; // HttpClientHandlerTypeBytes = MessagePack.Serialize("http-client-handler-type"); private static ReadOnlySpan HttpClientHandlerTypeBytes => [184, 104, 116, 116, 112, 45, 99, 108, 105, 101, 110, 116, 45, 104, 97, 110, 100, 108, 101, 114, 45, 116, 121, 112, 101]; @@ -47,8 +47,8 @@ partial class HttpTags // HostBytes = MessagePack.Serialize("out.host"); private static ReadOnlySpan HostBytes => [168, 111, 117, 116, 46, 104, 111, 115, 116]; - // HostOTelBytes = MessagePack.Serialize("server.address"); - private static ReadOnlySpan HostOTelBytes => [174, 115, 101, 114, 118, 101, 114, 46, 97, 100, 100, 114, 101, 115, 115]; + // HostOtelBytes = MessagePack.Serialize("server.address"); + private static ReadOnlySpan HostOtelBytes => [174, 115, 101, 114, 118, 101, 114, 46, 97, 100, 100, 114, 101, 115, 115]; // ServerPortBytes = MessagePack.Serialize("server.port"); private static ReadOnlySpan ServerPortBytes => [171, 115, 101, 114, 118, 101, 114, 46, 112, 111, 114, 116]; @@ -147,7 +147,7 @@ public override void EnumerateTags(ref TProcessor processor, bool op { if (openTelemetrySemanticsEnabled) { - processor.Process(new TagItem("http.request.method", HttpMethod, HttpMethodOTelBytes)); + processor.Process(new TagItem("http.request.method", HttpMethod, HttpMethodOtelBytes)); } else { @@ -164,7 +164,7 @@ public override void EnumerateTags(ref TProcessor processor, bool op { if (openTelemetrySemanticsEnabled) { - processor.Process(new TagItem("url.full", HttpUrl, HttpUrlOTelBytes)); + processor.Process(new TagItem("url.full", HttpUrl, HttpUrlOtelBytes)); } else { @@ -193,7 +193,7 @@ public override void EnumerateTags(ref TProcessor processor, bool op { if (openTelemetrySemanticsEnabled) { - processor.Process(new TagItem("server.address", Host, HostOTelBytes)); + processor.Process(new TagItem("server.address", Host, HostOtelBytes)); } else { diff --git a/tracer/src/Datadog.Trace/Generated/netstandard2.0/Datadog.Trace.SourceGenerators/TagListGenerator/HttpTags.g.cs b/tracer/src/Datadog.Trace/Generated/netstandard2.0/Datadog.Trace.SourceGenerators/TagListGenerator/HttpTags.g.cs index e41164e84181..70953f44cb59 100644 --- a/tracer/src/Datadog.Trace/Generated/netstandard2.0/Datadog.Trace.SourceGenerators/TagListGenerator/HttpTags.g.cs +++ b/tracer/src/Datadog.Trace/Generated/netstandard2.0/Datadog.Trace.SourceGenerators/TagListGenerator/HttpTags.g.cs @@ -23,8 +23,8 @@ partial class HttpTags // HttpMethodBytes = MessagePack.Serialize("http.method"); private static ReadOnlySpan HttpMethodBytes => [171, 104, 116, 116, 112, 46, 109, 101, 116, 104, 111, 100]; - // HttpMethodOTelBytes = MessagePack.Serialize("http.request.method"); - private static ReadOnlySpan HttpMethodOTelBytes => [179, 104, 116, 116, 112, 46, 114, 101, 113, 117, 101, 115, 116, 46, 109, 101, 116, 104, 111, 100]; + // HttpMethodOtelBytes = MessagePack.Serialize("http.request.method"); + private static ReadOnlySpan HttpMethodOtelBytes => [179, 104, 116, 116, 112, 46, 114, 101, 113, 117, 101, 115, 116, 46, 109, 101, 116, 104, 111, 100]; // HttpRequestMethodOriginalBytes = MessagePack.Serialize("http.request.method_original"); private static ReadOnlySpan HttpRequestMethodOriginalBytes => [188, 104, 116, 116, 112, 46, 114, 101, 113, 117, 101, 115, 116, 46, 109, 101, 116, 104, 111, 100, 95, 111, 114, 105, 103, 105, 110, 97, 108]; @@ -32,8 +32,8 @@ partial class HttpTags // HttpUrlBytes = MessagePack.Serialize("http.url"); private static ReadOnlySpan HttpUrlBytes => [168, 104, 116, 116, 112, 46, 117, 114, 108]; - // HttpUrlOTelBytes = MessagePack.Serialize("url.full"); - private static ReadOnlySpan HttpUrlOTelBytes => [168, 117, 114, 108, 46, 102, 117, 108, 108]; + // HttpUrlOtelBytes = MessagePack.Serialize("url.full"); + private static ReadOnlySpan HttpUrlOtelBytes => [168, 117, 114, 108, 46, 102, 117, 108, 108]; // HttpClientHandlerTypeBytes = MessagePack.Serialize("http-client-handler-type"); private static ReadOnlySpan HttpClientHandlerTypeBytes => [184, 104, 116, 116, 112, 45, 99, 108, 105, 101, 110, 116, 45, 104, 97, 110, 100, 108, 101, 114, 45, 116, 121, 112, 101]; @@ -47,8 +47,8 @@ partial class HttpTags // HostBytes = MessagePack.Serialize("out.host"); private static ReadOnlySpan HostBytes => [168, 111, 117, 116, 46, 104, 111, 115, 116]; - // HostOTelBytes = MessagePack.Serialize("server.address"); - private static ReadOnlySpan HostOTelBytes => [174, 115, 101, 114, 118, 101, 114, 46, 97, 100, 100, 114, 101, 115, 115]; + // HostOtelBytes = MessagePack.Serialize("server.address"); + private static ReadOnlySpan HostOtelBytes => [174, 115, 101, 114, 118, 101, 114, 46, 97, 100, 100, 114, 101, 115, 115]; // ServerPortBytes = MessagePack.Serialize("server.port"); private static ReadOnlySpan ServerPortBytes => [171, 115, 101, 114, 118, 101, 114, 46, 112, 111, 114, 116]; @@ -147,7 +147,7 @@ public override void EnumerateTags(ref TProcessor processor, bool op { if (openTelemetrySemanticsEnabled) { - processor.Process(new TagItem("http.request.method", HttpMethod, HttpMethodOTelBytes)); + processor.Process(new TagItem("http.request.method", HttpMethod, HttpMethodOtelBytes)); } else { @@ -164,7 +164,7 @@ public override void EnumerateTags(ref TProcessor processor, bool op { if (openTelemetrySemanticsEnabled) { - processor.Process(new TagItem("url.full", HttpUrl, HttpUrlOTelBytes)); + processor.Process(new TagItem("url.full", HttpUrl, HttpUrlOtelBytes)); } else { @@ -193,7 +193,7 @@ public override void EnumerateTags(ref TProcessor processor, bool op { if (openTelemetrySemanticsEnabled) { - processor.Process(new TagItem("server.address", Host, HostOTelBytes)); + processor.Process(new TagItem("server.address", Host, HostOtelBytes)); } else { diff --git a/tracer/src/Datadog.Trace/Tagging/HttpTags.cs b/tracer/src/Datadog.Trace/Tagging/HttpTags.cs index 644c03d4a8ea..eabd67865a2d 100644 --- a/tracer/src/Datadog.Trace/Tagging/HttpTags.cs +++ b/tracer/src/Datadog.Trace/Tagging/HttpTags.cs @@ -18,7 +18,7 @@ internal partial class HttpTags : InstrumentationTags, IHasStatusCode [Tag(Trace.Tags.InstrumentationName)] public string InstrumentationName { get; set; } - [Tag(Trace.Tags.HttpMethod, OTelName = Trace.Tags.HttpRequestMethod)] + [Tag(Trace.Tags.HttpMethod, OtelName = Trace.Tags.HttpRequestMethod)] public string HttpMethod { get; set; } /// @@ -33,7 +33,7 @@ internal partial class HttpTags : InstrumentationTags, IHasStatusCode /// Gets or sets the request URL. Serialized as "http.url" with Datadog semantics /// and as "url.full" with OpenTelemetry semantics. /// - [Tag(Trace.Tags.HttpUrl, OTelName = Trace.Tags.UrlFull)] + [Tag(Trace.Tags.HttpUrl, OtelName = Trace.Tags.UrlFull)] public string HttpUrl { get; set; } [Tag(HttpClientHandlerTypeKey)] @@ -42,7 +42,7 @@ internal partial class HttpTags : InstrumentationTags, IHasStatusCode [Tag(Trace.Tags.HttpStatusCode, OtelName = Trace.Tags.HttpResponseStatusCode)] public int? HttpStatusCode { get; set; } - [Tag(Trace.Tags.OutHost, OTelName = Trace.Tags.ServerAddress)] + [Tag(Trace.Tags.OutHost, OtelName = Trace.Tags.ServerAddress)] public string Host { get; set; } /// From b3da552364d001dde8887ede0d5d3aa1475ce6d5 Mon Sep 17 00:00:00 2001 From: Zach Montoya Date: Fri, 31 Jul 2026 15:45:58 -0700 Subject: [PATCH 9/9] Delete superpowers docs --- .../2026-07-30-webrequest-otlp-snapshots.md | 966 ------------------ ...-07-30-webrequest-otlp-snapshots-design.md | 252 ----- 2 files changed, 1218 deletions(-) delete mode 100644 docs/superpowers/plans/2026-07-30-webrequest-otlp-snapshots.md delete mode 100644 docs/superpowers/specs/2026-07-30-webrequest-otlp-snapshots-design.md diff --git a/docs/superpowers/plans/2026-07-30-webrequest-otlp-snapshots.md b/docs/superpowers/plans/2026-07-30-webrequest-otlp-snapshots.md deleted file mode 100644 index 6b36f5eba638..000000000000 --- a/docs/superpowers/plans/2026-07-30-webrequest-otlp-snapshots.md +++ /dev/null @@ -1,966 +0,0 @@ -# WebRequest OTLP Snapshot Tests Implementation Plan - -> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking. - -**Goal:** Add OTLP snapshot coverage for `Samples.WebRequest` so the HTTP semantic-convention attributes added on this branch are verified as they appear on the wire in OTLP, not just in Datadog msgpack. - -**Architecture:** Extract the OTLP payload-normalization logic currently private to `OpenTelemetrySdkTests` into a shared `OtlpSnapshotHelper`, then add a four-case `SubmitsOtlpTraces` theory to `WebRequestTests` that exports through the `test-agent` container and snapshots the normalized JSON. `OpenTelemetrySdkTests` must keep producing byte-identical snapshots throughout. - -**Tech Stack:** xUnit (`SkippableTheory`), Verify/VerifyXunit snapshots, FluentAssertions, `Datadog.Trace.Vendors.Newtonsoft.Json.Linq`, `ddapm-test-agent` docker container. - -**Spec:** `docs/superpowers/specs/2026-07-30-webrequest-otlp-snapshots-design.md` - -## Global Constraints - -- **Never regenerate or modify an existing snapshot.** `WebRequestTests_v0`, `WebRequestTests_v1`, `WebRequestTests_otel`, `WebRequestTests_netfx_*`, and every `OpenTelemetrySdkTests.*` snapshot must remain byte-identical. If one changes, the change is a bug — revert and rethink. -- Copyright header on every new file, matching the repo's exact format (see any existing file under `tracer/test/`). -- Follow `.editorconfig` and `tracer/stylecop.json`. Use `is not null` over `!= null`. Add `using` directives rather than fully-qualified type names. -- Use `Datadog.Trace.Vendors.Newtonsoft.Json` / `.Linq` — **not** `Newtonsoft.Json`. This is what `OpenTelemetrySdkTests` uses and the only JSON library referenced by the test project. -- The new helper files use `#nullable enable`, but the code being moved into them came from a file that does not. Expect nullability warnings on the copied bodies (`CS8602` on `span[key].ToString()`, `CS8600` on `JToken previousResourceAttributes = null`). Resolve them with `?` on locals and the `!` null-forgiving operator — both are compile-time only and cannot change behavior. Do **not** restructure the copied logic to satisfy the compiler. -- `OtlpFieldNames` is passed **by value**, never as an `in`/`ref` parameter. Lambdas cannot capture `in` parameters, and several call sites close over it. -- The test-agent OTLP HTTP endpoint is always port **4318** for both `http/json` and `http/protobuf`. Host comes from `TEST_AGENT_HOST`, defaulting to `127.0.0.1`. -- gRPC is out of scope: `ExporterSettings` only maps `HttpProtobuf`/`HttpJson` to an OTLP traces encoding and silently falls back to Datadog v0.4 otherwise. - -## Prerequisites - -Start the test-agent before running anything in Task 1, 2, or 4: - -```bash -docker compose up -d test-agent -curl -sf http://127.0.0.1:4318/test/session/clear && echo OK -``` - -## File Structure - -| File | Responsibility | -| --- | --- | -| `tracer/test/Datadog.Trace.ClrProfiler.IntegrationTests/Helpers/OtlpFieldNames.cs` (create) | Maps a protocol to the OTLP field-name casing the test-agent renders (`resourceSpans` vs `resource_spans`, etc.) | -| `tracer/test/Datadog.Trace.ClrProfiler.IntegrationTests/Helpers/OtlpSnapshotHelper.cs` (create) | Test-agent session I/O, protobuf→json scrubbers, OTLP payload normalization, request merging, attribute lookup | -| `tracer/test/Datadog.Trace.ClrProfiler.IntegrationTests/Helpers/TestAgentOtlpCollection.cs` (create) | xUnit collection that serializes every class sharing the test-agent OTLP session | -| `tracer/test/Datadog.Trace.ClrProfiler.IntegrationTests/OpenTelemetrySdkTests.cs` (modify) | Loses its private OTLP plumbing; delegates to the helper. Behavior unchanged. | -| `tracer/test/Datadog.Trace.ClrProfiler.IntegrationTests/WebRequestTests.cs` (modify) | Gains `SubmitsOtlpTraces` plus WebRequest-specific normalization | -| `tracer/test/snapshots/WebRequestTests.SubmitsOtlpTraces_DD.verified.txt` (create) | Snapshot, semantics off | -| `tracer/test/snapshots/WebRequestTests.SubmitsOtlpTraces_DD_OtelSemantics.verified.txt` (create) | Snapshot, semantics on | - ---- - -### Task 1: Extract test-agent I/O and protocol scrubbers - -Pure code motion. Everything moved here is currently private to `OpenTelemetrySdkTests` and used verbatim by its traces, metrics, and logs tests. - -**Files:** -- Create: `tracer/test/Datadog.Trace.ClrProfiler.IntegrationTests/Helpers/OtlpFieldNames.cs` -- Create: `tracer/test/Datadog.Trace.ClrProfiler.IntegrationTests/Helpers/OtlpSnapshotHelper.cs` -- Modify: `tracer/test/Datadog.Trace.ClrProfiler.IntegrationTests/OpenTelemetrySdkTests.cs` - -**Interfaces:** -- Consumes: nothing -- Produces: - - `OtlpFieldNames.For(bool isJson) -> OtlpFieldNames` with `string` properties `ResourceSpans`, `ScopeSpans`, `StringValue`, `IntValue`, `TraceId`, `SpanId`, `ParentSpanId`, `StartTimeUnixNano`, `EndTimeUnixNano`, `TimeUnixNano`, and `bool IsJson` - - `OtlpSnapshotHelper.ClearTestAgentSessionAsync(string testAgentHost, int maxRetries = 5, int delayMs = 1000) -> Task` - - `OtlpSnapshotHelper.WaitForTestAgentDataAsync(string url, int timeoutSeconds = 60, int pollIntervalMs = 500) -> Task` - - `OtlpSnapshotHelper.AddProtobufToJsonScrubbers(VerifySettings settings) -> void` - -- [ ] **Step 1: Create `OtlpFieldNames.cs`** - -```csharp -// -// Unless explicitly stated otherwise all files in this repository are licensed under the Apache 2 License. -// This product includes software developed at Datadog (https://www.datadoghq.com/). Copyright 2017 Datadog, Inc. -// - -#nullable enable - -namespace Datadog.Trace.ClrProfiler.IntegrationTests.Helpers -{ - /// - /// The test agent renders an OTLP http/json payload with camelCase field names and an - /// http/protobuf payload with snake_case field names. This maps a protocol to the casing - /// used when walking the rendered JSON. - /// - internal readonly struct OtlpFieldNames - { - private OtlpFieldNames(bool isJson) - { - IsJson = isJson; - } - - public bool IsJson { get; } - - public string ResourceSpans => IsJson ? "resourceSpans" : "resource_spans"; - - public string ScopeSpans => IsJson ? "scopeSpans" : "scope_spans"; - - public string StringValue => IsJson ? "stringValue" : "string_value"; - - public string IntValue => IsJson ? "intValue" : "int_value"; - - public string TraceId => IsJson ? "traceId" : "trace_id"; - - public string SpanId => IsJson ? "spanId" : "span_id"; - - public string ParentSpanId => IsJson ? "parentSpanId" : "parent_span_id"; - - public string StartTimeUnixNano => IsJson ? "startTimeUnixNano" : "start_time_unix_nano"; - - public string EndTimeUnixNano => IsJson ? "endTimeUnixNano" : "end_time_unix_nano"; - - public string TimeUnixNano => IsJson ? "timeUnixNano" : "time_unix_nano"; - - public static OtlpFieldNames For(bool isJson) => new(isJson); - } -} -``` - -- [ ] **Step 2: Create `OtlpSnapshotHelper.cs` with the moved I/O and scrubbers** - -The two mapping tables and all three method bodies are copied verbatim from `OpenTelemetrySdkTests` — do not re-derive them. - -```csharp -// -// Unless explicitly stated otherwise all files in this repository are licensed under the Apache 2 License. -// This product includes software developed at Datadog (https://www.datadoghq.com/). Copyright 2017 Datadog, Inc. -// - -#nullable enable - -using System; -using System.Net.Http; -using System.Threading.Tasks; -using Datadog.Trace.Vendors.Newtonsoft.Json.Linq; -using VerifyTests; - -namespace Datadog.Trace.ClrProfiler.IntegrationTests.Helpers -{ - /// - /// Shared plumbing for tests that snapshot OTLP payloads captured by the ddapm test agent. - /// - internal static class OtlpSnapshotHelper - { - // Single source of truth for translating an OTLP http/protobuf payload (rendered as JSON - // by the test agent with snake_case field names and string-form enum values) to the - // OTLP http/json shape (camelCase field names, integer enum values). When a new OTLP - // field or enum reaches the serializer, add the mapping here. - private static readonly (string From, string To)[] ProtobufToJsonFieldNameMappings = - { - ("\"resource_spans\"", "\"resourceSpans\""), - ("\"scope_spans\"", "\"scopeSpans\""), - ("\"trace_id\"", "\"traceId\""), - ("\"span_id\"", "\"spanId\""), - ("\"parent_span_id\"", "\"parentSpanId\""), - ("\"start_time_unix_nano\"", "\"startTimeUnixNano\""), - ("\"end_time_unix_nano\"", "\"endTimeUnixNano\""), - ("\"time_unix_nano\"", "\"timeUnixNano\""), - ("\"string_value\"", "\"stringValue\""), - ("\"double_value\"", "\"doubleValue\""), - ("\"int_value\"", "\"intValue\""), - ("\"bool_value\"", "\"boolValue\""), - ("\"array_value\"", "\"arrayValue\""), - }; - - private static readonly (string From, string To)[] ProtobufToJsonEnumMappings = - { - ("\"kind\": \"SPAN_KIND_INTERNAL\"", "\"kind\": 1"), - ("\"kind\": \"SPAN_KIND_SERVER\"", "\"kind\": 2"), - ("\"kind\": \"SPAN_KIND_CLIENT\"", "\"kind\": 3"), - ("\"kind\": \"SPAN_KIND_PRODUCER\"", "\"kind\": 4"), - ("\"kind\": \"SPAN_KIND_CONSUMER\"", "\"kind\": 5"), - ("\"code\": \"STATUS_CODE_UNSET\"", "\"code\": 0"), - ("\"code\": \"STATUS_CODE_OK\"", "\"code\": 1"), - ("\"code\": \"STATUS_CODE_ERROR\"", "\"code\": 2"), - }; - - public static void AddProtobufToJsonScrubbers(VerifySettings settings) - { - foreach (var (from, to) in ProtobufToJsonFieldNameMappings) - { - settings.AddSimpleScrubber(from, to); - } - - foreach (var (from, to) in ProtobufToJsonEnumMappings) - { - settings.AddSimpleScrubber(from, to); - } - } - - /// - /// Clears the test-agent session, retrying if the agent is not yet ready. - /// Ensures the OTLP HTTP endpoint is accepting connections before tests proceed. - /// - public static async Task ClearTestAgentSessionAsync(string testAgentHost, int maxRetries = 5, int delayMs = 1000) - { - using var httpClient = new HttpClient { Timeout = TimeSpan.FromSeconds(5) }; - var url = $"http://{testAgentHost}:4318/test/session/clear"; - - for (var attempt = 1; attempt <= maxRetries; attempt++) - { - try - { - var response = await httpClient.GetAsync(url); - response.EnsureSuccessStatusCode(); - return; - } - catch (Exception) when (attempt < maxRetries) - { - await Task.Delay(delayMs); - } - } - - // Final attempt -- let it throw if it fails - var finalResponse = await httpClient.GetAsync(url); - finalResponse.EnsureSuccessStatusCode(); - } - - /// - /// Polls the test-agent for data until non-empty results are returned or timeout is reached. - /// The sample app exports data during shutdown, so there can be a brief delay - /// between process exit and data appearing in the test-agent. The timeout is generous - /// because first-time gRPC connections (TCP+HTTP/2+TLS handshake) plus tracer shutdown - /// flushing can stack up on slower CI runners. - /// - public static async Task WaitForTestAgentDataAsync(string url, int timeoutSeconds = 60, int pollIntervalMs = 500) - { - using var httpClient = new HttpClient { Timeout = TimeSpan.FromSeconds(10) }; - var deadline = DateTime.UtcNow.AddSeconds(timeoutSeconds); - - while (DateTime.UtcNow < deadline) - { - var response = await httpClient.GetAsync(url); - response.EnsureSuccessStatusCode(); - - var json = await response.Content.ReadAsStringAsync(); - var data = JToken.Parse(json); - - if (data.HasValues) - { - return data; - } - - await Task.Delay(pollIntervalMs); - } - - // Final attempt -- return whatever we get so the caller's assertion shows the actual value - var finalResponse = await httpClient.GetAsync(url); - finalResponse.EnsureSuccessStatusCode(); - var finalJson = await finalResponse.Content.ReadAsStringAsync(); - return JToken.Parse(finalJson); - } - } -} -``` - -`AddSimpleScrubber` is an extension method on `VerifySettings` defined in `tracer/test/Datadog.Trace.TestHelpers.SharedSource/VerifyHelper.cs:202`, so `OtlpSnapshotHelper.cs` also needs `using Datadog.Trace.TestHelpers;`. - -- [ ] **Step 3: Delete the moved members from `OpenTelemetrySdkTests.cs`** - -Delete `ProtobufToJsonFieldNameMappings` (lines ~83-98), `ProtobufToJsonEnumMappings` (~100-110), `AddProtobufToJsonScrubbers` (~979-990), `ClearTestAgentSession` (~892-914), and `WaitForTestAgentData` (~923-949). - -- [ ] **Step 4: Update the call sites in `OpenTelemetrySdkTests.cs`** - -There are five call sites. Replace each: - -| Old | New | -| --- | --- | -| `await ClearTestAgentSession(testAgentHost);` | `await OtlpSnapshotHelper.ClearTestAgentSessionAsync(testAgentHost);` | -| `await WaitForTestAgentData(...)` | `await OtlpSnapshotHelper.WaitForTestAgentDataAsync(...)` | -| `AddProtobufToJsonScrubbers(settings);` | `OtlpSnapshotHelper.AddProtobufToJsonScrubbers(settings);` | - -Add `using Datadog.Trace.ClrProfiler.IntegrationTests.Helpers;` to the file's using block. - -- [ ] **Step 5: Build** - -Run: `dotnet build tracer/test/Datadog.Trace.ClrProfiler.IntegrationTests/Datadog.Trace.ClrProfiler.IntegrationTests.csproj -f net10.0` -Expected: build succeeds with no new warnings. `System.Net.Http` and `System.Text.RegularExpressions` usings in `OpenTelemetrySdkTests.cs` may now be unused — if the analyzer flags them, remove only the ones it flags (`Regex` is still used by the `_versionRegex` fields, so do not blanket-remove). - -- [ ] **Step 6: Smoke-test one OTLP case** - -Run: -```bash -dotnet test tracer/test/Datadog.Trace.ClrProfiler.IntegrationTests/Datadog.Trace.ClrProfiler.IntegrationTests.csproj \ - -f net10.0 --no-build \ - --filter "FullyQualifiedName~OpenTelemetrySdkTests.SubmitsOtlpTraces" -``` -Expected: PASS. If the harness cannot locate the monitoring home, fall back to the documented Nuke path: -```bash -./tracer/build.sh BuildAndRunIntegrationTests --framework net10.0 \ - --filter "Datadog.Trace.ClrProfiler.IntegrationTests.OpenTelemetrySdkTests.SubmitsOtlpTraces" \ - --SampleName "Samples.OpenTelemetrySdk" -``` - -- [ ] **Step 7: Confirm no snapshot drifted** - -Run: `git status --porcelain tracer/test/snapshots/` -Expected: **empty output**. Any modified or new `.received.txt` file means the extraction changed behavior — stop and fix before continuing. - -- [ ] **Step 8: Commit** - -```bash -git add tracer/test/Datadog.Trace.ClrProfiler.IntegrationTests/Helpers/OtlpFieldNames.cs \ - tracer/test/Datadog.Trace.ClrProfiler.IntegrationTests/Helpers/OtlpSnapshotHelper.cs \ - tracer/test/Datadog.Trace.ClrProfiler.IntegrationTests/OpenTelemetrySdkTests.cs -git commit -m "test: extract OTLP test-agent helpers from OpenTelemetrySdkTests" -``` - ---- - -### Task 2: Extract OTLP payload normalization and request merging - -**Files:** -- Modify: `tracer/test/Datadog.Trace.ClrProfiler.IntegrationTests/Helpers/OtlpSnapshotHelper.cs` -- Modify: `tracer/test/Datadog.Trace.ClrProfiler.IntegrationTests/OpenTelemetrySdkTests.cs:340-535` - -**Interfaces:** -- Consumes: `OtlpFieldNames`, `OtlpSnapshotHelper` from Task 1 -- Produces: - - `OtlpSnapshotHelper.NormalizeResourceAttributes(JToken tracesRequests, OtlpFieldNames names) -> void` - - `OtlpSnapshotHelper.NormalizeSpans(JToken tracesRequests, OtlpFieldNames names, long applicationStartTimeUnixNano) -> void` - - `OtlpSnapshotHelper.MergeDatadogRequests(JToken tracesRequests, OtlpFieldNames names, Func, IEnumerable>? sortSpans = null) -> JToken` - - `OtlpSnapshotHelper.SortSpansPerScope(JToken tracesRequests, OtlpFieldNames names) -> void` - - `OtlpSnapshotHelper.GetAttributeStringValue(JToken span, OtlpFieldNames names, params string[] keys) -> string?` - - `OtlpSnapshotHelper.SetAttributeStringValue(JToken span, OtlpFieldNames names, string key, string value) -> void` - - `OtlpSnapshotHelper.SortSpanAttributes(JToken tracesRequests) -> void` - -**Critical:** `MergeDatadogRequests`'s default sort must stay `OrderBy(s => s["name"]!.ToString())` with the default string comparer — exactly what `OpenTelemetrySdkTests` does today. Do not "improve" it to `StringComparer.Ordinal` here or its snapshots will reorder. - -- [ ] **Step 1: Add the normalization methods to `OtlpSnapshotHelper`** - -Bodies are lifted verbatim from `OpenTelemetrySdkTests.SubmitsOtlpTraces`, with the local `*Key` variables replaced by `names.*`. - -```csharp - public static void NormalizeResourceAttributes(JToken tracesRequests, OtlpFieldNames names) - { - var stringValueKey = names.StringValue; - - foreach (var attribute in tracesRequests.SelectTokens("$..resource.attributes[?(@.key == 'telemetry.sdk.version')]")) - { - attribute["value"]![stringValueKey] = "sdk-version"; - } - - foreach (var attribute in tracesRequests.SelectTokens("$..resource.attributes[?(@.key == 'telemetry.sdk.name')]")) - { - attribute["value"]![stringValueKey] = "sdk-name"; - } - - foreach (var attribute in tracesRequests.SelectTokens("$..resource.attributes[?(@.key == 'git.commit.sha')]")) - { - attribute["value"]![stringValueKey] = "normalized-git-commit-sha"; - } - } - - public static void NormalizeSpans(JToken tracesRequests, OtlpFieldNames names, long applicationStartTimeUnixNano) - { - var isJson = names.IsJson; - var stringValueKey = names.StringValue; - var traceIdKey = names.TraceId; - var spanIdKey = names.SpanId; - var parentSpanIdKey = names.ParentSpanId; - var startTimeUnixNanoKey = names.StartTimeUnixNano; - var endTimeUnixNanoKey = names.EndTimeUnixNano; - var timeUnixNanoKey = names.TimeUnixNano; - - foreach (var span in tracesRequests.SelectTokens("$..spans[*]")) - { - // Parse unstable information from the span - string traceIdData = isJson ? span[traceIdKey].ToString() - : ToTraceId(Convert.FromBase64String(span[traceIdKey].ToString())); - string spanIdData = isJson ? span[spanIdKey].ToString() - : ToSpanId(Convert.FromBase64String(span[spanIdKey].ToString())); - var spanStartTimeUnixNano = long.Parse(span[startTimeUnixNanoKey].ToString()); - var spanEndTimeUnixNano = long.Parse(span[endTimeUnixNanoKey].ToString()); - - // Add strong assertions on unstable span information - spanStartTimeUnixNano.Should().BeGreaterThanOrEqualTo(applicationStartTimeUnixNano); - spanEndTimeUnixNano.Should().BeGreaterThanOrEqualTo(spanStartTimeUnixNano); - traceIdData.Should().MatchRegex(TraceIdRegex); - spanIdData.Should().MatchRegex(SpanIdRegex); - if (span[parentSpanIdKey] != null) - { - string parentSpanIdData = isJson ? span[parentSpanIdKey]?.ToString() - : ToSpanId(Convert.FromBase64String(span[parentSpanIdKey].ToString())); - parentSpanIdData.Should().MatchRegex(SpanIdRegex); - } - - // Normalize the unstable span information for our snapshots - span[startTimeUnixNanoKey] = "0"; - span[endTimeUnixNanoKey] = "0"; - span[traceIdKey] = "normalized-trace-id"; - span[spanIdKey] = "normalized-span-id"; - if (span[parentSpanIdKey] != null) - { - span[parentSpanIdKey] = "normalized-parent-span-id"; - } - - // Our JSON and Protobuf OTLP exporters differ in serialization behavior when there are no attributes. - // Standardize them here by removing an empty array - if (span["attributes"] is JArray attributes && attributes.Count == 0) - { - ((JObject)span).Remove("attributes"); - } - } - - foreach (var attribute in tracesRequests.SelectTokens("$..spans[*].attributes[?(@.key == 'otel.trace_id')]")) - { - attribute["value"]![stringValueKey] = "normalized-otel-trace-id"; - } - - foreach (var link in tracesRequests.SelectTokens("$..links[*]")) - { - if (isJson) - { - link[traceIdKey].ToString().Should().MatchRegex(TraceIdRegex); - link[spanIdKey].ToString().Should().MatchRegex(SpanIdRegex); - } - - link[traceIdKey] = "normalized-trace-id"; - link[spanIdKey] = "normalized-span-id"; - } - - foreach (var @event in tracesRequests.SelectTokens("$..events[*]")) - { - ((JObject)@event).Remove(timeUnixNanoKey); - ((JObject)@event).AddFirst(new JProperty(timeUnixNanoKey, "0")); - } - } - - private static string ToHexString(byte[] bytes, int length) - { - bytes.Length.Should().Be(length); - - var traceId = new byte[length * 2]; - for (int i = 0; i < length; i++) - { - traceId[2 * i] = (byte)(bytes[i] >> 4); // high 4 bits - traceId[(2 * i) + 1] = (byte)(bytes[i] & 0x0F); // low 4 bits - } - - // Convert each nibble (0-15) to its hex character - var result = new char[length * 2]; - for (int i = 0; i < length * 2; i++) - { - result[i] = (char)(traceId[i] < 10 ? '0' + traceId[i] : 'a' + traceId[i] - 10); - } - - return new string(result); - } - - private static string ToTraceId(byte[] bytes) => ToHexString(bytes, 16); - - private static string ToSpanId(byte[] bytes) => ToHexString(bytes, 8); -``` - -Add these static fields alongside the mapping tables (moved from `OpenTelemetrySdkTests`' instance fields `_traceIdRegex` / `_spanIdRegex`): - -```csharp - private static readonly Regex TraceIdRegex = new(@"^([a-fA-F0-9]{32})$"); - private static readonly Regex SpanIdRegex = new(@"^([a-fA-F0-9]{16})$"); -``` - -New usings for this file: `System.Collections.Generic`, `System.Linq`, `System.Text.RegularExpressions`, `Datadog.Trace.Vendors.Newtonsoft.Json`, `FluentAssertions`. - -The original `foreach (var link ...)` had a commented-out `else` branch for the protobuf case. It is dead code that was never enabled — drop the comment block and keep the `if (isJson)` guard, as written above. - -- [ ] **Step 2: Add merge, sort, and attribute helpers to `OtlpSnapshotHelper`** - -```csharp - /// - /// Collapses every captured request into the first one. Asserts that each request carries - /// identical resource attributes and a single instrumentation scope first, which holds for - /// the Datadog SDK because it emits one application-level resource and does not yet track - /// per-library scopes. - /// - public static JToken MergeDatadogRequests( - JToken tracesRequests, - OtlpFieldNames names, - Func, IEnumerable>? sortSpans = null) - { - var resourceSpansKey = names.ResourceSpans; - var scopeSpansKey = names.ScopeSpans; - - // First, for the DD SDK, assert that the resource attributes for all requests are identical - // This is analogous to DD_SERVICE, DD_VERSION, DD_ENV, etc. that define - // metadata for the telemetry at an application and host level. - JToken previousResourceAttributes = null; - foreach (var tracesRequest in tracesRequests) - { - tracesRequest[resourceSpansKey].Should().HaveCount(1); - var resourceAttributes = tracesRequest[resourceSpansKey][0]["resource"]["attributes"]; - - if (previousResourceAttributes == null) - { - previousResourceAttributes = resourceAttributes; - } - else - { - JToken.DeepEquals(previousResourceAttributes, resourceAttributes).Should().BeTrue(); - previousResourceAttributes = resourceAttributes; - } - } - - // Next, assert that we only have a singular InstrumentationScope in each request. - // In OpenTelemetry, an InstrumentationScope is a way to group spans by the library that produced them. - // We should be respecting this for each library/ActivitySource, but right now the DD SDK doesn't - // keep track of that information, so consolidate them into one single, empty InstrumentationScope. - // TODO: Properly track spans per instrumentation scope. - JArray firstSpans = null; - foreach (var tracesRequest in tracesRequests) - { - tracesRequest[resourceSpansKey][0][scopeSpansKey].Should().HaveCount(1); - var spans = tracesRequest[resourceSpansKey][0][scopeSpansKey][0]["spans"] as JArray; - - if (firstSpans == null) - { - firstSpans = spans; - } - else - { - foreach (var span in spans) - { - firstSpans.Add(span); - } - } - } - - // Now re-order and trim down to one single request - // This means the output is not a true 1:1 mapping of the input spans, but it's good enough for now - // and will make the results stable. - sortSpans ??= spans => spans.OrderBy(s => s["name"]!.ToString()); - var sortedSpans = new JArray(sortSpans(firstSpans)); - tracesRequests[0][resourceSpansKey][0][scopeSpansKey][0]["spans"] = sortedSpans; - return tracesRequests[0]; - } - - /// - /// Sorts spans by name within each scope, leaving the request structure intact. Used when the - /// payload comes from a real OTel SDK, which emits genuinely distinct scopes. - /// - public static void SortSpansPerScope(JToken tracesRequests, OtlpFieldNames names) - { - foreach (var scopeSpan in tracesRequests.SelectTokens($"$..{names.ScopeSpans}[*]")) - { - if (scopeSpan["spans"] is JArray spansArray) - { - var sorted = new JArray(spansArray.OrderBy(s => s["name"]?.ToString())); - scopeSpan["spans"] = sorted; - } - } - } - - /// - /// Returns the string value of the first attribute matching any of , - /// or null when the span carries none of them. Accepts several keys because a tag's name - /// changes with the semantic conventions in play (for example http.url vs url.full). - /// - public static string? GetAttributeStringValue(JToken span, OtlpFieldNames names, params string[] keys) - { - if (span["attributes"] is not JArray attributes) - { - return null; - } - - foreach (var key in keys) - { - foreach (var attribute in attributes) - { - if (attribute["key"]?.ToString() == key) - { - return attribute["value"]?[names.StringValue]?.ToString(); - } - } - } - - return null; - } - - /// - /// Sets a string attribute on a span, appending it when absent. - /// - public static void SetAttributeStringValue(JToken span, OtlpFieldNames names, string key, string value) - { - if (span["attributes"] is not JArray attributes) - { - attributes = new JArray(); - ((JObject)span)["attributes"] = attributes; - } - - foreach (var attribute in attributes) - { - if (attribute["key"]?.ToString() == key) - { - attribute["value"] = new JObject { [names.StringValue] = value }; - return; - } - } - - attributes.Add(new JObject - { - ["key"] = key, - ["value"] = new JObject { [names.StringValue] = value }, - }); - } - - /// - /// Sorts every span's attribute array by key. Attribute order otherwise follows tag - /// enumeration order, which is not guaranteed stable across runtimes. - /// - public static void SortSpanAttributes(JToken tracesRequests) - { - foreach (var span in tracesRequests.SelectTokens("$..spans[*]")) - { - if (span["attributes"] is JArray attributes) - { - ((JObject)span)["attributes"] = new JArray( - attributes.OrderBy(a => a["key"]?.ToString() ?? string.Empty, StringComparer.Ordinal)); - } - } - } -``` - -- [ ] **Step 3: Replace the inlined logic in `OpenTelemetrySdkTests.SubmitsOtlpTraces`** - -Delete everything from `// Normalize the data in resource attributes and spans` (the block of `*Key` local variables) through the end of the `else` branch that assigns `finalJson`, and replace with: - -```csharp - var names = OtlpFieldNames.For(isJson); - OtlpSnapshotHelper.NormalizeResourceAttributes(tracesRequests, names); - OtlpSnapshotHelper.NormalizeSpans(tracesRequests, names, applicationStartTimeUnixNano); - - string finalJson; - if (datadogTracesEnabled.Equals("true")) - { - finalJson = OtlpSnapshotHelper.MergeDatadogRequests(tracesRequests, names) - .ToString(Formatting.Indented); - } - else - { - OtlpSnapshotHelper.SortSpansPerScope(tracesRequests, names); - finalJson = tracesRequests.ToString(Formatting.Indented); - } -``` - -Then delete the now-unused `_traceIdRegex` and `_spanIdRegex` instance fields. - -- [ ] **Step 4: Build** - -Run: `dotnet build tracer/test/Datadog.Trace.ClrProfiler.IntegrationTests/Datadog.Trace.ClrProfiler.IntegrationTests.csproj -f net10.0` -Expected: build succeeds. Remove any using directives the analyzer now reports as unused. - -- [ ] **Step 5: Run the full OTLP traces theory** - -Run: -```bash -dotnet test tracer/test/Datadog.Trace.ClrProfiler.IntegrationTests/Datadog.Trace.ClrProfiler.IntegrationTests.csproj \ - -f net10.0 --no-build \ - --filter "FullyQualifiedName~OpenTelemetrySdkTests.SubmitsOtlpTraces" -``` -Expected: all cases PASS. This covers both the http/json and http/protobuf paths and both the merged (Datadog) and per-scope (OTel SDK) branches. - -- [ ] **Step 6: Confirm no snapshot drifted** - -Run: `git status --porcelain tracer/test/snapshots/` -Expected: **empty output**. - -- [ ] **Step 7: Commit** - -```bash -git add tracer/test/Datadog.Trace.ClrProfiler.IntegrationTests/Helpers/OtlpSnapshotHelper.cs \ - tracer/test/Datadog.Trace.ClrProfiler.IntegrationTests/OpenTelemetrySdkTests.cs -git commit -m "test: extract OTLP payload normalization into OtlpSnapshotHelper" -``` - ---- - -### Task 3: Serialize test-agent OTLP consumers into one xUnit collection - -xUnit runs distinct collections in parallel, and this project sets no `CollectionBehavior`. `ClearTestAgentSessionAsync` wipes the shared test-agent session globally, so once `WebRequestTests` also uses it, a clear from one class can delete another class's in-flight traces. Today `OpenTelemetrySdkTests` is safe only because all its tests live in one implicit per-class collection. - -In CI this costs almost nothing: the non-docker job filters `OpenTelemetrySdkTests` out entirely (`RequiresDockerDependency!=true`), and the docker job filters out `WebRequestTests`' msgpack tests, so the only serialization that actually happens is between OTLP tests — which is the intent. - -**Files:** -- Create: `tracer/test/Datadog.Trace.ClrProfiler.IntegrationTests/Helpers/TestAgentOtlpCollection.cs` -- Modify: `tracer/test/Datadog.Trace.ClrProfiler.IntegrationTests/OpenTelemetrySdkTests.cs:28` -- Modify: `tracer/test/Datadog.Trace.ClrProfiler.IntegrationTests/WebRequestTests.cs:21-22` - -**Interfaces:** -- Consumes: nothing -- Produces: collection name `TestAgentOtlpCollection` for use in `[Collection(nameof(TestAgentOtlpCollection))]` - -- [ ] **Step 1: Create the collection definition** - -```csharp -// -// Unless explicitly stated otherwise all files in this repository are licensed under the Apache 2 License. -// This product includes software developed at Datadog (https://www.datadoghq.com/). Copyright 2017 Datadog, Inc. -// - -using Xunit; - -namespace Datadog.Trace.ClrProfiler.IntegrationTests.Helpers -{ - /// - /// Serializes every test class that reads from the shared ddapm test-agent OTLP session. - /// Those tests call /test/session/clear, which wipes the session for everyone, so they must - /// not run concurrently with each other. - /// - [CollectionDefinition(nameof(TestAgentOtlpCollection), DisableParallelization = true)] - public class TestAgentOtlpCollection - { - } -} -``` - -- [ ] **Step 2: Move `WebRequestTests` into the shared collection** - -Replace lines 21-22 of `WebRequestTests.cs`: - -```csharp - [CollectionDefinition(nameof(WebRequestTests), DisableParallelization = true)] - [Collection(nameof(WebRequestTests))] -``` - -with: - -```csharp - [Collection(nameof(TestAgentOtlpCollection))] -``` - -The existing collection contained only this one class and existed purely to disable parallelization, which the new collection also does. - -- [ ] **Step 3: Add `OpenTelemetrySdkTests` to the shared collection** - -Add `[Collection(nameof(TestAgentOtlpCollection))]` to the attribute list on the class (alongside the existing `[UsesVerify]`). - -- [ ] **Step 4: Verify test discovery is unchanged** - -Run: -```bash -dotnet test tracer/test/Datadog.Trace.ClrProfiler.IntegrationTests/Datadog.Trace.ClrProfiler.IntegrationTests.csproj \ - -f net10.0 --list-tests --filter "FullyQualifiedName~WebRequestTests|FullyQualifiedName~OpenTelemetrySdkTests" \ - | grep -c "WebRequestTests\|OpenTelemetrySdkTests" -``` -Expected: a non-zero count, and no discovery errors. A class in two collections is a runtime error, so a clean listing confirms the attributes are correct. - -- [ ] **Step 5: Commit** - -```bash -git add tracer/test/Datadog.Trace.ClrProfiler.IntegrationTests/Helpers/TestAgentOtlpCollection.cs \ - tracer/test/Datadog.Trace.ClrProfiler.IntegrationTests/OpenTelemetrySdkTests.cs \ - tracer/test/Datadog.Trace.ClrProfiler.IntegrationTests/WebRequestTests.cs -git commit -m "test: serialize test-agent OTLP consumers into a shared xunit collection" -``` - ---- - -### Task 4: Add `WebRequestTests.SubmitsOtlpTraces` and generate the snapshots - -**Files:** -- Modify: `tracer/test/Datadog.Trace.ClrProfiler.IntegrationTests/WebRequestTests.cs` -- Create: `tracer/test/snapshots/WebRequestTests.SubmitsOtlpTraces_DD.verified.txt` -- Create: `tracer/test/snapshots/WebRequestTests.SubmitsOtlpTraces_DD_OtelSemantics.verified.txt` - -**Interfaces:** -- Consumes: everything produced by Tasks 1-3 -- Produces: nothing downstream - -- [ ] **Step 1: Write the test method (it will fail — no snapshot exists yet)** - -Add to `WebRequestTests`, after `SubmitsTracesV1WithOpenTelemetrySemantics`: - -```csharp - [SkippableTheory] - [Trait("Category", "EndToEnd")] - [Trait("RequiresDockerDependency", "true")] - [Trait("DockerGroup", "1")] - [InlineData("http/json", false)] - [InlineData("http/json", true)] - [InlineData("http/protobuf", false)] - [InlineData("http/protobuf", true)] - public async Task SubmitsOtlpTraces(string protocol, bool openTelemetrySemanticsEnabled) - { - SetInstrumentationVerification(); - - var isJson = protocol == "http/json"; - var names = OtlpFieldNames.For(isJson); - var testAgentHost = Environment.GetEnvironmentVariable("TEST_AGENT_HOST") ?? "127.0.0.1"; - - await OtlpSnapshotHelper.ClearTestAgentSessionAsync(testAgentHost); - - var httpPort = TcpPortProvider.GetOpenPort(); - Output.WriteLine($"Assigning port {httpPort} for the httpPort."); - - // OpenTelemetry semantics unilaterally force the v0 schema, so pin v0 for the - // semantics-off case too and keep the two snapshots directly comparable. - SetEnvironmentVariable("DD_TRACE_SPAN_ATTRIBUTE_SCHEMA", "v0"); - SetEnvironmentVariable("DD_TRACE_OTEL_SEMANTICS_ENABLED", openTelemetrySemanticsEnabled.ToString()); - - // OTEL_TRACES_EXPORTER=otlp is what makes the Datadog SDK emit OTLP instead of msgpack - SetEnvironmentVariable("OTEL_TRACES_EXPORTER", "otlp"); - SetEnvironmentVariable("OTEL_EXPORTER_OTLP_PROTOCOL", protocol); - SetEnvironmentVariable("OTEL_EXPORTER_OTLP_ENDPOINT", $"http://{testAgentHost}:4318"); - - var applicationStartTimeUnixNano = DateTimeOffset.UtcNow.ToUnixTimeNanoseconds(); - - // Traces go to the test-agent over OTLP, but telemetry still goes to the mock agent - using var telemetry = this.ConfigureTelemetry(); - using var agent = EnvironmentHelper.GetMockAgent(); - using ProcessResult processResult = await RunSampleAndWaitForExit(agent, arguments: $"Port={httpPort}"); - - var tracesRequests = await OtlpSnapshotHelper.WaitForTestAgentDataAsync($"http://{testAgentHost}:4318/test/session/traces"); - tracesRequests.Should().NotBeNullOrEmpty(); - - OtlpSnapshotHelper.NormalizeResourceAttributes(tracesRequests, names); - OtlpSnapshotHelper.NormalizeSpans(tracesRequests, names, applicationStartTimeUnixNano); - NormalizeWebRequestSpans(tracesRequests, names); - OtlpSnapshotHelper.SortSpanAttributes(tracesRequests); - - // Sort by name, then by the request URL, then by the span's own normalized JSON. - // IDs and timestamps are already normalized, so the last key is total: any two spans - // that still tie are byte-identical and their order cannot affect the snapshot. - var merged = OtlpSnapshotHelper.MergeDatadogRequests( - tracesRequests, - names, - spans => spans.OrderBy(s => s["name"]?.ToString() ?? string.Empty, StringComparer.Ordinal) - .ThenBy(s => OtlpSnapshotHelper.GetAttributeStringValue(s, names, "url.full", "http.url") ?? string.Empty, StringComparer.Ordinal) - .ThenBy(s => s.ToString(Formatting.None), StringComparer.Ordinal)); - - var finalJson = merged.ToString(Formatting.Indented); - - var settings = VerifyHelper.GetSpanVerifierSettings(); -#if NETCOREAPP - // different TFMs use different underlying handlers, which we don't really care about for the snapshots - settings.AddSimpleScrubber("System.Net.Http.HttpClientHandler", "System.Net.Http.SocketsHttpHandler"); -#endif - if (!isJson) - { - OtlpSnapshotHelper.AddProtobufToJsonScrubbers(settings); - } - - var suffix = openTelemetrySemanticsEnabled ? "_OtelSemantics" : string.Empty; - await Verifier.Verify(finalJson, settings) - .UseFileName($"{nameof(WebRequestTests)}.{nameof(SubmitsOtlpTraces)}_DD{suffix}") - .DisableRequireUniquePrefix(); - - await telemetry.AssertIntegrationEnabledAsync(IntegrationId.WebRequest); - VerifyInstrumentation(processResult.Process); - } -``` - -New usings for `WebRequestTests.cs`: `System`, `Datadog.Trace.ClrProfiler.IntegrationTests.Helpers` (already present), `Datadog.Trace.ExtensionMethods` (for `ToUnixTimeNanoseconds`), `Datadog.Trace.Vendors.Newtonsoft.Json` (for `Formatting`), `Datadog.Trace.Vendors.Newtonsoft.Json.Linq` (for `JToken`/`JObject`/`JTokenType`). - -- [ ] **Step 2: Add the WebRequest-specific normalization** - -Add as a private method on `WebRequestTests`: - -```csharp - /// - /// Normalizes the parts of the OTLP payload that are specific to this sample: the randomly - /// assigned listener port, and the one span whose shape changed on .NET 9. - /// - private void NormalizeWebRequestSpans(JToken tracesRequests, OtlpFieldNames names) - { - // The sample's HttpListener binds a random port each run. url.full is covered by - // VerifyHelper's localhost: scrubber, but server.port carries the bare number. - foreach (var attribute in tracesRequests.SelectTokens("$..spans[*].attributes[?(@.key == 'server.port')]")) - { - if (attribute["value"] is JObject value) - { - foreach (var property in value.Properties()) - { - // Preserve the value kind (stringValue vs intValue) so http/json and - // http/protobuf still render identically after scrubbing. - property.Value = property.Value.Type == JTokenType.String ? (JToken)"8080" : (JToken)8080; - } - } - } - -#if NET9_0_OR_GREATER - // .NET 9.0 changed the behaviour when AllowWriteStreamBuffering=false - // The net result is that we end up creating a "WebRequest" span instead - // of an "HttpClient" span in one of the cases. Rather than creating a whole - // separate set of snapshots for .NET 9+, just "fixing" that one span instead. - var rogueSpan = tracesRequests - .SelectTokens("$..spans[*]") - .SingleOrDefault(s => OtlpSnapshotHelper.GetAttributeStringValue(s, names, "url.full", "http.url") - ?.EndsWith("?BeginGetResponseAsync_NoBuffering") == true); - - // it should never be null, but fall through to fail the snapshots for easier debuggability if it is - if (rogueSpan is not null) - { - Output.WriteLine("Updating span with HttpClient tags"); - OtlpSnapshotHelper.SetAttributeStringValue(rogueSpan, names, "component", "HttpMessageHandler"); // previously "WebRequest" - OtlpSnapshotHelper.SetAttributeStringValue(rogueSpan, names, "http-client-handler-type", "System.Net.Http.SocketsHttpHandler"); // previously not set - } -#endif - } -``` - -`SortSpanAttributes` runs *after* this method in Step 1 precisely so the appended `http-client-handler-type` lands in key order rather than at the end of the array. - -- [ ] **Step 3: Build and run one case to watch it fail** - -Run: -```bash -dotnet build tracer/test/Datadog.Trace.ClrProfiler.IntegrationTests/Datadog.Trace.ClrProfiler.IntegrationTests.csproj -f net10.0 -dotnet test tracer/test/Datadog.Trace.ClrProfiler.IntegrationTests/Datadog.Trace.ClrProfiler.IntegrationTests.csproj \ - -f net10.0 --no-build \ - --filter "FullyQualifiedName~WebRequestTests.SubmitsOtlpTraces" -``` -Expected: FAIL — Verify reports a new `.received.txt` with no matching `.verified.txt`. Any *other* failure (no traces returned, assertion on trace ID format, `SingleOrDefault` throwing on multiple matches) is a real bug: fix it before accepting anything. - -- [ ] **Step 4: Inspect the received snapshots before accepting them** - -Run: -```bash -ls tracer/test/snapshots/WebRequestTests.SubmitsOtlpTraces*.received.txt -grep -nE '"(stringValue|string_value)": "[^"]*(:[0-9]{4,5})' tracer/test/snapshots/WebRequestTests.SubmitsOtlpTraces_DD.received.txt | head -grep -n "server.port" -A 3 tracer/test/snapshots/WebRequestTests.SubmitsOtlpTraces_DD.received.txt | head -8 -``` -Expected: exactly two `.received.txt` files. No raw port numbers, GUIDs, absolute paths, hostnames, or non-zero `timeUnixNano` values anywhere. `server.port` renders as `8080`. Confirm `url.full`/`http.url` shows `localhost:00000`. - -Also confirm the two files genuinely differ in the expected way — the `_OtelSemantics` one should carry `http.request.method`, `url.full`, `server.address`, `server.port`, `http.response.status_code`; the other should carry the v0 Datadog tag names: - -```bash -diff <(grep -oE '"key": "[^"]+"' tracer/test/snapshots/WebRequestTests.SubmitsOtlpTraces_DD.received.txt | sort -u) \ - <(grep -oE '"key": "[^"]+"' tracer/test/snapshots/WebRequestTests.SubmitsOtlpTraces_DD_OtelSemantics.received.txt | sort -u) -``` - -- [ ] **Step 5: Accept the snapshots** - -```bash -for f in tracer/test/snapshots/WebRequestTests.SubmitsOtlpTraces*.received.txt; do - mv "$f" "${f%.received.txt}.verified.txt" -done -``` - -- [ ] **Step 6: Re-run twice to prove stability** - -Run the Step 3 test command twice in a row. -Expected: PASS both times, and `git status --porcelain tracer/test/snapshots/` shows only the two new `.verified.txt` files as untracked — no `.received.txt` files. A `.received.txt` appearing here means the output is not deterministic; the sort key or a normalization step is incomplete. - -- [ ] **Step 7: Regression-check the existing msgpack tests** - -Run: -```bash -dotnet test tracer/test/Datadog.Trace.ClrProfiler.IntegrationTests/Datadog.Trace.ClrProfiler.IntegrationTests.csproj \ - -f net10.0 --no-build \ - --filter "FullyQualifiedName~WebRequestTests.SubmitsTraces|FullyQualifiedName~WebRequestTests.TracingDisabled" -git status --porcelain tracer/test/snapshots/ -``` -Expected: all PASS, and `git status` lists only the two new untracked `.verified.txt` files. `WebRequestTests_v0/_v1/_otel` must be unmodified. - -- [ ] **Step 8: Commit** - -```bash -git add tracer/test/Datadog.Trace.ClrProfiler.IntegrationTests/WebRequestTests.cs \ - tracer/test/snapshots/WebRequestTests.SubmitsOtlpTraces_DD.verified.txt \ - tracer/test/snapshots/WebRequestTests.SubmitsOtlpTraces_DD_OtelSemantics.verified.txt -git commit -m "test: add OTLP snapshot tests for Samples.WebRequest" -``` - ---- - -## Final verification - -- [ ] `git status --porcelain tracer/test/snapshots/` is clean apart from the two intended new files. -- [ ] `git diff --stat HEAD~4 -- tracer/test/snapshots/` shows **only** additions of the two new snapshots — zero modifications to existing ones. -- [ ] `dotnet build tracer/test/Datadog.Trace.ClrProfiler.IntegrationTests/Datadog.Trace.ClrProfiler.IntegrationTests.csproj -f net10.0` is warning-clean. -- [ ] `docker compose stop test-agent` when finished. diff --git a/docs/superpowers/specs/2026-07-30-webrequest-otlp-snapshots-design.md b/docs/superpowers/specs/2026-07-30-webrequest-otlp-snapshots-design.md deleted file mode 100644 index c7455972d7f2..000000000000 --- a/docs/superpowers/specs/2026-07-30-webrequest-otlp-snapshots-design.md +++ /dev/null @@ -1,252 +0,0 @@ -# OTLP snapshot tests for `Samples.WebRequest` - -**Date:** 2026-07-30 -**Branch:** `otel-httpclient` - -## Goal - -`WebRequestTests` currently snapshots the Datadog msgpack payload produced by -`Samples.WebRequest` (~134 spans) in three configurations: `v0`, `v1`, and -`otel` (`DD_TRACE_OTEL_SEMANTICS_ENABLED=true`). None of them exercise the OTLP -export path, so the HTTP semantic-convention attributes added on this branch -(`http.request.method`, `http.response.status_code`, `url.full`, -`server.address`, `server.port`, `http.request.method_original`) are never -verified as they appear on the wire in OTLP. - -Add OTLP snapshot coverage for the same sample, following the pattern -established by `OpenTelemetrySdkTests.SubmitsOtlpTraces`. - -Non-goal: changing, removing, or regenerating any existing msgpack snapshot. - -## Scope - -| In scope | Out of scope | -| --- | --- | -| New `SubmitsOtlpTraces` theory on `WebRequestTests` | Changing the existing `SubmitsTracesV0/V1(...)` tests | -| Extracting OTLP normalization into a shared helper | Changing `OpenTelemetrySdkTests`' observable behavior or its snapshots | -| Two new `.verified.txt` snapshots | gRPC protocol coverage (unsupported by the DD SDK trace exporter) | -| | `DD_AGENT_HOST` fallback coverage (already covered by `OpenTelemetrySdkTests`) | - -## Test matrix - -Four test cases, two snapshot files: - -| `protocol` | `openTelemetrySemanticsEnabled` | Snapshot | -| --- | --- | --- | -| `http/json` | `false` | `WebRequestTests.SubmitsOtlpTraces_DD` | -| `http/protobuf` | `false` | `WebRequestTests.SubmitsOtlpTraces_DD` | -| `http/json` | `true` | `WebRequestTests.SubmitsOtlpTraces_DD_OtelSemantics` | -| `http/protobuf` | `true` | `WebRequestTests.SubmitsOtlpTraces_DD_OtelSemantics` | - -The two protocols share a snapshot: the test-agent renders an http/protobuf -payload as JSON with snake_case field names and string-form enum values, and the -existing `ProtobufToJsonFieldNameMappings` / `ProtobufToJsonEnumMappings` tables -scrub that into the http/json shape. This is the same arrangement -`OpenTelemetrySdkTests.SubmitsOtlpTraces` uses. - -gRPC is excluded deliberately: `ExporterSettings` maps only `HttpProtobuf` and -`HttpJson` to an OTLP traces encoding and falls back to Datadog v0.4 otherwise. - -The metadata schema is pinned to `v0` for both cases. With -`DD_TRACE_OTEL_SEMANTICS_ENABLED=true` the tracer already forces v0, so pinning -it keeps the semantics-off baseline directly comparable. - -## Test method - -```csharp -[SkippableTheory] -[Trait("Category", "EndToEnd")] -[Trait("RequiresDockerDependency", "true")] -[Trait("DockerGroup", "1")] -[InlineData("http/json", false)] -[InlineData("http/json", true)] -[InlineData("http/protobuf", false)] -[InlineData("http/protobuf", true)] -public async Task SubmitsOtlpTraces(string protocol, bool openTelemetrySemanticsEnabled) -``` - -### Trait placement - -`RequiresDockerDependency` and `DockerGroup` go on the **method**, not the class. -CI partitions the integration-test run with -`(RequiresDockerDependency=true)` / `(RequiresDockerDependency!=true)` -(`tracer/build/_build/Build.Steps.cs`) and then further by -`DockerGroup=$(dockerGroup)` (`.azure-pipelines/ultimate-pipeline.yml`). A -class-level trait would pull the existing non-docker `WebRequestTests` into the -docker job. A docker test with no `DockerGroup` trait runs in neither group, so -the trait is required, not optional. `test-agent` is a dependency of both -`StartDependencies.Group1` and `Group2`, so group 1 is an arbitrary but valid -choice matching `OpenTelemetrySdkTests`. - -No `RunOnWindows` trait — the OTLP tests in `OpenTelemetrySdkTests` omit it too, -so these run on Linux only in CI. Consequence: no `_netfx` snapshot variant. - -### Environment - -``` -OTEL_TRACES_EXPORTER = otlp -OTEL_EXPORTER_OTLP_PROTOCOL = -OTEL_EXPORTER_OTLP_ENDPOINT = http://:4318 -DD_TRACE_OTEL_SEMANTICS_ENABLED = -DD_TRACE_SPAN_ATTRIBUTE_SCHEMA = v0 -``` - -`TEST_AGENT_HOST` falls back to `127.0.0.1` when unset, matching -`SubmitsOtlpTraces`. The port is always 4318 (http/json and http/protobuf both -use the HTTP endpoint). - -`DD_TRACE_HTTP_CLIENT_ERROR_STATUSES=410-499` and `SetServiceVersion("1.0.0")` -are inherited from the existing constructor and stay as-is. - -### Flow - -1. `ClearTestAgentSession(testAgentHost)` — with retries, so a not-yet-ready - test-agent doesn't fail the test. -2. Allocate `httpPort` via `TcpPortProvider.GetOpenPort()` for the sample's - `HttpListener`, as the existing tests do. -3. Construct `MockTracerAgent` and `RunSampleAndWaitForExit(agent, arguments: $"Port={httpPort}")`. - The mock agent is still needed: telemetry does not travel over OTLP, so - `telemetry.AssertIntegrationEnabledAsync(IntegrationId.WebRequest)` and - `VerifyInstrumentation(processResult.Process)` continue to work unchanged. - Only trace payloads divert to the test-agent. -4. `WaitForTestAgentData("http://:4318/test/session/traces")` — polls, - because the tracer flushes during shutdown. -5. Normalize (below), then `Verifier.Verify(finalJson, settings)` with - `.UseFileName(...)` and `.DisableRequireUniquePrefix()`. - -## Shared helper - -New `OtlpSnapshotHelper` in `Datadog.Trace.ClrProfiler.IntegrationTests`, holding -what is currently private to `OpenTelemetrySdkTests`: - -- `ProtobufToJsonFieldNameMappings` / `ProtobufToJsonEnumMappings` tables and - `AddProtobufToJsonScrubbers(settings)` -- `ClearTestAgentSession(host, maxRetries, delayMs)` -- `WaitForTestAgentData(url, timeoutSeconds, pollIntervalMs)` -- Resource-attribute normalization (`telemetry.sdk.version`, - `telemetry.sdk.name`, `git.commit.sha`) -- Per-span normalization: base64→hex conversion with the existing - `_traceIdRegex` / `_spanIdRegex` assertions and monotonic-timestamp - assertions, followed by flattening to placeholders -- Merging every request into a single `resource_spans` entry after asserting - the resource attributes and instrumentation scope are identical across - requests - -`OpenTelemetrySdkTests` is rewired to call the helper. **Its snapshots must stay -byte-identical**; verify by re-running its OTLP tests and confirming no diff. - -The one behavioral risk in the extraction is span ordering: `OpenTelemetrySdkTests` -sorts by `name` only. The helper therefore takes an **optional sort-key selector -defaulting to name-only**, preserving current behavior, and `WebRequestTests` -passes the composite key described below. - -## Test isolation - -`ClearTestAgentSession` clears the test-agent session **globally**. xUnit runs -distinct collections in parallel and this project declares no -`CollectionBehavior`, so once a second class starts clearing the session, a -clear from one class can delete another class's in-flight traces. - -Today `OpenTelemetrySdkTests` is safe only by accident: all of its OTLP tests -share one implicit per-class collection. Adding OTLP tests to `WebRequestTests` -breaks that. - -Fix: a shared `TestAgentOtlpCollection` with `DisableParallelization = true`, -applied to both classes. `WebRequestTests`' existing single-class -`CollectionDefinition` is replaced by it — that collection existed only to -disable parallelization, which the shared one also does. - -The CI cost is close to zero. The non-docker job filters `OpenTelemetrySdkTests` -out entirely via `RequiresDockerDependency!=true`, and the docker job filters out -`WebRequestTests`' msgpack tests, so the only work actually serialized is OTLP -tests against each other — which is the point. - -## Determinism - -Four sources of instability, each handled explicitly. - -### 1. Span ordering - -Under OTLP the span `name` is `Span.ResourceName` (see -`OtlpTracesJsonSerializer`), which for this sample is mostly `POST`/`GET` — -name-only sorting is nowhere near deterministic across 134 spans. - -Sort by: `name` → the `url.full` attribute value (empty string when absent) → -the span's own normalized JSON text. - -IDs and timestamps are normalized *before* sorting, which makes the third key -total: any two spans that still tie are byte-identical, so their relative order -cannot change the output. The first two keys exist only to make the snapshot -readable. - -### 2. Dynamic listener port - -`VerifyHelper.SpanScrubbers` already rewrites `localhost:\d+` → `localhost:00000` -and `127.0.0.1:\d+` → `localhost:00000`, which covers `url.full`. -`ScrubInlineGuids` covers the per-run GUID in the request path. - -`server.port` is a separate attribute carrying the raw port number, and it is -not a text match for those regexes. Normalize it via a JToken lookup on -`key == 'server.port'`, setting the value to a fixed `8080` — mirroring the -`server.port: \d+` regex scrubber the msgpack test already uses. - -### 3. TFM differences - -The existing msgpack test handles two TFM-dependent differences that apply -equally to the OTLP payload: - -- 49 spans carry `http-client-handler-type`. On .NET Core the test scrubs - `System.Net.Http.HttpClientHandler` → `System.Net.Http.SocketsHttpHandler`. -- On .NET 9+, the `?BeginGetResponseAsync_NoBuffering` request produces a - `WebRequest` span instead of an `HttpClient` span. The existing test patches - that single span's `component` to `HttpMessageHandler` and adds - `http-client-handler-type = System.Net.Http.SocketsHttpHandler`. - -Both must be replicated on the OTLP JSON (the handler-type as a simple string -scrubber; the .NET 9 fixup as a JToken edit locating the span by its `url.full` -attribute suffix). Without them, .NET 9 needs its own snapshot pair. - -### 4. IDs and timestamps - -Flattened to fixed placeholders (`normalized-trace-id`, `normalized-span-id`, -`normalized-parent-span-id`, `"0"` for start/end times), matching -`OpenTelemetrySdkTests`. This erases parent→child structure from the snapshot; -that structure is already asserted by the existing msgpack snapshots, and the -OTLP snapshot's job is to verify attributes and span shape on the wire. - -The hex-format and monotonic-timestamp assertions run *before* flattening, so -the real values are still validated. - -## Assertions beyond the snapshot - -Carried over from the existing `RunTest`: - -- `telemetry.AssertIntegrationEnabledAsync(IntegrationId.WebRequest)` -- `VerifyInstrumentation(processResult.Process)` (via `SetInstrumentationVerification()`) -- `tracesRequests.Should().NotBeNullOrEmpty()` - -`ValidateIntegrationSpans` is **not** applicable: it operates on `MockSpan`, -which is the msgpack representation. The OTLP payload has no `MockSpan` -equivalent, and the snapshot covers the same ground. - -## Known trade-off: snapshot size - -OTLP JSON is far more verbose than the Verify span format — roughly six lines -per attribute versus one. The existing `WebRequestTests_otel.verified.txt` is -3,130 lines for this same data; each OTLP snapshot is expected to land around -10–12k lines, for two files. Accepted in exchange for full-fidelity coverage; -the alternative (filtering to HTTP-client spans only) was considered and -rejected. - -## Verification plan - -1. `docker compose up -d test-agent` locally (macOS, Docker confirmed running; - `artifacts/monitoring-home` is already built). -2. Run the four new cases to generate the two `.verified.txt` files; inspect - them for leaked ports, GUIDs, timestamps, or machine-specific paths. -3. Re-run each case a second time to confirm the snapshots are stable - (ordering, merged-request handling). -4. Re-run `OpenTelemetrySdkTests.SubmitsOtlpTraces` and confirm its snapshots - are unchanged after the helper extraction. -5. Confirm the existing `SubmitsTracesV0/V1` msgpack tests still pass and their - snapshots are untouched.