.NET Tracker 0.1.2 - OXYGEN-MARKET/oxygen-market.github.io GitHub Wiki

HOME > [SNOWPLOW TECHNICAL DOCUMENTATION](Snowplow technical documentation) > Trackers > .NET Tracker

This page refers to version 0.1.2 of the Snowplow .NET Tracker. For the latest version, please see here.

Contents

## 1. Overview

The Snowplow .NET Tracker allows you to track Snowplow events from your .NET websites and desktop applications.

The tracker should be straightforward to use if you are comfortable with C# development; any prior experience with Snowplow's JavaScript Tracker, Python Tracker or Ruby Tracker, Google Analytics or Mixpanel (which have similar APIs to Snowplow) is helpful but not necessary.

Note that this tracker covers all the same events as the Python and Ruby Trackers.

There are three basic types of object you will create when using the Snowplow .NET Tracker: subjects, emitters, and trackers.

A subject represents a user whose events are tracked. A tracker constructs events and sends them to one or more emitters. Each emitter then sends the event to the endpoint you configure. This will usually be a Snowplow collector, but could also be a Redis database.

## 2 Initialization

Assuming you have completed the .NET Tracker Setup for your project, you are now ready to initialize the .NET Tracker.

### 2.1 Accessing the namespace

Add the .NET Tracker's namespace like so:

using Snowplow.Tracker

That's it - you are now ready to initialize a tracker instance.

### 2.2 Creating a tracker

The simplest tracker initialization only requires you to provide the URI of the collector to which the tracker will log events:

var e = new Emitter("d3rkrsqld9gmqf.cloudfront.net")
var t = new Tracker(e)

There are other optional keyword arguments:

Argument Name Description Required? Default
emitters The emitter to which events are sent Yes None
subject The user being tracked No new Subject()
namespace The name of the tracker instance No null
appId The application ID No null
encodeBase64 Whether to enable [base 64 encoding] base64 No true

A more complete example:

var tracker = new Tracker(new Emitter("d3rkrsqld9gmqf.cloudfront.net"), "cf", "my-application-id", false);
#### 2.2.1 `emitters`

This can be a single emitter or a List of emitters. The tracker will send events to these emitters, which will in turn send them to a collector. See Emitters for more on emitter configuration.

#### 2.2.2 `subject`

The user which the Tracker will track. This should be an instance of the Subject class. You don't need to set this during Tracker construction; you can use the Tracker.SetSubject method afterwards. In fact, you don't need to create a subject at all. If you don't, though, your events won't contain user-specific data such as timezone and language.

#### 2.2.3 `namespace`

If provided, the namespace argument will be attached to every event fired by the new tracker. This allows you to later identify which tracker fired which event if you have multiple trackers running.

#### 2.2.4 `appId`

The appId argument lets you set the application ID to any string.

#### 2.2.5 `encodeBase64`

By default, unstructured events and custom contexts are encoded into Base64 to ensure that no data is lost or corrupted. You can turn encoding on or off using the Boolean encodeBase64 argument.

Back to top

## 3. Adding extra data: The Subject class

You may have additional information about your application's environment, current user and so on, which you want to send to Snowplow with each event.

Create a subject like this:

var s = new Subject();

The Subject class has a set of set...() methods to attach extra data relating to the user to all tracked events:

All of these methods return the Subject instance.

If you initialize a Tracker instance without a subject, a default Subject instance will be attached to the tracker. You can access that subject by calling the relevant method on the Tracker instance:

t = new Tracker(myEmitter);
t.subject.SetPlatform(Platform.Pc).SetUserId("user-12345").SetLang("en");

We will discuss each of these in turn below:

#### 3.1 Change the tracker's platform with `SetPlatform`

The possible platforms are enumerated in the Platform enum. The default platform is Platform.Pc. You can change the platform the subject is using by calling:

s.SetPlatform( {{PLATFORM}} );

For example:

s.SetPlatform(Platform.Srv) // Running on a server

For a full list of supported platforms, please see the Snowplow Tracker Protocol.

Back to top

### 3.2 Set user ID with `SetUserId`

You can set the user ID to any string:

s.SetUserId( "{{USER ID}}" );

Example:

s.SetUserId("alexd");

Back to top

### 3.3 Set screen resolution with `SetScreenResolution`

