Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion eng/testing/linker/project.csproj.template
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@
<MicrosoftNetCoreAppRuntimePackDir>{MicrosoftNetCoreAppRuntimePackDir}</MicrosoftNetCoreAppRuntimePackDir>

<RepositoryEngineeringDir>{RepositoryEngineeringDir}</RepositoryEngineeringDir>
<_ExtraTrimmerArgs>{ExtraTrimmerArgs} $(_ExtraTrimmerArgs)</_ExtraTrimmerArgs>
<_ExtraTrimmerArgs>{ExtraTrimmerArgs} $(_ExtraTrimmerArgs) --dump-dependencies</_ExtraTrimmerArgs>
{AdditionalProperties}

<!-- Needed for PublishAot -->
Expand Down
12 changes: 0 additions & 12 deletions src/libraries/System.Net.Http/src/ILLink/ILLink.Substitutions.xml

This file was deleted.

4 changes: 0 additions & 4 deletions src/libraries/System.Net.Http/src/System.Net.Http.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -24,10 +24,6 @@
<EmitCompilerGeneratedFiles Condition="'$(Configuration)' == 'Debug' and '$(TargetPlatformIdentifier)' == 'browser'">true</EmitCompilerGeneratedFiles>
</PropertyGroup>

<ItemGroup>
<ILLinkSubstitutionsXmls Include="$(ILLinkDirectory)ILLink.Substitutions.xml" />
</ItemGroup>

<ItemGroup Condition="'$(TargetPlatformIdentifier)' != ''">
<Compile Include="System\Net\Http\ByteArrayContent.cs" />
<Compile Include="System\Net\Http\CancellationHelper.cs" />
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ internal sealed class DiagnosticsHandler : HttpMessageHandlerStage

public DiagnosticsHandler(HttpMessageHandler innerHandler, DistributedContextPropagator propagator, bool autoRedirect = false)
{
Debug.Assert(IsGloballyEnabled());
Debug.Assert(GlobalHttpSettings.DiagnosticsHandler.EnableActivityPropagation);
Debug.Assert(innerHandler is not null && propagator is not null);

_innerHandler = innerHandler;
Expand Down Expand Up @@ -71,8 +71,6 @@ private static bool IsEnabled()
return activity;
}

internal static bool IsGloballyEnabled() => GlobalHttpSettings.DiagnosticsHandler.EnableActivityPropagation;

