APM tips blog

Blog about application monitoring.

Disable Browser Telemetry From the Page

| Comments

There is a feedback page for the Azure portal. One of the feedback item is “I want to block certain items it sent to the app insights like the pagename. Can this be done?”. One of my previous posts explains how Application Insights javascript snippet works. Now I’ll try to answer the question and discuss another scenario - how to disable reporting of telemetry under certain conditions. I hope this post will help to understand the snippet even better.

Do not send page name to the portal

So - can page name be hidden from the Application Insights. Yes, sure. The standard JavaScript snippet ends with these two lines:

1
2
window.appInsights = appInsights;
appInsights.trackPageView();

So PageView event is not begin sent automatically. It is sent by this call to trackPageView you’ve pasted to your page. Looking at signature of this function you can find out that it accept four parameters, two of which are name and url:

1
public trackPageView(name?: string, url?: string, properties?: Object, measurements?: Object)

When you call trackPageView without parameters it will use window.document.title and window.location.href correspondingly as you can see on github.

If for some reasons you don’t want to use window.document.title as a page name you can replace it to window.location.pathname or any custom string you like:

1
2
window.appInsights = appInsights;
appInsights.trackPageView(window.location.pathname, window.location.href);

Disable telemetry from the page

Now, let’s imagine that you don’t want to report telemetry from certain pages or under certain conditions.

Do not inject snippet

Easiest way to disable any telemetry from the page is not to inject the javascript snippet. In many applications all pages are built out of one template. So you will need to have some server-side logic to disable snippet injection for the certain pages.

Typically for ASP.NET applications you will inject Application Insights javascript snippet to the template file Views\Shared\_Layout.cshtml. If you are using razor you can have condition code like this:

1
2
3
4
5
@if (needTelemetry) {
  <script language="javascript">
      Application Insights snippet code...
  </script>
}

Do not initialize javascript

There are cases when you do not run server-side code to generate snippets or you want to decide whether to send telemetry on client side. For instance, if you have a static HTML page and you want to disable tlemetry when it is opened as a file from file system - you can have client side “big switch” implemented like this:

1
2
3
4
5
<script language="javascript">
  if (location.protocol != "file:") {
      Application Insights snippet code...
  }
</script>

The big downside of this approach is that all custom telemetry calls like the call to appInsights.trackEvent will now fail. To fix this issue - you can create your own mock object that will replace the real appInisghts. Here is how this mock object may look like:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
<script language="javascript">
  if (location.protocol != "file:") {
      Application Insights snippet code...
  }
  else {
      var appInsights = window.appInsights || function (config) {
          var t = {};
          function s(config) {
              t[config] = function () {
                  console.log(config + " was called with the arguments: " +
                      Array.prototype.slice.call(arguments).toString());
              }
          }
          for (i = ["Event", "Exception", "Metric", "PageView", "Trace"]; i.length;)
              s("track" + i.pop());
          return t;
      }();
      window.appInsights = appInsights;
  }

Disable page view reporting

In some cases you may want to disable page view reporting. For instance, you may not want to have page view statistics from the test environment or development machine. Just wrap the call to trackPageView into condition like this:

1
2
3
if (!document.location.href.startsWith("http://localhost")) {
  appInsights.trackPageView();
}

Disable error reporting

It is also easy to disable reporting of javascript errors from the development machine. Set disableExceptionTracking setting in appInsights configuration:

1
2
3
4
5
6
var appInsights = window.appInsights || function (config) {
      //snippet code...
}({
  instrumentationKey: "595f59fc-f85f-425f-a336-827b13c1a03c",
  disableExceptionTracking: document.location.href.startsWith("http://localhost")
});

With Application Insights you are in full control over telemetry your application sends to the portal. We release right from github so you can always review what data Application Insights SDK collects.

Application Insights for Desktop Applications

| Comments

Good news, we open sourced Application Insights SDK for .NET. For now, just shared part of it - devices and ASP.NET SDKs are coming soon. Internally we call it “Core SDK”. In this article I want to describe how to use this SDK to track usage and crashes of your desktop application.

Let’s say you have a simple WPF app that shows the temperature in the location you’ve specified using zip code:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
public MainWindow()
{
  InitializeComponent();
}

