7

I am currently using ApplicationInsights-JS in my progressive web app. It works in my react components as I can import what I need from the relevant npm packages.

In my service worker however, I can only import logic using importScripts.

I did manage to find a CDN for ApplicationInsights-JS on their Github page however it seems that in order to initialise app insights using this library you need to have access to window in order to store the appinsights, which you cannot do from a service worker.

I tried to use the web snippet approach since the CDN seemed to be related to that particular library, but I can't use window and am not sure how else to implement this solution.

This is a copy paste of the suggested snippet to init the app insights object from: https://github.com/Microsoft/ApplicationInsights-JS

importScripts('https://az416426.vo.msecnd.net/beta/ai.2.min.js');

const sdkInstance = 'appInsightsSDK';
window[sdkInstance] = 'appInsights';
const aiName = window[sdkInstance];

const aisdk =
  window[aiName] ||
  (function(e) {
    function n(e) {
      i[e] = function() {
        const n = arguments;
        i.queue.push(function() {
          i[e](...n);
        });
      };
    }
    let i = { config: e };
    i.initialize = !0;
    const a = document;

    const t = window;
    setTimeout(function() {
      const n = a.createElement('script');
      (n.src = e.url || 'https://az416426.vo.msecnd.net/next/ai.2.min.js'),
        a.getElementsByTagName('script')[0].parentNode.appendChild(n);
    });
    try {
      i.cookie = a.cookie;
    } catch (e) {}
    (i.queue = []), (i.version = 2);
    for (
      const r = [
        'Event',
        'PageView',
        'Exception',
        'Trace',
        'DependencyData',
        'Metric',
        'PageViewPerformance'
      ];
      r.length;

    )
      n(`track${r.pop()}`);
    n('startTrackPage'), n('stopTrackPage');
    const o = `Track${r[0]}`;
    if (
      (n(`start${o}`),
      n(`stop${o}`),
      !(
        !0 === e.disableExceptionTracking ||
        (e.extensionConfig &&
          e.extensionConfig.ApplicationInsightsAnalytics &&
          !0 ===
            e.extensionConfig.ApplicationInsightsAnalytics
              .disableExceptionTracking)
      ))
    ) {
      n(`_${(r = 'onerror')}`);
      const s = t[r];
      (t[r] = function(e, n, a, t, o) {
        const c = s && s(e, n, a, t, o);
        return (
          !0 !== c &&
            i[`_${r}`]({
              message: e,
              url: n,
              lineNumber: a,
              columnNumber: t,
              error: o
            }),
          c
        );
      }),
        (e.autoExceptionInstrumented = !0);
    }
    return i;
  })({ instrumentationKey: 'xxx-xxx-xxx-xxx-xxx' });
(window[aiName] = aisdk),
  aisdk.queue && aisdk.queue.length === 0 && aisdk.trackPageView({});

I get window is not defined which is expected, but I'm not sure how else I can make use of this library from the service worker.

Has anyone else had a similar implementation in which they successfully logged telemetry using ApplicationInsights from a service worker?

JleruOHeP
  • 10,106
  • 3
  • 45
  • 71
B.L.Coskey
  • 285
  • 2
  • 9
  • 2
    This question is underrated because appInsights could be a great tool for measuring push notification behavior (and to my understanding push notification events should be treated in the ServiceWorkerScope context only, so without the window object, mandatory for appInsights). There is perhaps the trick with postMessage and code execution from the component/UI point of view. – Rajar Jul 08 '19 at 13:29

4 Answers4

3

I realised that I was over complicating this.

Since I only needed to track a custom event, and didn't need all the automated page tracking etc that appInsights does, I ended up doing a fetch from my service worker.

I just copied the header and body format from the requests that I made using my react pages.

The below successfully logged telemetry to my app insights dashboard:

fetch(url, {
  method: 'post',
  headers: {
    'Content-type': 'application/json'
  },
  body: JSON.stringify([
    {
      time: '2019-05-02T15:56:37.589Z',
      iKey: 'INSTRUMENTATION_KEY',
      name:
        'Microsoft.ApplicationInsights.INSTRUMENTATION_KEY.Event',
      tags: {
        'ai.user.id': 'l6Tey',
        'ai.session.id': 'TL+Ry',
        'ai.device.id': 'browser',
        'ai.device.type': 'Browser',
        'ai.operation.id': 'HUfNE',
        SampleRate: '100',
        // eslint-disable-next-line no-script-url
        'ai.internal.sdkVersion': 'javascript:2.0.0-rc4'
      },
      data: {
        baseType: 'EventData',
        baseData: {
          ver: 2,
          name: 'Testing manual event',
          properties: {},
          measurements: {}
        }
      }
    }
  ])
})
  .then(json)
  .then(function(data) {
  })
  .catch(function(error) {
  });
