The Internet is bulging with information about how to set up Google Analytics to optimize and monitor marketing and content pages. But how about using Google Analytics to measure user behavior in a SaaS app? Not so much.

Here at Pollen, we developed a little widget to measure user engagement via Google Analytics. In the process of doing that, we’ve also come upon several revelations and best practices for monitoring a SaaS application. What we’re trying to do here is understand user behavior and find ways to improve the usability of the application. This is different from the typical use of Google Analytics. Typically, you’ll find Google Analytics used to monitor inbound lead funnels, not monitor already-registered users.

Note: there are two very different versions of Google Analytics

There are two versions of Google Analytics: “Classic Analytics” and “Universal Analytics“. In this article, we’ll provide code examples in both styles by calling Classic Analytics “ga.js” and Universal Analytics “analytics.js”. The traditional Google Analytics code most people think of first is Classic Analytics so those code samples might feel the most familiar. Also, Universal Analytics is still in beta, so some might not want to use it on a production website.

However, Universal Analytics has one important advantage over Classic Analytics: it support custom dimensions. If possible, I recommend using Universal Analytics. Now, on to the recommendations…

First, use a different “property” for tracking your application vs. your marketing pages

Marketing pages are meant to convert leads. Applications are meant to solve user problems. These are two totally different purposes and if we try to track them under the same Google Analytics tracking id we’re going to create a real mess for ourselves. So, our first suggestion is to use a different tracking id for your application than you do for your marketing site. This way, you won’t be competing with your marketing folks over things like custom variables, dimensions and metrics.

It’s kind of difficult to find the right page for creating a new property in Google Analytics, so here are some instructions. First, go to the Account List page in Google Analytics:

Then click the Admin button in the upper right corner and select Create New Property in the middle drop down:

This brings you to a page where you have to select Classic or Universal Analytics. I recommend choosing Universal Analytics unless you have a good reason not to.

At the bottom of the page, you might want to select “https” for the domain name just for completeness, though frankly I don’t know if it matters (answers welcome in the comments!). You might also want to name the property “something.com Application” so you can easily tell it apart from the marketing property with the same URL.

Now you should have a different tracking code to embed in your application vs. your marketing pages (e.g. “UA-16297000-2″).

Now, make sure you track your Ajax “page views”.

Certainly, you’re writing a nice, modern, Ajax-y web application, right? Well, when using Google Analytics, one painful thing about using Ajax is that your Ajax “page views” are no longer tracked. For single page web applications, this makes GA essentially useless unless you explicitly create virtual page views for each Ajax request. Luckily, GA makes this pretty easy. Assuming you set up your initial Google Analytics page load like this:

(ga.js example)

      var _gaq = _gaq || [];
      _gaq.push(['_setAccount', 'UA-16297xxx-4']);
      _gaq.push(['_setDomainName', 'featureviz.com']);
      _gaq.push(['_trackPageview']);

      (function() {
        var gac = document.createElement('script'); gac.type = 'text/javascript'; gac.async = true;
        gac.src = ('https:' == document.location.protocol ? 'https://ssl' : 'http://www') + '.google-analytics.com/ga.js';
        var s = document.getElementsByTagName('script')[0]; s.parentNode.insertBefore(gac, s);
      })();

