Google Analytics server-side event tracking
This post describes how to consistently track business events (sign-ups, sign-ins, purchases) using Google Analytics event tracking. Tracking events with Google Analytics greatly eases goal and funnel analysis and makes figuring out stuff like what traffic sources yield the most sign-ups easy. The sample code below uses the ASP.NET MVC framework, but the technique is applicable to web applications written in any framework.
Problem
Imagine you have a standard Google Analytics tracking setup:
<script type="text/javascript">
var _gaq = _gaq || [];
_gaq.push(['_setAccount', 'ACCOUNT-ID']);
_gaq.push(['_trackPageview']);
(function () {
var ga = document.createElement('script'); ga.type = 'text/javascript'; ga.async = true;
ga.src = ('https:' == document.location.protocol ? 'https://ssl' : 'http://www') +
'.google-analytics.com/ga.js';
var s = document.getElementsByTagName('script')[0]; s.parentNode.insertBefore(ga, s);
})();
</script>
And imagine that and you want to do funnel analysis on visitors that sign up for your service. People seem to currently go about this in one of two ways:
- Add a
/thankyou.html
page, send users there after sign-up and configure that as a "Goal URL" in Google Analytics - Add an
onClick
event handler to the form button that users click when signing up and call_trackEvent
from the event handler
Neither of these options are attractive. Number 1 forces you to redirect users to a particular page to accurately track them even though redirecting to the front page and displaying a message might be a more natural flow. If you don’t send users to a particular page and redirect them to the front page after registering the sign up, you won’t be able to tell from Google Analytics whether the user arrived there on his own or after being redirected after signup.
Option number 2 is bad because clicking the sign-up button does not necessarily mean the visitor actually gets to sign up. If there's a validation error, the visitor will fill out and click the submit button again, registering an extra sign-up. If the user doesn't fix the validation error and gives up, a signup that never actually happened will be tracked in Google Analytics.
Solution
This problem is easily fixed: Simply track the event on the next page (whatever it is) before that page view itself is tracked. We’ll track events using the Google Analytics Event Tracking API. While nominally a client side api to track stuff like video plays and AJAX requests, we’re going to be using it to track server-side events by conditionally outputting Javascript calling the _trackEvent method:
_trackEvent(category, action, opt_label, opt_value, opt_noninteraction)
In the AppHarbor ASP.NET MVC codebase, we track server-side events using a TrackEvent
method in our ControllerBase class:
protected void TrackEvent(string category, string action, string label = null,
decimal? value = null, bool? nonInteraction = null)
{
const string tempDataKey = "GoogleAnalyticsEvents";
var intValue = Convert.ToInt32(value);
var events = (List<GoogleAnalyticsEventViewModel>)TempData[tempDataKey]
?? new List<GoogleAnalyticsEventViewModel>();
events.Add(new GoogleAnalyticsEventViewModel
{
Category = category,
Action = action,
Label = label,
Value = intValue,
NonInteraction = nonInteraction,
});
TempData[tempDataKey] = events;
}
The code above works with the CookieTempData provider mentioned in a previous blog post. TrackEvent
is invoked from controllers:
if (ModelState.IsValid)
{
TrackEvent("User", "UserCreated");
SetSuccessMessage("Your account has been created");
}
The events are used to generate the required _trackEvent
calls:
<script type="text/javascript">
var _gaq = _gaq || [];
_gaq.push(['_setAccount', 'ACCOUNT-ID']);
@{
var googleAnalyticsEvents =
(List<GoogleAnalyticsEventViewModel>)TempData["GoogleAnalyticsEvents"];
}
@if (googleAnalyticsEvents != null) {
foreach (var googleAnalyticsEvent in googleAnalyticsEvents) {
var label = googleAnalyticsEvent.Label == null ? "undefined" :
string.Format("'{0}'", Json.Encode(googleAnalyticsEvent.Label));
var value = googleAnalyticsEvent.Value == null ? "undefined" :
string.Format("{0}", googleAnalyticsEvent.Value);
var nonInteraction = googleAnalyticsEvent.NonInteraction == null ? "false" :
string.Format("{0}", googleAnalyticsEvent.NonInteraction).ToLower();
@:_gaq.push(['_trackEvent', '@googleAnalyticsEvent.Category',
'@googleAnalyticsEvent.Action', @Html.Raw(label), @value, @nonInteraction]);
}
}
_gaq.push(['_trackPageview']);
(function () {
var ga = document.createElement('script'); ga.type = 'text/javascript'; ga.async = true;
ga.src = ('https:' == document.location.protocol ? 'https://ssl' : 'http://www') +
'.google-analytics.com/ga.js';
var s = document.getElementsByTagName('script')[0]; s.parentNode.insertBefore(ga, s);
})();
</script>
Note that events are tracked before the pageview of the current page itself is tracked with _gaq.push(['_trackPageview']);
. This ensures correct sequencing of events and pageviews in Google Analytics.
The code outlined in this post uses ASP.NET MVC, but the technique is applicable to apps written in any decent web application framework.
The screenshot at the top of the post shows the signup goal flow for AppHarbor by traffic source. Information like this lets us figure out stuff like what traffic sources convert well to signups (and purchases) and effectively prioritize our marketing and outreach efforts. All thanks to accurate tracking of server side events with Google Analytics.