If your code has access to the device's screen resolution, then you can pass this in to Snowplow too:

s.SetScreenResolution( {{WIDTH}}, {{HEIGHT}} );

Both numbers should be positive integers; note the order is width followed by height. Example:

s.SetScreenResolution(1366, 768);

Back to top

### 3.4 Set viewport dimensions with `SetViewport`

If your code has access to the viewport dimensions, then you can pass this in to Snowplow too:

s.SetViewport( {{WIDTH}}, {{HEIGHT}} );

Both numbers should be positive integers; note the order is width followed by height. Example:

s.SetViewport(300, 200);

Back to top

### 3.5 Set color depth with `SetColorDepth`

If your code has access to the bit depth of the device's color palette for displaying images, then you can pass this in to Snowplow too:

s.SetColorDepth( {{BITS PER PIXEL}} );

The number should be a positive integer, in bits per pixel. Example:

s.SetColorDepth(32);

Back to top

### 3.6 Set timezone with `SetTimezone`

This method lets you pass a user's timezone in to Snowplow:

s.timezone( {{TIMEZONE}} );

The timezone should be a string:

s.SetColorDepth("Europe/London");

Back to top

### 3.7 Set the language with `SetLang`

This method lets you pass a user's language in to Snowplow:

s.SetLang( {{LANGUAGE}} );

The language should be a string:

s.SetLang('en');
### 3.7 Tracking multiple subjects

You may want to track more than one subject concurrently. To avoid data about one subject being added to events pertaining to another subject, create two subject instances and switch between them using Tracker.SetSubject:

// Create a simple Emitter which will log events to http://d3rkrsqld9gmqf.cloudfront.net/i
var e = new Emitter("d3rkrsqld9gmqf.cloudfront.net");

// Create a Tracker instance
var t = new Tracker(emitters=e, namespace="cf", appId="CF63A");

// Create a Subject corresponding to a pc user
var s1 = new Subject();

// Set some data for that user
s1.SetPlatform(Platform.Pc);
s1.SetUserId("0a78f2867de");

// Set s1 as the tracker subject
// All events fired will have the information we set about s1 attached
t.SetSubject(s1);

// Track user s1 viewing a page
t.TrackPageView("http://www.example.com");

// Create another Subject instance corresponding to a mobile user
var s2 = new Subject();

// All methods of the Subject class return the Subject instance so methods can be chained:
s2.SetPlatform(Platform.Web).SetUserId("0b08f8be3f1");

// Change the tracker subject from s1 to s2
// All events fired will have instead have information we set about s2 attached
t.SetSubject(s2);

// Track user s2 viewing a page
t.TrackPageView("http://www.example.com");

// Switch back to s1 and track a structured event, this time using method chaining:
t.SetSubject(s1).TrackStructEvent("Ecomm", "add-to-basket", "dog-skateboarding-video", "hd", 13.99);
## 4. Tracking specific events

Snowplow has been built to enable you to track a wide range of events that occur when users interact with your websites and apps. We are constantly growing the range of functions available in order to capture that data more richly.

Tracking methods supported by the .NET Tracker at a glance:

Function Description
TrackPageView() Track and record views of web pages.
TrackEcommerceTransaction() Track an ecommerce transaction
TrackScreenView() Track the user viewing a screen within the application
TrackStructEvent() Track a Snowplow custom structured event
TrackUnstructEvent() Track a Snowplow custom unstructured event
### 4.1 Common

All events are tracked with specific methods on the tracker instance, of the form TrackXXX(), where XXX is the name of the event to track.

### 4.1.1 Custom contexts

In short, custom contexts let you add additional information about the circumstances surrounding an event in the form of a C# dictionary object. Each tracking method accepts an additional optional contexts parameter after all the parameters specific to that method, which should be a list of contexts:

public Tracker TrackPageView(string pageUrl, string pageTitle = null, string referrer = null, 
                             List<Dictionary<String, Object>> context = null, Int64? tstamp = null)

The context argument should consist of a List<Dictionary<String, Object>>. The format of each dictionary is the same as for an unstructured event.

If a visitor arrives on a page advertising a movie, the dictionary for a single custom context in the list might look like this:

var movieContext = new List<Dictionary<String, Object>>
{ 
  { "schema", "iglu:com.acme_company/movie_poster/jsonschema/2-1-1" },
  { "data", new Dictionary<String, Object> 
  {
    { "movie_name", "Solaris" }, 
    { "poster_country": "JP" }
  }
  }
};

This is how to fire a page view event with the above custom context:

t.TrackPageView("http://www.films.com", "Homepage", null, new List<Dictionary<String, String>> {movieContext});

Note that even though there is only one custom context attached to the event, it still needs to be placed in an array.

### 4.1.2 Optional timestamp argument

Each Track...() method supports an optional timestamp as its final argument; this allows you to manually override the timestamp attached to this event. The timestamp should be in milliseconds since the Unix epoch.

If you do not pass this timestamp in as an argument, then the .NET Tracker will use the current time to be the timestamp for the event.

Here is an example tracking a structured event and supplying the optional timestamp argument. We can explicitly supply Nones for the intervening arguments which are empty:

t.TrackStructEvent("some cat", "save action", None, None, None, 1368725287000)

Timestamp is counted in milliseconds since the Unix epoch - the same format as generated by:

Int64 tstamp = (Int64)(DateTime.UtcNow.Subtract(new DateTime(1970, 1, 1))).TotalMilliseconds;
### 4.1.3 Tracker method return values

All tracker methods will return the tracker instance, allowing tracker methods to be chained:

var e = new Emitter("d3rkrsqld9gmqf.cloudfront.net");
var t = new Tracker(e);

t.TrackPageView("http://www.example.com").TrackScreenView("title screen");

Back to top

### 4.2 Track screen views with `TrackScreenView()`

Use TrackScreenView() to track a user viewing a screen (or equivalent) within your app. Arguments are:

Argument Description Required? Type
name Human-readable name for this screen No String
id Unique identifier for this screen No String
context Custom context for the event No List<Dictionary<String, object>>>
tstamp When the screen was viewed No Int64

Although name and id are not individually required, at least one must be provided or the event will fail validation.

Example:

t.TrackScreenView("HUD > Save Game", "screen23", null, 1368725287000)

Back to top

### 4.3 Track pageviews with `TrackPageView()`

Use TrackPageView() to track a user viewing a page within your app. Arguments are:

Argument Description Required? Validation
pageUrl The URL of the page Yes String
pageTitle The title of the page No String
referrer The address which linked to the page No String
context Custom context for the event No List<Dictionary<String, Object>>>
tstamp When the pageview occurred No Int64

Example:

t.TrackPageView("www.example.com", "example", "www.referrer.com")

Back to top

### 4.4 Track ecommerce transactions with `track-ecommerce-transaction()`

Use TrackEcommerceTransaction() to track an ecommerce transaction. Arguments:

Argument Description Required? Validation
orderId ID of the eCommerce transaction Yes String
totalValuee Total transaction value Yes Double
affiliation Transaction affiliation No String
taxValue Transaction tax value No Double
shipping Delivery cost charged No Double
city Delivery address city No String
state Delivery address state No String
country Delivery address country No String
currency Transaction currency No String
items Items in the transaction Yes List
context Custom context for the event No List<Dictionary<String, Object>>>
tstamp When the transaction event occurred No Int64

The items argument is a List of TransactionItems. TrackEcommerceTransaction fires multiple events: one transaction event for the transaction as a whole, and one transaction item event for each element of the items list. Each transaction item event will have the same timestamp, order ID, and currency as the main transaction event.

These are the fields with which a TransactionItem can be created.

Field Description Required? Validation
"sku" Item SKU Yes String
"price" Item price Yes Double
"quantity" Item quantity Yes Int
"name" Item name No String
"category" Item category No String
"context" Custom context for the event No List<Dictionary<String, Object>>>

Example of tracking a transaction containing two items:

var items = new List<TransactionItem>
{
	new TransactionItem("pbz0026", 20, 1),
	new TransactionItem("pbz0038", 15, 1, "red hat", "menswear")
};
t.TrackEcommerceTransaction("6a8078be", 35, "some-affiliation", 6.125, 0, "London", null, "UK", currency="GBP", items);

Back to top

### 4.6 Track structured events with `TrackStructEvent()`

Use TrackStructEvent() to track a custom event happening in your app which fits the Google Analytics-style structure of having up to five fields (with only the first two required):