(analytics.js example)

       (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','//www.google-analytics.com/analytics.js','ga');

       ga('create', 'UA-16297xxx-6', 'featureviz.com');
       ga('send', 'pageview');

Then all you have to do is add one line of Javascript just before your Ajax call, like this:

(ga.js example)

$.ajax({
  url: "/some-ajax-url",
  context: document.body,
  success: function(){
    _gaq.push(['_trackPageview', '/some-ajax-url']);
    ...do something else useful...
  }
});

(analytics.js example)

$.ajax({
  url: "/some-ajax-url",
  context: document.body,
  success: function(){
    ga('send','pageview',"/some-ajax-url");
    ...do something else useful...
  }
});

But we can do even better than that.

Instead of having to explicitly invoke page views in each Ajax call, you might want to just add a generic hook in your over-arching application template to automatically create these page views before each Ajax call, like this (assuming you’re using jQuery):

(ga.js example)

    $(document).ajaxSend(function(event, xhr, options) {  
          _gaq.push(['_trackPageview',options.url]);
     });

(analytics.js example)

    $(document).ajaxSend(function(event, xhr, options) {  
          ga('send','pageview',options.url);
     });

This way, every Ajax call will be tracked as a page view automatically, and you won’t have to worry about remembering it.

Now, track something besides page views using Google Analytics events.

Google Analytics events are an under utilized aspect of GA. Events are meant to be more granular than page views. You can use events to track every button click, mouseover, video frame, and popup in your entire application if you want to.

The simplest way to do this is to register a callback on everything in one shot. For example, the below code registers a callback in your application any time someone clicks an anchor tag, even if clicking that tag doesn’t result in a page view. This is fairly typical in applications, were the HTML contains an anchor tag that is styled to look like a button with some CSS and the anchor doesn’t actually navigate to a page but instead manipulates the application in some way.

(ga.js example)

$('a').click(function() {
      _gaq.push(['_trackEvent','Button',$(this).prop('href'),null]);
});


(analytics.js example)

$('a').click(function() {
      ga('send','event','Button',$(this).prop('href'),null);
});


This will ensure you get at least minimal data on what your users are clicking. For “important” pieces of functionality, you can explicitly add another event with a better name. For example, in our Ruby on Rails application, we use an event whenever a user signs into the application. Users might sign in using any number of mechanisms, so we make sure to use the same event name and action for all of them so that they get aggregated together.

(ga.js example)

<%= f.submit "Sign in" , :onClick=>"_gaq.push(['_trackEvent','Account','SignIn',null]);"%>

(analytics.js example)

<%= f.submit "Sign in" , :onClick=>"ga('send','event','Account','SignIn',null]);"%>

Here are some other suggested events to put in your application:

  • Sign In
  • Sign Up
  • Unsubscribe/Cancel
  • Change Plan
  • Create [Invoice|Proposal|Contact|Whatever your app does]
  • Upload [Image|Document|Video]
  • Use of a keyboard shortcut
  • View [Image|Document|Graph]
  • Download [Image|Document|Video]
  • Interact with (for social apps)

Also log an event whenever there’s an error.

Don’t forget to log events whenever there’s an error, especially a client-side error. In fact, if you track nothing else, you should track errors so that you know which users are having a frustrating experience and which are not. For example, the following code, put in your header, will log an event for all Ajax errors:

(ga.js example)

    $(document).ajaxError(function(event, xhr, options, exception) {  
          _gaq.push(['_trackEvent','Error','Ajax',options.url]);
     });


(analytics.js example)

    $(document).ajaxError(function(event, xhr, options, exception) {  
          ga('send','event','Error','Ajax',options.url);
     });


Use dimensions to add some context to the user experience.

Google Analytics dimensions are a great way to add context to all your page view and event data. For example, knowing how many error events users are seeing on average is powerful. But knowing how many errors users see on average split out by how much they’re paying you can be simply mind blowing.

So, think about the various ways you might want to slice and dice your page view and event data. Here are some suggestions:

  • Plan level (e.g. “Basic”, “Pro”, “Enterprise”)
  • Acquisition channel where you got this user in the first place (CPC vs guest post)
  • Year and month when user first signed up
  • Acquisition cost of customer
  • Arbitrary segments you calculate yourself (e.g. “VIP”, “unwashed masses”)
  • Some bucket of engagement level that you calculate from their past activity (e.g. “Heavy User”, “Casual User”)
  • Total tenure of user (e.g. “> 6 months”, “1 – 6 months”, “< 1 month")
  • Login status (“Logged in”, “Not logged in”)

Dimensions should be discrete, relatively unchanging values rather than continuous ones. For example, having “number of seconds since last login” is not a useful dimension because every user will have a slightly different value, and that value will change even for the same user each time they come back. But… having “time bucket since last login” instead where the buckets are “today”, “< 48 hrs","< 3 weeks", and "> 3 weeks” might be useful and will bucket your users into segments that might have behavior that differs in interesting ways.
To set up dimensions, you have to create them first in Google Analytics. Custom dimensions are only available if you are using Universal Analytics (analytics.js) and not if you are using classic Google Analytics (ga.js).

Once a custom dimension is configured in Google, you have to set the right values for that dimension in your page tracking code. Exactly how you do this depends on your application, but here’s an example for a typical Ruby on Rails application (all in the ‘application.html.erb’ file):

(analytics.js example)

       <% if current_user.present? %>
          ga('set', 'dimension1', '<%=current_user.plan.name%>');//this is the user's plan
     <% end %>

Be aware that custom dimensions do not appear in the normal Google Analytics reports. They only appear in advanced segments and in custom reports. But creating custom reports is not difficult, just click the “Customization” tab in the orange bar between “Reporting” and “Admin”. Once created, custom reports can even be added to your Google Analytics dashboard.

Use custom variables if you are using Classic Analytics.

If you are stuck with Classic Analytics (ga.js) then Google Analytics custom variables are another good way to add context to your page view and event data. Unlike dimensions, though, you’re much more limited how many different custom variables you can use. In the free version of Google Analytics, you’re limited to five different custom variables, so make sure you think through them carefully before using them all up.

Here is an example from our own application where we track the user’s plan as a custom variable:

<% if current_user.present? %>
     _gaq.push(['_setCustomVar',4,'FeatureVizUser','<%=current_user.plan.name%>',2]);
<% end %>


Custom variables can be set on the visitor, session, and page level. In most cases, you’ll probably want the session level (“2″).

Track the user id as a dimension.

I’m about to suggest you ride a fine line. It’s possible to track individual user activity in Google Analytics by simply adding the user’s id as a dimension. Doing this is extremely useful for your support people as well as for identifying segments of users who use your application in different ways. But it’s tricky for two reasons: 1) there are some obvious privacy concerns, and 2) if you do it wrong, you’ll be violating the Google terms of service.

