Try our conversational search powered by Generative AI!

Steve Celius
Dec 10, 2014
  13036
(5 votes)

Google Analytics Tracking and Customization

The new version of the Google Analytics Add-on for EPiServer has some exiting new features that allows you better control over how and what you track with Google Analytics. Also, with the new possibility to install Add-ons through Visual Studio, the install is quick and easy to do, and makes it easy to get started customizing the tracking script.

Installing the Google Analytics Add-on as a Nuget package

Install Google Analytics Add-on as a Nuget package from the EPiServer Nuget feed

install-package EPiServer.GoogleAnalytics

Configure it to connect to your Google Analytics account from Admin Mode:

Image configure-ga-admin.png

Select the tracking method that your Google Analytics account supports. Note! Most of the code examples here require Universal Analytics (UA):
Image configure-ga-admin-select-script-option.png

If you want to track Ecommerce events, make sure your UA account has Ecommerce tracking turned on in the Google Analytics administration view. If you want to track Enhanced Ecommerce events, also enable “Enhanced Ecommerce Reporting”:

Image configure-ga-ecommerce-setup.png

Note! Do not mix Ecommerce and Enhanced Ecommerce tracking on your pages.

Check that the Add-on is installed and working by looking at the source of any of your pages:

Image ga-see-page-script.png

You can also install the Google Analytics Debugger extension for Chrome:
https://chrome.google.com/webstore/detail/google-analytics-debugger/jnkmfdileelhofjcijamephohjechhna


It will show debug information in the Chrome console:
Image ga-debugging-tools.png

More information about troubleshooting Google Analytics: https://developers.google.com/analytics/resources/articles/gaTrackingTroubleshooting

Note! If you plan to customize the tracking, you need to install the add-on through Visual Studio. It will add the necessary reference to the Add-on assembly. You should not add a reference to an assembly installed as an add-on through the Add-on UI as it would break your site if the add-on is uninstalled. 

If you have worked with previous versions of the add-on, you’ll now see that the new version has fewer dependencies, which makes for a cleaner install.

Support for Universal Analytics

Google has launched a major update to the Google Analytics offering called Universal Analytics. It is a better tracking script, extensible, easier to use, and most importantly, allows more types of tracking, especially when it comes to Ecommerce.

I recommend reading up on the new possibilities, there are numerous blog posts that shows how and why and can really inspire your imagination. The official documentation is also good reading:
https://developers.google.com/analytics/devguides/collection/analyticsjs/   

Unless you have good reasons not too, you should upgrade your site to use Universal Tracking, and use the new script. If you haven’t customized how you track using the previous version of the Add-on, you can install the new one, and change the tracking type:
Image configure-ga-admin-select-script-option.png

Make sure you also upgrade your current analytics account in the administration interface for Google Analytics.

Ecommerce Tracking

If the basic page view tracking is not enough, you typically want to extend with more tracking instructions. For an example, if you’re running an EPiServer Commerce site, the new Enhanced Ecommerce tracking that is part of Universal Analytics is a must.

The “old” tracking only allowed tracking purchases, and would show you what products were most popular among people actually buying them. The new Enhanced Ecommerce allow you to track both impressions, detail views, product clicks, adding to the cart, and the whole checkout process, giving you an easy way to see where people fall off.

Image ga-report-shopping.png

With all this information about product views, lists, clicks etc. you’ll get a whole new view of how your site is actually performing.

Note! The Add-on will help you add this tracking, but it wont do it for you. As every site is different, it would be hard to add this tracking automatically. Don’t worry, I’ll show you how.

Tracking Basics

The online documentation for the Google Analytics Add-on explains how the add-on works pretty well, so I will not repeat that here. I recommend you browse through that documentation if you have trouble following what I do in the code below. 

If you want to add more script lines to the tracking script before the pageview is sent off to Google, you need to work with AnalyticsInteraction classes. This is done through extensions to HttpContext and HttpContextBase (for MVC).

Web Forms:

Page.Context.AddAnalyticsEvent("button", "click", "nav buttons", 4, clearWhenContextChanged: true);

MVC Controller:

ControllerContext.HttpContext.AddAnalyticsEvent("button", "click", "nav buttons", 4, clearWhenContextChanged: true);

The heavy lifting is done in one of the classes inheriting from GASyntax, like the UniversalSyntax class which is responsible for retrieving all registered AnalyticsInteraction objects for a request and render them correctly.

This is also the class you need to change if you want to take full control over the script rendering.

Creating Your Own Syntax Implementation

The Commerce Starter Kit has quite a lot of customized tracking, and has it’s own syntax class. We register it with the service locator like this:

[InitializableModule]
[ModuleDependency(typeof(EPiServer.Web.InitializationModule))]
public class InitializeAnalytics : IConfigurableModule
{
   public void Initialize(InitializationEngine context)
   {
   }
 
   public void Preload(string[] parameters) { }
 
   public void ConfigureContainer(ServiceConfigurationContext context)
   {
       // Our own syntax implementation
       context.Container.Configure(c => c.For<UniversalSyntax>().Use<UniversalSyntaxEx>());
   }
 
   public void Uninitialize(InitializationEngine context)
   {
   }
}

Our enhanced UniversalSyntaxEx looks like this: 

public class UniversalSyntaxEx : UniversalSyntax
{
   // standard script
   protected string _gaScript =        "(function(i,s,o,g,r,a,m){i['GoogleAnalyticsObject']=r;i[r]=i[r]||function(){\r\n(i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date();a=s.createElement(o),\r\nm=s.getElementsByTagName(o)[0];a.async=1;a.src=g;m.parentNode.insertBefore(a,m)\r\n})(window,document,'script','//www.google-analytics.com/analytics.js','ga');";
   
   
   public override string BuildTrackingScript(ScriptBuilderContext appenderContext, SiteTrackerSettings siteSettings,
       out bool requiresScriptReference)
   {
       requiresScriptReference = false;
       if (siteSettings == null)
       {
           return null;
       }
 
       StringBuilder stringBuilder = new StringBuilder();
       stringBuilder.AppendLine("/* Begin GA Script */");
       stringBuilder.AppendLine(_gaScript);
 
       stringBuilder.AppendLine(string.Format("ga('create', '{0}', 'auto');", siteSettings.TrackingId));
 
       stringBuilder.AppendLine("// Extended Tracking");
       stringBuilder.AppendLine(AppendExtendedTracking(siteSettings, ref requiresScriptReference));
 
       stringBuilder.AppendLine("// Plugin Script");
       stringBuilder.AppendLine(this.GetPluginScript());
 
       if (siteSettings.TrackAuthors && !string.IsNullOrEmpty(appenderContext.Author))
       {
           stringBuilder.AppendLine("// Custom Author Tracking");
           stringBuilder.AppendLine(this.GetCustomDimension("Author", CustomVariables.AuthorVariable, appenderContext.Author));
       }
       
       ContentReference contentReference = new ContentReference(appenderContext.PageId);
       
       ICollection<AnalyticsInteraction> interactions =  EPiServer.GoogleAnalytics.Helpers.Extensions.GetInteractions(appenderContext.InteractionStore);
 
       // This is where the interesting stuff happens
       // All custom interactions are added here
       stringBuilder.AppendLine("// Begin Interactions");
       foreach (AnalyticsInteraction interaction in interactions)
       {
           // Skip any interactions that are tied to a specific page
           if (ContentReference.IsNullOrEmpty(interaction.ContentLink) == false &&
               contentReference.Equals(interaction.ContentLink) == false)
           {
               continue;
           }
           stringBuilder.AppendLine(interaction.GetUAScript());
       }
       stringBuilder.AppendLine("// End Interactions");
       
       // Clear any interactions that should not persist
       // across a request
       this.ClearRedundantInteractions(interactions);
 
       stringBuilder.AppendLine("ga('send', 'pageview');");
       stringBuilder.AppendLine("/* End GA Script */");
       return stringBuilder.ToString();
   }
 
   protected string AppendExtendedTracking(SiteTrackerSettings siteSettings, ref bool requiresScriptReference)
   {
       Dictionary<string, object> extendedTracking = this.GetExtendedTracking(siteSettings);
       requiresScriptReference = extendedTracking.Count > 0;
       return this.SerializeTrackerSettings(extendedTracking, siteSettings.TrackingScriptOption.ToString());
   }
 
   private string GetCustomDimension(string dimensionName, string dimensionIndex, string value)
   {
       return string.Format("ga('set', '{0}{1}', '{2}');", dimensionName, dimensionIndex, value);
   }
}

This class will render the script, any plugin, the interactions and then send it off to Google.

Personally, I find that adding AnalyticsInteraction objects using the standard exension methods is a bit tedious. I’d rather have full control over the script instead of using the dictionary, so I created my own AnalyticsInteraction variation:

public class UniversalAnalyticsInteraction  : AnalyticsInteraction
{
   // We're not using these, but we need the InteractionKey to be unique
   // Note! We always mark this for deletion after is has been rendered
   // to the page.
   public UniversalAnalyticsInteraction() :
       base("overridden-interaction", true, Guid.NewGuid().ToString())
   {
   }
 
