/gdsc-analytics

Use the Google Analytics Measurement Protocol to collect usage information from a Java application

Primary LanguageJavaMIT LicenseMIT

GDSC Analytics

The GDSC Analytics package contains code to use the Google Analytics Measurement Protocol to collect usage information from a Java application.

License: MIT Build Status Coverage Status Maven Central Javadocs

Total alerts Language grade: Java

Google Universal Analytics is going away

The code is suitable for Google's Universal Analytics using the Measurement Protocol. This service will stop processing data in July 2023.

The replacement service is the Measurement Protocol (Google Analytics 4).

Currently there is not a GA 4 client in this library.

Of note is the change of the HTTP data Content-Type from application/x-www-form-urlencoded to application/json. The former is easily created by appending URL encoded data to a string via a builder pattern to add parameters; the later is suitable for creation of event data using declaritive JavaScript statements which are readily converted using JSON.stringify(...). The new measurement protocol is thus an improvement for JavaScript web clients, but not for a Java client.

It is possible to create a Java client to generate the new HTTP data, although the simplicity of generating the data declaritively is not available in Java.

Features

  • Support for Google Universal Analytics (to be decommissioned in July 2023)
  • Builders to construct hit parameter strings
  • Java type-safe handling of each protocol parameter Value Type
  • Configurable asynchronous requests using java.util.concurrent.ExecutorService
  • Minimal logging using java.util.logging
  • Configurable session handling
  • Graceful disabling when no internet connection
  • No external dependencies

Example

// Create the tracker
String trackingId = "UA-12345-6"; // Your Google Analytics tracking ID
String userId = "Anything";

GoogleAnalyticsClient ga =
    GoogleAnalyticsClient.newBuilder(trackingId)
                         .setUserId(userId)
                         .build();

// Submit requests
String documentHostName = "www.abc.com";
String documentPath = "/path/within/application/";
ga.pageview(documentHostName, documentPath).send();

This would create a protocol parameter string of:

v=1&je=1&tid=UA-12345-6&uid=Anything&dh=www.abc.com&t=pageview&sc=start&&dp=%2Fpath%2Fwithin%2Fapplication%2F

See the Measurement Protocol Parameter Reference Guide for more details.

Asynchronous Measurements

The main functionality of the code is composed of creating a hit of name=value pairs and then sending the hit to Google Analytics.

The only part that operates inline is storage of the parameter names and values. Storage is done using Java primitives in a type-safe manner. This effectively queues the hit for processing.

An ExecutorService is used to perform the construction of the hit and the sending to Google Analytics in the background. Therefore the code has minimal run-time impact. The Queue Time parameter is used to ensure that hit times are collated correctly even if the hit is sent some time after the hit was queued.

The default ExecutorService uses a single low priority thread. The service is configurable and can be shared.

Parameter Caching

Parameters that are the same with each hit can be cached at the hit or session level. For example if the host never changes:

// Create the tracker
String trackingId = "UA-12345-6"; // Your Google Analytics tracking ID
String userId = "Anything";
String documentHostName = "www.abc.com";

GoogleAnalyticsClient ga =
    GoogleAnalyticsClient.newBuilder(trackingId)
                         .setUserId(userId)
                         .getOrCreatePerHitParameters()
                             .addDocumentHostName(documentHostName)
                             .getParent()
                         .build();

// Submit requests
String documentPath = "/path/within/application/";
ga.hit(HitType.PAGEVIEW).addDocumentPath(documentPath).send();

Builder API

A Measurement Protocol hit is simply a set of name=value pairs separated by the & character:

v=1&t=event&tid=UA-12345-6&uid=Test&cd1=StartUp

The Builder API allows adding named Measurement Protocol parameters to a collection. The parameters collection can be used to construct a protocol hit composed of name=value pairs.

The following sections of the Measurement Protocol Parameter Reference are supported:

Section Support
General Full
User Full
Session Full
Traffic Sources -
System Info Full
Hit Full
Content Information Full
App Tracking Full
Event Tracking Full
E-Commerce -
Enhanced E-Commerce -
Social Interactions Full
Timing Full
Exceptions Full
Custom Dimensions/Metrics Full
Content Experiments Full

Example:

String trackingId = "UA-12345-6"; // Your Google Analytics tracking ID
String hit = Parameters.newRequiredBuilder(trackingId)
                       .addHitType(HitType.EXCEPTION)
                       .addExceptionDescription("Something went wrong")
                       .build()
                       .format();

Creates the following hit with the required version (v) and client Id (cid) parameters set:

v=1&je=1&tid=UA-12345-6&t=exception&exd=Something+went+wrong&cid=da51f86a-346d-4aa1-933a-4883887a34cb

