Don't miss out on the action at this year's Chrome Dev Summit, streaming live on YouTube. Watch now.

Lab: Integrating Analytics

Concepts: Integrating Analytics

Overview

This lab shows you how to integrate Google Analytics into your web apps.

What you will learn

  • How to create a Google Analytics account
  • How to create a Google Firebase account
  • How to integrate Google Analytics into a web app
  • How to add and track custom events (including push notifications)
  • How to use Google Analytics with service workers
  • How to use analytics even when offline

What you should know

What you will need

1. Get set up

If you have not downloaded the repository, installed Node, and started a local server, follow the instructions in Setting up the labs.

Open Chrome and navigate to localhost:8080/google-analytics-lab/app.

If you have a text editor that lets you open a project, open the google-analytics-lab/app folder. This will make it easier to stay organized. Otherwise, open the folder in your computer's file system. The app folder is where you will be building the lab.

This folder contains:

  • pages folder contains sample resources that we use in experimenting:
  • page-push-notification.html
  • other.html
  • images folder contains images to style our notifications
  • index.html is the main HTML page for our sample site/application
  • main.js is the main JavaScript for the app
  • analytics-helper.js is an empty helper file
  • sw.js is the service worker file
  • manifest.json is the manifest for push notifications
  • package.json is used for tracking Node packages (optional)

In the browser, you should be prompted to allow notifications. If the prompt does not appear, then manually allow notifications. You should see a permission status of "granted" in the console.

You should also see that a service worker registration is logged to the console.

The app for this lab is a simple web page that has some push notification code.

main.js requests notification permission and registers a service worker, sw.js. The service worker has listeners for push events and notification events.

main.js also contains functions for subscribing and unsubscribing for push notifications. We will address that later (subscribing to push isn't yet possible because we haven't registered with a push service).

Test the notification code by using developer tools to send a push notification.

A notification should appear on your screen. Try clicking it. It should take you to a sample page.

For more information

You can learn how to build the starter app and learn more about push in Push Notifications codelab.

2. Create a Google Analytics account

In a separate tab or window, navigate to analytics.google.com. Sign in with your Gmail account, and follow the step that matches your status:

If you already have a Google Analytics account:

Create another one. Select the Admin tab. Under account, select your current Google Analytics account and choose create new account. A single Gmail account can have multiple (currently 100) Google Analytics accounts.

Adding an account

If you don't have a Google Analytics account:

Select Sign up to begin creating your account.

The account creation screen should look like this:

Creating an account

What would you like to track?

Choose Website.

Setting up your account

Enter an account name (for example "PWA Training").

Setting up your property

