This article has been originally written several months ago, but it's been substantially reviewed and integrated after ElmahR's 1.0.0 release.
Introduction
I've been listening and reading about SignalR since the beginning of the project, at the same time I'm an ELMAH user since a while and I have had the pleasure to share my working hours and my lunches with its author. At some point I had this idea to join these two libraries to offer real time and interactive experience to the logging capabilities that ELMAH offers, with a special focus on the fact that a dashboard should aggregate multiple applications's error feed in one place. ELMAH + SignalR = ElmahR.
Background
ElmahR is a web dashboard where you can aggregate several monitored applications; adding them to ElmahR configuration will enable them to post error events, which will show themselves on all the connected client dashboards in real-time. ElmahR is about error logging in real-time, and to do that it's based on ELMAH for the error logging part and on SignalR for the real-time notification piece.
The integration with ELMAH is not based on binary dependencies, but on data exchange. ELMAH already defines a set of fields and collections which describe an error, and ElmahR borrows this data structure and builds its logics on top of it. This approach makes ElmahR virtually independent from ELMAH: whoever is able to craft an error description matching the data format from ELMAH can send error events to ElmahR and have them published. It has not to be an ASP.NET application, it could be a desktop client, a mobile app, it could even be developed on a non-.NET platform, as long as it can post data over HTTP. Nevertheless the most convenient way to use ElmahR is to enable ELMAH on the ASP.NET web applications you have to monitor, and configure them to post errors to ElmahR, this way you'll leverage all the great logging features that ELMAH already has. To do that you will need the new ErrorPostModule, which I wrote myself and is not part of official ELMAH but it belongs to the ElmahR.Elmah component of ElmahR.
SignalR is a project which allows a set of clients to talk to each other in real-time through a server, but the most interesting part is the abstraction from the network details that SignalR is capable to deliver. There are several clients for different platforms, but the first and, at least in our case, most interesting one is the JavaScript client, which can be used in web browsers, with a high level of compatibility across different ones, transforming them in real-time messaging environments. Under the hood SignalR is capable to switch among several communication techniques, from WebSockets to long polling just to name a couple. You can read more about it on its project web site.
Hooking ELMAH
Let's suppose you already have ELMAH correctly configured on the application you want to monitor, all you have to do to connect it to an instance of a dashboard is to configure ErrorPostModule appropriately:
="1.0" ="utf-8"
<configuration>
<configSections>
<sectionGroup name="elmah">
<section name="errorLog" requirePermission="false" type="Elmah.ErrorLogSectionHandler, Elmah" />
<section name="errorPost" requirePermission="false" type="Elmah.ErrorLogSectionHandler, Elmah" />
</sectionGroup>
</configSections>
<elmah>
<errorLog type="Elmah.MemoryErrorLog, Elmah" />
<errorPost targetUrl="http://dashboardUrl/posterror.axd"
sourceId="SomeUniqueCode" />
</elmah>
<system.webServer>
<validation validateIntegratedModeConfiguration="false" />
<modules runAllManagedModulesForAllRequests="true">
<add name="ErrorLog" type="Elmah.ErrorLogModule, Elmah" />
<add name="ErrorPost" type="ElmahR.Elmah.ErrorPostModule, ElmahR.Elmah"/>
</modules>
</system.webServer>
<system.web>
<compilation targetFramework="4.0" />
</system.web>
</configuration>
Just as simple as that. There are 2 relevant parts:
- where the
errorPost
section is configured with the targetUrl
and the sourceId
: targetUrl
: address where posterror.axd
can be found; posterror.axd
is the ElmahR endpoint you have to target in order to HTTP POST errors, it understands the ELMAH data format and it's able to receive errors and treat them properlysourceId:
key you specify to identify the application, it will be used by ElmahR to match errors coming from this source to the right feed on the dashboard
- where the new http module is added in the standard ASP.NET section modules in
system.webServer
Please note that ErrorPostModule has no dependency at all on SignalR, it's just doing a plain old HTTP POST following some conventions about how to pack data. If you want you can configure things to have the same application running both the monitoring and monitored roles (although that's not recommended for several reasons, that particular scenario is handles with another internal module called ErrorTrapModule which avoids the HTTP POST). You will find more info about how to configure a source application, and about how to wire it to a specific dashboard, reading the docs available on the project site on Bitbucket.
Enter SignalR
Let's move to analyze the SignalR side of the project, that's what I did:
- I wrote a SignalR hub which easily enables the client-server communication
- I introduced a
.axd
handler where errors are posted and unpacked thanks to the same conventions used on the ELMAH side - I wrote a dashboard page where you can see the errors popping up in real time, thanks to the
JavaScript client-side features from SignalR
- I added some optional features to persist received errors on durable devices in order to enable the application to notify new clients about older exceptions
When an error arrives to ElmahR after the post steps described earlier, the server side uses SignalR to redistribute it to all the connected clients. When SignalR does its magic, errors arrive to the clients as JSON structures which still resemble the ELMAH data format. One of the goals I had here was to reach is a good level of separation of concerns and modularity, which would more easily enable a seamless integration of new modules. That's why the "low level" code which handles SignalR notifications is made "overridable" in order to enable you to hook more advanced client side functionalities and user experiences on top of "core" broadcast features. There are more areas where SignalR has been helpful, things like server-side filtering, smart history persistence for new or reconnecting clients, and even client side plugins delivery, are features made available to the dashboard thanks to great SignalR features like hubs, persistent connections or groups.
UI
As soon as ElmahR started growing as a project, improvements on the UI became necessary. The amount of information started getting big, and potential UI scalability problems became clear. Finally we had time to concentrate on the visual aspects of the dashboard, trying to come up with something more reactive, better at bringing the right information and at scaling along with the number of monitored applications. This last issue has been worrying us the most, it's not easy to accommodate the potentially quite big volume of info that ElmahR handles.
We let ourselves be inspired by Twitter web site user experience, and we took advantage of the Twitter bootstrap framework to build a responsive layout. It is a very interesting framework, it allowed us to greatly improve how the dashboard looks like, and now we have better support of different form factors and browsers.
Twitter bootstrap is quite simple, but it can also help deliver sophisticated UIs, and in order to enable that it can get pretty complex. We cannot really say we understood everything of it, but we also wanted to deliver soon, so we found a quite good and quick compromise analyzing a Bootstrap sample application and modifying it to our needs. This will probably be one of the areas where enhancements and updates will be produced in the near future.
Should you try the demo web site, you will notice that we have a responsive layout, normally composed of 3 columns which collapse to just 1 when the available width goes below a certain limit, as it happens on a phone.
The first columns is composed by summary boxes. The first box shows overall info like the total number of errors, the latest error type and the name of the application which raised it. The remaining boxes are similar, but specific for each monitored application. Those app-specific boxes are sortable by dragging and dropping them, they can be expanded to show the errors belonging to each of them, and the relative error feeds can be paused or resumed. From there you will also be able to trigger test exception on source applications thanks to the skull button, just to check if everything is wired correctly.
The second column is a global feed where all the errors are stacked up, the newest ones going on the top, this column let the user know about the latest errors occurred at a glance. Each error is expandable to see full details in a popup, which is disabled when the dashboard runs on small form factors (we're still looking for a better way to show details on those kind of devices).
The third column is intended to host additional pieces, so far we built 2 statistical 'plugins' which should be considered samples about how to build such extensions using ElmahR extensibility features.
Modularity
As already mentioned, ElmahR has some extensibility features, which come from the fact that the structure of the project has been made more and more modular during time. Now ElmahR dashboard is made of several pieces:
- ElmahR.Core: this is where most of the logic lives, from error handling to broadcasting, from default persistence to pluggability systems; a simplified real-time log stream is delivered at this level
- ElmahR.Modules.Dashboard: the "true" real time interactive dashboard is delivered by this module
- ElmahR.IoC.*: ElmahR has an embedded dependency injection mechanism thanks to TinyIoC, but it lets you override it in order to integrate with your favorite IoC picking an existing module or building your own (so far only NInject has a proper module available)
- ElmahR.Persistence.*: ElmahR's default persistence mechanism stores error in memory, but this subsystem comes from a module and can be replaced in case you need to store errors onto a durable system. We have modules for EntityFramework in order to support and relational database, but also for MongoDB in case you need to support it and, as for the IoC container, you could easily build your own modules to support other scenarios
This modularity has some positive impact on the client side
JavaScript files structure, which have to be as much independent as possible. All these factors allow anybody to extend ElmahR with new modules or plugins, adding both server and client side functionality without having to modify its internal code. Documentation about how to leverage this modularity in order to build new modules or plugins will be made available on the project website
Nuget packages
Thanks to the above mentioned modularity, it's been possible to find a ways to distribute both dashboard instances and the ElmahR.Elmah module through Nuget packages, which you will find here.
Latest additions
During the last few months more features have been added, the most notable being:
- Dashboard-related Nuget packages
- ElmahR.Elmah Nuget package
- Test exceptions launcher
- ElmahR as a source of errors for itself
- Low level logging
- Historical errors statistics and clean up
- MongoDB persistence
- Error post encryption
- Modular structure
- Plugins system
- CSS + JS bundling and minification
- IoC container support
Please refer to the project repository for more detailed documentation.
Project nature and goals
It's been real fun to work on it because it allowed me to try a lot of great stuff from out there, like:
- ELMAH, of course, writing a new module for it
- SignalR, really amazing library
- Knockout for doing MVVM in the browser, 'magical'
- Raphaël to build like graphs in the browser
- Underscore.js to 'compose' statistics client-side 'à la LINQ'
- jQuery, of course
- Mercurial on Google Code for the ELMAH Sandbox, and on Bitbucket for the official ElmahR home
- AppHarbor for hosting the live demo, it's a fantastic platform, deploying .NET apps is never been so easy!
- Trello for light 'project management'
- ...and many more!
I admit I'd like to have ElmahR evolve in a way that people would consider it a good sample app for libraries like ELMAH, SignalR or Knockout. I cannot say if this goal will ever be reached, if some of you guys out there want to help me in this effort (even simple advices or suggestions would be very welcomed) I'd be glad to try with you!
You can:
- follow the evolution of the project here
- check the code and read the docs here
- see a live demo here
- get the Nuget packages here
History
- 2013-03-XX: Version 1.0.0 finally available! Modularity and Nuget
packages among other improvements
- 2012-11-11: Version 0.9.7, error post encryption available
- 2012-10-24: Version 0.9.6, async persistence + MongoDB persistor
- 2012-09-20: Version 0.9.4, errors statistics and cleanup
- 2012-07-18: I gave a talk about ElmahR at aspConf, a recording of the talk is available on
Channel9
- 2012-07-15: Version 0.8.8, plugins system, plus various refinements.
- 2012-06-01: Version 0.7.2, persistence user preferences like application feeds status.
- 2012-05-21: Version 0.7.0, new persistence module writing on database, and retrieving historical errors from there.
- 2012-05-17: Version 0.6.5, enabling user to ask for older errors.
- 2012-05-16: Version 0.6.1, project now uses SignalR 0.5.
- 2012-05-04: Project is currently at version 0.6.