Application
Insights in Sitefinity

Feb 27

If you are running your Sitefinity site on Azure you might as well be taking advantage of Application Insights to record and analyse performance. By default, Sitefinity logs everything to a text file. Annoyingly you can't read this because the file is locked by the app. You need to wait until it starts a new file the following day. You can push all these events to Elmah which is supported out of the box. But with Sitefinity's extension model you can write your own class to push out this information to your own logging source.

Application Insights is a good option to monitor your apps. I'm not going to go cover the service here but you can check out the marketing page to decide if, for yourself, it has the features that will aid you. Microsoft Application insights.
Assuming you like it, it is very easy to install. Right click on your project and select the option to Add Application Insights Telemetry. Then follow the promotes. You can get it all registered and going pretty quickly. (I am assuming you have an Azure subscription already).

Add Application Insights

Project Set Up

Once installed you have a set of nuget installed references along with a ApplicationInsights.config file in your project and some modules and handlers added to your web.config. No issues there.

You will find your Instrumentation Key in the ApplicationInsights.config file.

Another thing you will want to add to your project is some JavaScript to the master\_layout page. This script records and monitors client side events including script errors thrown. You can find this script by clicking the 'Quick Start' link in your Application Insights Azure portal blade.

Get App Ins code

You can also add JavaScript\C# code to raise and record events. Have a read of an example on this Application Insights documentation page.

Start your project and you'll get a notification from Visual Studio that information has been sent to your Application Insights account for you to view. Use Visual Studio to quickly browse to the Azure Application Insights... (OK I am just going to type AppIns now as I'm getting tired fingers). Use Visual Studio to quickly browse to the AppIns 'blade' online and you will see the data sent and recorded.

AppIns Chart

Pretty cool, pretty easy.

But after a while I soon realised that, for me, I didn't want all this dev telemetry data sent to the cloud. It slows my testing down and I only want my production data being recorded.

You can of course set up different AppIns repo's for your different environments if you like. Just ensure you have a good process to have the AppIns key correct in each environment so errors in QA are not showing up in Prod and you are not pushing code to prod that is actually throwing errors or you are looking in prod for errors that actually occurred in QA.

To stop this happening I added some code to my global.ascx file.

protected void Application_Start(object sender, EventArgs e)
{
    Boolean isAppInsLoggingEnabled = false;
    Boolean.TryParse(ConfigurationManager.AppSettings["EnableApplicationInsightLogging"], out isAppInsLoggingEnabled);
 
    if(!isAppInsLoggingEnabled)
    {
        TelemetryConfiguration.Active.DisableTelemetry = true;
    }
}

Pretty simple. I check my App Settings and disable AppIns telemetry according to the result. An alternative could be to check if you are in Debug mode. Depending on your environment and deployment process I am sure you can think of appropriate ways to deal with what you want. The main point is that you can disable it when you don't want it running.

Another thing you may want to look at is actually logging helpful developer info by using:
TelemetryConfiguration.Active.TelemetryChannel.DeveloperMode = true;

If you have, (and why would you not?), added the JavaScript code you also want to disable that. I have a simple but primitive process where I have the script commented out by default and as part of my deployment process to Prod I have it un-commented. If you have a better idea please tell me.

External Logging Project

AppIns is all set up and logging its stuff. Our next task is to push all the Sitefinity logging to AppIns.

You can access a project on Git Hub which allows you to push Sitefinity errors off to Raygun.io. You can find a walk through of this project in the Sitefinity documentation. You can take this project and easily replace the RayGun implementation with an AppIns implementation in the TraceListenerClient class.

public AppInsightsTraceListenerClient()
{
    Assembly appInsAssembly = AppInsightsTraceListenerClient.AppInsightsAssemblyFromAppDomain();
    if (appInsAssembly == null)
    {
        throw new Exception("Application Insights assembly is not added to the app domain of the web application!");
    }
 
    this.telementryClient = new TelemetryClient();
}
public void LogMessage(String message)
{
    if (SystemManager.CurrentHttpContext.Error != null)
    {
        telementryClient.TrackException(SystemManager.CurrentHttpContext.Error);
    }
    else
    {
        telementryClient.TrackEvent(message);
    }
}