The client Id will be automatically added using a random GUID. The Java enabled (je) parameter is also set (because it's Java).

Note

Support was added for parameters that will be of use from within a Java application. Hence no support for the campaign functionality in the Traffic Sources section or any E-Commerce functionality.

Support was complete in 2018. Features may have been added to the protocol since that date (see also Custom Parameters).

Core API

The builder API is a user-friendly wrapper to an underlying API that provides type-safe support of the entire Measurement Protocol parameter reference. Thus any parameter can be added to a hit by adding a FormattedParameter to a builder using the appropriate ProtocolSpecification.

The ProtocolSpecification enum contains all of the Measurement Protocol parameters and specifies the:

  • Formal Name
  • Name (for the name=value pair)
  • Value Type (for the name=value pair)
  • Number of indices in the name
  • Supported hit types

Examples:

  • Queue Time : qt : integer : 0 : all
  • Custom Dimension : cd_ : text : 1 : all
  • Exception : exd : text : 0 : exception
  • Is Exception Fatal? : exf : boolean : 0 : exception

It is expected that any _ character in a parameter name is replaced with an index. The number of indexes within the protocol parameters is in the range 0-3 and type-safe parameter classes are provided for the entire specification to allow dynamic index replacement into a hit.

Example:

String trackingId = "UA-12345-6"; // Your Google Analytics tracking ID
String hit = Parameters.newRequiredBuilder(trackingId)
                       .addHitType(HitType.ITEM)
                       .add(new NoIndexTextParameter(ProtocolSpecification.TRANSACTION_ID, "Trans.1"))
                       .add(new NoIndexTextParameter(ProtocolSpecification.ITEM_NAME, "Item.2"))
                       .add(new OneIndexTextParameter(ProtocolSpecification.PRODUCT_SKU, 23, "SKU.4567"))
                       .build()
                       .format();

Creates hit:

v=1&je=1&tid=UA-12345-6&t=item&ti=Trans.1&in=Item.2&pr23id=SKU.4567&cid=1e96ab9f-bb03-4a9d-b4b3-dc31b7d01b51

Custom Parameters

The supported parameters are based on a snapshot of the Measurement Protocol Parameter Reference.

If the parameter reference changes then the API will be out-of-date. If this occurs then please provide feedback so the library can be updated.

However it is possible to add any parameter to a set of parameters using either a generic name=value pair or a custom parameter specification:

// Generic name=value pair
String name = "anything";
String value = "some text";

// Custom indexed parameter
String formalName = "My parameter"; // Used for error messages
String nameFormat = "myp_"; // Underscore for the index
ValueType valueType = ValueType.INTEGER;
int maxLength = 0; // Ignored for non-text types
ParameterSpecification specification = new CustomParameterSpecification(
    formalName, nameFormat, valueType, maxLength);
int index = 44;
int value2 = 123;

String hit = Parameters.newBuilder()
    .add(name, value)
    .add(new OneIndexIntParameter(specification, index, value2))
    .build()
    .format();

Will create a hit:

anything=some+text&myp44=123

Note this is only a partial hit as the required parameters (v, tid, cid/uid, t) are missing.

GDPR

Applications with users located in the EU must comply with the General Data Protection Regulation (GDPR).

Note this is not a replacement for legal advice.

The conditions of use for Google Analytics requires that any information sent is not personally identifiable thus cannot be linked to an individual. Breach of these conditions will result in deletion of both analytics data and the analytics account. Developers using this library should be aware of this condition and further regulations that may be imposed by GDPR.

The library can be used with settings to secure the connection and anonymize the IP address:

String trackingId = "UA-12345-6"; // Your Google Analytics tracking ID
String clientId = java.lang.UUID.randomUUID().toString();
GoogleAnalyticsClient ga =
    GoogleAnalyticsClient.newBuilder(trackingId)
                         .setClientId(clientId)
                         // Always use anonymised and secure connection.
                         .getOrCreatePerHitParameters().addAnonymizeIp(true)
                             .getParent()
                         .setSecure(true)
                         // ...
                         .build();

Installation

This package is a library to be used used by other Java programs. The latest version is on Maven central:

Maven Central

The package can be installed locally from the source using Maven.

    git clone https://github.com/aherbert/gdsc-analytics.git
    cd gdsc-analytics
    mvn install

Background

Developed at the Genome Damage and Stability Centre, University of Sussex.

Origin

This project originated using ideas from JGoogleAnalyticsTracker by Daniel Murphy. A similar package is JGoogleAnalytics by Siddique Hameed. These projects dummied the GET request sent to Google Analytics by a web browser, i.e. used the legacy Google Analytics protocol.

This code uses the new Analytics Measurement Protocol which is designed to allow any web connected device to measure user interaction via a HTTP POST request. Version 1 of the code was based on JGoogleAnalyticsTracker to send the formatted hits to Google analytics. Version 2 has been completely rewritten to use Java 1.8 features and add a comprehensive test suite. It shares no similarity to version 1 other than the name.

GDSC ImageJ plugins

The code was previously used within the GDSC ImageJ plugins to collect minimal usage information whenever a plugin was run. This functionality is no longer used.

Tracking was done by identifying each ImageJ plugin using the Document Path hit parameter and a pageview hit. The data showed that some plugins with minimal usage internally at the GDSC had an uptake by the community; thus although tracking is no longer used we continue to publish all plugins we develop in the hope that they are useful.

Testing

The code has been tested using:

  • AssertJ for URL parameter testing
  • Mockito for HTTP connection testing of all conditions including various mocked failures
  • JUnit 5 for everything else