First, let’s address the privacy concerns. If your URLs are structured in such a way that they contain inherently sensitive information then you might not want to track users individually. For example, if you have an application that shows the results of blood tests for sexually transmitted diseases and one of your URLs is “/diagnosis/syphillis/makeAnAppointment” then you probably should not track user ids against your requests. But, most applications are not like that. Most applications have URLs that don’t really give much away and aren’t a problem. Only you, the application owner, can know for sure, so it’s really a judgement call on your part.

Let’s assume you’ve decided your URLs have nothing sensitive in them. The second hurdle is the Google Analytics terms of service. In short, they say that you may not put anything in a custom dimension that allows Google to identify an individual in the real world. This is called “personally identifiable information.” Personally identifiable information is things like names, email addresses, phone numbers, etc. Under no circumstances should you put personally identifiable information in a custom variable or dimension.

But, that doesn’t mean you’re totally stuck. Because we’re talking about an actual application here, presumably each of your users is represented by a record in a database somewhere. And that database has a primary key, right? And that primary key is totally arbitrary, so there’s no way Google would know who the real-world person is behind that primary key. So, put that primary key in a dimension and track away.

Here is an example from our own application, which is a Ruby on Rails application that uses Devise for authentication:
(analytics.js example)

       ga('create', 'UA-16297xxx-6', 'featureviz.com');
       <% if current_user.present? %>
          ga('set', 'dimension19', '<%=current_user.id%>');//this is the user's id
       <% end %>
       ga('send', 'pageview');


If you add the user id as a custom dimension or variable, you can track page views down to individual users (especially with tools like FeatureViz).
Having the user id isn’t terribly useful when just using the Google Analytics dashboard itself. But it’s hugely useful if you write some quick scripts or admin dashboards that query the Google Analytics API. If you do that, you can build yourself a little admin screen to see everything a user has done in your application. This is tremendously helpful when, for example, providing support. Incidentally, we can build that admin screen for you if you don’t have the time.

Lastly, remember that Google Analytics uses sampling liberally.

Google Analytics has a dizzying number of hidden limitations on the granularity of data it collects. This means that once you tick over a certain number of events, page views, dimensions or custom variable values, GA starts dropping data on the floor and doing statistical sampling instead. This sampling can happen both on the collection side when events and page views are sent to Google as well as on the reporting side when you’re looking at data. Even if you don’t have a high-traffic site, you might easily run across some of these limitations without realizing it.

This statistical sampling isn’t a big deal on a marketing site where you’re mostly interested in aggregate numbers and conversions and whatnot. But as soon as you’re talking about tracking for individual button clicks for individual users you might start seeing some puzzling results. For example, if you have a workflow where users click Button A, then Button B, then Button C, you might see fewer click events for Button B than for Button C. Don’t panic if this happens. It doesn’t (necessarily) mean your application punched a hole in the space-time continuum. It just means you’ve been a victim of statistical sampling.

There isn’t a good way to get around this sampling. You just need to be aware of it so you don’t freak out.

Here are some of the limitations that trigger statistical sampling:

  • When reporting, more than 250k values in the date range you’re reporting on
  • When reporting, more than 75k rows in a single table for a single day
  • When reporting, more than 100k visits for a flow visualization
  • When reporting, more than 10mm hits inbound per month
  • For ga.js collection, each web property starts with 10 hits that are replenished at a rate of 1 hit per second
  • For analytics.js collection, each web property starts with 20 hits that are replenished at a rate of 2 hit per second
  • Both libraries have a 500 hit hard limit for a single session
  • For collection, there’s a 200k hits per visitor per day limit for Universal Analytics

So, now you should have “poor man’s” application monitoring with free Google Analytics.

If you’ve followed all of the above, you’re part way towards getting user monitoring like the Big Boys get, without paying Big Boy prices. Have you followed these steps and discovered something interesting about your the way users use your product? We'd love to hear from you.