How to track IP address as custom dimension in google analytics drupal 8

Submitted by abhaisasidharan on Sun, 03/15/2020 - 08:11
Track IP analytics

IP address tracking is important when you need to recognize returning visitors, track unique user activity on e-commerce websites, for security or just for the fun of it. Like I do!

It is surprisingly tricky to track IP addresses in the Drupal Google Analytics module because of the way in which JavaScript is handled. The custom dimension has to be set right after the tracker is created in the Google Analytics module, and right before it is being sent, i.e., after ga('create', 'UA-XXXXX-Y', 'auto'); and before ga('send', 'pageview'). You can read more about how to do it in Drupal 7 here: https://www.axelerant.com/resources/team-blog/how-to-track-ip-address-as-a-custom-dimension-using-the-drupal-google-analytics-module 

Drupal 8, in all its complexity, makes it easier or harder depending on how you look at it!. Firstly, we will create an Event Subscriber Service. This service will be called every time the KernelEvents::REQUEST event is dispatched. 

Thanks to drupal console, it is quite easy to create a service in Drupal 8. Just use the command 'drupal generate:service'. You will get a command line prompting you for details of the service as such :

drupal generate:service

Code for TrackIPService.php : 

<?php

namespace Drupal\custom\EventSubscriber;

use Symfony\Component\EventDispatcher\EventSubscriberInterface;
use Symfony\Component\HttpKernel\Event\GetResponseEvent;
use Symfony\Component\HttpKernel\KernelEvents;
use Drupal\Core\Session\AccountInterface;
use Symfony\Component\DependencyInjection\ContainerInterface;

/**
 * Class TrackIPSubscriber.
 */
class TrackIPSubscriber implements EventSubscriberInterface {

  /**
   * The account object.
   *
   * @var \Drupal\Core\Session\AccountInterface
   *   The account object.
   */
  protected $account;
  
  /**
   * Constructs a new TrackIPSubscriber object.
   */
  public function __construct(AccountInterface $account) {
    $this->account = $account;
  }

  /**
   * {@inheritdoc}
   */
  public function create(ContainerInterface $container) {
    return new static(
      // Load the service required to construct this class.
      $container->get('current_user')
    );
  }
  
  /**
   * {@inheritdoc}
   */
  public static function getSubscribedEvents() {
    $events[KernelEvents::REQUEST][] = ['trackIPAddress'];
    return $events;
  }

  /**
   * Method is called whenever the KernelEvents::REQUEST event is dispatched.
   *
   * @param \Symfony\Component\HttpKernel\Event\GetResponseEvent $event
   *   The current event.
   */
  public function trackIPAddress(GetResponseEvent $event) {
    if ($this->account->isAnonymous() || $this->account->isAuthenticated()) {
      if (!empty($_SERVER['HTTP_X_FORWARDED_FOR'])) {
        if (strpos($_SERVER['HTTP_X_FORWARDED_FOR'], ',') !== false) {
          // Separate client IP from proxies' IP
          // Format is in [X-Forwarded-For: client, proxy1, proxy2, ..]
          $ips = explode (',', $_SERVER['HTTP_X_FORWARDED_FOR']);
          $ip = trim($ips[0]);
        }
        else {
          // There is no comma in the value; we use it as is.
          $ip = $_SERVER['HTTP_X_FORWARDED_FOR'];
        }
      }
      else {
        // Client is not using a proxy; we use the default REMOTE_ADDR.
        $ip = $_SERVER['REMOTE_ADDR'];
      }
      setrawcookie('visitor_ip', rawurlencode($ip), REQUEST_TIME + 31536000, '/');
    }
  }

}

As you can see from the code, we have injected a dependency (AccountInterface). So it is necessary to pass the arguments to the service in the custom.services.yml. Here is how it would look :

services:
  custom.track_ip:
    class: Drupal\custom\EventSubscriber\TrackIPSubscriber
    arguments: ['@current_user']
    tags:
      - { name: event_subscriber }

Now that's done, we need to add the tracking code in the google analytics module. Go to the google analytics config form (admin/config/system/googleanalytics) and add the following code in the section Custom JavaScript code > Code snippet (before). 

var cookieList = (document.cookie) ? document.cookie.split('; ') : [];
for (var i = 0, n = cookieList.length; i != n; ++i) {
  var cookie = cookieList[i];
  var f = cookie.indexOf('=');
  if (f >= 0) {
    var cookieName = cookie.substring(0, f);
    var cookieValue = cookie.substring(f + 1);
    if (cookieName == 'visitor_ip') {
      var ip = cookieValue;
    }
  }
}
try {
  ga("set", "dimension1", ip);
}
catch (e) {console.log('Could not set Dimension 1');}

We can get the cookie easily using 'jQuery.cookie('visitor_ip')', but its probably going to throw 'jQuery is not defined' error. It's not much different if you use pure JavaScript as I did.

Also, replace 'custom' in my code to whatever your module name is in your code.

Now you should be able to track the IP addresses of your Drupal 8 website visitors. Learn how to create custom reports to read the sent data here (google analytics custom reports).