The GDSC Analytics package contains code to use the Google Analytics Measurement Protocol to collect usage information from a Java application.
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.
- 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
// 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.
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.
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();
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).
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).
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
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.
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();
This package is a library to be used used by other Java programs. The latest version is on 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
Developed at the Genome Damage and Stability Centre, University of Sussex.
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.
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.
The code has been tested using: