Collecting data with Trackers and Webhooks

  1. Home
  2. Docs
  3. Collecting data with Trackers and Webhooks
  4. Trackers – collecting data from your own applications
  5. Java Tracker
  6. Tracking events

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") .build(); tracker.track(event);
Code language: Java (java)

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 class e in raw event eventType in enriched event
Unstructured (custom) ue unstruct
ScreenView ue unstruct
Timing ue unstruct
PageView pv page_view
Structured se struct
EcommerceTransaction* tr transaction
EcommerceTransactionItem* ti transaction_item

Note: EcommerceTransaction/EcommerceTransactionItem are a legacy design and may be deprecated soon.

Unstructured events (also called Self-Describing events elsewhere in Snowplow) 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 Unstructured events: the method parameters 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 Unstructured 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 "primitive" or "canonical".

Unstructured events and primitive/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 Unstructured 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. These properties are 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 Unstructured 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 (Unstructured events)

To track data using an Unstructured event, the data must be structured as a SelfDescribingJson object, discussed fully in Custom tracking with schemas. 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. Unstructured events can be considered wrappers for sending SelfDescribingJson.

Unstructured is a legacy name. This event type will be renamed the more accurate SelfDescribing in the next release.

The simplest initialisation looks like this:

Unstructured unstructured = Unstructured.builder() .eventData(dataAsSelfDescribingJson) .build();
Code language: Java (java)

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

Creating a ScreenView event

Track screen views with the ScreenView event. It’s a wrapper around an Unstructured event, using this schema.

A simple initialisation looks like this:

ScreenView screenView = ScreenView.builder() .name("human readable screen name") .id("unique screen ID") .build();
Code language: Java (java)

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 an Unstructured 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") .build();
Code language: Java (java)

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 "primitive" event type; data will end up in individual "atomic" columns in the data warehouse.

Property Field in raw event Column in enriched event
page URL url page_url
page title page page_title
referrer URL refr page_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() .pageUrl("https://www.snowplowanalytics.com") .pagetitle("Snowplow") .referrer("https://github.com/snowplow/snowplow-java-tracker") .build();
Code language: Java (java)

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 "primitive" equivalent of Unstructured 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.

Property Often contains data about Field in raw event Column in enriched event
category Grouping for the action se_ca se_category
action Type of user activity se_ac se_action
label Additional event data se_la se_label
property The action or object acted on se_pr se_property
value Numerical event data se_va se_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") .value(12.34) .build();
Code language: Java (java)

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 Unstructured event. We suggest creating one schema for the overall transaction, and another schema for individual items. The transaction schema can be used for the Unstructured 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 "primitive" events. The data will end up in individual "atomic" columns in the data warehouse.

EcommerceTransaction property Field in raw event Column in enriched event
orderId tr_id tr_orderid
totalValue tr_tt tr_total
affiliation tr_af tr_affiliation
taxValue tr_tx tr_tax
shipping tr_sh tr_shipping
city tr_ci tr_city
state tr_st tr_state
country tr_co tr_country
currency tr_cu tr_currency
EcommerceTransactionItem property Field in raw event Column in enriched event
itemId ti_id ti_orderid
sku ti_sk ti_sku
price ti_pr ti_price
quantity ti_qu ti_quantity
name ti_nm ti_name
category ti_ca ti_category
currency ti_cu ti_currency

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") .sku("sku") .price(1.0) .quantity(2) .name("item name") .category("category") .currency("currency") .build(); // Then make the EcommerceTransaction event EcommerceTransaction ecommerceTransaction = EcommerceTransaction.builder() .items(item) // Add the EcommerceTransactionItem events .orderId("should be the same as item_id") .totalValue(2.0) .affiliation("affiliation") .taxValue(2.0) .shipping(3.0) .city("city") .state("state") .country("country") .currency("currency") .build();
Code language: Java (java)

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 Unstructured unstructured = Unstructured.builder() .eventData(dataAsSelfDescribingJson) .trueTimestamp(1647616394785L) .build();
Code language: Java (java)

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

If you’d like to learn more about Snowplow BDP you can book a demo with our team, or if you’d prefer, you can try Snowplow technology for yourself quickly and easily.