First of all, I’m sorry about the title. I should really stop throwing the word “simple” around, since people always tell me that the stuff I claim to be easy and straightforward is rarely so. But since this is my blog, I reserve the right to use whatever stupid and misleading terminology I want. I maintain that what follows IS quite simple, especially when considering the amount of complexity it reduces in your Universal Analytics setup.

Next, I want to direct your gaze to my latest Keynote / PowerPoint creation:

This piece of art is called “…while waiting for the cosmos”. Note the enigmatic ellipsis in the beginning, the lack of capitalization, and the fact that the title has nothing to do with the image. Yes, friend. This is post-post-modernism at its best!

So, I guess my amazing art kind of gave away what I want to show to you in this article. In short, it’s a simple little plugin I’ve been using in projects I work on, which duplicates all hits sent to a Google Analytics property. These duplicated hits are sent to another Google Analytics property, whose property ID you specify when initiating the plugin.

This tutorial uses a Universal Analytics plugin (d’oh), which, in turn, utilizies the Tasks API.

Before I proceed, I want to direct you to David Vallejo’s blog, where he and a bunch of other people are working together on an open-source project which does similar stuff, but on a WAY more configurable level. The plugin I’m about to walk you through will simply make an exact duplicate of the hit you sent, without letting you modify the payload one bit.

Why, you may ask? Well, a surprisingly large number of projects I work with have a need for a “rollup” property, which collects data from all sites in the organization. The data sent to the rollup often mirrors whatever is collected in the local sites. It’s quite a chore to duplicate send commands across all trackers, so if the project is fine with simple duplication, I use this plugin.

CAVEAT: This will best work with named trackers. Thus Google Tag Manager setting this up in Google Tag Manager is difficult, as you’ll have to mess with the Tracker name field. If you’re confident with your GTM implementation skills, feel free to do whatever you wish, of course. Nevertheless, in its current state, the plugin caters best to an on-page Universal Analytics implementation.

Modifying the tracking code

For this to work, you will need to make a small change to your Universal Analytics tracking code. Let’s say the code looks like this now:

<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-12345-1', 'auto');
  ga('send', 'pageview');
</script>

You’ll need to add the following modification:

<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-12345-1', 'auto');

  // ADD THIS LINE:
  ga('require', 'simolatorDuplicator', {'newId' : 'UA-12345-2'});

  ga('send', 'pageview');
</script>

There’s a single addition: ga('require', 'simolatorDuplicator', {'newId' : 'UA-12345-2'});. This line invokes a plugin called simolatorDuplicator (I know! The awesomest plugin name in the UNIVERSE!), after which you pass an object with a single key-value pair: {newId : newTrackerId}. In place of newTrackerId, you place a String with the Google Analytics property ID to which you want to duplicate all the hits.

The plugin itself

There are two quick and easy (sorry about those words again) ways to load the plugin. Either host it in its own JavaScript file which you then load with a <script></script> loader, or just run the code in the page template itself.

What’s important is that the plugin code needs to be loaded AFTER the tracking code. The code looks like this:

(function() {
  var ga = window[window['GoogleAnalyticsObject']];
  var GADuplicate = function(tracker, propertyId) {
    var o = tracker.get('sendHitTask');
    var temp;
    tracker.set('sendHitTask', function(model) {
      o(model);
      temp = model.get('hitPayload').replace(new RegExp(model.get('trackingId'), 'g'), propertyId.newId);
      if (temp) {
        model.set('hitPayload', temp, true);
        o(model);
      }
    });
  };
  ga('provide', 'simolatorDuplicator', GADuplicate);
})();

So either write this in a file named something.js (feel free to replace something with something else), or just add the code in its own <script> block after the tracking snippet.

<!-- METHOD 1 -->
<script>
  // GA Tracking code here
</script>
<script src="something.js" async></script>

<!-- METHOD 2 -->
<script>
  // GA Tracking code here
</script>
<script>
(function() {
  var ga = window[window['GoogleAnalyticsObject']] || function() {};
  var GADuplicate = function(tracker, config) {
    var o = tracker.get('sendHitTask');
    var temp;
    tracker.set('sendHitTask', function(model) {
      o(model);
      temp = model.get('hitPayload').replace(new RegExp(model.get('trackingId'), 'g'), config.newId);
      if (temp) {
        model.set('hitPayload', temp, true);
        o(model);
      }
    });
  };
  ga('provide', 'simolatorDuplicator', GADuplicate);
})();
</script>

Both are equally fine, though to keep things nice and tidy I do recommend the first method.

Let’s take a quick stroll through the code.

(function() {
  var ga = window[window['GoogleAnalyticsObject']] || function() {};
  ...
  ga('provide', 'simolatorDuplicator', GADuplicate);
})();

These lines wrap the plugin code in an immediately invoked function expression, which I use to protect the global namespace. No, you didn’t need to understand any of that.

The next line establishes the ga() interface locally, by scoping it to the global Google Analytics Object. This is so that the plugin still works even if you’ve renamed the global GA interface from ga to something else.

After the plugin code (represented by the “…”) we make the plugin available to the ga interface. This is a very important line, as it is the counterpart to the ga('require', 'simolatorDuplicator'...); command used in the tracking code. If you didn’t have this line, or if you had a typo in the plugin name, your GA code would not work at all! So remember to test, test, TEST.

Next, the plugin constructor itself:

  var GADuplicate = function(tracker, config) {
    var o = tracker.get('sendHitTask');
    var temp;
    tracker.set('sendHitTask', function(model) {
      o(model);
      temp = model.get('hitPayload').replace(new RegExp(model.get('trackingId'), 'g'), config.newId);
      if (temp) {
        model.set('hitPayload', temp, true);
        o(model);
      }
    });
  };

Lots of things going on here.

First, the function expression establishes a new function called GADuplicate, which takes two parameters: tracker and config. The tracker parameter is passed automatically by the plugin logic, and it contains a reference to the Universal Analytics tracker object which invoked the plugin. The config object is passed as a parameter in the modified tracking code. It’s the {'newId' : 'UA-12345-2'} which we covered earlier.

Next, we make a copy of the original sendHitTask. This little method is actually the entire dispatch logic of analytics.js, so we’ll need it to send our data to Google Analytics.

We need it especially because on the very next line after the variable declarations we overwrite the sendHitTask of the tracker with a new one!

First, the original sendHitTask, temporarily copied to the method o(), is used to send the regular hit to Universal Analytics. This is really important, as without this you’d just be sending your duplicate hit!

Then, we take the hitPayload you just sent to GA, and we replace all instances of the current property ID with the new property ID that you configured in the tracking code! This is the main logic in this plugin. We take the payload, we send it first regularly, and then we modify it and send the modified version. By using model.set('hitPayload', temp, true); we’re rewriting the payload, which is then submitted with a new invocation of the o() method.

I maintain that it’s really quite simple when you think about it, but naturally it does require some understanding of how the APIs work. So feel free to plunge into the documentation.

And that’s it! That’s the code, the setup, and the implementation. Remember, you will need to modify the tracking code accordingly on all pages of your site where you want to use this plugin. You can use it with named trackers, too; just modify the plugin call to:

ga('myNamedTracker:require', 'simolatorDuplicator', {'newId' : 'UA-12345-2'});

And yes, you can rename the plugin to something else than simolatorDuplicator, though I will love you slightly less if you do so.

Summary

Please remember, this is an exact hit duplication method. If you want to modify the payload, you’ll need to add some additional logic to the code, and I really recommend you check out the link to David Vallejo’s blog in the introductory chapter of this article.

Let me know if you’re having problems with this, or if you have suggestions! Please do test this thoroughly before implementing it. You are messing with code that can potentially cripple your Google Analytics implementation.