The property must be associated with a site. We will use a mock GitHub Pages site.

  1. Set the website name to whatever you want, for example "GA Code Lab Site".
  2. Set the website URL to USERNAME.github.io/google-analytics-lab/, where USERNAME is your GitHub username (or just your name if you don't have a GitHub account). Set the protocol to https://.
  1. Select any industry or category.
  2. Select your timezone.
  3. Unselect any data sharing settings.
  4. Then choose Get Tracking ID and agree to the terms and conditions.

Explanation

Your account is the top most level of organization. For example, an account might represent a company. An account has properties that represent individual collections of data. One property in an account might represent the company's web site, while another property might represent the company's iOS app. These properties have tracking IDs (also called property IDs) that identify them to Google Analytics. You will need to get the tracking ID to use for your app.

For more information

3. Get your tracking ID and snippet

You should now see your property's tracking ID and tracking code snippet.

If you lost your place:

  1. Select the Admin tab.
  2. Under account, select your account (for example "PWA Training") from the drop down list.
  3. Then under property, select your property (for example "GA Code Lab Site") from the down list.
  4. Now choose Tracking Info, followed by Tracking Code.

Finding the snippet

Your tracking ID looks like UA-XXXXXXXX-Y and your tracking code snippet looks like:

index.html

<script>
  (function(i,s,o,g,r,a,m){i['GoogleAnalyticsObject']=r;i[r]=i[r]||function(){(i[r].q=i[r].q||[]) \ 
.push(arguments)},i[r].l=1*new Date();a=s.createElement(o),m=s.getElementsByTagName(o)[0]; \
a.async=1;a.src=g;m.parentNode.insertBefore(a,m)})(window,document,'script', \
'https://www.google-analytics.com/analytics.js','ga');

  ga('create', 'UA-XXXXXXXX-Y', 'auto');
  ga('send', 'pageview');

</script>

Copy this script (from the Google Analytics page) and paste it in TODO 3 in index.html and pages/other.html. Save the scripts and refresh the app page (you can close the page-push-notification.html page that was opened from the notification click).

Now return to the Google Analytics site. Examine the real time data by selecting Real-Time and then Overview:

Real-time navigation

You should see yourself being tracked. The screen should look similar to this:

Real-time screen

The Active Page indicates which page is being viewed. Back in the app, click Other page to navigate to the other page. Then return to the Google Analytics site and check Active Page again. It should now show app/pages/other.html (this might take a few seconds).

Explanation

When a page loads, the tracking snippet script is executed. The Immediately Invoked Function Expression (IIFE) in the script does two things:

  1. Creates another script tag that starts asynchronously downloading analytics.js, the library that does all of the analytics work.
  2. Initializes a global ga function, called the command queue. This function allows "commands" to be scheduled and run once the analytics.js library has loaded.

The next lines add two commands to the queue. The first creates a new tracker object. Tracker objects track and store data. When the new tracker is created, the analytics library gets the user's IP address, user agent, and other page information, and stores it in the tracker. From this info Google Analytics can extract:

  • User's geographic location
  • User's browser and operating system (OS)
  • Screen size
  • If Flash or Java is installed
  • The referring site

The second command sends a " hit." This sends the tracker's data to Google Analytics. Sending a hit is also used to note a user interaction with your app. The user interaction is specified by the hit type, in this case a "pageview." Because the tracker was created with your tracking ID, this data is sent to your account and property.

Real-time mode in the Google Analytics dashboard shows the hit received from this script execution, along with the page (Active Page) that it was executed on.

You can read this documentation to learn more about how analytics.js works.

The code so far provides the basic functionality of Google Analytics. A tracker is created and a pageview hit is sent every time the page is visited. In addition to the data gathered by tracker creation, the pageview event allows Google Analytics to infer:

  • The total time the user spends on the site
  • The time spent on each page and the order in which the pages are visited
  • Which internal links are clicked (based on the URL of the next pageview)

For more information

4. View user data

We are using the real-time viewing mode because we have just created the app. Normally, records of past data would also be available. You can view this by selecting Audience and then Overview.

Here you can see general information such as pageview records, bounce rate, ratio of new and returning visitors, and other statistics.

Records overview

You can also see specific information like visitors' language, country, city, browser, operating system, service provider, screen resolution, and device.

Records details

For more information

5. Use Debug Mode

Checking the dashboard is not an efficient method of testing. Google Analytics offers the analytics.js library with a debug mode.

TODO: Replace analytics.js in the tracking snippet (in index.html and pages/other.html) with analytics_debug.js.

Save the scripts and refresh the page. You should see the browser console logging details of the "create" and "send" commands.

Navigate back to app/index.html using the Back link. Check the console logs again. Note how the location field changes on the data sent by the send command.

For more information

6. Add custom events

Google Analytics supports custom events that allow fine grain analysis of user behavior.

In main.js, replace TODO 6 with the following:

main.js

ga('send', {
  hitType: 'event',
  eventCategory: 'products',
  eventAction: 'purchase',
  eventLabel: 'Summer products launch'
});

Save the script and refresh the page. Click BUY NOW!!!. Check the console log, do you see the custom event?

Now return to the Real-Time reporting section of the Google Analytics dashboard. Instead of selecting Overview, select Events. Do you see the custom event? (If not, try clicking BUY NOW!!! again.)

Real-time events

Explanation

When using the send command in the ga command queue, the hit type can be set to 'event', and values associated with an event can be added as parameters. These values represent the eventCategory, eventAction, and eventLabel. All of these are arbitrary, and used to organize events. Sending these custom events allows us to deeply understand user interactions with our site.

You can see all method signatures in the command queue reference.

Optional: Update the custom event that you just added to use the alternative signature described in the command queue reference. Hint: Look for the "send" command examples.

You can view past events in the Google Analytics dashboard by selecting Behavior, followed by Events and then Overview. However your account won't yet have any past events to view (because you just created it).

Recorded events

For more information

7. Showing push notifications

Lets use a custom event to let us know when users subscribe to push notifications.

7.1 Create a project on Firebase

First we need to add push subscribing to our app. To subscribe to the push service in Chrome, you need to create a project on Firebase.

  1. In the Firebase console, select Create New Project.
  2. Supply a project name and click Create Project.
  3. Click the Settings (gear) icon next to your project name in the navigation pane, and select Project Settings.
  4. Select the Cloud Messaging tab. You can find your Server key and Sender ID in this page. Save these values.

Replace YOUR_SENDER_ID in the manifest.json file with the Sender ID of your Firebase project. The manifest.json file should look like this:

manifest.json

{
  "name": "Google Analytics codelab",
  "gcm_sender_id": "YOUR_SENDER_ID"
}

Save the file. Refresh the app and click Subscribe. The browser console should indicate that you have subscribed to push notifications.

Explanation

Chrome uses Firebase Cloud Messaging (FCM) to route its push messages. All push messages are sent to FCM, and then FCM passes them to the correct client.

7.2 Add custom analytics

Now we can add custom analytics events for push subscriptions.

Replace TODO 7.2a with the following code

main.js

ga('send', 'event', 'push', 'subscribe', 'success');

Replace TODO 7.2b with the following code

main.js

ga('send', 'event', 'push', 'unsubscribe', 'success');

Save the script and refresh the app. Now test the subscribe and unsubscribe buttons. Confirm that you see the custom events logged in the browser console, and that they are also shown on the Google Analytics dashboard.

Note that this time we used the alternative send command signature, which is more concise.

Optional: Add analytics hits for the catch blocks of the subscribe and unsubscribe functions. In other words, add analytics code to record when users have errors subscribing or unsubscribing. Then manually block notifications in the app by clicking the icon next to the URL and revoking permission for notifications. Refresh the page and test subscribing, you should see an event fired for the subscription error logged in the console (and in the real-time section of the Google Analytics dashboard). Remember to restore notification permissions when you are done.

Explanation

We have added Google Analytics send commands inside our push subscription code. This lets us track how often users are subscribing and unsubscribing to our push notifications, and if they are experiencing errors in the process.

8. Using analytics in the service worker

The service worker does not have access to the analytics command queue, ga, because the command queue is in the main thread (not the service worker thread) and requires the window object. We will need to use a separate API to send hits from the service worker.

8.1 Use the Measurement Protocol interface

In analytics-helper.js, replace TODO 8.1a with the following code, but use your analytics tracking ID instead of UA-XXXXXXXX-Y:

analytics-helper.js

// Set this to your tracking ID
var trackingId = 'UA-XXXXXXXX-Y';

Replace TODO 8.1b in the same file with the following code:

analytics-helper.js

function sendAnalyticsEvent(eventAction, eventCategory) {
  'use strict';

  console.log('Sending analytics event: ' + eventCategory + '/' + eventAction);

  if (!trackingId) {
    console.error('You need your tracking ID in analytics-helper.js');
    console.error('Add this code:\nvar trackingId = \'UA-XXXXXXXX-X\';');
    // We want this to be a safe method, so avoid throwing unless absolutely necessary.
    return Promise.resolve();
  }

  if (!eventAction && !eventCategory) {
    console.warn('sendAnalyticsEvent() called with no eventAction or ' +
    'eventCategory.');
    // We want this to be a safe method, so avoid throwing unless absolutely necessary.
    return Promise.resolve();
  }

  return self.registration.pushManager.getSubscription()
  .then(function(subscription) {
    if (subscription === null) {
      throw new Error('No subscription currently available.');
    }

    // Create hit data
    var payloadData = {
      // Version Number
      v: 1,
      // Client ID
      cid: subscription.endpoint,
      // Tracking ID
      tid: trackingId,
      // Hit Type
      t: 'event',
      // Event Category
      ec: eventCategory,
      // Event Action
      ea: eventAction,
      // Event Label
      el: 'serviceworker'
    };

    // Format hit data into URI
    var payloadString = Object.keys(payloadData)
    .filter(function(analyticsKey) {
      return payloadData[analyticsKey];
    })
    .map(function(analyticsKey) {
      return analyticsKey + '=' + encodeURIComponent(payloadData[analyticsKey]);
    })
    .join('&');

    // Post to Google Analytics endpoint
    return fetch('https://www.google-analytics.com/collect', {
      method: 'post',
      body: payloadString
    });
  })
  .then(function(response) {
    if (!response.ok) {
      return response.text()
      .then(function(responseText) {
        throw new Error(
          'Bad response from Google Analytics:\n' + response.status
        );
      });
    } else {
      console.log(eventCategory + '/' + eventAction +
        'hit sent, check the Analytics dashboard');
    }
  })
  .catch(function(err) {
    console.warn('Unable to send the analytics event', err);
  });
}

Save the script.

Explanation

Because the service worker does not have access to the analytics command queue, ga, we need to use the Google Analytics Measurement Protocol interface. This interface lets us make HTTP requests to send hits, regardless of the execution context.

We start by creating a variable with your tracking ID. This will be used to ensure that hits are sent to your account and property, just like in the analytics snippet.

The sendAnalyticsEvent helper function starts by checking that the tracking ID is set and that the function is being called with the correct parameters. After checking that the client is subscribed to push, the hit data is created in the payloadData variable:

analytics-helper.js

var payloadData = {
  // Version Number
  v: 1,
  // Client ID
  cid: subscription.endpoint,
  // Tracking ID
  tid: trackingId,
  // Hit Type
  t: 'event',
  // Event Category
  ec: eventCategory,
  // Event Action
  ea: eventAction,
  // Event Label
  el: 'serviceworker'
};

The version number, client ID, tracking ID, and hit type parameters are required by the API. The event category, event action, and event label are the same parameters that we have been using with the command queue interface.

Next, the hit data is formatted into a URI with the following code:

analytics-helper.js

var payloadString = Object.keys(payloadData)
.filter(function(analyticsKey) {
  return payloadData[analyticsKey];
})
.map(function(analyticsKey) {
  return analyticsKey + '=' + encodeURIComponent(payloadData[analyticsKey]);
})
.join('&');

Finally the data is sent to the API endpoint (https://www.google-analytics.com/collect) with the following code:

analytics-helper.js

return fetch('https://www.google-analytics.com/collect', {
  method: 'post',
  body: payloadString
});

The hit is sent with the Fetch API using a POST request. The body of the request is the hit data.

For more information

8.2 Send hits from the service worker

Now that we can use the Measurement Protocol interface to send hits, let's add custom events to the service worker.

Replace TODO 8.2a in sw.js with the following code:

sw.js

self.importScripts('js/analytics-helper.js');

Replace TODO 8.2b in sw.js with the following code:

sw.js

e.waitUntil(
  sendAnalyticsEvent('close', 'notification')
);

Replace TODO 8.2c in sw.js with the following code:

sw.js

sendAnalyticsEvent('click', 'notification')

Replace TODO 8.2d in sw.js with the following code:

sw.js

sendAnalyticsEvent('received', 'push')

Save the script. Refresh the page to install the new service worker. Then close and reopen the app to activate the new service worker (remember to close all tabs and windows running the app).

Now try these experiments and check the console and Google Analytics dashboard for each:

  1. Trigger a push notification.
  2. Click the notification, and note what happens.
  3. Trigger another notification and then close it (with the x in the upper right corner).

Do you see console logs for each event? Do you see events on Google Analytics?

Explanation

We start by using ImportScripts to import the analytics-helper.js file with our sendAnalyticsEvent helper function. Then we use this function to send custom events at appropriate places (such as when push events are received, or notifications are interacted with). We pass in the eventAction and eventCategory that we want to associate with the event as parameters.

For more information

9. Use analytics offline

What can you do about sending analytics hits when your users are offline? Analytics data can be stored when users are offline and sent at a later time when they have reconnected. Fortunately, there is an npm package for this.

From the app/ directory, run the following command line command:

npm install sw-offline-google-analytics

This will import the node module.

In sw.js replace TODO 9 with:

sw.js

importScripts('path/to/offline-google-analytics-import.js');
goog.offlineGoogleAnalytics.initialize();

Where path/to/offline-google-analytics-import.js is the path to the offline-google-analytics-import.js file in the node_module folder.

Now save the script. Update the service worker by refreshing the page and closing and reopening the app (remember to close all tabs and windows running the app).

Now simulate offline behavior.

Click BUY NOW!!! to fire our first custom analytics event.

You will see an error in the console because we are offline and can't make requests to Google Analytics servers. You can confirm by checking the real-time section of Google Analytics dashboard and noting that the event is not shown.

Now check IndexedDB. Open offline-google-analytics. You should see a URL cached. If you are using Chrome (see screenshot below), it is shown in urls.You may need to click the refresh icon in the urls interface.

Offline hits

Now disable offline mode, and refresh the page. Check IndexedDB again, and observe that the URL is no longer cached.

Now check the Google Analytics dashboard. You should see the custom event!

Explanation

Here we import and initialize the offline-google-analytics-import.js library. You can check out the documentation for details, but this library adds a fetch event handler to the service worker that only listens for requests made to the Google Analytics domain. The handler attempts to send Google Analytics data just like we have done so far, by network requests. If the network request fails, the request is stored in IndexedDB. The requests are then sent later when connectivity is re-established.

This strategy won't work for hits sent from our service worker because the service worker doesn't listen to fetch events from itself (that could cause some serious problems!). This isn't so important in this case because all the hits that we would want to send from the service worker are tied to online events (like push notifications) anyways.

For more information

10. Optional: Add hits for notification actions

Add two actions to the push notifications. Send a distinct analytics hit for each action that the user clicks. Remember that you will need to use the Measurement Protocol interface because this code will be in the service worker. Test the actions and make sure the hits are sending.

11. Optional: Use hitCallback

How can you send analytics hits for an action that takes the user away from your page, such as clicking a link to an external vendor or submitting a form (many browsers stop executing JavaScript once the current page starts unloading, preventing send commands from being executed)?

Research the hitCallback functionality. Use a hitCallback to send an analytics event whenever a user clicks the Special offers external link. Make sure to use a timeout so that if the analytics library fails, the user's desired action will still complete!

For more information

Solution code

To get a copy of the working code, navigate to the solution folder.

Congratulations!

You now know how to integrate Google Analytics into your apps, and how to use analytics with service worker and push notifications.

Resources