Introduction
In this tip, I show how you can use SignalR to transfer server-side log and trace messages to an HTML client side. This can help to create a centralized and easy log facility that is necessary to debug complex systems.
Background
In this tip, I also provide a class that will dump all HTTP interactions into Trace
, which then will be shown in our SignalR client in the browser. In the real world, you probably would like to filter to include only some requests. For example, WebAPI
calls from your SPA to the server.
To run the code, you will need Nuget packages required to build a SignalR application in ASP.NET. You can check the repositories file in the provided source to see what I have been using.
Using the Code
The code has these components:
- An HTML & JavaScript bit to show incoming messages from SignalR
- Codes to create SignalR Hub in the server-side
- A
TraceListener
to listen to all Trace
writes, which we add to Listeners in Global.asax
- A HTTP Module that puts all HTTP actions into
Trace
by decorating the stream in each HTTP request lifecycle
Now, let's look at the important bits of each component.
I have created a SignalrLog
JavaScript type which is defined like this (I removed the DOM manipulation so you can see what is happening here.
var SignalrLog = (function () {
function SignalrLog(element) {
this.Element = element;
this.Ul = document.createElement("ul");
this.Element[0].appendChild(this.Ul);
}
SignalrLog.prototype.Start = function () {
var parent = this;
var hub = $.connection.serviceSpyHub;
var ul = this.Ul;
var e = this.Element;
hub.client.clientSideCall = function (msg) {
};
$.connection.hub.start()
.done(function () {
})
.fail(function (e) {
});
};
return SignalrLog;
})();
Note how we set the hub.client.clientSideCall
. Here, clientSideCall
is the method that we will call from server side.
Now, in our _Layout.cshtml, after making sure I have reference to my JavaScript file, I add this:
<script>
$(function(){
var log1 =new SignalrLog($("#EmbededLog"));
log1.Start();
});
</script>
which is going to nominate a div
with id of EmbededLog
for our logs. You can have that div
as a popup or in an iFrame.
Then we do our serverside code. The code is a quite standard SignalR setup that you can find in any SignalR tutorial. This is the Hub
definition. As you probably know, the ServerSideCall
and ClientSideCall
methods are going to be exposed in camel-case in JavaScript.
public class ServiceSpyHub : Hub
{
public ServiceSpyHub()
{
}
public void ServerSideCall(string msg)
{
var user = HttpContext.Current.User.Identity;
var machine = Environment.MachineName;
Clients.All.ClientSideCall("("+machine+"):"+ msg);
}
}
Then I have created a TraceListener
that invokes the Hub
's method.
public class MemoryTraceListener : System.Diagnostics.TraceListener
{
public MemoryTraceListener()
{
}
public override void Write(string message)
{
WriteToHub(message);
}
public override void WriteLine(string message)
{
WriteToHub(message);
}
private void WriteToHub(string msg)
{
var hubContext = GlobalHost.ConnectionManager.GetHubContext<ServiceSpyHub>();
hubContext.Clients.All.ClientSideCall("( Trace):" + msg);
}
}
I have added this listener to the list of listeners in Global.asax code like this:
protected void Application_Start()
{
System.Diagnostics.Trace.Listeners.Add(new MemoryTraceListener());
This is actually all you need to start seeing your Trace
messages in client side. But to make this really useful, you should start putting useful information into your Trace
. For example, you may want to know what REST API calls your SAP does to the server and see them real-time in client-side. You can achieve that by creating a custom HttpModule
that looks into HTTP requests getting processed and collects the information for you and puts them in Trace
. In the attached code, I am putting every Response
text into the Trace
using my Module
implementation.