   public UniversalAnalyticsInteraction(string script)
       : base("overridden-interaction", true, Guid.NewGuid().ToString())
   {
       Script = script;
   }
   /// <summary>
   /// The javascript to add to the tracking script
   /// </summary>
   /// <remarks>
   /// The javascript must be complete and valid, or it might break
   /// the whole analytics tracking feature.
   /// </remarks>
   public string Script { get; set; }
 
   public override string GetUAScript()
   {
       return Script;
   }
}

If you know how the script should look like, you can use this one, and just set the script directly. You can add it like this:

protected void AddInteraction(HttpContextBase context, string script)
{
   var interactions = EPiServer.GoogleAnalytics.Helpers.Extensions.GetInteractions(context);
   UniversalAnalyticsInteraction interaction = new UniversalAnalyticsInteraction(script);
   interactions.Add(interaction);
}

If we look at the example from the Google Analytics documentation for measuring a product detail view, it could look like this (from a product detail MVC controller): 

HttpContextBase context = ControllerContext.HttpContext;
// The plugin is required for tracking enhanced ecommerce
AddInteraction(context, "ga('require', 'ec');"));
 
// This would come from your commerce product
AddInteraction(context, "ga('ec:addProduct', {'id': 'P12345', 'name': 'Android Warhol T-Shirt', 'category': 'Apparel', 'brand': 'Google', 'variant': 'black'});"));
 
// This ties the product to the detail action
AddInteraction(context, "ga('ec:setAction', 'detail');");

Note! Since the add-on will end the script with a "send pageview" method, you do not need to include that yourself.

If you plan to use Enhanced Ecommerce actions on most pages on your site, moving the ga('require', 'ec'); script to your own IPluginScript class is a good idea. Add this to your IInitializableModule:

// Add enhanced ecommerce to all pages
context.Container.Configure(c => c.For<IPluginScript>().Use<RequireEnhancedCommercePlugin>());

This is how the RequireEnhancedCommercePlugin look like in the Commerce Starter Kit:

public class RequireEnhancedCommercePlugin : IPluginScript
{
   public string GetScript()
   {
       ICurrentMarket currentMarket = ServiceLocator.Current.GetInstance<ICurrentMarket>();
       IMarket market = currentMarket.GetCurrentMarket();
 
       string script = "ga('require', 'ec');\n";
       script = script + string.Format("ga('set', '&cu', '{0}');", market.DefaultCurrency);
       return script;
   }
}

It adds a require statement for the ec plugin. Since the starter kit supports multiple markets it also registers the current currency so any prices or conversions will be tracked correctly in Google Analytics.

Enhanced Ecommerce Helper Library

Getting all the script valid can be a bit cumbersome, there are different field objects for different actions, and some fields are mandatory, others not. To help you out I've create a small helper project to make this easier. You can find it on Github: https://github.com/EPiServerNorway/UniversalTrackingHelper

Adding a require statement for the ec plugin would be done like this with the helper library:

using EPiCode.GoogleAnalyticsTracking;
...
Tracking tracking = new Tracking();
// Get the script
string script = tracking.Require("ec");
 
// Add it as an interaction (as shown earlier)
AddInteraction(context, script);

See the GoogleAnalyticsTracking class in the Commerce Starter Kit for more examples on how to use the helper library.

Session State storage for AnalyticsInteractions

The analytics interactions are by default stored in Session state, and you can -  in theory - add an interaction that spans page requests. I would advise against it as is hard to predict the order of the interactions.

IPluginScript supports only one registration

It is important to be aware that only on IPluginScript should be registered at any one time. Only one will be used, and if you have several registered classes with a ServiceConfiguration attribute, which one is selected cannot be predicted. Make sure you register your own through an IInitializableModule.

Implementation of Google Analytics Tracking in the Commerce Starter Kit

The Commerce Starter Kit uses Enhanced Ecommerce tracking, and the current implementation contains:

  • Tracking regular page views
  • Tracking the product detail view
  • Tracking product impressions from the Configurable Wine Search List
  • Reviewing the cart (with products)
  • Checkout page (with products)
  • Payment page (with products)
  • Receipt page with purchase command tracking order total and more

Still remaining:

  • Add to cart command (in lists and details view)
  • Related products (impressions) on product detail page
  • Returns (from Commerce Manager)
  • Category view (list pages)
  • Click product link
  • Add payment method as a checkout option

Interesting Analytics Data

