Monitoring windows forms applications with Application Insights

Most modern-day applications are being written in a web-based language. And by doing so that’s where the biggest market share can be found for vendors to deliver their monitoring solutions. Also, the monitoring of web applications seems a lot more convenient, by just tracking the HTTP calls you receive a lot of information. For a customer, I did a comparison between Dynatrace App Mon, Splunk and Application Insights. This post isn’t about that comparison but about the implemented results and why this could also work for your situation.

Getting started

Application Insights (AI) is like they claim on their website an extensible Application Performance Management (APM) service for web developers on multiple platforms. But when it was developed there was another claim they made, that was the use for Windows Forms applications. If you dig a little deeper then the introduction on their site you can find the desktop and console getting started guides. This gives us the possibility to take advantage of their cloud solution and use all the nice out of the box analytics and reports. Getting your team in control of how their application is being used by the customers.

First, we need to create a place to send our data to, so we are going to make use of the Azure CLI. This is an easy way to set up your Azure, the commands used can also be used in a Bash script as Infrastructure as Code (IaC). The creation of the Application Insights is a little tricky but more explanation on that on the blog of Donovan Brown.

az group create --name $RESOURCEGROUPNAME --location "West Europe"

az resource create --name $APPLICATIONNAME
--resource-group $RESOURCEGROUPNAME
--resource-type "Microsoft.Insights/components"
--location "West Europe"
--properties "{""""ApplicationId"""": """"$APPLICATIONNAME """"}"

Adding Application Insights to your solution

In modern-day application development, it easy to get started with AI. Just right click your project and choose “Add Application Insights Telemetry”. But for Windows Forms applications you are missing that nice option. Therefore you must know which dependencies to add. Fire up the trusted package manager and search and install the following dependencies:

  • Microsoft.ApplicationInsights.WindowsServer
  • Depends on
    • .NETFramework
    • Microsoft.ApplicationInsights
    • Microsoft.ApplicationInsights.DependencyCollector
    • Microsoft.ApplicationInsights.WindowsServer.TelemetryChannel
    • Microsoft.ApplicationInsights.PerfCounterCollector

Missing the nice ‘Add Application Insights Telemetry’ in Windows Forms applications

Adding the package will add an “ApplicationInsights.config” to your project. You only need to fill in your instrumentation key and create a new TelemetryClient() instance.

<?xml version="1.0" encoding="utf-8"?>
<ApplicationInsights xmlns="http://schemas.microsoft.com/ApplicationInsights/2013/Settings">
  <InstrumentationKey>enter your key here</InstrumentationKey>
  <TelemetryInitializers> .....
.....
</ApplicationInsights>
    public partial class Form1 : Form
    {
        private TelemetryClient tc = new TelemetryClient();

Performance data

When you start the application you should, (within a few seconds) see some performance data in application insights. This data includes the live feed with information about; your computer processes, CPU and memory. When you don’t see any data within a few minutes you probably didn’t initiate the TelementryClient().

Logging data

Whether you have a console application or a windows form application, there probably is some kind of logging already in place. When you want to diagnose a problem you want to have all the information together and it would be inconvenient to have your log files separate from your telemetry data. The most used form of logging is Log4Net so let’s add a Microsoft.ApplicationInsights.Log4NetAppender to your project. This will add the following to your <log4net> in your configuration.

  <log4net>
    <root>
      <level value="ALL" />
      <appender-ref ref="aiAppender" />
    </root>
    <appender name="aiAppender" type="Microsoft.ApplicationInsights.Log4NetAppender.ApplicationInsightsAppender, Microsoft.ApplicationInsights.Log4NetAppender">
      <layout type="log4net.Layout.PatternLayout">
        <conversionPattern value="%message%newline" />
      </layout>
    </appender>
  </log4net> 

Next, you will see a new trace event in your log. For every line written to your aiAppender, there will be a trace event. This will, of course, work great together when you use them with StartOperation<> as explained in the Chaining it together section.

Functional data

The performance data is nice and all but you also want to know how the customer is using your application. What flow they are using and what their overall experience is. Where in web-based programming it’s easy to paste a piece of javascript in a generic header. In windows forms, you need to do a little more. First, you need to provide the TelemetryClient() with information about the person running the program. So we know which events belong together. Just treat every application start as a new session, and make sure every call to the TelemetryClient() uses the same instance.

var client = new TelemetryClient();
client.Context.User.Id = Environment.UserName;
client.Context.Session.Id = Guid.NewGuid().ToString();
client.Context.Device.OperatingSystem = Environment.OSVersion.ToString();

Now we have set a context on a user or a session, remember to read up on your GDPR compliancy before you start tracking users :). We can start implementing tracking for forms because we want to know when a screen is loaded. You probably already have a base class for your forms that would be an ideal place to add the following. The flush forces the TelemetryClient() to send back the aggregated data. Normally Application Insights will do this more batch wise and more efficiently. With flush or development mode you can force this immediately. It’s up to the situation if you want this line in or not.

