asp.net-mvcasp.net-mvc-4tracesystem.diagnosticstrace-listener

How should I make a custom Trace:Listener for displaying trace info on a web page?


I have an MVC4 app that has one or two long running requests. For this, first, iteration I will be leaving these as synchronous tasks. I would, however, like trace output from these tasks to be displayed on an optional 'Progress' window in my web app.

I see two candidate solutions, involving either a database trace listener, or a trace listener that rights to a 'trace collection' object stored in session. Then the 'Progress' window can poll these trace stores periodically using setInterval and an Ajax call.

This sounds too simple, and that is why I am asking how I should do this. Are there better ways, established patterns and techniques, or is it an exercise of little value?


Solution

  • You could use SignalR to send the trace messages from the server side directly to the "Progress window" in the browser.

    Dino Esposito wrote an article on the msdn doing something similar with a progress bar.

    This way you don´t need to poll the server from the progress window, and you don´t need to use the Session for storing the messages in between poll requests.


    Edited: Sample added

    SignalR has changed a bit since that article was written. Apart from registering the route in global.asax and include the scripts in the view, you will need the following piece of javascript that initializes the client side signalR proxy and adds a function to display the progress information:

    <script type="text/javascript">
        $(document).ready(function () {    
    
            // Create a proxy for the server endpoint            
            var progressHub = $.connection.progressHub;
            $.connection.hub.start();
    
            // Add a client-side callback to process any data
            // received from the server
            progressHub.client.addProgress = function (message) {
                $('#progress').append('<li>' + message + '</li>');
            };
    
        });
    </script>
    

    Let's say you have a button on that page that triggers the long-running process in the server. To make it simple, I will use the following jquery call to a method named RunProcess in the home controller. It will pass the id of the SignalR client so the server knows who needs to receive the progress data:

    $("#runProcess").click(function () {                
        var id = $.connection.hub.id;
        $.get("/Home/RunProcess/" + id);
    });
    

    On the server side, the following method of the HomeController will simulate the long running process. In order to notify the client using SignalR, you need to get the context using GlobalHost.ConnectionManager.GetHubContext<T>(). It will then need to call the method addProgress(message) that has been defined on the client proxy, but just for the client with the received Id:

    public void RunProcess(string id)
    {
        var progressHub = GlobalHost.ConnectionManager.GetHubContext<ProgressHub>();
    
        progressHub.Clients.Client(id).addProgress("25% completed");
        Thread.Sleep(2000);
    
        progressHub.Clients.Client(id).addProgress("50% completed");
        Thread.Sleep(3000);
    
        progressHub.Clients.Client(id).addProgress("75% completed");
        Thread.Sleep(2000);
    
        progressHub.Clients.Client(id).addProgress("Process Finished");
    }