@@ -18,6 +18,8 @@ namespace Microsoft.AspNetCore.SignalR.Internal;
1818
1919internal sealed partial class DefaultHubDispatcher < THub > : HubDispatcher < THub > where THub : Hub
2020{
21+ private static readonly string _fullHubName = typeof ( THub ) . FullName ?? typeof ( THub ) . Name ;
22+
2123 private readonly Dictionary < string , HubMethodDescriptor > _methods = new ( StringComparer . OrdinalIgnoreCase ) ;
2224 private readonly Utf8HashLookup _cachedMethodNames = new ( ) ;
2325 private readonly IServiceScopeFactory _serviceScopeFactory ;
@@ -76,11 +78,14 @@ public override async Task OnConnectedAsync(HubConnectionContext connection)
7678
7779 var hubActivator = scope . ServiceProvider . GetRequiredService < IHubActivator < THub > > ( ) ;
7880 var hub = hubActivator . Create ( ) ;
81+ Activity ? activity = null ;
7982 try
8083 {
8184 // OnConnectedAsync won't work with client results (ISingleClientProxy.InvokeAsync)
8285 InitializeHub ( hub , connection , invokeAllowed : false ) ;
8386
87+ activity = StartActivity ( connection , scope . ServiceProvider , nameof ( hub . OnConnectedAsync ) ) ;
88+
8489 if ( _onConnectedMiddleware != null )
8590 {
8691 var context = new HubLifetimeContext ( connection . HubCallerContext , scope . ServiceProvider , hub ) ;
@@ -91,8 +96,14 @@ public override async Task OnConnectedAsync(HubConnectionContext connection)
9196 await hub . OnConnectedAsync ( ) ;
9297 }
9398 }
99+ catch ( Exception ex )
100+ {
101+ SetActivityError ( activity , ex ) ;
102+ throw ;
103+ }
94104 finally
95105 {
106+ activity ? . Stop ( ) ;
96107 hubActivator . Release ( hub ) ;
97108 }
98109 }
@@ -103,10 +114,13 @@ public override async Task OnDisconnectedAsync(HubConnectionContext connection,
103114
104115 var hubActivator = scope . ServiceProvider . GetRequiredService < IHubActivator < THub > > ( ) ;
105116 var hub = hubActivator . Create ( ) ;
117+ Activity ? activity = null ;
106118 try
107119 {
108120 InitializeHub ( hub , connection ) ;
109121
122+ activity = StartActivity ( connection , scope . ServiceProvider , nameof ( hub . OnDisconnectedAsync ) ) ;
123+
110124 if ( _onDisconnectedMiddleware != null )
111125 {
112126 var context = new HubLifetimeContext ( connection . HubCallerContext , scope . ServiceProvider , hub ) ;
@@ -117,8 +131,14 @@ public override async Task OnDisconnectedAsync(HubConnectionContext connection,
117131 await hub . OnDisconnectedAsync ( exception ) ;
118132 }
119133 }
134+ catch ( Exception ex )
135+ {
136+ SetActivityError ( activity , ex ) ;
137+ throw ;
138+ }
120139 finally
121140 {
141+ activity ? . Stop ( ) ;
122142 hubActivator . Release ( hub ) ;
123143 }
124144 }
@@ -367,6 +387,10 @@ static async Task ExecuteInvocation(DefaultHubDispatcher<THub> dispatcher,
367387 var logger = dispatcher . _logger ;
368388 var enableDetailedErrors = dispatcher . _enableDetailedErrors ;
369389
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+
370394 object ? result ;
371395 try
372396 {
@@ -375,13 +399,17 @@ static async Task ExecuteInvocation(DefaultHubDispatcher<THub> dispatcher,
375399 }
376400 catch ( Exception ex )
377401 {
402+ SetActivityError ( activity , ex ) ;
403+
378404 Log . FailedInvokingHubMethod ( logger , hubMethodInvocationMessage . Target , ex ) ;
379405 await SendInvocationError ( hubMethodInvocationMessage . InvocationId , connection ,
380406 ErrorMessageHelper . BuildErrorMessage ( $ "An unexpected error occurred invoking '{ hubMethodInvocationMessage . Target } ' on the server.", ex , enableDetailedErrors ) ) ;
381407 return ;
382408 }
383409 finally
384410 {
411+ activity ? . Stop ( ) ;
412+
385413 // Stream response handles cleanup in StreamResultsAsync
386414 // And normal invocations handle cleanup below in the finally
387415 if ( isStreamCall )
@@ -467,6 +495,8 @@ private async Task StreamAsync(string invocationId, HubConnectionContext connect
467495
468496 streamCts ??= CancellationTokenSource . CreateLinkedTokenSource ( connection . ConnectionAborted ) ;
469497
498+ var activity = StartActivity ( connection , scope . ServiceProvider , hubMethodInvocationMessage . Target ) ;
499+
470500 try
471501 {
472502 if ( ! connection . ActiveRequestCancellationSources . TryAdd ( invocationId , streamCts ) )
@@ -483,6 +513,8 @@ private async Task StreamAsync(string invocationId, HubConnectionContext connect
483513 }
484514 catch ( Exception ex )
485515 {
516+ SetActivityError ( activity , ex ) ;
517+
486518 Log . FailedInvokingHubMethod ( _logger , hubMethodInvocationMessage . Target , ex ) ;
487519 error = ErrorMessageHelper . BuildErrorMessage ( $ "An unexpected error occurred invoking '{ hubMethodInvocationMessage . Target } ' on the server.", ex , _enableDetailedErrors ) ;
488520 return ;
@@ -509,20 +541,27 @@ private async Task StreamAsync(string invocationId, HubConnectionContext connect
509541 catch ( ChannelClosedException ex )
510542 {
511543 // 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 ) ;
514549 }
515550 catch ( Exception ex )
516551 {
517552 // If the streaming method was canceled we don't want to send a HubException message - this is not an error case
518553 if ( ! ( ex is OperationCanceledException && streamCts . IsCancellationRequested ) )
519554 {
555+ SetActivityError ( activity , ex ) ;
556+
520557 Log . FailedStreaming ( _logger , invocationId , descriptor . MethodExecutor . MethodInfo . Name , ex ) ;
521558 error = ErrorMessageHelper . BuildErrorMessage ( "An error occurred on the server while streaming results." , ex , _enableDetailedErrors ) ;
522559 }
523560 }
524561 finally
525562 {
563+ activity ? . Stop ( ) ;
564+
526565 await CleanupInvocation ( connection , hubMethodInvocationMessage , hubActivator , hub , scope ) ;
527566
528567 streamCts . Dispose ( ) ;
@@ -749,4 +788,35 @@ public override IReadOnlyList<Type> GetParameterTypes(string methodName)
749788
750789 return null ;
751790 }
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+ }
752822}
0 commit comments