internal override ValueTask<HttpResponseMessage> SendAsync(HttpRequestMessage request, bool async, CancellationToken cancellationToken)
{
if (IsEnabled())
Expand All @@ -89,7 +87,7 @@ internal override ValueTask<HttpResponseMessage> SendAsync(HttpRequestMessage re

private async ValueTask<HttpResponseMessage> SendAsyncCore(HttpRequestMessage request, bool async, CancellationToken cancellationToken)
{
// HttpClientHandler is responsible to call static DiagnosticsHandler.IsEnabled() before forwarding request here.
// HttpClientHandler is responsible to call static GlobalHttpSettings.DiagnosticsHandler.IsEnabled before forwarding request here.
// It will check if propagation is on (because parent Activity exists or there is a listener) or off (forcibly disabled)
// This code won't be called unless consumer unsubscribes from DiagnosticListener right after the check.
// So some requests happening right after subscription starts might not be instrumented. Similarly,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

using System;
using System.Diagnostics.CodeAnalysis;

namespace System.Net.Http
{
/// <summary>
Expand All @@ -10,12 +13,21 @@ internal static class GlobalHttpSettings
{
internal static class DiagnosticsHandler
{
[FeatureSwitchDefinition("System.Net.Http.EnableActivityPropagation")]
public static bool EnableActivityPropagation { get; } = RuntimeSettingParser.QueryRuntimeSettingSwitch(
"System.Net.Http.EnableActivityPropagation",
"DOTNET_SYSTEM_NET_HTTP_ENABLEACTIVITYPROPAGATION",
true);
}

internal static class MetricsHandler
{
[FeatureSwitchDefinition("System.Diagnostics.Metrics.Meter.IsSupported")]
public static bool IsGloballyEnabled { get; } = RuntimeSettingParser.QueryRuntimeSettingSwitch(
"System.Diagnostics.Metrics.Meter.IsSupported",
true);
}

internal static class SocketsHttpHandler
{
#if !TARGET_BROWSER && !TARGET_WASI
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -45,8 +45,11 @@ private HttpMessageHandler Handler

// MetricsHandler should be descendant of DiagnosticsHandler in the handler chain to make sure the 'http.request.duration'
// metric is recorded before stopping the request Activity. This is needed to make sure that our telemetry supports Exemplars.
handler = new MetricsHandler(handler, _nativeMeterFactory, out _);
if (DiagnosticsHandler.IsGloballyEnabled())
if (GlobalHttpSettings.MetricsHandler.IsGloballyEnabled)
{
handler = new MetricsHandler(handler, _nativeMeterFactory, out _);
}
if (GlobalHttpSettings.DiagnosticsHandler.EnableActivityPropagation)
{
handler = new DiagnosticsHandler(handler, DistributedContextPropagator.Current);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -45,8 +45,11 @@ private HttpMessageHandler Handler

// MetricsHandler should be descendant of DiagnosticsHandler in the handler chain to make sure the 'http.request.duration'
// metric is recorded before stopping the request Activity. This is needed to make sure that our telemetry supports Exemplars.
handler = new MetricsHandler(handler, _meterFactory, out _);
if (DiagnosticsHandler.IsGloballyEnabled())
if (GlobalHttpSettings.MetricsHandler.IsGloballyEnabled)
{
handler = new MetricsHandler(handler, _meterFactory, out _);
}
if (GlobalHttpSettings.DiagnosticsHandler.EnableActivityPropagation)
{
handler = new DiagnosticsHandler(handler, DistributedContextPropagator.Current);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@

using System.Collections.Generic;
using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
using System.Diagnostics.Metrics;
using System.Threading;
using System.Threading.Tasks;
Expand All @@ -17,6 +18,8 @@ internal sealed class MetricsHandler : HttpMessageHandlerStage

public MetricsHandler(HttpMessageHandler innerHandler, IMeterFactory? meterFactory, out Meter meter)
{
Debug.Assert(GlobalHttpSettings.MetricsHandler.IsGloballyEnabled);

_innerHandler = innerHandler;

meter = meterFactory?.Create("System.Net.Http") ?? SharedMeter.Instance;
Expand Down Expand Up @@ -49,6 +52,8 @@ internal override ValueTask<HttpResponseMessage> SendAsync(HttpRequestMessage re

private async ValueTask<HttpResponseMessage> SendAsyncWithMetrics(HttpRequestMessage request, bool async, CancellationToken cancellationToken)
{
Debug.Assert(GlobalHttpSettings.MetricsHandler.IsGloballyEnabled);

(long startTimestamp, bool recordCurrentRequests) = RequestStart(request);
HttpResponseMessage? response = null;
Exception? exception = null;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -85,7 +85,7 @@ public static void AddConnectionLinkToRequestActivity(Activity connectionSetupAc
Debug.Assert(connectionSetupActivity is not null);

// We only support links for request activities created by the "System.Net.Http" ActivitySource.
if (DiagnosticsHandler.s_activitySource.HasListeners())
if (GlobalHttpSettings.DiagnosticsHandler.EnableActivityPropagation && DiagnosticsHandler.s_activitySource.HasListeners())
{
Activity? requestActivity = Activity.Current;
if (requestActivity?.Source == DiagnosticsHandler.s_activitySource)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -87,7 +87,9 @@ internal sealed partial class HttpConnectionPool
ThrowGetVersionException(request, 3, reasonException);
}

long queueStartingTimestamp = HttpTelemetry.Log.IsEnabled() || Settings._metrics!.RequestsQueueDuration.Enabled ? Stopwatch.GetTimestamp() : 0;
long queueStartingTimestamp = HttpTelemetry.Log.IsEnabled() || (GlobalHttpSettings.MetricsHandler.IsGloballyEnabled && Settings._metrics!.RequestsQueueDuration.Enabled)
? Stopwatch.GetTimestamp()
: 0;
Activity? waitForConnectionActivity = ConnectionSetupDistributedTracing.StartWaitForConnectionActivity(authority);

if (!TryGetPooledHttp3Connection(request, out Http3Connection? connection, out http3ConnectionWaiter))
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,10 @@ internal sealed class HttpConnectionWaiter<T> : TaskCompletionSourceWithCancella

public ValueTask<T> WaitForConnectionAsync(HttpRequestMessage request, HttpConnectionPool pool, bool async, CancellationToken requestCancellationToken)
{
return HttpTelemetry.Log.IsEnabled() || pool.Settings._metrics!.RequestsQueueDuration.Enabled || Activity.Current?.Source == DiagnosticsHandler.s_activitySource
bool withTelemetry = HttpTelemetry.Log.IsEnabled()
|| (GlobalHttpSettings.MetricsHandler.IsGloballyEnabled && pool.Settings._metrics!.RequestsQueueDuration.Enabled)
|| (GlobalHttpSettings.DiagnosticsHandler.EnableActivityPropagation && Activity.Current?.Source == DiagnosticsHandler.s_activitySource);
return withTelemetry
? WaitForConnectionWithTelemetryAsync(request, pool, async, requestCancellationToken)
: WaitWithCancellationAsync(async, requestCancellationToken);
}
Expand All @@ -42,14 +45,19 @@ private async ValueTask<T> WaitForConnectionWithTelemetryAsync(HttpRequestMessag
}
finally
{
TimeSpan duration = Stopwatch.GetElapsedTime(startingTimestamp);
int versionMajor = typeof(T) == typeof(HttpConnection) ? 1 : 2;

pool.Settings._metrics!.RequestLeftQueue(request, pool, duration, versionMajor);

if (HttpTelemetry.Log.IsEnabled())
if (HttpTelemetry.Log.IsEnabled() || GlobalHttpSettings.MetricsHandler.IsGloballyEnabled)
{
HttpTelemetry.Log.RequestLeftQueue(versionMajor, duration);
TimeSpan duration = Stopwatch.GetElapsedTime(startingTimestamp);
int versionMajor = typeof(T) == typeof(HttpConnection) ? 1 : 2;
if (GlobalHttpSettings.MetricsHandler.IsGloballyEnabled)
{
pool.Settings._metrics!.RequestLeftQueue(request, pool, duration, versionMajor);
}

if (HttpTelemetry.Log.IsEnabled())
{
HttpTelemetry.Log.RequestLeftQueue(versionMajor, duration);
}
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -287,8 +287,10 @@ public async Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, lon
if (queueStartingTimestamp != 0)
{
TimeSpan duration = Stopwatch.GetElapsedTime(queueStartingTimestamp);

_pool.Settings._metrics!.RequestLeftQueue(request, Pool, duration, versionMajor: 3);
if (GlobalHttpSettings.MetricsHandler.IsGloballyEnabled)
{
_pool.Settings._metrics!.RequestLeftQueue(request, Pool, duration, versionMajor: 3);
}

if (HttpTelemetry.Log.IsEnabled())
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -56,26 +56,29 @@ public HttpConnectionBase(HttpConnectionPool pool, Activity? connectionSetupActi
protected void MarkConnectionAsEstablished(Activity? connectionSetupActivity, IPEndPoint? remoteEndPoint)
{
ConnectionSetupActivity = connectionSetupActivity;
Debug.Assert(_pool.Settings._metrics is not null);

SocketsHttpHandlerMetrics metrics = _pool.Settings._metrics;
if (metrics.OpenConnections.Enabled || metrics.ConnectionDuration.Enabled)
if (GlobalHttpSettings.MetricsHandler.IsGloballyEnabled)
{
// While requests may report HTTP/1.0 as the protocol, we treat all HTTP/1.X connections as HTTP/1.1.
string protocol =
this is HttpConnection ? "1.1" :
this is Http2Connection ? "2" :
"3";

_connectionMetrics = new ConnectionMetrics(
metrics,
protocol,
_pool.IsSecure ? "https" : "http",
_pool.OriginAuthority.HostValue,
_pool.OriginAuthority.Port,
remoteEndPoint?.Address?.ToString());

_connectionMetrics.ConnectionEstablished();
Debug.Assert(_pool.Settings._metrics is not null);

SocketsHttpHandlerMetrics metrics = _pool.Settings._metrics!;
if (metrics.OpenConnections.Enabled || metrics.ConnectionDuration.Enabled)
{
// While requests may report HTTP/1.0 as the protocol, we treat all HTTP/1.X connections as HTTP/1.1.
string protocol =
this is HttpConnection ? "1.1" :
this is Http2Connection ? "2" :
"3";

_connectionMetrics = new ConnectionMetrics(
metrics,
protocol,
_pool.IsSecure ? "https" : "http",
_pool.OriginAuthority.HostValue,
_pool.OriginAuthority.Port,
remoteEndPoint?.Address?.ToString());

_connectionMetrics.ConnectionEstablished();
}
}

_idleSinceTickCount = _creationTickCount;
Expand All @@ -96,7 +99,7 @@ protected void MarkConnectionAsEstablished(Activity? connectionSetupActivity, IP

public void MarkConnectionAsClosed()
{
_connectionMetrics?.ConnectionClosed(durationMs: Environment.TickCount64 - _creationTickCount);
if (GlobalHttpSettings.MetricsHandler.IsGloballyEnabled) _connectionMetrics?.ConnectionClosed(durationMs: Environment.TickCount64 - _creationTickCount);

if (HttpTelemetry.Log.IsEnabled())
{
Expand All @@ -113,13 +116,13 @@ public void MarkConnectionAsClosed()
public void MarkConnectionAsIdle()
{
_idleSinceTickCount = Environment.TickCount64;
_connectionMetrics?.IdleStateChanged(idle: true);
if (GlobalHttpSettings.MetricsHandler.IsGloballyEnabled) _connectionMetrics?.IdleStateChanged(idle: true);
}

public void MarkConnectionAsNotIdle()
{
_idleSinceTickCount = null;
_connectionMetrics?.IdleStateChanged(idle: false);
if (GlobalHttpSettings.MetricsHandler.IsGloballyEnabled) _connectionMetrics?.IdleStateChanged(idle: false);
}

/// <summary>Uses <see cref="HeaderDescriptor.GetHeaderValue"/>, but first special-cases several known headers for which we can use caching.</summary>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -109,8 +109,6 @@ public HttpConnectionSettings CloneAndNormalize()
_maxResponseDrainSize = _maxResponseDrainSize,
_maxResponseDrainTime = _maxResponseDrainTime,
_maxResponseHeadersLength = _maxResponseHeadersLength,
_meterFactory = _meterFactory,
_metrics = _metrics,
_pooledConnectionLifetime = _pooledConnectionLifetime,
_pooledConnectionIdleTimeout = _pooledConnectionIdleTimeout,
_preAuthenticate = _preAuthenticate,
Expand All @@ -136,6 +134,12 @@ public HttpConnectionSettings CloneAndNormalize()
_impersonationLevel = _impersonationLevel,
};

if (GlobalHttpSettings.MetricsHandler.IsGloballyEnabled)
{
settings._meterFactory = _meterFactory;
settings._metrics = _metrics;
}

return settings;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -531,11 +531,14 @@ private HttpMessageHandlerStage SetupHandlerChain()

// MetricsHandler should be descendant of DiagnosticsHandler in the handler chain to make sure the 'http.request.duration'
// metric is recorded before stopping the request Activity. This is needed to make sure that our telemetry supports Exemplars.
handler = new MetricsHandler(handler, settings._meterFactory, out Meter meter);
settings._metrics = new SocketsHttpHandlerMetrics(meter);
if (GlobalHttpSettings.MetricsHandler.IsGloballyEnabled)
{
handler = new MetricsHandler(handler, settings._meterFactory, out Meter meter);
settings._metrics = new SocketsHttpHandlerMetrics(meter);
}

// DiagnosticsHandler is inserted before RedirectHandler so that trace propagation is done on redirects as well
if (DiagnosticsHandler.IsGloballyEnabled() && settings._activityHeadersPropagator is DistributedContextPropagator propagator)
if (GlobalHttpSettings.DiagnosticsHandler.EnableActivityPropagation && settings._activityHeadersPropagator is DistributedContextPropagator propagator)
{
handler = new DiagnosticsHandler(handler, propagator, settings._allowAutoRedirect);
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

using System;
using System.IO;
using System.Net.Http;
using System.Runtime.Versioning;
using System.Threading.Tasks;

class Program
{
static async Task<int> Main(string[] args)
{
using HttpClient client = new();

// send a request, but ignore its result
try
{
await client.GetAsync("https://www.microsoft.com");
}
catch { }

Type diagnosticsHandler = GetHttpType("System.Net.Http.DiagnosticsHandler");

// DiagnosticsHandler should have been trimmed
if (diagnosticsHandler is not null)
{
return -1;
}

return 100;
}

// The intention of this method is to ensure the trimmer doesn't preserve the Type.
private static Type GetHttpType(string name) =>
typeof(HttpClient).Assembly.GetType(name, throwOnError: false);
}
Loading
Loading