From 58054d16cc03cae07df48ebbf949791571a732d0 Mon Sep 17 00:00:00 2001 From: Mikel Blanchard Date: Mon, 20 Sep 2021 10:37:29 -0700 Subject: [PATCH] .NET Framework HttpClient Instrumentation: Fix missing events on instances created before TracerProvider (Part 2) (#2375) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * CHANGELOG update. * Test and bug fixes. * Update src/OpenTelemetry.Instrumentation.Http/CHANGELOG.md Co-authored-by: Robert Pająk * Lint Co-authored-by: Cijo Thomas Co-authored-by: Robert Pająk --- .../CHANGELOG.md | 4 +++ .../HttpWebRequestActivitySource.netfx.cs | 31 ++++++++++++------- .../HttpWebRequestTests.Basic.netfx.cs | 18 +++++++++++ 3 files changed, 42 insertions(+), 11 deletions(-) diff --git a/src/OpenTelemetry.Instrumentation.Http/CHANGELOG.md b/src/OpenTelemetry.Instrumentation.Http/CHANGELOG.md index de6cd0e0736..53bf878be3c 100644 --- a/src/OpenTelemetry.Instrumentation.Http/CHANGELOG.md +++ b/src/OpenTelemetry.Instrumentation.Http/CHANGELOG.md @@ -5,6 +5,10 @@ * Removes .NET Framework 4.5.2 support. The minimum .NET Framework version supported is .NET 4.6.1. ([#2138](https://github.com/open-telemetry/opentelemetry-dotnet/issues/2138)) +* `HttpClient` instances created before `AddHttpClientInstrumentation` is called + on .NET Framework will now also be instrumented + ([#2364](https://github.com/open-telemetry/opentelemetry-dotnet/pull/2364)) + ## 1.0.0-rc7 Released 2021-Jul-12 diff --git a/src/OpenTelemetry.Instrumentation.Http/Implementation/HttpWebRequestActivitySource.netfx.cs b/src/OpenTelemetry.Instrumentation.Http/Implementation/HttpWebRequestActivitySource.netfx.cs index d7a1c821e82..b4e6d545cad 100644 --- a/src/OpenTelemetry.Instrumentation.Http/Implementation/HttpWebRequestActivitySource.netfx.cs +++ b/src/OpenTelemetry.Instrumentation.Http/Implementation/HttpWebRequestActivitySource.netfx.cs @@ -492,10 +492,29 @@ private static void HookConnectionGroup(object value) ArrayList originalArrayList = connectionListField.GetValue(value) as ArrayList; ConnectionArrayList newArrayList = new ConnectionArrayList(originalArrayList ?? new ArrayList()); + foreach (object connection in originalArrayList) + { + HookConnection(connection); + } + connectionListField.SetValue(value, newArrayList); } } + private static void HookConnection(object value) + { + if (connectionType.IsInstanceOfType(value)) + { + // Replace the HttpWebRequest arraylist inside this Connection object, + // which allows us to intercept each new HttpWebRequest object added under + // this Connection. + ArrayList originalArrayList = writeListField.GetValue(value) as ArrayList; + HttpWebRequestArrayList newArrayList = new HttpWebRequestArrayList(originalArrayList ?? new ArrayList()); + + writeListField.SetValue(value, newArrayList); + } + } + private static Func CreateFieldGetter(string fieldName, BindingFlags flags) where TClass : class { @@ -968,17 +987,7 @@ public ConnectionArrayList(ArrayList list) public override int Add(object value) { - if (connectionType.IsInstanceOfType(value)) - { - // Replace the HttpWebRequest arraylist inside this Connection object, - // which allows us to intercept each new HttpWebRequest object added under - // this Connection. - ArrayList originalArrayList = writeListField.GetValue(value) as ArrayList; - HttpWebRequestArrayList newArrayList = new HttpWebRequestArrayList(originalArrayList ?? new ArrayList()); - - writeListField.SetValue(value, newArrayList); - } - + HookConnection(value); return base.Add(value); } } diff --git a/test/OpenTelemetry.Instrumentation.Http.Tests/HttpWebRequestTests.Basic.netfx.cs b/test/OpenTelemetry.Instrumentation.Http.Tests/HttpWebRequestTests.Basic.netfx.cs index db2ec6ddd2c..affd904cad0 100644 --- a/test/OpenTelemetry.Instrumentation.Http.Tests/HttpWebRequestTests.Basic.netfx.cs +++ b/test/OpenTelemetry.Instrumentation.Http.Tests/HttpWebRequestTests.Basic.netfx.cs @@ -264,6 +264,24 @@ public void AddHttpClientInstrumentationUsesHttpWebRequestInstrumentationOptions }) .Build(); } + + [Fact] + public async Task HttpWebRequestInstrumentationOnExistingInstance() + { + using HttpClient client = new HttpClient(); + + await client.GetAsync(this.url).ConfigureAwait(false); + + var activityProcessor = new Mock>(); + using var shutdownSignal = Sdk.CreateTracerProviderBuilder() + .AddProcessor(activityProcessor.Object) + .AddHttpClientInstrumentation() + .Build(); + + await client.GetAsync(this.url).ConfigureAwait(false); + + Assert.Equal(3, activityProcessor.Invocations.Count); // SetParentProvider/Begin/End called + } } } #endif