From d9280a97099e7244722917860b93a38cba973201 Mon Sep 17 00:00:00 2001 From: Vishwesh Bankwar Date: Fri, 6 Oct 2023 17:33:45 -0700 Subject: [PATCH 01/16] Add support for .NET8.0 HttpClient metrics --- .../HttpHandlerMetricsDiagnosticListener.cs | 13 + .../MeterProviderBuilderExtensions.cs | 14 +- .../HttpClientTests.cs | 346 +++++++++++++++++- 3 files changed, 369 insertions(+), 4 deletions(-) diff --git a/src/OpenTelemetry.Instrumentation.Http/Implementation/HttpHandlerMetricsDiagnosticListener.cs b/src/OpenTelemetry.Instrumentation.Http/Implementation/HttpHandlerMetricsDiagnosticListener.cs index f43e3c586de..71e989d5b74 100644 --- a/src/OpenTelemetry.Instrumentation.Http/Implementation/HttpHandlerMetricsDiagnosticListener.cs +++ b/src/OpenTelemetry.Instrumentation.Http/Implementation/HttpHandlerMetricsDiagnosticListener.cs @@ -31,6 +31,7 @@ namespace OpenTelemetry.Instrumentation.Http.Implementation; internal sealed class HttpHandlerMetricsDiagnosticListener : ListenerHandler { internal const string OnStopEvent = "System.Net.Http.HttpRequestOut.Stop"; + internal static readonly bool IsNet8OrGreater; internal static readonly AssemblyName AssemblyName = typeof(HttpClientMetrics).Assembly.GetName(); internal static readonly string MeterName = AssemblyName.Name; @@ -45,6 +46,18 @@ internal sealed class HttpHandlerMetricsDiagnosticListener : ListenerHandler private readonly bool emitOldAttributes; private readonly bool emitNewAttributes; + static HttpHandlerMetricsDiagnosticListener() + { + try + { + IsNet8OrGreater = typeof(HttpClient).Assembly.GetName().Version.Major >= 8; + } + catch (Exception) + { + IsNet8OrGreater = false; + } + } + public HttpHandlerMetricsDiagnosticListener(string name, HttpClientMetricInstrumentationOptions options) : base(name) { diff --git a/src/OpenTelemetry.Instrumentation.Http/MeterProviderBuilderExtensions.cs b/src/OpenTelemetry.Instrumentation.Http/MeterProviderBuilderExtensions.cs index c82138dbbc1..a367ff74a9a 100644 --- a/src/OpenTelemetry.Instrumentation.Http/MeterProviderBuilderExtensions.cs +++ b/src/OpenTelemetry.Instrumentation.Http/MeterProviderBuilderExtensions.cs @@ -64,10 +64,18 @@ public static MeterProviderBuilder AddHttpClientInstrumentation( }); } #else - builder.AddMeter(HttpHandlerMetricsDiagnosticListener.MeterName); + if (HttpHandlerMetricsDiagnosticListener.IsNet8OrGreater) + { + builder.AddMeter("System.Net.Http"); + builder.AddMeter("System.Net.NameResolution"); + } + else + { + builder.AddMeter(HttpHandlerMetricsDiagnosticListener.MeterName); - builder.AddInstrumentation(sp => new HttpClientMetrics( - sp.GetRequiredService>().Get(Options.DefaultName))); + builder.AddInstrumentation(sp => new HttpClientMetrics( + sp.GetRequiredService>().Get(Options.DefaultName))); + } #endif return builder; } diff --git a/test/OpenTelemetry.Instrumentation.Http.Tests/HttpClientTests.cs b/test/OpenTelemetry.Instrumentation.Http.Tests/HttpClientTests.cs index 07cf5df58ae..c05994979a7 100644 --- a/test/OpenTelemetry.Instrumentation.Http.Tests/HttpClientTests.cs +++ b/test/OpenTelemetry.Instrumentation.Http.Tests/HttpClientTests.cs @@ -18,8 +18,10 @@ #if NETFRAMEWORK using System.Net.Http; #endif +#if !NET8_0 using System.Reflection; using System.Text.Json; +#endif using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; using OpenTelemetry.Metrics; @@ -33,6 +35,7 @@ public partial class HttpClientTests { public static readonly IEnumerable TestData = HttpTestData.ReadTestCases(); +#if !NET8_0 [Theory] [MemberData(nameof(TestData))] public async Task HttpOutCallsAreCollectedSuccessfullyTracesAndMetricsOldSemanticConventionsAsync(HttpTestData.HttpOutTestCase tc) @@ -71,6 +74,7 @@ await HttpOutCallsAreCollectedSuccessfullyBodyAsync( enableMetrics: true, semanticConvention: HttpSemanticConvention.Dupe).ConfigureAwait(false); } +#endif [Theory] [MemberData(nameof(TestData))] @@ -84,6 +88,7 @@ await HttpOutCallsAreCollectedSuccessfullyBodyAsync( enableMetrics: false).ConfigureAwait(false); } +#if !NET8_0 [Theory] [MemberData(nameof(TestData))] public async Task HttpOutCallsAreCollectedSuccessfullyMetricsOnlyAsync(HttpTestData.HttpOutTestCase tc) @@ -95,6 +100,7 @@ await HttpOutCallsAreCollectedSuccessfullyBodyAsync( enableTracing: false, enableMetrics: true).ConfigureAwait(false); } +#endif [Theory] [MemberData(nameof(TestData))] @@ -108,6 +114,7 @@ await HttpOutCallsAreCollectedSuccessfullyBodyAsync( enableMetrics: false).ConfigureAwait(false); } +#if !NET8_0 [Fact] public async Task DebugIndividualTestAsync() { @@ -140,6 +147,7 @@ public async Task DebugIndividualTestAsync() var t = (Task)this.GetType().InvokeMember(nameof(this.HttpOutCallsAreCollectedSuccessfullyTracesAndMetricsOldSemanticConventionsAsync), BindingFlags.InvokeMethod, null, this, HttpTestData.GetArgumentsFromTestCaseObject(input).First()); await t.ConfigureAwait(false); } +#endif [Fact] public async Task CheckEnrichmentWhenSampling() @@ -148,6 +156,92 @@ public async Task CheckEnrichmentWhenSampling() await CheckEnrichment(new AlwaysOnSampler(), true, this.url).ConfigureAwait(false); } +#if NET8_0 + [Theory] + [MemberData(nameof(TestData))] + public async Task ValidateNet8MetricsAsync(HttpTestData.HttpOutTestCase tc) + { + var metrics = new List(); + var meterProvider = Sdk.CreateMeterProviderBuilder() + .AddHttpClientInstrumentation() + .AddInMemoryExporter(metrics) + .Build(); + + var testUrl = HttpTestData.NormalizeValues(tc.Url, this.host, this.port); + + try + { + using var c = new HttpClient(); + using var request = new HttpRequestMessage + { + RequestUri = new Uri(testUrl), + Method = new HttpMethod(tc.Method), + Version = new Version(2, 0), + }; + + request.Headers.Add("contextRequired", "false"); + request.Headers.Add("responseCode", (tc.ResponseCode == 0 ? 200 : tc.ResponseCode).ToString()); + await c.SendAsync(request).ConfigureAwait(false); + } + catch (Exception) + { + // test case can intentionally send request that will result in exception + } + finally + { + meterProvider.Dispose(); + } + + // dns.lookups.duration is a typo + // https://github.com/dotnet/runtime/issues/92917 + var requestMetrics = metrics + .Where(metric => + metric.Name == "http.client.request.duration" || + metric.Name == "http.client.active_requests" || + metric.Name == "http.client.request.time_in_queue" || + metric.Name == "http.client.connection.duration" || + metric.Name == "http.client.open_connections" || + metric.Name == "dns.lookups.duration") + .ToArray(); + + if (tc.ResponseExpected) + { + Assert.Equal(6, requestMetrics.Count()); + } + else + { + Assert.Equal(4, requestMetrics.Count()); + } + + // Validate http.client.request.duration metric + var requestDurationMetric = requestMetrics.FirstOrDefault(m => m.Name == "http.client.request.duration"); + this.ValidateRequestDurationMetric(requestDurationMetric, tc); + + // Validate http.client.request.time_in_queue metric + var requestTimeInQueueMetric = requestMetrics.FirstOrDefault(m => m.Name == "http.client.request.time_in_queue"); + this.ValidateRequestTimeInQueueMetric(requestTimeInQueueMetric, tc); + + // Validate dns.lookups.duration metric + var dnsLookUpDuration = requestMetrics.FirstOrDefault(m => m.Name == "dns.lookups.duration"); + this.ValidateDnsLookUpDurationMetric(dnsLookUpDuration, tc); + + if (tc.ResponseExpected) + { + // Validate http.client.connection.duration metric + var clientConnectionDuration = requestMetrics.FirstOrDefault(m => m.Name == "http.client.connection.duration"); + this.ValidateHttpClientConnectionDurationMetric(clientConnectionDuration, tc); + + // Validate http.client.open_connections metric + var clientOpenConnections = requestMetrics.FirstOrDefault(m => m.Name == "http.client.open_connections"); + this.ValidateClientOpenConnections(clientOpenConnections, tc); + } + + // Validate http.client.active_requests metric + var clientActiveRequests = requestMetrics.FirstOrDefault(m => m.Name == "http.client.active_requests"); + this.ValidateClientActiveRequests(clientActiveRequests, tc); + } +#endif + private static async Task HttpOutCallsAreCollectedSuccessfullyBodyAsync( string host, int port, @@ -351,6 +445,7 @@ private static async Task HttpOutCallsAreCollectedSuccessfullyBodyAsync( Assert.Single(requestMetrics); } +#if !NET8_0 if (semanticConvention == null || semanticConvention.Value.HasFlag(HttpSemanticConvention.Old)) { var metric = requestMetrics.FirstOrDefault(m => m.Name == "http.client.duration"); @@ -419,7 +514,7 @@ private static async Task HttpOutCallsAreCollectedSuccessfullyBodyAsync( expected: new List { 0, 5, 10, 25, 50, 75, 100, 250, 500, 750, 1000, 2500, 5000, 7500, 10000, double.PositiveInfinity }, actual: histogramBounds); } - +#endif if (semanticConvention != null && semanticConvention.Value.HasFlag(HttpSemanticConvention.New)) { var metric = requestMetrics.FirstOrDefault(m => m.Name == "http.client.request.duration"); @@ -558,4 +653,253 @@ private static async Task CheckEnrichment(Sampler sampler, bool enrichExpected, Assert.False(enrichWithHttpResponseMessageCalled); } } + +#if NET8_0 + private void ValidateClientActiveRequests(Metric clientActiveRequests, HttpTestData.HttpOutTestCase tc) + { + var normalizedAttributesTestCase = tc.SpanAttributes.ToDictionary(x => x.Key, x => HttpTestData.NormalizeValues(x.Value, this.host, this.port)); + + Assert.NotNull(clientActiveRequests); + Assert.Equal("{request}", clientActiveRequests.Unit); + Assert.True(clientActiveRequests.MetricType == MetricType.LongSumNonMonotonic); + + var clientActiveRequestsMetricPoints = new List(); + foreach (var p in clientActiveRequests.GetMetricPoints()) + { + clientActiveRequestsMetricPoints.Add(p); + } + + Assert.Single(clientActiveRequestsMetricPoints); + var clientActiveRequestsMetricPoint = clientActiveRequestsMetricPoints[0]; + + var clientActiveRequestsAttributes = new Dictionary(); + foreach (var tag in clientActiveRequestsMetricPoint.Tags) + { + clientActiveRequestsAttributes[tag.Key] = tag.Value; + } + + Assert.Equal(4, clientActiveRequestsAttributes.Count()); + + Assert.Contains(clientActiveRequestsAttributes, kvp => kvp.Key == SemanticConventions.AttributeUrlScheme && kvp.Value.ToString() == normalizedAttributesTestCase[SemanticConventions.AttributeHttpScheme]); + Assert.Contains(clientActiveRequestsAttributes, kvp => kvp.Key == SemanticConventions.AttributeHttpRequestMethod && kvp.Value.ToString() == normalizedAttributesTestCase[SemanticConventions.AttributeHttpMethod]); + Assert.Contains(clientActiveRequestsAttributes, kvp => kvp.Key == SemanticConventions.AttributeServerAddress && kvp.Value.ToString() == normalizedAttributesTestCase[SemanticConventions.AttributeNetPeerName]); + Assert.Contains(clientActiveRequestsAttributes, kvp => kvp.Key == SemanticConventions.AttributeServerPort && kvp.Value.ToString() == normalizedAttributesTestCase[SemanticConventions.AttributeNetPeerPort]); + } + + private void ValidateClientOpenConnections(Metric clientOpenConnections, HttpTestData.HttpOutTestCase tc) + { + var normalizedAttributesTestCase = tc.SpanAttributes.ToDictionary(x => x.Key, x => HttpTestData.NormalizeValues(x.Value, this.host, this.port)); + + Assert.NotNull(clientOpenConnections); + Assert.Equal("{connection}", clientOpenConnections.Unit); + Assert.True(clientOpenConnections.MetricType == MetricType.LongSumNonMonotonic); + + var clientOpenConnectionsMetricPoints = new List(); + foreach (var p in clientOpenConnections.GetMetricPoints()) + { + clientOpenConnectionsMetricPoints.Add(p); + } + + // Two metricPoints + // one for active connections + // one for idle connetions + Assert.Equal(2, clientOpenConnectionsMetricPoints.Count()); + + clientOpenConnectionsMetricPoints.ForEach((clientOpenConnectionsMetricPoint) => + { + var clientOpenConnectionsAttributes = new Dictionary(); + foreach (var tag in clientOpenConnectionsMetricPoint.Tags) + { + clientOpenConnectionsAttributes[tag.Key] = tag.Value; + } + + Assert.Equal(6, clientOpenConnectionsAttributes.Count()); + + Assert.Contains(clientOpenConnectionsAttributes, kvp => kvp.Key == "http.connection.state" && (kvp.Value.ToString() == "idle" || kvp.Value.ToString() == "active")); + Assert.Contains(clientOpenConnectionsAttributes, kvp => kvp.Key == SemanticConventions.AttributeUrlScheme && kvp.Value.ToString() == normalizedAttributesTestCase[SemanticConventions.AttributeHttpScheme]); + Assert.Contains(clientOpenConnectionsAttributes, kvp => kvp.Key == SemanticConventions.AttributeServerAddress && kvp.Value.ToString() == normalizedAttributesTestCase[SemanticConventions.AttributeNetPeerName]); + Assert.Contains(clientOpenConnectionsAttributes, kvp => kvp.Key == SemanticConventions.AttributeServerPort && kvp.Value.ToString() == normalizedAttributesTestCase[SemanticConventions.AttributeNetPeerPort]); + Assert.Contains(clientOpenConnectionsAttributes, kvp => kvp.Key == SemanticConventions.AttributeServerSocketAddress && kvp.Value.ToString() == "::1"); + + // See https://github.com/open-telemetry/opentelemetry-dotnet/issues/4928 + // Assert.Contains(attributes, kvp => kvp.Key == SemanticConventions.AttributeNetworkProtocolVersion && kvp.Value.ToString() == normalizedAttributesTestCase[SemanticConventions.AttributeHttpFlavor]); + }); + } + + private void ValidateHttpClientConnectionDurationMetric(Metric clientConnectionDuration, HttpTestData.HttpOutTestCase tc) + { + var normalizedAttributesTestCase = tc.SpanAttributes.ToDictionary(x => x.Key, x => HttpTestData.NormalizeValues(x.Value, this.host, this.port)); + + Assert.NotNull(clientConnectionDuration); + Assert.Equal("s", clientConnectionDuration.Unit); + Assert.True(clientConnectionDuration.MetricType == MetricType.Histogram); + + var clientConnectionDurationMetricMetricPoints = new List(); + foreach (var p in clientConnectionDuration.GetMetricPoints()) + { + clientConnectionDurationMetricMetricPoints.Add(p); + } + + Assert.Single(clientConnectionDurationMetricMetricPoints); + var clientConnectionDurationMetricPoint = clientConnectionDurationMetricMetricPoints[0]; + + var count = clientConnectionDurationMetricPoint.GetHistogramCount(); + var sum = clientConnectionDurationMetricPoint.GetHistogramSum(); + + Assert.Equal(1L, count); + + // Inspect Metric Attributes + var clientConnectionDurationAttributes = new Dictionary(); + foreach (var tag in clientConnectionDurationMetricPoint.Tags) + { + clientConnectionDurationAttributes[tag.Key] = tag.Value; + } + + Assert.Equal(5, clientConnectionDurationAttributes.Count()); + + Assert.Contains(clientConnectionDurationAttributes, kvp => kvp.Key == SemanticConventions.AttributeUrlScheme && kvp.Value.ToString() == normalizedAttributesTestCase[SemanticConventions.AttributeHttpScheme]); + Assert.Contains(clientConnectionDurationAttributes, kvp => kvp.Key == SemanticConventions.AttributeServerAddress && kvp.Value.ToString() == normalizedAttributesTestCase[SemanticConventions.AttributeNetPeerName]); + Assert.Contains(clientConnectionDurationAttributes, kvp => kvp.Key == SemanticConventions.AttributeServerPort && kvp.Value.ToString() == normalizedAttributesTestCase[SemanticConventions.AttributeNetPeerPort]); + Assert.Contains(clientConnectionDurationAttributes, kvp => kvp.Key == SemanticConventions.AttributeServerSocketAddress && kvp.Value.ToString() == "::1"); + + // See https://github.com/open-telemetry/opentelemetry-dotnet/issues/4928 + // Assert.Contains(attributes, kvp => kvp.Key == SemanticConventions.AttributeNetworkProtocolVersion && kvp.Value.ToString() == normalizedAttributesTestCase[SemanticConventions.AttributeHttpFlavor]); + } + + private void ValidateDnsLookUpDurationMetric(Metric dnsLookUpDurationMetric, HttpTestData.HttpOutTestCase tc) + { + var normalizedAttributesTestCase = tc.SpanAttributes.ToDictionary(x => x.Key, x => HttpTestData.NormalizeValues(x.Value, this.host, this.port)); + + Assert.NotNull(dnsLookUpDurationMetric); + Assert.Equal("s", dnsLookUpDurationMetric.Unit); + Assert.True(dnsLookUpDurationMetric.MetricType == MetricType.Histogram); + + var dnsLookUpDurationMetricMetricPoints = new List(); + foreach (var p in dnsLookUpDurationMetric.GetMetricPoints()) + { + dnsLookUpDurationMetricMetricPoints.Add(p); + } + + Assert.Single(dnsLookUpDurationMetricMetricPoints); + var dnsLookUpDurationMetricMetricPoint = dnsLookUpDurationMetricMetricPoints[0]; + + var count = dnsLookUpDurationMetricMetricPoint.GetHistogramCount(); + var sum = dnsLookUpDurationMetricMetricPoint.GetHistogramSum(); + + Assert.Equal(1L, count); + + // Inspect Metric Attributes + var dnsLookUpDurationAttributes = new Dictionary(); + foreach (var tag in dnsLookUpDurationMetricMetricPoint.Tags) + { + dnsLookUpDurationAttributes[tag.Key] = tag.Value; + } + + Assert.Single(dnsLookUpDurationAttributes); + + Assert.Contains(dnsLookUpDurationAttributes, kvp => kvp.Key == "dns.question.name" && kvp.Value.ToString() == normalizedAttributesTestCase[SemanticConventions.AttributeNetPeerName]); + } + + private void ValidateRequestTimeInQueueMetric(Metric requestTimeInQueueMetric, HttpTestData.HttpOutTestCase tc) + { + var normalizedAttributesTestCase = tc.SpanAttributes.ToDictionary(x => x.Key, x => HttpTestData.NormalizeValues(x.Value, this.host, this.port)); + + Assert.NotNull(requestTimeInQueueMetric); + Assert.Equal("s", requestTimeInQueueMetric.Unit); + Assert.True(requestTimeInQueueMetric.MetricType == MetricType.Histogram); + + var requestTimeInQueueMetricPoints = new List(); + foreach (var p in requestTimeInQueueMetric.GetMetricPoints()) + { + requestTimeInQueueMetricPoints.Add(p); + } + + Assert.Single(requestTimeInQueueMetricPoints); + var requestTimeInQueueMetricPoint = requestTimeInQueueMetricPoints[0]; + + var count = requestTimeInQueueMetricPoint.GetHistogramCount(); + var sum = requestTimeInQueueMetricPoint.GetHistogramSum(); + + Assert.Equal(1L, count); + + // Inspect Metric Attributes + var requestTimeInQueueAttributes = new Dictionary(); + foreach (var tag in requestTimeInQueueMetricPoint.Tags) + { + requestTimeInQueueAttributes[tag.Key] = tag.Value; + } + + Assert.Equal(5, requestTimeInQueueAttributes.Count()); + + Assert.Contains(requestTimeInQueueAttributes, kvp => kvp.Key == SemanticConventions.AttributeUrlScheme && kvp.Value.ToString() == normalizedAttributesTestCase[SemanticConventions.AttributeHttpScheme]); + Assert.Contains(requestTimeInQueueAttributes, kvp => kvp.Key == SemanticConventions.AttributeHttpRequestMethod && kvp.Value.ToString() == normalizedAttributesTestCase[SemanticConventions.AttributeHttpMethod]); + Assert.Contains(requestTimeInQueueAttributes, kvp => kvp.Key == SemanticConventions.AttributeServerAddress && kvp.Value.ToString() == normalizedAttributesTestCase[SemanticConventions.AttributeNetPeerName]); + Assert.Contains(requestTimeInQueueAttributes, kvp => kvp.Key == SemanticConventions.AttributeServerPort && kvp.Value.ToString() == normalizedAttributesTestCase[SemanticConventions.AttributeNetPeerPort]); + + // See https://github.com/open-telemetry/opentelemetry-dotnet/issues/4928 + // Assert.Contains(attributes, kvp => kvp.Key == SemanticConventions.AttributeNetworkProtocolVersion && kvp.Value.ToString() == normalizedAttributesTestCase[SemanticConventions.AttributeHttpFlavor]); + } + + private void ValidateRequestDurationMetric(Metric requestDurationMetric, HttpTestData.HttpOutTestCase tc) + { + var normalizedAttributesTestCase = tc.SpanAttributes.ToDictionary(x => x.Key, x => HttpTestData.NormalizeValues(x.Value, this.host, this.port)); + + Assert.NotNull(requestDurationMetric); + Assert.Equal("s", requestDurationMetric.Unit); + Assert.True(requestDurationMetric.MetricType == MetricType.Histogram); + + var requestDurationMetricPoints = new List(); + foreach (var p in requestDurationMetric.GetMetricPoints()) + { + requestDurationMetricPoints.Add(p); + } + + Assert.Single(requestDurationMetricPoints); + var requestDurationMetricPoint = requestDurationMetricPoints[0]; + + var count = requestDurationMetricPoint.GetHistogramCount(); + var sum = requestDurationMetricPoint.GetHistogramSum(); + + Assert.Equal(1L, count); + + // Inspect Metric Attributes + var requestDurationAttributes = new Dictionary(); + foreach (var tag in requestDurationMetricPoint.Tags) + { + requestDurationAttributes[tag.Key] = tag.Value; + } + + var expectedRequestDurationAttributeCount = 5 + (tc.ResponseExpected ? 1 : 0); + + Assert.Equal(expectedRequestDurationAttributeCount, requestDurationAttributes.Count); + + Assert.Contains(requestDurationAttributes, kvp => kvp.Key == SemanticConventions.AttributeUrlScheme && kvp.Value.ToString() == normalizedAttributesTestCase[SemanticConventions.AttributeHttpScheme]); + Assert.Contains(requestDurationAttributes, kvp => kvp.Key == SemanticConventions.AttributeHttpRequestMethod && kvp.Value.ToString() == normalizedAttributesTestCase[SemanticConventions.AttributeHttpMethod]); + Assert.Contains(requestDurationAttributes, kvp => kvp.Key == SemanticConventions.AttributeServerAddress && kvp.Value.ToString() == normalizedAttributesTestCase[SemanticConventions.AttributeNetPeerName]); + Assert.Contains(requestDurationAttributes, kvp => kvp.Key == SemanticConventions.AttributeServerPort && kvp.Value.ToString() == normalizedAttributesTestCase[SemanticConventions.AttributeNetPeerPort]); + + // See https://github.com/open-telemetry/opentelemetry-dotnet/issues/4928 + // Assert.Contains(attributes, kvp => kvp.Key == SemanticConventions.AttributeNetworkProtocolVersion && kvp.Value.ToString() == normalizedAttributesTestCase[SemanticConventions.AttributeHttpFlavor]); + if (tc.ResponseExpected) + { + Assert.Contains(requestDurationAttributes, kvp => kvp.Key == SemanticConventions.AttributeHttpResponseStatusCode && kvp.Value.ToString() == normalizedAttributesTestCase[SemanticConventions.AttributeHttpStatusCode]); + } + else + { + Assert.DoesNotContain(requestDurationAttributes, kvp => kvp.Key == SemanticConventions.AttributeHttpResponseStatusCode); + } + + // Inspect Histogram Bounds + var histogramBuckets = requestDurationMetricPoint.GetHistogramBuckets(); + var histogramBounds = new List(); + foreach (var t in histogramBuckets) + { + histogramBounds.Add(t.ExplicitBound); + } + + Assert.Equal( + expected: new List { 0, 0.005, 0.01, 0.025, 0.05, 0.075, 0.1, 0.25, 0.5, 0.75, 1, 2.5, 5, 7.5, 10, double.PositiveInfinity }, + actual: histogramBounds); + } +#endif } From ad201391e66207acb40a43674692e1b1b3df3839 Mon Sep 17 00:00:00 2001 From: Vishwesh Bankwar Date: Fri, 6 Oct 2023 17:55:59 -0700 Subject: [PATCH 02/16] add hist bound validations --- .../HttpClientTests.cs | 62 ++++++++++++++----- 1 file changed, 48 insertions(+), 14 deletions(-) diff --git a/test/OpenTelemetry.Instrumentation.Http.Tests/HttpClientTests.cs b/test/OpenTelemetry.Instrumentation.Http.Tests/HttpClientTests.cs index c05994979a7..95563aac3d9 100644 --- a/test/OpenTelemetry.Instrumentation.Http.Tests/HttpClientTests.cs +++ b/test/OpenTelemetry.Instrumentation.Http.Tests/HttpClientTests.cs @@ -18,7 +18,7 @@ #if NETFRAMEWORK using System.Net.Http; #endif -#if !NET8_0 +#if !NET8_0_OR_GREATER using System.Reflection; using System.Text.Json; #endif @@ -35,7 +35,7 @@ public partial class HttpClientTests { public static readonly IEnumerable TestData = HttpTestData.ReadTestCases(); -#if !NET8_0 +#if !NET8_0_OR_GREATER [Theory] [MemberData(nameof(TestData))] public async Task HttpOutCallsAreCollectedSuccessfullyTracesAndMetricsOldSemanticConventionsAsync(HttpTestData.HttpOutTestCase tc) @@ -74,33 +74,31 @@ await HttpOutCallsAreCollectedSuccessfullyBodyAsync( enableMetrics: true, semanticConvention: HttpSemanticConvention.Dupe).ConfigureAwait(false); } -#endif [Theory] [MemberData(nameof(TestData))] - public async Task HttpOutCallsAreCollectedSuccessfullyTracesOnlyAsync(HttpTestData.HttpOutTestCase tc) + public async Task HttpOutCallsAreCollectedSuccessfullyMetricsOnlyAsync(HttpTestData.HttpOutTestCase tc) { await HttpOutCallsAreCollectedSuccessfullyBodyAsync( this.host, this.port, tc, - enableTracing: true, - enableMetrics: false).ConfigureAwait(false); + enableTracing: false, + enableMetrics: true).ConfigureAwait(false); } +#endif -#if !NET8_0 [Theory] [MemberData(nameof(TestData))] - public async Task HttpOutCallsAreCollectedSuccessfullyMetricsOnlyAsync(HttpTestData.HttpOutTestCase tc) + public async Task HttpOutCallsAreCollectedSuccessfullyTracesOnlyAsync(HttpTestData.HttpOutTestCase tc) { await HttpOutCallsAreCollectedSuccessfullyBodyAsync( this.host, this.port, tc, - enableTracing: false, - enableMetrics: true).ConfigureAwait(false); + enableTracing: true, + enableMetrics: false).ConfigureAwait(false); } -#endif [Theory] [MemberData(nameof(TestData))] @@ -114,7 +112,7 @@ await HttpOutCallsAreCollectedSuccessfullyBodyAsync( enableMetrics: false).ConfigureAwait(false); } -#if !NET8_0 +#if !NET8_0_OR_GREATER [Fact] public async Task DebugIndividualTestAsync() { @@ -156,7 +154,7 @@ public async Task CheckEnrichmentWhenSampling() await CheckEnrichment(new AlwaysOnSampler(), true, this.url).ConfigureAwait(false); } -#if NET8_0 +#if NET8_0_OR_GREATER [Theory] [MemberData(nameof(TestData))] public async Task ValidateNet8MetricsAsync(HttpTestData.HttpOutTestCase tc) @@ -445,7 +443,7 @@ private static async Task HttpOutCallsAreCollectedSuccessfullyBodyAsync( Assert.Single(requestMetrics); } -#if !NET8_0 +#if !NET8_0_OR_GREATER if (semanticConvention == null || semanticConvention.Value.HasFlag(HttpSemanticConvention.Old)) { var metric = requestMetrics.FirstOrDefault(m => m.Name == "http.client.duration"); @@ -764,6 +762,18 @@ private void ValidateHttpClientConnectionDurationMetric(Metric clientConnectionD // See https://github.com/open-telemetry/opentelemetry-dotnet/issues/4928 // Assert.Contains(attributes, kvp => kvp.Key == SemanticConventions.AttributeNetworkProtocolVersion && kvp.Value.ToString() == normalizedAttributesTestCase[SemanticConventions.AttributeHttpFlavor]); + + // Inspect Histogram Bounds + var histogramBuckets = clientConnectionDurationMetricPoint.GetHistogramBuckets(); + var histogramBounds = new List(); + foreach (var t in histogramBuckets) + { + histogramBounds.Add(t.ExplicitBound); + } + + Assert.Equal( + expected: new List { 0, 0.005, 0.01, 0.025, 0.05, 0.075, 0.1, 0.25, 0.5, 0.75, 1, 2.5, 5, 7.5, 10, double.PositiveInfinity }, + actual: histogramBounds); } private void ValidateDnsLookUpDurationMetric(Metric dnsLookUpDurationMetric, HttpTestData.HttpOutTestCase tc) @@ -798,6 +808,18 @@ private void ValidateDnsLookUpDurationMetric(Metric dnsLookUpDurationMetric, Htt Assert.Single(dnsLookUpDurationAttributes); Assert.Contains(dnsLookUpDurationAttributes, kvp => kvp.Key == "dns.question.name" && kvp.Value.ToString() == normalizedAttributesTestCase[SemanticConventions.AttributeNetPeerName]); + + // Inspect Histogram Bounds + var histogramBuckets = dnsLookUpDurationMetricMetricPoint.GetHistogramBuckets(); + var histogramBounds = new List(); + foreach (var t in histogramBuckets) + { + histogramBounds.Add(t.ExplicitBound); + } + + Assert.Equal( + expected: new List { 0, 0.005, 0.01, 0.025, 0.05, 0.075, 0.1, 0.25, 0.5, 0.75, 1, 2.5, 5, 7.5, 10, double.PositiveInfinity }, + actual: histogramBounds); } private void ValidateRequestTimeInQueueMetric(Metric requestTimeInQueueMetric, HttpTestData.HttpOutTestCase tc) @@ -838,6 +860,18 @@ private void ValidateRequestTimeInQueueMetric(Metric requestTimeInQueueMetric, H // See https://github.com/open-telemetry/opentelemetry-dotnet/issues/4928 // Assert.Contains(attributes, kvp => kvp.Key == SemanticConventions.AttributeNetworkProtocolVersion && kvp.Value.ToString() == normalizedAttributesTestCase[SemanticConventions.AttributeHttpFlavor]); + + // Inspect Histogram Bounds + var histogramBuckets = requestTimeInQueueMetricPoint.GetHistogramBuckets(); + var histogramBounds = new List(); + foreach (var t in histogramBuckets) + { + histogramBounds.Add(t.ExplicitBound); + } + + Assert.Equal( + expected: new List { 0, 0.005, 0.01, 0.025, 0.05, 0.075, 0.1, 0.25, 0.5, 0.75, 1, 2.5, 5, 7.5, 10, double.PositiveInfinity }, + actual: histogramBounds); } private void ValidateRequestDurationMetric(Metric requestDurationMetric, HttpTestData.HttpOutTestCase tc) From a983f953f6fefd6d6d7414e01685b3be6a6af237 Mon Sep 17 00:00:00 2001 From: Vishwesh Bankwar Date: Fri, 6 Oct 2023 17:57:54 -0700 Subject: [PATCH 03/16] nit --- .../OpenTelemetry.Instrumentation.Http.Tests/HttpClientTests.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/OpenTelemetry.Instrumentation.Http.Tests/HttpClientTests.cs b/test/OpenTelemetry.Instrumentation.Http.Tests/HttpClientTests.cs index 95563aac3d9..f7c8dd0c0c5 100644 --- a/test/OpenTelemetry.Instrumentation.Http.Tests/HttpClientTests.cs +++ b/test/OpenTelemetry.Instrumentation.Http.Tests/HttpClientTests.cs @@ -652,7 +652,7 @@ private static async Task CheckEnrichment(Sampler sampler, bool enrichExpected, } } -#if NET8_0 +#if NET8_0_OR_GREATER private void ValidateClientActiveRequests(Metric clientActiveRequests, HttpTestData.HttpOutTestCase tc) { var normalizedAttributesTestCase = tc.SpanAttributes.ToDictionary(x => x.Key, x => HttpTestData.NormalizeValues(x.Value, this.host, this.port)); From dafdc186c1e237e6b63fbf98b80239b677344102 Mon Sep 17 00:00:00 2001 From: Vishwesh Bankwar Date: Fri, 6 Oct 2023 18:07:40 -0700 Subject: [PATCH 04/16] changelog --- .../CHANGELOG.md | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/src/OpenTelemetry.Instrumentation.Http/CHANGELOG.md b/src/OpenTelemetry.Instrumentation.Http/CHANGELOG.md index 1443489e51d..d8adc2ad80a 100644 --- a/src/OpenTelemetry.Instrumentation.Http/CHANGELOG.md +++ b/src/OpenTelemetry.Instrumentation.Http/CHANGELOG.md @@ -34,6 +34,23 @@ `http.client.request.duration` metrics on .NET Framework for `HttpWebRequest`. ([#4870](https://github.com/open-telemetry/opentelemetry-dotnet/pull/4870)) +* Following `HttpClient` metrics will now be enabled by default when targeting + `.NET8.0` framework. + + * **Meter** : `System.Net.Http` + * `http.client.request.duration` + * `http.client.active_requests` + * `http.client.open_connections` + * `http.client.connection.duration` + * `http.client.request.time_in_queue` + + * **Meter** : `System.Net.NameResolution` + * `dns.lookup.duration` + + **NOTE**: Users can opt-out of metrics that are not required using + [views](https://github.com/open-telemetry/opentelemetry-dotnet/tree/main/docs/metrics/customizing-the-sdk#view). + [#4931](https://github.com/open-telemetry/opentelemetry-dotnet/pull/4931) + ## 1.5.1-beta.1 Released 2023-Jul-20 From ee69bb14a3d8a44a648e8fe2db025d8804f3d954 Mon Sep 17 00:00:00 2001 From: Vishwesh Bankwar Date: Fri, 6 Oct 2023 18:09:40 -0700 Subject: [PATCH 05/16] fix md --- src/OpenTelemetry.Instrumentation.Http/CHANGELOG.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/OpenTelemetry.Instrumentation.Http/CHANGELOG.md b/src/OpenTelemetry.Instrumentation.Http/CHANGELOG.md index d8adc2ad80a..b777bf883f8 100644 --- a/src/OpenTelemetry.Instrumentation.Http/CHANGELOG.md +++ b/src/OpenTelemetry.Instrumentation.Http/CHANGELOG.md @@ -43,10 +43,10 @@ * `http.client.open_connections` * `http.client.connection.duration` * `http.client.request.time_in_queue` - + * **Meter** : `System.Net.NameResolution` * `dns.lookup.duration` - + **NOTE**: Users can opt-out of metrics that are not required using [views](https://github.com/open-telemetry/opentelemetry-dotnet/tree/main/docs/metrics/customizing-the-sdk#view). [#4931](https://github.com/open-telemetry/opentelemetry-dotnet/pull/4931) From 348d04710e281cfd117cf00ca6b8be94beeb94e3 Mon Sep 17 00:00:00 2001 From: Vishwesh Bankwar Date: Tue, 10 Oct 2023 09:13:07 -0700 Subject: [PATCH 06/16] simplify tests --- .../HttpClientTests.cs | 320 +----------------- .../HttpTestData.cs | 4 - 2 files changed, 2 insertions(+), 322 deletions(-) diff --git a/test/OpenTelemetry.Instrumentation.Http.Tests/HttpClientTests.cs b/test/OpenTelemetry.Instrumentation.Http.Tests/HttpClientTests.cs index f7c8dd0c0c5..ca3d8fb631c 100644 --- a/test/OpenTelemetry.Instrumentation.Http.Tests/HttpClientTests.cs +++ b/test/OpenTelemetry.Instrumentation.Http.Tests/HttpClientTests.cs @@ -74,6 +74,7 @@ await HttpOutCallsAreCollectedSuccessfullyBodyAsync( enableMetrics: true, semanticConvention: HttpSemanticConvention.Dupe).ConfigureAwait(false); } +#endif [Theory] [MemberData(nameof(TestData))] @@ -86,7 +87,6 @@ await HttpOutCallsAreCollectedSuccessfullyBodyAsync( enableTracing: false, enableMetrics: true).ConfigureAwait(false); } -#endif [Theory] [MemberData(nameof(TestData))] @@ -208,35 +208,9 @@ public async Task ValidateNet8MetricsAsync(HttpTestData.HttpOutTestCase tc) } else { + // http.client.connection.duration and http.client.open_connections will not be emitted. Assert.Equal(4, requestMetrics.Count()); } - - // Validate http.client.request.duration metric - var requestDurationMetric = requestMetrics.FirstOrDefault(m => m.Name == "http.client.request.duration"); - this.ValidateRequestDurationMetric(requestDurationMetric, tc); - - // Validate http.client.request.time_in_queue metric - var requestTimeInQueueMetric = requestMetrics.FirstOrDefault(m => m.Name == "http.client.request.time_in_queue"); - this.ValidateRequestTimeInQueueMetric(requestTimeInQueueMetric, tc); - - // Validate dns.lookups.duration metric - var dnsLookUpDuration = requestMetrics.FirstOrDefault(m => m.Name == "dns.lookups.duration"); - this.ValidateDnsLookUpDurationMetric(dnsLookUpDuration, tc); - - if (tc.ResponseExpected) - { - // Validate http.client.connection.duration metric - var clientConnectionDuration = requestMetrics.FirstOrDefault(m => m.Name == "http.client.connection.duration"); - this.ValidateHttpClientConnectionDurationMetric(clientConnectionDuration, tc); - - // Validate http.client.open_connections metric - var clientOpenConnections = requestMetrics.FirstOrDefault(m => m.Name == "http.client.open_connections"); - this.ValidateClientOpenConnections(clientOpenConnections, tc); - } - - // Validate http.client.active_requests metric - var clientActiveRequests = requestMetrics.FirstOrDefault(m => m.Name == "http.client.active_requests"); - this.ValidateClientActiveRequests(clientActiveRequests, tc); } #endif @@ -302,11 +276,6 @@ private static async Task HttpOutCallsAreCollectedSuccessfullyBodyAsync( { RequestUri = new Uri(testUrl), Method = new HttpMethod(tc.Method), -#if NETFRAMEWORK - Version = new Version(1, 1), -#else - Version = new Version(2, 0), -#endif }; if (tc.Headers != null) @@ -651,289 +620,4 @@ private static async Task CheckEnrichment(Sampler sampler, bool enrichExpected, Assert.False(enrichWithHttpResponseMessageCalled); } } - -#if NET8_0_OR_GREATER - private void ValidateClientActiveRequests(Metric clientActiveRequests, HttpTestData.HttpOutTestCase tc) - { - var normalizedAttributesTestCase = tc.SpanAttributes.ToDictionary(x => x.Key, x => HttpTestData.NormalizeValues(x.Value, this.host, this.port)); - - Assert.NotNull(clientActiveRequests); - Assert.Equal("{request}", clientActiveRequests.Unit); - Assert.True(clientActiveRequests.MetricType == MetricType.LongSumNonMonotonic); - - var clientActiveRequestsMetricPoints = new List(); - foreach (var p in clientActiveRequests.GetMetricPoints()) - { - clientActiveRequestsMetricPoints.Add(p); - } - - Assert.Single(clientActiveRequestsMetricPoints); - var clientActiveRequestsMetricPoint = clientActiveRequestsMetricPoints[0]; - - var clientActiveRequestsAttributes = new Dictionary(); - foreach (var tag in clientActiveRequestsMetricPoint.Tags) - { - clientActiveRequestsAttributes[tag.Key] = tag.Value; - } - - Assert.Equal(4, clientActiveRequestsAttributes.Count()); - - Assert.Contains(clientActiveRequestsAttributes, kvp => kvp.Key == SemanticConventions.AttributeUrlScheme && kvp.Value.ToString() == normalizedAttributesTestCase[SemanticConventions.AttributeHttpScheme]); - Assert.Contains(clientActiveRequestsAttributes, kvp => kvp.Key == SemanticConventions.AttributeHttpRequestMethod && kvp.Value.ToString() == normalizedAttributesTestCase[SemanticConventions.AttributeHttpMethod]); - Assert.Contains(clientActiveRequestsAttributes, kvp => kvp.Key == SemanticConventions.AttributeServerAddress && kvp.Value.ToString() == normalizedAttributesTestCase[SemanticConventions.AttributeNetPeerName]); - Assert.Contains(clientActiveRequestsAttributes, kvp => kvp.Key == SemanticConventions.AttributeServerPort && kvp.Value.ToString() == normalizedAttributesTestCase[SemanticConventions.AttributeNetPeerPort]); - } - - private void ValidateClientOpenConnections(Metric clientOpenConnections, HttpTestData.HttpOutTestCase tc) - { - var normalizedAttributesTestCase = tc.SpanAttributes.ToDictionary(x => x.Key, x => HttpTestData.NormalizeValues(x.Value, this.host, this.port)); - - Assert.NotNull(clientOpenConnections); - Assert.Equal("{connection}", clientOpenConnections.Unit); - Assert.True(clientOpenConnections.MetricType == MetricType.LongSumNonMonotonic); - - var clientOpenConnectionsMetricPoints = new List(); - foreach (var p in clientOpenConnections.GetMetricPoints()) - { - clientOpenConnectionsMetricPoints.Add(p); - } - - // Two metricPoints - // one for active connections - // one for idle connetions - Assert.Equal(2, clientOpenConnectionsMetricPoints.Count()); - - clientOpenConnectionsMetricPoints.ForEach((clientOpenConnectionsMetricPoint) => - { - var clientOpenConnectionsAttributes = new Dictionary(); - foreach (var tag in clientOpenConnectionsMetricPoint.Tags) - { - clientOpenConnectionsAttributes[tag.Key] = tag.Value; - } - - Assert.Equal(6, clientOpenConnectionsAttributes.Count()); - - Assert.Contains(clientOpenConnectionsAttributes, kvp => kvp.Key == "http.connection.state" && (kvp.Value.ToString() == "idle" || kvp.Value.ToString() == "active")); - Assert.Contains(clientOpenConnectionsAttributes, kvp => kvp.Key == SemanticConventions.AttributeUrlScheme && kvp.Value.ToString() == normalizedAttributesTestCase[SemanticConventions.AttributeHttpScheme]); - Assert.Contains(clientOpenConnectionsAttributes, kvp => kvp.Key == SemanticConventions.AttributeServerAddress && kvp.Value.ToString() == normalizedAttributesTestCase[SemanticConventions.AttributeNetPeerName]); - Assert.Contains(clientOpenConnectionsAttributes, kvp => kvp.Key == SemanticConventions.AttributeServerPort && kvp.Value.ToString() == normalizedAttributesTestCase[SemanticConventions.AttributeNetPeerPort]); - Assert.Contains(clientOpenConnectionsAttributes, kvp => kvp.Key == SemanticConventions.AttributeServerSocketAddress && kvp.Value.ToString() == "::1"); - - // See https://github.com/open-telemetry/opentelemetry-dotnet/issues/4928 - // Assert.Contains(attributes, kvp => kvp.Key == SemanticConventions.AttributeNetworkProtocolVersion && kvp.Value.ToString() == normalizedAttributesTestCase[SemanticConventions.AttributeHttpFlavor]); - }); - } - - private void ValidateHttpClientConnectionDurationMetric(Metric clientConnectionDuration, HttpTestData.HttpOutTestCase tc) - { - var normalizedAttributesTestCase = tc.SpanAttributes.ToDictionary(x => x.Key, x => HttpTestData.NormalizeValues(x.Value, this.host, this.port)); - - Assert.NotNull(clientConnectionDuration); - Assert.Equal("s", clientConnectionDuration.Unit); - Assert.True(clientConnectionDuration.MetricType == MetricType.Histogram); - - var clientConnectionDurationMetricMetricPoints = new List(); - foreach (var p in clientConnectionDuration.GetMetricPoints()) - { - clientConnectionDurationMetricMetricPoints.Add(p); - } - - Assert.Single(clientConnectionDurationMetricMetricPoints); - var clientConnectionDurationMetricPoint = clientConnectionDurationMetricMetricPoints[0]; - - var count = clientConnectionDurationMetricPoint.GetHistogramCount(); - var sum = clientConnectionDurationMetricPoint.GetHistogramSum(); - - Assert.Equal(1L, count); - - // Inspect Metric Attributes - var clientConnectionDurationAttributes = new Dictionary(); - foreach (var tag in clientConnectionDurationMetricPoint.Tags) - { - clientConnectionDurationAttributes[tag.Key] = tag.Value; - } - - Assert.Equal(5, clientConnectionDurationAttributes.Count()); - - Assert.Contains(clientConnectionDurationAttributes, kvp => kvp.Key == SemanticConventions.AttributeUrlScheme && kvp.Value.ToString() == normalizedAttributesTestCase[SemanticConventions.AttributeHttpScheme]); - Assert.Contains(clientConnectionDurationAttributes, kvp => kvp.Key == SemanticConventions.AttributeServerAddress && kvp.Value.ToString() == normalizedAttributesTestCase[SemanticConventions.AttributeNetPeerName]); - Assert.Contains(clientConnectionDurationAttributes, kvp => kvp.Key == SemanticConventions.AttributeServerPort && kvp.Value.ToString() == normalizedAttributesTestCase[SemanticConventions.AttributeNetPeerPort]); - Assert.Contains(clientConnectionDurationAttributes, kvp => kvp.Key == SemanticConventions.AttributeServerSocketAddress && kvp.Value.ToString() == "::1"); - - // See https://github.com/open-telemetry/opentelemetry-dotnet/issues/4928 - // Assert.Contains(attributes, kvp => kvp.Key == SemanticConventions.AttributeNetworkProtocolVersion && kvp.Value.ToString() == normalizedAttributesTestCase[SemanticConventions.AttributeHttpFlavor]); - - // Inspect Histogram Bounds - var histogramBuckets = clientConnectionDurationMetricPoint.GetHistogramBuckets(); - var histogramBounds = new List(); - foreach (var t in histogramBuckets) - { - histogramBounds.Add(t.ExplicitBound); - } - - Assert.Equal( - expected: new List { 0, 0.005, 0.01, 0.025, 0.05, 0.075, 0.1, 0.25, 0.5, 0.75, 1, 2.5, 5, 7.5, 10, double.PositiveInfinity }, - actual: histogramBounds); - } - - private void ValidateDnsLookUpDurationMetric(Metric dnsLookUpDurationMetric, HttpTestData.HttpOutTestCase tc) - { - var normalizedAttributesTestCase = tc.SpanAttributes.ToDictionary(x => x.Key, x => HttpTestData.NormalizeValues(x.Value, this.host, this.port)); - - Assert.NotNull(dnsLookUpDurationMetric); - Assert.Equal("s", dnsLookUpDurationMetric.Unit); - Assert.True(dnsLookUpDurationMetric.MetricType == MetricType.Histogram); - - var dnsLookUpDurationMetricMetricPoints = new List(); - foreach (var p in dnsLookUpDurationMetric.GetMetricPoints()) - { - dnsLookUpDurationMetricMetricPoints.Add(p); - } - - Assert.Single(dnsLookUpDurationMetricMetricPoints); - var dnsLookUpDurationMetricMetricPoint = dnsLookUpDurationMetricMetricPoints[0]; - - var count = dnsLookUpDurationMetricMetricPoint.GetHistogramCount(); - var sum = dnsLookUpDurationMetricMetricPoint.GetHistogramSum(); - - Assert.Equal(1L, count); - - // Inspect Metric Attributes - var dnsLookUpDurationAttributes = new Dictionary(); - foreach (var tag in dnsLookUpDurationMetricMetricPoint.Tags) - { - dnsLookUpDurationAttributes[tag.Key] = tag.Value; - } - - Assert.Single(dnsLookUpDurationAttributes); - - Assert.Contains(dnsLookUpDurationAttributes, kvp => kvp.Key == "dns.question.name" && kvp.Value.ToString() == normalizedAttributesTestCase[SemanticConventions.AttributeNetPeerName]); - - // Inspect Histogram Bounds - var histogramBuckets = dnsLookUpDurationMetricMetricPoint.GetHistogramBuckets(); - var histogramBounds = new List(); - foreach (var t in histogramBuckets) - { - histogramBounds.Add(t.ExplicitBound); - } - - Assert.Equal( - expected: new List { 0, 0.005, 0.01, 0.025, 0.05, 0.075, 0.1, 0.25, 0.5, 0.75, 1, 2.5, 5, 7.5, 10, double.PositiveInfinity }, - actual: histogramBounds); - } - - private void ValidateRequestTimeInQueueMetric(Metric requestTimeInQueueMetric, HttpTestData.HttpOutTestCase tc) - { - var normalizedAttributesTestCase = tc.SpanAttributes.ToDictionary(x => x.Key, x => HttpTestData.NormalizeValues(x.Value, this.host, this.port)); - - Assert.NotNull(requestTimeInQueueMetric); - Assert.Equal("s", requestTimeInQueueMetric.Unit); - Assert.True(requestTimeInQueueMetric.MetricType == MetricType.Histogram); - - var requestTimeInQueueMetricPoints = new List(); - foreach (var p in requestTimeInQueueMetric.GetMetricPoints()) - { - requestTimeInQueueMetricPoints.Add(p); - } - - Assert.Single(requestTimeInQueueMetricPoints); - var requestTimeInQueueMetricPoint = requestTimeInQueueMetricPoints[0]; - - var count = requestTimeInQueueMetricPoint.GetHistogramCount(); - var sum = requestTimeInQueueMetricPoint.GetHistogramSum(); - - Assert.Equal(1L, count); - - // Inspect Metric Attributes - var requestTimeInQueueAttributes = new Dictionary(); - foreach (var tag in requestTimeInQueueMetricPoint.Tags) - { - requestTimeInQueueAttributes[tag.Key] = tag.Value; - } - - Assert.Equal(5, requestTimeInQueueAttributes.Count()); - - Assert.Contains(requestTimeInQueueAttributes, kvp => kvp.Key == SemanticConventions.AttributeUrlScheme && kvp.Value.ToString() == normalizedAttributesTestCase[SemanticConventions.AttributeHttpScheme]); - Assert.Contains(requestTimeInQueueAttributes, kvp => kvp.Key == SemanticConventions.AttributeHttpRequestMethod && kvp.Value.ToString() == normalizedAttributesTestCase[SemanticConventions.AttributeHttpMethod]); - Assert.Contains(requestTimeInQueueAttributes, kvp => kvp.Key == SemanticConventions.AttributeServerAddress && kvp.Value.ToString() == normalizedAttributesTestCase[SemanticConventions.AttributeNetPeerName]); - Assert.Contains(requestTimeInQueueAttributes, kvp => kvp.Key == SemanticConventions.AttributeServerPort && kvp.Value.ToString() == normalizedAttributesTestCase[SemanticConventions.AttributeNetPeerPort]); - - // See https://github.com/open-telemetry/opentelemetry-dotnet/issues/4928 - // Assert.Contains(attributes, kvp => kvp.Key == SemanticConventions.AttributeNetworkProtocolVersion && kvp.Value.ToString() == normalizedAttributesTestCase[SemanticConventions.AttributeHttpFlavor]); - - // Inspect Histogram Bounds - var histogramBuckets = requestTimeInQueueMetricPoint.GetHistogramBuckets(); - var histogramBounds = new List(); - foreach (var t in histogramBuckets) - { - histogramBounds.Add(t.ExplicitBound); - } - - Assert.Equal( - expected: new List { 0, 0.005, 0.01, 0.025, 0.05, 0.075, 0.1, 0.25, 0.5, 0.75, 1, 2.5, 5, 7.5, 10, double.PositiveInfinity }, - actual: histogramBounds); - } - - private void ValidateRequestDurationMetric(Metric requestDurationMetric, HttpTestData.HttpOutTestCase tc) - { - var normalizedAttributesTestCase = tc.SpanAttributes.ToDictionary(x => x.Key, x => HttpTestData.NormalizeValues(x.Value, this.host, this.port)); - - Assert.NotNull(requestDurationMetric); - Assert.Equal("s", requestDurationMetric.Unit); - Assert.True(requestDurationMetric.MetricType == MetricType.Histogram); - - var requestDurationMetricPoints = new List(); - foreach (var p in requestDurationMetric.GetMetricPoints()) - { - requestDurationMetricPoints.Add(p); - } - - Assert.Single(requestDurationMetricPoints); - var requestDurationMetricPoint = requestDurationMetricPoints[0]; - - var count = requestDurationMetricPoint.GetHistogramCount(); - var sum = requestDurationMetricPoint.GetHistogramSum(); - - Assert.Equal(1L, count); - - // Inspect Metric Attributes - var requestDurationAttributes = new Dictionary(); - foreach (var tag in requestDurationMetricPoint.Tags) - { - requestDurationAttributes[tag.Key] = tag.Value; - } - - var expectedRequestDurationAttributeCount = 5 + (tc.ResponseExpected ? 1 : 0); - - Assert.Equal(expectedRequestDurationAttributeCount, requestDurationAttributes.Count); - - Assert.Contains(requestDurationAttributes, kvp => kvp.Key == SemanticConventions.AttributeUrlScheme && kvp.Value.ToString() == normalizedAttributesTestCase[SemanticConventions.AttributeHttpScheme]); - Assert.Contains(requestDurationAttributes, kvp => kvp.Key == SemanticConventions.AttributeHttpRequestMethod && kvp.Value.ToString() == normalizedAttributesTestCase[SemanticConventions.AttributeHttpMethod]); - Assert.Contains(requestDurationAttributes, kvp => kvp.Key == SemanticConventions.AttributeServerAddress && kvp.Value.ToString() == normalizedAttributesTestCase[SemanticConventions.AttributeNetPeerName]); - Assert.Contains(requestDurationAttributes, kvp => kvp.Key == SemanticConventions.AttributeServerPort && kvp.Value.ToString() == normalizedAttributesTestCase[SemanticConventions.AttributeNetPeerPort]); - - // See https://github.com/open-telemetry/opentelemetry-dotnet/issues/4928 - // Assert.Contains(attributes, kvp => kvp.Key == SemanticConventions.AttributeNetworkProtocolVersion && kvp.Value.ToString() == normalizedAttributesTestCase[SemanticConventions.AttributeHttpFlavor]); - if (tc.ResponseExpected) - { - Assert.Contains(requestDurationAttributes, kvp => kvp.Key == SemanticConventions.AttributeHttpResponseStatusCode && kvp.Value.ToString() == normalizedAttributesTestCase[SemanticConventions.AttributeHttpStatusCode]); - } - else - { - Assert.DoesNotContain(requestDurationAttributes, kvp => kvp.Key == SemanticConventions.AttributeHttpResponseStatusCode); - } - - // Inspect Histogram Bounds - var histogramBuckets = requestDurationMetricPoint.GetHistogramBuckets(); - var histogramBounds = new List(); - foreach (var t in histogramBuckets) - { - histogramBounds.Add(t.ExplicitBound); - } - - Assert.Equal( - expected: new List { 0, 0.005, 0.01, 0.025, 0.05, 0.075, 0.1, 0.25, 0.5, 0.75, 1, 2.5, 5, 7.5, 10, double.PositiveInfinity }, - actual: histogramBounds); - } -#endif } diff --git a/test/OpenTelemetry.Instrumentation.Http.Tests/HttpTestData.cs b/test/OpenTelemetry.Instrumentation.Http.Tests/HttpTestData.cs index 111d4f16769..86daf630db5 100644 --- a/test/OpenTelemetry.Instrumentation.Http.Tests/HttpTestData.cs +++ b/test/OpenTelemetry.Instrumentation.Http.Tests/HttpTestData.cs @@ -49,11 +49,7 @@ public static string NormalizeValues(string value, string host, int port) return value .Replace("{host}", host) .Replace("{port}", port.ToString()) -#if NETFRAMEWORK .Replace("{flavor}", "1.1"); -#else - .Replace("{flavor}", "2.0"); -#endif } public class HttpOutTestCase From f3ef10d998c7363436083cb46cc7ae331329c709 Mon Sep 17 00:00:00 2001 From: Vishwesh Bankwar Date: Tue, 10 Oct 2023 09:15:35 -0700 Subject: [PATCH 07/16] fix typo --- src/OpenTelemetry.Instrumentation.Http/CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/OpenTelemetry.Instrumentation.Http/CHANGELOG.md b/src/OpenTelemetry.Instrumentation.Http/CHANGELOG.md index b777bf883f8..b721a078d6b 100644 --- a/src/OpenTelemetry.Instrumentation.Http/CHANGELOG.md +++ b/src/OpenTelemetry.Instrumentation.Http/CHANGELOG.md @@ -45,7 +45,7 @@ * `http.client.request.time_in_queue` * **Meter** : `System.Net.NameResolution` - * `dns.lookup.duration` + * `dns.lookups.duration` **NOTE**: Users can opt-out of metrics that are not required using [views](https://github.com/open-telemetry/opentelemetry-dotnet/tree/main/docs/metrics/customizing-the-sdk#view). From c44b7707957aabcde6dc9191f16f171030e012f2 Mon Sep 17 00:00:00 2001 From: Vishwesh Bankwar Date: Tue, 10 Oct 2023 13:16:57 -0700 Subject: [PATCH 08/16] Update src/OpenTelemetry.Instrumentation.Http/CHANGELOG.md Co-authored-by: Cijo Thomas --- src/OpenTelemetry.Instrumentation.Http/CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/OpenTelemetry.Instrumentation.Http/CHANGELOG.md b/src/OpenTelemetry.Instrumentation.Http/CHANGELOG.md index b721a078d6b..13e3a8d2969 100644 --- a/src/OpenTelemetry.Instrumentation.Http/CHANGELOG.md +++ b/src/OpenTelemetry.Instrumentation.Http/CHANGELOG.md @@ -35,7 +35,7 @@ ([#4870](https://github.com/open-telemetry/opentelemetry-dotnet/pull/4870)) * Following `HttpClient` metrics will now be enabled by default when targeting - `.NET8.0` framework. + `.NET8.0` framework or newer. * **Meter** : `System.Net.Http` * `http.client.request.duration` From 5deb9f0568bd9e6600251d3cddb6ef7ec433fa05 Mon Sep 17 00:00:00 2001 From: Vishwesh Bankwar Date: Wed, 18 Oct 2023 09:38:42 -0700 Subject: [PATCH 09/16] address feedback --- .../CHANGELOG.md | 14 +++++++++++--- .../HttpClientTests.cs | 1 - 2 files changed, 11 insertions(+), 4 deletions(-) diff --git a/src/OpenTelemetry.Instrumentation.Http/CHANGELOG.md b/src/OpenTelemetry.Instrumentation.Http/CHANGELOG.md index 13e3a8d2969..980ac500cc5 100644 --- a/src/OpenTelemetry.Instrumentation.Http/CHANGELOG.md +++ b/src/OpenTelemetry.Instrumentation.Http/CHANGELOG.md @@ -47,9 +47,17 @@ * **Meter** : `System.Net.NameResolution` * `dns.lookups.duration` - **NOTE**: Users can opt-out of metrics that are not required using - [views](https://github.com/open-telemetry/opentelemetry-dotnet/tree/main/docs/metrics/customizing-the-sdk#view). - [#4931](https://github.com/open-telemetry/opentelemetry-dotnet/pull/4931) + **NOTES**: + * When targeting `.NET8.0` framework, `http.client.request.duration` metric + will only follow + [v1.22.0](https://github.com/open-telemetry/semantic-conventions/blob/v1.22.0/docs/http/http-metrics.md#metric-httpclientrequestduration) + semantic conventions specification. Ability to switch behavior to older + conventions using `OTEL_SEMCONV_STABILITY_OPT_IN` environment variable is + not available. + * Users can opt-out of metrics that are not required using + [views](https://github.com/open-telemetry/opentelemetry-dotnet/tree/main/docs/metrics/customizing-the-sdk#drop-an-instrument). + + ([#4931](https://github.com/open-telemetry/opentelemetry-dotnet/pull/4931)) ## 1.5.1-beta.1 diff --git a/test/OpenTelemetry.Instrumentation.Http.Tests/HttpClientTests.cs b/test/OpenTelemetry.Instrumentation.Http.Tests/HttpClientTests.cs index ca3d8fb631c..4629e9a036f 100644 --- a/test/OpenTelemetry.Instrumentation.Http.Tests/HttpClientTests.cs +++ b/test/OpenTelemetry.Instrumentation.Http.Tests/HttpClientTests.cs @@ -174,7 +174,6 @@ public async Task ValidateNet8MetricsAsync(HttpTestData.HttpOutTestCase tc) { RequestUri = new Uri(testUrl), Method = new HttpMethod(tc.Method), - Version = new Version(2, 0), }; request.Headers.Add("contextRequired", "false"); From 10ca63f06f408701297ccfa7d6fde1d01788d36a Mon Sep 17 00:00:00 2001 From: Vishwesh Bankwar Date: Thu, 19 Oct 2023 11:01:47 -0700 Subject: [PATCH 10/16] use preproc directive for net8 --- .../MeterProviderBuilderExtensions.cs | 17 +++++++---------- 1 file changed, 7 insertions(+), 10 deletions(-) diff --git a/src/OpenTelemetry.Instrumentation.Http/MeterProviderBuilderExtensions.cs b/src/OpenTelemetry.Instrumentation.Http/MeterProviderBuilderExtensions.cs index a367ff74a9a..b20c6d51c1e 100644 --- a/src/OpenTelemetry.Instrumentation.Http/MeterProviderBuilderExtensions.cs +++ b/src/OpenTelemetry.Instrumentation.Http/MeterProviderBuilderExtensions.cs @@ -14,8 +14,10 @@ // limitations under the License. // +#if !NET8_0_OR_GREATER using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Options; +#endif using OpenTelemetry.Instrumentation.Http; using OpenTelemetry.Instrumentation.Http.Implementation; using OpenTelemetry.Internal; @@ -63,19 +65,14 @@ public static MeterProviderBuilder AddHttpClientInstrumentation( HttpWebRequestActivitySource.MetricsOptions = options; }); } +#elif NET8_0_OR_GREATER + builder.AddMeter("System.Net.Http"); + builder.AddMeter("System.Net.NameResolution"); #else - if (HttpHandlerMetricsDiagnosticListener.IsNet8OrGreater) - { - builder.AddMeter("System.Net.Http"); - builder.AddMeter("System.Net.NameResolution"); - } - else - { - builder.AddMeter(HttpHandlerMetricsDiagnosticListener.MeterName); + builder.AddMeter(HttpHandlerMetricsDiagnosticListener.MeterName); - builder.AddInstrumentation(sp => new HttpClientMetrics( + builder.AddInstrumentation(sp => new HttpClientMetrics( sp.GetRequiredService>().Get(Options.DefaultName))); - } #endif return builder; } From 825ef1d3a946c4a953274f8068b970bab4f3b6f2 Mon Sep 17 00:00:00 2001 From: Vishwesh Bankwar Date: Thu, 19 Oct 2023 14:29:28 -0700 Subject: [PATCH 11/16] fix indent --- .../MeterProviderBuilderExtensions.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/OpenTelemetry.Instrumentation.Http/MeterProviderBuilderExtensions.cs b/src/OpenTelemetry.Instrumentation.Http/MeterProviderBuilderExtensions.cs index b20c6d51c1e..6c466e2ae06 100644 --- a/src/OpenTelemetry.Instrumentation.Http/MeterProviderBuilderExtensions.cs +++ b/src/OpenTelemetry.Instrumentation.Http/MeterProviderBuilderExtensions.cs @@ -72,7 +72,7 @@ public static MeterProviderBuilder AddHttpClientInstrumentation( builder.AddMeter(HttpHandlerMetricsDiagnosticListener.MeterName); builder.AddInstrumentation(sp => new HttpClientMetrics( - sp.GetRequiredService>().Get(Options.DefaultName))); + sp.GetRequiredService>().Get(Options.DefaultName))); #endif return builder; } From 27b92c72457bbec17d308a24e4b77f1534b222f3 Mon Sep 17 00:00:00 2001 From: Vishwesh Bankwar Date: Thu, 19 Oct 2023 14:31:11 -0700 Subject: [PATCH 12/16] rmv unused --- .../HttpHandlerMetricsDiagnosticListener.cs | 13 ------------- 1 file changed, 13 deletions(-) diff --git a/src/OpenTelemetry.Instrumentation.Http/Implementation/HttpHandlerMetricsDiagnosticListener.cs b/src/OpenTelemetry.Instrumentation.Http/Implementation/HttpHandlerMetricsDiagnosticListener.cs index 71e989d5b74..f43e3c586de 100644 --- a/src/OpenTelemetry.Instrumentation.Http/Implementation/HttpHandlerMetricsDiagnosticListener.cs +++ b/src/OpenTelemetry.Instrumentation.Http/Implementation/HttpHandlerMetricsDiagnosticListener.cs @@ -31,7 +31,6 @@ namespace OpenTelemetry.Instrumentation.Http.Implementation; internal sealed class HttpHandlerMetricsDiagnosticListener : ListenerHandler { internal const string OnStopEvent = "System.Net.Http.HttpRequestOut.Stop"; - internal static readonly bool IsNet8OrGreater; internal static readonly AssemblyName AssemblyName = typeof(HttpClientMetrics).Assembly.GetName(); internal static readonly string MeterName = AssemblyName.Name; @@ -46,18 +45,6 @@ internal sealed class HttpHandlerMetricsDiagnosticListener : ListenerHandler private readonly bool emitOldAttributes; private readonly bool emitNewAttributes; - static HttpHandlerMetricsDiagnosticListener() - { - try - { - IsNet8OrGreater = typeof(HttpClient).Assembly.GetName().Version.Major >= 8; - } - catch (Exception) - { - IsNet8OrGreater = false; - } - } - public HttpHandlerMetricsDiagnosticListener(string name, HttpClientMetricInstrumentationOptions options) : base(name) { From 2b03b156512470b4960993e2b944ad7e45944572 Mon Sep 17 00:00:00 2001 From: Vishwesh Bankwar Date: Thu, 19 Oct 2023 14:38:37 -0700 Subject: [PATCH 13/16] refactor --- .../MeterProviderBuilderExtensions.cs | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/src/OpenTelemetry.Instrumentation.Http/MeterProviderBuilderExtensions.cs b/src/OpenTelemetry.Instrumentation.Http/MeterProviderBuilderExtensions.cs index 6c466e2ae06..1699d9d4bbe 100644 --- a/src/OpenTelemetry.Instrumentation.Http/MeterProviderBuilderExtensions.cs +++ b/src/OpenTelemetry.Instrumentation.Http/MeterProviderBuilderExtensions.cs @@ -39,6 +39,11 @@ public static MeterProviderBuilder AddHttpClientInstrumentation( { Guard.ThrowIfNull(builder); +#if NET8_0_OR_GREATER + return builder + .AddMeter("System.Net.Http"); + .AddMeter("System.Net.NameResolution"); +#else // Note: Warm-up the status code mapping. _ = TelemetryHelper.BoxedStatusCodes; @@ -65,9 +70,6 @@ public static MeterProviderBuilder AddHttpClientInstrumentation( HttpWebRequestActivitySource.MetricsOptions = options; }); } -#elif NET8_0_OR_GREATER - builder.AddMeter("System.Net.Http"); - builder.AddMeter("System.Net.NameResolution"); #else builder.AddMeter(HttpHandlerMetricsDiagnosticListener.MeterName); @@ -75,5 +77,6 @@ public static MeterProviderBuilder AddHttpClientInstrumentation( sp.GetRequiredService>().Get(Options.DefaultName))); #endif return builder; +#endif } } From b4782f3c99cbd92cd7e5cbb141214015abbc9c01 Mon Sep 17 00:00:00 2001 From: Vishwesh Bankwar Date: Thu, 19 Oct 2023 14:44:55 -0700 Subject: [PATCH 14/16] fix preprocessor --- .../MeterProviderBuilderExtensions.cs | 11 +++-------- 1 file changed, 3 insertions(+), 8 deletions(-) diff --git a/src/OpenTelemetry.Instrumentation.Http/MeterProviderBuilderExtensions.cs b/src/OpenTelemetry.Instrumentation.Http/MeterProviderBuilderExtensions.cs index 1699d9d4bbe..6c14bc14b3d 100644 --- a/src/OpenTelemetry.Instrumentation.Http/MeterProviderBuilderExtensions.cs +++ b/src/OpenTelemetry.Instrumentation.Http/MeterProviderBuilderExtensions.cs @@ -17,9 +17,10 @@ #if !NET8_0_OR_GREATER using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Options; -#endif using OpenTelemetry.Instrumentation.Http; using OpenTelemetry.Instrumentation.Http.Implementation; +#endif + using OpenTelemetry.Internal; namespace OpenTelemetry.Metrics; @@ -41,7 +42,7 @@ public static MeterProviderBuilder AddHttpClientInstrumentation( #if NET8_0_OR_GREATER return builder - .AddMeter("System.Net.Http"); + .AddMeter("System.Net.Http") .AddMeter("System.Net.NameResolution"); #else // Note: Warm-up the status code mapping. @@ -52,12 +53,6 @@ public static MeterProviderBuilder AddHttpClientInstrumentation( services.RegisterOptionsFactory(configuration => new HttpClientMetricInstrumentationOptions(configuration)); }); - // TODO: Handle HttpClientMetricInstrumentationOptions - // SetHttpFlavor - seems like this would be handled by views - // Filter - makes sense for metric instrumentation - // Enrich - do we want a similar kind of functionality for metrics? - // RecordException - probably doesn't make sense for metric instrumentation - #if NETFRAMEWORK builder.AddMeter(HttpWebRequestActivitySource.MeterName); From 5a39ff125505f4e7fa797b8c836f6886bc18fb87 Mon Sep 17 00:00:00 2001 From: Vishwesh Bankwar Date: Fri, 20 Oct 2023 11:31:33 -0700 Subject: [PATCH 15/16] Update src/OpenTelemetry.Instrumentation.Http/CHANGELOG.md Co-authored-by: Cijo Thomas --- src/OpenTelemetry.Instrumentation.Http/CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/OpenTelemetry.Instrumentation.Http/CHANGELOG.md b/src/OpenTelemetry.Instrumentation.Http/CHANGELOG.md index 980ac500cc5..aca6e2f52a8 100644 --- a/src/OpenTelemetry.Instrumentation.Http/CHANGELOG.md +++ b/src/OpenTelemetry.Instrumentation.Http/CHANGELOG.md @@ -48,7 +48,7 @@ * `dns.lookups.duration` **NOTES**: - * When targeting `.NET8.0` framework, `http.client.request.duration` metric + * When targeting `.NET8.0` framework or newer, `http.client.request.duration` metric will only follow [v1.22.0](https://github.com/open-telemetry/semantic-conventions/blob/v1.22.0/docs/http/http-metrics.md#metric-httpclientrequestduration) semantic conventions specification. Ability to switch behavior to older From 3e23030e4af7e620ade75a429e54cf73429d1814 Mon Sep 17 00:00:00 2001 From: Vishwesh Bankwar Date: Fri, 20 Oct 2023 12:35:48 -0700 Subject: [PATCH 16/16] address feedback and add links to changelog --- src/OpenTelemetry.Instrumentation.Http/CHANGELOG.md | 4 ++++ .../MeterProviderBuilderExtensions.cs | 4 ++-- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/src/OpenTelemetry.Instrumentation.Http/CHANGELOG.md b/src/OpenTelemetry.Instrumentation.Http/CHANGELOG.md index aca6e2f52a8..de1a8224da4 100644 --- a/src/OpenTelemetry.Instrumentation.Http/CHANGELOG.md +++ b/src/OpenTelemetry.Instrumentation.Http/CHANGELOG.md @@ -47,6 +47,10 @@ * **Meter** : `System.Net.NameResolution` * `dns.lookups.duration` + For details about each individual metric check [System.Net metrics + docs + page](https://learn.microsoft.com/dotnet/core/diagnostics/built-in-metrics-system-net). + **NOTES**: * When targeting `.NET8.0` framework or newer, `http.client.request.duration` metric will only follow diff --git a/src/OpenTelemetry.Instrumentation.Http/MeterProviderBuilderExtensions.cs b/src/OpenTelemetry.Instrumentation.Http/MeterProviderBuilderExtensions.cs index 6c14bc14b3d..0890a662c8b 100644 --- a/src/OpenTelemetry.Instrumentation.Http/MeterProviderBuilderExtensions.cs +++ b/src/OpenTelemetry.Instrumentation.Http/MeterProviderBuilderExtensions.cs @@ -42,8 +42,8 @@ public static MeterProviderBuilder AddHttpClientInstrumentation( #if NET8_0_OR_GREATER return builder - .AddMeter("System.Net.Http") - .AddMeter("System.Net.NameResolution"); + .AddMeter("System.Net.Http") + .AddMeter("System.Net.NameResolution"); #else // Note: Warm-up the status code mapping. _ = TelemetryHelper.BoxedStatusCodes;