Argument Description Required? Validation
category The grouping of structured events which this action belongs to Yes Non-empty string
action Defines the type of user interaction which this event involves Yes Non-empty string
label A string to provide additional dimensions to the event data No String
property A string describing the object or the action performed on it No String
value A value to provide numerical data about the event No Int or Float
context Custom context for the event No List<Dictionary<String, Object>>>
tstamp When the structured event occurred No Int64

Example:

t.TrackStructEvent("shop", "add-to-basket", None, "pcs", 2);

Back to top

### 4.7 Track unstructured events with `TrackUnstructEvent()`

Use TrackUnstructEvent() to track a custom event which consists of a name and an unstructured set of properties. This is useful when:

  • You want to track event types which are proprietary/specific to your business (i.e. not already part of Snowplow), or
  • You want to track events which have unpredictable or frequently changing properties

The arguments are as follows:

Argument Description Required? Validation
eventJson The properties of the event Yes <Dictionary<String, Object >>
context Custom context for the event No List<Dictionary<String, Object>>>
tstamp When the unstructured event occurred No Int64

Example:

var eventData = new Dictionary<String, Object>
{
	{ "level", 5 },
	{ "saveId", "ju302" },
	{ "hardMode", true }
};
var eventJson = new Dictionary<String, Object>
{
	{ "schema", "iglu:com.example_company/save-game/jsonschema/1-0-2" },
	{ "data", eventData },
};
t.TrackUnstructEvent(eventJson);

The eventJson must be a Dictionary<String, Object> with two fields: schema and data. data is a flat dictionary containing the properties of the unstructured event. schema identifies the JSON schema against which data should be validated.

For more on JSON schema, see the [blog post] self-describing-jsons.

## 5. Emitters

Tracker instances must be initialized with an emitter. This section will go into more depth about the Emitter class and its subclasses.

### 5.1 The basic Emitter class

At its most basic, the Emitter class only needs a collector URI:

var e = new Emitter("d3rkrsqld9gmqf.cloudfront.net");

This is the signature of the constructor for the base Emitter class:

public Emitter(string endpoint, 
             HttpProtocol protocol = HttpProtocol.HTTP, int? port = null, HttpMethod = HttpMethod.GET, 
             int? bufferSize = null, Action<Int> onSuccess = null, Action<Int, List<Dictionary<String, String>>> onFailure = null, offlineModeEnabled = true):
Argument Description Required?
endpoint The collector URI Yes
protocol Request protocol: HTTP or HTTPS No
port The port to connect to No
method The method to use: "get" or "post" No
bufferSize Number of events to store before flushing No
onSuccess Callback executed when a flush is successful No
onFailure Callback executed when a flush is unsuccessful No
offlineModeEnabled Whether to store events when unable to connect to the server No

protocol must be a member of the HttpProtocol enum: HttpProtocol.HTTP or HttpProtocol.HTTPS. method must be a member of the HttpMethod enum: HttpMethod.GET or HttpMethod.POST.

When the emitter receives an event, it adds it to a buffer. When the queue is full, all events in the queue get sent to the collector. The bufferSize argument allows you to customize the queue size. By default, it is 1 for GET requests and 10 for POST requests. (So in the case of GET requests, each event is fired as soon as the emitter receives it.) If the emitter is configured to send POST requests, then instead of sending one for every event in the buffer, it will send a sing request containing all those events in JSON format.

onSuccess is an optional callback that will execute whenever the queue is flushed successfully, that is, whenever every request sent has status code 200. It will be passed one argument: the number of events that were sent.

onFailure is similar to onSuccess, but executes when the flush is not wholly successful. It will be passed two arguments: the number of events that were successfully sent, and a list of unsent requests.

offlineModeEnabled determines whether offline tracking is enabled. It defaults to true.

An example:

unsentEvents = new List<Dictionary<String, String>>();

Action<Int> successCallback = (successCount) => 
{
	Console.WriteLine(String.Format("{0} events sent successfully!", successCount.ToString()));
};

Action<Int, List<Dictionary<String, String>>> failureCallback = (successCount, unsentEvents) => 
{
	Console.WriteLine(String.Format("{0} events sent successfully!", successCount.ToString()));
	Console.WriteLine(String.Format("Failed to send {0} events", unsentEvents.Count.ToString()));
	foreach (event e in unsentEvents)
	{
		unsentEvents.Add(e);
	}
};

