diff --git a/src/libraries/System.Diagnostics.DiagnosticSource/ref/System.Diagnostics.DiagnosticSource.cs b/src/libraries/System.Diagnostics.DiagnosticSource/ref/System.Diagnostics.DiagnosticSource.cs index 989d4486ffb1a..1b08291cccd2a 100644 --- a/src/libraries/System.Diagnostics.DiagnosticSource/ref/System.Diagnostics.DiagnosticSource.cs +++ b/src/libraries/System.Diagnostics.DiagnosticSource/ref/System.Diagnostics.DiagnosticSource.cs @@ -19,6 +19,7 @@ public virtual void Dispose() { } public virtual System.IDisposable Subscribe(System.IObserver> observer, System.Func? isEnabled) { throw null; } public virtual System.IDisposable Subscribe(System.IObserver> observer, System.Predicate? isEnabled) { throw null; } public override string ToString() { throw null; } + [System.Diagnostics.CodeAnalysis.RequiresUnreferencedCode("The type of object being written to DiagnosticSource cannot be discovered statically.")] public override void Write(string name, object? value) { } } public abstract partial class DiagnosticSource @@ -26,6 +27,7 @@ public abstract partial class DiagnosticSource protected DiagnosticSource() { } public abstract bool IsEnabled(string name); public virtual bool IsEnabled(string name, object? arg1, object? arg2 = null) { throw null; } + [System.Diagnostics.CodeAnalysis.RequiresUnreferencedCode("The type of object being written to DiagnosticSource cannot be discovered statically.")] public abstract void Write(string name, object? value); } } diff --git a/src/libraries/System.Diagnostics.DiagnosticSource/ref/System.Diagnostics.DiagnosticSource.csproj b/src/libraries/System.Diagnostics.DiagnosticSource/ref/System.Diagnostics.DiagnosticSource.csproj index e34e24c6fbf3b..779380efcdefc 100644 --- a/src/libraries/System.Diagnostics.DiagnosticSource/ref/System.Diagnostics.DiagnosticSource.csproj +++ b/src/libraries/System.Diagnostics.DiagnosticSource/ref/System.Diagnostics.DiagnosticSource.csproj @@ -12,6 +12,7 @@ + diff --git a/src/libraries/System.Diagnostics.DiagnosticSource/ref/System.Diagnostics.DiagnosticSourceActivity.cs b/src/libraries/System.Diagnostics.DiagnosticSource/ref/System.Diagnostics.DiagnosticSourceActivity.cs index 73ce2d5eab153..9402388ca45b9 100644 --- a/src/libraries/System.Diagnostics.DiagnosticSource/ref/System.Diagnostics.DiagnosticSourceActivity.cs +++ b/src/libraries/System.Diagnostics.DiagnosticSource/ref/System.Diagnostics.DiagnosticSourceActivity.cs @@ -192,7 +192,9 @@ public abstract partial class DiagnosticSource { public virtual void OnActivityExport(System.Diagnostics.Activity activity, object? payload) { } public virtual void OnActivityImport(System.Diagnostics.Activity activity, object? payload) { } + [System.Diagnostics.CodeAnalysis.RequiresUnreferencedCode("The type of object being written to DiagnosticSource cannot be discovered statically.")] public System.Diagnostics.Activity StartActivity(System.Diagnostics.Activity activity, object? args) { throw null; } + [System.Diagnostics.CodeAnalysis.RequiresUnreferencedCode("The type of object being written to DiagnosticSource cannot be discovered statically.")] public void StopActivity(System.Diagnostics.Activity activity, object? args) { } } public enum ActivitySamplingResult diff --git a/src/libraries/System.Diagnostics.DiagnosticSource/src/ILLink/ILLink.Suppressions.xml b/src/libraries/System.Diagnostics.DiagnosticSource/src/ILLink/ILLink.Suppressions.xml deleted file mode 100644 index b958c7cf8e0c6..0000000000000 --- a/src/libraries/System.Diagnostics.DiagnosticSource/src/ILLink/ILLink.Suppressions.xml +++ /dev/null @@ -1,17 +0,0 @@ - - - - - ILLink - IL2070 - member - M:System.Diagnostics.DiagnosticSourceEventSource.FilterAndTransform.MakeImplicitTransforms(System.Type) - - - ILLink - IL2070 - member - M:System.Diagnostics.DiagnosticSourceEventSource.TransformSpec.PropertySpec.PropertyFetch.FetcherForProperty(System.Type,System.String) - - - \ No newline at end of file diff --git a/src/libraries/System.Diagnostics.DiagnosticSource/src/System.Diagnostics.DiagnosticSource.csproj b/src/libraries/System.Diagnostics.DiagnosticSource/src/System.Diagnostics.DiagnosticSource.csproj index d5a7d0ac9ff5a..ab2970430a42c 100644 --- a/src/libraries/System.Diagnostics.DiagnosticSource/src/System.Diagnostics.DiagnosticSource.csproj +++ b/src/libraries/System.Diagnostics.DiagnosticSource/src/System.Diagnostics.DiagnosticSource.csproj @@ -1,4 +1,4 @@ - + true false @@ -31,6 +31,7 @@ + diff --git a/src/libraries/System.Diagnostics.DiagnosticSource/src/System/Diagnostics/DiagnosticListener.cs b/src/libraries/System.Diagnostics.DiagnosticSource/src/System/Diagnostics/DiagnosticListener.cs index 4aaf2bb37afba..c851f1db6c67d 100644 --- a/src/libraries/System.Diagnostics.DiagnosticSource/src/System/Diagnostics/DiagnosticListener.cs +++ b/src/libraries/System.Diagnostics.DiagnosticSource/src/System/Diagnostics/DiagnosticListener.cs @@ -3,6 +3,7 @@ using System.Threading; using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; namespace System.Diagnostics { @@ -255,6 +256,7 @@ public override bool IsEnabled(string name, object? arg1, object? arg2 = null) /// /// Override abstract method /// + [RequiresUnreferencedCode(WriteRequiresUnreferencedCode)] public override void Write(string name, object? value) { for (DiagnosticSubscription? curSubscription = _subscriptions; curSubscription != null; curSubscription = curSubscription.Next) diff --git a/src/libraries/System.Diagnostics.DiagnosticSource/src/System/Diagnostics/DiagnosticSource.cs b/src/libraries/System.Diagnostics.DiagnosticSource/src/System/Diagnostics/DiagnosticSource.cs index 10ad9e62a2616..2db2e7ff48cf2 100644 --- a/src/libraries/System.Diagnostics.DiagnosticSource/src/System/Diagnostics/DiagnosticSource.cs +++ b/src/libraries/System.Diagnostics.DiagnosticSource/src/System/Diagnostics/DiagnosticSource.cs @@ -2,6 +2,7 @@ // The .NET Foundation licenses this file to you under the MIT license. using System; +using System.Diagnostics.CodeAnalysis; namespace System.Diagnostics { @@ -15,6 +16,8 @@ namespace System.Diagnostics /// public abstract partial class DiagnosticSource { + internal const string WriteRequiresUnreferencedCode = "The type of object being written to DiagnosticSource cannot be discovered statically."; + /// /// Write is a generic way of logging complex payloads. Each notification /// is given a name, which identifies it as well as a object (typically an anonymous type) @@ -31,6 +34,7 @@ public abstract partial class DiagnosticSource /// The name of the event being written. /// An object that represent the value being passed as a payload for the event. /// This is often an anonymous type which contains several sub-values. + [RequiresUnreferencedCode(WriteRequiresUnreferencedCode)] public abstract void Write(string name, object? value); /// diff --git a/src/libraries/System.Diagnostics.DiagnosticSource/src/System/Diagnostics/DiagnosticSourceActivity.cs b/src/libraries/System.Diagnostics.DiagnosticSource/src/System/Diagnostics/DiagnosticSourceActivity.cs index 549c4a8274cb2..3a10702ca1400 100644 --- a/src/libraries/System.Diagnostics.DiagnosticSource/src/System/Diagnostics/DiagnosticSourceActivity.cs +++ b/src/libraries/System.Diagnostics.DiagnosticSource/src/System/Diagnostics/DiagnosticSourceActivity.cs @@ -2,6 +2,7 @@ // The .NET Foundation licenses this file to you under the MIT license. using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; namespace System.Diagnostics { @@ -24,6 +25,7 @@ public abstract partial class DiagnosticSource /// An object that represent the value being passed as a payload for the event. /// Started Activity for convenient chaining /// + [RequiresUnreferencedCode(WriteRequiresUnreferencedCode)] public Activity StartActivity(Activity activity, object? args) { activity.Start(); @@ -41,6 +43,7 @@ public Activity StartActivity(Activity activity, object? args) /// Activity to be stopped /// An object that represent the value being passed as a payload for the event. /// + [RequiresUnreferencedCode(WriteRequiresUnreferencedCode)] public void StopActivity(Activity activity, object? args) { // Stop sets the end time if it was unset, but we want it set before we issue the write diff --git a/src/libraries/System.Diagnostics.DiagnosticSource/src/System/Diagnostics/DiagnosticSourceEventSource.cs b/src/libraries/System.Diagnostics.DiagnosticSource/src/System/Diagnostics/DiagnosticSourceEventSource.cs index 154cd8c07b6b0..f1f0bcbceb793 100644 --- a/src/libraries/System.Diagnostics.DiagnosticSource/src/System/Diagnostics/DiagnosticSourceEventSource.cs +++ b/src/libraries/System.Diagnostics.DiagnosticSource/src/System/Diagnostics/DiagnosticSourceEventSource.cs @@ -717,7 +717,9 @@ public FilterAndTransform(string filterAndPayloadSpec, int startIdx, int endIdx, if (eventNameFilter != null) eventNameFilterPredicate = (string eventName) => eventNameFilter == eventName; - var subscription = newListener.Subscribe(new CallbackObserver>(delegate (KeyValuePair evnt) + [UnconditionalSuppressMessage("ReflectionAnalysis", "IL2026:RequiresUnreferencedCode", + Justification = "DiagnosticSource.Write is marked with RequiresUnreferencedCode.")] + void OnEventWritten(KeyValuePair evnt) { // The filter given to the DiagnosticSource may not work if users don't is 'IsEnabled' as expected. // Thus we look for any events that may have snuck through and filter them out before forwarding. @@ -727,7 +729,9 @@ public FilterAndTransform(string filterAndPayloadSpec, int startIdx, int endIdx, var outputArgs = this.Morph(evnt.Value); var eventName = evnt.Key; writeEvent(newListener.Name, eventName, outputArgs); - }), eventNameFilterPredicate); + } + + var subscription = newListener.Subscribe(new CallbackObserver>(OnEventWritten), eventNameFilterPredicate); _liveSubscriptions = new Subscriptions(subscription, _liveSubscriptions); } })); @@ -948,41 +952,55 @@ internal static void CreateActivityListener(DiagnosticSourceEventSource eventSou return false; }; - eventSource._activityListener.ActivityStarted = activity => + eventSource._activityListener.ActivityStarted = activity => OnActivityStarted(eventSource, activity); + + eventSource._activityListener.ActivityStopped = activity => OnActivityStopped(eventSource, activity); + + ActivitySource.AddActivityListener(eventSource._activityListener); + } + + [DynamicDependency(DynamicallyAccessedMemberTypes.PublicProperties, typeof(Activity))] + [DynamicDependency(DynamicallyAccessedMemberTypes.PublicProperties, typeof(ActivityContext))] + [DynamicDependency(DynamicallyAccessedMemberTypes.PublicProperties, typeof(ActivityEvent))] + [DynamicDependency(DynamicallyAccessedMemberTypes.PublicProperties, typeof(ActivityLink))] + [DynamicDependency(nameof(DateTime.Ticks), typeof(DateTime))] + [DynamicDependency(nameof(TimeSpan.Ticks), typeof(TimeSpan))] + [UnconditionalSuppressMessage("ReflectionAnalysis", "IL2026:RequiresUnreferencedCode", + Justification = "Activity's properties are being preserved with the DynamicDependencies on OnActivityStarted.")] + private static void OnActivityStarted(DiagnosticSourceEventSource eventSource, Activity activity) + { + FilterAndTransform? list = eventSource._activitySourceSpecs; + while (list != null) { - FilterAndTransform? list = eventSource._activitySourceSpecs; - while (list != null) + if ((list.Events & ActivityEvents.ActivityStart) != 0 && + (activity.Source.Name == list.SourceName || list.SourceName == "*") && + (list.ActivityName == null || list.ActivityName == activity.OperationName)) { - if ((list.Events & ActivityEvents.ActivityStart) != 0 && - (activity.Source.Name == list.SourceName || list.SourceName == "*") && - (list.ActivityName == null || list.ActivityName == activity.OperationName)) - { - eventSource.ActivityStart(activity.Source.Name, activity.OperationName, list.Morph(activity)); - return; - } - - list = list.Next; + eventSource.ActivityStart(activity.Source.Name, activity.OperationName, list.Morph(activity)); + return; } - }; - eventSource._activityListener.ActivityStopped = activity => + list = list.Next; + } + } + + [UnconditionalSuppressMessage("ReflectionAnalysis", "IL2026:RequiresUnreferencedCode", + Justification = "Activity's properties are being preserved with the DynamicDependencies on OnActivityStarted.")] + private static void OnActivityStopped(DiagnosticSourceEventSource eventSource, Activity activity) + { + FilterAndTransform? list = eventSource._activitySourceSpecs; + while (list != null) { - FilterAndTransform? list = eventSource._activitySourceSpecs; - while (list != null) + if ((list.Events & ActivityEvents.ActivityStop) != 0 && + (activity.Source.Name == list.SourceName || list.SourceName == "*") && + (list.ActivityName == null || list.ActivityName == activity.OperationName)) { - if ((list.Events & ActivityEvents.ActivityStop) != 0 && - (activity.Source.Name == list.SourceName || list.SourceName == "*") && - (list.ActivityName == null || list.ActivityName == activity.OperationName)) - { - eventSource.ActivityStop(activity.Source.Name, activity.OperationName, list.Morph(activity)); - return; - } - - list = list.Next; + eventSource.ActivityStop(activity.Source.Name, activity.OperationName, list.Morph(activity)); + return; } - }; - ActivitySource.AddActivityListener(eventSource._activityListener); + list = list.Next; + } } // Move all wildcard nodes at the end of the list. @@ -1067,6 +1085,7 @@ private void Dispose() } } + [RequiresUnreferencedCode(DiagnosticSource.WriteRequiresUnreferencedCode)] public List> Morph(object? args) { // Transform the args into a bag of key-value strings. @@ -1105,7 +1124,11 @@ private void Dispose() Interlocked.CompareExchange(ref _implicitTransformsTable, new ConcurrentDictionary(1, 8), null); } - implicitTransforms = _implicitTransformsTable.GetOrAdd(argType, type => MakeImplicitTransforms(type)); + implicitTransforms = _implicitTransformsTable.GetOrAdd(argType, type => MakeImplicitTransformsWrapper(type)); + + [UnconditionalSuppressMessage("ReflectionAnalysis", "IL2026:RequiresUnreferencedCode", + Justification = "The Morph method has RequiresUnreferencedCode, but the trimmer can't see through lamdba calls.")] + static TransformSpec? MakeImplicitTransformsWrapper(Type transformType) => MakeImplicitTransforms(transformType); } // implicitTransformas now fetched from cache or constructed, use it to Fetch all the implicit fields. @@ -1145,6 +1168,7 @@ private void Dispose() // Given a type generate all the implicit transforms for type (that is for every field // generate the spec that fetches it). + [RequiresUnreferencedCode(DiagnosticSource.WriteRequiresUnreferencedCode)] private static TransformSpec? MakeImplicitTransforms(Type type) { TransformSpec? newSerializableArgs = null; @@ -1239,6 +1263,7 @@ public TransformSpec(string transformSpec, int startIdx, int endIdx, TransformSp /// if the spec is OUTSTR=EVENT_VALUE.PROP1.PROP2.PROP3 and the ultimate value of PROP3 is /// 10 then the return key value pair is KeyValuePair("OUTSTR","10") /// + [RequiresUnreferencedCode(DiagnosticSource.WriteRequiresUnreferencedCode)] public KeyValuePair Morph(object? obj) { for (PropertySpec? cur = _fetches; cur != null; cur = cur.Next) @@ -1289,6 +1314,7 @@ public PropertySpec(string propertyName, PropertySpec? next) /// Given an object fetch the property that this PropertySpec represents. /// obj may be null when IsStatic is true, otherwise it must be non-null. /// + [RequiresUnreferencedCode(DiagnosticSource.WriteRequiresUnreferencedCode)] public object? Fetch(object? obj) { PropertyFetch? fetch = _fetchForExpectedType; @@ -1331,9 +1357,7 @@ public PropertyFetch(Type? type) /// /// Create a property fetcher for a propertyName /// - [DynamicDependency("#ctor(System.Type)", typeof(EnumeratePropertyFetch<>))] - [DynamicDependency("#ctor(System.Type,System.Reflection.PropertyInfo)", typeof(RefTypedFetchProperty<,>))] - [DynamicDependency("#ctor(System.Type,System.Reflection.PropertyInfo)", typeof(ValueTypedFetchProperty<,>))] + [RequiresUnreferencedCode(DiagnosticSource.WriteRequiresUnreferencedCode)] public static PropertyFetch FetcherForProperty(Type? type, string propertyName) { if (propertyName == null) @@ -1385,7 +1409,7 @@ public static PropertyFetch FetcherForProperty(Type? type, string propertyName) PropertyInfo? propertyInfo = typeInfo.GetDeclaredProperty(propertyName); if (propertyInfo == null) { - Logger.Message($"Property {propertyName} not found on {type}"); + Logger.Message($"Property {propertyName} not found on {type}. Ensure the name is spelled correctly. If you published the application with PublishTrimmed=true, ensure the property was not trimmed away."); return new PropertyFetch(type); } // Delegate creation below is incompatible with static properties. diff --git a/src/libraries/System.Net.Http/src/System/Net/Http/DiagnosticsHandler.cs b/src/libraries/System.Net.Http/src/System/Net/Http/DiagnosticsHandler.cs index ab88b3578df1a..4e4d730c4c188 100644 --- a/src/libraries/System.Net.Http/src/System/Net/Http/DiagnosticsHandler.cs +++ b/src/libraries/System.Net.Http/src/System/Net/Http/DiagnosticsHandler.cs @@ -3,6 +3,7 @@ using System.Collections.Generic; using System.Diagnostics; +using System.Diagnostics.CodeAnalysis; using System.Net.Http.Headers; using System.Threading; using System.Threading.Tasks; @@ -90,7 +91,7 @@ await base.SendAsync(request, cancellationToken).ConfigureAwait(false) : // Only send start event to users who subscribed for it, but start activity anyway if (diagnosticListener.IsEnabled(DiagnosticsHandlerLoggingStrings.ActivityStartName)) { - diagnosticListener.StartActivity(activity, new ActivityStartData(request)); + StartActivity(diagnosticListener, activity, new ActivityStartData(request)); } else { @@ -102,7 +103,7 @@ await base.SendAsync(request, cancellationToken).ConfigureAwait(false) : { long timestamp = Stopwatch.GetTimestamp(); loggingRequestId = Guid.NewGuid(); - diagnosticListener.Write(DiagnosticsHandlerLoggingStrings.RequestWriteNameDeprecated, + Write(diagnosticListener, DiagnosticsHandlerLoggingStrings.RequestWriteNameDeprecated, new RequestData(request, loggingRequestId, timestamp)); } @@ -138,7 +139,7 @@ await base.SendAsync(request, cancellationToken).ConfigureAwait(false) : // If request was initially instrumented, Activity.Current has all necessary context for logging // Request is passed to provide some context if instrumentation was disabled and to avoid // extensive Activity.Tags usage to tunnel request properties - diagnosticListener.Write(DiagnosticsHandlerLoggingStrings.ExceptionEventName, new ExceptionData(ex, request)); + Write(diagnosticListener, DiagnosticsHandlerLoggingStrings.ExceptionEventName, new ExceptionData(ex, request)); } throw; } @@ -147,7 +148,7 @@ await base.SendAsync(request, cancellationToken).ConfigureAwait(false) : // always stop activity if it was started if (activity != null) { - diagnosticListener.StopActivity(activity, new ActivityStopData( + StopActivity(diagnosticListener, activity, new ActivityStopData( response, // If request is failed or cancelled, there is no response, therefore no information about request; // pass the request in the payload, so consumers can have it in Stop for failed/canceled requests @@ -159,7 +160,7 @@ await base.SendAsync(request, cancellationToken).ConfigureAwait(false) : if (diagnosticListener.IsEnabled(DiagnosticsHandlerLoggingStrings.ResponseWriteNameDeprecated)) { long timestamp = Stopwatch.GetTimestamp(); - diagnosticListener.Write(DiagnosticsHandlerLoggingStrings.ResponseWriteNameDeprecated, + Write(diagnosticListener, DiagnosticsHandlerLoggingStrings.ResponseWriteNameDeprecated, new ResponseData( response, loggingRequestId, @@ -173,6 +174,12 @@ await base.SendAsync(request, cancellationToken).ConfigureAwait(false) : private sealed class ActivityStartData { + // matches the properties selected in https://github.com/dotnet/diagnostics/blob/ffd0254da3bcc47847b1183fa5453c0877020abd/src/Microsoft.Diagnostics.Monitoring.EventPipe/Configuration/HttpRequestSourceConfiguration.cs#L36-L40 + [DynamicDependency(nameof(HttpRequestMessage.RequestUri), typeof(HttpRequestMessage))] + [DynamicDependency(nameof(HttpRequestMessage.Method), typeof(HttpRequestMessage))] + [DynamicDependency(nameof(HttpRequestMessage.RequestUri), typeof(HttpRequestMessage))] + [DynamicDependency(nameof(Uri.Host), typeof(Uri))] + [DynamicDependency(nameof(Uri.Port), typeof(Uri))] internal ActivityStartData(HttpRequestMessage request) { Request = request; @@ -201,6 +208,14 @@ internal ActivityStopData(HttpResponseMessage? response, HttpRequestMessage requ private sealed class ExceptionData { + // preserve the same properties as ActivityStartData above + common Exception properties + [DynamicDependency(nameof(HttpRequestMessage.RequestUri), typeof(HttpRequestMessage))] + [DynamicDependency(nameof(HttpRequestMessage.Method), typeof(HttpRequestMessage))] + [DynamicDependency(nameof(HttpRequestMessage.RequestUri), typeof(HttpRequestMessage))] + [DynamicDependency(nameof(Uri.Host), typeof(Uri))] + [DynamicDependency(nameof(Uri.Port), typeof(Uri))] + [DynamicDependency(nameof(System.Exception.Message), typeof(Exception))] + [DynamicDependency(nameof(System.Exception.StackTrace), typeof(Exception))] internal ExceptionData(Exception exception, HttpRequestMessage request) { Exception = exception; @@ -215,6 +230,12 @@ internal ExceptionData(Exception exception, HttpRequestMessage request) private sealed class RequestData { + // preserve the same properties as ActivityStartData above + [DynamicDependency(nameof(HttpRequestMessage.RequestUri), typeof(HttpRequestMessage))] + [DynamicDependency(nameof(HttpRequestMessage.Method), typeof(HttpRequestMessage))] + [DynamicDependency(nameof(HttpRequestMessage.RequestUri), typeof(HttpRequestMessage))] + [DynamicDependency(nameof(Uri.Host), typeof(Uri))] + [DynamicDependency(nameof(Uri.Port), typeof(Uri))] internal RequestData(HttpRequestMessage request, Guid loggingRequestId, long timestamp) { Request = request; @@ -231,6 +252,7 @@ internal RequestData(HttpRequestMessage request, Guid loggingRequestId, long tim private sealed class ResponseData { + [DynamicDependency(nameof(HttpResponseMessage.StatusCode), typeof(HttpResponseMessage))] internal ResponseData(HttpResponseMessage? response, Guid loggingRequestId, long timestamp, TaskStatus requestTaskStatus) { Response = response; @@ -316,6 +338,36 @@ private static void InjectHeaders(Activity currentActivity, HttpRequestMessage r } } + [UnconditionalSuppressMessage("ReflectionAnalysis", "IL2026:UnrecognizedReflectionPattern", + Justification = "The values being passed into Write have the commonly used properties being preserved with DynamicDependency.")] + private static void Write<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicProperties)] T>( + DiagnosticSource diagnosticSource, + string name, + T value) + { + diagnosticSource.Write(name, value); + } + + [UnconditionalSuppressMessage("ReflectionAnalysis", "IL2026:UnrecognizedReflectionPattern", + Justification = "The args being passed into StartActivity have the commonly used properties being preserved with DynamicDependency.")] + private static Activity StartActivity<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicProperties)] T>( + DiagnosticSource diagnosticSource, + Activity activity, + T? args) + { + return diagnosticSource.StartActivity(activity, args); + } + + [UnconditionalSuppressMessage("ReflectionAnalysis", "IL2026:UnrecognizedReflectionPattern", + Justification = "The args being passed into StopActivity have the commonly used properties being preserved with DynamicDependency.")] + private static void StopActivity<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicProperties)] T>( + DiagnosticSource diagnosticSource, + Activity activity, + T? args) + { + diagnosticSource.StopActivity(activity, args); + } + #endregion } }