From 85331a62db9b5b63ee21b68c1b23db78e65f5aaf Mon Sep 17 00:00:00 2001 From: Matthew Hensley Date: Thu, 10 Aug 2023 15:00:28 -0400 Subject: [PATCH 01/10] piggyback off HttpClient tracing implementation to record metrics --- .../HttpWebRequestActivitySource.netfx.cs | 48 +++++++++++++++++++ 1 file changed, 48 insertions(+) diff --git a/src/OpenTelemetry.Instrumentation.Http/Implementation/HttpWebRequestActivitySource.netfx.cs b/src/OpenTelemetry.Instrumentation.Http/Implementation/HttpWebRequestActivitySource.netfx.cs index 670ce8f361..2a7a8ad192 100644 --- a/src/OpenTelemetry.Instrumentation.Http/Implementation/HttpWebRequestActivitySource.netfx.cs +++ b/src/OpenTelemetry.Instrumentation.Http/Implementation/HttpWebRequestActivitySource.netfx.cs @@ -17,6 +17,7 @@ #if NETFRAMEWORK using System.Collections; using System.Diagnostics; +using System.Diagnostics.Metrics; using System.Net; using System.Reflection; using System.Reflection.Emit; @@ -39,6 +40,7 @@ internal static class HttpWebRequestActivitySource internal static readonly AssemblyName AssemblyName = typeof(HttpWebRequestActivitySource).Assembly.GetName(); internal static readonly string ActivitySourceName = AssemblyName.Name + ".HttpWebRequest"; internal static readonly string ActivityName = ActivitySourceName + ".HttpRequestOut"; + internal static readonly string InstrumentationName = AssemblyName.Name; internal static readonly Func> HttpWebRequestHeaderValuesGetter = (request, name) => request.Headers.GetValues(name); internal static readonly Action HttpWebRequestHeaderValuesSetter = (request, name, value) => request.Headers.Add(name, value); @@ -46,6 +48,9 @@ internal static class HttpWebRequestActivitySource private static readonly Version Version = AssemblyName.Version; private static readonly ActivitySource WebRequestActivitySource = new ActivitySource(ActivitySourceName, Version.ToString()); + private static readonly Meter WebRequestMeter = new Meter(InstrumentationName, Version.ToString()); + private static readonly Histogram HttpClientDuration; + private static HttpClientInstrumentationOptions options; private static bool emitOldAttributes; @@ -87,6 +92,7 @@ static HttpWebRequestActivitySource() PerformInjection(); Options = new HttpClientInstrumentationOptions(); + HttpClientDuration = WebRequestMeter.CreateHistogram("http.client.duration", "ms", "Measures the duration of outbound HTTP requests."); } catch (Exception ex) { @@ -395,7 +401,49 @@ private static void ProcessResult(IAsyncResult asyncResult, AsyncCallback asyncC HttpInstrumentationEventSource.Log.FailedProcessResult(ex); } + activity.SetEndTime(DateTime.UtcNow); activity.Stop(); + + TagList tags = default; + + // see the spec https://github.com/open-telemetry/opentelemetry-specification/blob/v1.20.0/specification/trace/semantic_conventions/http.md + if (emitOldAttributes) + { + foreach (var tag in activity.TagObjects) + { + switch (tag.Key) + { + case SemanticConventions.AttributeHttpMethod: + case SemanticConventions.AttributeHttpScheme: + case SemanticConventions.AttributeHttpFlavor: + case SemanticConventions.AttributeNetPeerName: + case SemanticConventions.AttributeNetPeerPort: + case SemanticConventions.AttributeHttpStatusCode: + tags.Add(new KeyValuePair(tag.Key, tag.Value)); + break; + } + } + } + + // see the spec https://github.com/open-telemetry/semantic-conventions/blob/v1.21.0/docs/http/http-spans.md + if (emitNewAttributes) + { + foreach (var tag in activity.TagObjects) + { + switch (tag.Key) + { + case SemanticConventions.AttributeHttpRequestMethod: + case SemanticConventions.AttributeNetworkProtocolVersion: + case SemanticConventions.AttributeServerAddress: + case SemanticConventions.AttributeServerPort: + case SemanticConventions.AttributeHttpResponseStatusCode: + tags.Add(new KeyValuePair(tag.Key, tag.Value)); + break; + } + } + } + + HttpClientDuration.Record(activity.Duration.TotalMilliseconds, tags); } private static void PrepareReflectionObjects() From c565eff805053af328999b666803a14f62cff047 Mon Sep 17 00:00:00 2001 From: Matthew Hensley Date: Thu, 10 Aug 2023 15:02:33 -0400 Subject: [PATCH 02/10] modify test for net462 metrics --- .../HttpClientTests.cs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/test/OpenTelemetry.Instrumentation.Http.Tests/HttpClientTests.cs b/test/OpenTelemetry.Instrumentation.Http.Tests/HttpClientTests.cs index 2b0f0e6fe0..810bbea7b8 100644 --- a/test/OpenTelemetry.Instrumentation.Http.Tests/HttpClientTests.cs +++ b/test/OpenTelemetry.Instrumentation.Http.Tests/HttpClientTests.cs @@ -157,9 +157,6 @@ public async Task HttpOutCallsAreCollectedSuccessfullyAsync(HttpTestData.HttpOut Assert.True(enrichWithExceptionCalled); } -#if NETFRAMEWORK - Assert.Empty(requestMetrics); -#else Assert.Single(requestMetrics); var metric = requestMetrics[0]; @@ -191,7 +188,11 @@ public async Task HttpOutCallsAreCollectedSuccessfullyAsync(HttpTestData.HttpOut var method = new KeyValuePair(SemanticConventions.AttributeHttpMethod, tc.Method); var scheme = new KeyValuePair(SemanticConventions.AttributeHttpScheme, "http"); var statusCode = new KeyValuePair(SemanticConventions.AttributeHttpStatusCode, tc.ResponseCode == 0 ? 200 : tc.ResponseCode); +#if NETFRAMEWORK + var flavor = new KeyValuePair(SemanticConventions.AttributeHttpFlavor, "1.1"); +#else var flavor = new KeyValuePair(SemanticConventions.AttributeHttpFlavor, "2.0"); +#endif var hostName = new KeyValuePair(SemanticConventions.AttributeNetPeerName, tc.ResponseExpected ? host : "sdlfaldfjalkdfjlkajdflkajlsdjf"); var portNumber = new KeyValuePair(SemanticConventions.AttributeNetPeerPort, port); Assert.Contains(hostName, attributes); @@ -209,7 +210,6 @@ public async Task HttpOutCallsAreCollectedSuccessfullyAsync(HttpTestData.HttpOut Assert.DoesNotContain(statusCode, attributes); Assert.Equal(5, attributes.Length); } -#endif } [Fact] From de9863506a7e151795aea6ae94b51bee6a9811df Mon Sep 17 00:00:00 2001 From: Matthew Hensley Date: Sun, 13 Aug 2023 20:14:40 -0400 Subject: [PATCH 03/10] modify AddHttpClientInstrumentation to match tracing impl with name param for config --- .../.publicApi/net462/PublicAPI.Unshipped.txt | 1 + .../MeterProviderBuilderExtensions.cs | 31 ++++++++++++++++++- 2 files changed, 31 insertions(+), 1 deletion(-) diff --git a/src/OpenTelemetry.Instrumentation.Http/.publicApi/net462/PublicAPI.Unshipped.txt b/src/OpenTelemetry.Instrumentation.Http/.publicApi/net462/PublicAPI.Unshipped.txt index 2dd55551b0..1efd333d9a 100644 --- a/src/OpenTelemetry.Instrumentation.Http/.publicApi/net462/PublicAPI.Unshipped.txt +++ b/src/OpenTelemetry.Instrumentation.Http/.publicApi/net462/PublicAPI.Unshipped.txt @@ -19,6 +19,7 @@ OpenTelemetry.Instrumentation.Http.HttpClientInstrumentationOptions.RecordExcept OpenTelemetry.Metrics.MeterProviderBuilderExtensions OpenTelemetry.Trace.TracerProviderBuilderExtensions static OpenTelemetry.Metrics.MeterProviderBuilderExtensions.AddHttpClientInstrumentation(this OpenTelemetry.Metrics.MeterProviderBuilder builder) -> OpenTelemetry.Metrics.MeterProviderBuilder +static OpenTelemetry.Metrics.MeterProviderBuilderExtensions.AddHttpClientInstrumentation(this OpenTelemetry.Metrics.MeterProviderBuilder builder, string name) -> OpenTelemetry.Metrics.MeterProviderBuilder static OpenTelemetry.Trace.TracerProviderBuilderExtensions.AddHttpClientInstrumentation(this OpenTelemetry.Trace.TracerProviderBuilder builder) -> OpenTelemetry.Trace.TracerProviderBuilder static OpenTelemetry.Trace.TracerProviderBuilderExtensions.AddHttpClientInstrumentation(this OpenTelemetry.Trace.TracerProviderBuilder builder, string name, System.Action configureHttpClientInstrumentationOptions) -> OpenTelemetry.Trace.TracerProviderBuilder static OpenTelemetry.Trace.TracerProviderBuilderExtensions.AddHttpClientInstrumentation(this OpenTelemetry.Trace.TracerProviderBuilder builder, System.Action configureHttpClientInstrumentationOptions) -> OpenTelemetry.Trace.TracerProviderBuilder diff --git a/src/OpenTelemetry.Instrumentation.Http/MeterProviderBuilderExtensions.cs b/src/OpenTelemetry.Instrumentation.Http/MeterProviderBuilderExtensions.cs index 38d375436b..5046430602 100644 --- a/src/OpenTelemetry.Instrumentation.Http/MeterProviderBuilderExtensions.cs +++ b/src/OpenTelemetry.Instrumentation.Http/MeterProviderBuilderExtensions.cs @@ -34,12 +34,26 @@ public static class MeterProviderBuilderExtensions /// The instance of to chain the calls. public static MeterProviderBuilder AddHttpClientInstrumentation( this MeterProviderBuilder builder) + { + return AddHttpClientInstrumentation(builder, name: null); + } + + /// + /// Enables HttpClient instrumentation. + /// + /// being configured. + /// Name which is used when retrieving options. + /// The instance of to chain the calls. + public static MeterProviderBuilder AddHttpClientInstrumentation( + this MeterProviderBuilder builder, string name) { Guard.ThrowIfNull(builder); // Note: Warm-up the status code mapping. _ = TelemetryHelper.BoxedStatusCodes; + name ??= Options.DefaultName; + builder.ConfigureServices(services => { services.RegisterOptionsFactory(configuration => new HttpClientMetricInstrumentationOptions(configuration)); @@ -54,7 +68,22 @@ public static MeterProviderBuilder AddHttpClientInstrumentation( // RecordException - probably doesn't make sense for metric instrumentation builder.AddMeter(HttpClientMetrics.InstrumentationName); - return builder.AddInstrumentation(sp => new HttpClientMetrics( + builder.AddInstrumentation(sp => new HttpClientMetrics( sp.GetRequiredService>().CurrentValue)); + +#if NETFRAMEWORK + builder.AddMeter(HttpWebRequestActivitySource.InstrumentationName); + builder.ConfigureServices(s => + { + s.ConfigureOpenTelemetryMeterProvider((sp, _) => + { + var options = sp.GetRequiredService>().Get(name); + + HttpWebRequestActivitySource.Options = options; + }); + }); +#endif + + return builder; } } From 3a208b92cefb4a3f49a3d5c75d0472548e3e61a9 Mon Sep 17 00:00:00 2001 From: Matthew Hensley Date: Wed, 16 Aug 2023 10:14:53 -0400 Subject: [PATCH 04/10] removed call to set endtime --- .../Implementation/HttpWebRequestActivitySource.netfx.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/src/OpenTelemetry.Instrumentation.Http/Implementation/HttpWebRequestActivitySource.netfx.cs b/src/OpenTelemetry.Instrumentation.Http/Implementation/HttpWebRequestActivitySource.netfx.cs index 2a7a8ad192..444f835694 100644 --- a/src/OpenTelemetry.Instrumentation.Http/Implementation/HttpWebRequestActivitySource.netfx.cs +++ b/src/OpenTelemetry.Instrumentation.Http/Implementation/HttpWebRequestActivitySource.netfx.cs @@ -401,7 +401,6 @@ private static void ProcessResult(IAsyncResult asyncResult, AsyncCallback asyncC HttpInstrumentationEventSource.Log.FailedProcessResult(ex); } - activity.SetEndTime(DateTime.UtcNow); activity.Stop(); TagList tags = default; From f52b4698210852794ab6cbabe071f610911293de Mon Sep 17 00:00:00 2001 From: Matthew Hensley Date: Wed, 16 Aug 2023 22:12:29 -0400 Subject: [PATCH 05/10] updated CHANGELOG + README --- src/OpenTelemetry.Instrumentation.Http/CHANGELOG.md | 2 ++ src/OpenTelemetry.Instrumentation.Http/README.md | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/src/OpenTelemetry.Instrumentation.Http/CHANGELOG.md b/src/OpenTelemetry.Instrumentation.Http/CHANGELOG.md index a94fa9789e..0ae13ac49c 100644 --- a/src/OpenTelemetry.Instrumentation.Http/CHANGELOG.md +++ b/src/OpenTelemetry.Instrumentation.Http/CHANGELOG.md @@ -2,6 +2,8 @@ ## Unreleased +* Added support for exposing `http.client.duration` on .NET Framework for `HttpWebRequest`, including `HttpClient`. Metrics support requires tracing to be enabled via `AddHttpClientInstrumentation` (#4768)[https://github.com/open-telemetry/opentelemetry-dotnet/pull/4768] + ## 1.5.1-beta.1 Released 2023-Jul-20 diff --git a/src/OpenTelemetry.Instrumentation.Http/README.md b/src/OpenTelemetry.Instrumentation.Http/README.md index 7ca9400d4b..9b8580938a 100644 --- a/src/OpenTelemetry.Instrumentation.Http/README.md +++ b/src/OpenTelemetry.Instrumentation.Http/README.md @@ -68,7 +68,7 @@ public class Program #### Metrics > **Note** -> Metrics are not available for .NET Framework. +> Metrics are only available for .NET Framework when tracing is enabled. The following example demonstrates adding `HttpClient` instrumentation with the extension method `.AddHttpClientInstrumentation()` on `MeterProviderBuilder` to From 56e220360ae196a18bc471069c986c302c3f472d Mon Sep 17 00:00:00 2001 From: Matthew Hensley Date: Thu, 17 Aug 2023 19:01:06 -0400 Subject: [PATCH 06/10] resolve public API warnings --- .../.publicApi/net6.0/PublicAPI.Unshipped.txt | 1 + .../.publicApi/netstandard2.0/PublicAPI.Unshipped.txt | 1 + 2 files changed, 2 insertions(+) diff --git a/src/OpenTelemetry.Instrumentation.Http/.publicApi/net6.0/PublicAPI.Unshipped.txt b/src/OpenTelemetry.Instrumentation.Http/.publicApi/net6.0/PublicAPI.Unshipped.txt index 2dd55551b0..1efd333d9a 100644 --- a/src/OpenTelemetry.Instrumentation.Http/.publicApi/net6.0/PublicAPI.Unshipped.txt +++ b/src/OpenTelemetry.Instrumentation.Http/.publicApi/net6.0/PublicAPI.Unshipped.txt @@ -19,6 +19,7 @@ OpenTelemetry.Instrumentation.Http.HttpClientInstrumentationOptions.RecordExcept OpenTelemetry.Metrics.MeterProviderBuilderExtensions OpenTelemetry.Trace.TracerProviderBuilderExtensions static OpenTelemetry.Metrics.MeterProviderBuilderExtensions.AddHttpClientInstrumentation(this OpenTelemetry.Metrics.MeterProviderBuilder builder) -> OpenTelemetry.Metrics.MeterProviderBuilder +static OpenTelemetry.Metrics.MeterProviderBuilderExtensions.AddHttpClientInstrumentation(this OpenTelemetry.Metrics.MeterProviderBuilder builder, string name) -> OpenTelemetry.Metrics.MeterProviderBuilder static OpenTelemetry.Trace.TracerProviderBuilderExtensions.AddHttpClientInstrumentation(this OpenTelemetry.Trace.TracerProviderBuilder builder) -> OpenTelemetry.Trace.TracerProviderBuilder static OpenTelemetry.Trace.TracerProviderBuilderExtensions.AddHttpClientInstrumentation(this OpenTelemetry.Trace.TracerProviderBuilder builder, string name, System.Action configureHttpClientInstrumentationOptions) -> OpenTelemetry.Trace.TracerProviderBuilder static OpenTelemetry.Trace.TracerProviderBuilderExtensions.AddHttpClientInstrumentation(this OpenTelemetry.Trace.TracerProviderBuilder builder, System.Action configureHttpClientInstrumentationOptions) -> OpenTelemetry.Trace.TracerProviderBuilder diff --git a/src/OpenTelemetry.Instrumentation.Http/.publicApi/netstandard2.0/PublicAPI.Unshipped.txt b/src/OpenTelemetry.Instrumentation.Http/.publicApi/netstandard2.0/PublicAPI.Unshipped.txt index 2dd55551b0..1efd333d9a 100644 --- a/src/OpenTelemetry.Instrumentation.Http/.publicApi/netstandard2.0/PublicAPI.Unshipped.txt +++ b/src/OpenTelemetry.Instrumentation.Http/.publicApi/netstandard2.0/PublicAPI.Unshipped.txt @@ -19,6 +19,7 @@ OpenTelemetry.Instrumentation.Http.HttpClientInstrumentationOptions.RecordExcept OpenTelemetry.Metrics.MeterProviderBuilderExtensions OpenTelemetry.Trace.TracerProviderBuilderExtensions static OpenTelemetry.Metrics.MeterProviderBuilderExtensions.AddHttpClientInstrumentation(this OpenTelemetry.Metrics.MeterProviderBuilder builder) -> OpenTelemetry.Metrics.MeterProviderBuilder +static OpenTelemetry.Metrics.MeterProviderBuilderExtensions.AddHttpClientInstrumentation(this OpenTelemetry.Metrics.MeterProviderBuilder builder, string name) -> OpenTelemetry.Metrics.MeterProviderBuilder static OpenTelemetry.Trace.TracerProviderBuilderExtensions.AddHttpClientInstrumentation(this OpenTelemetry.Trace.TracerProviderBuilder builder) -> OpenTelemetry.Trace.TracerProviderBuilder static OpenTelemetry.Trace.TracerProviderBuilderExtensions.AddHttpClientInstrumentation(this OpenTelemetry.Trace.TracerProviderBuilder builder, string name, System.Action configureHttpClientInstrumentationOptions) -> OpenTelemetry.Trace.TracerProviderBuilder static OpenTelemetry.Trace.TracerProviderBuilderExtensions.AddHttpClientInstrumentation(this OpenTelemetry.Trace.TracerProviderBuilder builder, System.Action configureHttpClientInstrumentationOptions) -> OpenTelemetry.Trace.TracerProviderBuilder From dadfeb4e654b72597cee8121feaf81e33f8e2e33 Mon Sep 17 00:00:00 2001 From: Matthew Hensley Date: Thu, 17 Aug 2023 19:09:03 -0400 Subject: [PATCH 07/10] swap to more performant enumeration https://github.com/dotnet/runtime/issues/68056 --- .../Implementation/HttpWebRequestActivitySource.netfx.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/OpenTelemetry.Instrumentation.Http/Implementation/HttpWebRequestActivitySource.netfx.cs b/src/OpenTelemetry.Instrumentation.Http/Implementation/HttpWebRequestActivitySource.netfx.cs index 444f835694..ce95cce076 100644 --- a/src/OpenTelemetry.Instrumentation.Http/Implementation/HttpWebRequestActivitySource.netfx.cs +++ b/src/OpenTelemetry.Instrumentation.Http/Implementation/HttpWebRequestActivitySource.netfx.cs @@ -408,7 +408,7 @@ private static void ProcessResult(IAsyncResult asyncResult, AsyncCallback asyncC // see the spec https://github.com/open-telemetry/opentelemetry-specification/blob/v1.20.0/specification/trace/semantic_conventions/http.md if (emitOldAttributes) { - foreach (var tag in activity.TagObjects) + foreach (ref readonly var tag in activity.EnumerateTagObjects()) { switch (tag.Key) { @@ -427,7 +427,7 @@ private static void ProcessResult(IAsyncResult asyncResult, AsyncCallback asyncC // see the spec https://github.com/open-telemetry/semantic-conventions/blob/v1.21.0/docs/http/http-spans.md if (emitNewAttributes) { - foreach (var tag in activity.TagObjects) + foreach (ref readonly var tag in activity.EnumerateTagObjects()) { switch (tag.Key) { From 364b1c2a43de01390fdb71455030b2f403a16962 Mon Sep 17 00:00:00 2001 From: Matthew Hensley Date: Fri, 18 Aug 2023 20:03:44 -0400 Subject: [PATCH 08/10] skip copying activity tags if metric isn't subscribed to (enabled) --- .../HttpWebRequestActivitySource.netfx.cs | 62 ++++++++++--------- 1 file changed, 33 insertions(+), 29 deletions(-) diff --git a/src/OpenTelemetry.Instrumentation.Http/Implementation/HttpWebRequestActivitySource.netfx.cs b/src/OpenTelemetry.Instrumentation.Http/Implementation/HttpWebRequestActivitySource.netfx.cs index ce95cce076..18ec90f70c 100644 --- a/src/OpenTelemetry.Instrumentation.Http/Implementation/HttpWebRequestActivitySource.netfx.cs +++ b/src/OpenTelemetry.Instrumentation.Http/Implementation/HttpWebRequestActivitySource.netfx.cs @@ -403,46 +403,50 @@ private static void ProcessResult(IAsyncResult asyncResult, AsyncCallback asyncC activity.Stop(); - TagList tags = default; - - // see the spec https://github.com/open-telemetry/opentelemetry-specification/blob/v1.20.0/specification/trace/semantic_conventions/http.md - if (emitOldAttributes) + // Only calculate duration if the Meter is subscribed to + if (HttpClientDuration.Enabled) { - foreach (ref readonly var tag in activity.EnumerateTagObjects()) + TagList tags = default; + + // see the spec https://github.com/open-telemetry/opentelemetry-specification/blob/v1.20.0/specification/trace/semantic_conventions/http.md + if (emitOldAttributes) { - switch (tag.Key) + foreach (ref readonly var tag in activity.EnumerateTagObjects()) { - case SemanticConventions.AttributeHttpMethod: - case SemanticConventions.AttributeHttpScheme: - case SemanticConventions.AttributeHttpFlavor: - case SemanticConventions.AttributeNetPeerName: - case SemanticConventions.AttributeNetPeerPort: - case SemanticConventions.AttributeHttpStatusCode: - tags.Add(new KeyValuePair(tag.Key, tag.Value)); - break; + switch (tag.Key) + { + case SemanticConventions.AttributeHttpMethod: + case SemanticConventions.AttributeHttpScheme: + case SemanticConventions.AttributeHttpFlavor: + case SemanticConventions.AttributeNetPeerName: + case SemanticConventions.AttributeNetPeerPort: + case SemanticConventions.AttributeHttpStatusCode: + tags.Add(new KeyValuePair(tag.Key, tag.Value)); + break; + } } } - } - // see the spec https://github.com/open-telemetry/semantic-conventions/blob/v1.21.0/docs/http/http-spans.md - if (emitNewAttributes) - { - foreach (ref readonly var tag in activity.EnumerateTagObjects()) + // see the spec https://github.com/open-telemetry/semantic-conventions/blob/v1.21.0/docs/http/http-spans.md + if (emitNewAttributes) { - switch (tag.Key) + foreach (ref readonly var tag in activity.EnumerateTagObjects()) { - case SemanticConventions.AttributeHttpRequestMethod: - case SemanticConventions.AttributeNetworkProtocolVersion: - case SemanticConventions.AttributeServerAddress: - case SemanticConventions.AttributeServerPort: - case SemanticConventions.AttributeHttpResponseStatusCode: - tags.Add(new KeyValuePair(tag.Key, tag.Value)); - break; + switch (tag.Key) + { + case SemanticConventions.AttributeHttpRequestMethod: + case SemanticConventions.AttributeNetworkProtocolVersion: + case SemanticConventions.AttributeServerAddress: + case SemanticConventions.AttributeServerPort: + case SemanticConventions.AttributeHttpResponseStatusCode: + tags.Add(new KeyValuePair(tag.Key, tag.Value)); + break; + } } } - } - HttpClientDuration.Record(activity.Duration.TotalMilliseconds, tags); + HttpClientDuration.Record(activity.Duration.TotalMilliseconds, tags); + } } private static void PrepareReflectionObjects() From 95c433bc43b7d185160dce923afc4934c8ae47a3 Mon Sep 17 00:00:00 2001 From: Matthew Hensley Date: Fri, 18 Aug 2023 20:04:20 -0400 Subject: [PATCH 09/10] move .NET/.NET Core instrumentation to else block --- .../MeterProviderBuilderExtensions.cs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/OpenTelemetry.Instrumentation.Http/MeterProviderBuilderExtensions.cs b/src/OpenTelemetry.Instrumentation.Http/MeterProviderBuilderExtensions.cs index 5046430602..fe9ee65275 100644 --- a/src/OpenTelemetry.Instrumentation.Http/MeterProviderBuilderExtensions.cs +++ b/src/OpenTelemetry.Instrumentation.Http/MeterProviderBuilderExtensions.cs @@ -67,10 +67,6 @@ public static MeterProviderBuilder AddHttpClientInstrumentation( // Enrich - do we want a similar kind of functionality for metrics? // RecordException - probably doesn't make sense for metric instrumentation - builder.AddMeter(HttpClientMetrics.InstrumentationName); - builder.AddInstrumentation(sp => new HttpClientMetrics( - sp.GetRequiredService>().CurrentValue)); - #if NETFRAMEWORK builder.AddMeter(HttpWebRequestActivitySource.InstrumentationName); builder.ConfigureServices(s => @@ -82,6 +78,10 @@ public static MeterProviderBuilder AddHttpClientInstrumentation( HttpWebRequestActivitySource.Options = options; }); }); +#else + builder.AddMeter(HttpClientMetrics.InstrumentationName); + builder.AddInstrumentation(sp => new HttpClientMetrics( + sp.GetRequiredService>().CurrentValue)); #endif return builder; From 1b4c3cdb127231c090cc1beb8fe056aad00ff720 Mon Sep 17 00:00:00 2001 From: Matthew Hensley Date: Mon, 21 Aug 2023 11:07:15 -0400 Subject: [PATCH 10/10] expanded instructions around tracing requirements --- src/OpenTelemetry.Instrumentation.Http/README.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/OpenTelemetry.Instrumentation.Http/README.md b/src/OpenTelemetry.Instrumentation.Http/README.md index 9b8580938a..ec33b7303c 100644 --- a/src/OpenTelemetry.Instrumentation.Http/README.md +++ b/src/OpenTelemetry.Instrumentation.Http/README.md @@ -68,7 +68,9 @@ public class Program #### Metrics > **Note** -> Metrics are only available for .NET Framework when tracing is enabled. +> Metrics are only available for .NET Framework when traces are recorded. This requires: +> 1. Tracing to be enabled by calling `.AddHttpClientInstrumentation()` on `TracerProviderBuilder` +> 2. [If a sampler is in place, return `SamplingDecision.RecordAndSampled` for `ShouldSample`](https://github.com/open-telemetry/opentelemetry-dotnet/blob/main/docs/trace/extending-the-sdk/README.md#filtering-processor) The following example demonstrates adding `HttpClient` instrumentation with the extension method `.AddHttpClientInstrumentation()` on `MeterProviderBuilder` to