Skip to content

Commit 7739efc

Browse files
committed
- feature System.Diagnostics.ActivitySource.IsSupported
1 parent cbd6446 commit 7739efc

File tree

12 files changed

+222
-29
lines changed

12 files changed

+222
-29
lines changed

src/libraries/System.Diagnostics.DiagnosticSource/src/ILLink/ILLink.Substitutions.Shared.xml

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,4 +4,9 @@
44
<method signature="System.Boolean get_Enabled()" body="stub" value="false" />
55
</type>
66
</assembly>
7+
<assembly fullname="System.Diagnostics.DiagnosticSource" feature="System.Diagnostics.ActivitySource.IsSupported" featurevalue="false">
8+
<type fullname="System.Diagnostics.ActivitySource">
9+
<method signature="System.Boolean HasListeners()" body="stub" value="false" />
10+
</type>
11+
</assembly>
712
</linker>

src/libraries/System.Diagnostics.DiagnosticSource/src/System/Diagnostics/Activity.cs

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -55,12 +55,10 @@ internal ActivityChangedEventArgs(Activity? previous, Activity? current)
5555
/// </summary>
5656
public partial class Activity : IDisposable
5757
{
58-
#pragma warning disable CA1825 // Array.Empty<T>() doesn't exist in all configurations
59-
private static readonly IEnumerable<KeyValuePair<string, string?>> s_emptyBaggageTags = new KeyValuePair<string, string?>[0];
60-
private static readonly IEnumerable<KeyValuePair<string, object?>> s_emptyTagObjects = new KeyValuePair<string, object?>[0];
61-
private static readonly IEnumerable<ActivityLink> s_emptyLinks = new DiagLinkedList<ActivityLink>();
62-
private static readonly IEnumerable<ActivityEvent> s_emptyEvents = new DiagLinkedList<ActivityEvent>();
63-
#pragma warning restore CA1825
58+
private static readonly IEnumerable<KeyValuePair<string, string?>> s_emptyBaggageTags = [];
59+
private static readonly IEnumerable<KeyValuePair<string, object?>> s_emptyTagObjects = [];
60+
private static readonly IEnumerable<ActivityLink> s_emptyLinks = [];
61+
private static readonly IEnumerable<ActivityEvent> s_emptyEvents = [];
6462
private static readonly ActivitySource s_defaultSource = new ActivitySource(string.Empty);
6563
private static readonly AsyncLocal<Activity?> s_current = new AsyncLocal<Activity?>();
6664

src/libraries/System.Diagnostics.DiagnosticSource/src/System/Diagnostics/ActivitySource.cs

Lines changed: 47 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@
33

44
using System.Collections.Generic;
55
using System.ComponentModel;
6+
using System.Diagnostics.CodeAnalysis;
7+
using System.Diagnostics.Metrics;
68
using System.Runtime.CompilerServices;
79
using System.Threading;
810

@@ -14,6 +16,9 @@ public sealed class ActivitySource : IDisposable
1416
private static readonly SynchronizedList<ActivityListener> s_allListeners = new SynchronizedList<ActivityListener>();
1517
private SynchronizedList<ActivityListener>? _listeners;
1618

19+
[FeatureSwitchDefinition("System.Diagnostics.ActivitySource.IsSupported")]
20+
internal static bool IsSupported { get; } = AppContext.TryGetSwitch("System.Diagnostics.ActivitySource.IsSupported", out bool isSupported) ? isSupported : true;
21+
1722
/// <summary>
1823
/// Construct an ActivitySource object with the input name
1924
/// </summary>
@@ -47,6 +52,10 @@ private ActivitySource(string name, string? version, IEnumerable<KeyValuePair<st
4752
Name = name ?? throw new ArgumentNullException(nameof(name));
4853
Version = version;
4954
TelemetrySchemaUrl = telemetrySchemaUrl;
55+
if (!IsSupported)
56+
{
57+
return;
58+
}
5059

5160
// Sorting the tags to make sure the tags are always in the same order.
5261
// Sorting can help in comparing the tags used for any scenario.
@@ -71,8 +80,10 @@ private ActivitySource(string name, string? version, IEnumerable<KeyValuePair<st
7180
}
7281
}
7382
}, this);
74-
75-
GC.KeepAlive(DiagnosticSourceEventSource.Log);
83+
if (Meter.IsEventSourceSupported)
84+
{
85+
GC.KeepAlive(DiagnosticSourceEventSource.Log);
86+
}
7687
}
7788

