@@ -15,8 +15,8 @@ namespace System.Net.Http
15
15
/// </summary>
16
16
internal sealed class DiagnosticsHandler : HttpMessageHandlerStage
17
17
{
18
- private static readonly DiagnosticListener s_diagnosticListener =
19
- new DiagnosticListener ( DiagnosticsHandlerLoggingStrings . DiagnosticListenerName ) ;
18
+ private static readonly DiagnosticListener s_diagnosticListener = new DiagnosticListener ( DiagnosticsHandlerLoggingStrings . DiagnosticListenerName ) ;
19
+ private static readonly ActivitySource s_activitySource = new ActivitySource ( DiagnosticsHandlerLoggingStrings . Namespace ) ;
20
20
21
21
private readonly HttpMessageHandler _innerHandler ;
22
22
private readonly DistributedContextPropagator _propagator ;
@@ -47,8 +47,29 @@ public DiagnosticsHandler(HttpMessageHandler innerHandler, DistributedContextPro
47
47
48
48
private static bool IsEnabled ( )
49
49
{
50
- // check if there is a parent Activity or if someone listens to HttpHandlerDiagnosticListener
51
- return Activity . Current != null || s_diagnosticListener . IsEnabled ( ) ;
50
+ // check if there is a parent Activity or if someone listens to "System.Net.Http" ActivitySource or "HttpHandlerDiagnosticListener" DiagnosticListener.
51
+ return Activity . Current != null ||
52
+ s_activitySource . HasListeners ( ) ||
53
+ s_diagnosticListener . IsEnabled ( ) ;
54
+ }
55
+
56
+ private static Activity ? CreateActivity ( HttpRequestMessage requestMessage )
57
+ {
58
+ Activity ? activity = null ;
59
+ if ( s_activitySource . HasListeners ( ) )
60
+ {
61
+ activity = s_activitySource . CreateActivity ( DiagnosticsHandlerLoggingStrings . ActivityName , ActivityKind . Client ) ;
62
+ }
63
+
64
+ if ( activity is null )
65
+ {
66
+ if ( Activity . Current is not null || s_diagnosticListener . IsEnabled ( DiagnosticsHandlerLoggingStrings . ActivityName , requestMessage ) )
67
+ {
68
+ activity = new Activity ( DiagnosticsHandlerLoggingStrings . ActivityName ) ;
69
+ }
70
+ }
71
+
72
+ return activity ;
52
73
}
53
74
54
75
internal static bool IsGloballyEnabled ( ) => GlobalHttpSettings . DiagnosticsHandler . EnableActivityPropagation ;
@@ -91,60 +112,38 @@ private async ValueTask<HttpResponseMessage> SendAsyncCore(HttpRequestMessage re
91
112
}
92
113
}
93
114
94
- Activity ? activity = null ;
95
115
DiagnosticListener diagnosticListener = s_diagnosticListener ;
96
116
97
- // if there is no listener, but propagation is enabled (with previous IsEnabled() check)
98
- // do not write any events just start/stop Activity and propagate Ids
99
- if ( ! diagnosticListener . IsEnabled ( ) )
100
- {
101
- activity = new Activity ( DiagnosticsHandlerLoggingStrings . ActivityName ) ;
102
- activity . Start ( ) ;
103
- InjectHeaders ( activity , request ) ;
104
-
105
- try
106
- {
107
- return async ?
108
- await _innerHandler . SendAsync ( request , cancellationToken ) . ConfigureAwait ( false ) :
109
- _innerHandler . Send ( request , cancellationToken ) ;
110
- }
111
- finally
112
- {
113
- activity . Stop ( ) ;
114
- }
115
- }
116
-
117
117
Guid loggingRequestId = Guid . Empty ;
118
+ Activity ? activity = CreateActivity ( request ) ;
118
119
119
- // There is a listener. Check if listener wants to be notified about HttpClient Activities
120
- if ( diagnosticListener . IsEnabled ( DiagnosticsHandlerLoggingStrings . ActivityName , request ) )
120
+ // Start activity anyway if it was created.
121
+ if ( activity is not null )
121
122
{
122
- activity = new Activity ( DiagnosticsHandlerLoggingStrings . ActivityName ) ;
123
+ activity . Start ( ) ;
123
124
124
- // Only send start event to users who subscribed for it, but start activity anyway
125
+ // Only send start event to users who subscribed for it.
125
126
if ( diagnosticListener . IsEnabled ( DiagnosticsHandlerLoggingStrings . ActivityStartName ) )
126
127
{
127
- StartActivity ( diagnosticListener , activity , new ActivityStartData ( request ) ) ;
128
- }
129
- else
130
- {
131
- activity . Start ( ) ;
128
+ Write ( diagnosticListener , DiagnosticsHandlerLoggingStrings . ActivityStartName , new ActivityStartData ( request ) ) ;
132
129
}
133
130
}
134
- // try to write System.Net.Http.Request event (deprecated)
131
+
132
+ // Try to write System.Net.Http.Request event (deprecated)
135
133
if ( diagnosticListener . IsEnabled ( DiagnosticsHandlerLoggingStrings . RequestWriteNameDeprecated ) )
136
134
{
137
135
long timestamp = Stopwatch . GetTimestamp ( ) ;
138
136
loggingRequestId = Guid . NewGuid ( ) ;
139
137
Write ( diagnosticListener , DiagnosticsHandlerLoggingStrings . RequestWriteNameDeprecated ,
140
- new RequestData ( request , loggingRequestId , timestamp ) ) ;
138
+ new RequestData (
139
+ request ,
140
+ loggingRequestId ,
141
+ timestamp ) ) ;
141
142
}
142
143
143
- // If we are on at all, we propagate current activity information
144
- Activity ? currentActivity = Activity . Current ;
145
- if ( currentActivity != null )
144
+ if ( activity is not null )
146
145
{
147
- InjectHeaders ( currentActivity , request ) ;
146
+ InjectHeaders ( activity , request ) ;
148
147
}
149
148
150
149
HttpResponseMessage ? response = null ;
@@ -178,17 +177,20 @@ await _innerHandler.SendAsync(request, cancellationToken).ConfigureAwait(false)
178
177
}
179
178
finally
180
179
{
181
- // always stop activity if it was started
182
- if ( activity != null )
180
+ // Always stop activity if it was started.
181
+ if ( activity is not null )
183
182
{
184
- StopActivity ( diagnosticListener , activity , new ActivityStopData (
185
- response ,
186
- // If request is failed or cancelled, there is no response, therefore no information about request;
187
- // pass the request in the payload, so consumers can have it in Stop for failed/canceled requests
188
- // and not retain all requests in Start
189
- request ,
190
- taskStatus ) ) ;
183
+ activity . SetEndTime ( DateTime . UtcNow ) ;
184
+
185
+ // Only send stop event to users who subscribed for it.
186
+ if ( diagnosticListener . IsEnabled ( DiagnosticsHandlerLoggingStrings . ActivityStopName ) )
187
+ {
188
+ Write ( diagnosticListener , DiagnosticsHandlerLoggingStrings . ActivityStopName , new ActivityStopData ( response , request , taskStatus ) ) ;
189
+ }
190
+
191
+ activity . Stop ( ) ;
191
192
}
193
+
192
194
// Try to write System.Net.Http.Response event (deprecated)
193
195
if ( diagnosticListener . IsEnabled ( DiagnosticsHandlerLoggingStrings . ResponseWriteNameDeprecated ) )
194
196
{
@@ -335,27 +337,6 @@ key is not null &&
335
337
{
336
338
diagnosticSource . Write ( name , value ) ;
337
339
}
338
-
339
- [ UnconditionalSuppressMessage ( "ReflectionAnalysis" , "IL2026:UnrecognizedReflectionPattern" ,
340
- Justification = "The args being passed into StartActivity have the commonly used properties being preserved with DynamicDependency." ) ]
341
- private static Activity StartActivity < [ DynamicallyAccessedMembers ( DynamicallyAccessedMemberTypes . PublicProperties ) ] T > (
342
- DiagnosticSource diagnosticSource ,
343
- Activity activity ,
344
- T ? args )
345
- {
346
- return diagnosticSource . StartActivity ( activity , args ) ;
347
- }
348
-
349
- [ UnconditionalSuppressMessage ( "ReflectionAnalysis" , "IL2026:UnrecognizedReflectionPattern" ,
350
- Justification = "The args being passed into StopActivity have the commonly used properties being preserved with DynamicDependency." ) ]
351
- private static void StopActivity < [ DynamicallyAccessedMembers ( DynamicallyAccessedMemberTypes . PublicProperties ) ] T > (
352
- DiagnosticSource diagnosticSource ,
353
- Activity activity ,
354
- T ? args )
355
- {
356
- diagnosticSource . StopActivity ( activity , args ) ;
357
- }
358
-
359
340
#endregion
360
341
}
361
342
}
0 commit comments