5

How can I get the full stack of an exception that's happening in an otherwise functioning Web application during SignalR connection setup?

Background

I'm part of a team maintaining a Web application with C# clients that uses an extremely basic SignalR setup (version 2.2) to effectively deliver push notifications about progress during long-running server processes. Like, out-of-the-box,

app.MapSignalR();
HubConnection connection = new HubConnection(_applicationConfiguration.ApiBaseUri + "/signalr");
await connection.Start();

basic. Some of our clients run on remoting services and periodically run into an issue where the other functions of the Web application work fine, but the client code that calls connection.Start() returns a 500 internal server error with no further information. They can address it by refreshing the remote connection but this is less than ideal, so I'm trying to get some information about where in the connection setup process this error is happening.

Problem

Following the information about setting up error handling for SignalR on MSDN, I've tried to simulate the problem by inserting the following pipeline module into the GlobalHost.HubPipeline:

public class HubErrorHandlingModule : HubPipelineModule
  {
    public override Func<IHub, Task> BuildConnect(Func<IHub, Task> connect)
    {
      throw new InvalidOperationException("Testing Connection Exceptions");
    }

    protected override void OnIncomingError(ExceptionContext exceptionContext, 
      IHubIncomingInvokerContext invokerContext)
    {
      // some logging happens here
      base.OnIncomingError(exceptionContext, invokerContext);
    }
  }

and it kind of works, in that I can see the exception get thrown in the pipeline code, and my test C# client is also seeing a 500 internal server error with no further information.

But it also kind of doesn't work, in that I've dropped in breakpoints and the OnIncomingError code is never hit. That sort of makes sense, since it's not code in any Hub method that's causing the exception, but I don't know where this exception is happening; it could be anywhere during the client call to connection.Start.

I've also tried passing in an alternate HubConfiguration with EnableDetailedErrors = true but that doesn't seem to improve anything.

It doesn't really matter where I get the full stack trace, since I control both the server and the client code, but in order to understand their problem I need to see the full trace somewhere.

What I've Tried And Why It Doesn't Work

app.MapSignalR(new HubConfiguration { EnableDetailedErrors = true });

I think this is meant to show detailed errors from Hub processing, not connection handshaking? Supposedly it's meant to send a message tagged as an error that might be traced by the connection even if it's never bubbled up to any consumer. Unfortunately...

var writer = new StreamWriter("C:\\Logs\\ClientLog.txt");
writer.AutoFlush = true;
connection.TraceLevel = TraceLevels.All;
connection.TraceWriter = writer;

This does trace successful communication to the SignalR backend, once I remove the deliberate pipeline error. But when I set it back up, all I see is a failed attempt to establish the connection and a 500 internal server error. No trace.

<system.diagnostics>
  <sharedListeners ... >
  <switches ...>
  <sources ...>
  <trace autoflush="true" />
</system.diagnostics>

Set up both after the MSDN trace details and this commentary on GitHub. Neither set of details works. As I play around by moving the pipeline exception to different pipeline events, I can sometimes see a stack trace show up in the SignalR.HubDispatcher source mentioned only in the GitHub details, but it happens when I throw the exception after the connection's been established and what arrives at the client side is a different error than just a 500 internal server error, so that's probably happening too late to be whatever's going wrong at the client installation.

Community
  • 1
  • 1
Glazius
  • 729
  • 7
  • 28
  • Hey Glazius, feel free to comment, if you need more help or have any queries related to the solution – Clint Mar 08 '20 at 08:32
  • I think I had this problem before. Quite sure the problem is from here `HubConnection connection = new HubConnection(_applicationConfiguration.ApiBaseUri + "/signalr");`. You need to put the SignalR.cs in the root path of your project. At least this was the problem for me. – Alvin Stefanus Mar 12 '20 at 03:41

2 Answers2

1

In my case I have to put the SignalR.cs in my root path.

enter image description here

Then in the view I include the script:

<script src="~/signalr/hubs"></script>

This is what my SignalR.cs looks like:

public class NotificationHub : Hub
{
    public void SendUpdateNotification(string message)
    {
        // message = "show" / "hide"
        if (message.Equals("show"))
            Config._MaintenanceMode = true;
        else
            Config._MaintenanceMode = false;

        // Call the broadcastUpdate method to update clients.
        Clients.All.broadcastUpdate(message);
    }
}
Alvin Stefanus
  • 1,873
  • 2
  • 22
  • 60
  • I don't understand what this has to do with anything. I'm using a C# client, not a web client - the application serves HTTP responses that mostly contain JSON. The SignalR part of it also works consistently on dev servers and only fails intermittently in the field. I don't understand how this helps me log a stack trace when connection.Start() fails. – Glazius Mar 12 '20 at 06:38
  • Probably the url is wrong here `HubConnection connection = new HubConnection(_applicationConfiguration.ApiBaseUri + "/signalr");`, or the server does not allow CORS connection. – Alvin Stefanus Mar 12 '20 at 08:27
  • Is the hub class name `signalr`? Try change "/signalr" to "/hubName" – Alvin Stefanus Mar 12 '20 at 09:20
  • I really don't understand what you're saying. I'm using SignalR 2 - are you talking about a different version? I have multiple typed hubs, which are all linked to the common endpoint through app.mapSignalR() and monitored through CreateHubProxy on the clients. The URL is correct; it functions in our dev environment and on most installations. It's only intermittently failing and all I want is a stack trace to get a better idea of why. – Glazius Mar 12 '20 at 18:29
-1

To handle errors that SignalR raises, you can add a handler for the error event on the connection object

connection.Error += ex => Console.WriteLine("SignalR error: {0}", ex.StackTrace);

To handle errors from method invocations, wrap the code in a try-catch block.

HubConnection connection = new HubConnection(_applicationConfiguration.ApiBaseUri + "/signalr");
try
{   
    await connection.Start(); 
}
catch (Exception ex)
{
    Console.WriteLine("Error " + ex);
}

To enable detailed error messages for troubleshooting purposes,

var hubConfiguration = new HubConfiguration();
hubConfiguration.EnableDetailedErrors = true;
App.MapSignalR(hubConfiguration);

In your code the hub pipeline module I do not see you are logging/printing the error

Console.WriteLine("Exception " + exceptionContext.Error.Message);
base.OnIncomingError(exceptionContext, invokerContext);

and now hook up the custom HubPipelineModule we've created, this is achieved in the startup class

public partial class Startup
{
    public void Configuration(IAppBuilder app)
    {
        GlobalHost.HubPipeline.AddModule(new HubErrorHandlingModule());

        app.MapSignalR();
    }
}

References:

SignalR Notify user about disconnections
SingalR How to Handle Errors
SignalR 500 Internal Server Error

Clint
  • 6,011
  • 1
  • 21
  • 28
  • In this example, where would I see the InvalidOperationException that was thrown by HubErrorHandlingModule? – Glazius Mar 08 '20 at 19:00
  • did you subscribe to the event handler `connection.Error+=` in the example or surround your `connection.Start()` within try-catch ? – Clint Mar 09 '20 at 07:14
  • connection.Error is not being fired. This makes sense; the web application is functional and in the field people have to connect to it to assemble the launch point for a long operation that makes the SignalR connection. A non-Aggregate Exception is being caught from connection.Start(). Its stack trace ends at connection.Start(). I cannot see the InvalidOperationException that is being thrown by the HubErrorHandlingModule. – Glazius Mar 09 '20 at 19:16
  • yea, for `Exception ex` instead of printing only the stacktrace, did you try printing the whole object, like `Console.WriteLine("Error " + ex);` – Clint Mar 13 '20 at 13:26