When you start tracking ecommerce, you'll quickly see that you're getting a lot of useful data to base your decisions on. Here are some simple examples:

Checkout Funnel

See Checkout funnel for device categories to find out if your checkout works across devices
Image ga-report-shopping.png

Example: if smartphones are not able to purchase due to an error or incompatible payment provider you should see a high abandonment rate on the Checkout Behaviour: 

Image ga-report-abandonment.png

Product List Performance

The start page is important, do you get click-through on all products on the start page, or should you change some of them for something else?

Are people just browsing and not adding anything to the cart? Compare timespans with and without campaigns with discounts, are your customers motivated by price. Is your Cart-to-Detail rate low? How good is the content on your detail pages? Are people seeing what they need in order convert?

Image ga-report-product-performance.png

Remember, statistics can be misleading, this also applies to your tracking, and if you've got a bug in your tracking code you could be looking at some really strange results. Question your data.

When you have started tracking, you should also think about defining goals and actions in order to meet your goals, and then see how this all ties together. Are your customers behaving the way you think they are on your site? Maybe you need to change how you work, present product information, run campaigns, discount products etc.

And then see conversion rate increase. All with the new Google Analytics add-on for EPiServer. Happy tracking!

Dec 10, 2014

Comments

Dec 9, 2014 02:48 PM

Great blog Steve!

Thach
Thach Dec 16, 2014 03:39 AM

Thank you Steve, you really make the GA addon shine with this blog post!

Marija Jemuovic
Marija Jemuovic Dec 26, 2014 03:56 PM

Thx Steve! I can't seem to install google analytics due to GA nuget package that requires older version of EPiServer.Framework.
Updating 'EPiServer.Framework 7.15.0' to 'EPiServer.Framework 7.13.2' failed. Unable to find a version of
'EPiServer.Packaging' that is compatible with 'EPiServer.Framework 7.13.2'.

Do you have any info on when a new one will be out or perhaps this is a bug in the GA, why does it not work with newer versions, shouldn't it be built for the lowest it works on, but still work on the new ones?

Here, it's written that it requires >7.5 and < =8 http://nuget.episerver.com/en/OtherPages/Package/?packageId=EPiServer.GoogleAnalytics

Johan Kronberg
Johan Kronberg Jan 28, 2015 05:31 PM

Just a heads up: If tracking is done with Universal Analytics through a Google Tag Manager container most code in this post won't work.
It's still pretty easy to get the Enhanced E-com Tracking working though but you need to build and push the JSON according to your Tag Manager setup instead of calling ga().

Another thing... Is the plugin (still) connecting to GA's API and using that data for something?

Jan 28, 2015 09:28 PM

Thanks Johan, good point. You still need to render your data layer, and this small lib can help with that: https://github.com/EPiServerNorway/UniversalTrackingHelper

The gadgets (dashboard and page) connects to the GA API (though the need for the Google C# libs is gone).

Johan Kronberg
Johan Kronberg Mar 15, 2015 07:54 PM

OK, thanks for the info! Also I put up a blog post on strategies when using GTM: http://krompaco.nu/2015/03/google-analytics-enhanced-ecommerce-features-with-tag-manager/

Please login to comment.
Latest blogs
Optimizely and the never-ending story of the missing globe!

I've worked with Optimizely CMS for 14 years, and there are two things I'm obsessed with: Link validation and the globe that keeps disappearing on...

Tomas Hensrud Gulla | Apr 18, 2024 | Syndicated blog

Visitor Groups Usage Report For Optimizely CMS 12

This add-on offers detailed information on how visitor groups are used and how effective they are within Optimizely CMS. Editors can monitor and...

Adnan Zameer | Apr 18, 2024 | Syndicated blog

Azure AI Language – Abstractive Summarisation in Optimizely CMS

In this article, I show how the abstraction summarisation feature provided by the Azure AI Language platform, can be used within Optimizely CMS to...

Anil Patel | Apr 18, 2024 | Syndicated blog

Fix your Search & Navigation (Find) indexing job, please

Once upon a time, a colleague asked me to look into a customer database with weird spikes in database log usage. (You might start to wonder why I a...

Quan Mai | Apr 17, 2024 | Syndicated blog

The A/A Test: What You Need to Know

Sure, we all know what an A/B test can do. But what is an A/A test? How is it different? With an A/B test, we know that we can take a webpage (our...

Lindsey Rogers | Apr 15, 2024

.Net Core Timezone ID's Windows vs Linux

Hey all, First post here and I would like to talk about Timezone ID's and How Windows and Linux systems use different IDs. We currently run a .NET...

sheider | Apr 15, 2024