ElmahR and Appharbor: plugins and structure review

Tagged: c# elmah signalr elmahr appharbor

Last time I've been already mentioning the new Appharbor integration plugin I've been working on, but there's more to tell about it, mainly for two reasons: the plugin system deserves some more details, and along with the Appharbor plugin I completed a quite deep restructuring of the whole project, which actually makes much easier to start from a repository clone and build up a fully functional and customizable ElmahR installation with just a few clicks. Nothing is perfect, and actually these advantages come with a little more maintenance burden on my side, but so far I'm happy with it and when I'll have some more time I'll work on it. But first let's see more about the finished Appharbor plugin.

Appharbor build events integration

When you add a source application inside a dashboard, you know that you will receive there any unhandled exception occurring on the monitored application in real-time. In a context where applications are monitored for errors, probably you will have someone taking care of fixing them, and when fixed a new application version will be deployed. People monitoring the applications must be very interested in knowing when the mentioned deploys occur, and what this plugin makes possible is to have live notifications about the builds occurring if the hosting infrastructure is from Appharbor. The plugin takes advantage of the Appharbor HTTP API, and it's basically creating an endpoint where build events can be sent through HTTP POSTs by any service hook you may configure inside your Appharbor control panel. When those are received, a new SignalR hub packs and routes them to the dashboard, which the plugin enriches with some new UI elements where the messages end up stacking up, as for the error messages.

But how a plugin works?

Let's see how a plugin (which by the way is a new .NET class library, no surprise here) in the context of ElmahR project is composed by up to 5 components:

  • a server-side endpoint: a plugin might set up an endpoint where external system would send messages, such an endpoint should normally be built as an http handler where to POST data
  • a server-side hub: if the plugin is not about errors, it might need to deliver real-time communication events about different type of messages, therefore a new type of SignalR hub might be necessary; this piece is actually very easy because SignalR infrastructure already does all the discovery job, so adding a Hub-derived type inside the plugin assembly will do the trick
  • client-side logic: the plugin might need some javascript code to do its job on the client, and this will be mandatory if you have an additional hub sending stuff down to the client
  • client-side rendering: ElmahR is a visual dashboard, so quite likely a plugin will need to emit appropriate HTML, which could be created with whatever DOM manipulation strategy you like, but probably the easiest way would be to leverage the already integrated Knockoutjs support for this
  • client-side resources: images, css... those will be needed too, right?

Several of the just described pieces are straightforward, but there is one of them which is actually tricky: if you need to integrate new client-side visual widgets or processing logic, you need to integrate them inside a framework which must be totally unaware of the added plugin. That's obvious, a plugin requiring some manual intervention on the existing code or UI in order to be used is not really a plugin, but that actually poses a small challenge, because you'll need a way to add those pieces at run time, which also mean at the right time. Back to the Appharbor plugin, I needed to add a new messages counter for each enabled application, and also a container where to stack received build messages. It's similar to what happens for errors, but these are not error messages, so they have ad hoc data and visual representations. ElmahR is a Single Page Application, and that page is there unaware of plugins, how can we enrich it with new UI widgets? Believe it or not, it's again SignalR coming to rescue. Here you have the main steps:

  1. I defined a few points inside default.cshtml where I might want to drop UI widgets, and I just put there divs with well-known ids
  2. when a plugin wants to fill one of more of those extension points with its stuff, all it has to do is put cshtml extensors (that's how I call them now) files inside App_Data folder of the target dashboard, with a well-known subfolders structure (which I'll document as soon as possible); extensor are mainly pieces of Razor-enabled html, but thanks for some naming conventions in place they can also be blocks of javascript or css
  3. when default.cshtml kicks in on the browser, it first does a call to a SignalR endpoint called commands, which is bound to a persistent connection that looks for extensors, compiles them through RazorEngine, caches them, and sends them back to the client
  4. when the extensors are received, they are dynamically added to the DOM through jQuery
  5. at this point the normal lifecycle starts, the SignalR hubs are initialized and Knockoutjs bindings are applied

Why using a persistent connection? As far as I know (and I might not know the whole story), SignalR initializes all the hubs at the same time with the call to the start() javascript method, and at that moment all the javascript callbacks must have been already added to the client side hub counterparts, so that SignalR machinery can register them. This means that a plugin must have a chance to add its javascript pieces before hubs start. The only way to use SignalR before hubs are started is using a persistent connection, which is not related to hubs infrastructure and can be used beforehand. Sequencing asynchroous client-side callbacks in an appropriate way, I've been able to guarantee that extensors get added to the DOM before hubs kick in, and to ensure that Knockoutjs bindings are applied as the last step. This last detail is also important, because it allows us to enrich our view models before Knockoutjs starts running, therefore plugins can take full advantage of the MVVM infrastructure in place.

This is been a very interesting pieces to build, and I could say more about it, but there one more piece that deserves more details now.

A new structure for the project

The plugins infrastructure brings several advantages, but in order for it to be effective the host must be of course unaware of any extension. This brought to my attention a problem that was already there since the beginning of the project, but whose importance became relevant just after these latest additions. ElmahR solution was made of a few libraries and a web application, which was setup to be up and running after a build with all the features enabled, and that was the way I arranged the solution because I wanted an easy and seamlessly way to deploy it to Appharbor, but this organization was also bringing some confusion to any people wanting to try themselves to setup an ElmahR dashboard for their infrastructure. The demo application was playing both source and dashboard roles, and despite to some documentation I wrote I had the feeling that this was not enough clear. The plugins made this even worse, and a reorganization became necessary. Now the project contains 3 web applications:

  • ElmahR: this is a dashboard with minimal configuration, ready to use and with no unnecessary configuration bits, hardcoded plugins or fake parts
  • ElmahR.SampleSource: this is a minimal web application which is there just to show how to raise errors and send them to a dashboard; it is already configured to talk to ElmahR dashboard just mentioned, making very easy to test things with no effort
  • ElmahR.Demo: this is a more complex setup, it's the dashboard hosted on Appharbor which is able to play as a set of sources and as an aggregating dashboard; its dependencies are declared directly inside the csproj file

When I say project, I do not mean a C# project, or a web application project, but I mean the whole ElmahR work in a more general sense. In such a way, the project is composed by 3 web apps, a few libraries and 2 solutions, one called ElmahR containing all the other pieces, and one called Appharbor which is the one the gets deployed on Appharbor infrastructure thanks to some naming conventions in place, and which contains only one web application, ElmahR.Demo. The project also contains all the bits necessary to build 2 Nuget packages, one for the ElmahR.Elmah assembly which exposed the ErrorPostModule, and one for ElmahR.Appharbor. Both can be found in the Nuget Gallery.

Conclusions

In the next weeks I'll try to write more formal documentation about all these things, for now what I want to say to close this long post is that it should be much easier for you to play with ElmahR code, and more straightforward to hack a repository clone for your need in your real infrastructure, and maybe to try to contribute back with interesting stuff. Just try and let me know :)

Add a Comment