Codelab: Integrating Analytics
What is Google Analytics?
Google Analytics is a service that collects, processes, and reports data about an application's use patterns and performance. Adding Google Analytics to a web application enables the collection of data like visitor traffic, user agent, user's location, and so forth. This data is sent to Google Analytics servers where it is processed. The processed data is then reported to the developer and/or application owner. This information is accessible from the Google Analytics web interface (dashboard) and through the reporting API.
Why use it?
Using analytics tools gives developers valuable information about their application such as:
- User's geographic location, user agent, screen resolution, and language
- How long users spend on pages, how often they visit pages, and the order in which pages are viewed
- What times users are visiting the site and from where they arrived at the site
Google Analytics is free, relatively simple to integrate, and customizable.
Creating an account
Google Analytics requires creating a Google Analytics account. An account has properties that represent individual collections of data. These properties have tracking IDs (also called property IDs) that identify them to Google Analytics. For example, an account might represent a company. One property in that account might represent the company's web site, while another property might represent the company's iOS app.
If you only have one app, the simplest scenario is to create a single Google Analytics account, and add a single property to that account. That property can represent your app.
A Google Analytics account can be created from analytics.google.com.
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.
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:
What would you like to track?
Websites and mobile apps implement Google Analytics differently. This document assumes a web app is being used. For mobile apps, see analytics for mobile applications.
Setting up your account
This is where you can set the name for your account, for example "PWA Training" or "Company X".
Setting up your property
A property must be associated with a website (for web apps). The website name can be whatever you want, for example "GA Code Lab Site" or "My New App". The website URL should be the URL where your app is hosted.
You can set an industry category to get benchmarking information later (in other words, to compare your app with other apps in the same industry). You can set your timezone here as well. You may also see data sharing options, but these are not required.
Once you have filled in your information, choose Get Tracking ID and agree to the terms and conditions to finish creating your account and its first property. This will take you to the tracking code page where you get the tracking ID and tracking snippet for your app.
Add analytics to your site
Once you have created an account, you need to add the tracking snippet to your app. You can find the tracking snippet with the following steps:
- Select the Admin tab.
- Under account, select your account (for example "PWA Training") from the dropdown list.
- Then under property, select your property (for example "GA Code Lab Site") from the dropdown list.
- Now choose Tracking Info, and then Tracking Code.
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>
Your tracking ID is embedded into your tracking snippet. This snippet needs to be embedded into every page that you want to track.
When a page with the snippet loads, the tracking snippet script is executed. The IIFE ( Immediately Invoked Function Expression) in the script does two things:
- Creates another
script
tag that starts asynchronously downloading analytics.js, the library that does all of the analytics work. - Initializes a global
ga
function, called the command queue.
The ga
command queue is the main interface for using analytics.js. The command queue stores commands (in order) until analytics.js has loaded. Once analytics.js has loaded, the commands are executed sequentially. This functionality ensures that analytics can begin independent of the loading time of analytics.js.
Commands are added by calling ga()
. The first argument passed is the command itself, which is a method of the analytics.js library. The remaining arguments are parameters for that method.
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
You can learn more about creating trackers in the documentation.
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". Since the tracker was created with your tracking ID, this data is sent to your account and property. You can learn more about sending data in the Google Analytics documentation.
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)
Debugging and development
Google Analytics offers the analytics.js library with a debug mode: analytics_debug.js. Using this version will log detailed messages to the console that break down each hit sent. It also logs warnings and errors for your tracking code. To use this version, replace analytics.js with analytics_debug.js (in all instances of your tracking snippet).
For more information
- Adding analytics.js to Your Site
- Google Reporting API v4
- Google Analytics Debugger
- Google Analytics Debugging
- Getting and Setting Tracker Data
Google Analytics dashboard
All of the data that is sent to Google Analytics can be viewed in the Google Analytics dashboard (the Google Analytics web interface). For example, overview data is available by selecting Audience and then Overview (shown below).
From the overview page you can see general information such as pageview records, bounce rate, ratio of new and returning visitor, and other statistics.
You can also see specific information like visitors' language, country, city, browser, operating system, service provider, screen resolution, and device.
Real time analytics
It's also possible to view analytics information in real time from the Real-Time tab. The Overview section is shown below:
If you are visiting your app in another tab or window, you should see yourself being tracked. The screen should look similar to this:
These are only the basic aspects of the Google Analytics dashboard. There is an extensive set of features and functionality.
For more information
Custom events
Google Analytics supports custom events that allow for fine-grain analysis of user behavior.
For example, the following code will send a custom event:
main.js
ga('send', {
hitType: 'event',
eventCategory: 'products',
eventAction: 'purchase',
eventLabel: 'Summer products launch'
});
Here the hit type is set to 'event' and values associated with the event are 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 allow us to deeply understand user interactions with our site.
Event data can also be viewed in the Google Analytics dashboard. Real-time events are found in the Real-Time tab under Events, and should look like the following:
Here you can see events as they are occurring. You can view past events in the Google Analytics dashboard by selecting Behavior, followed by Events and then Overview:
For more information
Analytics and service worker
Service workers do 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. You need to use the Measurement Protocol interface to send hits from the service worker.
This interface allows us to make HTTP requests to send hits, regardless of the execution context. This can be achieved by sending a URI containing your tracking ID and the custom event parameters (eventCategory
, eventAction
, and eventLabel
) along with some required parameters (version number, client ID, and hit type) to the API endpoint (https://www.google-analytics.com/collect). Let's look at an example using the Measurement Protocol interface to send hits related to push events in the service worker.
A helper script, analytics-helper.js has the following code:
analytics-helper.js
// Set this to your tracking ID
var trackingId = 'UA-XXXXXXXX-Y';
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);
});
}
The script starts by creating a variable with your tracking ID (replace UA-XXXXXXXX-Y
with your actual tracking ID). This ensures 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'
};
Again, the version number, client ID, tracking ID, and hit type parameters are required by the API. The eventCategory
, eventAction
, and eventLabel
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
});
This sends the hit with the Fetch API using a POST request. The body of the request is the hit data.
Now we can import the helper script functionality into a service worker by adding the following code to the service worker file:
sw.js
self.importScripts('path/to/analytics-helper.js');
Where path/to/analytics-helper.js
is the path to the analytics-helper.js file. Now we should be able to send custom events from the service worker by making calls to the sendAnalyticsEvent
function. For example, to send a custom "notification close" event, we could add code like this to the service worker file:
sw.js
self.addEventListener('notificationclose', function(event) {
event.waitUntil(
sendAnalyticsEvent('close', 'notification')
);
});
Observe that we have used event.waitUntil
to wrap an asynchronous operation. If unfamiliar, event.waitUntil
extends the life of an event until the asynchronous actions inside of it have completed. This ensures that the service worker will not be terminated pre-emptively while waiting for an asynchronous action to complete.
For more information
- WorkerGlobalScope.importScripts()
- Measurement Protocol Overview
- Simple Push Demo (includes Measurement Protocol example code)
Offline analytics
With the help of service workers, analytics data can be stored when users are offline and sent at a later time when they have reconnected based on an npm package.
Install the package with the following command-line command:
npm install sw-offline-google-analytics
This imports the node module.
In your service worker file, add the following code:
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. This will likely look something like:
sw.js
node_modules/sw-offline-google-analytics/offline-google-analytics-import.js
We import and initialize the offline-google-analytics-import.js library. 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 first 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.
You can test this by simulating offline behavior, and then firing hit events. You will see an error in the console since you are offline and can't make requests to Google Analytics servers. Then check IndexedDB. Open offline-google-analytics. You should see URLs cached in urls (you may need to click the refresh icon inside the indexedDB interface). These are the stored hits.
Now disable offline mode, and refresh the page. Check IndexedDB again, and observe that the URL is no longer cached (and has been sent to analytics servers).