diff --git a/src/SharedKernel/Extensions/SignalRExtensions.cs b/src/SharedKernel/Extensions/SignalRExtensions.cs index b1ecdaf..1d39c29 100644 --- a/src/SharedKernel/Extensions/SignalRExtensions.cs +++ b/src/SharedKernel/Extensions/SignalRExtensions.cs @@ -2,36 +2,41 @@ using Microsoft.AspNetCore.SignalR; using Microsoft.Extensions.DependencyInjection; using ResponseCrafter.ExceptionHandlers.SignalR; +using SharedKernel.Logging; using StackExchange.Redis; namespace SharedKernel.Extensions; public static class SignalRExtensions { - public static WebApplicationBuilder AddSignalR(this WebApplicationBuilder builder) { - builder - .Services - .AddSignalR(o => o.AddFilter()) - .AddMessagePackProtocol(); - + builder.AddSignalRWithFiltersAndMessagePack(); return builder; } - - public static WebApplicationBuilder AddDistributedSignalR(this WebApplicationBuilder builder, string redisChannelName) + + public static WebApplicationBuilder AddDistributedSignalR(this WebApplicationBuilder builder, + string redisChannelName) { - builder - .Services - .AddSignalR(o => o.AddFilter()) - .AddMessagePackProtocol() - .AddStackExchangeRedis(builder.Configuration.GetRedisUrl(), - options => - { - options.Configuration.ChannelPrefix = RedisChannel.Literal("FinHub:SignalR:"); - }); + builder.AddSignalRWithFiltersAndMessagePack() + .AddStackExchangeRedis(builder.Configuration.GetRedisUrl(), + options => + { + options.Configuration.ChannelPrefix = RedisChannel.Literal("FinHub:SignalR:"); + }); return builder; } + + private static ISignalRServerBuilder AddSignalRWithFiltersAndMessagePack(this WebApplicationBuilder builder) + { + return builder.Services + .AddSignalR(o => + { + o.AddFilter(); + o.AddFilter(); + }) + .AddMessagePackProtocol(); + } } \ No newline at end of file diff --git a/src/SharedKernel/Logging/SignalRLoggingHubFilter.cs b/src/SharedKernel/Logging/SignalRLoggingHubFilter.cs new file mode 100644 index 0000000..8cd3e1f --- /dev/null +++ b/src/SharedKernel/Logging/SignalRLoggingHubFilter.cs @@ -0,0 +1,78 @@ +using System.Diagnostics; +using System.Text.Json; +using Microsoft.AspNetCore.SignalR; +using Microsoft.Extensions.Logging; + +namespace SharedKernel.Logging; + +internal sealed class SignalRLoggingHubFilter(ILogger logger) : IHubFilter +{ + public async ValueTask InvokeMethodAsync(HubInvocationContext invocationContext, + Func> next) + { + var start = Stopwatch.GetTimestamp(); + + // Basic context info + var hubName = invocationContext.Hub.GetType() + .Name; + var connectionId = invocationContext.Context.ConnectionId; + var userId = invocationContext.Context.UserIdentifier; + var methodName = invocationContext.HubMethodName; + + // Redact arguments + var serializedArgs = JsonSerializer.Serialize(invocationContext.HubMethodArguments); + var redactedArgs = RedactionHelper.ParseAndRedactJson(serializedArgs); + + object? result = null; + Exception? exception = null; + + try + { + // Invoke the actual hub method + result = await next(invocationContext); + } + catch (Exception ex) + { + exception = ex; + } + + var elapsedMs = Stopwatch.GetElapsedTime(start) + .TotalMilliseconds; + + if (exception is not null) + { + logger.LogError(exception, + "[SignalR] Hub {HubName}, ConnId {ConnectionId}, UserId {UserId} - Method {MethodName} threw an exception after {ElapsedMs}ms. " + + "Inbound Args: {Args}", + hubName, + connectionId, + userId, + methodName, + elapsedMs, + redactedArgs); + throw exception; + } + + // Redact return value, if any + var redactedResult = string.Empty; + if (result is not null) + { + var serializedResult = JsonSerializer.Serialize(result); + var redactedObj = RedactionHelper.ParseAndRedactJson(serializedResult); + redactedResult = JsonSerializer.Serialize(redactedObj); + } + + logger.LogInformation( + "[SignalR] Hub {HubName}, ConnId {ConnectionId}, UserId {UserId} - Method {MethodName} completed in {ElapsedMs}ms. " + + "Inbound Args: {Args}, Outbound Result: {Result}", + hubName, + connectionId, + userId, + methodName, + elapsedMs, + redactedArgs, + redactedResult); + + return result; + } +} \ No newline at end of file diff --git a/src/SharedKernel/SharedKernel.csproj b/src/SharedKernel/SharedKernel.csproj index 8deb8e8..e6a3629 100644 --- a/src/SharedKernel/SharedKernel.csproj +++ b/src/SharedKernel/SharedKernel.csproj @@ -8,13 +8,13 @@ Readme.md Pandatech MIT - 1.1.0 + 1.1.1 Pandatech.SharedKernel Pandatech Shared Kernel Library Pandatech, shared kernel, library, OpenAPI, Swagger, utilities, scalar Pandatech.SharedKernel provides centralized configurations, utilities, and extensions for ASP.NET Core projects. For more information refere to readme.md document. https://github.com/PandaTechAM/be-lib-sharedkernel - app.UseRequestResponseLogging() changed to app.UseRequestLogging() and added new HttpClientHandler loger. + Added signal r incomming and outgoing message logging