// Initialize an emitter with a bufferSize of 2 and with onSuccess and onFailure callbacks
var e = new Emitter("d3rkrsqld9gmqf.cloudfront.net", HttpProtocol.HTTP, null, HttpMethod.GET, 2, successCallback, failureCallback);

var t = new Tracker(e);

// This doesn't cause the emitter to send a request because the bufferSize was set to 3, not 1
t.TrackPageView("http://www.example.com");
t.TrackPageView("http://www.example.com/page1");

// This does cause the emitter to try to send all 3 events
t.TrackPageView("http://www.example.com/page2");

// Since the method is GET by default, 3 separate requests are sent
// If any of them are unsuccessful, they will be stored in the unsentEvents variable
### 5.2 The AsyncEmitter class

**Warning: the AsyncEmitter in version 0.1.0 is susceptible to a race condition. Until a release is published fixing this, the synchronous Emitter class should be used instead.

var e = new AsyncEmitter("d3rkrsqld9gmqf.cloudfront.net");

The AsyncEmitter class works just like the Emitter class (from which it inherits). It has one advantage, though: HTTP(S) requests are sent asynchronously, using C# Tasks, so the Tracker won't be blocked while the Emitter waits for a response. For this reason, the AsyncEmitter is recommended over the base Emitter class.

### 5.3 Manual flushing

You can flush the emitter manually using the Flush method of the Tracker instance which is sending events to the emitter. If you pass it the Boolean value true, it will be a blocking call which synchronously sends all events in the emitter's buffer. In the case of the AsyncEmitter, it also forces all running tasks to finish within 10 seconds.

t.Flush(true);
### 5.4 Periodic flushing

You can use the SetFlushTimer method to set an interval at which the buffer will be flushed:

e.SetFlushTimer(60000);

It takes one parameter: the number of milliseconds to wait between flushes. The above example will flush every minute.

### 5.5 Offline tracking

If offline tracking is enabled for an emitter, then whenever it attempts to send a request and is unable to connect to the server, it will move all events in that request into an MSMQ queue.

The events stored in the queue will be resent whenever one of two events occurs:

  1. A NetworkAvailabilityChanged event indicates that the network has become available
  2. The emitter successfully sends a request

Note that the onFailure callback will be called when the unsent events are moved into the queue.

### 5.6 The RedisEmitter class

The RedisEmitter class can be used to store events in a Redis database. It uses the Sider library. Its constructor takes two optional parameters: an instance of the Sider RedisClient class, representing the database in which events will be stored, and a key under which events will be stored. The RedisClient parameter defaults to new RedisClient() and the key defaults to "snowplow".

var re = new RedisEmitter(null, "myeventqueue");

In the above example, events will be stored in the default database in a list with key "myeventqueue".

### 5.7 Multiple emitters

You can configure a tracker instance to send events to multiple emitters by passing the Tracker constructor function an array of emitters instead of a single emitter, or by using the addEmitter method:

var e1 = new Emitter("collector1.cloudfront.net", HttpProtocol.HTTP, null, HttpMethod.GET);
var e1 = new Emitter("collector2.cloudfront.net", HttpProtocol.HTTP, null, HttpMethod.POST);
var e3 = new Emitter("collector2.cloudfront.net", HttpProtocol.HTTPS, null, HttpMethod.POST);

var t = new Tracker([e1, e2]);

t.addEmitter(e3);
### 5.8 Custom emitters

You can create your own custom emitter class, implementing the IEmitter interface. This means it must have the following methods:

void Flush(bool sync);

void Input(Dictionary<String, String> payload);

The Input method is called whenever the Tracker instance gives the Emitter a dictionary of name-value pairs representing one event.

The Flush method is called be the Tracker's Flush method. It need not do anything.

Back to top

## 6. Logging

The emitters.rb module has NLog logging enabled to give you information about requests being sent. The logger prints to the console messages about what emitters are doing. By default, only messages with priority "INFO" or higher will be logged.

To change this, use the static SetLogLevel method of the Log class:

Log.SetLogLevel(Log.Level.Debug);

The levels are:

Level Description
Off Nothing logged
Warn Notification for requests with status code not equal to 200
Info Notification for all requests
Debug Contents of all requests

Back to top

⚠️ **GitHub.com Fallback** ⚠️