diff --git a/src/libraries/System.Net.Http/src/System/Net/Http/SocketsHttpHandler/HttpConnectionBase.cs b/src/libraries/System.Net.Http/src/System/Net/Http/SocketsHttpHandler/HttpConnectionBase.cs index 6b333b7702d9ab..d92131e892a0a4 100644 --- a/src/libraries/System.Net.Http/src/System/Net/Http/SocketsHttpHandler/HttpConnectionBase.cs +++ b/src/libraries/System.Net.Http/src/System/Net/Http/SocketsHttpHandler/HttpConnectionBase.cs @@ -75,7 +75,8 @@ protected void MarkConnectionAsEstablished(Activity? connectionSetupActivity, IP protocol, _pool.IsSecure ? "https" : "http", _pool.TelemetryServerAddress, - _pool.OriginAuthority.Port); + _pool.OriginAuthority.Port, + remoteEndPoint?.Address?.ToString()); _connectionMetrics.ConnectionEstablished(); } diff --git a/src/libraries/System.Net.Http/src/System/Net/Http/SocketsHttpHandler/Metrics/ConnectionMetrics.cs b/src/libraries/System.Net.Http/src/System/Net/Http/SocketsHttpHandler/Metrics/ConnectionMetrics.cs index 9849dfb7a72f44..160592493caa85 100644 --- a/src/libraries/System.Net.Http/src/System/Net/Http/SocketsHttpHandler/Metrics/ConnectionMetrics.cs +++ b/src/libraries/System.Net.Http/src/System/Net/Http/SocketsHttpHandler/Metrics/ConnectionMetrics.cs @@ -14,9 +14,10 @@ internal sealed class ConnectionMetrics private readonly object _schemeTag; private readonly object _hostTag; private readonly object _portTag; + private readonly object? _peerAddressTag; private bool _currentlyIdle; - public ConnectionMetrics(SocketsHttpHandlerMetrics metrics, string protocolVersion, string scheme, string host, int port) + public ConnectionMetrics(SocketsHttpHandlerMetrics metrics, string protocolVersion, string scheme, string host, int port, string? peerAddress) { _metrics = metrics; _openConnectionsEnabled = _metrics.OpenConnections.Enabled; @@ -24,6 +25,7 @@ public ConnectionMetrics(SocketsHttpHandlerMetrics metrics, string protocolVersi _schemeTag = scheme; _hostTag = host; _portTag = DiagnosticsHelper.GetBoxedInt32(port); + _peerAddressTag = peerAddress; } // TagList is a huge struct, so we avoid storing it in a field to reduce the amount we allocate on the heap. @@ -36,6 +38,11 @@ private TagList GetTags() tags.Add("server.address", _hostTag); tags.Add("server.port", _portTag); + if (_peerAddressTag is not null) + { + tags.Add("network.peer.address", _peerAddressTag); + } + return tags; } diff --git a/src/libraries/System.Net.Http/tests/FunctionalTests/MetricsTest.cs b/src/libraries/System.Net.Http/tests/FunctionalTests/MetricsTest.cs index db8626b0145510..f1f536896925dc 100644 --- a/src/libraries/System.Net.Http/tests/FunctionalTests/MetricsTest.cs +++ b/src/libraries/System.Net.Http/tests/FunctionalTests/MetricsTest.cs @@ -73,6 +73,15 @@ protected HttpMetricsTestBase(ITestOutputHelper output) : base(output) } + private static void VerifyPeerAddress(KeyValuePair[] tags, IPAddress[] validPeerAddresses = null) + { + string ipString = (string)tags.Single(t => t.Key == "network.peer.address").Value; + validPeerAddresses ??= [IPAddress.Loopback.MapToIPv6(), IPAddress.Loopback, IPAddress.IPv6Loopback]; + IPAddress ip = IPAddress.Parse(ipString); + Assert.Contains(ip, validPeerAddresses); + } + + protected static void VerifyRequestDuration(Measurement measurement, Uri uri, Version? protocolVersion = null, @@ -118,23 +127,29 @@ protected static void VerifyActiveRequests(string instrumentName, long measureme Assert.Equal(method, tags.Single(t => t.Key == "http.request.method").Value); } - protected static void VerifyOpenConnections(string actualName, object measurement, KeyValuePair[] tags, long expectedValue, Uri uri, Version? protocolVersion, string state) + protected static void VerifyOpenConnections(string actualName, object measurement, KeyValuePair[] tags, long expectedValue, Uri uri, Version? protocolVersion, string state, IPAddress[] validPeerAddresses = null) { Assert.Equal(InstrumentNames.OpenConnections, actualName); Assert.Equal(expectedValue, Assert.IsType(measurement)); VerifySchemeHostPortTags(tags, uri); VerifyTag(tags, "network.protocol.version", GetVersionString(protocolVersion)); VerifyTag(tags, "http.connection.state", state); + VerifyPeerAddress(tags, validPeerAddresses); } - protected static void VerifyConnectionDuration(string instrumentName, object measurement, KeyValuePair[] tags, Uri uri, Version? protocolVersion) + protected static void VerifyConnectionDuration(string instrumentName, object measurement, KeyValuePair[] tags, Uri uri, Version? protocolVersion, IPAddress[] validPeerAddresses = null) { Assert.Equal(InstrumentNames.ConnectionDuration, instrumentName); double value = Assert.IsType(measurement); - Assert.InRange(value, double.Epsilon, 60); + // This flakes for remote requests on CI. + if (validPeerAddresses is null) + { + Assert.InRange(value, double.Epsilon, 60); + } VerifySchemeHostPortTags(tags, uri); VerifyTag(tags, "network.protocol.version", GetVersionString(protocolVersion)); + VerifyPeerAddress(tags, validPeerAddresses); } protected static void VerifyTimeInQueue(string instrumentName, object measurement, KeyValuePair[] tags, Uri uri, Version? protocolVersion, string method = "GET") @@ -371,6 +386,8 @@ public async Task ExternalServer_DurationMetrics_Recorded() Uri uri = UseVersion == HttpVersion.Version11 ? Test.Common.Configuration.Http.RemoteHttp11Server.EchoUri : Test.Common.Configuration.Http.RemoteHttp2Server.EchoUri; + IPAddress[] addresses = await Dns.GetHostAddressesAsync(uri.Host); + addresses = addresses.Union(addresses.Select(a => a.MapToIPv6())).ToArray(); using (HttpMessageInvoker client = CreateHttpMessageInvoker()) { @@ -383,9 +400,9 @@ public async Task ExternalServer_DurationMetrics_Recorded() VerifyRequestDuration(Assert.Single(requestDurationRecorder.GetMeasurements()), uri, UseVersion, 200, "GET"); Measurement cd = Assert.Single(connectionDurationRecorder.GetMeasurements()); - VerifyConnectionDuration(InstrumentNames.ConnectionDuration, cd.Value, cd.Tags.ToArray(), uri, UseVersion); + VerifyConnectionDuration(InstrumentNames.ConnectionDuration, cd.Value, cd.Tags.ToArray(), uri, UseVersion, addresses); Measurement oc = openConnectionsRecorder.GetMeasurements().First(); - VerifyOpenConnections(InstrumentNames.OpenConnections, oc.Value, oc.Tags.ToArray(), 1, uri, UseVersion, "idle"); + VerifyOpenConnections(InstrumentNames.OpenConnections, oc.Value, oc.Tags.ToArray(), 1, uri, UseVersion, "idle", addresses); } [ConditionalFact(typeof(RemoteExecutor), nameof(RemoteExecutor.IsSupported))]