Flow Studio
SignalR Log Channel
Logs are pushed from the server to clients via the NodeLogEmitted hub method on ExecutionHub. Clients subscribe to an execution-specific SignalR group. The server sends batches of LogEntry objects to reduce round-trips.
ExecutionHub — Server Side
// ExecutionHub.cs
[Authorize]
public class ExecutionHub : Hub
{
// Client joins the execution group to receive events for one execution
public async Task JoinExecution(string executionId)
{
await Groups.AddToGroupAsync(Context.ConnectionId, $"exec:{executionId}");
}
public async Task LeaveExecution(string executionId)
{
await Groups.RemoveFromGroupAsync(Context.ConnectionId, $"exec:{executionId}");
}
}
// The buffer flushes by calling:
await _hubContext.Clients
.Group($"exec:{executionId}")
.SendAsync("NodeLogEmitted", logEntries, cancellationToken);
NodeLogEmitted Payload Schema
// The hub method sends an array of LogEntry objects (batch):
interface NodeLogEmittedPayload {
entries: LogEntry[]; // 1..N entries per message (batched by buffer)
}
interface LogEntry {
logId : string; // GUID — for deduplication
nodeId : string;
level : 'Trace' | 'Debug' | 'Information' | 'Warning' | 'Error';
message : string;
timestamp : string; // ISO 8601
fields : Record<string, string>; // includes executionId, tenantId, traceId
traceId ?: string;
}
Client Connection
// useExecutionSignalR.ts
const connection = new HubConnectionBuilder()
.withUrl('/hubs/execution', {
accessTokenFactory: () => authStore.getState().accessToken
})
.withAutomaticReconnect()
.build();
await connection.start();
// Join the group for this execution
await connection.invoke('JoinExecution', executionId);
// Listen for log batches
connection.on('NodeLogEmitted', (payload: NodeLogEmittedPayload) => {
payload.entries.forEach(entry =>
useExecutionStore.getState().appendLog(entry)
);
});
// On unmount: leave group
return () => {
connection.invoke('LeaveExecution', executionId);
connection.stop();
};
Group-scoped delivery: Only clients that have joined the
exec:{executionId} group receive NodeLogEmitted events for that execution. Other users watching different executions, or with no execution open, receive nothing. This is the tenant isolation boundary for real-time log streaming.