private async void buttonClick_requestCurrentTemperature(object sender, RoutedEventArgs e)
{
  // get zip code from the text box
  var zip = zipTextBox.Text;
  
  HttpClient client = new HttpClient();
  var apiUrl = string.Format(
        "http://api.openweathermap.org/data/2.5/weather?units=metric&zip={0},us",
        zip);
  var result = await client.GetStringAsync(apiUrl);
  
  JObject o = JObject.Load(new JsonTextReader(new StringReader(result)));
  var temperature = o.SelectToken("main.temp").Value<float>();
  
  // display the temperature:
  label.Content = temperature.ToString();
}

From usage perspective you might be curious what zip code being used most often. It is easy to track with Application Insights. You’ll need to install Application Insights API NuGet. Than you initialize Application Insights. Since I only have one form in my applicaiton, I’ve created a private telemetry client that will be used in this WPF form:

1
2
3
4
5
6
7
8
9
10
private readonly TelemetryClient telemetryClient;

public MainWindow()
{
  TelemetryConfiguration config = TelemetryConfiguration.CreateDefault();
  config.InstrumentationKey = "Foo";
  telemetryClient = new TelemetryClient(config);
  
  InitializeComponent();
}

With this you can now track telemetry event every time somebody click the button:

1
2
telemetryClient.TrackEvent("TemperatureRequested",
  new Dictionary<string, string>() { { "zip", zip } });

In the Azure portal you can open Metric Exporer and group events by zip code:

Once configured you’ll see a view like this that can be saved as favourite view for later access:

It was easy. Now let’s say you want to decide whether you need to show a spinner to your customer while she is waiting for results. You wonder - how long does it typically take to display the temperature.

Every event has a metrics collection you can associate with it. Just start a timer and report the duration as a metric. Now you can have charts showing the average duration for TemperatureRequested event. Code may look like this:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
private async void buttonClick_requestCurrentTemperature(object sender, RoutedEventArgs e)
{
    var timer = Stopwatch.StartNew();
    var zip = zipTextBox.Text;

    try
    {
        HttpClient client = new HttpClient();
          var apiUrl = string.Format(
               "http://api.openweathermap.org/data/2.5/weather?units=metric&zip={0},us",
               zip);
        var result = await client.GetStringAsync(apiUrl);

        JObject o = JObject.Load(new JsonTextReader(new StringReader(result)));
        var temperature = o.SelectToken("main.temp").Value<float>();
      
        label.Content = temperature.ToString();
    }
    finally
    {
        telemetryClient.TrackEvent("TemperatureRequested",
            new Dictionary<string, string>() { { "zip", zip } },
            new Dictionary<string, double>() { { "duration", timer.ElapsedMilliseconds } });
    }
}

Finally, you want to know how many users of your application clicking this button. So you need to start tracking users and sessions. In my application I’m creating the new session every time user opens the form. I’m using the user name from environment. You need to make sure that user names are unique enough so you’ll get the real number of users.

1
2
3
4
5
6
7
8
9
10
public MainWindow()
{
    TelemetryConfiguration config = TelemetryConfiguration.CreateDefault();
    config.InstrumentationKey = "954f17ff-47ee-4aa1-a03b-bf0b1a33dbaf";
    telemetryClient = new TelemetryClient(config);
    telemetryClient.Context.User.Id = Environment.UserName;
    telemetryClient.Context.Session.Id = Guid.NewGuid().ToString();

    InitializeComponent();
}

You can find more information on usage tracking on Application Insights documentation page.

Note, in this example I do not initialize singleton TelemetryConfiguration.Active and do not use ApplicationInsights.config configuration file. So in the documentation above you cannot just create TelemetryClient c = new TelemetryClient();. This telemetry client will not be initialized - it will not have instrumentaiton key and telemetry channel configured.