You can now log any errors thrown by Sitefinity into your AppIns instance and have everything in one place.

One issue.

If in your Sitefinity code you use Log.Write("Record an event"); this data is logged to the trace.log file. Ideally I want this also to be logged to AppIns as an event. To do this you need to do two things. First change the code so that messages are logged as events.
Second, you need to alter the registration and add the Trace Listener to your list of policies. (Thanks to Sitefinity support for helping me sort out this one.)

ISitefinityLogCategoryConfigurator defaultConfigurator = ObjectFactory.Resolve<ISitefinityLogCategoryConfigurator>();
var logs = new ConfigurationPolicy[] { ConfigurationPolicy.Trace, ConfigurationPolicy.ErrorLog };
var appInsightsConfigurator = new AppInsightsConfigurator(defaultConfigurator, logs);
ObjectFactory.Container.RegisterInstance<ISitefinityLogCategoryConfigurator>(appInsightsConfigurator);

Comparing the RayGun implementation and mine you will sew in the LogMessage method that RayGun creates a new exception when there is just a string while I am calling TrackEvent. When there is no error the message is most likely an actual trace event that I have added in the above code. I would argue that it is wrong to turn it into an exception as in the RayGun implementation.

Hang On - What about Dev

Now I am back at that problem of pushing Dev issues up to my AppIns instance, which I don't want. So how do I record these errors and traces locally for my testing?

Back to my conditional switch. If in dev I write out to a local text file. (I know, kinda full circle back to the start).

public void LogMessage(String message)
{
    Boolean isAppInsLoggingEnabled = false;
    Boolean.TryParse(ConfigurationManager.AppSettings["EnableApplicationInsightLogging"], out isAppInsLoggingEnabled);
 
    if (SystemManager.CurrentHttpContext.Error != null)
    {
        if(isAppInsLoggingEnabled)
        {
            telementryClient.TrackException(SystemManager.CurrentHttpContext.Error);
        }
        else
        {
            LogToFile.Write(LogToFile.LogType.Error, SystemManager.CurrentHttpContext.Error.Message);
            LogToFile.Write(LogToFile.LogType.Error, SystemManager.CurrentHttpContext.Error.StackTrace);
        }
    }
    else
    {
        if(isAppInsLoggingEnabled)
        {
            telementryClient.TrackEvent(message);
        }
        else
        {
            LogToFile.Write(LogToFile.LogType.Trace, message);
        }
    }
}

Here is my simple Write to file method which I added to the project. You will see that it writes a new file each day.

public static void Write(LogType logType, String content)
{
    String logDirectory = @"~/App_Data/Sitefinity/Logs/ApplicationInsights/";
    if (!System.IO.Directory.Exists(HttpContext.Current.Server.MapPath(logDirectory)))
    {
        System.IO.Directory.CreateDirectory(HttpContext.Current.Server.MapPath(logDirectory));
    }
 
    String fileName = String.Format("{0}{1}-{2}.log", logDirectory, logType.ToString(),  DateTime.Now.ToString("yyyy-MM-dd"));
    String resolvedName = HttpContext.Current.Server.MapPath(fileName);
    using (StreamWriter sw = File.AppendText(resolvedName))
    {
        sw.WriteLine(DateTime.Now.ToShortTimeString() + " : " + content);
    }
}

There you have it. Using Application Insights in your Sitefinity project. If you are on Azure I encourage you to give it a go and check out the information that you get. I have found it to be a great central source to monitor and analyse my Sitefinity sites.


Darrin Robertson - Sitefinity Developer

Thanks for reading and feel free to comment
Darrin Robertson


Leave a comment
Load more comments

Make a Comment

Please type the code above

comment-avatar