-
Notifications
You must be signed in to change notification settings - Fork 10.4k
Add client span to SignalR .NET client #57101
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
@BrennanConroy A problem I found when implementing this is the SignalR client doesn't know the name of the hub on the server. The client just specifies a URL and then sends messages to a method at that URL. Because the hub name isn't available, as a substitute I replaced it with the URL path, e.g. Is there a better option? If not, should the server record the URL path as the OTEL service name instead of the .NET type name? That will keep consistency between what the client and server record. |
@tarekgh @noahfalk Do you know how the Activity name interacts with OTEL? Activity has OperationName and DisplayName. Right now, It seems like opentelemetry-dotnet uses Would it be more correct to do this? var activity = activitySource.CreateActivity("Microsoft.AspNetCore.SignalR.Client.InvocationOut", ...);
activity.DisplayName = $"{serviceName}/{serviceMethod}"; |
CC @cijothomas @CodeBlanch @reyang to answer the question in #57101 (comment). What @JamesNK propose is matching the runtime guidelines as |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can we see a screenshot of these changes in a UI like the Aspire Dashboard?
SendCoreAsync
doesn't have an activity being created for it. It's for "fire and forget" calls. Do we just create an activity, send the message, and stop the activity immediately?
There should be a few more tests:
Multiple different invokes to make sure there is a different activity every time.
Creating an activity in "user code" and seeing that it's the parent of the activity created in an invoke.
Making sure the activity stopped.
src/SignalR/clients/csharp/Client.Core/src/Internal/InvocationRequest.cs
Show resolved
Hide resolved
OTel has a single concept of Span Name, which is expected to be the low cardinality Spanname, also used for UI. The name can be changed after span creation. |
@cijothomas Thanks! |
Yes.
This is done in
Done in
I'll add some asserts for |
Huh, maybe there is something weird happening here since it's merging into another PR, but those tests look like they're being deleted, which would be odd since they seem specific to this change. |
They're deleted from their original location and moved to |
🙈 The file was collapsed and when I looked for HubConnectionTests.Tracing.cs I only noticed one of them but there are two files with that name. |
Updates:
I don't think there are any outstanding changes. There is still this open question: #57101 (comment). I think the server should use the path. |
src/SignalR/clients/csharp/Client.Core/src/Internal/InvocationRequest.cs
Show resolved
Hide resolved
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think this is good to go after my last questions/comments.
src/SignalR/clients/csharp/Client.Core/src/Internal/InvocationRequest.cs
Show resolved
Hide resolved
src/SignalR/clients/csharp/Http.Connections.Client/src/HttpConnection.cs
Outdated
Show resolved
Hide resolved
This PR depends on the propagation PR being merged first |
eff6c7e
to
2f4adcc
Compare
ab2ff3f
to
691bf0f
Compare
691bf0f
to
a698667
Compare
{ | ||
ConnectionState connectionState; | ||
var connectionStateTask = _state.WaitForActiveConnectionAsync(sendingMethodName, token); | ||
if (connectionStateTask.Status == TaskStatus.RanToCompletion) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do we need to set any server tags if connectionStateTask
throws. It throws when you're either not connected or the user canceled the passed in CTS. In either case it's not actually doing anything except creating and stopping an activity.
Plus, it'd clean up the code a bit.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If there is a problem starting the connection then it would be useful to know the server address being called
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
But the connection failing to start, or closing before the send calls, or not even calling start in the first place, is not reflected in SendAsync or InvokeAsync calls. All it knows is that there is no connection.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
With telemetry it's useful to view it from the perspective of the user. The mechanics of what happens internally that could cause the call to fail are invisible to them. They're focused on the end result: the call they want to make failed and there was an error. Having information about what they were trying to do - make a call to a hub with the specified rpc.service
, rpc.method
, server.address
, server.port
- is what they care about. Then the error reason would be in error.type
.
For an example of prior art, HttpClient
always reports server.address
and server.port
when making a HTTP request. It does this even if the server connection couldn't be established, or the request was canceled before sending any data.
If you want to make it clearer that the call failed because of the connection not being available, there is room to do that by providing good values to error.type
. By default, it is the exception type name, but it can be customized in known scenarios. For example, if there is a problem with the connection, error.type
could be something like: negotiate-failed
, hub-connection-not-started
, etc.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Also, server.address
and server.port
are considered required attributes according to the spec: https://github.com/open-telemetry/semantic-conventions/blob/1e34b57b9f73b08b109cdc0e8841e857e5f5c205/docs/rpc/rpc-spans.md#client-attributes
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ok thanks, I wanted to know some concrete reason we were including it.
src/SignalR/clients/csharp/Client/test/UnitTests/HubConnectionTests.Tracing.cs
Show resolved
Hide resolved
/azp run |
Azure Pipelines successfully started running 3 pipeline(s). |
/azp run |
Azure Pipelines successfully started running 3 pipeline(s). |
Addresses #51557. Specifically, the client activity source item.
Follow up to #55439 and builds on top of #57049.
Changes:
Not planned in this PR:
Nothing is blocking additional work in the future in the future.