B.L.Coskey
  • 285
  • 2
  • 9
  • It misses some information in your response: for instance there is `INSTRUMENTATION_KEY` in **iKey** and **name**, but in **name** it is the same as **iKey** without `-`; and how are you filling the **tags** field? – Rajar Jul 09 '19 at 07:59
1

I've almost managed to use Microsoft Application Insights in our app's service worker.

The key parts are:

  • Using the lightweight version of appInsights (see this small remark at 4th step) with importScripts('https://az416426.vo.msecnd.net/next/aib.2.min.js').
  • Initialize an appInsights object:
appInsights = new Microsoft.AppInsights.AppInsights({ instrumentationKey: "[replace with your own key]" });
  • when track needed (during onpush event or onnotificationclick), go for appInsight.track({ eventItemFields }) then appInsights.flush().

I've said "almost" because the flush part seems to not working, I've got: "Sender was not initialized" internal error after enabling debugging. I will publish here a working sample code if I successfully manage this issue.

References:

Rajar
  • 274
  • 1
  • 7
  • Would be great if you could get it working like this. My original solution felt like a bit of a hack to be honest but it ended up being the only way I could send the telemetry. Will also tinker around with this and see if I can get it working. – B.L.Coskey Jul 16 '19 at 14:30
1

Using the Web SDK in a service worker is troublesome. The full version depends on a window object, while the basic SDK depends on Beacon or XmlHttpRequest for sending the messages (in file https://github.com/microsoft/ApplicationInsights-JS/blob/master/channels/applicationinsights-channel-js/src/Sender.ts):

                if (!_self._senderConfig.isBeaconApiDisabled() && Util.IsBeaconApiSupported()) {
                    _self._sender = _beaconSender;
                } else {
                    if (typeof XMLHttpRequest !== undefined) {
                        const xhr:any = getGlobalInst("XMLHttpRequest");
                        if(xhr) {
                            const testXhr = new xhr();
                            if ("withCredentials" in testXhr) {
                                _self._sender = _xhrSender;
                                _self._XMLHttpRequestSupported = true;
                            } else if (typeof XDomainRequest !== undefined) {
                                _self._sender = _xdrSender; // IE 8 and 9
                            }
                        }
                    }
                }

At the moment Application Insights SDK does not seem to support service workers. Rajars solution seems to be the best option for now.

Update: There is an issue in the Github Repo about this: https://github.com/microsoft/ApplicationInsights-JS/issues/1436

A suggestion that works is by using the basic/lightweight version of Application Insights (as mentioned by Rajar) and adding a XMLHttpRequest polyfill (that uses the fetch api) before inititializing Application Insights. After that you can use the lightweight version.

An example can be found here: https://github.com/Pkiri/pwa-ai

Pkiri
  • 21
  • 2
0

I was trying to use AppInsightsSDK in E2E tests environment (pupeteer) and when I tried to log event or metric I got with "Sender was not initialized" error.

As @Pkiri mentioned one would need XMLHttpRequest polyfill to solve the issue. Although my scenario is not directly related to Service worker I wanted to mention that @Pkiri answer is not entirely true, because one can also use globalThis, self, window or global to get the same result according to SDK source code function getGlobalInst("XMLHttpRequest"); resolves to

function getGlobal() {
  if (typeof globalThis !== strShimUndefined && globalThis) {
    return globalThis;
  }
  if (typeof self !== strShimUndefined && self) {
    return self;
  }
  if (typeof window !== strShimUndefined && window) {
    return window;
  }
  if (typeof global !== strShimUndefined && global) {
    return global;
  }
  return null;
}

And for my scenario this was a valid solution

const appInsights = new ApplicationInsights({
  config: {
    instrumentationKey: 'AppInsights_InstrumentationKey',
  },
});
global.XMLHttpRequest = require("xmlhttprequest").XMLHttpRequest;
global.appInsights = appInsights.loadAppInsights();