The Orchard CMS Application Insights module and DotNest case study
Application Insights has an active ecosystem with our partners developing integrations using our Open Source SDKs and public endpoints. We recently had Lombiq (one of our partners) integrate Application Insights into Orchard CMS and a multi-tenant public SaaS version of the same.
Here is a case study of their experience in their own words, by Zoltán Lehóczky, co-founder of Lombiq, Orchard CMS developer.
We have integrated Application Insights into a multi-tenant service in such a way that each tenant gets its own separate performance and usage monitoring. At the same time, we, the providers of the service, get overall monitoring of the whole platform. The code we wrote is open-source.
Adding Application Insights telemetry to an ASP.NET web app is easy with just a few clicks in Visual Studio. But the complexity of monitoring needs increases when the web app is a rich-featured multi-tenant content management system (CMS) that can be self-hosted or offered as CMS as a Service. So you need to build an integration that feels native to the platform by extending the Application Insights libraries. The aim is to give people the great analytical and monitoring capabilities of Application Insights, specific to the CMS platform, that enables as easily. This blog post explains some techniques and practices that are used in the Orchard CMS Application Insights module.
We at Lombiq Technologies are a .NET software services company from Hungary. We have international clients like Microsoft itself. Orchard, an open source ASP.NET MVC CMS started and still supported by Microsoft, is what we mainly work with, having also built the public multi-tenant Orchard as a Service called DotNest. Being a long-time Azure user we learned about Application Insights when it was still very early in development and started to build an easy to use Orchard integration that can be utilized on DotNest. So, what are our experiences worth sharing?
The Application Insights Orchard module we developed is open source, so make sure to check it out on GitHub if you want to see more code! Everything discussed here is implemented there.
Using Application Insights in a modular multi-tenant CMS
Application Insights, as it is delivered “out of the box”, works easily for single-tenant applications, where it’s no issue that you need some root-level XML config files. However, if your code is a module that will be integrated into other people’s applications, like our Orchard CMS, then you want your code, including all the monitoring extensions, to be self-contained. We don’t want our clients to be exposed to configuration files at the application level. In short, we need to integrate Application Insights into our code to make a single, independently distributable MVC project. The distributed form might be a source repository or a zip file.
To package Application Insights into our code, we must:
- Move Application Insights configuration to code—that is, do the same in C# that would normally be done in the XML config file.
- Manage the lifetime of telemetry modules in code. Each module handles a different type of telemetry—requests, exceptions, dependencies, and so on. Normally, these modules are instantiated when the .config file is read, and have parameters set in the config file. (Learn more. Our code).
- Instead of relying on static singletons, manage
TelemetryClient
andTelemetryConfiguration
objects in a custom way. This allows the telemetry for separate tenants to be kept separate. (See for example this code) - Orchard uses log4net for logging. We can collect this data in Application Insights, but again we need to write code to configure ApplicationInsightsAppender instead of relying on the config files. (Code)
All good, so now we got rid of app-level XML configs. But what if we have multiple tenants in the same app? The default setup of Application Insights only has single-tenancy in mind, so we need to dig a bit deeper. (For the purpose of this post “tenant” will mean a sub-application, a component within the application that maintains a high level of data isolation to other tenants)
- We can’t utilize the
HttpModule
that ships with Application Insights for request tracking, since that would require changes to a global config file (theWeb.config
) and wouldn’t allow us to easily switch request tracking on or off per tenant. Time to implement an Owin middleware and do request tracking with some custom code! Such middlewares can be registered entirely from code and can be enabled on a per tenant basis. - Since request tracking is done in our own way we also need to add an operation ID from code for each request. In Application Insights, Operation ID is used to correlate telemetry that occur as part of servicing the same request.
- Let’s also add an
ITelemetryInitializer
that will add which tenant a piece of telemetry originates from. (Learn more. Code)If everything is done we’ll end up with an Application Insights plugin that can be enabled and disabled from the Orchard admin site, separately for each tenant:
Adding some Orchardyness
So far so good, but the result still needs some more work to really be part of the CMS: There’s no place to configure it yet!
In Orchard, the site settings can be used for that. It’s easy to add some configuration options that admins can change from the web UI; these settings are on the level of a tenant. We’ve added a settings screen like this:
Note that calls to dependencies, like SQL queries, storage operations or HTTP requests to remote resources are tracked. However, since this generates a lot of data it’s possible to switch dependency tracking off.
Do note that some settings are either not possible to configure on a tenant level (and thus need to be app level), or it doesn’t make sense to do so: e.g. since log entries might not be tied to a tenant (but rather to the whole application) those are only available for app-wide collection in our module (nevertheless an additional tenant-level log collection would be possible). What you see is the full config that’s only available on the “main” tenant.
Furthermore, we added several extension points for developers to hook into. So if you’re a fellow Orchard developer you can override the Application Insights configuration, add your own context to telemetry data or utilize event handlers (and Orchard-style events for that matter).
Making Application Insights available in a public SaaS
What we’ve seen until now was all the fundamental functionality that’s needed for a self-contained component monitored by Application Insights. However, in DotNest, where everyone can sign up, we need two distinct layers of monitoring by Application Insights:
- We want detailed telemetry about the whole application, for our own use.
- Users of DotNest tenants want to separately configure Application Insights and collect telemetry that they’re allowed to see, just for their tenants.
Users of DotNest thus don’t even see the original Application Insights configuration options, as those are managed on the level of the whole platform. However, they get another site settings screen where they can configure their own instrumentation key:
When such a key is provided, then another, second Application Insights configuration will be created on the tenant and used together with the platform-level one, providing server-side and client-side request tracking and error reporting. Thus, while we at Lombiq, the owners of the service see all data under our own Application Insights account, each user will also be able to see just their own tenant’s data in the Azure Portal as usual.
This tenant configuration is created and managed in the same way as the original one, from code.
Seeing the results
Once all of this is set up, we want to see what kind of data we gathered, and this happens as usual in the Azure Portal.
Live Metrics Stream
Live Metrics Stream provides real time monitoring. We included the appropriate telemetry processor in our initialization chain. It includes system metrics like memory and CPU usage as well, and as of recently you don’t even need to install the Application Insights Extensions for an App Service to see these:
Tracing errors
But what if something goes wrong? Log entries are visible as Traces (standard log entries) or Exceptions (when exceptions are caught and logged) in the Azure Portal:
But remember that we’ve implemented an operation ID? The great thing once we have that is that events, exceptions, request, any data points are not just visible alone, but in context: Using the operation ID, Application Insights will be able to correlate telemetry data with other data points, for example to tell you the request in which the exception happened.
This makes it easier to find out how you can reproduce a problem that just happened in production.
Wrapping it up
All in all, if you need more than just to add Application Insights to your application with a single configuration, without the need to redistribute the integration, then you need to dig into the Application Insights libraries’ API. Now with the libraries being open source this is not much of an issue and you can fully configure and utilize them just by writing C#. With the Azure Application Insights Orchard module you even have a documented example of doing it.
So, don’t be afraid and code some awesome Application Insights integration! And if you just want to play with fancy graphs on the Azure Portal you can quickly create a free DotNest site and start gathering some data right away!