Most of the code above it not specific for desktop applications. One of the difference of desktop applications is that they may be running without internet connection. By default when you create TelemetryConfiguration in-memory channel will be used to communicate with the Application Insights backend. This channel has no persistence of events and will lose data if internet connection is not reliable. You may use this channel if it is not the issue for you. However for more reliable telemetry you may want to use persistence channel. For desktop applicaitons use Microsoft.ApplicationInsights.PersistenceChannel NuGet:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
public MainWindow()
{
    TelemetryConfiguration config = TelemetryConfiguration.CreateDefault();
    config.InstrumentationKey = "954f17ff-47ee-4aa1-a03b-bf0b1a33dbaf";

    config.TelemetryChannel = new PersistenceChannel();
    config.TelemetryChannel.DeveloperMode = Debugger.IsAttached;

    telemetryClient = new TelemetryClient(config);
    telemetryClient.Context.User.Id = Environment.UserName;
    telemetryClient.Context.Session.Id = Guid.NewGuid().ToString();

    InitializeComponent();
}

Persistence channel is optimized for devices scenario when the number of events produced by application is relatively small and connection is unreliable quite often. This channel will write events to the disk into reliable storage first and then attempt to send it. Here is how it works.

Let’s say you want to monitor unhandled exceptions. You’d subscribe on UnhandledException event and in the corresponding callback you want to make sure that telemetry will be persisted. So you call Flush on telemetry client.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
AppDomain.CurrentDomain.UnhandledException += CurrentDomain_UnhandledException;

...

private void CurrentDomain_UnhandledException(object sender, UnhandledExceptionEventArgs e)
{
    ExceptionTelemetry excTelemetry = new ExceptionTelemetry((Exception)e.ExceptionObject);
    excTelemetry.SeverityLevel = SeverityLevel.Critical;
    excTelemetry.HandledAt = ExceptionHandledAt.Unhandled;

    telemetryClient.TrackException(excTelemetry);

    telemetryClient.Flush();
}

The only thing method Flush will do is to make sure that all telemetry events from the buffer are stored in persistence storage. In my case when I enter incorrect zip code - applicaiton will crash with ArgumentExcetpion and I’ll see new file named 20150810005005_84fb4de977e24c8399618daf2c4eb57d.trn into the folder %LocalAppData%\Microsoft\ApplicationInsights\35eb39bd0bb5855e732748ad369ffacc10de7340.

This file has all events scheduled to be send to the backend compressed with GZIP.

1
2
3
4
5
https://dc.services.visualstudio.com/v2/track
Content-Type:application/x-json-stream
Content-Encoding:gzip

H4sIAAAAAAAEAN2YbW/bNhDH3w/YdzD0tjahR8sWkgCps6JZ4yyL3ebFXASUdLI5S5RKUk7cIN99R9mulYd5SR/UYMkbizqefve/I0/UjcFpBkZgDFkkcpknihwWRcoiqljOj7lk05mSpO+5ieUniesDuJRa1HTCMDFDizpOHNKE/LYAroy2oVjlzTYtr2P2OmZ/bPmBZwamR3pez7S7Tsf0A9NEU/YOlmi6cd3RvjvaeUd779Tda8d0Ko3gxqCMMK5AcJoSGc8/gJAIin4sYhOTeF2nj9ZoVUoQhMV4B39M55R/Xo1LkHrG6pYdU8tzqdfx+0jrdm2vE/a6Vsft22DaNPQ8yzRu20ZMFdVPD6mE8bLQIVYRH+nxdjV8tDZZgDACu73RdQxZAYKqUsA5fCpBKohxRgZU4lCGPqqw4lJUihuBY3n4wELkOE0xqO5+ZoVWqmd6dmzc4t+vv9x8r8RdR1BUT96dvL5t2v7/J3mbqHckcEZ5nEJ8qND+PV9foS1s5mJIf90YGsXCxDhmz8FI0f/pKjGjJeY6I4diWuo0n5ZpWhc7w1DoVBt+oGkJrYhynqtWCC2OlmQiJvyMCnSFerV0roOWzEsRgaHR5Bs0GikazY1AiRKwYqiQEK+HkCuFBaRGYOonqVmuBTuFK4XYulZ+l6jiCeOfsAAUcC2qJBWIFlpKyMJ0+XBKu7XO2b6P+cL/dmtQprq49zmUStC03TorQyxDrI9xPge+75g0dhPohjbtUkAFb9tf4KyXDGfX4C6KpLa6LDKkjF8wHudXr/bCUiHFAG/OL8VqiQ9KITDltbV/EF9eOmSYL+AUrtXdMO4534ZhPTUMXTHoM2HppviiYDLB6ismk3veHxnYBkOuaZaSSKKvlHH049s1QZyaIOviPi+53jTIIM8KfLgYgViwCCQ5lEseDSvr1yVLYxCDXMCrvYPo8vKIySKly0GKGthkbzwT+VVlfxCiSHe1yWSUi5SFW1Hcp4oS+j71Iq9r9R0XzF6/nlv3YSgrBSRBHKAx49Pt1nghaIGJJMfrvescaDqgleT1NK4cvMat5CtoHStMnJ7XpbHTdcHx6rTeV9GOxXJAVTS7mAFvirT7FFKdfw0G4o9qeegN/Zgv0PlxVjQmqv+NqHwEUSmYWg5yLIsHa/qHcfcecteLAKE05RpKL9BN1TaysPrPpWuEyjJfJpb1bTXYVMlZ9vM4yZnIsQnIP0u437J/IOQj7Wkn5AWPNefbPJ83xljvO8OR5nNs8vaKx5tNew3VGJD3b0CjMox0g64pplteiK+XX8qwMcontZUX066tJ7WWF9GvrUe6yc5FcwJTGi2b79ZW/z8LdfOj4SVkm4+RveeSJvgyrtgCVq/B25U0XJ/8mgJ8ZpchZ6WcvdEnzyYTbD+7x2woGyPc0WBqByr99rDFvAt3JkDiubCyq9ivcjH/3pg7zjb3MB9/M2wEcseR5h7kT4Db0W5+Ppy/47MEXlSfJl7W94U8/HsyOYKwnE4mmnCqvzDcfrz92DYkBqXPUCer4IwBXuDEVH/k/AeYVOh5mxYAAA==

