@@ -18,6 +18,8 @@ namespace Microsoft.AspNetCore.SignalR.Internal;
18
18
19
19
internal sealed partial class DefaultHubDispatcher < THub > : HubDispatcher < THub > where THub : Hub
20
20
{
21
+ private static readonly string _fullHubName = typeof ( THub ) . FullName ?? typeof ( THub ) . Name ;
22
+
21
23
private readonly Dictionary < string , HubMethodDescriptor > _methods = new ( StringComparer . OrdinalIgnoreCase ) ;
22
24
private readonly Utf8HashLookup _cachedMethodNames = new ( ) ;
23
25
private readonly IServiceScopeFactory _serviceScopeFactory ;
@@ -76,11 +78,14 @@ public override async Task OnConnectedAsync(HubConnectionContext connection)
76
78
77
79
var hubActivator = scope . ServiceProvider . GetRequiredService < IHubActivator < THub > > ( ) ;
78
80
var hub = hubActivator . Create ( ) ;
81
+ Activity ? activity = null ;
79
82
try
80
83
{
81
84
// OnConnectedAsync won't work with client results (ISingleClientProxy.InvokeAsync)
82
85
InitializeHub ( hub , connection , invokeAllowed : false ) ;
83
86
87
+ activity = StartActivity ( connection , scope . ServiceProvider , nameof ( hub . OnConnectedAsync ) ) ;
88
+
84
89
if ( _onConnectedMiddleware != null )
85
90
{
86
91
var context = new HubLifetimeContext ( connection . HubCallerContext , scope . ServiceProvider , hub ) ;
@@ -91,8 +96,14 @@ public override async Task OnConnectedAsync(HubConnectionContext connection)
91
96
await hub . OnConnectedAsync ( ) ;
92
97
}
93
98
}
99
+ catch ( Exception ex )
100
+ {
101
+ SetActivityError ( activity , ex ) ;
102
+ throw ;
103
+ }
94
104
finally
95
105
{
106
+ activity ? . Stop ( ) ;
96
107
hubActivator . Release ( hub ) ;
97
108
}
98
109
}
@@ -103,10 +114,13 @@ public override async Task OnDisconnectedAsync(HubConnectionContext connection,
103
114
104
115
var hubActivator = scope . ServiceProvider . GetRequiredService < IHubActivator < THub > > ( ) ;
105
116
var hub = hubActivator . Create ( ) ;
117
+ Activity ? activity = null ;
106
118
try
107
119
{
108
120
InitializeHub ( hub , connection ) ;
109
121
122
+ activity = StartActivity ( connection , scope . ServiceProvider , nameof ( hub . OnDisconnectedAsync ) ) ;
123
+
110
124
if ( _onDisconnectedMiddleware != null )
111
125
{
112
126
var context = new HubLifetimeContext ( connection . HubCallerContext , scope . ServiceProvider , hub ) ;
@@ -117,8 +131,14 @@ public override async Task OnDisconnectedAsync(HubConnectionContext connection,
117
131
await hub . OnDisconnectedAsync ( exception ) ;
118
132
}
119
133
}
134
+ catch ( Exception ex )
135
+ {
136
+ SetActivityError ( activity , ex ) ;
137
+ throw ;
138
+ }
120
139
finally
121
140
{
141
+ activity ? . Stop ( ) ;
122
142
hubActivator . Release ( hub ) ;
123
143
}
124
144
}
@@ -367,6 +387,10 @@ static async Task ExecuteInvocation(DefaultHubDispatcher<THub> dispatcher,
367
387
var logger = dispatcher . _logger ;
368
388
var enableDetailedErrors = dispatcher . _enableDetailedErrors ;
369
389
390
+ // Use hubMethodInvocationMessage.Target instead of methodExecutor.MethodInfo.Name
391
+ // We want to take HubMethodNameAttribute into account which will be the same as what the invocation target is
392
+ var activity = StartActivity ( connection , scope . ServiceProvider , hubMethodInvocationMessage . Target ) ;
393
+
370
394
object ? result ;
371
395
try
372
396
{
@@ -375,13 +399,17 @@ static async Task ExecuteInvocation(DefaultHubDispatcher<THub> dispatcher,
375
399
}
376
400
catch ( Exception ex )
377
401
{
402
+ SetActivityError ( activity , ex ) ;
403
+
378
404
Log . FailedInvokingHubMethod ( logger , hubMethodInvocationMessage . Target , ex ) ;
379
405
await SendInvocationError ( hubMethodInvocationMessage . InvocationId , connection ,
380
406
ErrorMessageHelper . BuildErrorMessage ( $ "An unexpected error occurred invoking '{ hubMethodInvocationMessage . Target } ' on the server.", ex , enableDetailedErrors ) ) ;
381
407
return ;
382
408
}
383
409
finally
384
410
{
411
+ activity ? . Stop ( ) ;
412
+
385
413
// Stream response handles cleanup in StreamResultsAsync
386
414
// And normal invocations handle cleanup below in the finally
387
415
if ( isStreamCall )
@@ -467,6 +495,8 @@ private async Task StreamAsync(string invocationId, HubConnectionContext connect
467
495
468
496
streamCts ??= CancellationTokenSource . CreateLinkedTokenSource ( connection . ConnectionAborted ) ;
469
497
498
+ var activity = StartActivity ( connection , scope . ServiceProvider , hubMethodInvocationMessage . Target ) ;
499
+
470
500
try
471
501
{
472
502
if ( ! connection . ActiveRequestCancellationSources . TryAdd ( invocationId , streamCts ) )
@@ -483,6 +513,8 @@ private async Task StreamAsync(string invocationId, HubConnectionContext connect
483
513
}
484
514
catch ( Exception ex )
485
515
{
516
+ SetActivityError ( activity , ex ) ;
517
+
486
518
Log . FailedInvokingHubMethod ( _logger , hubMethodInvocationMessage . Target , ex ) ;
487
519
error = ErrorMessageHelper . BuildErrorMessage ( $ "An unexpected error occurred invoking '{ hubMethodInvocationMessage . Target } ' on the server.", ex , _enableDetailedErrors ) ;
488
520
return ;
@@ -509,20 +541,27 @@ private async Task StreamAsync(string invocationId, HubConnectionContext connect
509
541
catch ( ChannelClosedException ex )
510
542
{
511
543
// If the channel closes from an exception in the streaming method, grab the innerException for the error from the streaming method
512
- Log . FailedStreaming ( _logger , invocationId , descriptor . MethodExecutor . MethodInfo . Name , ex . InnerException ?? ex ) ;
513
- error = ErrorMessageHelper . BuildErrorMessage ( "An error occurred on the server while streaming results." , ex . InnerException ?? ex , _enableDetailedErrors ) ;
544
+ var exception = ex . InnerException ?? ex ;
545
+ SetActivityError ( activity , exception ) ;
546
+
547
+ Log . FailedStreaming ( _logger , invocationId , descriptor . MethodExecutor . MethodInfo . Name , exception ) ;
548
+ error = ErrorMessageHelper . BuildErrorMessage ( "An error occurred on the server while streaming results." , exception , _enableDetailedErrors ) ;
514
549
}
515
550
catch ( Exception ex )
516
551
{
517
552
// If the streaming method was canceled we don't want to send a HubException message - this is not an error case
518
553
if ( ! ( ex is OperationCanceledException && streamCts . IsCancellationRequested ) )
519
554
{
555
+ SetActivityError ( activity , ex ) ;
556
+
520
557
Log . FailedStreaming ( _logger , invocationId , descriptor . MethodExecutor . MethodInfo . Name , ex ) ;
521
558
error = ErrorMessageHelper . BuildErrorMessage ( "An error occurred on the server while streaming results." , ex , _enableDetailedErrors ) ;
522
559
}
523
560
}
524
561
finally
525
562
{
563
+ activity ? . Stop ( ) ;
564
+
526
565
await CleanupInvocation ( connection , hubMethodInvocationMessage , hubActivator , hub , scope ) ;
527
566
528
567
streamCts . Dispose ( ) ;
@@ -749,4 +788,35 @@ public override IReadOnlyList<Type> GetParameterTypes(string methodName)
749
788
750
789
return null ;
751
790
}
791
+
792
+ // Starts an Activity for a Hub method invocation and sets up all the tags and other state.
793
+ // Make sure to call Activity.Stop() once the Hub method completes, and consider calling SetActivityError on exception.
794
+ private static Activity ? StartActivity ( HubConnectionContext connectionContext , IServiceProvider serviceProvider , string methodName )
795
+ {
796
+ if ( serviceProvider . GetService < SignalRActivitySource > ( ) is SignalRActivitySource signalRActivitySource
797
+ && signalRActivitySource . ActivitySource . HasListeners ( ) )
798
+ {
799
+ var requestContext = connectionContext . OriginalActivity ? . Context ;
800
+
801
+ return signalRActivitySource . ActivitySource . StartActivity ( $ "{ _fullHubName } /{ methodName } ", ActivityKind . Server , parentId : null ,
802
+ // https://github.com/open-telemetry/semantic-conventions/blob/main/docs/rpc/rpc-spans.md#server-attributes
803
+ tags : [
804
+ new ( "rpc.method" , methodName ) ,
805
+ new ( "rpc.system" , "signalr" ) ,
806
+ new ( "rpc.service" , _fullHubName ) ,
807
+ // See https://github.com/dotnet/aspnetcore/blob/027c60168383421750f01e427e4f749d0684bc02/src/Servers/Kestrel/Core/src/Internal/Infrastructure/KestrelMetrics.cs#L308
808
+ // And https://github.com/dotnet/aspnetcore/issues/43786
809
+ //new("server.address", ...),
810
+ ] ,
811
+ links : requestContext . HasValue ? [ new ActivityLink ( requestContext . Value ) ] : null ) ;
812
+ }
813
+
814
+ return null ;
815
+ }
816
+
817
+ private static void SetActivityError ( Activity ? activity , Exception ex )
818
+ {
819
+ activity ? . SetTag ( "error.type" , ex . GetType ( ) . FullName ) ;
820
+ activity ? . SetStatus ( ActivityStatusCode . Error ) ;
821
+ }
752
822
}
0 commit comments