public class BaseForm : Form
{
    protected TelemetryClient telemetryClient = AppInsights.Client;
    new public void Show(IWin32Window owner)
    {
        telemetryClient.TrackPageView(this.Name);
        telemetryClient.Flush();
        base.Show(owner);
    }

As a direct result of the tracking of the pageviews, the user flows come active. Giving you an overview of how the customer is using your application to resolve their tasks. Giving you insights on how to improve on the flow.

Chaining it together

Because a typical windows forms applications do contain forms the previous suggestion was an easy one. The next part is a little more dependent on your application architecture. If you got some behavior patterns like a chain of responsibility or a command bus that would be an awesome place to inject some monitoring. With just a few lines of code, you will be able to monitor a lot of behavior in your application. Because if you want information about what process called and how long it took, you want to start to use StartOperation<>. This can be chained together with all the other calls, just make sure you use the same TelemetryClient().

public class CommandBus : ICommandBus
{
    protected TelemetryClient telemetryClient = AppInsights.Client;
    public void Send<TCommand>(TCommand command) where TCommand : class, ICommand
    {
        var operation = telemetryClient.StartOperation<RequestTelemetry>($"Command: {command}");

        try
        {
            //Resolve your commandbus processing
            operation.Telemetry.Success = true;
        }
        catch (Exception)
        {
            operation.Telemetry.Success = false;

            throw;
        }
        finally
        {
            telemetryClient.StopOperation(operation);
        }
    }

You can do the same in a RepositoryBase() if your application uses a CRUD like repository.

public class ReadWriteRepository<TDomainEntity, TDataEntity> : ReadOnlyRepository<TDomainEntity, TDataEntity>
{
    protected TelemetryClient telemetryClient = AppInsights.Client;
    public long Save(TDomainEntity domainEntity)
    {
        operation = telemetryClient.StartOperation<DependencyTelemetry>($"Save {domainEntity.GetType()}");
        using (var context = DataContext.Load())
        {
            try
            {
                operation.Telemetry.Target = context.Database.Connection.DataSource;
                operation.Telemetry.Type = "Database";
                operation.Telemetry.Data = $"Save({domainEntity}, Id: {domainEntity.Id})";

                //Save operation
                operation.Telemetry.Success = true;
            }
            catch (Exception)
            {
                operation.Telemetry.Success = false;

                throw;
            }
            finally
            {
                telemetryClient.StopOperation(operation);
            }
        }
    }
}

Take away

  • It isn’t hard to give your windows form application proper monitoring
  • Use strategic placement in your architecture to prevent a big spaghetti of monitoring data code
  • Always use the same TelemetryClient();
  • Chain events together by using the StartOperation<>
  • Give context e.g. SessionId and UserId
  • Start with some easy to implement locations and work from there
  • It isn’t expensive, just see for yourself
  • Check your GDPR before logging user information
  • Make use of the log4net appender
Share

You may also like...

Leave a Reply