Skip to main content

Tracking events

To track an event, pass an Event instance to the Tracker.

For example, tracking a ScreenView:

Event event = new ScreenView.builder()
.name("screen name")

The Java tracker makes it easy to track different kinds of data. We provide a range of Event classes for tracking out-of-the-box event types as well as fully custom events.

Every tracked event payload has a unique event_id UUID string. Other ubiquitous properties include the name_tracker (trackerNamespace) and app_id (appId) set when the Tracker was initialized. From version 0.12 onwards, Tracker.track() returns the payload's eventId.

Snowplow events have a defined structure and protocol that is identical regardless of the tracker used. A minimal payload - the raw event - is sent from the tracker to your collector. The raw event is enriched as it passes through your pipeline. By the time the event arrives in your data storage, depending which enrichments you have enabled, it will have gained different kinds of metadata, and have many more fields than it started with. The default Java tracker event fields are shown here.

The Java tracker Github repository includes a mini demo, "simple-console". The demo sends one event of each type to your event collector.

Auto-tracked eventsโ€‹

The Java tracker does not yet support automatic event tracking. All tracking must be implemented manually.

Manually-tracked events summaryโ€‹

The Java tracker provides classes for tracking different types of events. They are listed below.

Event classe in raw eventeventType in enriched event
SelfDescribing (custom)ueunstruct

EcommerceTransaction/EcommerceTransactionItem are a legacy design and will be deprecated soon.

SelfDescribing events (called Unstructured prior to v1) allow you to track anything that can be described by a JSON schema. The data you provide will be sent as a JSON inside the raw event payload. The specific type of JSON schema needed are described fully on the next page.

The ScreenView and Timing out-of-the-box event types are actually wrappers for SelfDescribing events: the methods for building those events represent fields in their hidden self-describing JSON schemas. This is why both ScreenView and Timing events are labelled "unstruct" in the data warehouse.

The PageView and Structured event types are processed differently from SelfDescribing events. Properties tracked using these events are not sent packaged as JSON, but as individual "atomic" fields in the raw event payload. We call these event types "canonical" or "primitive".

SelfDescribing events and canonical events are loaded into and modelled differently in your data warehouse. The "atomic" fields will always have individual columns.

EcommerceTransaction and EcommerceTransactionItem events are legacy primitive events. We recommend instead designing your own SelfDescribing events plus context entities for eCommerce tracking.

Tracking data that is not event-type specificโ€‹

Some data, such as that relating to the user whose activity is being tracked, is relevant across all event types. The Java tracker provides two mechanisms for tracking this kind of data.

Certain properties, including userId or ipAddress, can be set as "atomic" properties in the raw event, using the Subject class. Subject properties relate mainly to client-side tracking. If you are using the Java tracker for server-side tracking, you may wish to pass client-side data for tracking server-side. This is discussed here.

A more general and powerful method is to attach self-describing JSON "context entities" to your events - the same JSON schemas as used for SelfDescribing events. This means that any data that can be described by a JSON schema can be added to any or all of your events. Read more on the next page.

All events also provide the option for setting a custom timestamp, called trueTimestamp. See below for details.

Creating a custom event (SelfDescribing)โ€‹

To track data using an SelfDescribing event, the data must be structured as a SelfDescribingJson object, discussed fully on the next page. These require two fields. The first is a URI for a self-describing JSON schema. The second is a data map, and the data must be valid against the schema. SelfDescribing events can be considered wrappers for sending SelfDescribingJson.

The simplest initialisation looks like this:

SelfDescribing selfDescribing = SelfDescribing.builder()

See the API docs for the full SelfDescribing.Builder options.

Creating a ScreenView eventโ€‹

Track screen views with the ScreenView event. It's a wrapper around a SelfDescribing event, using this schema.

A simple initialisation looks like this:

ScreenView screenView = ScreenView.builder()
.name("human readable screen name")
.id("unique screen ID")

At least one of name or id must be set. See the API docs for the full ScreenView.Builder options.

Creating a Timing eventโ€‹

Track how long something took with the Timing event. It's a wrapper around a SelfDescribing event, using this schema.

A simple initialisation looks like this:

Timing timing = Timing.builder()
.category("category of the timed event")
.variable("name of the timed event")
.timing(10) // in milliseconds
.label("optional label")

Provide your timing value in milliseconds. The label property is optional. See the API docs for the full Timing.Builder options.

Creating a PageView eventโ€‹

Track page views with the PageView event. This is a "canonical" event type; data will end up in individual "atomic" columns in the data warehouse.

PropertyField in raw eventColumn in enriched event
page URLurlpage_url
page titlepagepage_title
referrer URLrefrpage_referrer

The provided URLs will also be decomposed into other columns, such as page_urlscheme, during event enrichment.

A simple initialisation looks like this:

PageView pageViewEvent = PageView.builder()

Only pageUrl is required. See the API docs for the full PageView.Builder options.

Creating a Structured eventโ€‹

To track custom data without schemas, use Structured events. They are the "canonical" equivalent of SelfDescribing events. The provided data will end up in individual "atomic" columns in the data warehouse. Because of this, it's not possible to fully customise a Structured event: the fields cannot be renamed, nor new fields added. Structured events are designed to be similar to Google-style events.

The Structured event fields have flexible definitions, and what you put into each field is up to you. This is a double-edged sword. It's highly advisable to agree business-wide on definitions for each of these fields, before implementing tracking.

PropertyOften contains data aboutField in raw eventColumn in enriched event
categoryGrouping for the actionse_case_category
actionType of user activityse_acse_action
labelAdditional event datase_lase_label
propertyThe action or object acted onse_prse_property
valueNumerical event datase_vase_value

A simple initialisation looks like this:

Structured structured = Structured.builder()
.category("category e.g. auth")
.action("action e.g. logout")
.label("optional label")
.property("optional property")

Both category and action are required. See the API docs for the full Structured.Builder options.

Creating EcommerceTransaction and EcommerceTransactionItem eventsโ€‹

To track eCommerce data, we recommend designing your own schemas for an SelfDescribing event. We suggest creating one schema for the overall transaction, and another schema for individual items. The transaction schema can be used for the SelfDescribing event. Items in the transaction can be added as context entities.

The EcommerceTransaction and EcommerceTransactionItem events are legacy events, and it's highly likely they will be deprecated in the future. They are designed to be sent together, with one EcommerceTransaction containing an EcommerceTransactionItem for every item in the transaction. When the EcommerceTransaction event is tracked, the EcommerceTransactionItem events are extracted and sent separately. This means that although you have only tracked one event (using Tracker.track()), multple events are generated.

EcommerceTransaction and EcommerceTransactionItem are "canonical" events. The data will end up in individual "atomic" columns in the data warehouse.

EcommerceTransaction propertyField in raw eventColumn in enriched event
EcommerceTransactionItem propertyField in raw eventColumn in enriched event

The orderId and itemId should be the same, as it's the most direct way to associate the two events once they are in the warehouse.

A simple initialisation looks like this:

// Create events for each item in the transaction
EcommerceTransactionItem item = EcommerceTransactionItem.builder()
.itemId("should be the same as order_id")
.name("item name")

// Then make the EcommerceTransaction event
EcommerceTransaction ecommerceTransaction = EcommerceTransaction.builder()
.items(item) // Add the EcommerceTransactionItem events
.orderId("should be the same as item_id")

For EcommerceTransactionItem events, itemId, sku, price and quantity are required. For EcommerceTransaction events, only orderId and totalValue are required. See the API docs for the full EcommerceTransaction.Builder and EcommerceTransactionItem.Builder options.

Adding custom timestamps to eventsโ€‹

Snowplow events have several timestamps. The raw event payload always contains a deviceCreatedTimestamp (dtm) and a deviceSentTimestamp (stm). Other timestamps are added as the event moves through the pipeline.

Every Event.Builder in the Java tracker allows for a custom timestamp, called trueTimestamp to be set. Read more about timestamps in this still relevant forums post.

A trueTimestamp can be added to any event using the trueTimestamp() Builder method:

// This example shows an Unstructured event, but all events can have a trueTimestamp
SelfDescribing selfDescribing = SelfDescribing.builder()

trueTimestamp should be a Long representing milliseconds since the Unix epoch.