Next time you’ll start this application - channel will pick up this file and deliver telemetry to the Application Insights.

All the implementation details of this telemetry configuration you may find on github.

There are many more aspect of desktop applications monitoring. I didn’t explain how you will track page views - one of the main concept of devices monitoring. You may also want to track failures, collect diagnostics logs or track dependencies and performance counters. Maybe next time I can cover these aspects as well.

Application Versioning: Semantic or Automatic?

| Comments

Looking at @pksorensen’s example of OWIN middleware to monitor server request I noticed this line of code:

1
rt.Context.Component.Version = "1.0.0";

It will set application version for telemetry items so you can group telemetry by version and understand in which version of application certain exception happened.

Note: Yes, it is confusing name. SDK refers to application as “Component” when in UI it is called “Application”.

This code snippet uses constant string as an application version. My guess is that this version represent semantic version of API call. If you versioned your REST API using urls like this: https://management.azure.com/subscriptions?api-version=2014-04-01-preview you’d probably need to change your middleware to read version from api-version query string parameter.

Semantic version is good for certain telemetry reports. For instance you can see how much traffic goes to which version of API to decide when to shut down older version. However semantic version is something you need to code explicitly - there is no generic way to version applications and APIs.

So instead of using semantic version - we suggest to use automatically generated version number. For instance, this article suggest to use Assembly version of your application as application version.

You’ll need to use wildcard in Assembly.cs:

1
[assembly: AssemblyVersion("1.0.*")]

and use telemetry initializer to initialize application version:

1
2
telemetry.Context.Component.Version =
    typeof(TestBuildInfo).Assembly.GetName().Version.ToString();

Now all telemetry items will be marked with the version like 1.0.5647.32696, where 5647 and 32696 are some semi-random numbers.

Drawback of this approach is that again, you need to write some code. Furthermore, there is no way to detect which assembly is a “main” assembly of an application. So your telemetry initializer should be application specific.

This brings us to the reason I write this blog post. Visual Studio has a feature that I believe undeservedly is not well known and widely used. It is called build information file or BuildInfo.config.

Turing this feature on is simple. Just add a property to your project file:

1
2
3
<PropertyGroup>
  <GenerateBuildInfoConfigFile>true</GenerateBuildInfoConfigFile>
</PropertyGroup>

This will generate the file bin/$(ProjectName).BuildInfo.config when you compile locally with the commit number and auto-generated build label and BuildInfo.config when you publish your application. For instance, once I enabled continues integration in Visual Studio online this file is placed next to web.config:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
<?xml version="1.0" encoding="utf-8"?>
<DeploymentEvent xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://schemas.microsoft.com/VisualStudio/DeploymentEvent/2013/06">
  <ProjectName>TestBuildInfo</ProjectName>
  <SourceControl type="Git">
    <GitSourceControl xmlns="http://schemas.microsoft.com/visualstudio/deploymentevent_git/2013/09">
      <RepositoryUrl>/DefaultCollection/temp/_git/temp</RepositoryUrl>
      <ProjectPath>/TestBuildInfo/TestBuildInfo.csproj</ProjectPath>
      <BuiltSolution>/TestBuildInfo.sln</BuiltSolution>
      <CommitId>fbbaf40f804ad2646d5ce70b545fd9fd257feca8</CommitId>
      <ShortCommitId>fbbaf40</ShortCommitId>
      <CommitDateUTC>Thu, 18 Jun 2015 17:07:56 GMT</CommitDateUTC>
      <CommitComment>initial commit</CommitComment>
      <CommitAuthor>Sergey Kanzhelev</CommitAuthor>
    </GitSourceControl>
  </SourceControl>
  <Build type="TeamBuild">
    <MSBuild>
      <BuildDefinition kind="informative, summary">TestBuildInfoApp_CD</BuildDefinition>
      <BuildLabel kind="label">TestBuildInfoApp_CD_20150618.1</BuildLabel>
      <BuildId kind="id">e6e457a1-debf-4bda-b0c9-61344fd55ae2,vstfs:///Build/Build/37</BuildId>
      <BuildTimestamp kind="informative, summary">Thu, 18 Jun 2015 18:08:19 GMT</BuildTimestamp>
      <Configuration kind="informative">Debug</Configuration>
      <Platform kind="informative">AnyCPU</Platform>
      <BuiltSolution>/TestBuildInfo.sln</BuiltSolution>
    </MSBuild>
  </Build>
</DeploymentEvent>

It is great to have this file published with your application as you will always know which version of source code it was built from.

Application Insights Web SDK will install context initializer called BuildInfoConfigComponentVersionContextInitializer that will read this file and mark telemetry items with the application version equal to BuildLabel from the file above. In this example it will be TestBuildInfoApp_CD_20150618.1. Now, looking at your telemetry you can always find the build that produced this binary.

Here are some additional details on how this feature used to work in the old version of Application Insights. From this article you can find that you can use IncludeServerNameInBuildInfo property to enrich BuildInfo.config even more and how to configure copying of this file next to web.config while developing. Do not forget to .gitignore it though…

Do Not Use Context Initializers

| Comments

I’ve been already writing about telemetry initializers. Using them you can add custom properties to telemetry items reported by your application. For every telemetry item Initialize method of all configured telemetry initializer will be called.

Typical use of telemetry initializer is to set properties like user name, operation ID or timestamp. If you look at ApplicationInsights.config file you’ll find a list of default telemetry initializers used for web applications monitoring.

You’d also discover a set of context initializers defined in the standard ApplicationInsights.config file.

The difference between telemetry initializer and context initializer is that context initializer will only be called once per TelemetryClient instance. So context initializers collects information that is not specific to telemetry item and will be the same for all telemetry items. Examples may be application version, process ID and computer name.

So why you should NOT use context initializer?

Reason 1. You never know when context initializer will be called.

Context initializers will be called no later than the first access to Context property of TelemetryClient object instance will be made. If the only TelemetryClient object is one you constructed - you can control when context initializers will be executed. However if you are using features like requests monitoring, dependencies monitoring or performance counters collection - number of telemetry clients will be created under the hood. So if you add context initializer to TelemetryConfiguration.Active.ContextInitializers collection programmatically you can never guarantee that those TelemetryClient objects were not initialized already.

In fact with the changes in 0.16 SDK some initialization ordering was changed and if you’ll add context initializer programmatically in Global.asax to the TelemetryConfiguration.Active.ContextInitializers collection - it will NOT be run for web requests telemetry module. So requests and exception items will not be stamped with the properties you need.

Reason 2. Once set it cannot be unset.

There are cases when you want to change globally set properties in running application. For instance you may need to change environment name from pre-production to production after VIP swap. You cannot achieve it using context initializers. You can remove context initializer from the collection TelemetryConfiguration.Active.ContextInitializers, but properties set by it will still be attached to telemetry items reported from the already created instances of TelemetryClient.

Reason 3. You can do it with telemetry initializer.

You can always use telemetry initializer instead of context initializer. The only thing you need to do is to cache the property value you need to set so it will not be calculated every time.

So instead of context initializer that will set application version like this:

1
2
3
4
5
6
7
public class AppVersionContextInitializer : IContextInitializer
{
    public void Initialize(TelemetryContext context)
    {
        context.Component.Version = GetApplicationVersion();
    }
}

you can create telemetry initializer that will do the same:

1
2
3
4
5
6
7
8
9
public class AppVersionTelemetryInitializer : ITelemetryInitializer
{
    string applicationVersion = GetApplicationVersion();

    public void Initialize(ITelemetry telemetry)
    {
        telemetry.Context.Component.Version = applicationVersion;
    }
}

Reason 4. Context initializer name is confusing.

If you think what context initializer is doing - it initializes TelemetryClient, not telemetry context. If you’ll construct an instance of TelemetryContext context initializer will not be called. So why is it called context initializer and not client initializer? My telemetry item also have a context. Will context initializer be called when it is constructed? And why not?

The whole reason I’m writing this article is that we regularly getting questions about it and found out that the concept of context initializer if very confusing.

So even if you understood the concept - somebody else may be confused by it and hit a problem with initialization ordering. It is typically hard to troubleshoot why some properties were not added to your telemetry items in production, especially if the issues is timing specific.

The biggest reason we still have a concept of Context Initializer is to keep backward compatibility and do not break existing context initializers you might already have. We are thinking of moving away from context initializers in the future versions and mark this interface as deprecated.

DIY: Data Sampling

| Comments

[Updated 3-Sept-2015]: updated as channel was renamed

Now when pricing for Application Insights is announced you might be wondering - how can you make these prices truly cloud-friendly and only pay for what you are using. You may also wonder - how to fit into throttling limits even if you are willing to pay for all the telemetry data your application produces. Note, you wouldn’t need any of this if your application doesn’t have a big load. There are great filtering and grouping capabilities in Application Insights UI so you will be better off having all the data on the server.

There are four techniques to minimize the amount of data your application reports - separate traffic, filter not interesting data, sample and aggregate. Today I’ll explain how to implement sampling.

Sampling will only work for high-load applications. The idea is to send only every n-th request to the server. With the normal distribution of requests and high load you’ll get statistically correct values for all sorts of aggregations (don’t forget to multiply all values you see in UI to n).

There is no out-of-the-box support for sampling or filtering in Application Insights today. So the idea of implementing it will be to replace standard channel with the custom-made. For every telemetry item this channel may decide whether to send it to the portal or not.

First, define a new class. You’ll need to have your own instance of ServerTelemetryChannel from the NuGet package Microsoft.ApplicationInsights.WindowsServer.TelemetryChannel. I’ve also defined public property SampleEvery so you can configure how much data to sample out using configuration file:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
public class RequestsSamplingChannel : ITelemetryChannel, ITelemetryModule
{
    private int counter = 0;

    private ServerTelemetryChannel channel;

    public int SampleEvery { get; set; }

    public RequestsSamplingChannel()
    {
        this.channel = new ServerTelemetryChannel();
    }

    public void Initialize(TelemetryConfiguration configuration)
    {
        this.channel.Initialize(configuration);
    }
}

Now you should implement Send method. In this example I apply sampling only to Requests so performance counters, metrics, dependencies and traces will not be sampled. If telemetry item is of type RequestTelemetry I’d increment counter and every SampleEvery-th time will send this item using standard channel:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
public void Send(ITelemetry item)
{
    if (item is RequestTelemetry)
    {
        int value = Interlocked.Increment(ref this.counter);
        if (value % this.SampleEvery == 0)
        {
            this.channel.Send(item);
        }
        else
        {
            //value was sampled out. Do nothing
        }
    }
    else
    {
        this.channel.Send(item);
    }
}