7889
/// <summary>
@@ -103,6 +114,10 @@ private ActivitySource(string name, string? version, IEnumerable<KeyValuePair<st
103114
/// </summary>
104115
public bool HasListeners()
105116
{
117+
if (!IsSupported)
118+
{
119+
return false;
120+
}
106121
SynchronizedList<ActivityListener>? listeners = _listeners;
107122
return listeners != null && listeners.Count > 0;
108123
}
@@ -203,6 +218,10 @@ public bool HasListeners()
203218
private Activity? CreateActivity(string name, ActivityKind kind, ActivityContext context, string? parentId, IEnumerable<KeyValuePair<string, object?>>? tags,
204219
IEnumerable<ActivityLink>? links, DateTimeOffset startTime, bool startIt = true, ActivityIdFormat idFormat = ActivityIdFormat.Unknown)
205220
{
221+
if (!IsSupported)
222+
{
223+
return null;
224+
}
206225
// _listeners can get assigned to null in Dispose.
207226
SynchronizedList<ActivityListener>? listeners = _listeners;
208227
if (listeners == null || listeners.Count == 0)
@@ -336,8 +355,11 @@ public bool HasListeners()
336355
/// </summary>
337356
public void Dispose()
338357
{
339-
_listeners = null;
340-
s_activeSources.Remove(this);
358+
if (IsSupported)
359+
{
360+
_listeners = null;
361+
s_activeSources.Remove(this);
362+
}
341363
}
342364

343365
/// <summary>
@@ -351,7 +373,7 @@ public static void AddActivityListener(ActivityListener listener)
351373
throw new ArgumentNullException(nameof(listener));
352374
}
353375

354-
if (s_allListeners.AddIfNotExist(listener))
376+
if (IsSupported && s_allListeners.AddIfNotExist(listener))
355377
{
356378
s_activeSources.EnumWithAction((source, obj) => {
357379
var shouldListenTo = ((ActivityListener)obj).ShouldListenTo;
@@ -367,6 +389,10 @@ public static void AddActivityListener(ActivityListener listener)
367389

368390
internal void AddListener(ActivityListener listener)
369391
{
392+
if (!IsSupported)
393+
{
394+
return;
395+
}
370396
if (_listeners == null)
371397
{
372398
Interlocked.CompareExchange(ref _listeners, new SynchronizedList<ActivityListener>(), null);
@@ -377,12 +403,20 @@ internal void AddListener(ActivityListener listener)
377403

378404
internal static void DetachListener(ActivityListener listener)
379405
{
406+
if (!IsSupported)
407+
{
408+
return;
409+
}
380410
s_allListeners.Remove(listener);
381411
s_activeSources.EnumWithAction((source, obj) => source._listeners?.Remove((ActivityListener) obj), listener);
382412
}
383413

384414
internal void NotifyActivityStart(Activity activity)
385415
{
416+
if (!IsSupported)
417+
{
418+
return;
419+
}
386420
Debug.Assert(activity != null);
387421

388422
// _listeners can get assigned to null in Dispose.
@@ -395,6 +429,10 @@ internal void NotifyActivityStart(Activity activity)
395429

396430
internal void NotifyActivityStop(Activity activity)
397431
{
432+
if (!IsSupported)
433+
{
434+
return;
435+
}
398436
Debug.Assert(activity != null);
399437

400438
// _listeners can get assigned to null in Dispose.
@@ -407,6 +445,10 @@ internal void NotifyActivityStop(Activity activity)
407445

408446
internal void NotifyActivityAddException(Activity activity, Exception exception, ref TagList tags)
409447
{
448+
if (!IsSupported)
449+
{
450+
return;
451+
}
410452
Debug.Assert(activity != null);
411453

412454
// _listeners can get assigned to null in Dispose.

src/libraries/System.Diagnostics.DiagnosticSource/src/System/Diagnostics/DiagnosticListener.cs

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33

44
using System.Collections.Generic;
55
using System.Diagnostics.CodeAnalysis;
6+
using System.Diagnostics.Metrics;
67
using System.Threading;
78

89
namespace System.Diagnostics
@@ -144,7 +145,10 @@ public DiagnosticListener(string name)
144145

145146
// Touch DiagnosticSourceEventSource.Logger so we ensure that the
146147
// DiagnosticSourceEventSource has been constructed (and thus is responsive to ETW requests to be enabled).
147-
GC.KeepAlive(DiagnosticSourceEventSource.Log);
148+
if (Meter.IsEventSourceSupported)
149+
{
150+
GC.KeepAlive(DiagnosticSourceEventSource.Log);
151+
}
148152
}
149153

150154
/// <summary>

src/libraries/System.Diagnostics.DiagnosticSource/src/System/Diagnostics/Metrics/Meter.cs

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,9 @@ public class Meter : IDisposable
2727
private static bool InitializeIsSupported() =>
2828
AppContext.TryGetSwitch("System.Diagnostics.Metrics.Meter.IsSupported", out bool isSupported) ? isSupported : true;
2929

30+
[FeatureSwitchDefinition("System.Diagnostics.Tracing.EventSource.IsSupported")]
31+
internal static bool IsEventSourceSupported { get; } = AppContext.TryGetSwitch("System.Diagnostics.Tracing.EventSource.IsSupported", out bool isSupported) ? isSupported : true;
32+
3033
/// <summary>
3134
/// Initialize a new instance of the Meter using the <see cref="MeterOptions" />.
3235
/// </summary>
@@ -99,7 +102,10 @@ private void Initialize(string name, string? version, IEnumerable<KeyValuePair<s
99102
}
100103

101104
// Ensure the metrics EventSource has been created in case we need to log this meter
102-
GC.KeepAlive(MetricsEventSource.Log);
105+
if (IsEventSourceSupported)
106+
{
107+
GC.KeepAlive(MetricsEventSource.Log);
108+
}
103109
}
104110

105111
/// <summary>

src/libraries/System.Net.Http/src/System/Net/Http/DiagnosticsHandler.cs

Lines changed: 14 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -16,12 +16,23 @@ namespace System.Net.Http
1616
internal sealed class DiagnosticsHandler : HttpMessageHandlerStage
1717
{
1818
private static readonly DiagnosticListener s_diagnosticListener = new DiagnosticListener(DiagnosticsHandlerLoggingStrings.DiagnosticListenerName);
19-
internal static readonly ActivitySource s_activitySource = new ActivitySource(DiagnosticsHandlerLoggingStrings.RequestNamespace);
19+
internal static readonly ActivitySource? s_activitySource;
2020

2121
private readonly HttpMessageHandler _innerHandler;
2222
private readonly DistributedContextPropagator _propagator;
2323
private readonly HeaderDescriptor[]? _propagatorFields;
2424

25+
[FeatureSwitchDefinition("System.Diagnostics.ActivitySource.IsSupported")]
26+
private static bool IsActivitySourceSupported { get; } = AppContext.TryGetSwitch("System.Diagnostics.ActivitySource.IsSupported", out bool isSupported) ? isSupported : true;
27+
28+
static DiagnosticsHandler()
29+
{
30+
if (IsActivitySourceSupported)
31+
{
32+
s_activitySource = new ActivitySource(DiagnosticsHandlerLoggingStrings.RequestNamespace);
33+
}
34+
}
35+
2536
public DiagnosticsHandler(HttpMessageHandler innerHandler, DistributedContextPropagator propagator, bool autoRedirect = false)
2637
{
2738
Debug.Assert(GlobalHttpSettings.DiagnosticsHandler.EnableActivityPropagation);
@@ -49,14 +60,14 @@ private static bool IsEnabled()
4960
{
5061
// check if there is a parent Activity or if someone listens to "System.Net.Http" ActivitySource or "HttpHandlerDiagnosticListener" DiagnosticListener.
5162
return Activity.Current != null ||
52-
s_activitySource.HasListeners() ||
63+
(IsActivitySourceSupported && s_activitySource!.HasListeners()) ||
5364
s_diagnosticListener.IsEnabled();
5465
}
5566

5667
private static Activity? StartActivity(HttpRequestMessage request)
5768
{
5869
Activity? activity = null;
59-
if (s_activitySource.HasListeners())
70+
if (IsActivitySourceSupported && s_activitySource!.HasListeners())
6071
{
6172
activity = s_activitySource.StartActivity(DiagnosticsHandlerLoggingStrings.RequestActivityName, ActivityKind.Client);
6273
}

src/libraries/System.Net.Http/src/System/Net/Http/SocketsHttpHandler/ConnectionPool/ConnectionSetupDistributedTracing.cs

Lines changed: 20 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,19 +3,31 @@
33

44
using System.Collections.Generic;
55
using System.Diagnostics;
6+
using System.Diagnostics.CodeAnalysis;
67
using System.Threading;
78

89
namespace System.Net.Http
910
{
1011
// Implements distributed tracing logic for managing the "HTTP connection_setup" and "HTTP wait_for_connection" Activities.
1112
internal static class ConnectionSetupDistributedTracing
1213
{
13-
private static readonly ActivitySource s_connectionsActivitySource = new ActivitySource(DiagnosticsHandlerLoggingStrings.ConnectionsNamespace);
14+
[FeatureSwitchDefinition("System.Diagnostics.ActivitySource.IsSupported")]
15+
private static bool IsActivitySourceSupported { get; } = AppContext.TryGetSwitch("System.Diagnostics.ActivitySource.IsSupported", out bool isSupported) ? isSupported : true;
16+
17+
private static readonly ActivitySource? s_connectionsActivitySource;
18+
19+
static ConnectionSetupDistributedTracing()
20+
{
21+
if (IsActivitySourceSupported)
22+
{
23+
s_connectionsActivitySource = new ActivitySource(DiagnosticsHandlerLoggingStrings.ConnectionsNamespace);
24+
}
25+
}
1426

1527
public static Activity? StartConnectionSetupActivity(bool isSecure, HttpAuthority authority)
1628
{
1729
Activity? activity = null;
18-
if (s_connectionsActivitySource.HasListeners())
30+
if (IsActivitySourceSupported && s_connectionsActivitySource!.HasListeners())
1931
{
2032
// Connection activities should be new roots and not parented under whatever
2133
// request happens to be in progress when the connection is started.
@@ -71,7 +83,11 @@ public static void ReportError(Activity? activity, Exception exception)
7183

7284
public static Activity? StartWaitForConnectionActivity(HttpAuthority authority)
7385
{
74-
Activity? activity = s_connectionsActivitySource.StartActivity(DiagnosticsHandlerLoggingStrings.WaitForConnectionActivityName);
86+
Activity? activity = null;
87+
if (IsActivitySourceSupported && s_connectionsActivitySource!.HasListeners())
88+
{
89+
activity = s_connectionsActivitySource.StartActivity(DiagnosticsHandlerLoggingStrings.WaitForConnectionActivityName);
90+
}
7591
if (activity is not null)
7692
{
7793
activity.DisplayName = $"HTTP wait_for_connection {authority.HostValue}:{authority.Port}";
@@ -85,7 +101,7 @@ public static void AddConnectionLinkToRequestActivity(Activity connectionSetupAc
85101
Debug.Assert(connectionSetupActivity is not null);
86102

87103
// We only support links for request activities created by the "System.Net.Http" ActivitySource.
88-
if (GlobalHttpSettings.DiagnosticsHandler.EnableActivityPropagation && DiagnosticsHandler.s_activitySource.HasListeners())
104+
if (IsActivitySourceSupported && GlobalHttpSettings.DiagnosticsHandler.EnableActivityPropagation && DiagnosticsHandler.s_activitySource!.HasListeners())
89105
{
90106
Activity? requestActivity = Activity.Current;
91107
if (requestActivity?.Source == DiagnosticsHandler.s_activitySource)
Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
// Licensed to the .NET Foundation under one or more agreements.
2+
// The .NET Foundation licenses this file to you under the MIT license.
3+
4+
using System;
5+
using System.Reflection;
6+
using System.Collections.Generic;
7+
using System.Diagnostics;
8+
using System.Net;
9+
using System.Net.Sockets;
10+
using System.Net.Security;
11+
using System.IO;
12+
using System.Net.Http;
13+
using System.Runtime.Versioning;
14+
using System.Threading.Tasks;
15+
16+
class Program
17+
{
18+
static async Task<int> Main(string[] args)
19+
{
20+
using HttpClient client = new();
21+
22+
// send a request, but ignore its result
23+
try
24+
{
25+
await client.GetAsync("https://www.microsoft.com");
26+
}
27+
catch { }
28+
29+
var trimmedTypes = new List<(Assembly,string,bool)>();
30+
trimmedTypes.Add((typeof(HttpClient).Assembly,"System.Diagnostics.Metrics.Meter", true));
31+
// TODO trimmedTypes.Add((typeof(Object).Assembly,"System.Diagnostics.Tracing.ActivityTracker", true));
32+
33+
trimmedTypes.Add((typeof(ActivityStatusCode).Assembly,"System.Diagnostics.Metrics.MetricsEventSource", true));
34+
trimmedTypes.Add((typeof(ActivityStatusCode).Assembly,"System.Diagnostics.DiagnosticSourceEventSource", true));
35+
36+
trimmedTypes.Add((typeof(ActivityStatusCode).Assembly,"System.Diagnostics.Metrics.AggregationManager", true));
37+
trimmedTypes.Add((typeof(ActivityStatusCode).Assembly,"System.Diagnostics.Metrics.RuntimeMetrics", true));
38+
trimmedTypes.Add((typeof(ActivityStatusCode).Assembly,"System.Diagnostics.ActivityListener", true));
39+
trimmedTypes.Add((typeof(ActivityStatusCode).Assembly,"System.Diagnostics.DiagnosticSource", true));
40+
trimmedTypes.Add((typeof(ActivityStatusCode).Assembly,"System.Diagnostics.DiagnosticListener", true));
41+
trimmedTypes.Add((typeof(ActivityStatusCode).Assembly,"System.Diagnostics.HttpHandlerDiagnosticListener", true));
42+
43+
trimmedTypes.Add((typeof(ActivityStatusCode).Assembly,"System.Diagnostics.DiagLinkedList ", false));
44+
trimmedTypes.Add((typeof(ActivityStatusCode).Assembly,"System.Diagnostics.ActivitySource", false));
45+
trimmedTypes.Add((typeof(ActivityStatusCode).Assembly,"System.Diagnostics.Activity", false));
46+
47+
if(!OperatingSystem.IsBrowser())
48+
{
49+
// TODO trimmedTypes.Add((typeof(Socket).Assembly,"System.Net.Sockets.SocketsTelemetry", true));
50+
// TODO trimmedTypes.Add((typeof(Dns).Assembly,"System.Net.NameResolutionTelemetry", true));
51+
// TODO trimmedTypes.Add((typeof(SslStream).Assembly,"System.Net.Security.NetSecurityTelemetry", true));
52+
// TODO trimmedTypes.Add((typeof(Object).Assembly,"System.Diagnostics.Tracing.EventSource", true));
53+
}
54+
55+
var ok = 100;
56+
for (var i=0; i < trimmedTypes.Count; i++)
57+
{
58+
var (assembly,typeName,trim) = trimmedTypes[i];
59+
// The intention of this method is to ensure the trimmer doesn't preserve the Type.
60+
Type type = assembly.GetType(typeName, throwOnError: false);
61+
bool isTrimmed = (type == null);
62+
bool expectTrimmed=trim;
63+
if (expectTrimmed != isTrimmed)
64+
{
65+
ok = 0-i;
66+
Console.WriteLine($"Type {typeName} is isTrimmed:{isTrimmed} expectTrimmed:{expectTrimmed}.");
67+
}
68+
}
69+
return ok;
70+
}
71+
}

src/libraries/System.Net.Http/tests/TrimmingTests/System.Net.Http.TrimmingTests.proj

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,13 +2,18 @@
22
<Import Project="$([MSBuild]::GetPathOfFileAbove(Directory.Build.props))" />
33

44
<ItemGroup>
5+
<!--
56
<TestConsoleAppSourceFiles Include="DecompressionHandlerTrimmedTest.cs" />
67
<TestConsoleAppSourceFiles Include="MetricsHandlerTrimmedTest.cs" >
78
<DisabledFeatureSwitches>System.Diagnostics.Metrics.Meter.IsSupported</DisabledFeatureSwitches>
89
</TestConsoleAppSourceFiles>
910
<TestConsoleAppSourceFiles Include="DiagnosticsHandlerTrimmedTest.cs">
1011
<DisabledFeatureSwitches>System.Net.Http.EnableActivityPropagation</DisabledFeatureSwitches>
1112
</TestConsoleAppSourceFiles>
13+
-->
14+
<TestConsoleAppSourceFiles Include="DiagnosticsAndMetricsTrimmedTest.cs">
15+
<DisabledFeatureSwitches>System.Diagnostics.ActivitySource.IsSupported;System.Net.Http.EnableActivityPropagation;System.Diagnostics.Metrics.Meter.IsSupported;System.Diagnostics.Tracing.EventSource.IsSupported;System.Diagnostics.StackTrace.IsSupported</DisabledFeatureSwitches>
16+
</TestConsoleAppSourceFiles>
1217
</ItemGroup>
1318

1419
<Import Project="$([MSBuild]::GetPathOfFileAbove(Directory.Build.targets))" />

0 commit comments

Comments
 (0)