diff --git a/src/HotChocolate/AspNetCore/src/AspNetCore.Pipeline/Instrumentation/AggregateServerDiagnosticEventListener.cs b/src/HotChocolate/AspNetCore/src/AspNetCore.Pipeline/Instrumentation/AggregateServerDiagnosticEventListener.cs
index 3fd66e60177..2e0a6110b91 100644
--- a/src/HotChocolate/AspNetCore/src/AspNetCore.Pipeline/Instrumentation/AggregateServerDiagnosticEventListener.cs
+++ b/src/HotChocolate/AspNetCore/src/AspNetCore.Pipeline/Instrumentation/AggregateServerDiagnosticEventListener.cs
@@ -1,4 +1,6 @@
using Microsoft.AspNetCore.Http;
+using HotChocolate.AspNetCore.Subscriptions;
+using HotChocolate.AspNetCore.Subscriptions.Protocols;
using HotChocolate.Language;
namespace HotChocolate.AspNetCore.Instrumentation;
@@ -111,6 +113,16 @@ public IDisposable WebSocketSession(HttpContext context)
return new AggregateActivityScope(scopes);
}
+ public void WebSocketConnectionInitialized(
+ ISocketSession session,
+ IOperationMessagePayload connectionInitMessage)
+ {
+ for (var i = 0; i < _listeners.Length; i++)
+ {
+ _listeners[i].WebSocketConnectionInitialized(session, connectionInitMessage);
+ }
+ }
+
public void WebSocketSessionError(HttpContext context, Exception exception)
{
for (var i = 0; i < _listeners.Length; i++)
diff --git a/src/HotChocolate/AspNetCore/src/AspNetCore.Pipeline/Instrumentation/IServerDiagnosticEvents.cs b/src/HotChocolate/AspNetCore/src/AspNetCore.Pipeline/Instrumentation/IServerDiagnosticEvents.cs
index 781460bcdb1..52ea3bc9006 100644
--- a/src/HotChocolate/AspNetCore/src/AspNetCore.Pipeline/Instrumentation/IServerDiagnosticEvents.cs
+++ b/src/HotChocolate/AspNetCore/src/AspNetCore.Pipeline/Instrumentation/IServerDiagnosticEvents.cs
@@ -1,4 +1,6 @@
using Microsoft.AspNetCore.Http;
+using HotChocolate.AspNetCore.Subscriptions;
+using HotChocolate.AspNetCore.Subscriptions.Protocols;
using HotChocolate.Language;
namespace HotChocolate.AspNetCore.Instrumentation;
@@ -138,6 +140,21 @@ void StartOperationBatchRequest(
///
IDisposable WebSocketSession(HttpContext context);
+ ///
+ /// Called within the scope once the client's connection
+ /// initialization message has been accepted.
+ ///
+ ///
+ /// The GraphQL socket session.
+ ///
+ ///
+ /// The connection initialization message sent by the client. Its payload is only valid
+ /// for the duration of this call.
+ ///
+ void WebSocketConnectionInitialized(
+ ISocketSession session,
+ IOperationMessagePayload connectionInitMessage);
+
///
/// Called within the scope and signals
/// that an error occurred that terminated the session.
diff --git a/src/HotChocolate/AspNetCore/src/AspNetCore.Pipeline/Instrumentation/NoopServerDiagnosticEventListener.cs b/src/HotChocolate/AspNetCore/src/AspNetCore.Pipeline/Instrumentation/NoopServerDiagnosticEventListener.cs
index 2270cb61880..72c9df2d215 100644
--- a/src/HotChocolate/AspNetCore/src/AspNetCore.Pipeline/Instrumentation/NoopServerDiagnosticEventListener.cs
+++ b/src/HotChocolate/AspNetCore/src/AspNetCore.Pipeline/Instrumentation/NoopServerDiagnosticEventListener.cs
@@ -1,4 +1,6 @@
using Microsoft.AspNetCore.Http;
+using HotChocolate.AspNetCore.Subscriptions;
+using HotChocolate.AspNetCore.Subscriptions.Protocols;
using HotChocolate.Language;
using static HotChocolate.AspNetCore.Instrumentation.ServerDiagnosticEventListener;
@@ -41,6 +43,12 @@ public void ParserErrors(HttpContext context, IReadOnlyList errors)
public IDisposable WebSocketSession(HttpContext context) => EmptyScope;
+ public void WebSocketConnectionInitialized(
+ ISocketSession session,
+ IOperationMessagePayload connectionInitMessage)
+ {
+ }
+
public void WebSocketSessionError(HttpContext context, Exception exception)
{
}
diff --git a/src/HotChocolate/AspNetCore/src/AspNetCore.Pipeline/Instrumentation/ServerDiagnosticEventListener.cs b/src/HotChocolate/AspNetCore/src/AspNetCore.Pipeline/Instrumentation/ServerDiagnosticEventListener.cs
index ee6e9db0de0..5f1772cd182 100644
--- a/src/HotChocolate/AspNetCore/src/AspNetCore.Pipeline/Instrumentation/ServerDiagnosticEventListener.cs
+++ b/src/HotChocolate/AspNetCore/src/AspNetCore.Pipeline/Instrumentation/ServerDiagnosticEventListener.cs
@@ -1,4 +1,6 @@
using Microsoft.AspNetCore.Http;
+using HotChocolate.AspNetCore.Subscriptions;
+using HotChocolate.AspNetCore.Subscriptions.Protocols;
using HotChocolate.Language;
namespace HotChocolate.AspNetCore.Instrumentation;
@@ -64,6 +66,13 @@ public virtual IDisposable FormatHttpResponse(HttpContext context, OperationResu
public virtual IDisposable WebSocketSession(HttpContext context)
=> EmptyScope;
+ ///
+ public virtual void WebSocketConnectionInitialized(
+ ISocketSession session,
+ IOperationMessagePayload connectionInitMessage)
+ {
+ }
+
///
public virtual void WebSocketSessionError(HttpContext context, Exception exception)
{
diff --git a/src/HotChocolate/AspNetCore/src/AspNetCore.Pipeline/Subscriptions/Protocols/Apollo/ApolloSubscriptionProtocolHandler.cs b/src/HotChocolate/AspNetCore/src/AspNetCore.Pipeline/Subscriptions/Protocols/Apollo/ApolloSubscriptionProtocolHandler.cs
index 8a5372ff28b..1d54c295e30 100644
--- a/src/HotChocolate/AspNetCore/src/AspNetCore.Pipeline/Subscriptions/Protocols/Apollo/ApolloSubscriptionProtocolHandler.cs
+++ b/src/HotChocolate/AspNetCore/src/AspNetCore.Pipeline/Subscriptions/Protocols/Apollo/ApolloSubscriptionProtocolHandler.cs
@@ -3,6 +3,7 @@
using System.Runtime.InteropServices;
using System.Text.Json;
using HotChocolate.AspNetCore.Formatters;
+using HotChocolate.AspNetCore.Instrumentation;
using HotChocolate.Buffers;
using HotChocolate.Language;
using HotChocolate.Text.Json;
@@ -18,13 +19,16 @@ internal sealed class ApolloSubscriptionProtocolHandler : IProtocolHandler
{
private readonly ISocketSessionInterceptor _interceptor;
private readonly IWebSocketPayloadFormatter _formatter;
+ private readonly IServerDiagnosticEvents _diagnosticEvents;
public ApolloSubscriptionProtocolHandler(
ISocketSessionInterceptor interceptor,
- IWebSocketPayloadFormatter formatter)
+ IWebSocketPayloadFormatter formatter,
+ IServerDiagnosticEvents diagnosticEvents)
{
_interceptor = interceptor;
_formatter = formatter;
+ _diagnosticEvents = diagnosticEvents;
}
public string Name => GraphQL_WS;
@@ -113,6 +117,7 @@ await _interceptor.OnConnectAsync(
if (connectionStatus.Accepted)
{
((WebSocketConnection)connection).IsConnected = true;
+ _diagnosticEvents.WebSocketConnectionInitialized(session, operationMessageObj);
await SendConnectionAcceptMessage(
session,
connectionStatus.Extensions,
diff --git a/src/HotChocolate/AspNetCore/src/AspNetCore.Pipeline/Subscriptions/Protocols/GraphQLOverWebSocket/GraphQLOverWebSocketProtocolHandler.cs b/src/HotChocolate/AspNetCore/src/AspNetCore.Pipeline/Subscriptions/Protocols/GraphQLOverWebSocket/GraphQLOverWebSocketProtocolHandler.cs
index 373364a99e1..09f029a6bd9 100644
--- a/src/HotChocolate/AspNetCore/src/AspNetCore.Pipeline/Subscriptions/Protocols/GraphQLOverWebSocket/GraphQLOverWebSocketProtocolHandler.cs
+++ b/src/HotChocolate/AspNetCore/src/AspNetCore.Pipeline/Subscriptions/Protocols/GraphQLOverWebSocket/GraphQLOverWebSocketProtocolHandler.cs
@@ -3,6 +3,7 @@
using System.Runtime.InteropServices;
using System.Text.Json;
using HotChocolate.AspNetCore.Formatters;
+using HotChocolate.AspNetCore.Instrumentation;
using HotChocolate.Buffers;
using HotChocolate.Language;
using HotChocolate.AspNetCore.Utilities;
@@ -19,7 +20,8 @@ internal sealed class GraphQLOverWebSocketProtocolHandler(
IWebSocketPayloadFormatter formatter,
IDocumentCache documentCache,
IDocumentHashProvider documentHashProvider,
- ParserOptions parserOptions)
+ ParserOptions parserOptions,
+ IServerDiagnosticEvents diagnosticEvents)
: IGraphQLOverWebSocketProtocolHandler
{
public string Name => GraphQL_Transport_WS;
@@ -128,6 +130,7 @@ await interceptor.OnConnectAsync(
if (connectionStatus.Accepted)
{
((WebSocketConnection)connection).IsConnected = true;
+ diagnosticEvents.WebSocketConnectionInitialized(session, operationMessageObj);
await SendConnectionAcceptMessage(
session,
connectionStatus.Extensions,
diff --git a/src/HotChocolate/AspNetCore/src/AspNetCore/Extensions/HotChocolateAspNetCoreServiceCollectionExtensions.Subscriptions.cs b/src/HotChocolate/AspNetCore/src/AspNetCore/Extensions/HotChocolateAspNetCoreServiceCollectionExtensions.Subscriptions.cs
index 95aa367b0d4..cb06a512ce3 100644
--- a/src/HotChocolate/AspNetCore/src/AspNetCore/Extensions/HotChocolateAspNetCoreServiceCollectionExtensions.Subscriptions.cs
+++ b/src/HotChocolate/AspNetCore/src/AspNetCore/Extensions/HotChocolateAspNetCoreServiceCollectionExtensions.Subscriptions.cs
@@ -2,6 +2,7 @@
using Microsoft.Extensions.DependencyInjection.Extensions;
using HotChocolate.AspNetCore;
using HotChocolate.AspNetCore.Formatters;
+using HotChocolate.AspNetCore.Instrumentation;
using HotChocolate.AspNetCore.Subscriptions.Protocols;
using HotChocolate.AspNetCore.Subscriptions.Protocols.Apollo;
using HotChocolate.AspNetCore.Subscriptions.Protocols.GraphQLOverWebSocket;
@@ -86,7 +87,8 @@ private static IRequestExecutorBuilder AddApolloProtocol(
s => s.AddSingleton(
sp => new ApolloSubscriptionProtocolHandler(
sp.GetRequiredService(),
- sp.GetRequiredService())));
+ sp.GetRequiredService(),
+ sp.GetRequiredService())));
private static IRequestExecutorBuilder AddGraphQLOverWebSocketProtocol(
this IRequestExecutorBuilder builder)
@@ -97,7 +99,8 @@ private static IRequestExecutorBuilder AddGraphQLOverWebSocketProtocol(
sp.GetRequiredService(),
sp.GetRequiredService(),
sp.GetRequiredService(),
- sp.GetRequiredService())));
+ sp.GetRequiredService(),
+ sp.GetRequiredService())));
///
/// Adds a custom WebSocket payload formatter to the DI.
diff --git a/src/HotChocolate/Core/src/Types/Types/Descriptors/Conventions/DescriptorContext.cs b/src/HotChocolate/Core/src/Types/Types/Descriptors/Conventions/DescriptorContext.cs
index 8cb7f493d17..bcdf72682da 100644
--- a/src/HotChocolate/Core/src/Types/Types/Descriptors/Conventions/DescriptorContext.cs
+++ b/src/HotChocolate/Core/src/Types/Types/Descriptors/Conventions/DescriptorContext.cs
@@ -6,6 +6,7 @@
using HotChocolate.Resolvers;
using HotChocolate.Types.Relay;
using HotChocolate.Utilities;
+using EmptyServiceProvider = HotChocolate.Utilities.EmptyServiceProvider;
using ThrowHelper = HotChocolate.Utilities.ThrowHelper;
namespace HotChocolate.Types.Descriptors;
diff --git a/src/HotChocolate/Core/test/Types.Tests/Resolvers/ResolverCompilerTests.cs b/src/HotChocolate/Core/test/Types.Tests/Resolvers/ResolverCompilerTests.cs
index 9b07b50af26..c67c7cb5715 100644
--- a/src/HotChocolate/Core/test/Types.Tests/Resolvers/ResolverCompilerTests.cs
+++ b/src/HotChocolate/Core/test/Types.Tests/Resolvers/ResolverCompilerTests.cs
@@ -9,8 +9,8 @@
using HotChocolate.Tests;
using HotChocolate.Types;
using HotChocolate.Types.Descriptors;
-using HotChocolate.Utilities;
using Moq;
+using EmptyServiceProvider = HotChocolate.Utilities.EmptyServiceProvider;
namespace HotChocolate.Resolvers;
diff --git a/src/HotChocolate/Diagnostics/src/Diagnostics.Core/ActivityEnricherBase.cs b/src/HotChocolate/Diagnostics/src/Diagnostics.Core/ActivityEnricherBase.cs
index 8f276a64d98..bb2e1e49a97 100644
--- a/src/HotChocolate/Diagnostics/src/Diagnostics.Core/ActivityEnricherBase.cs
+++ b/src/HotChocolate/Diagnostics/src/Diagnostics.Core/ActivityEnricherBase.cs
@@ -1,6 +1,8 @@
using System.Diagnostics;
using Microsoft.AspNetCore.Http;
using HotChocolate.AspNetCore.Instrumentation;
+using HotChocolate.AspNetCore.Subscriptions;
+using HotChocolate.AspNetCore.Subscriptions.Protocols;
using HotChocolate.Execution;
using HotChocolate.Language;
@@ -12,6 +14,11 @@ namespace HotChocolate.Diagnostics;
///
public abstract class ActivityEnricherBase
{
+ public virtual void OnWebSocketConnectionInitialized(
+ ISocketSession session,
+ IOperationMessagePayload connectionInitMessage)
+ { }
+
public virtual void EnrichExecuteHttpRequest(
HttpContext httpContext,
HttpRequestKind kind,
diff --git a/src/HotChocolate/Diagnostics/src/Diagnostics/Listeners/ActivityExecutionDiagnosticListener.cs b/src/HotChocolate/Diagnostics/src/Diagnostics/Listeners/ActivityExecutionDiagnosticListener.cs
index e464a81128b..a4d945214ba 100644
--- a/src/HotChocolate/Diagnostics/src/Diagnostics/Listeners/ActivityExecutionDiagnosticListener.cs
+++ b/src/HotChocolate/Diagnostics/src/Diagnostics/Listeners/ActivityExecutionDiagnosticListener.cs
@@ -25,15 +25,18 @@ public override IDisposable ExecuteRequest(RequestContext context)
if (options.SkipExecuteRequest)
{
- if (!options.SkipExecuteHttpRequest
- && context.Features.TryGet(out var httpContext)
- && httpContext.Features.Get() is { } httpRequestSpan)
+ if (options.SkipExecuteHttpRequest
+ || !context.Features.TryGet(out var httpContext))
{
- httpContextActivity = httpRequestSpan.Activity;
+ return EmptyScope;
}
- else
+
+ // A GraphQL over HTTP request reuses the transport span as the root request
+ // span instead of emitting a second one. A transport that has no span of its
+ // own, like a WebSocket session, falls through to a dedicated request span.
+ if (httpContext.Features.Get() is { } httpRequestSpan)
{
- return EmptyScope;
+ httpContextActivity = httpRequestSpan.Activity;
}
}
diff --git a/src/HotChocolate/Diagnostics/src/Diagnostics/Listeners/ActivityServerDiagnosticListener.cs b/src/HotChocolate/Diagnostics/src/Diagnostics/Listeners/ActivityServerDiagnosticListener.cs
index b10d244b12a..c81ef74843e 100644
--- a/src/HotChocolate/Diagnostics/src/Diagnostics/Listeners/ActivityServerDiagnosticListener.cs
+++ b/src/HotChocolate/Diagnostics/src/Diagnostics/Listeners/ActivityServerDiagnosticListener.cs
@@ -1,5 +1,7 @@
using Microsoft.AspNetCore.Http;
using HotChocolate.AspNetCore.Instrumentation;
+using HotChocolate.AspNetCore.Subscriptions;
+using HotChocolate.AspNetCore.Subscriptions.Protocols;
using HotChocolate.Execution;
using HotChocolate.Language;
using static HotChocolate.Diagnostics.HotChocolateActivitySource;
@@ -103,6 +105,11 @@ public override void ParserErrors(HttpContext context, IReadOnlyList err
}
}
+ public override void WebSocketConnectionInitialized(
+ ISocketSession session,
+ IOperationMessagePayload connectionInitMessage)
+ => enricher.OnWebSocketConnectionInitialized(session, connectionInitMessage);
+
public override IDisposable FormatHttpResponse(HttpContext context, OperationResult result)
{
if (options.SkipFormatHttpResponse)
diff --git a/src/HotChocolate/Diagnostics/test/Diagnostics.Tests/ActivityServerDiagnosticListenerTests.cs b/src/HotChocolate/Diagnostics/test/Diagnostics.Tests/ActivityServerDiagnosticListenerTests.cs
index f120ccf54fa..f281240ff33 100644
--- a/src/HotChocolate/Diagnostics/test/Diagnostics.Tests/ActivityServerDiagnosticListenerTests.cs
+++ b/src/HotChocolate/Diagnostics/test/Diagnostics.Tests/ActivityServerDiagnosticListenerTests.cs
@@ -1,13 +1,23 @@
+using System.Diagnostics;
using System.Net.Http.Headers;
+using System.Net.WebSockets;
using System.Text;
+using System.Text.Json;
+using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.TestHost;
using Microsoft.Extensions.DependencyInjection;
+using HotChocolate.AspNetCore.Subscriptions;
+using HotChocolate.AspNetCore.Subscriptions.Protocols;
using HotChocolate.AspNetCore.Tests.Utilities;
+using HotChocolate.AspNetCore.Tests.Utilities.Subscriptions.Apollo;
using HotChocolate.Execution;
using HotChocolate.Execution.Configuration;
+using HotChocolate.Language;
using HotChocolate.Resolvers;
using HotChocolate.Subscriptions;
using HotChocolate.Transport.Http;
+using HotChocolate.Transport.Sockets;
+using HotChocolate.Transport.Sockets.Client;
using HotChocolate.Types;
using static CookieCrumble.TestEnvironment;
using static HotChocolate.Diagnostics.ActivityTestHelper;
@@ -19,6 +29,7 @@ namespace HotChocolate.Diagnostics;
public class ActivityServerDiagnosticListenerTests(TestServerFactory serverFactory) : ServerTestBase(serverFactory)
{
private static readonly Uri s_url = new("http://localhost:5000/graphql");
+ private static readonly Uri s_webSocketUrl = new("ws://localhost:5000/graphql");
[Fact]
public async Task Http_Post_SingleRequest_GetHeroName_Default()
@@ -743,6 +754,482 @@ public async Task Http_Subscription_Event_Should_Be_Error_When_Timeout()
}
}
+ [Fact]
+ public async Task WebSocket_Subscription_Should_Be_Ok_When_Server_Completes_Default()
+ {
+ using var guard = new CancellationTokenSource(TimeSpan.FromSeconds(10));
+
+ using (CaptureActivities(out var activities))
+ {
+ // arrange
+ var signal = new HttpSubscriptionSignal();
+ using var server = CreateInstrumentedServer(
+ configureBuilder: b => b
+ .AddTypeExtension()
+ .Services.AddSingleton(signal));
+ using var webSocket = await ConnectWebSocketAsync(server, guard.Token);
+ await using var client = await SocketClient.ConnectAsync(webSocket, guard.Token);
+ var sender = server.Services.GetRequiredService();
+
+ var request = new OperationRequest("subscription OnMessageSubscription { onMessage }");
+
+ using var result = await client.ExecuteAsync(request, guard.Token);
+ var results = result.ReadResultsAsync().GetAsyncEnumerator(guard.Token);
+
+ // act
+ try
+ {
+ var moveNext = results.MoveNextAsync().AsTask();
+ await signal.Subscribed.Task.WaitAsync(guard.Token);
+ await sender.SendAsync("OnMessage", "hello", guard.Token);
+ Assert.True(await moveNext);
+ await sender.CompleteAsync("OnMessage");
+ Assert.False(await results.MoveNextAsync());
+ }
+ finally
+ {
+ await IgnoreSocketTeardownAsync(results.DisposeAsync().AsTask());
+ }
+
+ // the session encloses every span, so close it before the trace is read
+ await CloseWebSocketAsync(webSocket, guard.Token);
+
+ // assert
+ activities.MatchSnapshot(Postfix([NET11_0]));
+ }
+ }
+
+ [Fact]
+ public async Task WebSocket_Subscription_Should_Be_Ok_When_Server_Completes()
+ {
+ using var guard = new CancellationTokenSource(TimeSpan.FromSeconds(10));
+
+ using (CaptureActivities(out var activities))
+ {
+ // arrange
+ var signal = new HttpSubscriptionSignal();
+ using var server = CreateInstrumentedServer(
+ o => o.Scopes = ActivityScopes.All,
+ b => b
+ .AddTypeExtension()
+ .Services.AddSingleton(signal));
+ using var webSocket = await ConnectWebSocketAsync(server, guard.Token);
+ await using var client = await SocketClient.ConnectAsync(webSocket, guard.Token);
+ var sender = server.Services.GetRequiredService();
+
+ var request = new OperationRequest("subscription OnMessageSubscription { onMessage }");
+
+ using var result = await client.ExecuteAsync(request, guard.Token);
+ var results = result.ReadResultsAsync().GetAsyncEnumerator(guard.Token);
+
+ // act
+ try
+ {
+ var moveNext = results.MoveNextAsync().AsTask();
+ await signal.Subscribed.Task.WaitAsync(guard.Token);
+ await sender.SendAsync("OnMessage", "hello", guard.Token);
+ Assert.True(await moveNext);
+ await sender.CompleteAsync("OnMessage");
+ Assert.False(await results.MoveNextAsync());
+ }
+ finally
+ {
+ await IgnoreSocketTeardownAsync(results.DisposeAsync().AsTask());
+ }
+
+ // the session encloses every span, so close it before the trace is read
+ await CloseWebSocketAsync(webSocket, guard.Token);
+
+ // assert
+ activities.MatchSnapshot(Postfix([NET11_0]));
+ }
+ }
+
+ [Fact]
+ public async Task WebSocket_Subscription_Should_Be_Ok_When_Client_Closes_Connection()
+ {
+ using var guard = new CancellationTokenSource(TimeSpan.FromSeconds(10));
+
+ using (CaptureActivities(out var activities))
+ {
+ // arrange
+ var signal = new HttpSubscriptionSignal();
+ using var server = CreateInstrumentedServer(
+ o => o.Scopes = ActivityScopes.All,
+ b => b
+ .AddTypeExtension()
+ .Services.AddSingleton(signal));
+ using var webSocket = await ConnectWebSocketAsync(server, guard.Token);
+ await using var client = await SocketClient.ConnectAsync(webSocket, guard.Token);
+ var sender = server.Services.GetRequiredService();
+
+ var request = new OperationRequest("subscription OnMessageSubscription { onMessage }");
+
+ using var result = await client.ExecuteAsync(request, guard.Token);
+ var results = result.ReadResultsAsync().GetAsyncEnumerator(guard.Token);
+
+ var moveNext = results.MoveNextAsync().AsTask();
+ await signal.Subscribed.Task.WaitAsync(guard.Token);
+ await sender.SendAsync("OnMessage", "hello", guard.Token);
+ Assert.True(await moveNext);
+
+ // act
+ // close the connection without unsubscribing, so the server has to tear the
+ // still-running subscription down with the session
+ await CloseWebSocketAsync(webSocket, guard.Token);
+ await IgnoreSocketTeardownAsync(results.DisposeAsync().AsTask());
+
+ // assert
+ activities.MatchSnapshot(Postfix([NET11_0]));
+ }
+ }
+
+ [Fact]
+ public async Task WebSocket_Subscription_Event_Should_Be_Error_When_Timeout()
+ {
+ using var guard = new CancellationTokenSource(TimeSpan.FromSeconds(10));
+
+ using (CaptureActivities(out var activities))
+ {
+ // arrange
+ // a blocking resolver plus a tiny timeout forces a server-side event timeout
+ var signal = new HttpSubscriptionSignal();
+ using var server = CreateInstrumentedServer(
+ o => o.Scopes = ActivityScopes.All,
+ b => b
+ .AddTypeExtension()
+ .ModifyRequestOptions(o => o.ExecutionTimeout = TimeSpan.FromMilliseconds(200))
+ .Services.AddSingleton(signal));
+ using var webSocket = await ConnectWebSocketAsync(server, guard.Token);
+ await using var client = await SocketClient.ConnectAsync(webSocket, guard.Token);
+ var sender = server.Services.GetRequiredService();
+
+ var request = new OperationRequest(
+ "subscription OnBlockingMessageSubscription { onBlockingMessage }");
+
+ using var result = await client.ExecuteAsync(request, guard.Token);
+ var results = result.ReadResultsAsync().GetAsyncEnumerator(guard.Token);
+
+ // act
+ // the timeout tears the operation down, so no result is ever delivered
+ var first = results.MoveNextAsync().AsTask();
+ await signal.Subscribed.Task.WaitAsync(guard.Token);
+ await sender.SendAsync("OnBlockingMessage", "hello", guard.Token);
+ await signal.Entered.Task.WaitAsync(guard.Token);
+
+ Assert.False(await first);
+
+ await IgnoreSocketTeardownAsync(results.DisposeAsync().AsTask());
+ await CloseWebSocketAsync(webSocket, guard.Token);
+
+ // assert
+ activities.MatchSnapshot(Postfix([NET11_0]));
+ }
+ }
+
+ [Fact]
+ public async Task WebSocket_Header_Should_Be_Added_As_Tag_To_Request_And_Event_Spans()
+ {
+ using var guard = new CancellationTokenSource(TimeSpan.FromSeconds(10));
+
+ using (CaptureActivities(out var activities))
+ {
+ // arrange
+ // non-browser clients can send the tenant as a handshake header instead
+ var signal = new HttpSubscriptionSignal();
+ using var server = CreateInstrumentedServer(
+ o => o.Scopes = ActivityScopes.All,
+ b => b
+ .AddTypeExtension()
+ .AddApplicationService()
+ .Services
+ .AddSingleton(signal)
+ .AddSingleton());
+ using var webSocket = await ConnectWebSocketAsync(
+ server,
+ guard.Token,
+ r => r.Headers[TenantHeaderName] = "acme-42");
+ await using var client = await SocketClient.ConnectAsync(webSocket, guard.Token);
+ var sender = server.Services.GetRequiredService();
+
+ var request = new OperationRequest("subscription OnMessageSubscription { onMessage }");
+
+ using var result = await client.ExecuteAsync(request, guard.Token);
+ var results = result.ReadResultsAsync().GetAsyncEnumerator(guard.Token);
+
+ // act
+ try
+ {
+ var moveNext = results.MoveNextAsync().AsTask();
+ await signal.Subscribed.Task.WaitAsync(guard.Token);
+ await sender.SendAsync("OnMessage", "hello", guard.Token);
+ Assert.True(await moveNext);
+ await sender.CompleteAsync("OnMessage");
+ Assert.False(await results.MoveNextAsync());
+ }
+ finally
+ {
+ await IgnoreSocketTeardownAsync(results.DisposeAsync().AsTask());
+ }
+
+ await CloseWebSocketAsync(webSocket, guard.Token);
+
+ // assert
+ activities.MatchSnapshot(Postfix([NET11_0]));
+ }
+ }
+
+ [Fact]
+ public async Task WebSocket_ConnectionInit_Payload_Should_Be_Added_As_Tag_To_Request_And_Event_Spans()
+ {
+ using var guard = new CancellationTokenSource(TimeSpan.FromSeconds(10));
+
+ using (CaptureActivities(out var activities))
+ {
+ // arrange
+ var signal = new HttpSubscriptionSignal();
+ using var server = CreateInstrumentedServer(
+ o => o.Scopes = ActivityScopes.All,
+ b => b
+ .AddTypeExtension()
+ .AddApplicationService()
+ .Services
+ .AddSingleton(signal)
+ .AddSingleton());
+ using var webSocket = await ConnectWebSocketAsync(server, guard.Token);
+ var payload = JsonSerializer.SerializeToElement(new { tenant = "acme-42" });
+ await using var client = await SocketClient.ConnectAsync(webSocket, payload, guard.Token);
+ var sender = server.Services.GetRequiredService();
+
+ var request = new OperationRequest("subscription OnMessageSubscription { onMessage }");
+
+ using var result = await client.ExecuteAsync(request, guard.Token);
+ var results = result.ReadResultsAsync().GetAsyncEnumerator(guard.Token);
+
+ // act
+ try
+ {
+ var moveNext = results.MoveNextAsync().AsTask();
+ await signal.Subscribed.Task.WaitAsync(guard.Token);
+ await sender.SendAsync("OnMessage", "hello", guard.Token);
+ Assert.True(await moveNext);
+ await sender.CompleteAsync("OnMessage");
+ Assert.False(await results.MoveNextAsync());
+ }
+ finally
+ {
+ await IgnoreSocketTeardownAsync(results.DisposeAsync().AsTask());
+ }
+
+ await CloseWebSocketAsync(webSocket, guard.Token);
+
+ // assert
+ activities.MatchSnapshot(Postfix([NET11_0]));
+ }
+ }
+
+ [Fact]
+ public async Task WebSocket_Apollo_ConnectionInit_Payload_Should_Be_Added_As_Tag_To_Request_And_Event_Spans()
+ {
+ using var guard = new CancellationTokenSource(TimeSpan.FromSeconds(10));
+
+ using (CaptureActivities(out var activities))
+ {
+ // arrange
+ // the legacy apollo protocol raises the event from its own protocol handler
+ var signal = new HttpSubscriptionSignal();
+ using var server = CreateInstrumentedServer(
+ o => o.Scopes = ActivityScopes.All,
+ b => b
+ .AddTypeExtension()
+ .AddApplicationService()
+ .Services
+ .AddSingleton(signal)
+ .AddSingleton());
+ var sender = server.Services.GetRequiredService();
+
+ var webSocketClient = server.CreateWebSocketClient();
+ webSocketClient.ConfigureRequest =
+ r => r.Headers.SecWebSocketProtocol = WellKnownProtocols.GraphQL_WS;
+ using var webSocket = await webSocketClient.ConnectAsync(s_webSocketUrl, guard.Token);
+
+ await webSocket.SendConnectionInitializeAsync(
+ new Dictionary { ["tenant"] = "acme-42" },
+ guard.Token);
+ Assert.NotNull(await WaitForApolloMessageAsync(webSocket, "connection_ack", guard.Token));
+
+ // act
+ await webSocket.SendSubscriptionStartAsync(
+ "1",
+ new GraphQLRequest(
+ Utf8GraphQLParser.Parse("subscription OnMessageSubscription { onMessage }")));
+ await signal.Subscribed.Task.WaitAsync(guard.Token);
+ await sender.SendAsync("OnMessage", "hello", guard.Token);
+ Assert.NotNull(await WaitForApolloMessageAsync(webSocket, "data", guard.Token));
+
+ await sender.CompleteAsync("OnMessage");
+ Assert.NotNull(await WaitForApolloMessageAsync(webSocket, "complete", guard.Token));
+
+ await CloseWebSocketAsync(webSocket, guard.Token);
+
+ // assert
+ activities.MatchSnapshot(Postfix([NET11_0]));
+ }
+ }
+
+ private static async Task WaitForApolloMessageAsync(
+ WebSocket webSocket,
+ string type,
+ CancellationToken cancellationToken)
+ {
+ using var timeout = new CancellationTokenSource(TimeSpan.FromSeconds(5));
+ using var combined =
+ CancellationTokenSource.CreateLinkedTokenSource(cancellationToken, timeout.Token);
+
+ try
+ {
+ while (!combined.Token.IsCancellationRequested)
+ {
+ var message = await webSocket.ReceiveServerMessageAsync(combined.Token);
+
+ if (message is null)
+ {
+ await Task.Delay(5, combined.Token);
+ continue;
+ }
+
+ if (message.RootElement.GetProperty("type").GetString() == type)
+ {
+ return message;
+ }
+
+ message.Dispose();
+ }
+ }
+ catch (OperationCanceledException)
+ {
+ // expected: no matching message arrived within the timeout
+ }
+
+ return null;
+ }
+
+ private const string TenantHeaderName = "X-Tenant";
+
+ public sealed record TenantFeature(string Tenant);
+
+ public sealed class TenantActivityEnricher(InstrumentationOptions options)
+ : ActivityEnricher(options)
+ {
+ public override void OnWebSocketConnectionInitialized(
+ ISocketSession session,
+ IOperationMessagePayload connectionInitMessage)
+ {
+ if (connectionInitMessage.Payload is { ValueKind: JsonValueKind.Object } payload
+ && payload.TryGetProperty("tenant", out var tenant)
+ && tenant.ValueKind is JsonValueKind.String)
+ {
+ session.Connection.Features.Set(new TenantFeature(tenant.GetString()!));
+ }
+ }
+
+ public override void EnrichExecuteRequest(RequestContext context, Activity activity)
+ {
+ base.EnrichExecuteRequest(context, activity);
+ SetTenantTag(context, activity);
+ }
+
+ public override void EnrichOnSubscriptionEvent(
+ RequestContext context,
+ ulong subscriptionId,
+ Activity activity)
+ {
+ base.EnrichOnSubscriptionEvent(context, subscriptionId, activity);
+ SetTenantTag(context, activity);
+ }
+
+ private static void SetTenantTag(RequestContext context, Activity activity)
+ {
+ if (ResolveTenant(context) is { } tenant)
+ {
+ activity.SetTag("test.tenant", tenant);
+ }
+ }
+
+ private static string? ResolveTenant(RequestContext context)
+ {
+ if (context.ContextData.TryGetValue(nameof(ISocketSession), out var value)
+ && value is ISocketSession session
+ && session.Connection.Features.Get() is { } tenant)
+ {
+ return tenant.Tenant;
+ }
+
+ if (context.Features.Get() is { } httpContext
+ && httpContext.Request.Headers.TryGetValue(TenantHeaderName, out var header))
+ {
+ return header.ToString();
+ }
+
+ return null;
+ }
+ }
+
+ private static async Task ConnectWebSocketAsync(
+ TestServer server,
+ CancellationToken cancellationToken,
+ Action? configureRequest = null)
+ {
+ var webSocketClient = server.CreateWebSocketClient();
+ webSocketClient.ConfigureRequest = r =>
+ {
+ r.Headers.SecWebSocketProtocol = WellKnownProtocols.GraphQL_Transport_WS;
+ configureRequest?.Invoke(r);
+ };
+ return await webSocketClient.ConnectAsync(s_webSocketUrl, cancellationToken);
+ }
+
+ private static async Task CloseWebSocketAsync(
+ WebSocket webSocket,
+ CancellationToken cancellationToken)
+ {
+ try
+ {
+ await webSocket.CloseOutputAsync(
+ WebSocketCloseStatus.NormalClosure,
+ "done",
+ cancellationToken);
+ }
+ catch (Exception ex) when (ex is WebSocketException or IOException or ObjectDisposedException)
+ {
+ // the server can end the session before the close frame is sent
+ }
+ }
+
+ private static async Task IgnoreSocketTeardownAsync(Task task)
+ {
+ try
+ {
+ await task;
+ }
+ catch (OperationCanceledException)
+ {
+ // expected: the streamed read was aborted by the client
+ }
+ catch (IOException)
+ {
+ // expected: aborting an in-flight read can surface as an I/O failure
+ }
+ catch (WebSocketException)
+ {
+ // expected: the socket was aborted while a read was in flight
+ }
+ catch (SocketClosedException)
+ {
+ // expected: the client dropped the connection without a close frame
+ }
+ }
+
private static async Task DrainAsync(
IAsyncEnumerator results,
CancellationToken cancellationToken)
diff --git a/src/HotChocolate/Diagnostics/test/Diagnostics.Tests/__snapshots__/ActivityServerDiagnosticListenerTests.WebSocket_Apollo_ConnectionInit_Payload_Should_Be_Added_As_Tag_To_Request_And_Event_Spans.snap b/src/HotChocolate/Diagnostics/test/Diagnostics.Tests/__snapshots__/ActivityServerDiagnosticListenerTests.WebSocket_Apollo_ConnectionInit_Payload_Should_Be_Added_As_Tag_To_Request_And_Event_Spans.snap
new file mode 100644
index 00000000000..a82c92e88be
--- /dev/null
+++ b/src/HotChocolate/Diagnostics/test/Diagnostics.Tests/__snapshots__/ActivityServerDiagnosticListenerTests.WebSocket_Apollo_ConnectionInit_Payload_Should_Be_Added_As_Tag_To_Request_And_Event_Spans.snap
@@ -0,0 +1,256 @@
+{
+ "source": {
+ "name": "HotChocolate.Diagnostics"
+ },
+ "activities": [
+ {
+ "OperationName": "Microsoft.AspNetCore.Hosting.HttpRequestIn",
+ "DisplayName": "GET /graphql/{**slug}",
+ "Kind": "Server",
+ "Status": "Unset",
+ "tags": [
+ {
+ "Key": "server.address",
+ "Value": "localhost"
+ },
+ {
+ "Key": "server.port",
+ "Value": 5000
+ },
+ {
+ "Key": "http.request.method",
+ "Value": "GET"
+ },
+ {
+ "Key": "url.scheme",
+ "Value": "http"
+ },
+ {
+ "Key": "url.path",
+ "Value": "/graphql"
+ },
+ {
+ "Key": "network.protocol.version",
+ "Value": "1.1"
+ },
+ {
+ "Key": "http.route",
+ "Value": "/graphql/{**slug}"
+ },
+ {
+ "Key": "http.response.status_code",
+ "Value": 101
+ }
+ ],
+ "event": [],
+ "activities": [
+ {
+ "OperationName": "GraphQL Operation",
+ "DisplayName": "subscription",
+ "Kind": "Internal",
+ "Status": "Ok",
+ "tags": [
+ {
+ "Key": "graphql.processing.type",
+ "Value": "request"
+ },
+ {
+ "Key": "server.address",
+ "Value": "localhost"
+ },
+ {
+ "Key": "server.port",
+ "Value": 5000
+ },
+ {
+ "Key": "graphql.operation.type",
+ "Value": "subscription"
+ },
+ {
+ "Key": "graphql.operation.name",
+ "Value": "OnMessageSubscription"
+ },
+ {
+ "Key": "graphql.document.hash",
+ "Value": "md5:ef859b98c9d8c17038c8fd9aeecdb1e2"
+ },
+ {
+ "Key": "test.tenant",
+ "Value": "acme-42"
+ }
+ ],
+ "event": [
+ {
+ "Name": "AddedOperationToCache",
+ "Tags": []
+ },
+ {
+ "Name": "AddedDocumentToCache",
+ "Tags": []
+ }
+ ],
+ "activities": [
+ {
+ "OperationName": "GraphQL Document Validation",
+ "DisplayName": "GraphQL Document Validation",
+ "Kind": "Internal",
+ "Status": "Ok",
+ "tags": [
+ {
+ "Key": "graphql.processing.type",
+ "Value": "validate"
+ },
+ {
+ "Key": "graphql.document.hash",
+ "Value": "md5:ef859b98c9d8c17038c8fd9aeecdb1e2"
+ }
+ ],
+ "event": []
+ },
+ {
+ "OperationName": "GraphQL Complexity Analysis",
+ "DisplayName": "GraphQL Complexity Analysis",
+ "Kind": "Internal",
+ "Status": "Ok",
+ "tags": [
+ {
+ "Key": "graphql.document.hash",
+ "Value": "md5:ef859b98c9d8c17038c8fd9aeecdb1e2"
+ },
+ {
+ "Key": "graphql.operation.fieldCost",
+ "Value": 0.0
+ },
+ {
+ "Key": "graphql.operation.typeCost",
+ "Value": 1.0
+ }
+ ],
+ "event": []
+ },
+ {
+ "OperationName": "GraphQL Operation Planning",
+ "DisplayName": "GraphQL Operation Planning",
+ "Kind": "Internal",
+ "Status": "Ok",
+ "tags": [
+ {
+ "Key": "graphql.processing.type",
+ "Value": "plan"
+ },
+ {
+ "Key": "graphql.document.hash",
+ "Value": "md5:ef859b98c9d8c17038c8fd9aeecdb1e2"
+ },
+ {
+ "Key": "graphql.operation.type",
+ "Value": "subscription"
+ },
+ {
+ "Key": "graphql.operation.name",
+ "Value": "OnMessageSubscription"
+ }
+ ],
+ "event": []
+ },
+ {
+ "OperationName": "GraphQL Operation Execution",
+ "DisplayName": "GraphQL Operation Execution",
+ "Kind": "Internal",
+ "Status": "Ok",
+ "tags": [
+ {
+ "Key": "graphql.processing.type",
+ "Value": "execute"
+ },
+ {
+ "Key": "graphql.operation.type",
+ "Value": "subscription"
+ },
+ {
+ "Key": "graphql.operation.name",
+ "Value": "OnMessageSubscription"
+ },
+ {
+ "Key": "graphql.document.hash",
+ "Value": "md5:ef859b98c9d8c17038c8fd9aeecdb1e2"
+ }
+ ],
+ "event": [],
+ "activities": [
+ {
+ "OperationName": "GraphQL Subscription Event",
+ "DisplayName": "GraphQL Subscription Event",
+ "Kind": "Internal",
+ "Status": "Ok",
+ "tags": [
+ {
+ "Key": "graphql.processing.type",
+ "Value": "subscription_event"
+ },
+ {
+ "Key": "graphql.operation.type",
+ "Value": "subscription"
+ },
+ {
+ "Key": "graphql.operation.name",
+ "Value": "OnMessageSubscription"
+ },
+ {
+ "Key": "graphql.document.hash",
+ "Value": "md5:ef859b98c9d8c17038c8fd9aeecdb1e2"
+ },
+ {
+ "Key": "graphql.subscription.id",
+ "Value": "1"
+ },
+ {
+ "Key": "test.tenant",
+ "Value": "acme-42"
+ }
+ ],
+ "event": [],
+ "activities": [
+ {
+ "OperationName": "Subscription.onMessage",
+ "DisplayName": "Subscription.onMessage",
+ "Kind": "Internal",
+ "Status": "Ok",
+ "tags": [
+ {
+ "Key": "graphql.processing.type",
+ "Value": "resolve"
+ },
+ {
+ "Key": "graphql.field.alias",
+ "Value": "onMessage"
+ },
+ {
+ "Key": "graphql.field.path",
+ "Value": "onMessage"
+ },
+ {
+ "Key": "graphql.field.name",
+ "Value": "onMessage"
+ },
+ {
+ "Key": "graphql.field.schema_coordinate",
+ "Value": "Subscription.onMessage"
+ },
+ {
+ "Key": "graphql.field.parent_type",
+ "Value": "Subscription"
+ }
+ ],
+ "event": []
+ }
+ ]
+ }
+ ]
+ }
+ ]
+ }
+ ]
+ }
+ ]
+}
diff --git a/src/HotChocolate/Diagnostics/test/Diagnostics.Tests/__snapshots__/ActivityServerDiagnosticListenerTests.WebSocket_Apollo_ConnectionInit_Payload_Should_Be_Added_As_Tag_To_Request_And_Event_Spans_NET11_0.snap b/src/HotChocolate/Diagnostics/test/Diagnostics.Tests/__snapshots__/ActivityServerDiagnosticListenerTests.WebSocket_Apollo_ConnectionInit_Payload_Should_Be_Added_As_Tag_To_Request_And_Event_Spans_NET11_0.snap
new file mode 100644
index 00000000000..03affcdb64f
--- /dev/null
+++ b/src/HotChocolate/Diagnostics/test/Diagnostics.Tests/__snapshots__/ActivityServerDiagnosticListenerTests.WebSocket_Apollo_ConnectionInit_Payload_Should_Be_Added_As_Tag_To_Request_And_Event_Spans_NET11_0.snap
@@ -0,0 +1,256 @@
+{
+ "source": {
+ "name": "HotChocolate.Diagnostics"
+ },
+ "activities": [
+ {
+ "OperationName": "Microsoft.AspNetCore.Hosting.HttpRequestIn",
+ "DisplayName": "GET /graphql/{**slug}",
+ "Kind": "Server",
+ "Status": "Unset",
+ "tags": [
+ {
+ "Key": "server.address",
+ "Value": "localhost"
+ },
+ {
+ "Key": "server.port",
+ "Value": 5000
+ },
+ {
+ "Key": "http.request.method",
+ "Value": "GET"
+ },
+ {
+ "Key": "url.scheme",
+ "Value": "http"
+ },
+ {
+ "Key": "url.path",
+ "Value": "/graphql"
+ },
+ {
+ "Key": "network.protocol.version",
+ "Value": "1.1"
+ },
+ {
+ "Key": "http.response.status_code",
+ "Value": 101
+ },
+ {
+ "Key": "http.route",
+ "Value": "/graphql/{**slug}"
+ }
+ ],
+ "event": [],
+ "activities": [
+ {
+ "OperationName": "GraphQL Operation",
+ "DisplayName": "subscription",
+ "Kind": "Internal",
+ "Status": "Ok",
+ "tags": [
+ {
+ "Key": "graphql.processing.type",
+ "Value": "request"
+ },
+ {
+ "Key": "server.address",
+ "Value": "localhost"
+ },
+ {
+ "Key": "server.port",
+ "Value": 5000
+ },
+ {
+ "Key": "graphql.operation.type",
+ "Value": "subscription"
+ },
+ {
+ "Key": "graphql.operation.name",
+ "Value": "OnMessageSubscription"
+ },
+ {
+ "Key": "graphql.document.hash",
+ "Value": "md5:ef859b98c9d8c17038c8fd9aeecdb1e2"
+ },
+ {
+ "Key": "test.tenant",
+ "Value": "acme-42"
+ }
+ ],
+ "event": [
+ {
+ "Name": "AddedOperationToCache",
+ "Tags": []
+ },
+ {
+ "Name": "AddedDocumentToCache",
+ "Tags": []
+ }
+ ],
+ "activities": [
+ {
+ "OperationName": "GraphQL Document Validation",
+ "DisplayName": "GraphQL Document Validation",
+ "Kind": "Internal",
+ "Status": "Ok",
+ "tags": [
+ {
+ "Key": "graphql.processing.type",
+ "Value": "validate"
+ },
+ {
+ "Key": "graphql.document.hash",
+ "Value": "md5:ef859b98c9d8c17038c8fd9aeecdb1e2"
+ }
+ ],
+ "event": []
+ },
+ {
+ "OperationName": "GraphQL Complexity Analysis",
+ "DisplayName": "GraphQL Complexity Analysis",
+ "Kind": "Internal",
+ "Status": "Ok",
+ "tags": [
+ {
+ "Key": "graphql.document.hash",
+ "Value": "md5:ef859b98c9d8c17038c8fd9aeecdb1e2"
+ },
+ {
+ "Key": "graphql.operation.fieldCost",
+ "Value": 0.0
+ },
+ {
+ "Key": "graphql.operation.typeCost",
+ "Value": 1.0
+ }
+ ],
+ "event": []
+ },
+ {
+ "OperationName": "GraphQL Operation Planning",
+ "DisplayName": "GraphQL Operation Planning",
+ "Kind": "Internal",
+ "Status": "Ok",
+ "tags": [
+ {
+ "Key": "graphql.processing.type",
+ "Value": "plan"
+ },
+ {
+ "Key": "graphql.document.hash",
+ "Value": "md5:ef859b98c9d8c17038c8fd9aeecdb1e2"
+ },
+ {
+ "Key": "graphql.operation.type",
+ "Value": "subscription"
+ },
+ {
+ "Key": "graphql.operation.name",
+ "Value": "OnMessageSubscription"
+ }
+ ],
+ "event": []
+ },
+ {
+ "OperationName": "GraphQL Operation Execution",
+ "DisplayName": "GraphQL Operation Execution",
+ "Kind": "Internal",
+ "Status": "Ok",
+ "tags": [
+ {
+ "Key": "graphql.processing.type",
+ "Value": "execute"
+ },
+ {
+ "Key": "graphql.operation.type",
+ "Value": "subscription"
+ },
+ {
+ "Key": "graphql.operation.name",
+ "Value": "OnMessageSubscription"
+ },
+ {
+ "Key": "graphql.document.hash",
+ "Value": "md5:ef859b98c9d8c17038c8fd9aeecdb1e2"
+ }
+ ],
+ "event": [],
+ "activities": [
+ {
+ "OperationName": "GraphQL Subscription Event",
+ "DisplayName": "GraphQL Subscription Event",
+ "Kind": "Internal",
+ "Status": "Ok",
+ "tags": [
+ {
+ "Key": "graphql.processing.type",
+ "Value": "subscription_event"
+ },
+ {
+ "Key": "graphql.operation.type",
+ "Value": "subscription"
+ },
+ {
+ "Key": "graphql.operation.name",
+ "Value": "OnMessageSubscription"
+ },
+ {
+ "Key": "graphql.document.hash",
+ "Value": "md5:ef859b98c9d8c17038c8fd9aeecdb1e2"
+ },
+ {
+ "Key": "graphql.subscription.id",
+ "Value": "1"
+ },
+ {
+ "Key": "test.tenant",
+ "Value": "acme-42"
+ }
+ ],
+ "event": [],
+ "activities": [
+ {
+ "OperationName": "Subscription.onMessage",
+ "DisplayName": "Subscription.onMessage",
+ "Kind": "Internal",
+ "Status": "Ok",
+ "tags": [
+ {
+ "Key": "graphql.processing.type",
+ "Value": "resolve"
+ },
+ {
+ "Key": "graphql.field.alias",
+ "Value": "onMessage"
+ },
+ {
+ "Key": "graphql.field.path",
+ "Value": "onMessage"
+ },
+ {
+ "Key": "graphql.field.name",
+ "Value": "onMessage"
+ },
+ {
+ "Key": "graphql.field.schema_coordinate",
+ "Value": "Subscription.onMessage"
+ },
+ {
+ "Key": "graphql.field.parent_type",
+ "Value": "Subscription"
+ }
+ ],
+ "event": []
+ }
+ ]
+ }
+ ]
+ }
+ ]
+ }
+ ]
+ }
+ ]
+}
diff --git a/src/HotChocolate/Diagnostics/test/Diagnostics.Tests/__snapshots__/ActivityServerDiagnosticListenerTests.WebSocket_ConnectionInit_Payload_Should_Be_Added_As_Tag_To_Request_And_Event_Spans.snap b/src/HotChocolate/Diagnostics/test/Diagnostics.Tests/__snapshots__/ActivityServerDiagnosticListenerTests.WebSocket_ConnectionInit_Payload_Should_Be_Added_As_Tag_To_Request_And_Event_Spans.snap
new file mode 100644
index 00000000000..a82c92e88be
--- /dev/null
+++ b/src/HotChocolate/Diagnostics/test/Diagnostics.Tests/__snapshots__/ActivityServerDiagnosticListenerTests.WebSocket_ConnectionInit_Payload_Should_Be_Added_As_Tag_To_Request_And_Event_Spans.snap
@@ -0,0 +1,256 @@
+{
+ "source": {
+ "name": "HotChocolate.Diagnostics"
+ },
+ "activities": [
+ {
+ "OperationName": "Microsoft.AspNetCore.Hosting.HttpRequestIn",
+ "DisplayName": "GET /graphql/{**slug}",
+ "Kind": "Server",
+ "Status": "Unset",
+ "tags": [
+ {
+ "Key": "server.address",
+ "Value": "localhost"
+ },
+ {
+ "Key": "server.port",
+ "Value": 5000
+ },
+ {
+ "Key": "http.request.method",
+ "Value": "GET"
+ },
+ {
+ "Key": "url.scheme",
+ "Value": "http"
+ },
+ {
+ "Key": "url.path",
+ "Value": "/graphql"
+ },
+ {
+ "Key": "network.protocol.version",
+ "Value": "1.1"
+ },
+ {
+ "Key": "http.route",
+ "Value": "/graphql/{**slug}"
+ },
+ {
+ "Key": "http.response.status_code",
+ "Value": 101
+ }
+ ],
+ "event": [],
+ "activities": [
+ {
+ "OperationName": "GraphQL Operation",
+ "DisplayName": "subscription",
+ "Kind": "Internal",
+ "Status": "Ok",
+ "tags": [
+ {
+ "Key": "graphql.processing.type",
+ "Value": "request"
+ },
+ {
+ "Key": "server.address",
+ "Value": "localhost"
+ },
+ {
+ "Key": "server.port",
+ "Value": 5000
+ },
+ {
+ "Key": "graphql.operation.type",
+ "Value": "subscription"
+ },
+ {
+ "Key": "graphql.operation.name",
+ "Value": "OnMessageSubscription"
+ },
+ {
+ "Key": "graphql.document.hash",
+ "Value": "md5:ef859b98c9d8c17038c8fd9aeecdb1e2"
+ },
+ {
+ "Key": "test.tenant",
+ "Value": "acme-42"
+ }
+ ],
+ "event": [
+ {
+ "Name": "AddedOperationToCache",
+ "Tags": []
+ },
+ {
+ "Name": "AddedDocumentToCache",
+ "Tags": []
+ }
+ ],
+ "activities": [
+ {
+ "OperationName": "GraphQL Document Validation",
+ "DisplayName": "GraphQL Document Validation",
+ "Kind": "Internal",
+ "Status": "Ok",
+ "tags": [
+ {
+ "Key": "graphql.processing.type",
+ "Value": "validate"
+ },
+ {
+ "Key": "graphql.document.hash",
+ "Value": "md5:ef859b98c9d8c17038c8fd9aeecdb1e2"
+ }
+ ],
+ "event": []
+ },
+ {
+ "OperationName": "GraphQL Complexity Analysis",
+ "DisplayName": "GraphQL Complexity Analysis",
+ "Kind": "Internal",
+ "Status": "Ok",
+ "tags": [
+ {
+ "Key": "graphql.document.hash",
+ "Value": "md5:ef859b98c9d8c17038c8fd9aeecdb1e2"
+ },
+ {
+ "Key": "graphql.operation.fieldCost",
+ "Value": 0.0
+ },
+ {
+ "Key": "graphql.operation.typeCost",
+ "Value": 1.0
+ }
+ ],
+ "event": []
+ },
+ {
+ "OperationName": "GraphQL Operation Planning",
+ "DisplayName": "GraphQL Operation Planning",
+ "Kind": "Internal",
+ "Status": "Ok",
+ "tags": [
+ {
+ "Key": "graphql.processing.type",
+ "Value": "plan"
+ },
+ {
+ "Key": "graphql.document.hash",
+ "Value": "md5:ef859b98c9d8c17038c8fd9aeecdb1e2"
+ },
+ {
+ "Key": "graphql.operation.type",
+ "Value": "subscription"
+ },
+ {
+ "Key": "graphql.operation.name",
+ "Value": "OnMessageSubscription"
+ }
+ ],
+ "event": []
+ },
+ {
+ "OperationName": "GraphQL Operation Execution",
+ "DisplayName": "GraphQL Operation Execution",
+ "Kind": "Internal",
+ "Status": "Ok",
+ "tags": [
+ {
+ "Key": "graphql.processing.type",
+ "Value": "execute"
+ },
+ {
+ "Key": "graphql.operation.type",
+ "Value": "subscription"
+ },
+ {
+ "Key": "graphql.operation.name",
+ "Value": "OnMessageSubscription"
+ },
+ {
+ "Key": "graphql.document.hash",
+ "Value": "md5:ef859b98c9d8c17038c8fd9aeecdb1e2"
+ }
+ ],
+ "event": [],
+ "activities": [
+ {
+ "OperationName": "GraphQL Subscription Event",
+ "DisplayName": "GraphQL Subscription Event",
+ "Kind": "Internal",
+ "Status": "Ok",
+ "tags": [
+ {
+ "Key": "graphql.processing.type",
+ "Value": "subscription_event"
+ },
+ {
+ "Key": "graphql.operation.type",
+ "Value": "subscription"
+ },
+ {
+ "Key": "graphql.operation.name",
+ "Value": "OnMessageSubscription"
+ },
+ {
+ "Key": "graphql.document.hash",
+ "Value": "md5:ef859b98c9d8c17038c8fd9aeecdb1e2"
+ },
+ {
+ "Key": "graphql.subscription.id",
+ "Value": "1"
+ },
+ {
+ "Key": "test.tenant",
+ "Value": "acme-42"
+ }
+ ],
+ "event": [],
+ "activities": [
+ {
+ "OperationName": "Subscription.onMessage",
+ "DisplayName": "Subscription.onMessage",
+ "Kind": "Internal",
+ "Status": "Ok",
+ "tags": [
+ {
+ "Key": "graphql.processing.type",
+ "Value": "resolve"
+ },
+ {
+ "Key": "graphql.field.alias",
+ "Value": "onMessage"
+ },
+ {
+ "Key": "graphql.field.path",
+ "Value": "onMessage"
+ },
+ {
+ "Key": "graphql.field.name",
+ "Value": "onMessage"
+ },
+ {
+ "Key": "graphql.field.schema_coordinate",
+ "Value": "Subscription.onMessage"
+ },
+ {
+ "Key": "graphql.field.parent_type",
+ "Value": "Subscription"
+ }
+ ],
+ "event": []
+ }
+ ]
+ }
+ ]
+ }
+ ]
+ }
+ ]
+ }
+ ]
+}
diff --git a/src/HotChocolate/Diagnostics/test/Diagnostics.Tests/__snapshots__/ActivityServerDiagnosticListenerTests.WebSocket_ConnectionInit_Payload_Should_Be_Added_As_Tag_To_Request_And_Event_Spans_NET11_0.snap b/src/HotChocolate/Diagnostics/test/Diagnostics.Tests/__snapshots__/ActivityServerDiagnosticListenerTests.WebSocket_ConnectionInit_Payload_Should_Be_Added_As_Tag_To_Request_And_Event_Spans_NET11_0.snap
new file mode 100644
index 00000000000..03affcdb64f
--- /dev/null
+++ b/src/HotChocolate/Diagnostics/test/Diagnostics.Tests/__snapshots__/ActivityServerDiagnosticListenerTests.WebSocket_ConnectionInit_Payload_Should_Be_Added_As_Tag_To_Request_And_Event_Spans_NET11_0.snap
@@ -0,0 +1,256 @@
+{
+ "source": {
+ "name": "HotChocolate.Diagnostics"
+ },
+ "activities": [
+ {
+ "OperationName": "Microsoft.AspNetCore.Hosting.HttpRequestIn",
+ "DisplayName": "GET /graphql/{**slug}",
+ "Kind": "Server",
+ "Status": "Unset",
+ "tags": [
+ {
+ "Key": "server.address",
+ "Value": "localhost"
+ },
+ {
+ "Key": "server.port",
+ "Value": 5000
+ },
+ {
+ "Key": "http.request.method",
+ "Value": "GET"
+ },
+ {
+ "Key": "url.scheme",
+ "Value": "http"
+ },
+ {
+ "Key": "url.path",
+ "Value": "/graphql"
+ },
+ {
+ "Key": "network.protocol.version",
+ "Value": "1.1"
+ },
+ {
+ "Key": "http.response.status_code",
+ "Value": 101
+ },
+ {
+ "Key": "http.route",
+ "Value": "/graphql/{**slug}"
+ }
+ ],
+ "event": [],
+ "activities": [
+ {
+ "OperationName": "GraphQL Operation",
+ "DisplayName": "subscription",
+ "Kind": "Internal",
+ "Status": "Ok",
+ "tags": [
+ {
+ "Key": "graphql.processing.type",
+ "Value": "request"
+ },
+ {
+ "Key": "server.address",
+ "Value": "localhost"
+ },
+ {
+ "Key": "server.port",
+ "Value": 5000
+ },
+ {
+ "Key": "graphql.operation.type",
+ "Value": "subscription"
+ },
+ {
+ "Key": "graphql.operation.name",
+ "Value": "OnMessageSubscription"
+ },
+ {
+ "Key": "graphql.document.hash",
+ "Value": "md5:ef859b98c9d8c17038c8fd9aeecdb1e2"
+ },
+ {
+ "Key": "test.tenant",
+ "Value": "acme-42"
+ }
+ ],
+ "event": [
+ {
+ "Name": "AddedOperationToCache",
+ "Tags": []
+ },
+ {
+ "Name": "AddedDocumentToCache",
+ "Tags": []
+ }
+ ],
+ "activities": [
+ {
+ "OperationName": "GraphQL Document Validation",
+ "DisplayName": "GraphQL Document Validation",
+ "Kind": "Internal",
+ "Status": "Ok",
+ "tags": [
+ {
+ "Key": "graphql.processing.type",
+ "Value": "validate"
+ },
+ {
+ "Key": "graphql.document.hash",
+ "Value": "md5:ef859b98c9d8c17038c8fd9aeecdb1e2"
+ }
+ ],
+ "event": []
+ },
+ {
+ "OperationName": "GraphQL Complexity Analysis",
+ "DisplayName": "GraphQL Complexity Analysis",
+ "Kind": "Internal",
+ "Status": "Ok",
+ "tags": [
+ {
+ "Key": "graphql.document.hash",
+ "Value": "md5:ef859b98c9d8c17038c8fd9aeecdb1e2"
+ },
+ {
+ "Key": "graphql.operation.fieldCost",
+ "Value": 0.0
+ },
+ {
+ "Key": "graphql.operation.typeCost",
+ "Value": 1.0
+ }
+ ],
+ "event": []
+ },
+ {
+ "OperationName": "GraphQL Operation Planning",
+ "DisplayName": "GraphQL Operation Planning",
+ "Kind": "Internal",
+ "Status": "Ok",
+ "tags": [
+ {
+ "Key": "graphql.processing.type",
+ "Value": "plan"
+ },
+ {
+ "Key": "graphql.document.hash",
+ "Value": "md5:ef859b98c9d8c17038c8fd9aeecdb1e2"
+ },
+ {
+ "Key": "graphql.operation.type",
+ "Value": "subscription"
+ },
+ {
+ "Key": "graphql.operation.name",
+ "Value": "OnMessageSubscription"
+ }
+ ],
+ "event": []
+ },
+ {
+ "OperationName": "GraphQL Operation Execution",
+ "DisplayName": "GraphQL Operation Execution",
+ "Kind": "Internal",
+ "Status": "Ok",
+ "tags": [
+ {
+ "Key": "graphql.processing.type",
+ "Value": "execute"
+ },
+ {
+ "Key": "graphql.operation.type",
+ "Value": "subscription"
+ },
+ {
+ "Key": "graphql.operation.name",
+ "Value": "OnMessageSubscription"
+ },
+ {
+ "Key": "graphql.document.hash",
+ "Value": "md5:ef859b98c9d8c17038c8fd9aeecdb1e2"
+ }
+ ],
+ "event": [],
+ "activities": [
+ {
+ "OperationName": "GraphQL Subscription Event",
+ "DisplayName": "GraphQL Subscription Event",
+ "Kind": "Internal",
+ "Status": "Ok",
+ "tags": [
+ {
+ "Key": "graphql.processing.type",
+ "Value": "subscription_event"
+ },
+ {
+ "Key": "graphql.operation.type",
+ "Value": "subscription"
+ },
+ {
+ "Key": "graphql.operation.name",
+ "Value": "OnMessageSubscription"
+ },
+ {
+ "Key": "graphql.document.hash",
+ "Value": "md5:ef859b98c9d8c17038c8fd9aeecdb1e2"
+ },
+ {
+ "Key": "graphql.subscription.id",
+ "Value": "1"
+ },
+ {
+ "Key": "test.tenant",
+ "Value": "acme-42"
+ }
+ ],
+ "event": [],
+ "activities": [
+ {
+ "OperationName": "Subscription.onMessage",
+ "DisplayName": "Subscription.onMessage",
+ "Kind": "Internal",
+ "Status": "Ok",
+ "tags": [
+ {
+ "Key": "graphql.processing.type",
+ "Value": "resolve"
+ },
+ {
+ "Key": "graphql.field.alias",
+ "Value": "onMessage"
+ },
+ {
+ "Key": "graphql.field.path",
+ "Value": "onMessage"
+ },
+ {
+ "Key": "graphql.field.name",
+ "Value": "onMessage"
+ },
+ {
+ "Key": "graphql.field.schema_coordinate",
+ "Value": "Subscription.onMessage"
+ },
+ {
+ "Key": "graphql.field.parent_type",
+ "Value": "Subscription"
+ }
+ ],
+ "event": []
+ }
+ ]
+ }
+ ]
+ }
+ ]
+ }
+ ]
+ }
+ ]
+}
diff --git a/src/HotChocolate/Diagnostics/test/Diagnostics.Tests/__snapshots__/ActivityServerDiagnosticListenerTests.WebSocket_Header_Should_Be_Added_As_Tag_To_Request_And_Event_Spans.snap b/src/HotChocolate/Diagnostics/test/Diagnostics.Tests/__snapshots__/ActivityServerDiagnosticListenerTests.WebSocket_Header_Should_Be_Added_As_Tag_To_Request_And_Event_Spans.snap
new file mode 100644
index 00000000000..a82c92e88be
--- /dev/null
+++ b/src/HotChocolate/Diagnostics/test/Diagnostics.Tests/__snapshots__/ActivityServerDiagnosticListenerTests.WebSocket_Header_Should_Be_Added_As_Tag_To_Request_And_Event_Spans.snap
@@ -0,0 +1,256 @@
+{
+ "source": {
+ "name": "HotChocolate.Diagnostics"
+ },
+ "activities": [
+ {
+ "OperationName": "Microsoft.AspNetCore.Hosting.HttpRequestIn",
+ "DisplayName": "GET /graphql/{**slug}",
+ "Kind": "Server",
+ "Status": "Unset",
+ "tags": [
+ {
+ "Key": "server.address",
+ "Value": "localhost"
+ },
+ {
+ "Key": "server.port",
+ "Value": 5000
+ },
+ {
+ "Key": "http.request.method",
+ "Value": "GET"
+ },
+ {
+ "Key": "url.scheme",
+ "Value": "http"
+ },
+ {
+ "Key": "url.path",
+ "Value": "/graphql"
+ },
+ {
+ "Key": "network.protocol.version",
+ "Value": "1.1"
+ },
+ {
+ "Key": "http.route",
+ "Value": "/graphql/{**slug}"
+ },
+ {
+ "Key": "http.response.status_code",
+ "Value": 101
+ }
+ ],
+ "event": [],
+ "activities": [
+ {
+ "OperationName": "GraphQL Operation",
+ "DisplayName": "subscription",
+ "Kind": "Internal",
+ "Status": "Ok",
+ "tags": [
+ {
+ "Key": "graphql.processing.type",
+ "Value": "request"
+ },
+ {
+ "Key": "server.address",
+ "Value": "localhost"
+ },
+ {
+ "Key": "server.port",
+ "Value": 5000
+ },
+ {
+ "Key": "graphql.operation.type",
+ "Value": "subscription"
+ },
+ {
+ "Key": "graphql.operation.name",
+ "Value": "OnMessageSubscription"
+ },
+ {
+ "Key": "graphql.document.hash",
+ "Value": "md5:ef859b98c9d8c17038c8fd9aeecdb1e2"
+ },
+ {
+ "Key": "test.tenant",
+ "Value": "acme-42"
+ }
+ ],
+ "event": [
+ {
+ "Name": "AddedOperationToCache",
+ "Tags": []
+ },
+ {
+ "Name": "AddedDocumentToCache",
+ "Tags": []
+ }
+ ],
+ "activities": [
+ {
+ "OperationName": "GraphQL Document Validation",
+ "DisplayName": "GraphQL Document Validation",
+ "Kind": "Internal",
+ "Status": "Ok",
+ "tags": [
+ {
+ "Key": "graphql.processing.type",
+ "Value": "validate"
+ },
+ {
+ "Key": "graphql.document.hash",
+ "Value": "md5:ef859b98c9d8c17038c8fd9aeecdb1e2"
+ }
+ ],
+ "event": []
+ },
+ {
+ "OperationName": "GraphQL Complexity Analysis",
+ "DisplayName": "GraphQL Complexity Analysis",
+ "Kind": "Internal",
+ "Status": "Ok",
+ "tags": [
+ {
+ "Key": "graphql.document.hash",
+ "Value": "md5:ef859b98c9d8c17038c8fd9aeecdb1e2"
+ },
+ {
+ "Key": "graphql.operation.fieldCost",
+ "Value": 0.0
+ },
+ {
+ "Key": "graphql.operation.typeCost",
+ "Value": 1.0
+ }
+ ],
+ "event": []
+ },
+ {
+ "OperationName": "GraphQL Operation Planning",
+ "DisplayName": "GraphQL Operation Planning",
+ "Kind": "Internal",
+ "Status": "Ok",
+ "tags": [
+ {
+ "Key": "graphql.processing.type",
+ "Value": "plan"
+ },
+ {
+ "Key": "graphql.document.hash",
+ "Value": "md5:ef859b98c9d8c17038c8fd9aeecdb1e2"
+ },
+ {
+ "Key": "graphql.operation.type",
+ "Value": "subscription"
+ },
+ {
+ "Key": "graphql.operation.name",
+ "Value": "OnMessageSubscription"
+ }
+ ],
+ "event": []
+ },
+ {
+ "OperationName": "GraphQL Operation Execution",
+ "DisplayName": "GraphQL Operation Execution",
+ "Kind": "Internal",
+ "Status": "Ok",
+ "tags": [
+ {
+ "Key": "graphql.processing.type",
+ "Value": "execute"
+ },
+ {
+ "Key": "graphql.operation.type",
+ "Value": "subscription"
+ },
+ {
+ "Key": "graphql.operation.name",
+ "Value": "OnMessageSubscription"
+ },
+ {
+ "Key": "graphql.document.hash",
+ "Value": "md5:ef859b98c9d8c17038c8fd9aeecdb1e2"
+ }
+ ],
+ "event": [],
+ "activities": [
+ {
+ "OperationName": "GraphQL Subscription Event",
+ "DisplayName": "GraphQL Subscription Event",
+ "Kind": "Internal",
+ "Status": "Ok",
+ "tags": [
+ {
+ "Key": "graphql.processing.type",
+ "Value": "subscription_event"
+ },
+ {
+ "Key": "graphql.operation.type",
+ "Value": "subscription"
+ },
+ {
+ "Key": "graphql.operation.name",
+ "Value": "OnMessageSubscription"
+ },
+ {
+ "Key": "graphql.document.hash",
+ "Value": "md5:ef859b98c9d8c17038c8fd9aeecdb1e2"
+ },
+ {
+ "Key": "graphql.subscription.id",
+ "Value": "1"
+ },
+ {
+ "Key": "test.tenant",
+ "Value": "acme-42"
+ }
+ ],
+ "event": [],
+ "activities": [
+ {
+ "OperationName": "Subscription.onMessage",
+ "DisplayName": "Subscription.onMessage",
+ "Kind": "Internal",
+ "Status": "Ok",
+ "tags": [
+ {
+ "Key": "graphql.processing.type",
+ "Value": "resolve"
+ },
+ {
+ "Key": "graphql.field.alias",
+ "Value": "onMessage"
+ },
+ {
+ "Key": "graphql.field.path",
+ "Value": "onMessage"
+ },
+ {
+ "Key": "graphql.field.name",
+ "Value": "onMessage"
+ },
+ {
+ "Key": "graphql.field.schema_coordinate",
+ "Value": "Subscription.onMessage"
+ },
+ {
+ "Key": "graphql.field.parent_type",
+ "Value": "Subscription"
+ }
+ ],
+ "event": []
+ }
+ ]
+ }
+ ]
+ }
+ ]
+ }
+ ]
+ }
+ ]
+}
diff --git a/src/HotChocolate/Diagnostics/test/Diagnostics.Tests/__snapshots__/ActivityServerDiagnosticListenerTests.WebSocket_Header_Should_Be_Added_As_Tag_To_Request_And_Event_Spans_NET11_0.snap b/src/HotChocolate/Diagnostics/test/Diagnostics.Tests/__snapshots__/ActivityServerDiagnosticListenerTests.WebSocket_Header_Should_Be_Added_As_Tag_To_Request_And_Event_Spans_NET11_0.snap
new file mode 100644
index 00000000000..03affcdb64f
--- /dev/null
+++ b/src/HotChocolate/Diagnostics/test/Diagnostics.Tests/__snapshots__/ActivityServerDiagnosticListenerTests.WebSocket_Header_Should_Be_Added_As_Tag_To_Request_And_Event_Spans_NET11_0.snap
@@ -0,0 +1,256 @@
+{
+ "source": {
+ "name": "HotChocolate.Diagnostics"
+ },
+ "activities": [
+ {
+ "OperationName": "Microsoft.AspNetCore.Hosting.HttpRequestIn",
+ "DisplayName": "GET /graphql/{**slug}",
+ "Kind": "Server",
+ "Status": "Unset",
+ "tags": [
+ {
+ "Key": "server.address",
+ "Value": "localhost"
+ },
+ {
+ "Key": "server.port",
+ "Value": 5000
+ },
+ {
+ "Key": "http.request.method",
+ "Value": "GET"
+ },
+ {
+ "Key": "url.scheme",
+ "Value": "http"
+ },
+ {
+ "Key": "url.path",
+ "Value": "/graphql"
+ },
+ {
+ "Key": "network.protocol.version",
+ "Value": "1.1"
+ },
+ {
+ "Key": "http.response.status_code",
+ "Value": 101
+ },
+ {
+ "Key": "http.route",
+ "Value": "/graphql/{**slug}"
+ }
+ ],
+ "event": [],
+ "activities": [
+ {
+ "OperationName": "GraphQL Operation",
+ "DisplayName": "subscription",
+ "Kind": "Internal",
+ "Status": "Ok",
+ "tags": [
+ {
+ "Key": "graphql.processing.type",
+ "Value": "request"
+ },
+ {
+ "Key": "server.address",
+ "Value": "localhost"
+ },
+ {
+ "Key": "server.port",
+ "Value": 5000
+ },
+ {
+ "Key": "graphql.operation.type",
+ "Value": "subscription"
+ },
+ {
+ "Key": "graphql.operation.name",
+ "Value": "OnMessageSubscription"
+ },
+ {
+ "Key": "graphql.document.hash",
+ "Value": "md5:ef859b98c9d8c17038c8fd9aeecdb1e2"
+ },
+ {
+ "Key": "test.tenant",
+ "Value": "acme-42"
+ }
+ ],
+ "event": [
+ {
+ "Name": "AddedOperationToCache",
+ "Tags": []
+ },
+ {
+ "Name": "AddedDocumentToCache",
+ "Tags": []
+ }
+ ],
+ "activities": [
+ {
+ "OperationName": "GraphQL Document Validation",
+ "DisplayName": "GraphQL Document Validation",
+ "Kind": "Internal",
+ "Status": "Ok",
+ "tags": [
+ {
+ "Key": "graphql.processing.type",
+ "Value": "validate"
+ },
+ {
+ "Key": "graphql.document.hash",
+ "Value": "md5:ef859b98c9d8c17038c8fd9aeecdb1e2"
+ }
+ ],
+ "event": []
+ },
+ {
+ "OperationName": "GraphQL Complexity Analysis",
+ "DisplayName": "GraphQL Complexity Analysis",
+ "Kind": "Internal",
+ "Status": "Ok",
+ "tags": [
+ {
+ "Key": "graphql.document.hash",
+ "Value": "md5:ef859b98c9d8c17038c8fd9aeecdb1e2"
+ },
+ {
+ "Key": "graphql.operation.fieldCost",
+ "Value": 0.0
+ },
+ {
+ "Key": "graphql.operation.typeCost",
+ "Value": 1.0
+ }
+ ],
+ "event": []
+ },
+ {
+ "OperationName": "GraphQL Operation Planning",
+ "DisplayName": "GraphQL Operation Planning",
+ "Kind": "Internal",
+ "Status": "Ok",
+ "tags": [
+ {
+ "Key": "graphql.processing.type",
+ "Value": "plan"
+ },
+ {
+ "Key": "graphql.document.hash",
+ "Value": "md5:ef859b98c9d8c17038c8fd9aeecdb1e2"
+ },
+ {
+ "Key": "graphql.operation.type",
+ "Value": "subscription"
+ },
+ {
+ "Key": "graphql.operation.name",
+ "Value": "OnMessageSubscription"
+ }
+ ],
+ "event": []
+ },
+ {
+ "OperationName": "GraphQL Operation Execution",
+ "DisplayName": "GraphQL Operation Execution",
+ "Kind": "Internal",
+ "Status": "Ok",
+ "tags": [
+ {
+ "Key": "graphql.processing.type",
+ "Value": "execute"
+ },
+ {
+ "Key": "graphql.operation.type",
+ "Value": "subscription"
+ },
+ {
+ "Key": "graphql.operation.name",
+ "Value": "OnMessageSubscription"
+ },
+ {
+ "Key": "graphql.document.hash",
+ "Value": "md5:ef859b98c9d8c17038c8fd9aeecdb1e2"
+ }
+ ],
+ "event": [],
+ "activities": [
+ {
+ "OperationName": "GraphQL Subscription Event",
+ "DisplayName": "GraphQL Subscription Event",
+ "Kind": "Internal",
+ "Status": "Ok",
+ "tags": [
+ {
+ "Key": "graphql.processing.type",
+ "Value": "subscription_event"
+ },
+ {
+ "Key": "graphql.operation.type",
+ "Value": "subscription"
+ },
+ {
+ "Key": "graphql.operation.name",
+ "Value": "OnMessageSubscription"
+ },
+ {
+ "Key": "graphql.document.hash",
+ "Value": "md5:ef859b98c9d8c17038c8fd9aeecdb1e2"
+ },
+ {
+ "Key": "graphql.subscription.id",
+ "Value": "1"
+ },
+ {
+ "Key": "test.tenant",
+ "Value": "acme-42"
+ }
+ ],
+ "event": [],
+ "activities": [
+ {
+ "OperationName": "Subscription.onMessage",
+ "DisplayName": "Subscription.onMessage",
+ "Kind": "Internal",
+ "Status": "Ok",
+ "tags": [
+ {
+ "Key": "graphql.processing.type",
+ "Value": "resolve"
+ },
+ {
+ "Key": "graphql.field.alias",
+ "Value": "onMessage"
+ },
+ {
+ "Key": "graphql.field.path",
+ "Value": "onMessage"
+ },
+ {
+ "Key": "graphql.field.name",
+ "Value": "onMessage"
+ },
+ {
+ "Key": "graphql.field.schema_coordinate",
+ "Value": "Subscription.onMessage"
+ },
+ {
+ "Key": "graphql.field.parent_type",
+ "Value": "Subscription"
+ }
+ ],
+ "event": []
+ }
+ ]
+ }
+ ]
+ }
+ ]
+ }
+ ]
+ }
+ ]
+}
diff --git a/src/HotChocolate/Diagnostics/test/Diagnostics.Tests/__snapshots__/ActivityServerDiagnosticListenerTests.WebSocket_Subscription_Event_Should_Be_Error_When_Timeout.snap b/src/HotChocolate/Diagnostics/test/Diagnostics.Tests/__snapshots__/ActivityServerDiagnosticListenerTests.WebSocket_Subscription_Event_Should_Be_Error_When_Timeout.snap
new file mode 100644
index 00000000000..5caa6a73a68
--- /dev/null
+++ b/src/HotChocolate/Diagnostics/test/Diagnostics.Tests/__snapshots__/ActivityServerDiagnosticListenerTests.WebSocket_Subscription_Event_Should_Be_Error_When_Timeout.snap
@@ -0,0 +1,270 @@
+{
+ "source": {
+ "name": "HotChocolate.Diagnostics"
+ },
+ "activities": [
+ {
+ "OperationName": "Microsoft.AspNetCore.Hosting.HttpRequestIn",
+ "DisplayName": "GET /graphql/{**slug}",
+ "Kind": "Server",
+ "Status": "Unset",
+ "tags": [
+ {
+ "Key": "server.address",
+ "Value": "localhost"
+ },
+ {
+ "Key": "server.port",
+ "Value": 5000
+ },
+ {
+ "Key": "http.request.method",
+ "Value": "GET"
+ },
+ {
+ "Key": "url.scheme",
+ "Value": "http"
+ },
+ {
+ "Key": "url.path",
+ "Value": "/graphql"
+ },
+ {
+ "Key": "network.protocol.version",
+ "Value": "1.1"
+ },
+ {
+ "Key": "http.route",
+ "Value": "/graphql/{**slug}"
+ },
+ {
+ "Key": "http.response.status_code",
+ "Value": 101
+ }
+ ],
+ "event": [],
+ "activities": [
+ {
+ "OperationName": "GraphQL Operation",
+ "DisplayName": "subscription",
+ "Kind": "Internal",
+ "Status": "Ok",
+ "tags": [
+ {
+ "Key": "graphql.processing.type",
+ "Value": "request"
+ },
+ {
+ "Key": "server.address",
+ "Value": "localhost"
+ },
+ {
+ "Key": "server.port",
+ "Value": 5000
+ },
+ {
+ "Key": "graphql.operation.type",
+ "Value": "subscription"
+ },
+ {
+ "Key": "graphql.operation.name",
+ "Value": "OnBlockingMessageSubscription"
+ },
+ {
+ "Key": "graphql.document.hash",
+ "Value": "md5:41faa6dbd0de29d0cdf705c602b506bf"
+ }
+ ],
+ "event": [
+ {
+ "Name": "AddedOperationToCache",
+ "Tags": []
+ },
+ {
+ "Name": "AddedDocumentToCache",
+ "Tags": []
+ }
+ ],
+ "activities": [
+ {
+ "OperationName": "GraphQL Document Validation",
+ "DisplayName": "GraphQL Document Validation",
+ "Kind": "Internal",
+ "Status": "Ok",
+ "tags": [
+ {
+ "Key": "graphql.processing.type",
+ "Value": "validate"
+ },
+ {
+ "Key": "graphql.document.hash",
+ "Value": "md5:41faa6dbd0de29d0cdf705c602b506bf"
+ }
+ ],
+ "event": []
+ },
+ {
+ "OperationName": "GraphQL Complexity Analysis",
+ "DisplayName": "GraphQL Complexity Analysis",
+ "Kind": "Internal",
+ "Status": "Ok",
+ "tags": [
+ {
+ "Key": "graphql.document.hash",
+ "Value": "md5:41faa6dbd0de29d0cdf705c602b506bf"
+ },
+ {
+ "Key": "graphql.operation.fieldCost",
+ "Value": 10.0
+ },
+ {
+ "Key": "graphql.operation.typeCost",
+ "Value": 1.0
+ }
+ ],
+ "event": []
+ },
+ {
+ "OperationName": "GraphQL Operation Planning",
+ "DisplayName": "GraphQL Operation Planning",
+ "Kind": "Internal",
+ "Status": "Ok",
+ "tags": [
+ {
+ "Key": "graphql.processing.type",
+ "Value": "plan"
+ },
+ {
+ "Key": "graphql.document.hash",
+ "Value": "md5:41faa6dbd0de29d0cdf705c602b506bf"
+ },
+ {
+ "Key": "graphql.operation.type",
+ "Value": "subscription"
+ },
+ {
+ "Key": "graphql.operation.name",
+ "Value": "OnBlockingMessageSubscription"
+ }
+ ],
+ "event": []
+ },
+ {
+ "OperationName": "GraphQL Operation Execution",
+ "DisplayName": "GraphQL Operation Execution",
+ "Kind": "Internal",
+ "Status": "Ok",
+ "tags": [
+ {
+ "Key": "graphql.processing.type",
+ "Value": "execute"
+ },
+ {
+ "Key": "graphql.operation.type",
+ "Value": "subscription"
+ },
+ {
+ "Key": "graphql.operation.name",
+ "Value": "OnBlockingMessageSubscription"
+ },
+ {
+ "Key": "graphql.document.hash",
+ "Value": "md5:41faa6dbd0de29d0cdf705c602b506bf"
+ }
+ ],
+ "event": [],
+ "activities": [
+ {
+ "OperationName": "GraphQL Subscription Event",
+ "DisplayName": "GraphQL Subscription Event",
+ "Kind": "Internal",
+ "Status": "Error",
+ "tags": [
+ {
+ "Key": "graphql.processing.type",
+ "Value": "subscription_event"
+ },
+ {
+ "Key": "graphql.operation.type",
+ "Value": "subscription"
+ },
+ {
+ "Key": "graphql.operation.name",
+ "Value": "OnBlockingMessageSubscription"
+ },
+ {
+ "Key": "graphql.document.hash",
+ "Value": "md5:41faa6dbd0de29d0cdf705c602b506bf"
+ },
+ {
+ "Key": "graphql.subscription.id",
+ "Value": "1"
+ },
+ {
+ "Key": "error.type",
+ "Value": "System.OperationCanceledException"
+ }
+ ],
+ "event": [
+ {
+ "Name": "exception",
+ "Tags": [
+ {
+ "Key": "exception.message",
+ "Value": "The operation was canceled."
+ },
+ {
+ "Key": "exception.stacktrace",
+ "Value": "System.OperationCanceledException: The operation was canceled.\n at System.Threading.CancellationToken.ThrowOperationCanceledException()\n at System.Threading.CancellationToken.ThrowIfCancellationRequested()\n at HotChocolate.Execution.Processing.WorkScheduler.ExecuteInternalAsync(IExecutionTask[] buffer) in WorkScheduler.Execute.cs\n at HotChocolate.Execution.Processing.WorkScheduler.ExecuteAsync1() in WorkScheduler.Execute.cs\n at HotChocolate.Execution.Processing.QueryExecutor.ExecuteNoIncrementalAsync(OperationContext operationContext, IImmutableDictionary`2 scopedContext) in QueryExecutor.cs\n at HotChocolate.Execution.Processing.SubscriptionExecutor.Subscription.OnEvent(Object payload) in SubscriptionExecutor.Subscription.cs"
+ },
+ {
+ "Key": "exception.type",
+ "Value": "System.OperationCanceledException"
+ }
+ ]
+ }
+ ],
+ "activities": [
+ {
+ "OperationName": "Subscription.onBlockingMessage",
+ "DisplayName": "Subscription.onBlockingMessage",
+ "Kind": "Internal",
+ "Status": "Unset",
+ "tags": [
+ {
+ "Key": "graphql.processing.type",
+ "Value": "resolve"
+ },
+ {
+ "Key": "graphql.field.alias",
+ "Value": "onBlockingMessage"
+ },
+ {
+ "Key": "graphql.field.path",
+ "Value": "onBlockingMessage"
+ },
+ {
+ "Key": "graphql.field.name",
+ "Value": "onBlockingMessage"
+ },
+ {
+ "Key": "graphql.field.schema_coordinate",
+ "Value": "Subscription.onBlockingMessage"
+ },
+ {
+ "Key": "graphql.field.parent_type",
+ "Value": "Subscription"
+ }
+ ],
+ "event": []
+ }
+ ]
+ }
+ ]
+ }
+ ]
+ }
+ ]
+ }
+ ]
+}
diff --git a/src/HotChocolate/Diagnostics/test/Diagnostics.Tests/__snapshots__/ActivityServerDiagnosticListenerTests.WebSocket_Subscription_Event_Should_Be_Error_When_Timeout_NET11_0.snap b/src/HotChocolate/Diagnostics/test/Diagnostics.Tests/__snapshots__/ActivityServerDiagnosticListenerTests.WebSocket_Subscription_Event_Should_Be_Error_When_Timeout_NET11_0.snap
new file mode 100644
index 00000000000..03fc8c10618
--- /dev/null
+++ b/src/HotChocolate/Diagnostics/test/Diagnostics.Tests/__snapshots__/ActivityServerDiagnosticListenerTests.WebSocket_Subscription_Event_Should_Be_Error_When_Timeout_NET11_0.snap
@@ -0,0 +1,270 @@
+{
+ "source": {
+ "name": "HotChocolate.Diagnostics"
+ },
+ "activities": [
+ {
+ "OperationName": "Microsoft.AspNetCore.Hosting.HttpRequestIn",
+ "DisplayName": "GET /graphql/{**slug}",
+ "Kind": "Server",
+ "Status": "Unset",
+ "tags": [
+ {
+ "Key": "server.address",
+ "Value": "localhost"
+ },
+ {
+ "Key": "server.port",
+ "Value": 5000
+ },
+ {
+ "Key": "http.request.method",
+ "Value": "GET"
+ },
+ {
+ "Key": "url.scheme",
+ "Value": "http"
+ },
+ {
+ "Key": "url.path",
+ "Value": "/graphql"
+ },
+ {
+ "Key": "network.protocol.version",
+ "Value": "1.1"
+ },
+ {
+ "Key": "http.response.status_code",
+ "Value": 101
+ },
+ {
+ "Key": "http.route",
+ "Value": "/graphql/{**slug}"
+ }
+ ],
+ "event": [],
+ "activities": [
+ {
+ "OperationName": "GraphQL Operation",
+ "DisplayName": "subscription",
+ "Kind": "Internal",
+ "Status": "Ok",
+ "tags": [
+ {
+ "Key": "graphql.processing.type",
+ "Value": "request"
+ },
+ {
+ "Key": "server.address",
+ "Value": "localhost"
+ },
+ {
+ "Key": "server.port",
+ "Value": 5000
+ },
+ {
+ "Key": "graphql.operation.type",
+ "Value": "subscription"
+ },
+ {
+ "Key": "graphql.operation.name",
+ "Value": "OnBlockingMessageSubscription"
+ },
+ {
+ "Key": "graphql.document.hash",
+ "Value": "md5:41faa6dbd0de29d0cdf705c602b506bf"
+ }
+ ],
+ "event": [
+ {
+ "Name": "AddedOperationToCache",
+ "Tags": []
+ },
+ {
+ "Name": "AddedDocumentToCache",
+ "Tags": []
+ }
+ ],
+ "activities": [
+ {
+ "OperationName": "GraphQL Document Validation",
+ "DisplayName": "GraphQL Document Validation",
+ "Kind": "Internal",
+ "Status": "Ok",
+ "tags": [
+ {
+ "Key": "graphql.processing.type",
+ "Value": "validate"
+ },
+ {
+ "Key": "graphql.document.hash",
+ "Value": "md5:41faa6dbd0de29d0cdf705c602b506bf"
+ }
+ ],
+ "event": []
+ },
+ {
+ "OperationName": "GraphQL Complexity Analysis",
+ "DisplayName": "GraphQL Complexity Analysis",
+ "Kind": "Internal",
+ "Status": "Ok",
+ "tags": [
+ {
+ "Key": "graphql.document.hash",
+ "Value": "md5:41faa6dbd0de29d0cdf705c602b506bf"
+ },
+ {
+ "Key": "graphql.operation.fieldCost",
+ "Value": 10.0
+ },
+ {
+ "Key": "graphql.operation.typeCost",
+ "Value": 1.0
+ }
+ ],
+ "event": []
+ },
+ {
+ "OperationName": "GraphQL Operation Planning",
+ "DisplayName": "GraphQL Operation Planning",
+ "Kind": "Internal",
+ "Status": "Ok",
+ "tags": [
+ {
+ "Key": "graphql.processing.type",
+ "Value": "plan"
+ },
+ {
+ "Key": "graphql.document.hash",
+ "Value": "md5:41faa6dbd0de29d0cdf705c602b506bf"
+ },
+ {
+ "Key": "graphql.operation.type",
+ "Value": "subscription"
+ },
+ {
+ "Key": "graphql.operation.name",
+ "Value": "OnBlockingMessageSubscription"
+ }
+ ],
+ "event": []
+ },
+ {
+ "OperationName": "GraphQL Operation Execution",
+ "DisplayName": "GraphQL Operation Execution",
+ "Kind": "Internal",
+ "Status": "Ok",
+ "tags": [
+ {
+ "Key": "graphql.processing.type",
+ "Value": "execute"
+ },
+ {
+ "Key": "graphql.operation.type",
+ "Value": "subscription"
+ },
+ {
+ "Key": "graphql.operation.name",
+ "Value": "OnBlockingMessageSubscription"
+ },
+ {
+ "Key": "graphql.document.hash",
+ "Value": "md5:41faa6dbd0de29d0cdf705c602b506bf"
+ }
+ ],
+ "event": [],
+ "activities": [
+ {
+ "OperationName": "GraphQL Subscription Event",
+ "DisplayName": "GraphQL Subscription Event",
+ "Kind": "Internal",
+ "Status": "Error",
+ "tags": [
+ {
+ "Key": "graphql.processing.type",
+ "Value": "subscription_event"
+ },
+ {
+ "Key": "graphql.operation.type",
+ "Value": "subscription"
+ },
+ {
+ "Key": "graphql.operation.name",
+ "Value": "OnBlockingMessageSubscription"
+ },
+ {
+ "Key": "graphql.document.hash",
+ "Value": "md5:41faa6dbd0de29d0cdf705c602b506bf"
+ },
+ {
+ "Key": "graphql.subscription.id",
+ "Value": "1"
+ },
+ {
+ "Key": "error.type",
+ "Value": "System.OperationCanceledException"
+ }
+ ],
+ "event": [
+ {
+ "Name": "exception",
+ "Tags": [
+ {
+ "Key": "exception.message",
+ "Value": "The operation was canceled."
+ },
+ {
+ "Key": "exception.stacktrace",
+ "Value": "System.OperationCanceledException: The operation was canceled.\n at System.Threading.CancellationToken.ThrowOperationCanceledException()\n at System.Threading.CancellationToken.ThrowIfCancellationRequested()\n at HotChocolate.Execution.Processing.WorkScheduler.ExecuteInternalAsync(IExecutionTask[] buffer) in WorkScheduler.Execute.cs\n at HotChocolate.Execution.Processing.WorkScheduler.ExecuteAsync1() in WorkScheduler.Execute.cs\n at HotChocolate.Execution.Processing.QueryExecutor.ExecuteNoIncrementalAsync(OperationContext operationContext, IImmutableDictionary`2 scopedContext) in QueryExecutor.cs\n at HotChocolate.Execution.Processing.SubscriptionExecutor.Subscription.OnEvent(Object payload) in SubscriptionExecutor.Subscription.cs"
+ },
+ {
+ "Key": "exception.type",
+ "Value": "System.OperationCanceledException"
+ }
+ ]
+ }
+ ],
+ "activities": [
+ {
+ "OperationName": "Subscription.onBlockingMessage",
+ "DisplayName": "Subscription.onBlockingMessage",
+ "Kind": "Internal",
+ "Status": "Unset",
+ "tags": [
+ {
+ "Key": "graphql.processing.type",
+ "Value": "resolve"
+ },
+ {
+ "Key": "graphql.field.alias",
+ "Value": "onBlockingMessage"
+ },
+ {
+ "Key": "graphql.field.path",
+ "Value": "onBlockingMessage"
+ },
+ {
+ "Key": "graphql.field.name",
+ "Value": "onBlockingMessage"
+ },
+ {
+ "Key": "graphql.field.schema_coordinate",
+ "Value": "Subscription.onBlockingMessage"
+ },
+ {
+ "Key": "graphql.field.parent_type",
+ "Value": "Subscription"
+ }
+ ],
+ "event": []
+ }
+ ]
+ }
+ ]
+ }
+ ]
+ }
+ ]
+ }
+ ]
+}
diff --git a/src/HotChocolate/Diagnostics/test/Diagnostics.Tests/__snapshots__/ActivityServerDiagnosticListenerTests.WebSocket_Subscription_Should_Be_Ok_When_Client_Closes_Connection.snap b/src/HotChocolate/Diagnostics/test/Diagnostics.Tests/__snapshots__/ActivityServerDiagnosticListenerTests.WebSocket_Subscription_Should_Be_Ok_When_Client_Closes_Connection.snap
new file mode 100644
index 00000000000..95bcd1d95d2
--- /dev/null
+++ b/src/HotChocolate/Diagnostics/test/Diagnostics.Tests/__snapshots__/ActivityServerDiagnosticListenerTests.WebSocket_Subscription_Should_Be_Ok_When_Client_Closes_Connection.snap
@@ -0,0 +1,248 @@
+{
+ "source": {
+ "name": "HotChocolate.Diagnostics"
+ },
+ "activities": [
+ {
+ "OperationName": "Microsoft.AspNetCore.Hosting.HttpRequestIn",
+ "DisplayName": "GET /graphql/{**slug}",
+ "Kind": "Server",
+ "Status": "Unset",
+ "tags": [
+ {
+ "Key": "server.address",
+ "Value": "localhost"
+ },
+ {
+ "Key": "server.port",
+ "Value": 5000
+ },
+ {
+ "Key": "http.request.method",
+ "Value": "GET"
+ },
+ {
+ "Key": "url.scheme",
+ "Value": "http"
+ },
+ {
+ "Key": "url.path",
+ "Value": "/graphql"
+ },
+ {
+ "Key": "network.protocol.version",
+ "Value": "1.1"
+ },
+ {
+ "Key": "http.route",
+ "Value": "/graphql/{**slug}"
+ },
+ {
+ "Key": "http.response.status_code",
+ "Value": 101
+ }
+ ],
+ "event": [],
+ "activities": [
+ {
+ "OperationName": "GraphQL Operation",
+ "DisplayName": "subscription",
+ "Kind": "Internal",
+ "Status": "Ok",
+ "tags": [
+ {
+ "Key": "graphql.processing.type",
+ "Value": "request"
+ },
+ {
+ "Key": "server.address",
+ "Value": "localhost"
+ },
+ {
+ "Key": "server.port",
+ "Value": 5000
+ },
+ {
+ "Key": "graphql.operation.type",
+ "Value": "subscription"
+ },
+ {
+ "Key": "graphql.operation.name",
+ "Value": "OnMessageSubscription"
+ },
+ {
+ "Key": "graphql.document.hash",
+ "Value": "md5:ef859b98c9d8c17038c8fd9aeecdb1e2"
+ }
+ ],
+ "event": [
+ {
+ "Name": "AddedOperationToCache",
+ "Tags": []
+ },
+ {
+ "Name": "AddedDocumentToCache",
+ "Tags": []
+ }
+ ],
+ "activities": [
+ {
+ "OperationName": "GraphQL Document Validation",
+ "DisplayName": "GraphQL Document Validation",
+ "Kind": "Internal",
+ "Status": "Ok",
+ "tags": [
+ {
+ "Key": "graphql.processing.type",
+ "Value": "validate"
+ },
+ {
+ "Key": "graphql.document.hash",
+ "Value": "md5:ef859b98c9d8c17038c8fd9aeecdb1e2"
+ }
+ ],
+ "event": []
+ },
+ {
+ "OperationName": "GraphQL Complexity Analysis",
+ "DisplayName": "GraphQL Complexity Analysis",
+ "Kind": "Internal",
+ "Status": "Ok",
+ "tags": [
+ {
+ "Key": "graphql.document.hash",
+ "Value": "md5:ef859b98c9d8c17038c8fd9aeecdb1e2"
+ },
+ {
+ "Key": "graphql.operation.fieldCost",
+ "Value": 0.0
+ },
+ {
+ "Key": "graphql.operation.typeCost",
+ "Value": 1.0
+ }
+ ],
+ "event": []
+ },
+ {
+ "OperationName": "GraphQL Operation Planning",
+ "DisplayName": "GraphQL Operation Planning",
+ "Kind": "Internal",
+ "Status": "Ok",
+ "tags": [
+ {
+ "Key": "graphql.processing.type",
+ "Value": "plan"
+ },
+ {
+ "Key": "graphql.document.hash",
+ "Value": "md5:ef859b98c9d8c17038c8fd9aeecdb1e2"
+ },
+ {
+ "Key": "graphql.operation.type",
+ "Value": "subscription"
+ },
+ {
+ "Key": "graphql.operation.name",
+ "Value": "OnMessageSubscription"
+ }
+ ],
+ "event": []
+ },
+ {
+ "OperationName": "GraphQL Operation Execution",
+ "DisplayName": "GraphQL Operation Execution",
+ "Kind": "Internal",
+ "Status": "Ok",
+ "tags": [
+ {
+ "Key": "graphql.processing.type",
+ "Value": "execute"
+ },
+ {
+ "Key": "graphql.operation.type",
+ "Value": "subscription"
+ },
+ {
+ "Key": "graphql.operation.name",
+ "Value": "OnMessageSubscription"
+ },
+ {
+ "Key": "graphql.document.hash",
+ "Value": "md5:ef859b98c9d8c17038c8fd9aeecdb1e2"
+ }
+ ],
+ "event": [],
+ "activities": [
+ {
+ "OperationName": "GraphQL Subscription Event",
+ "DisplayName": "GraphQL Subscription Event",
+ "Kind": "Internal",
+ "Status": "Ok",
+ "tags": [
+ {
+ "Key": "graphql.processing.type",
+ "Value": "subscription_event"
+ },
+ {
+ "Key": "graphql.operation.type",
+ "Value": "subscription"
+ },
+ {
+ "Key": "graphql.operation.name",
+ "Value": "OnMessageSubscription"
+ },
+ {
+ "Key": "graphql.document.hash",
+ "Value": "md5:ef859b98c9d8c17038c8fd9aeecdb1e2"
+ },
+ {
+ "Key": "graphql.subscription.id",
+ "Value": "1"
+ }
+ ],
+ "event": [],
+ "activities": [
+ {
+ "OperationName": "Subscription.onMessage",
+ "DisplayName": "Subscription.onMessage",
+ "Kind": "Internal",
+ "Status": "Ok",
+ "tags": [
+ {
+ "Key": "graphql.processing.type",
+ "Value": "resolve"
+ },
+ {
+ "Key": "graphql.field.alias",
+ "Value": "onMessage"
+ },
+ {
+ "Key": "graphql.field.path",
+ "Value": "onMessage"
+ },
+ {
+ "Key": "graphql.field.name",
+ "Value": "onMessage"
+ },
+ {
+ "Key": "graphql.field.schema_coordinate",
+ "Value": "Subscription.onMessage"
+ },
+ {
+ "Key": "graphql.field.parent_type",
+ "Value": "Subscription"
+ }
+ ],
+ "event": []
+ }
+ ]
+ }
+ ]
+ }
+ ]
+ }
+ ]
+ }
+ ]
+}
diff --git a/src/HotChocolate/Diagnostics/test/Diagnostics.Tests/__snapshots__/ActivityServerDiagnosticListenerTests.WebSocket_Subscription_Should_Be_Ok_When_Client_Closes_Connection_NET11_0.snap b/src/HotChocolate/Diagnostics/test/Diagnostics.Tests/__snapshots__/ActivityServerDiagnosticListenerTests.WebSocket_Subscription_Should_Be_Ok_When_Client_Closes_Connection_NET11_0.snap
new file mode 100644
index 00000000000..cd7dc12365c
--- /dev/null
+++ b/src/HotChocolate/Diagnostics/test/Diagnostics.Tests/__snapshots__/ActivityServerDiagnosticListenerTests.WebSocket_Subscription_Should_Be_Ok_When_Client_Closes_Connection_NET11_0.snap
@@ -0,0 +1,248 @@
+{
+ "source": {
+ "name": "HotChocolate.Diagnostics"
+ },
+ "activities": [
+ {
+ "OperationName": "Microsoft.AspNetCore.Hosting.HttpRequestIn",
+ "DisplayName": "GET /graphql/{**slug}",
+ "Kind": "Server",
+ "Status": "Unset",
+ "tags": [
+ {
+ "Key": "server.address",
+ "Value": "localhost"
+ },
+ {
+ "Key": "server.port",
+ "Value": 5000
+ },
+ {
+ "Key": "http.request.method",
+ "Value": "GET"
+ },
+ {
+ "Key": "url.scheme",
+ "Value": "http"
+ },
+ {
+ "Key": "url.path",
+ "Value": "/graphql"
+ },
+ {
+ "Key": "network.protocol.version",
+ "Value": "1.1"
+ },
+ {
+ "Key": "http.response.status_code",
+ "Value": 101
+ },
+ {
+ "Key": "http.route",
+ "Value": "/graphql/{**slug}"
+ }
+ ],
+ "event": [],
+ "activities": [
+ {
+ "OperationName": "GraphQL Operation",
+ "DisplayName": "subscription",
+ "Kind": "Internal",
+ "Status": "Ok",
+ "tags": [
+ {
+ "Key": "graphql.processing.type",
+ "Value": "request"
+ },
+ {
+ "Key": "server.address",
+ "Value": "localhost"
+ },
+ {
+ "Key": "server.port",
+ "Value": 5000
+ },
+ {
+ "Key": "graphql.operation.type",
+ "Value": "subscription"
+ },
+ {
+ "Key": "graphql.operation.name",
+ "Value": "OnMessageSubscription"
+ },
+ {
+ "Key": "graphql.document.hash",
+ "Value": "md5:ef859b98c9d8c17038c8fd9aeecdb1e2"
+ }
+ ],
+ "event": [
+ {
+ "Name": "AddedOperationToCache",
+ "Tags": []
+ },
+ {
+ "Name": "AddedDocumentToCache",
+ "Tags": []
+ }
+ ],
+ "activities": [
+ {
+ "OperationName": "GraphQL Document Validation",
+ "DisplayName": "GraphQL Document Validation",
+ "Kind": "Internal",
+ "Status": "Ok",
+ "tags": [
+ {
+ "Key": "graphql.processing.type",
+ "Value": "validate"
+ },
+ {
+ "Key": "graphql.document.hash",
+ "Value": "md5:ef859b98c9d8c17038c8fd9aeecdb1e2"
+ }
+ ],
+ "event": []
+ },
+ {
+ "OperationName": "GraphQL Complexity Analysis",
+ "DisplayName": "GraphQL Complexity Analysis",
+ "Kind": "Internal",
+ "Status": "Ok",
+ "tags": [
+ {
+ "Key": "graphql.document.hash",
+ "Value": "md5:ef859b98c9d8c17038c8fd9aeecdb1e2"
+ },
+ {
+ "Key": "graphql.operation.fieldCost",
+ "Value": 0.0
+ },
+ {
+ "Key": "graphql.operation.typeCost",
+ "Value": 1.0
+ }
+ ],
+ "event": []
+ },
+ {
+ "OperationName": "GraphQL Operation Planning",
+ "DisplayName": "GraphQL Operation Planning",
+ "Kind": "Internal",
+ "Status": "Ok",
+ "tags": [
+ {
+ "Key": "graphql.processing.type",
+ "Value": "plan"
+ },
+ {
+ "Key": "graphql.document.hash",
+ "Value": "md5:ef859b98c9d8c17038c8fd9aeecdb1e2"
+ },
+ {
+ "Key": "graphql.operation.type",
+ "Value": "subscription"
+ },
+ {
+ "Key": "graphql.operation.name",
+ "Value": "OnMessageSubscription"
+ }
+ ],
+ "event": []
+ },
+ {
+ "OperationName": "GraphQL Operation Execution",
+ "DisplayName": "GraphQL Operation Execution",
+ "Kind": "Internal",
+ "Status": "Ok",
+ "tags": [
+ {
+ "Key": "graphql.processing.type",
+ "Value": "execute"
+ },
+ {
+ "Key": "graphql.operation.type",
+ "Value": "subscription"
+ },
+ {
+ "Key": "graphql.operation.name",
+ "Value": "OnMessageSubscription"
+ },
+ {
+ "Key": "graphql.document.hash",
+ "Value": "md5:ef859b98c9d8c17038c8fd9aeecdb1e2"
+ }
+ ],
+ "event": [],
+ "activities": [
+ {
+ "OperationName": "GraphQL Subscription Event",
+ "DisplayName": "GraphQL Subscription Event",
+ "Kind": "Internal",
+ "Status": "Ok",
+ "tags": [
+ {
+ "Key": "graphql.processing.type",
+ "Value": "subscription_event"
+ },
+ {
+ "Key": "graphql.operation.type",
+ "Value": "subscription"
+ },
+ {
+ "Key": "graphql.operation.name",
+ "Value": "OnMessageSubscription"
+ },
+ {
+ "Key": "graphql.document.hash",
+ "Value": "md5:ef859b98c9d8c17038c8fd9aeecdb1e2"
+ },
+ {
+ "Key": "graphql.subscription.id",
+ "Value": "1"
+ }
+ ],
+ "event": [],
+ "activities": [
+ {
+ "OperationName": "Subscription.onMessage",
+ "DisplayName": "Subscription.onMessage",
+ "Kind": "Internal",
+ "Status": "Ok",
+ "tags": [
+ {
+ "Key": "graphql.processing.type",
+ "Value": "resolve"
+ },
+ {
+ "Key": "graphql.field.alias",
+ "Value": "onMessage"
+ },
+ {
+ "Key": "graphql.field.path",
+ "Value": "onMessage"
+ },
+ {
+ "Key": "graphql.field.name",
+ "Value": "onMessage"
+ },
+ {
+ "Key": "graphql.field.schema_coordinate",
+ "Value": "Subscription.onMessage"
+ },
+ {
+ "Key": "graphql.field.parent_type",
+ "Value": "Subscription"
+ }
+ ],
+ "event": []
+ }
+ ]
+ }
+ ]
+ }
+ ]
+ }
+ ]
+ }
+ ]
+}
diff --git a/src/HotChocolate/Diagnostics/test/Diagnostics.Tests/__snapshots__/ActivityServerDiagnosticListenerTests.WebSocket_Subscription_Should_Be_Ok_When_Server_Completes.snap b/src/HotChocolate/Diagnostics/test/Diagnostics.Tests/__snapshots__/ActivityServerDiagnosticListenerTests.WebSocket_Subscription_Should_Be_Ok_When_Server_Completes.snap
new file mode 100644
index 00000000000..95bcd1d95d2
--- /dev/null
+++ b/src/HotChocolate/Diagnostics/test/Diagnostics.Tests/__snapshots__/ActivityServerDiagnosticListenerTests.WebSocket_Subscription_Should_Be_Ok_When_Server_Completes.snap
@@ -0,0 +1,248 @@
+{
+ "source": {
+ "name": "HotChocolate.Diagnostics"
+ },
+ "activities": [
+ {
+ "OperationName": "Microsoft.AspNetCore.Hosting.HttpRequestIn",
+ "DisplayName": "GET /graphql/{**slug}",
+ "Kind": "Server",
+ "Status": "Unset",
+ "tags": [
+ {
+ "Key": "server.address",
+ "Value": "localhost"
+ },
+ {
+ "Key": "server.port",
+ "Value": 5000
+ },
+ {
+ "Key": "http.request.method",
+ "Value": "GET"
+ },
+ {
+ "Key": "url.scheme",
+ "Value": "http"
+ },
+ {
+ "Key": "url.path",
+ "Value": "/graphql"
+ },
+ {
+ "Key": "network.protocol.version",
+ "Value": "1.1"
+ },
+ {
+ "Key": "http.route",
+ "Value": "/graphql/{**slug}"
+ },
+ {
+ "Key": "http.response.status_code",
+ "Value": 101
+ }
+ ],
+ "event": [],
+ "activities": [
+ {
+ "OperationName": "GraphQL Operation",
+ "DisplayName": "subscription",
+ "Kind": "Internal",
+ "Status": "Ok",
+ "tags": [
+ {
+ "Key": "graphql.processing.type",
+ "Value": "request"
+ },
+ {
+ "Key": "server.address",
+ "Value": "localhost"
+ },
+ {
+ "Key": "server.port",
+ "Value": 5000
+ },
+ {
+ "Key": "graphql.operation.type",
+ "Value": "subscription"
+ },
+ {
+ "Key": "graphql.operation.name",
+ "Value": "OnMessageSubscription"
+ },
+ {
+ "Key": "graphql.document.hash",
+ "Value": "md5:ef859b98c9d8c17038c8fd9aeecdb1e2"
+ }
+ ],
+ "event": [
+ {
+ "Name": "AddedOperationToCache",
+ "Tags": []
+ },
+ {
+ "Name": "AddedDocumentToCache",
+ "Tags": []
+ }
+ ],
+ "activities": [
+ {
+ "OperationName": "GraphQL Document Validation",
+ "DisplayName": "GraphQL Document Validation",
+ "Kind": "Internal",
+ "Status": "Ok",
+ "tags": [
+ {
+ "Key": "graphql.processing.type",
+ "Value": "validate"
+ },
+ {
+ "Key": "graphql.document.hash",
+ "Value": "md5:ef859b98c9d8c17038c8fd9aeecdb1e2"
+ }
+ ],
+ "event": []
+ },
+ {
+ "OperationName": "GraphQL Complexity Analysis",
+ "DisplayName": "GraphQL Complexity Analysis",
+ "Kind": "Internal",
+ "Status": "Ok",
+ "tags": [
+ {
+ "Key": "graphql.document.hash",
+ "Value": "md5:ef859b98c9d8c17038c8fd9aeecdb1e2"
+ },
+ {
+ "Key": "graphql.operation.fieldCost",
+ "Value": 0.0
+ },
+ {
+ "Key": "graphql.operation.typeCost",
+ "Value": 1.0
+ }
+ ],
+ "event": []
+ },
+ {
+ "OperationName": "GraphQL Operation Planning",
+ "DisplayName": "GraphQL Operation Planning",
+ "Kind": "Internal",
+ "Status": "Ok",
+ "tags": [
+ {
+ "Key": "graphql.processing.type",
+ "Value": "plan"
+ },
+ {
+ "Key": "graphql.document.hash",
+ "Value": "md5:ef859b98c9d8c17038c8fd9aeecdb1e2"
+ },
+ {
+ "Key": "graphql.operation.type",
+ "Value": "subscription"
+ },
+ {
+ "Key": "graphql.operation.name",
+ "Value": "OnMessageSubscription"
+ }
+ ],
+ "event": []
+ },
+ {
+ "OperationName": "GraphQL Operation Execution",
+ "DisplayName": "GraphQL Operation Execution",
+ "Kind": "Internal",
+ "Status": "Ok",
+ "tags": [
+ {
+ "Key": "graphql.processing.type",
+ "Value": "execute"
+ },
+ {
+ "Key": "graphql.operation.type",
+ "Value": "subscription"
+ },
+ {
+ "Key": "graphql.operation.name",
+ "Value": "OnMessageSubscription"
+ },
+ {
+ "Key": "graphql.document.hash",
+ "Value": "md5:ef859b98c9d8c17038c8fd9aeecdb1e2"
+ }
+ ],
+ "event": [],
+ "activities": [
+ {
+ "OperationName": "GraphQL Subscription Event",
+ "DisplayName": "GraphQL Subscription Event",
+ "Kind": "Internal",
+ "Status": "Ok",
+ "tags": [
+ {
+ "Key": "graphql.processing.type",
+ "Value": "subscription_event"
+ },
+ {
+ "Key": "graphql.operation.type",
+ "Value": "subscription"
+ },
+ {
+ "Key": "graphql.operation.name",
+ "Value": "OnMessageSubscription"
+ },
+ {
+ "Key": "graphql.document.hash",
+ "Value": "md5:ef859b98c9d8c17038c8fd9aeecdb1e2"
+ },
+ {
+ "Key": "graphql.subscription.id",
+ "Value": "1"
+ }
+ ],
+ "event": [],
+ "activities": [
+ {
+ "OperationName": "Subscription.onMessage",
+ "DisplayName": "Subscription.onMessage",
+ "Kind": "Internal",
+ "Status": "Ok",
+ "tags": [
+ {
+ "Key": "graphql.processing.type",
+ "Value": "resolve"
+ },
+ {
+ "Key": "graphql.field.alias",
+ "Value": "onMessage"
+ },
+ {
+ "Key": "graphql.field.path",
+ "Value": "onMessage"
+ },
+ {
+ "Key": "graphql.field.name",
+ "Value": "onMessage"
+ },
+ {
+ "Key": "graphql.field.schema_coordinate",
+ "Value": "Subscription.onMessage"
+ },
+ {
+ "Key": "graphql.field.parent_type",
+ "Value": "Subscription"
+ }
+ ],
+ "event": []
+ }
+ ]
+ }
+ ]
+ }
+ ]
+ }
+ ]
+ }
+ ]
+}
diff --git a/src/HotChocolate/Diagnostics/test/Diagnostics.Tests/__snapshots__/ActivityServerDiagnosticListenerTests.WebSocket_Subscription_Should_Be_Ok_When_Server_Completes_Default.snap b/src/HotChocolate/Diagnostics/test/Diagnostics.Tests/__snapshots__/ActivityServerDiagnosticListenerTests.WebSocket_Subscription_Should_Be_Ok_When_Server_Completes_Default.snap
new file mode 100644
index 00000000000..a360db43ff4
--- /dev/null
+++ b/src/HotChocolate/Diagnostics/test/Diagnostics.Tests/__snapshots__/ActivityServerDiagnosticListenerTests.WebSocket_Subscription_Should_Be_Ok_When_Server_Completes_Default.snap
@@ -0,0 +1,200 @@
+{
+ "source": {
+ "name": "HotChocolate.Diagnostics"
+ },
+ "activities": [
+ {
+ "OperationName": "Microsoft.AspNetCore.Hosting.HttpRequestIn",
+ "DisplayName": "GET /graphql/{**slug}",
+ "Kind": "Server",
+ "Status": "Unset",
+ "tags": [
+ {
+ "Key": "server.address",
+ "Value": "localhost"
+ },
+ {
+ "Key": "server.port",
+ "Value": 5000
+ },
+ {
+ "Key": "http.request.method",
+ "Value": "GET"
+ },
+ {
+ "Key": "url.scheme",
+ "Value": "http"
+ },
+ {
+ "Key": "url.path",
+ "Value": "/graphql"
+ },
+ {
+ "Key": "network.protocol.version",
+ "Value": "1.1"
+ },
+ {
+ "Key": "http.route",
+ "Value": "/graphql/{**slug}"
+ },
+ {
+ "Key": "http.response.status_code",
+ "Value": 101
+ }
+ ],
+ "event": [],
+ "activities": [
+ {
+ "OperationName": "GraphQL Operation",
+ "DisplayName": "subscription",
+ "Kind": "Internal",
+ "Status": "Ok",
+ "tags": [
+ {
+ "Key": "graphql.processing.type",
+ "Value": "request"
+ },
+ {
+ "Key": "server.address",
+ "Value": "localhost"
+ },
+ {
+ "Key": "server.port",
+ "Value": 5000
+ },
+ {
+ "Key": "graphql.operation.type",
+ "Value": "subscription"
+ },
+ {
+ "Key": "graphql.operation.name",
+ "Value": "OnMessageSubscription"
+ },
+ {
+ "Key": "graphql.document.hash",
+ "Value": "md5:ef859b98c9d8c17038c8fd9aeecdb1e2"
+ }
+ ],
+ "event": [
+ {
+ "Name": "AddedOperationToCache",
+ "Tags": []
+ },
+ {
+ "Name": "AddedDocumentToCache",
+ "Tags": []
+ }
+ ],
+ "activities": [
+ {
+ "OperationName": "GraphQL Document Validation",
+ "DisplayName": "GraphQL Document Validation",
+ "Kind": "Internal",
+ "Status": "Ok",
+ "tags": [
+ {
+ "Key": "graphql.processing.type",
+ "Value": "validate"
+ },
+ {
+ "Key": "graphql.document.hash",
+ "Value": "md5:ef859b98c9d8c17038c8fd9aeecdb1e2"
+ }
+ ],
+ "event": []
+ },
+ {
+ "OperationName": "GraphQL Operation Planning",
+ "DisplayName": "GraphQL Operation Planning",
+ "Kind": "Internal",
+ "Status": "Ok",
+ "tags": [
+ {
+ "Key": "graphql.processing.type",
+ "Value": "plan"
+ },
+ {
+ "Key": "graphql.document.hash",
+ "Value": "md5:ef859b98c9d8c17038c8fd9aeecdb1e2"
+ },
+ {
+ "Key": "graphql.operation.type",
+ "Value": "subscription"
+ },
+ {
+ "Key": "graphql.operation.name",
+ "Value": "OnMessageSubscription"
+ }
+ ],
+ "event": []
+ },
+ {
+ "OperationName": "GraphQL Subscription Event",
+ "DisplayName": "GraphQL Subscription Event",
+ "Kind": "Internal",
+ "Status": "Ok",
+ "tags": [
+ {
+ "Key": "graphql.processing.type",
+ "Value": "subscription_event"
+ },
+ {
+ "Key": "graphql.operation.type",
+ "Value": "subscription"
+ },
+ {
+ "Key": "graphql.operation.name",
+ "Value": "OnMessageSubscription"
+ },
+ {
+ "Key": "graphql.document.hash",
+ "Value": "md5:ef859b98c9d8c17038c8fd9aeecdb1e2"
+ },
+ {
+ "Key": "graphql.subscription.id",
+ "Value": "1"
+ }
+ ],
+ "event": [],
+ "activities": [
+ {
+ "OperationName": "Subscription.onMessage",
+ "DisplayName": "Subscription.onMessage",
+ "Kind": "Internal",
+ "Status": "Ok",
+ "tags": [
+ {
+ "Key": "graphql.processing.type",
+ "Value": "resolve"
+ },
+ {
+ "Key": "graphql.field.alias",
+ "Value": "onMessage"
+ },
+ {
+ "Key": "graphql.field.path",
+ "Value": "onMessage"
+ },
+ {
+ "Key": "graphql.field.name",
+ "Value": "onMessage"
+ },
+ {
+ "Key": "graphql.field.schema_coordinate",
+ "Value": "Subscription.onMessage"
+ },
+ {
+ "Key": "graphql.field.parent_type",
+ "Value": "Subscription"
+ }
+ ],
+ "event": []
+ }
+ ]
+ }
+ ]
+ }
+ ]
+ }
+ ]
+}
diff --git a/src/HotChocolate/Diagnostics/test/Diagnostics.Tests/__snapshots__/ActivityServerDiagnosticListenerTests.WebSocket_Subscription_Should_Be_Ok_When_Server_Completes_Default_NET11_0.snap b/src/HotChocolate/Diagnostics/test/Diagnostics.Tests/__snapshots__/ActivityServerDiagnosticListenerTests.WebSocket_Subscription_Should_Be_Ok_When_Server_Completes_Default_NET11_0.snap
new file mode 100644
index 00000000000..a5a0a47fc94
--- /dev/null
+++ b/src/HotChocolate/Diagnostics/test/Diagnostics.Tests/__snapshots__/ActivityServerDiagnosticListenerTests.WebSocket_Subscription_Should_Be_Ok_When_Server_Completes_Default_NET11_0.snap
@@ -0,0 +1,200 @@
+{
+ "source": {
+ "name": "HotChocolate.Diagnostics"
+ },
+ "activities": [
+ {
+ "OperationName": "Microsoft.AspNetCore.Hosting.HttpRequestIn",
+ "DisplayName": "GET /graphql/{**slug}",
+ "Kind": "Server",
+ "Status": "Unset",
+ "tags": [
+ {
+ "Key": "server.address",
+ "Value": "localhost"
+ },
+ {
+ "Key": "server.port",
+ "Value": 5000
+ },
+ {
+ "Key": "http.request.method",
+ "Value": "GET"
+ },
+ {
+ "Key": "url.scheme",
+ "Value": "http"
+ },
+ {
+ "Key": "url.path",
+ "Value": "/graphql"
+ },
+ {
+ "Key": "network.protocol.version",
+ "Value": "1.1"
+ },
+ {
+ "Key": "http.response.status_code",
+ "Value": 101
+ },
+ {
+ "Key": "http.route",
+ "Value": "/graphql/{**slug}"
+ }
+ ],
+ "event": [],
+ "activities": [
+ {
+ "OperationName": "GraphQL Operation",
+ "DisplayName": "subscription",
+ "Kind": "Internal",
+ "Status": "Ok",
+ "tags": [
+ {
+ "Key": "graphql.processing.type",
+ "Value": "request"
+ },
+ {
+ "Key": "server.address",
+ "Value": "localhost"
+ },
+ {
+ "Key": "server.port",
+ "Value": 5000
+ },
+ {
+ "Key": "graphql.operation.type",
+ "Value": "subscription"
+ },
+ {
+ "Key": "graphql.operation.name",
+ "Value": "OnMessageSubscription"
+ },
+ {
+ "Key": "graphql.document.hash",
+ "Value": "md5:ef859b98c9d8c17038c8fd9aeecdb1e2"
+ }
+ ],
+ "event": [
+ {
+ "Name": "AddedOperationToCache",
+ "Tags": []
+ },
+ {
+ "Name": "AddedDocumentToCache",
+ "Tags": []
+ }
+ ],
+ "activities": [
+ {
+ "OperationName": "GraphQL Document Validation",
+ "DisplayName": "GraphQL Document Validation",
+ "Kind": "Internal",
+ "Status": "Ok",
+ "tags": [
+ {
+ "Key": "graphql.processing.type",
+ "Value": "validate"
+ },
+ {
+ "Key": "graphql.document.hash",
+ "Value": "md5:ef859b98c9d8c17038c8fd9aeecdb1e2"
+ }
+ ],
+ "event": []
+ },
+ {
+ "OperationName": "GraphQL Operation Planning",
+ "DisplayName": "GraphQL Operation Planning",
+ "Kind": "Internal",
+ "Status": "Ok",
+ "tags": [
+ {
+ "Key": "graphql.processing.type",
+ "Value": "plan"
+ },
+ {
+ "Key": "graphql.document.hash",
+ "Value": "md5:ef859b98c9d8c17038c8fd9aeecdb1e2"
+ },
+ {
+ "Key": "graphql.operation.type",
+ "Value": "subscription"
+ },
+ {
+ "Key": "graphql.operation.name",
+ "Value": "OnMessageSubscription"
+ }
+ ],
+ "event": []
+ },
+ {
+ "OperationName": "GraphQL Subscription Event",
+ "DisplayName": "GraphQL Subscription Event",
+ "Kind": "Internal",
+ "Status": "Ok",
+ "tags": [
+ {
+ "Key": "graphql.processing.type",
+ "Value": "subscription_event"
+ },
+ {
+ "Key": "graphql.operation.type",
+ "Value": "subscription"
+ },
+ {
+ "Key": "graphql.operation.name",
+ "Value": "OnMessageSubscription"
+ },
+ {
+ "Key": "graphql.document.hash",
+ "Value": "md5:ef859b98c9d8c17038c8fd9aeecdb1e2"
+ },
+ {
+ "Key": "graphql.subscription.id",
+ "Value": "1"
+ }
+ ],
+ "event": [],
+ "activities": [
+ {
+ "OperationName": "Subscription.onMessage",
+ "DisplayName": "Subscription.onMessage",
+ "Kind": "Internal",
+ "Status": "Ok",
+ "tags": [
+ {
+ "Key": "graphql.processing.type",
+ "Value": "resolve"
+ },
+ {
+ "Key": "graphql.field.alias",
+ "Value": "onMessage"
+ },
+ {
+ "Key": "graphql.field.path",
+ "Value": "onMessage"
+ },
+ {
+ "Key": "graphql.field.name",
+ "Value": "onMessage"
+ },
+ {
+ "Key": "graphql.field.schema_coordinate",
+ "Value": "Subscription.onMessage"
+ },
+ {
+ "Key": "graphql.field.parent_type",
+ "Value": "Subscription"
+ }
+ ],
+ "event": []
+ }
+ ]
+ }
+ ]
+ }
+ ]
+ }
+ ]
+}
diff --git a/src/HotChocolate/Diagnostics/test/Diagnostics.Tests/__snapshots__/ActivityServerDiagnosticListenerTests.WebSocket_Subscription_Should_Be_Ok_When_Server_Completes_NET11_0.snap b/src/HotChocolate/Diagnostics/test/Diagnostics.Tests/__snapshots__/ActivityServerDiagnosticListenerTests.WebSocket_Subscription_Should_Be_Ok_When_Server_Completes_NET11_0.snap
new file mode 100644
index 00000000000..cd7dc12365c
--- /dev/null
+++ b/src/HotChocolate/Diagnostics/test/Diagnostics.Tests/__snapshots__/ActivityServerDiagnosticListenerTests.WebSocket_Subscription_Should_Be_Ok_When_Server_Completes_NET11_0.snap
@@ -0,0 +1,248 @@
+{
+ "source": {
+ "name": "HotChocolate.Diagnostics"
+ },
+ "activities": [
+ {
+ "OperationName": "Microsoft.AspNetCore.Hosting.HttpRequestIn",
+ "DisplayName": "GET /graphql/{**slug}",
+ "Kind": "Server",
+ "Status": "Unset",
+ "tags": [
+ {
+ "Key": "server.address",
+ "Value": "localhost"
+ },
+ {
+ "Key": "server.port",
+ "Value": 5000
+ },
+ {
+ "Key": "http.request.method",
+ "Value": "GET"
+ },
+ {
+ "Key": "url.scheme",
+ "Value": "http"
+ },
+ {
+ "Key": "url.path",
+ "Value": "/graphql"
+ },
+ {
+ "Key": "network.protocol.version",
+ "Value": "1.1"
+ },
+ {
+ "Key": "http.response.status_code",
+ "Value": 101
+ },
+ {
+ "Key": "http.route",
+ "Value": "/graphql/{**slug}"
+ }
+ ],
+ "event": [],
+ "activities": [
+ {
+ "OperationName": "GraphQL Operation",
+ "DisplayName": "subscription",
+ "Kind": "Internal",
+ "Status": "Ok",
+ "tags": [
+ {
+ "Key": "graphql.processing.type",
+ "Value": "request"
+ },
+ {
+ "Key": "server.address",
+ "Value": "localhost"
+ },
+ {
+ "Key": "server.port",
+ "Value": 5000
+ },
+ {
+ "Key": "graphql.operation.type",
+ "Value": "subscription"
+ },
+ {
+ "Key": "graphql.operation.name",
+ "Value": "OnMessageSubscription"
+ },
+ {
+ "Key": "graphql.document.hash",
+ "Value": "md5:ef859b98c9d8c17038c8fd9aeecdb1e2"
+ }
+ ],
+ "event": [
+ {
+ "Name": "AddedOperationToCache",
+ "Tags": []
+ },
+ {
+ "Name": "AddedDocumentToCache",
+ "Tags": []
+ }
+ ],
+ "activities": [
+ {
+ "OperationName": "GraphQL Document Validation",
+ "DisplayName": "GraphQL Document Validation",
+ "Kind": "Internal",
+ "Status": "Ok",
+ "tags": [
+ {
+ "Key": "graphql.processing.type",
+ "Value": "validate"
+ },
+ {
+ "Key": "graphql.document.hash",
+ "Value": "md5:ef859b98c9d8c17038c8fd9aeecdb1e2"
+ }
+ ],
+ "event": []
+ },
+ {
+ "OperationName": "GraphQL Complexity Analysis",
+ "DisplayName": "GraphQL Complexity Analysis",
+ "Kind": "Internal",
+ "Status": "Ok",
+ "tags": [
+ {
+ "Key": "graphql.document.hash",
+ "Value": "md5:ef859b98c9d8c17038c8fd9aeecdb1e2"
+ },
+ {
+ "Key": "graphql.operation.fieldCost",
+ "Value": 0.0
+ },
+ {
+ "Key": "graphql.operation.typeCost",
+ "Value": 1.0
+ }
+ ],
+ "event": []
+ },
+ {
+ "OperationName": "GraphQL Operation Planning",
+ "DisplayName": "GraphQL Operation Planning",
+ "Kind": "Internal",
+ "Status": "Ok",
+ "tags": [
+ {
+ "Key": "graphql.processing.type",
+ "Value": "plan"
+ },
+ {
+ "Key": "graphql.document.hash",
+ "Value": "md5:ef859b98c9d8c17038c8fd9aeecdb1e2"
+ },
+ {
+ "Key": "graphql.operation.type",
+ "Value": "subscription"
+ },
+ {
+ "Key": "graphql.operation.name",
+ "Value": "OnMessageSubscription"
+ }
+ ],
+ "event": []
+ },
+ {
+ "OperationName": "GraphQL Operation Execution",
+ "DisplayName": "GraphQL Operation Execution",
+ "Kind": "Internal",
+ "Status": "Ok",
+ "tags": [
+ {
+ "Key": "graphql.processing.type",
+ "Value": "execute"
+ },
+ {
+ "Key": "graphql.operation.type",
+ "Value": "subscription"
+ },
+ {
+ "Key": "graphql.operation.name",
+ "Value": "OnMessageSubscription"
+ },
+ {
+ "Key": "graphql.document.hash",
+ "Value": "md5:ef859b98c9d8c17038c8fd9aeecdb1e2"
+ }
+ ],
+ "event": [],
+ "activities": [
+ {
+ "OperationName": "GraphQL Subscription Event",
+ "DisplayName": "GraphQL Subscription Event",
+ "Kind": "Internal",
+ "Status": "Ok",
+ "tags": [
+ {
+ "Key": "graphql.processing.type",
+ "Value": "subscription_event"
+ },
+ {
+ "Key": "graphql.operation.type",
+ "Value": "subscription"
+ },
+ {
+ "Key": "graphql.operation.name",
+ "Value": "OnMessageSubscription"
+ },
+ {
+ "Key": "graphql.document.hash",
+ "Value": "md5:ef859b98c9d8c17038c8fd9aeecdb1e2"
+ },
+ {
+ "Key": "graphql.subscription.id",
+ "Value": "1"
+ }
+ ],
+ "event": [],
+ "activities": [
+ {
+ "OperationName": "Subscription.onMessage",
+ "DisplayName": "Subscription.onMessage",
+ "Kind": "Internal",
+ "Status": "Ok",
+ "tags": [
+ {
+ "Key": "graphql.processing.type",
+ "Value": "resolve"
+ },
+ {
+ "Key": "graphql.field.alias",
+ "Value": "onMessage"
+ },
+ {
+ "Key": "graphql.field.path",
+ "Value": "onMessage"
+ },
+ {
+ "Key": "graphql.field.name",
+ "Value": "onMessage"
+ },
+ {
+ "Key": "graphql.field.schema_coordinate",
+ "Value": "Subscription.onMessage"
+ },
+ {
+ "Key": "graphql.field.parent_type",
+ "Value": "Subscription"
+ }
+ ],
+ "event": []
+ }
+ ]
+ }
+ ]
+ }
+ ]
+ }
+ ]
+ }
+ ]
+}
diff --git a/src/HotChocolate/Fusion/src/Fusion.AspNetCore/DependencyInjection/FusionServerServiceCollectionExtensions.cs b/src/HotChocolate/Fusion/src/Fusion.AspNetCore/DependencyInjection/FusionServerServiceCollectionExtensions.cs
index db4f19a125f..644c1374500 100644
--- a/src/HotChocolate/Fusion/src/Fusion.AspNetCore/DependencyInjection/FusionServerServiceCollectionExtensions.cs
+++ b/src/HotChocolate/Fusion/src/Fusion.AspNetCore/DependencyInjection/FusionServerServiceCollectionExtensions.cs
@@ -123,7 +123,8 @@ private static IFusionGatewayBuilder AddApolloProtocol(
(_, s) => s.AddSingleton(
sp => new ApolloSubscriptionProtocolHandler(
sp.GetRequiredService(),
- sp.GetRequiredService())));
+ sp.GetRequiredService(),
+ sp.GetRequiredService())));
private static IFusionGatewayBuilder AddGraphQLOverWebSocketProtocol(
this IFusionGatewayBuilder builder)
@@ -134,5 +135,6 @@ private static IFusionGatewayBuilder AddGraphQLOverWebSocketProtocol(
sp.GetRequiredService(),
sp.GetRequiredService(),
sp.GetRequiredService(),
- sp.GetRequiredService())));
+ sp.GetRequiredService(),
+ sp.GetRequiredService())));
}
diff --git a/src/HotChocolate/Fusion/src/Fusion.Diagnostics/Listeners/FusionActivityExecutionDiagnosticEventListener.cs b/src/HotChocolate/Fusion/src/Fusion.Diagnostics/Listeners/FusionActivityExecutionDiagnosticEventListener.cs
index 15ed77be47f..3c8e42d88b7 100644
--- a/src/HotChocolate/Fusion/src/Fusion.Diagnostics/Listeners/FusionActivityExecutionDiagnosticEventListener.cs
+++ b/src/HotChocolate/Fusion/src/Fusion.Diagnostics/Listeners/FusionActivityExecutionDiagnosticEventListener.cs
@@ -21,15 +21,18 @@ public override IDisposable ExecuteRequest(RequestContext context)
if (options.SkipExecuteRequest)
{
- if (!options.SkipExecuteHttpRequest
- && context.Features.TryGet(out var httpContext)
- && httpContext.Features.Get() is { } httpRequestSpan)
+ if (options.SkipExecuteHttpRequest
+ || !context.Features.TryGet(out var httpContext))
{
- httpContextActivity = httpRequestSpan.Activity;
+ return EmptyScope;
}
- else
+
+ // A GraphQL over HTTP request reuses the transport span as the root request
+ // span instead of emitting a second one. A transport that has no span of its
+ // own, like a WebSocket session, falls through to a dedicated request span.
+ if (httpContext.Features.Get() is { } httpRequestSpan)
{
- return EmptyScope;
+ httpContextActivity = httpRequestSpan.Activity;
}
}
diff --git a/src/HotChocolate/Fusion/src/Fusion.Diagnostics/Listeners/FusionActivityServerDiagnosticListener.cs b/src/HotChocolate/Fusion/src/Fusion.Diagnostics/Listeners/FusionActivityServerDiagnosticListener.cs
index 2bfb286ed56..6ed4d2e2ce3 100644
--- a/src/HotChocolate/Fusion/src/Fusion.Diagnostics/Listeners/FusionActivityServerDiagnosticListener.cs
+++ b/src/HotChocolate/Fusion/src/Fusion.Diagnostics/Listeners/FusionActivityServerDiagnosticListener.cs
@@ -1,5 +1,7 @@
using Microsoft.AspNetCore.Http;
using HotChocolate.AspNetCore.Instrumentation;
+using HotChocolate.AspNetCore.Subscriptions;
+using HotChocolate.AspNetCore.Subscriptions.Protocols;
using HotChocolate.Diagnostics;
using HotChocolate.Execution;
using HotChocolate.Language;
@@ -104,6 +106,11 @@ public override void ParserErrors(HttpContext context, IReadOnlyList err
}
}
+ public override void WebSocketConnectionInitialized(
+ ISocketSession session,
+ IOperationMessagePayload connectionInitMessage)
+ => enricher.OnWebSocketConnectionInitialized(session, connectionInitMessage);
+
public override IDisposable FormatHttpResponse(HttpContext context, OperationResult result)
{
if (options.SkipFormatHttpResponse)
diff --git a/src/HotChocolate/Fusion/test/Fusion.Diagnostics.Tests/FusionActivityServerDiagnosticListenerTests.cs b/src/HotChocolate/Fusion/test/Fusion.Diagnostics.Tests/FusionActivityServerDiagnosticListenerTests.cs
index b63be71e359..5a087f29163 100644
--- a/src/HotChocolate/Fusion/test/Fusion.Diagnostics.Tests/FusionActivityServerDiagnosticListenerTests.cs
+++ b/src/HotChocolate/Fusion/test/Fusion.Diagnostics.Tests/FusionActivityServerDiagnosticListenerTests.cs
@@ -1,15 +1,25 @@
+using System.Diagnostics;
+using System.Net.WebSockets;
using System.Runtime.CompilerServices;
using System.Text;
+using System.Text.Json;
+using HotChocolate.AspNetCore.Subscriptions;
+using HotChocolate.AspNetCore.Subscriptions.Protocols;
using HotChocolate.Diagnostics;
using HotChocolate.Execution;
+using HotChocolate.Fusion.Execution;
+using HotChocolate.Fusion.Execution.Nodes;
using HotChocolate.Language;
using HotChocolate.PersistedOperations;
using HotChocolate.Resolvers;
using HotChocolate.Transport.Http;
+using HotChocolate.Transport.Sockets;
+using HotChocolate.Transport.Sockets.Client;
using HotChocolate.Types;
using HotChocolate.Types.Composite;
using HotChocolate.Types.Relay;
using Microsoft.AspNetCore.Builder;
+using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.DependencyInjection;
using static CookieCrumble.TestEnvironment;
using static HotChocolate.Fusion.Diagnostics.ActivityTestHelper;
@@ -21,6 +31,7 @@ namespace HotChocolate.Fusion.Diagnostics;
public class FusionActivityServerDiagnosticListenerTests : FusionTestBase
{
private static readonly Uri s_url = new("http://localhost:5000/graphql");
+ private static readonly Uri s_webSocketUrl = new("ws://localhost:5000/graphql");
[Fact]
public async Task Http_Post_Single_Request_Default()
@@ -748,6 +759,484 @@ public async Task Http_Subscription_Should_Be_Unset_When_Client_Disconnects_Duri
}
}
+ [Fact]
+ public async Task WebSocket_Subscription_Should_Be_Ok_When_Server_Completes_Default()
+ {
+ using var guard = new CancellationTokenSource(TimeSpan.FromSeconds(10));
+
+ using (CaptureActivities(out var activities))
+ {
+ // arrange
+ using var server1 = CreateSourceSchema(
+ "a",
+ b => b
+ .AddQueryType()
+ .AddSubscriptionType());
+
+ using var gateway = await CreateCompositeSchemaAsync(
+ [
+ ("a", server1)
+ ],
+ configureGatewayBuilder: b => b.AddInstrumentation());
+
+ using var webSocket = await ConnectWebSocketAsync(gateway, guard.Token);
+ await using var client = await SocketClient.ConnectAsync(webSocket, guard.Token);
+
+ var request = new OperationRequest("subscription OnMessageSubscription { onMessage }");
+
+ using var result = await client.ExecuteAsync(request, guard.Token);
+ var results = result.ReadResultsAsync().GetAsyncEnumerator(guard.Token);
+
+ // act
+ // the subgraph emits one event then completes its stream
+ try
+ {
+ Assert.True(await results.MoveNextAsync());
+ Assert.False(await results.MoveNextAsync());
+ }
+ finally
+ {
+ await IgnoreSocketTeardownAsync(results.DisposeAsync().AsTask());
+ }
+
+ // the session encloses every span, so close it before the trace is read
+ await CloseWebSocketAsync(webSocket, guard.Token);
+
+ // assert
+ activities.MatchSnapshot(Postfix([NET11_0]));
+ }
+ }
+
+ [Fact]
+ public async Task WebSocket_Subscription_Should_Be_Ok_When_Server_Completes()
+ {
+ using var guard = new CancellationTokenSource(TimeSpan.FromSeconds(10));
+
+ using (CaptureActivities(out var activities))
+ {
+ // arrange
+ using var server1 = CreateSourceSchema(
+ "a",
+ b => b
+ .AddQueryType()
+ .AddSubscriptionType());
+
+ using var gateway = await CreateCompositeSchemaAsync(
+ [
+ ("a", server1)
+ ],
+ configureGatewayBuilder: b => b.AddInstrumentation(o =>
+ o.Scopes = FusionActivityScopes.All));
+
+ using var webSocket = await ConnectWebSocketAsync(gateway, guard.Token);
+ await using var client = await SocketClient.ConnectAsync(webSocket, guard.Token);
+
+ var request = new OperationRequest("subscription OnMessageSubscription { onMessage }");
+
+ using var result = await client.ExecuteAsync(request, guard.Token);
+ var results = result.ReadResultsAsync().GetAsyncEnumerator(guard.Token);
+
+ // act
+ // the subgraph emits one event then completes its stream
+ try
+ {
+ Assert.True(await results.MoveNextAsync());
+ Assert.False(await results.MoveNextAsync());
+ }
+ finally
+ {
+ await IgnoreSocketTeardownAsync(results.DisposeAsync().AsTask());
+ }
+
+ // the session encloses every span, so close it before the trace is read
+ await CloseWebSocketAsync(webSocket, guard.Token);
+
+ // assert
+ activities.MatchSnapshot(Postfix([NET11_0]));
+ }
+ }
+
+ [Fact]
+ public async Task WebSocket_Subscription_Should_Be_Ok_When_Client_Closes_Connection()
+ {
+ using var guard = new CancellationTokenSource(TimeSpan.FromSeconds(10));
+
+ using (CaptureActivities(out var activities))
+ {
+ // arrange
+ using var server1 = CreateSourceSchema(
+ "a",
+ b => b
+ .AddQueryType()
+ .AddSubscriptionType());
+
+ using var gateway = await CreateCompositeSchemaAsync(
+ [
+ ("a", server1)
+ ],
+ configureGatewayBuilder: b => b.AddInstrumentation(o =>
+ o.Scopes = FusionActivityScopes.All));
+
+ using var webSocket = await ConnectWebSocketAsync(gateway, guard.Token);
+ await using var client = await SocketClient.ConnectAsync(webSocket, guard.Token);
+
+ var request = new OperationRequest("subscription OnIdleMessageSubscription { onIdleMessage }");
+
+ using var result = await client.ExecuteAsync(request, guard.Token);
+ var results = result.ReadResultsAsync().GetAsyncEnumerator(guard.Token);
+
+ Assert.True(await results.MoveNextAsync());
+
+ // act
+ // close the connection without unsubscribing, so the gateway has to tear the
+ // still-running subscription down with the session
+ await CloseWebSocketAsync(webSocket, guard.Token);
+ await IgnoreSocketTeardownAsync(results.DisposeAsync().AsTask());
+
+ // assert
+ activities.MatchSnapshot(Postfix([NET11_0]));
+ }
+ }
+
+ [Fact]
+ public async Task WebSocket_Header_Should_Be_Added_As_Tag_To_Request_And_Event_Spans()
+ {
+ using var guard = new CancellationTokenSource(TimeSpan.FromSeconds(10));
+
+ using (CaptureActivities(out var activities))
+ {
+ // arrange
+ // non-browser clients can send the tenant as a handshake header instead
+ using var server1 = CreateSourceSchema(
+ "a",
+ b => b
+ .AddQueryType()
+ .AddSubscriptionType());
+
+ using var gateway = await CreateCompositeSchemaAsync(
+ [
+ ("a", server1)
+ ],
+ configureGatewayBuilder: b => b
+ .AddInstrumentation(o => o.Scopes = FusionActivityScopes.All)
+ .AddApplicationService()
+ .Services.AddSingleton());
+
+ using var webSocket = await ConnectWebSocketAsync(
+ gateway,
+ guard.Token,
+ r => r.Headers[TenantHeaderName] = "acme-42");
+ await using var client = await SocketClient.ConnectAsync(webSocket, guard.Token);
+
+ var request = new OperationRequest("subscription OnMessageSubscription { onMessage }");
+
+ using var result = await client.ExecuteAsync(request, guard.Token);
+ var results = result.ReadResultsAsync().GetAsyncEnumerator(guard.Token);
+
+ // act
+ // the subgraph emits one event then completes its stream
+ try
+ {
+ Assert.True(await results.MoveNextAsync());
+ Assert.False(await results.MoveNextAsync());
+ }
+ finally
+ {
+ await IgnoreSocketTeardownAsync(results.DisposeAsync().AsTask());
+ }
+
+ await CloseWebSocketAsync(webSocket, guard.Token);
+
+ // assert
+ activities.MatchSnapshot(Postfix([NET11_0]));
+ }
+ }
+
+ [Fact]
+ public async Task WebSocket_ConnectionInit_Payload_Should_Be_Added_As_Tag_To_Request_And_Event_Spans()
+ {
+ using var guard = new CancellationTokenSource(TimeSpan.FromSeconds(10));
+
+ using (CaptureActivities(out var activities))
+ {
+ // arrange
+ using var server1 = CreateSourceSchema(
+ "a",
+ b => b
+ .AddQueryType()
+ .AddSubscriptionType());
+
+ using var gateway = await CreateCompositeSchemaAsync(
+ [
+ ("a", server1)
+ ],
+ configureGatewayBuilder: b => b
+ .AddInstrumentation(o => o.Scopes = FusionActivityScopes.All)
+ .AddApplicationService()
+ .Services.AddSingleton());
+
+ using var webSocket = await ConnectWebSocketAsync(gateway, guard.Token);
+ var payload = JsonSerializer.SerializeToElement(new { tenant = "acme-42" });
+ await using var client = await SocketClient.ConnectAsync(webSocket, payload, guard.Token);
+
+ var request = new OperationRequest("subscription OnMessageSubscription { onMessage }");
+
+ using var result = await client.ExecuteAsync(request, guard.Token);
+ var results = result.ReadResultsAsync().GetAsyncEnumerator(guard.Token);
+
+ // act
+ // the subgraph emits one event then completes its stream
+ try
+ {
+ Assert.True(await results.MoveNextAsync());
+ Assert.False(await results.MoveNextAsync());
+ }
+ finally
+ {
+ await IgnoreSocketTeardownAsync(results.DisposeAsync().AsTask());
+ }
+
+ await CloseWebSocketAsync(webSocket, guard.Token);
+
+ // assert
+ activities.MatchSnapshot(Postfix([NET11_0]));
+ }
+ }
+
+ private const string TenantHeaderName = "X-Tenant";
+
+ public sealed record TenantFeature(string Tenant);
+
+ public sealed class TenantActivityEnricher(InstrumentationOptions options)
+ : FusionActivityEnricher(options)
+ {
+ ///
+ /// Stores the tenant on the connection so it outlives the connection
+ /// initialization message, which is only valid during this call.
+ ///
+ public override void OnWebSocketConnectionInitialized(
+ ISocketSession session,
+ IOperationMessagePayload connectionInitMessage)
+ {
+ if (connectionInitMessage.Payload is { ValueKind: JsonValueKind.Object } payload
+ && payload.TryGetProperty("tenant", out var tenant)
+ && tenant.ValueKind is JsonValueKind.String)
+ {
+ session.Connection.Features.Set(new TenantFeature(tenant.GetString()!));
+ }
+ }
+
+ public override void EnrichExecuteRequest(RequestContext context, Activity activity)
+ {
+ base.EnrichExecuteRequest(context, activity);
+ SetTenantTag(context, activity);
+ }
+
+ public override void EnrichOnSubscriptionEvent(
+ OperationPlanContext context,
+ ExecutionNode node,
+ string schemaName,
+ ulong subscriptionId,
+ Activity activity)
+ {
+ base.EnrichOnSubscriptionEvent(context, node, schemaName, subscriptionId, activity);
+ SetTenantTag(context.RequestContext, activity);
+ }
+
+ private static void SetTenantTag(RequestContext context, Activity activity)
+ {
+ if (ResolveTenant(context) is { } tenant)
+ {
+ activity.SetTag("test.tenant", tenant);
+ }
+ }
+
+ private static string? ResolveTenant(RequestContext context)
+ {
+ if (context.ContextData.TryGetValue(nameof(ISocketSession), out var value)
+ && value is ISocketSession session
+ && session.Connection.Features.Get() is { } tenant)
+ {
+ return tenant.Tenant;
+ }
+
+ if (context.Features.Get() is { } httpContext
+ && httpContext.Request.Headers.TryGetValue(TenantHeaderName, out var header))
+ {
+ return header.ToString();
+ }
+
+ return null;
+ }
+ }
+
+ [Fact]
+ public async Task WebSocket_Apollo_ConnectionInit_Payload_Should_Be_Added_As_Tag_To_Request_And_Event_Spans()
+ {
+ using var guard = new CancellationTokenSource(TimeSpan.FromSeconds(10));
+
+ using (CaptureActivities(out var activities))
+ {
+ // arrange
+ // the legacy apollo protocol raises the event from its own protocol handler
+ using var server1 = CreateSourceSchema(
+ "a",
+ b => b
+ .AddQueryType()
+ .AddSubscriptionType());
+
+ using var gateway = await CreateCompositeSchemaAsync(
+ [
+ ("a", server1)
+ ],
+ configureGatewayBuilder: b => b
+ .AddInstrumentation(o => o.Scopes = FusionActivityScopes.All)
+ .AddApplicationService()
+ .Services.AddSingleton());
+
+ var webSocketClient = gateway.CreateWebSocketClient();
+ webSocketClient.ConfigureRequest =
+ r => r.Headers.SecWebSocketProtocol = WellKnownProtocols.GraphQL_WS;
+ using var webSocket = await webSocketClient.ConnectAsync(s_webSocketUrl, guard.Token);
+
+ await SendApolloMessageAsync(
+ webSocket,
+ """{"type":"connection_init","payload":{"tenant":"acme-42"}}""",
+ guard.Token);
+ Assert.NotNull(await WaitForApolloMessageAsync(webSocket, "connection_ack", guard.Token));
+
+ // act
+ // the subgraph emits one event then completes its stream
+ await SendApolloMessageAsync(
+ webSocket,
+ """
+ {"type":"start","id":"1","payload":{"query":"subscription OnMessageSubscription { onMessage }"}}
+ """,
+ guard.Token);
+ Assert.NotNull(await WaitForApolloMessageAsync(webSocket, "data", guard.Token));
+ Assert.NotNull(await WaitForApolloMessageAsync(webSocket, "complete", guard.Token));
+
+ await CloseWebSocketAsync(webSocket, guard.Token);
+
+ // assert
+ activities.MatchSnapshot(Postfix([NET11_0]));
+ }
+ }
+
+ private static async Task SendApolloMessageAsync(
+ WebSocket webSocket,
+ string message,
+ CancellationToken cancellationToken)
+ => await webSocket.SendAsync(
+ Encoding.UTF8.GetBytes(message),
+ WebSocketMessageType.Text,
+ true,
+ cancellationToken);
+
+ private static async Task WaitForApolloMessageAsync(
+ WebSocket webSocket,
+ string type,
+ CancellationToken cancellationToken)
+ {
+ using var timeout = new CancellationTokenSource(TimeSpan.FromSeconds(5));
+ using var combined =
+ CancellationTokenSource.CreateLinkedTokenSource(cancellationToken, timeout.Token);
+ var buffer = new byte[4096];
+
+ try
+ {
+ while (!combined.Token.IsCancellationRequested)
+ {
+ await using var stream = new MemoryStream();
+ WebSocketReceiveResult result;
+
+ do
+ {
+ result = await webSocket.ReceiveAsync(
+ new ArraySegment(buffer),
+ combined.Token);
+ stream.Write(buffer, 0, result.Count);
+ }
+ while (!result.EndOfMessage);
+
+ if (stream.Length == 0)
+ {
+ continue;
+ }
+
+ var message = JsonDocument.Parse(stream.ToArray());
+
+ if (message.RootElement.GetProperty("type").GetString() == type)
+ {
+ return message;
+ }
+
+ message.Dispose();
+ }
+ }
+ catch (OperationCanceledException)
+ {
+ // expected: no matching message arrived within the timeout
+ }
+
+ return null;
+ }
+
+ private static async Task ConnectWebSocketAsync(
+ Gateway gateway,
+ CancellationToken cancellationToken,
+ Action? configureRequest = null)
+ {
+ var webSocketClient = gateway.CreateWebSocketClient();
+ webSocketClient.ConfigureRequest = r =>
+ {
+ r.Headers.SecWebSocketProtocol = WellKnownProtocols.GraphQL_Transport_WS;
+ configureRequest?.Invoke(r);
+ };
+ return await webSocketClient.ConnectAsync(s_webSocketUrl, cancellationToken);
+ }
+
+ private static async Task CloseWebSocketAsync(
+ WebSocket webSocket,
+ CancellationToken cancellationToken)
+ {
+ try
+ {
+ await webSocket.CloseOutputAsync(
+ WebSocketCloseStatus.NormalClosure,
+ "done",
+ cancellationToken);
+ }
+ catch (Exception ex) when (ex is WebSocketException or IOException or ObjectDisposedException)
+ {
+ // the gateway can end the session before the close frame is sent
+ }
+ }
+
+ private static async Task IgnoreSocketTeardownAsync(Task task)
+ {
+ try
+ {
+ await task;
+ }
+ catch (OperationCanceledException)
+ {
+ // expected: the streamed read was aborted by the client
+ }
+ catch (IOException)
+ {
+ // expected: aborting an in-flight read can surface as an I/O failure
+ }
+ catch (WebSocketException)
+ {
+ // expected: the socket was closed while a read was in flight
+ }
+ catch (SocketClosedException)
+ {
+ // expected: the client dropped the connection without a close frame
+ }
+ }
+
private static async Task IgnoreCancellationAsync(Task task)
{
try
diff --git a/src/HotChocolate/Fusion/test/Fusion.Diagnostics.Tests/__snapshots__/FusionActivityServerDiagnosticListenerTests.WebSocket_Apollo_ConnectionInit_Payload_Should_Be_Added_As_Tag_To_Request_And_Event_Spans.snap b/src/HotChocolate/Fusion/test/Fusion.Diagnostics.Tests/__snapshots__/FusionActivityServerDiagnosticListenerTests.WebSocket_Apollo_ConnectionInit_Payload_Should_Be_Added_As_Tag_To_Request_And_Event_Spans.snap
new file mode 100644
index 00000000000..e03f14a6a0a
--- /dev/null
+++ b/src/HotChocolate/Fusion/test/Fusion.Diagnostics.Tests/__snapshots__/FusionActivityServerDiagnosticListenerTests.WebSocket_Apollo_ConnectionInit_Payload_Should_Be_Added_As_Tag_To_Request_And_Event_Spans.snap
@@ -0,0 +1,321 @@
+{
+ "source": {
+ "name": "HotChocolate.Fusion.Diagnostics"
+ },
+ "activities": [
+ {
+ "OperationName": "Microsoft.AspNetCore.Hosting.HttpRequestIn",
+ "DisplayName": "GET /graphql/{**slug}",
+ "Kind": "Server",
+ "Status": "Unset",
+ "tags": [
+ {
+ "Key": "server.address",
+ "Value": "localhost"
+ },
+ {
+ "Key": "server.port",
+ "Value": 5000
+ },
+ {
+ "Key": "http.request.method",
+ "Value": "GET"
+ },
+ {
+ "Key": "url.scheme",
+ "Value": "http"
+ },
+ {
+ "Key": "url.path",
+ "Value": "/graphql"
+ },
+ {
+ "Key": "network.protocol.version",
+ "Value": "1.1"
+ },
+ {
+ "Key": "http.route",
+ "Value": "/graphql/{**slug}"
+ },
+ {
+ "Key": "http.response.status_code",
+ "Value": 101
+ }
+ ],
+ "event": [],
+ "activities": [
+ {
+ "OperationName": "GraphQL Operation",
+ "DisplayName": "subscription",
+ "Kind": "Internal",
+ "Status": "Ok",
+ "tags": [
+ {
+ "Key": "graphql.processing.type",
+ "Value": "request"
+ },
+ {
+ "Key": "server.address",
+ "Value": "localhost"
+ },
+ {
+ "Key": "server.port",
+ "Value": 5000
+ },
+ {
+ "Key": "graphql.operation.type",
+ "Value": "subscription"
+ },
+ {
+ "Key": "graphql.operation.name",
+ "Value": "OnMessageSubscription"
+ },
+ {
+ "Key": "graphql.document.hash",
+ "Value": "md5:ef859b98c9d8c17038c8fd9aeecdb1e2"
+ },
+ {
+ "Key": "test.tenant",
+ "Value": "acme-42"
+ }
+ ],
+ "event": [
+ {
+ "Name": "AddedOperationPlanToCache",
+ "Tags": []
+ },
+ {
+ "Name": "AddedDocumentToCache",
+ "Tags": []
+ }
+ ],
+ "activities": [
+ {
+ "OperationName": "GraphQL Document Validation",
+ "DisplayName": "GraphQL Document Validation",
+ "Kind": "Internal",
+ "Status": "Ok",
+ "tags": [
+ {
+ "Key": "graphql.processing.type",
+ "Value": "validate"
+ },
+ {
+ "Key": "graphql.document.hash",
+ "Value": "md5:ef859b98c9d8c17038c8fd9aeecdb1e2"
+ }
+ ],
+ "event": []
+ },
+ {
+ "OperationName": "GraphQL Operation Planning",
+ "DisplayName": "GraphQL Operation Planning",
+ "Kind": "Internal",
+ "Status": "Ok",
+ "tags": [
+ {
+ "Key": "graphql.processing.type",
+ "Value": "plan"
+ },
+ {
+ "Key": "graphql.document.hash",
+ "Value": "md5:ef859b98c9d8c17038c8fd9aeecdb1e2"
+ },
+ {
+ "Key": "graphql.operation.type",
+ "Value": "subscription"
+ },
+ {
+ "Key": "graphql.operation.name",
+ "Value": "OnMessageSubscription"
+ }
+ ],
+ "event": []
+ },
+ {
+ "OperationName": "GraphQL Operation Execution",
+ "DisplayName": "GraphQL Operation Execution",
+ "Kind": "Internal",
+ "Status": "Ok",
+ "tags": [
+ {
+ "Key": "graphql.processing.type",
+ "Value": "execute"
+ },
+ {
+ "Key": "graphql.operation.type",
+ "Value": "subscription"
+ },
+ {
+ "Key": "graphql.operation.name",
+ "Value": "OnMessageSubscription"
+ },
+ {
+ "Key": "graphql.document.hash",
+ "Value": "md5:ef859b98c9d8c17038c8fd9aeecdb1e2"
+ }
+ ],
+ "event": [],
+ "activities": [
+ {
+ "OperationName": "GraphQL Step Execution",
+ "DisplayName": "GraphQL Step Execution",
+ "Kind": "Internal",
+ "Status": "Ok",
+ "tags": [
+ {
+ "Key": "graphql.processing.type",
+ "Value": "step_execute"
+ },
+ {
+ "Key": "graphql.operation.type",
+ "Value": "subscription"
+ },
+ {
+ "Key": "graphql.operation.name",
+ "Value": "OnMessageSubscription"
+ },
+ {
+ "Key": "graphql.document.hash",
+ "Value": "md5:ef859b98c9d8c17038c8fd9aeecdb1e2"
+ },
+ {
+ "Key": "graphql.operation.step.id",
+ "Value": "1"
+ },
+ {
+ "Key": "graphql.operation.step.kind",
+ "Value": "operation"
+ },
+ {
+ "Key": "graphql.operation.step.plan.id",
+ "Value": "bb94346d204b2f13a21dcba79a06b47065115db28b9ba2c04bce67382b50cef0"
+ },
+ {
+ "Key": "graphql.source_schema.name",
+ "Value": "a"
+ },
+ {
+ "Key": "graphql.source_schema.operation.name",
+ "Value": "OnMessageSubscription_ef859b98_1"
+ },
+ {
+ "Key": "graphql.source_schema.operation.hash",
+ "Value": "sha256:f9abddbb651ad8e118793eb7c4a7c8db7fe43cae95faba710a0ff35b26b17136"
+ }
+ ],
+ "event": []
+ }
+ ]
+ },
+ {
+ "OperationName": "GraphQL Subscription Event",
+ "DisplayName": "GraphQL Subscription Event",
+ "Kind": "Internal",
+ "Status": "Ok",
+ "tags": [
+ {
+ "Key": "graphql.processing.type",
+ "Value": "subscription_event"
+ },
+ {
+ "Key": "graphql.operation.type",
+ "Value": "subscription"
+ },
+ {
+ "Key": "graphql.operation.name",
+ "Value": "OnMessageSubscription"
+ },
+ {
+ "Key": "graphql.document.hash",
+ "Value": "md5:ef859b98c9d8c17038c8fd9aeecdb1e2"
+ },
+ {
+ "Key": "graphql.subscription.id",
+ "Value": ""
+ },
+ {
+ "Key": "test.tenant",
+ "Value": "acme-42"
+ }
+ ],
+ "event": []
+ }
+ ]
+ },
+ {
+ "OperationName": "System.Net.Http.HttpRequestOut",
+ "DisplayName": "System.Net.Http.HttpRequestOut",
+ "Kind": "Client",
+ "Status": "Unset",
+ "tags": [
+ {
+ "Key": "http.request.method",
+ "Value": "POST"
+ },
+ {
+ "Key": "server.address",
+ "Value": "localhost"
+ },
+ {
+ "Key": "server.port",
+ "Value": 5000
+ },
+ {
+ "Key": "url.full",
+ "Value": "http://localhost:5000/graphql"
+ },
+ {
+ "Key": "http.response.status_code",
+ "Value": 200
+ }
+ ],
+ "event": [],
+ "activities": [
+ {
+ "OperationName": "Microsoft.AspNetCore.Hosting.HttpRequestIn",
+ "DisplayName": "POST /graphql/{**slug}",
+ "Kind": "Server",
+ "Status": "Unset",
+ "tags": [
+ {
+ "Key": "server.address",
+ "Value": "localhost"
+ },
+ {
+ "Key": "server.port",
+ "Value": 5000
+ },
+ {
+ "Key": "http.request.method",
+ "Value": "POST"
+ },
+ {
+ "Key": "url.scheme",
+ "Value": "http"
+ },
+ {
+ "Key": "url.path",
+ "Value": "/graphql"
+ },
+ {
+ "Key": "network.protocol.version",
+ "Value": "1.1"
+ },
+ {
+ "Key": "http.route",
+ "Value": "/graphql/{**slug}"
+ },
+ {
+ "Key": "http.response.status_code",
+ "Value": 200
+ }
+ ],
+ "event": []
+ }
+ ]
+ }
+ ]
+ }
+ ]
+}
diff --git a/src/HotChocolate/Fusion/test/Fusion.Diagnostics.Tests/__snapshots__/FusionActivityServerDiagnosticListenerTests.WebSocket_Apollo_ConnectionInit_Payload_Should_Be_Added_As_Tag_To_Request_And_Event_Spans_NET11_0.snap b/src/HotChocolate/Fusion/test/Fusion.Diagnostics.Tests/__snapshots__/FusionActivityServerDiagnosticListenerTests.WebSocket_Apollo_ConnectionInit_Payload_Should_Be_Added_As_Tag_To_Request_And_Event_Spans_NET11_0.snap
new file mode 100644
index 00000000000..f3c18f13722
--- /dev/null
+++ b/src/HotChocolate/Fusion/test/Fusion.Diagnostics.Tests/__snapshots__/FusionActivityServerDiagnosticListenerTests.WebSocket_Apollo_ConnectionInit_Payload_Should_Be_Added_As_Tag_To_Request_And_Event_Spans_NET11_0.snap
@@ -0,0 +1,321 @@
+{
+ "source": {
+ "name": "HotChocolate.Fusion.Diagnostics"
+ },
+ "activities": [
+ {
+ "OperationName": "Microsoft.AspNetCore.Hosting.HttpRequestIn",
+ "DisplayName": "GET /graphql/{**slug}",
+ "Kind": "Server",
+ "Status": "Unset",
+ "tags": [
+ {
+ "Key": "server.address",
+ "Value": "localhost"
+ },
+ {
+ "Key": "server.port",
+ "Value": 5000
+ },
+ {
+ "Key": "http.request.method",
+ "Value": "GET"
+ },
+ {
+ "Key": "url.scheme",
+ "Value": "http"
+ },
+ {
+ "Key": "url.path",
+ "Value": "/graphql"
+ },
+ {
+ "Key": "network.protocol.version",
+ "Value": "1.1"
+ },
+ {
+ "Key": "http.response.status_code",
+ "Value": 101
+ },
+ {
+ "Key": "http.route",
+ "Value": "/graphql/{**slug}"
+ }
+ ],
+ "event": [],
+ "activities": [
+ {
+ "OperationName": "GraphQL Operation",
+ "DisplayName": "subscription",
+ "Kind": "Internal",
+ "Status": "Ok",
+ "tags": [
+ {
+ "Key": "graphql.processing.type",
+ "Value": "request"
+ },
+ {
+ "Key": "server.address",
+ "Value": "localhost"
+ },
+ {
+ "Key": "server.port",
+ "Value": 5000
+ },
+ {
+ "Key": "graphql.operation.type",
+ "Value": "subscription"
+ },
+ {
+ "Key": "graphql.operation.name",
+ "Value": "OnMessageSubscription"
+ },
+ {
+ "Key": "graphql.document.hash",
+ "Value": "md5:ef859b98c9d8c17038c8fd9aeecdb1e2"
+ },
+ {
+ "Key": "test.tenant",
+ "Value": "acme-42"
+ }
+ ],
+ "event": [
+ {
+ "Name": "AddedOperationPlanToCache",
+ "Tags": []
+ },
+ {
+ "Name": "AddedDocumentToCache",
+ "Tags": []
+ }
+ ],
+ "activities": [
+ {
+ "OperationName": "GraphQL Document Validation",
+ "DisplayName": "GraphQL Document Validation",
+ "Kind": "Internal",
+ "Status": "Ok",
+ "tags": [
+ {
+ "Key": "graphql.processing.type",
+ "Value": "validate"
+ },
+ {
+ "Key": "graphql.document.hash",
+ "Value": "md5:ef859b98c9d8c17038c8fd9aeecdb1e2"
+ }
+ ],
+ "event": []
+ },
+ {
+ "OperationName": "GraphQL Operation Planning",
+ "DisplayName": "GraphQL Operation Planning",
+ "Kind": "Internal",
+ "Status": "Ok",
+ "tags": [
+ {
+ "Key": "graphql.processing.type",
+ "Value": "plan"
+ },
+ {
+ "Key": "graphql.document.hash",
+ "Value": "md5:ef859b98c9d8c17038c8fd9aeecdb1e2"
+ },
+ {
+ "Key": "graphql.operation.type",
+ "Value": "subscription"
+ },
+ {
+ "Key": "graphql.operation.name",
+ "Value": "OnMessageSubscription"
+ }
+ ],
+ "event": []
+ },
+ {
+ "OperationName": "GraphQL Operation Execution",
+ "DisplayName": "GraphQL Operation Execution",
+ "Kind": "Internal",
+ "Status": "Ok",
+ "tags": [
+ {
+ "Key": "graphql.processing.type",
+ "Value": "execute"
+ },
+ {
+ "Key": "graphql.operation.type",
+ "Value": "subscription"
+ },
+ {
+ "Key": "graphql.operation.name",
+ "Value": "OnMessageSubscription"
+ },
+ {
+ "Key": "graphql.document.hash",
+ "Value": "md5:ef859b98c9d8c17038c8fd9aeecdb1e2"
+ }
+ ],
+ "event": [],
+ "activities": [
+ {
+ "OperationName": "GraphQL Step Execution",
+ "DisplayName": "GraphQL Step Execution",
+ "Kind": "Internal",
+ "Status": "Ok",
+ "tags": [
+ {
+ "Key": "graphql.processing.type",
+ "Value": "step_execute"
+ },
+ {
+ "Key": "graphql.operation.type",
+ "Value": "subscription"
+ },
+ {
+ "Key": "graphql.operation.name",
+ "Value": "OnMessageSubscription"
+ },
+ {
+ "Key": "graphql.document.hash",
+ "Value": "md5:ef859b98c9d8c17038c8fd9aeecdb1e2"
+ },
+ {
+ "Key": "graphql.operation.step.id",
+ "Value": "1"
+ },
+ {
+ "Key": "graphql.operation.step.kind",
+ "Value": "operation"
+ },
+ {
+ "Key": "graphql.operation.step.plan.id",
+ "Value": "bb94346d204b2f13a21dcba79a06b47065115db28b9ba2c04bce67382b50cef0"
+ },
+ {
+ "Key": "graphql.source_schema.name",
+ "Value": "a"
+ },
+ {
+ "Key": "graphql.source_schema.operation.name",
+ "Value": "OnMessageSubscription_ef859b98_1"
+ },
+ {
+ "Key": "graphql.source_schema.operation.hash",
+ "Value": "sha256:f9abddbb651ad8e118793eb7c4a7c8db7fe43cae95faba710a0ff35b26b17136"
+ }
+ ],
+ "event": []
+ }
+ ]
+ },
+ {
+ "OperationName": "GraphQL Subscription Event",
+ "DisplayName": "GraphQL Subscription Event",
+ "Kind": "Internal",
+ "Status": "Ok",
+ "tags": [
+ {
+ "Key": "graphql.processing.type",
+ "Value": "subscription_event"
+ },
+ {
+ "Key": "graphql.operation.type",
+ "Value": "subscription"
+ },
+ {
+ "Key": "graphql.operation.name",
+ "Value": "OnMessageSubscription"
+ },
+ {
+ "Key": "graphql.document.hash",
+ "Value": "md5:ef859b98c9d8c17038c8fd9aeecdb1e2"
+ },
+ {
+ "Key": "graphql.subscription.id",
+ "Value": ""
+ },
+ {
+ "Key": "test.tenant",
+ "Value": "acme-42"
+ }
+ ],
+ "event": []
+ }
+ ]
+ },
+ {
+ "OperationName": "System.Net.Http.HttpRequestOut",
+ "DisplayName": "System.Net.Http.HttpRequestOut",
+ "Kind": "Client",
+ "Status": "Unset",
+ "tags": [
+ {
+ "Key": "http.request.method",
+ "Value": "POST"
+ },
+ {
+ "Key": "server.address",
+ "Value": "localhost"
+ },
+ {
+ "Key": "server.port",
+ "Value": 5000
+ },
+ {
+ "Key": "url.full",
+ "Value": "http://localhost:5000/graphql"
+ },
+ {
+ "Key": "http.response.status_code",
+ "Value": 200
+ }
+ ],
+ "event": [],
+ "activities": [
+ {
+ "OperationName": "Microsoft.AspNetCore.Hosting.HttpRequestIn",
+ "DisplayName": "POST /graphql/{**slug}",
+ "Kind": "Server",
+ "Status": "Unset",
+ "tags": [
+ {
+ "Key": "server.address",
+ "Value": "localhost"
+ },
+ {
+ "Key": "server.port",
+ "Value": 5000
+ },
+ {
+ "Key": "http.request.method",
+ "Value": "POST"
+ },
+ {
+ "Key": "url.scheme",
+ "Value": "http"
+ },
+ {
+ "Key": "url.path",
+ "Value": "/graphql"
+ },
+ {
+ "Key": "network.protocol.version",
+ "Value": "1.1"
+ },
+ {
+ "Key": "http.response.status_code",
+ "Value": 200
+ },
+ {
+ "Key": "http.route",
+ "Value": "/graphql/{**slug}"
+ }
+ ],
+ "event": []
+ }
+ ]
+ }
+ ]
+ }
+ ]
+}
diff --git a/src/HotChocolate/Fusion/test/Fusion.Diagnostics.Tests/__snapshots__/FusionActivityServerDiagnosticListenerTests.WebSocket_ConnectionInit_Payload_Should_Be_Added_As_Tag_To_Request_And_Event_Spans.snap b/src/HotChocolate/Fusion/test/Fusion.Diagnostics.Tests/__snapshots__/FusionActivityServerDiagnosticListenerTests.WebSocket_ConnectionInit_Payload_Should_Be_Added_As_Tag_To_Request_And_Event_Spans.snap
new file mode 100644
index 00000000000..e03f14a6a0a
--- /dev/null
+++ b/src/HotChocolate/Fusion/test/Fusion.Diagnostics.Tests/__snapshots__/FusionActivityServerDiagnosticListenerTests.WebSocket_ConnectionInit_Payload_Should_Be_Added_As_Tag_To_Request_And_Event_Spans.snap
@@ -0,0 +1,321 @@
+{
+ "source": {
+ "name": "HotChocolate.Fusion.Diagnostics"
+ },
+ "activities": [
+ {
+ "OperationName": "Microsoft.AspNetCore.Hosting.HttpRequestIn",
+ "DisplayName": "GET /graphql/{**slug}",
+ "Kind": "Server",
+ "Status": "Unset",
+ "tags": [
+ {
+ "Key": "server.address",
+ "Value": "localhost"
+ },
+ {
+ "Key": "server.port",
+ "Value": 5000
+ },
+ {
+ "Key": "http.request.method",
+ "Value": "GET"
+ },
+ {
+ "Key": "url.scheme",
+ "Value": "http"
+ },
+ {
+ "Key": "url.path",
+ "Value": "/graphql"
+ },
+ {
+ "Key": "network.protocol.version",
+ "Value": "1.1"
+ },
+ {
+ "Key": "http.route",
+ "Value": "/graphql/{**slug}"
+ },
+ {
+ "Key": "http.response.status_code",
+ "Value": 101
+ }
+ ],
+ "event": [],
+ "activities": [
+ {
+ "OperationName": "GraphQL Operation",
+ "DisplayName": "subscription",
+ "Kind": "Internal",
+ "Status": "Ok",
+ "tags": [
+ {
+ "Key": "graphql.processing.type",
+ "Value": "request"
+ },
+ {
+ "Key": "server.address",
+ "Value": "localhost"
+ },
+ {
+ "Key": "server.port",
+ "Value": 5000
+ },
+ {
+ "Key": "graphql.operation.type",
+ "Value": "subscription"
+ },
+ {
+ "Key": "graphql.operation.name",
+ "Value": "OnMessageSubscription"
+ },
+ {
+ "Key": "graphql.document.hash",
+ "Value": "md5:ef859b98c9d8c17038c8fd9aeecdb1e2"
+ },
+ {
+ "Key": "test.tenant",
+ "Value": "acme-42"
+ }
+ ],
+ "event": [
+ {
+ "Name": "AddedOperationPlanToCache",
+ "Tags": []
+ },
+ {
+ "Name": "AddedDocumentToCache",
+ "Tags": []
+ }
+ ],
+ "activities": [
+ {
+ "OperationName": "GraphQL Document Validation",
+ "DisplayName": "GraphQL Document Validation",
+ "Kind": "Internal",
+ "Status": "Ok",
+ "tags": [
+ {
+ "Key": "graphql.processing.type",
+ "Value": "validate"
+ },
+ {
+ "Key": "graphql.document.hash",
+ "Value": "md5:ef859b98c9d8c17038c8fd9aeecdb1e2"
+ }
+ ],
+ "event": []
+ },
+ {
+ "OperationName": "GraphQL Operation Planning",
+ "DisplayName": "GraphQL Operation Planning",
+ "Kind": "Internal",
+ "Status": "Ok",
+ "tags": [
+ {
+ "Key": "graphql.processing.type",
+ "Value": "plan"
+ },
+ {
+ "Key": "graphql.document.hash",
+ "Value": "md5:ef859b98c9d8c17038c8fd9aeecdb1e2"
+ },
+ {
+ "Key": "graphql.operation.type",
+ "Value": "subscription"
+ },
+ {
+ "Key": "graphql.operation.name",
+ "Value": "OnMessageSubscription"
+ }
+ ],
+ "event": []
+ },
+ {
+ "OperationName": "GraphQL Operation Execution",
+ "DisplayName": "GraphQL Operation Execution",
+ "Kind": "Internal",
+ "Status": "Ok",
+ "tags": [
+ {
+ "Key": "graphql.processing.type",
+ "Value": "execute"
+ },
+ {
+ "Key": "graphql.operation.type",
+ "Value": "subscription"
+ },
+ {
+ "Key": "graphql.operation.name",
+ "Value": "OnMessageSubscription"
+ },
+ {
+ "Key": "graphql.document.hash",
+ "Value": "md5:ef859b98c9d8c17038c8fd9aeecdb1e2"
+ }
+ ],
+ "event": [],
+ "activities": [
+ {
+ "OperationName": "GraphQL Step Execution",
+ "DisplayName": "GraphQL Step Execution",
+ "Kind": "Internal",
+ "Status": "Ok",
+ "tags": [
+ {
+ "Key": "graphql.processing.type",
+ "Value": "step_execute"
+ },
+ {
+ "Key": "graphql.operation.type",
+ "Value": "subscription"
+ },
+ {
+ "Key": "graphql.operation.name",
+ "Value": "OnMessageSubscription"
+ },
+ {
+ "Key": "graphql.document.hash",
+ "Value": "md5:ef859b98c9d8c17038c8fd9aeecdb1e2"
+ },
+ {
+ "Key": "graphql.operation.step.id",
+ "Value": "1"
+ },
+ {
+ "Key": "graphql.operation.step.kind",
+ "Value": "operation"
+ },
+ {
+ "Key": "graphql.operation.step.plan.id",
+ "Value": "bb94346d204b2f13a21dcba79a06b47065115db28b9ba2c04bce67382b50cef0"
+ },
+ {
+ "Key": "graphql.source_schema.name",
+ "Value": "a"
+ },
+ {
+ "Key": "graphql.source_schema.operation.name",
+ "Value": "OnMessageSubscription_ef859b98_1"
+ },
+ {
+ "Key": "graphql.source_schema.operation.hash",
+ "Value": "sha256:f9abddbb651ad8e118793eb7c4a7c8db7fe43cae95faba710a0ff35b26b17136"
+ }
+ ],
+ "event": []
+ }
+ ]
+ },
+ {
+ "OperationName": "GraphQL Subscription Event",
+ "DisplayName": "GraphQL Subscription Event",
+ "Kind": "Internal",
+ "Status": "Ok",
+ "tags": [
+ {
+ "Key": "graphql.processing.type",
+ "Value": "subscription_event"
+ },
+ {
+ "Key": "graphql.operation.type",
+ "Value": "subscription"
+ },
+ {
+ "Key": "graphql.operation.name",
+ "Value": "OnMessageSubscription"
+ },
+ {
+ "Key": "graphql.document.hash",
+ "Value": "md5:ef859b98c9d8c17038c8fd9aeecdb1e2"
+ },
+ {
+ "Key": "graphql.subscription.id",
+ "Value": ""
+ },
+ {
+ "Key": "test.tenant",
+ "Value": "acme-42"
+ }
+ ],
+ "event": []
+ }
+ ]
+ },
+ {
+ "OperationName": "System.Net.Http.HttpRequestOut",
+ "DisplayName": "System.Net.Http.HttpRequestOut",
+ "Kind": "Client",
+ "Status": "Unset",
+ "tags": [
+ {
+ "Key": "http.request.method",
+ "Value": "POST"
+ },
+ {
+ "Key": "server.address",
+ "Value": "localhost"
+ },
+ {
+ "Key": "server.port",
+ "Value": 5000
+ },
+ {
+ "Key": "url.full",
+ "Value": "http://localhost:5000/graphql"
+ },
+ {
+ "Key": "http.response.status_code",
+ "Value": 200
+ }
+ ],
+ "event": [],
+ "activities": [
+ {
+ "OperationName": "Microsoft.AspNetCore.Hosting.HttpRequestIn",
+ "DisplayName": "POST /graphql/{**slug}",
+ "Kind": "Server",
+ "Status": "Unset",
+ "tags": [
+ {
+ "Key": "server.address",
+ "Value": "localhost"
+ },
+ {
+ "Key": "server.port",
+ "Value": 5000
+ },
+ {
+ "Key": "http.request.method",
+ "Value": "POST"
+ },
+ {
+ "Key": "url.scheme",
+ "Value": "http"
+ },
+ {
+ "Key": "url.path",
+ "Value": "/graphql"
+ },
+ {
+ "Key": "network.protocol.version",
+ "Value": "1.1"
+ },
+ {
+ "Key": "http.route",
+ "Value": "/graphql/{**slug}"
+ },
+ {
+ "Key": "http.response.status_code",
+ "Value": 200
+ }
+ ],
+ "event": []
+ }
+ ]
+ }
+ ]
+ }
+ ]
+}
diff --git a/src/HotChocolate/Fusion/test/Fusion.Diagnostics.Tests/__snapshots__/FusionActivityServerDiagnosticListenerTests.WebSocket_ConnectionInit_Payload_Should_Be_Added_As_Tag_To_Request_And_Event_Spans_NET11_0.snap b/src/HotChocolate/Fusion/test/Fusion.Diagnostics.Tests/__snapshots__/FusionActivityServerDiagnosticListenerTests.WebSocket_ConnectionInit_Payload_Should_Be_Added_As_Tag_To_Request_And_Event_Spans_NET11_0.snap
new file mode 100644
index 00000000000..f3c18f13722
--- /dev/null
+++ b/src/HotChocolate/Fusion/test/Fusion.Diagnostics.Tests/__snapshots__/FusionActivityServerDiagnosticListenerTests.WebSocket_ConnectionInit_Payload_Should_Be_Added_As_Tag_To_Request_And_Event_Spans_NET11_0.snap
@@ -0,0 +1,321 @@
+{
+ "source": {
+ "name": "HotChocolate.Fusion.Diagnostics"
+ },
+ "activities": [
+ {
+ "OperationName": "Microsoft.AspNetCore.Hosting.HttpRequestIn",
+ "DisplayName": "GET /graphql/{**slug}",
+ "Kind": "Server",
+ "Status": "Unset",
+ "tags": [
+ {
+ "Key": "server.address",
+ "Value": "localhost"
+ },
+ {
+ "Key": "server.port",
+ "Value": 5000
+ },
+ {
+ "Key": "http.request.method",
+ "Value": "GET"
+ },
+ {
+ "Key": "url.scheme",
+ "Value": "http"
+ },
+ {
+ "Key": "url.path",
+ "Value": "/graphql"
+ },
+ {
+ "Key": "network.protocol.version",
+ "Value": "1.1"
+ },
+ {
+ "Key": "http.response.status_code",
+ "Value": 101
+ },
+ {
+ "Key": "http.route",
+ "Value": "/graphql/{**slug}"
+ }
+ ],
+ "event": [],
+ "activities": [
+ {
+ "OperationName": "GraphQL Operation",
+ "DisplayName": "subscription",
+ "Kind": "Internal",
+ "Status": "Ok",
+ "tags": [
+ {
+ "Key": "graphql.processing.type",
+ "Value": "request"
+ },
+ {
+ "Key": "server.address",
+ "Value": "localhost"
+ },
+ {
+ "Key": "server.port",
+ "Value": 5000
+ },
+ {
+ "Key": "graphql.operation.type",
+ "Value": "subscription"
+ },
+ {
+ "Key": "graphql.operation.name",
+ "Value": "OnMessageSubscription"
+ },
+ {
+ "Key": "graphql.document.hash",
+ "Value": "md5:ef859b98c9d8c17038c8fd9aeecdb1e2"
+ },
+ {
+ "Key": "test.tenant",
+ "Value": "acme-42"
+ }
+ ],
+ "event": [
+ {
+ "Name": "AddedOperationPlanToCache",
+ "Tags": []
+ },
+ {
+ "Name": "AddedDocumentToCache",
+ "Tags": []
+ }
+ ],
+ "activities": [
+ {
+ "OperationName": "GraphQL Document Validation",
+ "DisplayName": "GraphQL Document Validation",
+ "Kind": "Internal",
+ "Status": "Ok",
+ "tags": [
+ {
+ "Key": "graphql.processing.type",
+ "Value": "validate"
+ },
+ {
+ "Key": "graphql.document.hash",
+ "Value": "md5:ef859b98c9d8c17038c8fd9aeecdb1e2"
+ }
+ ],
+ "event": []
+ },
+ {
+ "OperationName": "GraphQL Operation Planning",
+ "DisplayName": "GraphQL Operation Planning",
+ "Kind": "Internal",
+ "Status": "Ok",
+ "tags": [
+ {
+ "Key": "graphql.processing.type",
+ "Value": "plan"
+ },
+ {
+ "Key": "graphql.document.hash",
+ "Value": "md5:ef859b98c9d8c17038c8fd9aeecdb1e2"
+ },
+ {
+ "Key": "graphql.operation.type",
+ "Value": "subscription"
+ },
+ {
+ "Key": "graphql.operation.name",
+ "Value": "OnMessageSubscription"
+ }
+ ],
+ "event": []
+ },
+ {
+ "OperationName": "GraphQL Operation Execution",
+ "DisplayName": "GraphQL Operation Execution",
+ "Kind": "Internal",
+ "Status": "Ok",
+ "tags": [
+ {
+ "Key": "graphql.processing.type",
+ "Value": "execute"
+ },
+ {
+ "Key": "graphql.operation.type",
+ "Value": "subscription"
+ },
+ {
+ "Key": "graphql.operation.name",
+ "Value": "OnMessageSubscription"
+ },
+ {
+ "Key": "graphql.document.hash",
+ "Value": "md5:ef859b98c9d8c17038c8fd9aeecdb1e2"
+ }
+ ],
+ "event": [],
+ "activities": [
+ {
+ "OperationName": "GraphQL Step Execution",
+ "DisplayName": "GraphQL Step Execution",
+ "Kind": "Internal",
+ "Status": "Ok",
+ "tags": [
+ {
+ "Key": "graphql.processing.type",
+ "Value": "step_execute"
+ },
+ {
+ "Key": "graphql.operation.type",
+ "Value": "subscription"
+ },
+ {
+ "Key": "graphql.operation.name",
+ "Value": "OnMessageSubscription"
+ },
+ {
+ "Key": "graphql.document.hash",
+ "Value": "md5:ef859b98c9d8c17038c8fd9aeecdb1e2"
+ },
+ {
+ "Key": "graphql.operation.step.id",
+ "Value": "1"
+ },
+ {
+ "Key": "graphql.operation.step.kind",
+ "Value": "operation"
+ },
+ {
+ "Key": "graphql.operation.step.plan.id",
+ "Value": "bb94346d204b2f13a21dcba79a06b47065115db28b9ba2c04bce67382b50cef0"
+ },
+ {
+ "Key": "graphql.source_schema.name",
+ "Value": "a"
+ },
+ {
+ "Key": "graphql.source_schema.operation.name",
+ "Value": "OnMessageSubscription_ef859b98_1"
+ },
+ {
+ "Key": "graphql.source_schema.operation.hash",
+ "Value": "sha256:f9abddbb651ad8e118793eb7c4a7c8db7fe43cae95faba710a0ff35b26b17136"
+ }
+ ],
+ "event": []
+ }
+ ]
+ },
+ {
+ "OperationName": "GraphQL Subscription Event",
+ "DisplayName": "GraphQL Subscription Event",
+ "Kind": "Internal",
+ "Status": "Ok",
+ "tags": [
+ {
+ "Key": "graphql.processing.type",
+ "Value": "subscription_event"
+ },
+ {
+ "Key": "graphql.operation.type",
+ "Value": "subscription"
+ },
+ {
+ "Key": "graphql.operation.name",
+ "Value": "OnMessageSubscription"
+ },
+ {
+ "Key": "graphql.document.hash",
+ "Value": "md5:ef859b98c9d8c17038c8fd9aeecdb1e2"
+ },
+ {
+ "Key": "graphql.subscription.id",
+ "Value": ""
+ },
+ {
+ "Key": "test.tenant",
+ "Value": "acme-42"
+ }
+ ],
+ "event": []
+ }
+ ]
+ },
+ {
+ "OperationName": "System.Net.Http.HttpRequestOut",
+ "DisplayName": "System.Net.Http.HttpRequestOut",
+ "Kind": "Client",
+ "Status": "Unset",
+ "tags": [
+ {
+ "Key": "http.request.method",
+ "Value": "POST"
+ },
+ {
+ "Key": "server.address",
+ "Value": "localhost"
+ },
+ {
+ "Key": "server.port",
+ "Value": 5000
+ },
+ {
+ "Key": "url.full",
+ "Value": "http://localhost:5000/graphql"
+ },
+ {
+ "Key": "http.response.status_code",
+ "Value": 200
+ }
+ ],
+ "event": [],
+ "activities": [
+ {
+ "OperationName": "Microsoft.AspNetCore.Hosting.HttpRequestIn",
+ "DisplayName": "POST /graphql/{**slug}",
+ "Kind": "Server",
+ "Status": "Unset",
+ "tags": [
+ {
+ "Key": "server.address",
+ "Value": "localhost"
+ },
+ {
+ "Key": "server.port",
+ "Value": 5000
+ },
+ {
+ "Key": "http.request.method",
+ "Value": "POST"
+ },
+ {
+ "Key": "url.scheme",
+ "Value": "http"
+ },
+ {
+ "Key": "url.path",
+ "Value": "/graphql"
+ },
+ {
+ "Key": "network.protocol.version",
+ "Value": "1.1"
+ },
+ {
+ "Key": "http.response.status_code",
+ "Value": 200
+ },
+ {
+ "Key": "http.route",
+ "Value": "/graphql/{**slug}"
+ }
+ ],
+ "event": []
+ }
+ ]
+ }
+ ]
+ }
+ ]
+}
diff --git a/src/HotChocolate/Fusion/test/Fusion.Diagnostics.Tests/__snapshots__/FusionActivityServerDiagnosticListenerTests.WebSocket_Header_Should_Be_Added_As_Tag_To_Request_And_Event_Spans.snap b/src/HotChocolate/Fusion/test/Fusion.Diagnostics.Tests/__snapshots__/FusionActivityServerDiagnosticListenerTests.WebSocket_Header_Should_Be_Added_As_Tag_To_Request_And_Event_Spans.snap
new file mode 100644
index 00000000000..e03f14a6a0a
--- /dev/null
+++ b/src/HotChocolate/Fusion/test/Fusion.Diagnostics.Tests/__snapshots__/FusionActivityServerDiagnosticListenerTests.WebSocket_Header_Should_Be_Added_As_Tag_To_Request_And_Event_Spans.snap
@@ -0,0 +1,321 @@
+{
+ "source": {
+ "name": "HotChocolate.Fusion.Diagnostics"
+ },
+ "activities": [
+ {
+ "OperationName": "Microsoft.AspNetCore.Hosting.HttpRequestIn",
+ "DisplayName": "GET /graphql/{**slug}",
+ "Kind": "Server",
+ "Status": "Unset",
+ "tags": [
+ {
+ "Key": "server.address",
+ "Value": "localhost"
+ },
+ {
+ "Key": "server.port",
+ "Value": 5000
+ },
+ {
+ "Key": "http.request.method",
+ "Value": "GET"
+ },
+ {
+ "Key": "url.scheme",
+ "Value": "http"
+ },
+ {
+ "Key": "url.path",
+ "Value": "/graphql"
+ },
+ {
+ "Key": "network.protocol.version",
+ "Value": "1.1"
+ },
+ {
+ "Key": "http.route",
+ "Value": "/graphql/{**slug}"
+ },
+ {
+ "Key": "http.response.status_code",
+ "Value": 101
+ }
+ ],
+ "event": [],
+ "activities": [
+ {
+ "OperationName": "GraphQL Operation",
+ "DisplayName": "subscription",
+ "Kind": "Internal",
+ "Status": "Ok",
+ "tags": [
+ {
+ "Key": "graphql.processing.type",
+ "Value": "request"
+ },
+ {
+ "Key": "server.address",
+ "Value": "localhost"
+ },
+ {
+ "Key": "server.port",
+ "Value": 5000
+ },
+ {
+ "Key": "graphql.operation.type",
+ "Value": "subscription"
+ },
+ {
+ "Key": "graphql.operation.name",
+ "Value": "OnMessageSubscription"
+ },
+ {
+ "Key": "graphql.document.hash",
+ "Value": "md5:ef859b98c9d8c17038c8fd9aeecdb1e2"
+ },
+ {
+ "Key": "test.tenant",
+ "Value": "acme-42"
+ }
+ ],
+ "event": [
+ {
+ "Name": "AddedOperationPlanToCache",
+ "Tags": []
+ },
+ {
+ "Name": "AddedDocumentToCache",
+ "Tags": []
+ }
+ ],
+ "activities": [
+ {
+ "OperationName": "GraphQL Document Validation",
+ "DisplayName": "GraphQL Document Validation",
+ "Kind": "Internal",
+ "Status": "Ok",
+ "tags": [
+ {
+ "Key": "graphql.processing.type",
+ "Value": "validate"
+ },
+ {
+ "Key": "graphql.document.hash",
+ "Value": "md5:ef859b98c9d8c17038c8fd9aeecdb1e2"
+ }
+ ],
+ "event": []
+ },
+ {
+ "OperationName": "GraphQL Operation Planning",
+ "DisplayName": "GraphQL Operation Planning",
+ "Kind": "Internal",
+ "Status": "Ok",
+ "tags": [
+ {
+ "Key": "graphql.processing.type",
+ "Value": "plan"
+ },
+ {
+ "Key": "graphql.document.hash",
+ "Value": "md5:ef859b98c9d8c17038c8fd9aeecdb1e2"
+ },
+ {
+ "Key": "graphql.operation.type",
+ "Value": "subscription"
+ },
+ {
+ "Key": "graphql.operation.name",
+ "Value": "OnMessageSubscription"
+ }
+ ],
+ "event": []
+ },
+ {
+ "OperationName": "GraphQL Operation Execution",
+ "DisplayName": "GraphQL Operation Execution",
+ "Kind": "Internal",
+ "Status": "Ok",
+ "tags": [
+ {
+ "Key": "graphql.processing.type",
+ "Value": "execute"
+ },
+ {
+ "Key": "graphql.operation.type",
+ "Value": "subscription"
+ },
+ {
+ "Key": "graphql.operation.name",
+ "Value": "OnMessageSubscription"
+ },
+ {
+ "Key": "graphql.document.hash",
+ "Value": "md5:ef859b98c9d8c17038c8fd9aeecdb1e2"
+ }
+ ],
+ "event": [],
+ "activities": [
+ {
+ "OperationName": "GraphQL Step Execution",
+ "DisplayName": "GraphQL Step Execution",
+ "Kind": "Internal",
+ "Status": "Ok",
+ "tags": [
+ {
+ "Key": "graphql.processing.type",
+ "Value": "step_execute"
+ },
+ {
+ "Key": "graphql.operation.type",
+ "Value": "subscription"
+ },
+ {
+ "Key": "graphql.operation.name",
+ "Value": "OnMessageSubscription"
+ },
+ {
+ "Key": "graphql.document.hash",
+ "Value": "md5:ef859b98c9d8c17038c8fd9aeecdb1e2"
+ },
+ {
+ "Key": "graphql.operation.step.id",
+ "Value": "1"
+ },
+ {
+ "Key": "graphql.operation.step.kind",
+ "Value": "operation"
+ },
+ {
+ "Key": "graphql.operation.step.plan.id",
+ "Value": "bb94346d204b2f13a21dcba79a06b47065115db28b9ba2c04bce67382b50cef0"
+ },
+ {
+ "Key": "graphql.source_schema.name",
+ "Value": "a"
+ },
+ {
+ "Key": "graphql.source_schema.operation.name",
+ "Value": "OnMessageSubscription_ef859b98_1"
+ },
+ {
+ "Key": "graphql.source_schema.operation.hash",
+ "Value": "sha256:f9abddbb651ad8e118793eb7c4a7c8db7fe43cae95faba710a0ff35b26b17136"
+ }
+ ],
+ "event": []
+ }
+ ]
+ },
+ {
+ "OperationName": "GraphQL Subscription Event",
+ "DisplayName": "GraphQL Subscription Event",
+ "Kind": "Internal",
+ "Status": "Ok",
+ "tags": [
+ {
+ "Key": "graphql.processing.type",
+ "Value": "subscription_event"
+ },
+ {
+ "Key": "graphql.operation.type",
+ "Value": "subscription"
+ },
+ {
+ "Key": "graphql.operation.name",
+ "Value": "OnMessageSubscription"
+ },
+ {
+ "Key": "graphql.document.hash",
+ "Value": "md5:ef859b98c9d8c17038c8fd9aeecdb1e2"
+ },
+ {
+ "Key": "graphql.subscription.id",
+ "Value": ""
+ },
+ {
+ "Key": "test.tenant",
+ "Value": "acme-42"
+ }
+ ],
+ "event": []
+ }
+ ]
+ },
+ {
+ "OperationName": "System.Net.Http.HttpRequestOut",
+ "DisplayName": "System.Net.Http.HttpRequestOut",
+ "Kind": "Client",
+ "Status": "Unset",
+ "tags": [
+ {
+ "Key": "http.request.method",
+ "Value": "POST"
+ },
+ {
+ "Key": "server.address",
+ "Value": "localhost"
+ },
+ {
+ "Key": "server.port",
+ "Value": 5000
+ },
+ {
+ "Key": "url.full",
+ "Value": "http://localhost:5000/graphql"
+ },
+ {
+ "Key": "http.response.status_code",
+ "Value": 200
+ }
+ ],
+ "event": [],
+ "activities": [
+ {
+ "OperationName": "Microsoft.AspNetCore.Hosting.HttpRequestIn",
+ "DisplayName": "POST /graphql/{**slug}",
+ "Kind": "Server",
+ "Status": "Unset",
+ "tags": [
+ {
+ "Key": "server.address",
+ "Value": "localhost"
+ },
+ {
+ "Key": "server.port",
+ "Value": 5000
+ },
+ {
+ "Key": "http.request.method",
+ "Value": "POST"
+ },
+ {
+ "Key": "url.scheme",
+ "Value": "http"
+ },
+ {
+ "Key": "url.path",
+ "Value": "/graphql"
+ },
+ {
+ "Key": "network.protocol.version",
+ "Value": "1.1"
+ },
+ {
+ "Key": "http.route",
+ "Value": "/graphql/{**slug}"
+ },
+ {
+ "Key": "http.response.status_code",
+ "Value": 200
+ }
+ ],
+ "event": []
+ }
+ ]
+ }
+ ]
+ }
+ ]
+}
diff --git a/src/HotChocolate/Fusion/test/Fusion.Diagnostics.Tests/__snapshots__/FusionActivityServerDiagnosticListenerTests.WebSocket_Header_Should_Be_Added_As_Tag_To_Request_And_Event_Spans_NET11_0.snap b/src/HotChocolate/Fusion/test/Fusion.Diagnostics.Tests/__snapshots__/FusionActivityServerDiagnosticListenerTests.WebSocket_Header_Should_Be_Added_As_Tag_To_Request_And_Event_Spans_NET11_0.snap
new file mode 100644
index 00000000000..f3c18f13722
--- /dev/null
+++ b/src/HotChocolate/Fusion/test/Fusion.Diagnostics.Tests/__snapshots__/FusionActivityServerDiagnosticListenerTests.WebSocket_Header_Should_Be_Added_As_Tag_To_Request_And_Event_Spans_NET11_0.snap
@@ -0,0 +1,321 @@
+{
+ "source": {
+ "name": "HotChocolate.Fusion.Diagnostics"
+ },
+ "activities": [
+ {
+ "OperationName": "Microsoft.AspNetCore.Hosting.HttpRequestIn",
+ "DisplayName": "GET /graphql/{**slug}",
+ "Kind": "Server",
+ "Status": "Unset",
+ "tags": [
+ {
+ "Key": "server.address",
+ "Value": "localhost"
+ },
+ {
+ "Key": "server.port",
+ "Value": 5000
+ },
+ {
+ "Key": "http.request.method",
+ "Value": "GET"
+ },
+ {
+ "Key": "url.scheme",
+ "Value": "http"
+ },
+ {
+ "Key": "url.path",
+ "Value": "/graphql"
+ },
+ {
+ "Key": "network.protocol.version",
+ "Value": "1.1"
+ },
+ {
+ "Key": "http.response.status_code",
+ "Value": 101
+ },
+ {
+ "Key": "http.route",
+ "Value": "/graphql/{**slug}"
+ }
+ ],
+ "event": [],
+ "activities": [
+ {
+ "OperationName": "GraphQL Operation",
+ "DisplayName": "subscription",
+ "Kind": "Internal",
+ "Status": "Ok",
+ "tags": [
+ {
+ "Key": "graphql.processing.type",
+ "Value": "request"
+ },
+ {
+ "Key": "server.address",
+ "Value": "localhost"
+ },
+ {
+ "Key": "server.port",
+ "Value": 5000
+ },
+ {
+ "Key": "graphql.operation.type",
+ "Value": "subscription"
+ },
+ {
+ "Key": "graphql.operation.name",
+ "Value": "OnMessageSubscription"
+ },
+ {
+ "Key": "graphql.document.hash",
+ "Value": "md5:ef859b98c9d8c17038c8fd9aeecdb1e2"
+ },
+ {
+ "Key": "test.tenant",
+ "Value": "acme-42"
+ }
+ ],
+ "event": [
+ {
+ "Name": "AddedOperationPlanToCache",
+ "Tags": []
+ },
+ {
+ "Name": "AddedDocumentToCache",
+ "Tags": []
+ }
+ ],
+ "activities": [
+ {
+ "OperationName": "GraphQL Document Validation",
+ "DisplayName": "GraphQL Document Validation",
+ "Kind": "Internal",
+ "Status": "Ok",
+ "tags": [
+ {
+ "Key": "graphql.processing.type",
+ "Value": "validate"
+ },
+ {
+ "Key": "graphql.document.hash",
+ "Value": "md5:ef859b98c9d8c17038c8fd9aeecdb1e2"
+ }
+ ],
+ "event": []
+ },
+ {
+ "OperationName": "GraphQL Operation Planning",
+ "DisplayName": "GraphQL Operation Planning",
+ "Kind": "Internal",
+ "Status": "Ok",
+ "tags": [
+ {
+ "Key": "graphql.processing.type",
+ "Value": "plan"
+ },
+ {
+ "Key": "graphql.document.hash",
+ "Value": "md5:ef859b98c9d8c17038c8fd9aeecdb1e2"
+ },
+ {
+ "Key": "graphql.operation.type",
+ "Value": "subscription"
+ },
+ {
+ "Key": "graphql.operation.name",
+ "Value": "OnMessageSubscription"
+ }
+ ],
+ "event": []
+ },
+ {
+ "OperationName": "GraphQL Operation Execution",
+ "DisplayName": "GraphQL Operation Execution",
+ "Kind": "Internal",
+ "Status": "Ok",
+ "tags": [
+ {
+ "Key": "graphql.processing.type",
+ "Value": "execute"
+ },
+ {
+ "Key": "graphql.operation.type",
+ "Value": "subscription"
+ },
+ {
+ "Key": "graphql.operation.name",
+ "Value": "OnMessageSubscription"
+ },
+ {
+ "Key": "graphql.document.hash",
+ "Value": "md5:ef859b98c9d8c17038c8fd9aeecdb1e2"
+ }
+ ],
+ "event": [],
+ "activities": [
+ {
+ "OperationName": "GraphQL Step Execution",
+ "DisplayName": "GraphQL Step Execution",
+ "Kind": "Internal",
+ "Status": "Ok",
+ "tags": [
+ {
+ "Key": "graphql.processing.type",
+ "Value": "step_execute"
+ },
+ {
+ "Key": "graphql.operation.type",
+ "Value": "subscription"
+ },
+ {
+ "Key": "graphql.operation.name",
+ "Value": "OnMessageSubscription"
+ },
+ {
+ "Key": "graphql.document.hash",
+ "Value": "md5:ef859b98c9d8c17038c8fd9aeecdb1e2"
+ },
+ {
+ "Key": "graphql.operation.step.id",
+ "Value": "1"
+ },
+ {
+ "Key": "graphql.operation.step.kind",
+ "Value": "operation"
+ },
+ {
+ "Key": "graphql.operation.step.plan.id",
+ "Value": "bb94346d204b2f13a21dcba79a06b47065115db28b9ba2c04bce67382b50cef0"
+ },
+ {
+ "Key": "graphql.source_schema.name",
+ "Value": "a"
+ },
+ {
+ "Key": "graphql.source_schema.operation.name",
+ "Value": "OnMessageSubscription_ef859b98_1"
+ },
+ {
+ "Key": "graphql.source_schema.operation.hash",
+ "Value": "sha256:f9abddbb651ad8e118793eb7c4a7c8db7fe43cae95faba710a0ff35b26b17136"
+ }
+ ],
+ "event": []
+ }
+ ]
+ },
+ {
+ "OperationName": "GraphQL Subscription Event",
+ "DisplayName": "GraphQL Subscription Event",
+ "Kind": "Internal",
+ "Status": "Ok",
+ "tags": [
+ {
+ "Key": "graphql.processing.type",
+ "Value": "subscription_event"
+ },
+ {
+ "Key": "graphql.operation.type",
+ "Value": "subscription"
+ },
+ {
+ "Key": "graphql.operation.name",
+ "Value": "OnMessageSubscription"
+ },
+ {
+ "Key": "graphql.document.hash",
+ "Value": "md5:ef859b98c9d8c17038c8fd9aeecdb1e2"
+ },
+ {
+ "Key": "graphql.subscription.id",
+ "Value": ""
+ },
+ {
+ "Key": "test.tenant",
+ "Value": "acme-42"
+ }
+ ],
+ "event": []
+ }
+ ]
+ },
+ {
+ "OperationName": "System.Net.Http.HttpRequestOut",
+ "DisplayName": "System.Net.Http.HttpRequestOut",
+ "Kind": "Client",
+ "Status": "Unset",
+ "tags": [
+ {
+ "Key": "http.request.method",
+ "Value": "POST"
+ },
+ {
+ "Key": "server.address",
+ "Value": "localhost"
+ },
+ {
+ "Key": "server.port",
+ "Value": 5000
+ },
+ {
+ "Key": "url.full",
+ "Value": "http://localhost:5000/graphql"
+ },
+ {
+ "Key": "http.response.status_code",
+ "Value": 200
+ }
+ ],
+ "event": [],
+ "activities": [
+ {
+ "OperationName": "Microsoft.AspNetCore.Hosting.HttpRequestIn",
+ "DisplayName": "POST /graphql/{**slug}",
+ "Kind": "Server",
+ "Status": "Unset",
+ "tags": [
+ {
+ "Key": "server.address",
+ "Value": "localhost"
+ },
+ {
+ "Key": "server.port",
+ "Value": 5000
+ },
+ {
+ "Key": "http.request.method",
+ "Value": "POST"
+ },
+ {
+ "Key": "url.scheme",
+ "Value": "http"
+ },
+ {
+ "Key": "url.path",
+ "Value": "/graphql"
+ },
+ {
+ "Key": "network.protocol.version",
+ "Value": "1.1"
+ },
+ {
+ "Key": "http.response.status_code",
+ "Value": 200
+ },
+ {
+ "Key": "http.route",
+ "Value": "/graphql/{**slug}"
+ }
+ ],
+ "event": []
+ }
+ ]
+ }
+ ]
+ }
+ ]
+}
diff --git a/src/HotChocolate/Fusion/test/Fusion.Diagnostics.Tests/__snapshots__/FusionActivityServerDiagnosticListenerTests.WebSocket_Subscription_Should_Be_Ok_When_Client_Closes_Connection.snap b/src/HotChocolate/Fusion/test/Fusion.Diagnostics.Tests/__snapshots__/FusionActivityServerDiagnosticListenerTests.WebSocket_Subscription_Should_Be_Ok_When_Client_Closes_Connection.snap
new file mode 100644
index 00000000000..610f071d287
--- /dev/null
+++ b/src/HotChocolate/Fusion/test/Fusion.Diagnostics.Tests/__snapshots__/FusionActivityServerDiagnosticListenerTests.WebSocket_Subscription_Should_Be_Ok_When_Client_Closes_Connection.snap
@@ -0,0 +1,313 @@
+{
+ "source": {
+ "name": "HotChocolate.Fusion.Diagnostics"
+ },
+ "activities": [
+ {
+ "OperationName": "Microsoft.AspNetCore.Hosting.HttpRequestIn",
+ "DisplayName": "GET /graphql/{**slug}",
+ "Kind": "Server",
+ "Status": "Unset",
+ "tags": [
+ {
+ "Key": "server.address",
+ "Value": "localhost"
+ },
+ {
+ "Key": "server.port",
+ "Value": 5000
+ },
+ {
+ "Key": "http.request.method",
+ "Value": "GET"
+ },
+ {
+ "Key": "url.scheme",
+ "Value": "http"
+ },
+ {
+ "Key": "url.path",
+ "Value": "/graphql"
+ },
+ {
+ "Key": "network.protocol.version",
+ "Value": "1.1"
+ },
+ {
+ "Key": "http.route",
+ "Value": "/graphql/{**slug}"
+ },
+ {
+ "Key": "http.response.status_code",
+ "Value": 101
+ }
+ ],
+ "event": [],
+ "activities": [
+ {
+ "OperationName": "GraphQL Operation",
+ "DisplayName": "subscription",
+ "Kind": "Internal",
+ "Status": "Ok",
+ "tags": [
+ {
+ "Key": "graphql.processing.type",
+ "Value": "request"
+ },
+ {
+ "Key": "server.address",
+ "Value": "localhost"
+ },
+ {
+ "Key": "server.port",
+ "Value": 5000
+ },
+ {
+ "Key": "graphql.operation.type",
+ "Value": "subscription"
+ },
+ {
+ "Key": "graphql.operation.name",
+ "Value": "OnIdleMessageSubscription"
+ },
+ {
+ "Key": "graphql.document.hash",
+ "Value": "md5:51d9b3bca5722d3209429e79f8ee6435"
+ }
+ ],
+ "event": [
+ {
+ "Name": "AddedOperationPlanToCache",
+ "Tags": []
+ },
+ {
+ "Name": "AddedDocumentToCache",
+ "Tags": []
+ }
+ ],
+ "activities": [
+ {
+ "OperationName": "GraphQL Document Validation",
+ "DisplayName": "GraphQL Document Validation",
+ "Kind": "Internal",
+ "Status": "Ok",
+ "tags": [
+ {
+ "Key": "graphql.processing.type",
+ "Value": "validate"
+ },
+ {
+ "Key": "graphql.document.hash",
+ "Value": "md5:51d9b3bca5722d3209429e79f8ee6435"
+ }
+ ],
+ "event": []
+ },
+ {
+ "OperationName": "GraphQL Operation Planning",
+ "DisplayName": "GraphQL Operation Planning",
+ "Kind": "Internal",
+ "Status": "Ok",
+ "tags": [
+ {
+ "Key": "graphql.processing.type",
+ "Value": "plan"
+ },
+ {
+ "Key": "graphql.document.hash",
+ "Value": "md5:51d9b3bca5722d3209429e79f8ee6435"
+ },
+ {
+ "Key": "graphql.operation.type",
+ "Value": "subscription"
+ },
+ {
+ "Key": "graphql.operation.name",
+ "Value": "OnIdleMessageSubscription"
+ }
+ ],
+ "event": []
+ },
+ {
+ "OperationName": "GraphQL Operation Execution",
+ "DisplayName": "GraphQL Operation Execution",
+ "Kind": "Internal",
+ "Status": "Ok",
+ "tags": [
+ {
+ "Key": "graphql.processing.type",
+ "Value": "execute"
+ },
+ {
+ "Key": "graphql.operation.type",
+ "Value": "subscription"
+ },
+ {
+ "Key": "graphql.operation.name",
+ "Value": "OnIdleMessageSubscription"
+ },
+ {
+ "Key": "graphql.document.hash",
+ "Value": "md5:51d9b3bca5722d3209429e79f8ee6435"
+ }
+ ],
+ "event": [],
+ "activities": [
+ {
+ "OperationName": "GraphQL Step Execution",
+ "DisplayName": "GraphQL Step Execution",
+ "Kind": "Internal",
+ "Status": "Ok",
+ "tags": [
+ {
+ "Key": "graphql.processing.type",
+ "Value": "step_execute"
+ },
+ {
+ "Key": "graphql.operation.type",
+ "Value": "subscription"
+ },
+ {
+ "Key": "graphql.operation.name",
+ "Value": "OnIdleMessageSubscription"
+ },
+ {
+ "Key": "graphql.document.hash",
+ "Value": "md5:51d9b3bca5722d3209429e79f8ee6435"
+ },
+ {
+ "Key": "graphql.operation.step.id",
+ "Value": "1"
+ },
+ {
+ "Key": "graphql.operation.step.kind",
+ "Value": "operation"
+ },
+ {
+ "Key": "graphql.operation.step.plan.id",
+ "Value": "8fef749a1b041aca849173d1dd38a6022e142b1ebbf92aeb666766e3a0654610"
+ },
+ {
+ "Key": "graphql.source_schema.name",
+ "Value": "a"
+ },
+ {
+ "Key": "graphql.source_schema.operation.name",
+ "Value": "OnIdleMessageSubscription_51d9b3bc_1"
+ },
+ {
+ "Key": "graphql.source_schema.operation.hash",
+ "Value": "sha256:6b6ed56d2f50e7e614c9096e8e46d789db0e605355b6baf21ab088bde107ca0e"
+ }
+ ],
+ "event": []
+ }
+ ]
+ },
+ {
+ "OperationName": "GraphQL Subscription Event",
+ "DisplayName": "GraphQL Subscription Event",
+ "Kind": "Internal",
+ "Status": "Ok",
+ "tags": [
+ {
+ "Key": "graphql.processing.type",
+ "Value": "subscription_event"
+ },
+ {
+ "Key": "graphql.operation.type",
+ "Value": "subscription"
+ },
+ {
+ "Key": "graphql.operation.name",
+ "Value": "OnIdleMessageSubscription"
+ },
+ {
+ "Key": "graphql.document.hash",
+ "Value": "md5:51d9b3bca5722d3209429e79f8ee6435"
+ },
+ {
+ "Key": "graphql.subscription.id",
+ "Value": ""
+ }
+ ],
+ "event": []
+ }
+ ]
+ },
+ {
+ "OperationName": "System.Net.Http.HttpRequestOut",
+ "DisplayName": "System.Net.Http.HttpRequestOut",
+ "Kind": "Client",
+ "Status": "Unset",
+ "tags": [
+ {
+ "Key": "http.request.method",
+ "Value": "POST"
+ },
+ {
+ "Key": "server.address",
+ "Value": "localhost"
+ },
+ {
+ "Key": "server.port",
+ "Value": 5000
+ },
+ {
+ "Key": "url.full",
+ "Value": "http://localhost:5000/graphql"
+ },
+ {
+ "Key": "http.response.status_code",
+ "Value": 200
+ }
+ ],
+ "event": [],
+ "activities": [
+ {
+ "OperationName": "Microsoft.AspNetCore.Hosting.HttpRequestIn",
+ "DisplayName": "POST /graphql/{**slug}",
+ "Kind": "Server",
+ "Status": "Unset",
+ "tags": [
+ {
+ "Key": "server.address",
+ "Value": "localhost"
+ },
+ {
+ "Key": "server.port",
+ "Value": 5000
+ },
+ {
+ "Key": "http.request.method",
+ "Value": "POST"
+ },
+ {
+ "Key": "url.scheme",
+ "Value": "http"
+ },
+ {
+ "Key": "url.path",
+ "Value": "/graphql"
+ },
+ {
+ "Key": "network.protocol.version",
+ "Value": "1.1"
+ },
+ {
+ "Key": "http.route",
+ "Value": "/graphql/{**slug}"
+ },
+ {
+ "Key": "http.response.status_code",
+ "Value": 200
+ }
+ ],
+ "event": []
+ }
+ ]
+ }
+ ]
+ }
+ ]
+}
diff --git a/src/HotChocolate/Fusion/test/Fusion.Diagnostics.Tests/__snapshots__/FusionActivityServerDiagnosticListenerTests.WebSocket_Subscription_Should_Be_Ok_When_Client_Closes_Connection_NET11_0.snap b/src/HotChocolate/Fusion/test/Fusion.Diagnostics.Tests/__snapshots__/FusionActivityServerDiagnosticListenerTests.WebSocket_Subscription_Should_Be_Ok_When_Client_Closes_Connection_NET11_0.snap
new file mode 100644
index 00000000000..9dfe166518d
--- /dev/null
+++ b/src/HotChocolate/Fusion/test/Fusion.Diagnostics.Tests/__snapshots__/FusionActivityServerDiagnosticListenerTests.WebSocket_Subscription_Should_Be_Ok_When_Client_Closes_Connection_NET11_0.snap
@@ -0,0 +1,313 @@
+{
+ "source": {
+ "name": "HotChocolate.Fusion.Diagnostics"
+ },
+ "activities": [
+ {
+ "OperationName": "Microsoft.AspNetCore.Hosting.HttpRequestIn",
+ "DisplayName": "GET /graphql/{**slug}",
+ "Kind": "Server",
+ "Status": "Unset",
+ "tags": [
+ {
+ "Key": "server.address",
+ "Value": "localhost"
+ },
+ {
+ "Key": "server.port",
+ "Value": 5000
+ },
+ {
+ "Key": "http.request.method",
+ "Value": "GET"
+ },
+ {
+ "Key": "url.scheme",
+ "Value": "http"
+ },
+ {
+ "Key": "url.path",
+ "Value": "/graphql"
+ },
+ {
+ "Key": "network.protocol.version",
+ "Value": "1.1"
+ },
+ {
+ "Key": "http.response.status_code",
+ "Value": 101
+ },
+ {
+ "Key": "http.route",
+ "Value": "/graphql/{**slug}"
+ }
+ ],
+ "event": [],
+ "activities": [
+ {
+ "OperationName": "GraphQL Operation",
+ "DisplayName": "subscription",
+ "Kind": "Internal",
+ "Status": "Ok",
+ "tags": [
+ {
+ "Key": "graphql.processing.type",
+ "Value": "request"
+ },
+ {
+ "Key": "server.address",
+ "Value": "localhost"
+ },
+ {
+ "Key": "server.port",
+ "Value": 5000
+ },
+ {
+ "Key": "graphql.operation.type",
+ "Value": "subscription"
+ },
+ {
+ "Key": "graphql.operation.name",
+ "Value": "OnIdleMessageSubscription"
+ },
+ {
+ "Key": "graphql.document.hash",
+ "Value": "md5:51d9b3bca5722d3209429e79f8ee6435"
+ }
+ ],
+ "event": [
+ {
+ "Name": "AddedOperationPlanToCache",
+ "Tags": []
+ },
+ {
+ "Name": "AddedDocumentToCache",
+ "Tags": []
+ }
+ ],
+ "activities": [
+ {
+ "OperationName": "GraphQL Document Validation",
+ "DisplayName": "GraphQL Document Validation",
+ "Kind": "Internal",
+ "Status": "Ok",
+ "tags": [
+ {
+ "Key": "graphql.processing.type",
+ "Value": "validate"
+ },
+ {
+ "Key": "graphql.document.hash",
+ "Value": "md5:51d9b3bca5722d3209429e79f8ee6435"
+ }
+ ],
+ "event": []
+ },
+ {
+ "OperationName": "GraphQL Operation Planning",
+ "DisplayName": "GraphQL Operation Planning",
+ "Kind": "Internal",
+ "Status": "Ok",
+ "tags": [
+ {
+ "Key": "graphql.processing.type",
+ "Value": "plan"
+ },
+ {
+ "Key": "graphql.document.hash",
+ "Value": "md5:51d9b3bca5722d3209429e79f8ee6435"
+ },
+ {
+ "Key": "graphql.operation.type",
+ "Value": "subscription"
+ },
+ {
+ "Key": "graphql.operation.name",
+ "Value": "OnIdleMessageSubscription"
+ }
+ ],
+ "event": []
+ },
+ {
+ "OperationName": "GraphQL Operation Execution",
+ "DisplayName": "GraphQL Operation Execution",
+ "Kind": "Internal",
+ "Status": "Ok",
+ "tags": [
+ {
+ "Key": "graphql.processing.type",
+ "Value": "execute"
+ },
+ {
+ "Key": "graphql.operation.type",
+ "Value": "subscription"
+ },
+ {
+ "Key": "graphql.operation.name",
+ "Value": "OnIdleMessageSubscription"
+ },
+ {
+ "Key": "graphql.document.hash",
+ "Value": "md5:51d9b3bca5722d3209429e79f8ee6435"
+ }
+ ],
+ "event": [],
+ "activities": [
+ {
+ "OperationName": "GraphQL Step Execution",
+ "DisplayName": "GraphQL Step Execution",
+ "Kind": "Internal",
+ "Status": "Ok",
+ "tags": [
+ {
+ "Key": "graphql.processing.type",
+ "Value": "step_execute"
+ },
+ {
+ "Key": "graphql.operation.type",
+ "Value": "subscription"
+ },
+ {
+ "Key": "graphql.operation.name",
+ "Value": "OnIdleMessageSubscription"
+ },
+ {
+ "Key": "graphql.document.hash",
+ "Value": "md5:51d9b3bca5722d3209429e79f8ee6435"
+ },
+ {
+ "Key": "graphql.operation.step.id",
+ "Value": "1"
+ },
+ {
+ "Key": "graphql.operation.step.kind",
+ "Value": "operation"
+ },
+ {
+ "Key": "graphql.operation.step.plan.id",
+ "Value": "8fef749a1b041aca849173d1dd38a6022e142b1ebbf92aeb666766e3a0654610"
+ },
+ {
+ "Key": "graphql.source_schema.name",
+ "Value": "a"
+ },
+ {
+ "Key": "graphql.source_schema.operation.name",
+ "Value": "OnIdleMessageSubscription_51d9b3bc_1"
+ },
+ {
+ "Key": "graphql.source_schema.operation.hash",
+ "Value": "sha256:6b6ed56d2f50e7e614c9096e8e46d789db0e605355b6baf21ab088bde107ca0e"
+ }
+ ],
+ "event": []
+ }
+ ]
+ },
+ {
+ "OperationName": "GraphQL Subscription Event",
+ "DisplayName": "GraphQL Subscription Event",
+ "Kind": "Internal",
+ "Status": "Ok",
+ "tags": [
+ {
+ "Key": "graphql.processing.type",
+ "Value": "subscription_event"
+ },
+ {
+ "Key": "graphql.operation.type",
+ "Value": "subscription"
+ },
+ {
+ "Key": "graphql.operation.name",
+ "Value": "OnIdleMessageSubscription"
+ },
+ {
+ "Key": "graphql.document.hash",
+ "Value": "md5:51d9b3bca5722d3209429e79f8ee6435"
+ },
+ {
+ "Key": "graphql.subscription.id",
+ "Value": ""
+ }
+ ],
+ "event": []
+ }
+ ]
+ },
+ {
+ "OperationName": "System.Net.Http.HttpRequestOut",
+ "DisplayName": "System.Net.Http.HttpRequestOut",
+ "Kind": "Client",
+ "Status": "Unset",
+ "tags": [
+ {
+ "Key": "http.request.method",
+ "Value": "POST"
+ },
+ {
+ "Key": "server.address",
+ "Value": "localhost"
+ },
+ {
+ "Key": "server.port",
+ "Value": 5000
+ },
+ {
+ "Key": "url.full",
+ "Value": "http://localhost:5000/graphql"
+ },
+ {
+ "Key": "http.response.status_code",
+ "Value": 200
+ }
+ ],
+ "event": [],
+ "activities": [
+ {
+ "OperationName": "Microsoft.AspNetCore.Hosting.HttpRequestIn",
+ "DisplayName": "POST /graphql/{**slug}",
+ "Kind": "Server",
+ "Status": "Unset",
+ "tags": [
+ {
+ "Key": "server.address",
+ "Value": "localhost"
+ },
+ {
+ "Key": "server.port",
+ "Value": 5000
+ },
+ {
+ "Key": "http.request.method",
+ "Value": "POST"
+ },
+ {
+ "Key": "url.scheme",
+ "Value": "http"
+ },
+ {
+ "Key": "url.path",
+ "Value": "/graphql"
+ },
+ {
+ "Key": "network.protocol.version",
+ "Value": "1.1"
+ },
+ {
+ "Key": "http.response.status_code",
+ "Value": 200
+ },
+ {
+ "Key": "http.route",
+ "Value": "/graphql/{**slug}"
+ }
+ ],
+ "event": []
+ }
+ ]
+ }
+ ]
+ }
+ ]
+}
diff --git a/src/HotChocolate/Fusion/test/Fusion.Diagnostics.Tests/__snapshots__/FusionActivityServerDiagnosticListenerTests.WebSocket_Subscription_Should_Be_Ok_When_Server_Completes.snap b/src/HotChocolate/Fusion/test/Fusion.Diagnostics.Tests/__snapshots__/FusionActivityServerDiagnosticListenerTests.WebSocket_Subscription_Should_Be_Ok_When_Server_Completes.snap
new file mode 100644
index 00000000000..1e77d211eae
--- /dev/null
+++ b/src/HotChocolate/Fusion/test/Fusion.Diagnostics.Tests/__snapshots__/FusionActivityServerDiagnosticListenerTests.WebSocket_Subscription_Should_Be_Ok_When_Server_Completes.snap
@@ -0,0 +1,313 @@
+{
+ "source": {
+ "name": "HotChocolate.Fusion.Diagnostics"
+ },
+ "activities": [
+ {
+ "OperationName": "Microsoft.AspNetCore.Hosting.HttpRequestIn",
+ "DisplayName": "GET /graphql/{**slug}",
+ "Kind": "Server",
+ "Status": "Unset",
+ "tags": [
+ {
+ "Key": "server.address",
+ "Value": "localhost"
+ },
+ {
+ "Key": "server.port",
+ "Value": 5000
+ },
+ {
+ "Key": "http.request.method",
+ "Value": "GET"
+ },
+ {
+ "Key": "url.scheme",
+ "Value": "http"
+ },
+ {
+ "Key": "url.path",
+ "Value": "/graphql"
+ },
+ {
+ "Key": "network.protocol.version",
+ "Value": "1.1"
+ },
+ {
+ "Key": "http.route",
+ "Value": "/graphql/{**slug}"
+ },
+ {
+ "Key": "http.response.status_code",
+ "Value": 101
+ }
+ ],
+ "event": [],
+ "activities": [
+ {
+ "OperationName": "GraphQL Operation",
+ "DisplayName": "subscription",
+ "Kind": "Internal",
+ "Status": "Ok",
+ "tags": [
+ {
+ "Key": "graphql.processing.type",
+ "Value": "request"
+ },
+ {
+ "Key": "server.address",
+ "Value": "localhost"
+ },
+ {
+ "Key": "server.port",
+ "Value": 5000
+ },
+ {
+ "Key": "graphql.operation.type",
+ "Value": "subscription"
+ },
+ {
+ "Key": "graphql.operation.name",
+ "Value": "OnMessageSubscription"
+ },
+ {
+ "Key": "graphql.document.hash",
+ "Value": "md5:ef859b98c9d8c17038c8fd9aeecdb1e2"
+ }
+ ],
+ "event": [
+ {
+ "Name": "AddedOperationPlanToCache",
+ "Tags": []
+ },
+ {
+ "Name": "AddedDocumentToCache",
+ "Tags": []
+ }
+ ],
+ "activities": [
+ {
+ "OperationName": "GraphQL Document Validation",
+ "DisplayName": "GraphQL Document Validation",
+ "Kind": "Internal",
+ "Status": "Ok",
+ "tags": [
+ {
+ "Key": "graphql.processing.type",
+ "Value": "validate"
+ },
+ {
+ "Key": "graphql.document.hash",
+ "Value": "md5:ef859b98c9d8c17038c8fd9aeecdb1e2"
+ }
+ ],
+ "event": []
+ },
+ {
+ "OperationName": "GraphQL Operation Planning",
+ "DisplayName": "GraphQL Operation Planning",
+ "Kind": "Internal",
+ "Status": "Ok",
+ "tags": [
+ {
+ "Key": "graphql.processing.type",
+ "Value": "plan"
+ },
+ {
+ "Key": "graphql.document.hash",
+ "Value": "md5:ef859b98c9d8c17038c8fd9aeecdb1e2"
+ },
+ {
+ "Key": "graphql.operation.type",
+ "Value": "subscription"
+ },
+ {
+ "Key": "graphql.operation.name",
+ "Value": "OnMessageSubscription"
+ }
+ ],
+ "event": []
+ },
+ {
+ "OperationName": "GraphQL Operation Execution",
+ "DisplayName": "GraphQL Operation Execution",
+ "Kind": "Internal",
+ "Status": "Ok",
+ "tags": [
+ {
+ "Key": "graphql.processing.type",
+ "Value": "execute"
+ },
+ {
+ "Key": "graphql.operation.type",
+ "Value": "subscription"
+ },
+ {
+ "Key": "graphql.operation.name",
+ "Value": "OnMessageSubscription"
+ },
+ {
+ "Key": "graphql.document.hash",
+ "Value": "md5:ef859b98c9d8c17038c8fd9aeecdb1e2"
+ }
+ ],
+ "event": [],
+ "activities": [
+ {
+ "OperationName": "GraphQL Step Execution",
+ "DisplayName": "GraphQL Step Execution",
+ "Kind": "Internal",
+ "Status": "Ok",
+ "tags": [
+ {
+ "Key": "graphql.processing.type",
+ "Value": "step_execute"
+ },
+ {
+ "Key": "graphql.operation.type",
+ "Value": "subscription"
+ },
+ {
+ "Key": "graphql.operation.name",
+ "Value": "OnMessageSubscription"
+ },
+ {
+ "Key": "graphql.document.hash",
+ "Value": "md5:ef859b98c9d8c17038c8fd9aeecdb1e2"
+ },
+ {
+ "Key": "graphql.operation.step.id",
+ "Value": "1"
+ },
+ {
+ "Key": "graphql.operation.step.kind",
+ "Value": "operation"
+ },
+ {
+ "Key": "graphql.operation.step.plan.id",
+ "Value": "bb94346d204b2f13a21dcba79a06b47065115db28b9ba2c04bce67382b50cef0"
+ },
+ {
+ "Key": "graphql.source_schema.name",
+ "Value": "a"
+ },
+ {
+ "Key": "graphql.source_schema.operation.name",
+ "Value": "OnMessageSubscription_ef859b98_1"
+ },
+ {
+ "Key": "graphql.source_schema.operation.hash",
+ "Value": "sha256:f9abddbb651ad8e118793eb7c4a7c8db7fe43cae95faba710a0ff35b26b17136"
+ }
+ ],
+ "event": []
+ }
+ ]
+ },
+ {
+ "OperationName": "GraphQL Subscription Event",
+ "DisplayName": "GraphQL Subscription Event",
+ "Kind": "Internal",
+ "Status": "Ok",
+ "tags": [
+ {
+ "Key": "graphql.processing.type",
+ "Value": "subscription_event"
+ },
+ {
+ "Key": "graphql.operation.type",
+ "Value": "subscription"
+ },
+ {
+ "Key": "graphql.operation.name",
+ "Value": "OnMessageSubscription"
+ },
+ {
+ "Key": "graphql.document.hash",
+ "Value": "md5:ef859b98c9d8c17038c8fd9aeecdb1e2"
+ },
+ {
+ "Key": "graphql.subscription.id",
+ "Value": ""
+ }
+ ],
+ "event": []
+ }
+ ]
+ },
+ {
+ "OperationName": "System.Net.Http.HttpRequestOut",
+ "DisplayName": "System.Net.Http.HttpRequestOut",
+ "Kind": "Client",
+ "Status": "Unset",
+ "tags": [
+ {
+ "Key": "http.request.method",
+ "Value": "POST"
+ },
+ {
+ "Key": "server.address",
+ "Value": "localhost"
+ },
+ {
+ "Key": "server.port",
+ "Value": 5000
+ },
+ {
+ "Key": "url.full",
+ "Value": "http://localhost:5000/graphql"
+ },
+ {
+ "Key": "http.response.status_code",
+ "Value": 200
+ }
+ ],
+ "event": [],
+ "activities": [
+ {
+ "OperationName": "Microsoft.AspNetCore.Hosting.HttpRequestIn",
+ "DisplayName": "POST /graphql/{**slug}",
+ "Kind": "Server",
+ "Status": "Unset",
+ "tags": [
+ {
+ "Key": "server.address",
+ "Value": "localhost"
+ },
+ {
+ "Key": "server.port",
+ "Value": 5000
+ },
+ {
+ "Key": "http.request.method",
+ "Value": "POST"
+ },
+ {
+ "Key": "url.scheme",
+ "Value": "http"
+ },
+ {
+ "Key": "url.path",
+ "Value": "/graphql"
+ },
+ {
+ "Key": "network.protocol.version",
+ "Value": "1.1"
+ },
+ {
+ "Key": "http.route",
+ "Value": "/graphql/{**slug}"
+ },
+ {
+ "Key": "http.response.status_code",
+ "Value": 200
+ }
+ ],
+ "event": []
+ }
+ ]
+ }
+ ]
+ }
+ ]
+}
diff --git a/src/HotChocolate/Fusion/test/Fusion.Diagnostics.Tests/__snapshots__/FusionActivityServerDiagnosticListenerTests.WebSocket_Subscription_Should_Be_Ok_When_Server_Completes_Default.snap b/src/HotChocolate/Fusion/test/Fusion.Diagnostics.Tests/__snapshots__/FusionActivityServerDiagnosticListenerTests.WebSocket_Subscription_Should_Be_Ok_When_Server_Completes_Default.snap
new file mode 100644
index 00000000000..3b4398e3218
--- /dev/null
+++ b/src/HotChocolate/Fusion/test/Fusion.Diagnostics.Tests/__snapshots__/FusionActivityServerDiagnosticListenerTests.WebSocket_Subscription_Should_Be_Ok_When_Server_Completes_Default.snap
@@ -0,0 +1,286 @@
+{
+ "source": {
+ "name": "HotChocolate.Fusion.Diagnostics"
+ },
+ "activities": [
+ {
+ "OperationName": "Microsoft.AspNetCore.Hosting.HttpRequestIn",
+ "DisplayName": "GET /graphql/{**slug}",
+ "Kind": "Server",
+ "Status": "Unset",
+ "tags": [
+ {
+ "Key": "server.address",
+ "Value": "localhost"
+ },
+ {
+ "Key": "server.port",
+ "Value": 5000
+ },
+ {
+ "Key": "http.request.method",
+ "Value": "GET"
+ },
+ {
+ "Key": "url.scheme",
+ "Value": "http"
+ },
+ {
+ "Key": "url.path",
+ "Value": "/graphql"
+ },
+ {
+ "Key": "network.protocol.version",
+ "Value": "1.1"
+ },
+ {
+ "Key": "http.route",
+ "Value": "/graphql/{**slug}"
+ },
+ {
+ "Key": "http.response.status_code",
+ "Value": 101
+ }
+ ],
+ "event": [],
+ "activities": [
+ {
+ "OperationName": "GraphQL Operation",
+ "DisplayName": "subscription",
+ "Kind": "Internal",
+ "Status": "Ok",
+ "tags": [
+ {
+ "Key": "graphql.processing.type",
+ "Value": "request"
+ },
+ {
+ "Key": "server.address",
+ "Value": "localhost"
+ },
+ {
+ "Key": "server.port",
+ "Value": 5000
+ },
+ {
+ "Key": "graphql.operation.type",
+ "Value": "subscription"
+ },
+ {
+ "Key": "graphql.operation.name",
+ "Value": "OnMessageSubscription"
+ },
+ {
+ "Key": "graphql.document.hash",
+ "Value": "md5:ef859b98c9d8c17038c8fd9aeecdb1e2"
+ }
+ ],
+ "event": [
+ {
+ "Name": "AddedOperationPlanToCache",
+ "Tags": []
+ },
+ {
+ "Name": "AddedDocumentToCache",
+ "Tags": []
+ }
+ ],
+ "activities": [
+ {
+ "OperationName": "GraphQL Document Validation",
+ "DisplayName": "GraphQL Document Validation",
+ "Kind": "Internal",
+ "Status": "Ok",
+ "tags": [
+ {
+ "Key": "graphql.processing.type",
+ "Value": "validate"
+ },
+ {
+ "Key": "graphql.document.hash",
+ "Value": "md5:ef859b98c9d8c17038c8fd9aeecdb1e2"
+ }
+ ],
+ "event": []
+ },
+ {
+ "OperationName": "GraphQL Operation Planning",
+ "DisplayName": "GraphQL Operation Planning",
+ "Kind": "Internal",
+ "Status": "Ok",
+ "tags": [
+ {
+ "Key": "graphql.processing.type",
+ "Value": "plan"
+ },
+ {
+ "Key": "graphql.document.hash",
+ "Value": "md5:ef859b98c9d8c17038c8fd9aeecdb1e2"
+ },
+ {
+ "Key": "graphql.operation.type",
+ "Value": "subscription"
+ },
+ {
+ "Key": "graphql.operation.name",
+ "Value": "OnMessageSubscription"
+ }
+ ],
+ "event": []
+ },
+ {
+ "OperationName": "GraphQL Subscription Event",
+ "DisplayName": "GraphQL Subscription Event",
+ "Kind": "Internal",
+ "Status": "Ok",
+ "tags": [
+ {
+ "Key": "graphql.processing.type",
+ "Value": "subscription_event"
+ },
+ {
+ "Key": "graphql.operation.type",
+ "Value": "subscription"
+ },
+ {
+ "Key": "graphql.operation.name",
+ "Value": "OnMessageSubscription"
+ },
+ {
+ "Key": "graphql.document.hash",
+ "Value": "md5:ef859b98c9d8c17038c8fd9aeecdb1e2"
+ },
+ {
+ "Key": "graphql.subscription.id",
+ "Value": ""
+ }
+ ],
+ "event": []
+ }
+ ]
+ },
+ {
+ "OperationName": "GraphQL Step Execution",
+ "DisplayName": "GraphQL Step Execution",
+ "Kind": "Internal",
+ "Status": "Ok",
+ "tags": [
+ {
+ "Key": "graphql.processing.type",
+ "Value": "step_execute"
+ },
+ {
+ "Key": "graphql.operation.type",
+ "Value": "subscription"
+ },
+ {
+ "Key": "graphql.operation.name",
+ "Value": "OnMessageSubscription"
+ },
+ {
+ "Key": "graphql.document.hash",
+ "Value": "md5:ef859b98c9d8c17038c8fd9aeecdb1e2"
+ },
+ {
+ "Key": "graphql.operation.step.id",
+ "Value": "1"
+ },
+ {
+ "Key": "graphql.operation.step.kind",
+ "Value": "operation"
+ },
+ {
+ "Key": "graphql.operation.step.plan.id",
+ "Value": "bb94346d204b2f13a21dcba79a06b47065115db28b9ba2c04bce67382b50cef0"
+ },
+ {
+ "Key": "graphql.source_schema.name",
+ "Value": "a"
+ },
+ {
+ "Key": "graphql.source_schema.operation.name",
+ "Value": "OnMessageSubscription_ef859b98_1"
+ },
+ {
+ "Key": "graphql.source_schema.operation.hash",
+ "Value": "sha256:f9abddbb651ad8e118793eb7c4a7c8db7fe43cae95faba710a0ff35b26b17136"
+ }
+ ],
+ "event": []
+ },
+ {
+ "OperationName": "System.Net.Http.HttpRequestOut",
+ "DisplayName": "System.Net.Http.HttpRequestOut",
+ "Kind": "Client",
+ "Status": "Unset",
+ "tags": [
+ {
+ "Key": "http.request.method",
+ "Value": "POST"
+ },
+ {
+ "Key": "server.address",
+ "Value": "localhost"
+ },
+ {
+ "Key": "server.port",
+ "Value": 5000
+ },
+ {
+ "Key": "url.full",
+ "Value": "http://localhost:5000/graphql"
+ },
+ {
+ "Key": "http.response.status_code",
+ "Value": 200
+ }
+ ],
+ "event": [],
+ "activities": [
+ {
+ "OperationName": "Microsoft.AspNetCore.Hosting.HttpRequestIn",
+ "DisplayName": "POST /graphql/{**slug}",
+ "Kind": "Server",
+ "Status": "Unset",
+ "tags": [
+ {
+ "Key": "server.address",
+ "Value": "localhost"
+ },
+ {
+ "Key": "server.port",
+ "Value": 5000
+ },
+ {
+ "Key": "http.request.method",
+ "Value": "POST"
+ },
+ {
+ "Key": "url.scheme",
+ "Value": "http"
+ },
+ {
+ "Key": "url.path",
+ "Value": "/graphql"
+ },
+ {
+ "Key": "network.protocol.version",
+ "Value": "1.1"
+ },
+ {
+ "Key": "http.route",
+ "Value": "/graphql/{**slug}"
+ },
+ {
+ "Key": "http.response.status_code",
+ "Value": 200
+ }
+ ],
+ "event": []
+ }
+ ]
+ }
+ ]
+ }
+ ]
+}
diff --git a/src/HotChocolate/Fusion/test/Fusion.Diagnostics.Tests/__snapshots__/FusionActivityServerDiagnosticListenerTests.WebSocket_Subscription_Should_Be_Ok_When_Server_Completes_Default_NET11_0.snap b/src/HotChocolate/Fusion/test/Fusion.Diagnostics.Tests/__snapshots__/FusionActivityServerDiagnosticListenerTests.WebSocket_Subscription_Should_Be_Ok_When_Server_Completes_Default_NET11_0.snap
new file mode 100644
index 00000000000..e6e5ffeacc2
--- /dev/null
+++ b/src/HotChocolate/Fusion/test/Fusion.Diagnostics.Tests/__snapshots__/FusionActivityServerDiagnosticListenerTests.WebSocket_Subscription_Should_Be_Ok_When_Server_Completes_Default_NET11_0.snap
@@ -0,0 +1,286 @@
+{
+ "source": {
+ "name": "HotChocolate.Fusion.Diagnostics"
+ },
+ "activities": [
+ {
+ "OperationName": "Microsoft.AspNetCore.Hosting.HttpRequestIn",
+ "DisplayName": "GET /graphql/{**slug}",
+ "Kind": "Server",
+ "Status": "Unset",
+ "tags": [
+ {
+ "Key": "server.address",
+ "Value": "localhost"
+ },
+ {
+ "Key": "server.port",
+ "Value": 5000
+ },
+ {
+ "Key": "http.request.method",
+ "Value": "GET"
+ },
+ {
+ "Key": "url.scheme",
+ "Value": "http"
+ },
+ {
+ "Key": "url.path",
+ "Value": "/graphql"
+ },
+ {
+ "Key": "network.protocol.version",
+ "Value": "1.1"
+ },
+ {
+ "Key": "http.response.status_code",
+ "Value": 101
+ },
+ {
+ "Key": "http.route",
+ "Value": "/graphql/{**slug}"
+ }
+ ],
+ "event": [],
+ "activities": [
+ {
+ "OperationName": "GraphQL Operation",
+ "DisplayName": "subscription",
+ "Kind": "Internal",
+ "Status": "Ok",
+ "tags": [
+ {
+ "Key": "graphql.processing.type",
+ "Value": "request"
+ },
+ {
+ "Key": "server.address",
+ "Value": "localhost"
+ },
+ {
+ "Key": "server.port",
+ "Value": 5000
+ },
+ {
+ "Key": "graphql.operation.type",
+ "Value": "subscription"
+ },
+ {
+ "Key": "graphql.operation.name",
+ "Value": "OnMessageSubscription"
+ },
+ {
+ "Key": "graphql.document.hash",
+ "Value": "md5:ef859b98c9d8c17038c8fd9aeecdb1e2"
+ }
+ ],
+ "event": [
+ {
+ "Name": "AddedOperationPlanToCache",
+ "Tags": []
+ },
+ {
+ "Name": "AddedDocumentToCache",
+ "Tags": []
+ }
+ ],
+ "activities": [
+ {
+ "OperationName": "GraphQL Document Validation",
+ "DisplayName": "GraphQL Document Validation",
+ "Kind": "Internal",
+ "Status": "Ok",
+ "tags": [
+ {
+ "Key": "graphql.processing.type",
+ "Value": "validate"
+ },
+ {
+ "Key": "graphql.document.hash",
+ "Value": "md5:ef859b98c9d8c17038c8fd9aeecdb1e2"
+ }
+ ],
+ "event": []
+ },
+ {
+ "OperationName": "GraphQL Operation Planning",
+ "DisplayName": "GraphQL Operation Planning",
+ "Kind": "Internal",
+ "Status": "Ok",
+ "tags": [
+ {
+ "Key": "graphql.processing.type",
+ "Value": "plan"
+ },
+ {
+ "Key": "graphql.document.hash",
+ "Value": "md5:ef859b98c9d8c17038c8fd9aeecdb1e2"
+ },
+ {
+ "Key": "graphql.operation.type",
+ "Value": "subscription"
+ },
+ {
+ "Key": "graphql.operation.name",
+ "Value": "OnMessageSubscription"
+ }
+ ],
+ "event": []
+ },
+ {
+ "OperationName": "GraphQL Subscription Event",
+ "DisplayName": "GraphQL Subscription Event",
+ "Kind": "Internal",
+ "Status": "Ok",
+ "tags": [
+ {
+ "Key": "graphql.processing.type",
+ "Value": "subscription_event"
+ },
+ {
+ "Key": "graphql.operation.type",
+ "Value": "subscription"
+ },
+ {
+ "Key": "graphql.operation.name",
+ "Value": "OnMessageSubscription"
+ },
+ {
+ "Key": "graphql.document.hash",
+ "Value": "md5:ef859b98c9d8c17038c8fd9aeecdb1e2"
+ },
+ {
+ "Key": "graphql.subscription.id",
+ "Value": ""
+ }
+ ],
+ "event": []
+ }
+ ]
+ },
+ {
+ "OperationName": "GraphQL Step Execution",
+ "DisplayName": "GraphQL Step Execution",
+ "Kind": "Internal",
+ "Status": "Ok",
+ "tags": [
+ {
+ "Key": "graphql.processing.type",
+ "Value": "step_execute"
+ },
+ {
+ "Key": "graphql.operation.type",
+ "Value": "subscription"
+ },
+ {
+ "Key": "graphql.operation.name",
+ "Value": "OnMessageSubscription"
+ },
+ {
+ "Key": "graphql.document.hash",
+ "Value": "md5:ef859b98c9d8c17038c8fd9aeecdb1e2"
+ },
+ {
+ "Key": "graphql.operation.step.id",
+ "Value": "1"
+ },
+ {
+ "Key": "graphql.operation.step.kind",
+ "Value": "operation"
+ },
+ {
+ "Key": "graphql.operation.step.plan.id",
+ "Value": "bb94346d204b2f13a21dcba79a06b47065115db28b9ba2c04bce67382b50cef0"
+ },
+ {
+ "Key": "graphql.source_schema.name",
+ "Value": "a"
+ },
+ {
+ "Key": "graphql.source_schema.operation.name",
+ "Value": "OnMessageSubscription_ef859b98_1"
+ },
+ {
+ "Key": "graphql.source_schema.operation.hash",
+ "Value": "sha256:f9abddbb651ad8e118793eb7c4a7c8db7fe43cae95faba710a0ff35b26b17136"
+ }
+ ],
+ "event": []
+ },
+ {
+ "OperationName": "System.Net.Http.HttpRequestOut",
+ "DisplayName": "System.Net.Http.HttpRequestOut",
+ "Kind": "Client",
+ "Status": "Unset",
+ "tags": [
+ {
+ "Key": "http.request.method",
+ "Value": "POST"
+ },
+ {
+ "Key": "server.address",
+ "Value": "localhost"
+ },
+ {
+ "Key": "server.port",
+ "Value": 5000
+ },
+ {
+ "Key": "url.full",
+ "Value": "http://localhost:5000/graphql"
+ },
+ {
+ "Key": "http.response.status_code",
+ "Value": 200
+ }
+ ],
+ "event": [],
+ "activities": [
+ {
+ "OperationName": "Microsoft.AspNetCore.Hosting.HttpRequestIn",
+ "DisplayName": "POST /graphql/{**slug}",
+ "Kind": "Server",
+ "Status": "Unset",
+ "tags": [
+ {
+ "Key": "server.address",
+ "Value": "localhost"
+ },
+ {
+ "Key": "server.port",
+ "Value": 5000
+ },
+ {
+ "Key": "http.request.method",
+ "Value": "POST"
+ },
+ {
+ "Key": "url.scheme",
+ "Value": "http"
+ },
+ {
+ "Key": "url.path",
+ "Value": "/graphql"
+ },
+ {
+ "Key": "network.protocol.version",
+ "Value": "1.1"
+ },
+ {
+ "Key": "http.response.status_code",
+ "Value": 200
+ },
+ {
+ "Key": "http.route",
+ "Value": "/graphql/{**slug}"
+ }
+ ],
+ "event": []
+ }
+ ]
+ }
+ ]
+ }
+ ]
+}
diff --git a/src/HotChocolate/Fusion/test/Fusion.Diagnostics.Tests/__snapshots__/FusionActivityServerDiagnosticListenerTests.WebSocket_Subscription_Should_Be_Ok_When_Server_Completes_NET11_0.snap b/src/HotChocolate/Fusion/test/Fusion.Diagnostics.Tests/__snapshots__/FusionActivityServerDiagnosticListenerTests.WebSocket_Subscription_Should_Be_Ok_When_Server_Completes_NET11_0.snap
new file mode 100644
index 00000000000..d7c24ff2863
--- /dev/null
+++ b/src/HotChocolate/Fusion/test/Fusion.Diagnostics.Tests/__snapshots__/FusionActivityServerDiagnosticListenerTests.WebSocket_Subscription_Should_Be_Ok_When_Server_Completes_NET11_0.snap
@@ -0,0 +1,313 @@
+{
+ "source": {
+ "name": "HotChocolate.Fusion.Diagnostics"
+ },
+ "activities": [
+ {
+ "OperationName": "Microsoft.AspNetCore.Hosting.HttpRequestIn",
+ "DisplayName": "GET /graphql/{**slug}",
+ "Kind": "Server",
+ "Status": "Unset",
+ "tags": [
+ {
+ "Key": "server.address",
+ "Value": "localhost"
+ },
+ {
+ "Key": "server.port",
+ "Value": 5000
+ },
+ {
+ "Key": "http.request.method",
+ "Value": "GET"
+ },
+ {
+ "Key": "url.scheme",
+ "Value": "http"
+ },
+ {
+ "Key": "url.path",
+ "Value": "/graphql"
+ },
+ {
+ "Key": "network.protocol.version",
+ "Value": "1.1"
+ },
+ {
+ "Key": "http.response.status_code",
+ "Value": 101
+ },
+ {
+ "Key": "http.route",
+ "Value": "/graphql/{**slug}"
+ }
+ ],
+ "event": [],
+ "activities": [
+ {
+ "OperationName": "GraphQL Operation",
+ "DisplayName": "subscription",
+ "Kind": "Internal",
+ "Status": "Ok",
+ "tags": [
+ {
+ "Key": "graphql.processing.type",
+ "Value": "request"
+ },
+ {
+ "Key": "server.address",
+ "Value": "localhost"
+ },
+ {
+ "Key": "server.port",
+ "Value": 5000
+ },
+ {
+ "Key": "graphql.operation.type",
+ "Value": "subscription"
+ },
+ {
+ "Key": "graphql.operation.name",
+ "Value": "OnMessageSubscription"
+ },
+ {
+ "Key": "graphql.document.hash",
+ "Value": "md5:ef859b98c9d8c17038c8fd9aeecdb1e2"
+ }
+ ],
+ "event": [
+ {
+ "Name": "AddedOperationPlanToCache",
+ "Tags": []
+ },
+ {
+ "Name": "AddedDocumentToCache",
+ "Tags": []
+ }
+ ],
+ "activities": [
+ {
+ "OperationName": "GraphQL Document Validation",
+ "DisplayName": "GraphQL Document Validation",
+ "Kind": "Internal",
+ "Status": "Ok",
+ "tags": [
+ {
+ "Key": "graphql.processing.type",
+ "Value": "validate"
+ },
+ {
+ "Key": "graphql.document.hash",
+ "Value": "md5:ef859b98c9d8c17038c8fd9aeecdb1e2"
+ }
+ ],
+ "event": []
+ },
+ {
+ "OperationName": "GraphQL Operation Planning",
+ "DisplayName": "GraphQL Operation Planning",
+ "Kind": "Internal",
+ "Status": "Ok",
+ "tags": [
+ {
+ "Key": "graphql.processing.type",
+ "Value": "plan"
+ },
+ {
+ "Key": "graphql.document.hash",
+ "Value": "md5:ef859b98c9d8c17038c8fd9aeecdb1e2"
+ },
+ {
+ "Key": "graphql.operation.type",
+ "Value": "subscription"
+ },
+ {
+ "Key": "graphql.operation.name",
+ "Value": "OnMessageSubscription"
+ }
+ ],
+ "event": []
+ },
+ {
+ "OperationName": "GraphQL Operation Execution",
+ "DisplayName": "GraphQL Operation Execution",
+ "Kind": "Internal",
+ "Status": "Ok",
+ "tags": [
+ {
+ "Key": "graphql.processing.type",
+ "Value": "execute"
+ },
+ {
+ "Key": "graphql.operation.type",
+ "Value": "subscription"
+ },
+ {
+ "Key": "graphql.operation.name",
+ "Value": "OnMessageSubscription"
+ },
+ {
+ "Key": "graphql.document.hash",
+ "Value": "md5:ef859b98c9d8c17038c8fd9aeecdb1e2"
+ }
+ ],
+ "event": [],
+ "activities": [
+ {
+ "OperationName": "GraphQL Step Execution",
+ "DisplayName": "GraphQL Step Execution",
+ "Kind": "Internal",
+ "Status": "Ok",
+ "tags": [
+ {
+ "Key": "graphql.processing.type",
+ "Value": "step_execute"
+ },
+ {
+ "Key": "graphql.operation.type",
+ "Value": "subscription"
+ },
+ {
+ "Key": "graphql.operation.name",
+ "Value": "OnMessageSubscription"
+ },
+ {
+ "Key": "graphql.document.hash",
+ "Value": "md5:ef859b98c9d8c17038c8fd9aeecdb1e2"
+ },
+ {
+ "Key": "graphql.operation.step.id",
+ "Value": "1"
+ },
+ {
+ "Key": "graphql.operation.step.kind",
+ "Value": "operation"
+ },
+ {
+ "Key": "graphql.operation.step.plan.id",
+ "Value": "bb94346d204b2f13a21dcba79a06b47065115db28b9ba2c04bce67382b50cef0"
+ },
+ {
+ "Key": "graphql.source_schema.name",
+ "Value": "a"
+ },
+ {
+ "Key": "graphql.source_schema.operation.name",
+ "Value": "OnMessageSubscription_ef859b98_1"
+ },
+ {
+ "Key": "graphql.source_schema.operation.hash",
+ "Value": "sha256:f9abddbb651ad8e118793eb7c4a7c8db7fe43cae95faba710a0ff35b26b17136"
+ }
+ ],
+ "event": []
+ }
+ ]
+ },
+ {
+ "OperationName": "GraphQL Subscription Event",
+ "DisplayName": "GraphQL Subscription Event",
+ "Kind": "Internal",
+ "Status": "Ok",
+ "tags": [
+ {
+ "Key": "graphql.processing.type",
+ "Value": "subscription_event"
+ },
+ {
+ "Key": "graphql.operation.type",
+ "Value": "subscription"
+ },
+ {
+ "Key": "graphql.operation.name",
+ "Value": "OnMessageSubscription"
+ },
+ {
+ "Key": "graphql.document.hash",
+ "Value": "md5:ef859b98c9d8c17038c8fd9aeecdb1e2"
+ },
+ {
+ "Key": "graphql.subscription.id",
+ "Value": ""
+ }
+ ],
+ "event": []
+ }
+ ]
+ },
+ {
+ "OperationName": "System.Net.Http.HttpRequestOut",
+ "DisplayName": "System.Net.Http.HttpRequestOut",
+ "Kind": "Client",
+ "Status": "Unset",
+ "tags": [
+ {
+ "Key": "http.request.method",
+ "Value": "POST"
+ },
+ {
+ "Key": "server.address",
+ "Value": "localhost"
+ },
+ {
+ "Key": "server.port",
+ "Value": 5000
+ },
+ {
+ "Key": "url.full",
+ "Value": "http://localhost:5000/graphql"
+ },
+ {
+ "Key": "http.response.status_code",
+ "Value": 200
+ }
+ ],
+ "event": [],
+ "activities": [
+ {
+ "OperationName": "Microsoft.AspNetCore.Hosting.HttpRequestIn",
+ "DisplayName": "POST /graphql/{**slug}",
+ "Kind": "Server",
+ "Status": "Unset",
+ "tags": [
+ {
+ "Key": "server.address",
+ "Value": "localhost"
+ },
+ {
+ "Key": "server.port",
+ "Value": 5000
+ },
+ {
+ "Key": "http.request.method",
+ "Value": "POST"
+ },
+ {
+ "Key": "url.scheme",
+ "Value": "http"
+ },
+ {
+ "Key": "url.path",
+ "Value": "/graphql"
+ },
+ {
+ "Key": "network.protocol.version",
+ "Value": "1.1"
+ },
+ {
+ "Key": "http.response.status_code",
+ "Value": 200
+ },
+ {
+ "Key": "http.route",
+ "Value": "/graphql/{**slug}"
+ }
+ ],
+ "event": []
+ }
+ ]
+ }
+ ]
+ }
+ ]
+}
diff --git a/website/content/docs/fusion/migration/migrate-from-16-6-to-16-7.md b/website/content/docs/fusion/migration/migrate-from-16-6-to-16-7.md
index 892365ee9a0..7ed684805ff 100644
--- a/website/content/docs/fusion/migration/migrate-from-16-6-to-16-7.md
+++ b/website/content/docs/fusion/migration/migrate-from-16-6-to-16-7.md
@@ -1,10 +1,30 @@
---
title: Migrate Hot Chocolate Fusion from 16.6 to 16.7
-description: "Migration guide for Hot Chocolate Fusion v16.6 to v16.7: replace raw condition masks with ConditionFlags and configure wide operation limits."
+description: "Migration guide for Hot Chocolate Fusion v16.6 to v16.7: implement the new WebSocket connection initialization diagnostic event, replace raw condition masks with ConditionFlags, and configure wide operation limits."
---
Update every Hot Chocolate Fusion package in the application to version 16.7 before applying these changes.
+# Breaking changes
+
+Things that have been removed or had a change in behavior that may cause your code not to compile or lead to unexpected behavior at runtime if not addressed.
+
+## IServerDiagnosticEvents gained a WebSocket connection initialization event
+
+`IServerDiagnosticEvents` has a new `WebSocketConnectionInitialized` member. The gateway raises it once per WebSocket session, for both the `graphql-transport-ws` and the legacy `graphql-ws` protocol, after the client's connection initialization message has been accepted.
+
+Listeners that derive from `ServerDiagnosticEventListener` need no change, because the base class provides a virtual no-op. Types that implement `IServerDiagnosticEvents` directly have to implement the new member:
+
+```csharp
+public void WebSocketConnectionInitialized(
+ ISocketSession session,
+ IOperationMessagePayload connectionInitMessage)
+{
+}
+```
+
+The payload of `connectionInitMessage` is only valid for the duration of the call. Read out any value that is needed later inside the callback, for example onto `ISocketConnection.Features`.
+
# Deprecations
## Raw condition masks replaced by ConditionFlags
diff --git a/website/content/docs/hotchocolate/migrating/migrate-from-16-6-to-16-7.md b/website/content/docs/hotchocolate/migrating/migrate-from-16-6-to-16-7.md
index 0e5694a34cc..f8a3ae84420 100644
--- a/website/content/docs/hotchocolate/migrating/migrate-from-16-6-to-16-7.md
+++ b/website/content/docs/hotchocolate/migrating/migrate-from-16-6-to-16-7.md
@@ -1,11 +1,31 @@
---
title: Migrate Hot Chocolate from 16.6 to 16.7
metaTitle: "Hot Chocolate 16.7 Migration Guide"
-description: "Migration guide for Hot Chocolate v16.6 to v16.7: replace raw condition masks with ConditionFlags and configure wide operation limits."
+description: "Migration guide for Hot Chocolate v16.6 to v16.7: implement the new WebSocket connection initialization diagnostic event, replace raw condition masks with ConditionFlags, and configure wide operation limits."
---
Update every `HotChocolate.*` package in the application to version 16.7 before applying these changes.
+# Breaking changes
+
+Things that have been removed or had a change in behavior that may cause your code not to compile or lead to unexpected behavior at runtime if not addressed.
+
+## IServerDiagnosticEvents gained a WebSocket connection initialization event
+
+`IServerDiagnosticEvents` has a new `WebSocketConnectionInitialized` member. It is raised once per WebSocket session, for both the `graphql-transport-ws` and the legacy `graphql-ws` protocol, after the client's connection initialization message has been accepted.
+
+Listeners that derive from `ServerDiagnosticEventListener` need no change, because the base class provides a virtual no-op. Types that implement `IServerDiagnosticEvents` directly have to implement the new member:
+
+```csharp
+public void WebSocketConnectionInitialized(
+ ISocketSession session,
+ IOperationMessagePayload connectionInitMessage)
+{
+}
+```
+
+The payload of `connectionInitMessage` is only valid for the duration of the call. Read out any value that is needed later inside the callback, for example onto `ISocketConnection.Features`.
+
# Deprecations
## Raw condition masks replaced by ConditionFlags