For all other properties and methods - just proxy them to the standard channel:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
public bool DeveloperMode
{
    get
    {
        return this.channel.DeveloperMode;
    }
    set
    {
        this.channel.DeveloperMode = value;
    }
}

public string EndpointAddress
{
    get
    {
        return this.channel.EndpointAddress;
    }
    set
    {
        this.channel.EndpointAddress = value;
    }
}

public void Flush()
{
    this.channel.Flush();
}

public void Dispose()
{
    this.channel.Dispose();
}

Now you can use this channel. In ApplicationInsights.config file replace the TelemetryChannel node with the following. You can read more on how Application Insights SDK instantiate objects from configuration in my previous post:

1
2
3
<TelemetryChannel Type="ApmTips.RequestsSamplingChannel, ApmTips">
  <SampleEvery>10</SampleEvery>
</TelemetryChannel>

You can implement all sort of interesting sampling algorithms using this approach. Instead of counter you can use random generated value or even RequestTelemetry.ID property that is fairly random.

Next time I’ll cover other ways to minimize the amount of data you are sending to Application Insights.

New SDK Version Published

| Comments

The version 0.15 of Application Insights SDK was published on NuGet. Good news - this SDK reverts back API changes made in 0.14. Specifically it returns back TelemetryContext.Properties mentioned here.

More changes:

  • Couple NuGet packages were renamed to make names more descriptive:
    • PerformanceCollector package renamed to PerfCounterCollector
    • RuntimeTelemetry renamed to DependencyCollector
  • New property Operation.SyntheticSource now available on TelemetryContext. Now you can mark your telemetry items as “not a real user traffic” and specify how this traffic was generated. As an example by setting this property you can distinguish traffic from your test automation from load test traffic.
  • Application Insights Web package now detects the traffic from Availability monitoring of Application Insights and marks it with specific SyntheticSource property.
  • Channel logic was moved to the separate NuGet called Microsoft.ApplicationInsights.PersistenceChannel.

JavaScript SDK Is Now on GitHub

| Comments

Please welcome JavaScript SDK on github. Recently I explained snippet code you need to inject to your project to use Application Insights JavaScript SDK. Now you can see the code of the main script. Please report issues on github and welcome to contribute!

Where Are the Telemetry Context Properties?

| Comments

Recently the change was made. Upgrading from Application Insights SDK version 0.13 to the version 0.14 you may notice that some public interfaces were changed. Specifically, public property Properties was removed from TelemetryContext class. Yes, one that mentioned in the documentation and blog post. One that is very important to enable many scenarios.

It is not a change we plan to keep for a long time. Public interface will be reverted back soon.

I thought a lot on how to explain this change and what led to this. Now I know what was the motivation of this change, I can tell that semantic versioning is designed to experiment with API surface, I know why it wasn’t immediately reverted. However I better not to go into details. I want to assure you that we understand that such a big API change should not happen again without the notice.

Version 0.14 of SDK brings great new features like ability to monitor custom performance counters. If you want to use this features and need custom properties I’d recommend to wait for the new version of SDK. If you don’t want to wait - here is a workaround (it will only work in 0.14 SDK).

If you are using context initializer - convert it to telemetry initializer. Now in telemetry initializer use ISupportProperties interface to set properties for telemetry item:

1
2
3
4
5
6
7
8
9
10
11
public class WorkaroundTelemetryInitializer : ITelemetryInitializer
{
    public void Initialize(ITelemetry telemetry)
    {
        var propsTelemetry = telemetry as ISupportProperties;
        if (propsTelemetry != null)
        {
            propsTelemetry.Properties["environment"] = "development";
        }
    }
}

Again, this workaround will only be needed for the version 0.14 and will not work in the next version of SDK.

Javascript Snippet Explained

| Comments

Big thanks to Scott Southwood who helped to prepare this post.

For end user monitoring Application Insights requires to add this JavaScript snippet to the page:

1
2
3
4
5
6
7
8
var appInsights=window.appInsights||function(config){
    function s(config){t[config]=function(){var i=arguments;t.queue.push(function(){t[config].apply(t,i)})}}var t={config:config},r=document,f=window,e="script",o=r.createElement(e),i,u;for(o.src=config.url||"//az416426.vo.msecnd.net/scripts/a/ai.0.js",r.getElementsByTagName(e)[0].parentNode.appendChild(o),t.cookie=r.cookie,t.queue=[],i=["Event","Exception","Metric","PageView","Trace"];i.length;)s("track"+i.pop());return config.disableExceptionTracking||(i="onerror",s("_"+i),u=f[i],f[i]=function(config,r,f,e,o){var s=u&&u(config,r,f,e,o);return s!==!0&&t["_"+i](config,r,f,e,o),s}),t
}({
    instrumentationKey:"d2cb4759-8e2c-453a-996c-e65c9d0e946a"
});

window.appInsights=appInsights;
appInsights.trackPageView();

This snippet will make initial set up for end user tracking and then download the reset of the monitoring logic from CDN.

There are number of reasons why this script needs to be injected into the page html code. First, placing script into the page will not require additional download at the early stage of the page loading phase. So page loading time will not be affected. Second, it provides you API to track metrics and events so you don’t need to check whether the full Application Insights script is already loaded or not. Third, this script is working in application domain so it can subscribe on onerror callback and get a full error stack. Due to security restrictions browser will not give you the full error stack if you subscribe on onerror callback from the script downloaded from the different domain. It also takes cookies from application domain so they can be used for user/session tracking. Here is more detailed explanation of what it is doing.

First, we check that Application Insights object haven’t been created yet. If so - we will create it:

1
var appInsights = window.appInsights || function (config) {

Next goes a helper function that will define a new callback with the name passed as an argument. Methods like appInsights.trackEvent will be created using this helper. Initial implementation of this method is to put an object into the queue for further processing. “Real” implementation will come with Application Insights javascript file downloaded from CDN later:

1
2
3
4
5
6
7
8
   function s(config) {
         t[config] = function () {
                var i = arguments;
                t.queue.push(function () {
                       t[config].apply(t, i)
                })
         }
   }

Now real appInsights object is defined under the pseudo name t. Initially it only contains config field. More fields and methods will be created later:

1
   var t = { config: config },

Bunch of constants and variables:

1
2
3
4
5
6
                r = document,
                f = window,
                e = "script",
                o = r.createElement(e),
                i,
                u;

In the beginning of this for loop snippet will create script element in DOM model so Application Insights javascript file will be download later from CDN:

1
2
   for (o.src = config.url || "//az416426.vo.msecnd.net/scripts/a/ai.0.js",
                r.getElementsByTagName(e)[0].parentNode.appendChild(o),

Store domain cookies in appInsights object:

1
                t.cookie = r.cookie,

Create an events queue:

1
                    t.queue = [],

And now there is an actual loop. Iterating thru the collection methods like appInsights.trackEvent will be created (remember helper s in the very beginning of the snippet):

1
2
3
4
                i = ["Event", "Exception", "Metric", "PageView", "Trace"];
                i.length;
                )
                s("track" + i.pop());

Now, subscribe to onerror callback to catch javascript errors in page initialization. You can disable thie logic by setting disableExceptionTracking property to false:

1
   return config.disableExceptionTracking ||

Using the same helper from the beggining of script define appInsights._onerror method:

1
2
                (i = "onerror",
                s("_" + i),

Now save existing window.onerror (f is window, see above in constants section) callback from the page and replace it with the new implementation. New implementation will chain the call to appInsights._onerror and call to initial window.onerror implementation:

1
2
3
4
5
                u = f[i],
                f[i] = function (config, r, f, e, o) {
                       var s = u && u(config, r, f, e, o);
                       return s !== !0 && t["_" + i](config, r, f, e, o), s
                }),

Return freshly created appInsights object from the method:

1
2
                t
}

Constructor function is created. Now call it, providing initial configuration. The only required piece of configuration is instrumentationKey:

1
2
3
({
       instrumentationKey: "@RequestTelelemtry.Context.InstrumentationKey"
});

Now we store appInsights as a global variable and save into the queue pageView event:

1
2
window.appInsights = appInsights;
appInsights.trackPageView();

Read more on out-of-the box usage analytics. Inforamtion on tracking custom events and metrics here.