Overview

Supported Platforms

The PreEmptive Analytics .NET API supports most major .NET platforms. This includes:

The PAClient classes exposed for each platform are thread-safe and instance methods can be used freely from multiple threads. All instance methods should return very quickly except for ApplicationStopSync. Messages are added to the queue and transmitted/saved on a background thread.

Assemblies

There are a total of 10 assemblies that ship with this document.

API Docs

In each folder containing the aforementioned assemblies, you will find corresponding API documentation in HTML. The entry point for each document is api_doc/index.html.

Windows Phone 8 Applications

There are two different types of Windows Phone applications. There is the traditional Silverlight based Windows Phone applications. These applications are similar to Windows Phone 7 applications and use XAP packages. Introduced in Windows Phone 8.1, there is also the Windows Store based Windows Phone application type. These are similar to Windows Store applications, and use APPX packages. These different application types expose different system APIs at not just compile-time, but also at runtime. For this reason, there are two different PreEmptive Analytics APIs provided for Windows Phone applications. For Silverlight based XAP packages, you should use the PreEmptive.Analytics.WinPhone8.dll assembly. For Windows Store based APPX packages, you should use the PreEmptive.Analytics.WinPhoneApp.dll assembly.

Message Queuing and Transmission

Messages are not immediately sent to the configured endpoint. The API queues messages and sends them either when a certain amount of time has elapsed, or when a number of messages have accumulated. On platforms where transmission may have a performance impact, such as on mobile devices, the transmission of messages can be directly controlled by your program using Send Disabled

Message transmission is not guaranteed and messages may be lost. In cases of forced application shutdown or storage limitations the API may be unable to transmit queued messages or store them for off-line transmission. Messages may also be discarded if they overflow the API's queue before they can be transmitted.

Off-line Storage

Your application is not required to always have network connectivity. By default, the API will store messages locally when the configured endpoint cannot be reached. The messages will automatically be sent and removed from offline storage once the endpoint can be reached.

API Usage Pattern

The general usage pattern of the API is:

Adding Analytics

Start up and Shut down

The API is idle until ApplicationStart is called. At this point, the API and queues start and are ready to process messages. They remain ready until ApplicationStop is called. If a message function such as FeatureTick is used before the API is ready, it will be treated as a no-operation.

You will need to choose where you want to put the Application Start and Application Stop. While it is typical to have one of each, you can use multiple Starts and/or multiple Stops depending upon the entry point and exit points to your application. Extra calls to Start and Stop don't nest: it's the first of each one that defines starting and stopping points of your application.

Company and Application ID

To start sending analytic messages you will need two IDs:

  1. The Company ID
  2. An Application ID

The company ID can be any valid GUID, it should be consistent among business entities. The application ID can also be any valid GUID, but should not usually change between versions of your application.

Both of these values are GUIDs. Your Application ID represents your application across versions and platforms.

Application Start

The only thing you need to call Application Start is a Company ID and an Application ID. Optionally, you can create the PAClient with a Configuration object, which allows for more advanced configuration options.

C# Example

var client = new PAClient(
"7D2B02E0-064D-49A0-BC1B-4BE4381C62D3", //company ID
"21EC2020-AAA1-1069-A2DD-08002B30309D"); //application ID
client.ApplicationStart();

JavaScript Windows Store Example

var analytics = PreEmptive.Analytics.WinRT;
var client = new analytics.PAClient(
"7D2B02E0-064D-49A0-BC1B-4BE4381C62D3", //company ID
"21EC2020-AAA1-1069-A2DD-08002B30309D", //application ID 
null); //The logger interface (optional)
client.applicationStart();

C++ Windows Store Example

auto client = ref new PAClient(
"7D2B02E0-064D-49A0-BC1B-4BE4381C62D3", //company ID
"21EC2020-AAA1-1069-A2DD-08002B30309D", //application ID
nullptr); //The logger interface (optional)
client->ApplicationStart();

Of course, there are many other configuration options you can set. Typical options include:

After the API has been started with an Application Start, changes to StartupConfiguration values will not affect the API's behavior.

Omit Personally Identifiable Data

Some data sent by instrumentation code can contain personally identifiable information. By setting OmitPII, this data can be excluded or replaced with generic data that is not personally identifiable. The following fields will be anonymized or excluded when OmitPII is set to true:

Note: there may still be things in SystemProfile which are somewhat personally identifiable with this option set. It is of course possible to send personally identifiable extended keys and other custom data.

Application Stop

Just before your application terminates you should make a call to Application Stop. This generates the messages that are used to calculate the duration of the user's interaction with your application and handles any messages queued in memory.

C# Example

client.ApplicationStop();

JavaScript Windows Store Example

client.applicationStop();

C++ Windows Store Example

client->ApplicationStop();

There are several optional arguments you can pass to the Application Stop as well as to most of the other API functions. These are covered in the Sending Custom Data and Information About Your Application sections.

ApplicationStop is synchronous (blocking) on some platforms and asynchronous on other platforms. To override the default behavior of the platform, you can use ApplicationStopAsync and ApplicationStopSync, which is asynchronous and synchronous, respectively.

Tracking Feature Use

The most common usage of analytics is to track which features are popular among users and how they interact with them. You can indicate that a feature was used by using the FeatureTick method. You can track the duration of a feature's use by using FeatureStart and FeatureStop.

Feature Ticks

The FeatureTick method generates a message that the configured endpoint tallies up. All you need to provide is a name that defines the feature:

C# Example

public void ConstructTriangle(int rows)
{
    client.FeatureTick("ConstructTriangle");
    // ...
}

JavaScript Windows Store Example

function constructTriangle(rows) {
    client.featureTick("ConstructTriangle");
    // ...
}

C++ Windows Store Example

void Pascal::ConstructTriangle(int rows)
{
    client->FeatureTick("ConstructTriangle");
    // ...
}

You can place the call to Feature Tick anywhere in your code, they need not have a one-to-one mapping of methods to features.

Feature Starts and Stops

A pair of Feature Start and Stops is used to measure not only the occurrence of a feature usage but also the duration of its use. One possible way to use it is to bracket the entire body of the function with a Feature Start and Feature Stop. This is useful to time how long a function takes to execute:

C# Example

public void ConstructTriangle(int rows)
{
    client.FeatureStart("ConstructTriangle");
    // ...
    client.FeatureStop("ConstructTriangle");
}

JavaScript Windows Store Example

function constructTriangle(rows) {
    client.featureStart("ConstructTriangle");
    // ...
    client.featureStop("ConstructTriangle");
}

C++ Windows Store Example

void Pascal::ConstructTriangle(int rows)
{
    client->FeatureStart("ConstructTriangle");
    // ...
    client->FeatureStop("ConstructTriangle");
}

It is important that you use the exact same name in the Start and Stop calls otherwise it will look like two separate features.

Feature Start and Stop can also be used to determine how much time a user spends using a certain feature.

C# Example

public void EnterTextEditor()
{
    client.FeatureStart("TextEditor");
    // ... 
}
public void LeaveTextEditor()
{
    client.FeatureStop("TextEditor");
    // ...
}

JavaScript Windows Store Example

function enterTextEditor() {
    client.featureStart("TextEditor");
    // ...
}
function leaveTextEditor() {
    client.featureStop("TextEditor");
 // ...
}

C++ Windows Store Example

void MyProgram::EnterTextEditor()
{
    client->FeatureStart("TextEditor");
    // ... 
}
void MyProgram::LeaveTextEditor()
{
    client->FeatureStop("TextEditor");
    // ...
}

A function can contain any number of Feature Starts and Stops, and there does not have to be a one-to-one mapping of functions to features.

Feature Start/Stop Partitioning

Feature Starts and Stops are "partitioned". To demonstrate this, look at this example:

  1. Thread A does FeatureStart("foo");
  2. Thread B does FeatureStart("foo");
  3. Thread A does FeatureStop("foo");
  4. Thread B does FeatureStop("foo");

A naive implementation would result in Thread A's stop ending the feature which Thread B started, which is most likely unintended. The default Feature Partitioner will "partition" sets of starts and stops based on Thread ID. So, in this case, Thread A's stop would properly match Thread A's stop, not Thread B's.

However, consider this scenario:

  1. Thread A does FeatureStart("bar");
  2. Thread B does FeatureStop("bar");

In this scenario, the start and stop would match to each other. If a stop can't find a start within its own partition, then it will look at all of the tracked starts in the current client instance.

Sending Custom Data

You can send custom data to the configured endpoint with any type of message. To send over the data you construct an object to hold key-value pairs. For applications using the WinRT components, you should use a collection of string to object key-value pairs that implement the IMap interface. For other applications written in C#/VB.Net, you should use the provided ExtendedKeys class. You can send custom data with any type of message.

One common use case is to report the arguments a method was called with and what the method will return:

C# Example

public bool ConstructTriangle(int rows)
{
    client.FeatureStart("ConstructTriangle");
    bool result = false;
    var keys = new ExtendedKeys(); 
    keys.Add("rows", rows);
    client.FeatureStart("ConstructTriangle", keys);
    // ...
    keys.Add("result", result);
    client.FeatureStop("ConstructTriangle", keys);
    return result;
}

JavaScript Windows Store Example

function ConstructTriangle(rows){
    client.featureStart("ConstructTriangle");
    var result = false;
    var keys = new Windows.Foundation.Collections.PropertySet();
    keys["rows"] = rows;
    client.FeatureStart("ConstructTriangle", keys);
    // ...
    keys["result"] = result;
    client.featureStop("ConstructTriangle", keys);
    return result;
}

C++ Windows Store Example

bool Pascal::ConstructTriangle(int rows)
{
    client->FeatureStart("ConstructTriangle");
    bool result = false;
    auto keys = ref new Platform::Collections::Map<String^, Object^>;
    keys->Insert("rows", rows);
    client->FeatureStart("ConstructTriangle", keys);
    // ...
    keys->Insert("result", result);
    client.FeatureStop("ConstructTriangle", keys);
    return result;
}

Values for keys can be either numeric or strings. You can use the same ExtendedKeys structure as many times as you need. If you set a key with the same name a second time its original value is replaced.

ExtendedKeys have some limits. The length of the name for the key is limited to 2000 characters and the value part for strings is limited to 4000 characters. Numeric values can have up to 18 digits of precision with 5 digits to the right of the decimal point.

Getting System Information

The API can generate two types of system information: a system profile and performance information.

System Profile

The System Profile generates information about the operating system, its environment, and the hardware. System Profile has varying amounts of support on each platform:

You can generate the profile by calling SystemProfile:

C# Example

//...
client.SystemProfile();
//...

JavaScript Windows Store Example

//...
client.systemProfile();
//...

C++ Windows Store Example

//...
client->SystemProfile();
//...

It's recommended that you only generate one system profile per application run. This is typically done immediately after the Application Start. The System Profile sends back information about many different aspects of the machine running your application:

There are two values in the Configuration that affect the data reported in the profile:

Performance Probe

The Performance Probe reports information about the CPU usage percentage, the amount of memory available to the application, and the amount of memory used by the application. In contrast to the System Profile, the Performance Probe is designed to be used in multiple places and called many times in your application. Performance Probe has varying amounts of support on each platform:

The Performance Probe includes a name so you can track performance information in multiple places in your application:

C# Example

public void ConstructTriangle(int rows)
{
    client.PerformanceProbe("ConstructTriangle.Starting");
    // ...
}

Reporting Tampering

The API provides a simple way to report tampering of your application using the ReportTamper method.

Extended Keys can be used to report additional details about the tampering. It is up to you to detect that your application has been tampered with and to call the ReportTamper method.

C# Example

client.ReportTamper();

JavaScript Windows Store Example

client.reportTamper();

C++ Windows Store Example

client->ReportTamper();

Reporting Debugging

The API provides a simple way to report debugging of your applicationg using the ReportDebugging method.

Extended Keys can be used to report additional details about the debugging. It is up to you to detect that your application is being debugged and to call the ReportDebugging method. One method of detecting debugging is to use the .NET Framework's System.Diagnostics.Debugger::IsAttached property (if available).

C# Example

client.ReportDebugging();

JavaScript Windows Store Example

client.reportDebugging();

C++ Windows Store Example

client->ReportDebugging();

Note that the debugging is reported using the Fault message, with a caught exception of type com.preemptive.DebuggingCheck.

Reporting Exceptions

The API provides a simple way to report exceptional conditions in your application. The exception reports can be used to track exceptions reported by your application or from third party software. The report can also have user added information added to it to aid support staff. And of course you can always add Extended Key information to track application state.

Exception Event Types

Exception events come in three types:

In the following example we will show you how to set the event type.

Reporting Details

To pass all the information about the exception we use an ExceptionInfo instance. We must fill it in with information such as:

Note that this can all be gathered from C# applications by simply passing the relevant System.Exception object into ExceptionInfo. It's not as easy for non-C# applications:

C# Example

//...
catch (Exception exception)
{
    client.ReportException(new ExceptionInfo(ExceptionType.Caught,
        exception));
}
//...

JavaScript Windows Store Example

//...
catch (error) {
    var info = new analytics.ExceptionInfo();
    client.reportException(
        analytics.ExceptionInfo.caught(error.name, error.message,
        error.stack));
}
//...

C++ Windows Store Example

//...
catch(Exception^ exception)
{
    client->ReportException(ExceptionInfo::Caught(
        exception ->GetType()->FullName, exception->Message));
} //...

To indicate the origin of the exception in languages (such as C++) without an official stack trace tool, you can either create a BinaryInfo instance that will accompany the ExceptionInfo or create your own stacktrace string with an accompanying regular expression.

The regex should break the provided stacktrace string into groups. These groups are:

Adding User Details

Many GUI programs can display a message box indicating the error that has occurred to the user. This is an opportunity to have the user enter some additional information that developers can use to diagnose the problem. The ExceptionInfo provides a way to attach up to 500 characters of text to the exception report that the user can use to describe the circumstances of the event. It also has a way to pass in contact information, such as a user name or email address.

C# Example

//...
catch (Exception exception)
{
    client.ReportException(new ExceptionInfo(ExceptionType.Caught,
        exception, contact, comment));
}
//...

JavaScript Windows Store Example

//...
catch (error) {
    var info = new analytics.ExceptionInfo();
    client.reportException(
        analytics.ExceptionInfo.caught(error.name,
        Error.message, error.stack, "", contact, comment));
}
//...

C++ Windows Store Example

//...
catch(Exception^ exception)
{
    client->ReportException(ExceptionInfo::Caught(
        exception ->GetType()->FullName, exception->Message, "", "",
        contact, comment));
} //...

Advanced Configuration

User Opt-In

Your application should allow the user to decide if analytics data is gathered or not. The API provides two ways to control the user opting-in to analytics gathering.

The first way to tell the API about the user's opt-in is via the Configuration. You use this way when your application stores the user's previous selection somewhere:

C# Example

var configuration = new Configuration();
configuration.OptIn = GetSavedOptInState();
//...
var client = new PAClient(configuration);

JavaScript Windows Store Example

var configuration = new analytics.Configuration();
configuration.optIn = getSavedOptInState();
//...
var client = new analytics.PAClient(configuration, null);

C++ Windows Store Example

var configuration = ref new Configuration();
configuration->OptIn = GetSavedOptInState();
//...
var client = ref new PAClient(configuration, nullptr);

If the opt-in value is set to false, then calls to ApplicationStart will not start the API.

The second way the opt-in option can be changed is by talking directly to the API at runtime. You would use this if your application has a dialog that lets the user change the opt-in status after an Application Start:

C# Example

client.SetSessionOptIn(true);

JavaScript Windows Store Example

client.setSessionOptIn(true);

C++ Windows Store Example

client->SetSessionOptIn(true);

If the opt-in state changes from false to true the API will do an automatic Application Start and analytics will start being gathered. Note that this behavior only occurs if a properly configured Application Start has previously been tried and has failed due to the opt-in status.

There are a couple of things to note about the opt-in state. If the Application Start is skipped because of the opt-in state, the API may still send messages that were previously gathered and stored offline. Once it is done sending that data it will go back to the stopped state. If you change the opt-in state from true to false the API does not immediately shut down. Instead it stops gathering analytics by ignoring messages like Feature Tick et. al., but will send any previously created messages. Opt-In does not affect the sending of Application Stop messages if the API was previously started with Opt-In set to true, or if the API was started by Opt-In being set to true.

Information About Your Application

There is much more optional information you can send to the configured endpoint about your application than just its application ID.

C# Example

//...    

var configuration = new Configuration("7D2B02E0-064D-49A0-BC1B-4BE4381C62D3", "21EC2020-CCCC-1069-A2DD-08002B30309D"); configuration.ApplicationName = "C# Sample App";
configuration.ApplicationType = "C# Sample";
configuration.ApplicationVersion = "1.0"; configuration.InstanceID = GetSerialNumber(); //...

JavaScript Windows Store Example

//...
var configuration = new analytics.Configuration();
configuration.companyID = "7D2B02E0-064D-49A0-BC1B-4BE4381C62D3"
configuration.applicationID = "21EC2020-CCCC-1069-A2DD-08002B30309D"
configuration.applicationName = "JavaScript Sample App";
configuration.applicationType = "JavaScript Sample";
configuration.applicationVersion = "1.0";
configuration.applicationID = getSerialNumber();
//...

C++ Windows Store Example

//...
auto configuration = ref new Configuration();
configuration->CompanyID = "7D2B02E0-064D-49A0-BC1B-4BE4381C62D3"
configuration->ApplicationID = "21EC2020-CCCC-1069-A2DD-08002B30309D"
configuration->ApplicationName = "C++ Sample App"; 
configuration->ApplicationType = "C++ Sample";
configuration->ApplicationVersion = "1.0";
configuration->ApplicationID = GetSerialNumber();
//...

Binary Information

In addition to tracking general application details, you may want to track assembly level details. BinaryInfo can be used for this functionality.

Every type of message is capable of sending a BinaryInfo object. With this you can track details such as:

Due to limitations of some platforms, there are a few different methods you can use to get an automatically populated BinaryInfo

Storing Data Offline

By default the API will try to send messages directly to the configured endpoint. If there is no internet connection or there is a problem reaching the server it will save the data locally for later transmission.

You can control this behavior with the Configuration instance before Application Start. The settings that control the API's behavior are:

Changing values in the StartupConfiguration instance will not have any effect while an application run is active.

There is also an option called SendDisabled. An initial value for it can be set on the FlowController instance. This differs from Offline in that it is a temporary condition and can be changed by calling the SetSendDisabled method in the API instance while the application run is active. We will talk more about that in Controlling the Transmission Window.

Controlling Data Transmission

The API doesn't send messages immediately to the configured endpoint, instead it bundles them up into groups. The number of message that make up a group is based on a combination of the maximum size of the message queue, how quickly your application is generating messages, and how long you want to keep messages in memory. All of this behavior is determined by the Flow Controller passed into Application Start.

Field Size Limits

It is assumed that the server enforces certain size limitations on specific fields. For instance, feature tick names may be truncated to 50 characters by the endpoint because its database is only configured to allow 50 characters. To avoid sending data which will not be utilized and/or may cause problems for the endpoint, the API supports limiting the size of specific XML attributes.

New size limits can be created by using the DictionaryAttributeLimiter class from C#/.NET.

C# Example

var limiter=new DictionaryAttributeLimiter();
limiter.Set("feature", "name", 100); //set the limit of feature names to 100 characters
MyConfiguration.AttributeLimiter=limiter;
var client = new PAClient(MyConfiguration);

The DictionaryAttributeLimiter is only exposed in C#/.NET, not in WinRT. However, we provide four built in limiters for our official endpoints:

By default, the Common limiter is used for the field size limits. It uses the least common denominator between RI and PATFS. For instance, Runtime Intelligence has a company name limit of 100 characters. PA for TFS has a company name limit of 600 characters. So, by default we limit the company name to 100 characters to minimize bandwidth needed. Note that both endpoints can handle larger company names, but the server will truncate the name to its respective limit.

These can be set from C# and from WinRT-supported languages. Here are examples of using the RI field size limits from C# and WinRT languages:

C# Example

configuration.AttributeLimiter = 
PreEmptive.Analytics.Common.EndpointAttributeLimits.RI; 

JavaScript Windows Store Example

configuration.attributeLimiter = 
PreEmptive.Analytics.WinRT.EndpointAttributeLimits.ri; 

C++ Windows Store Example

configuration->AttributeLimiter = 
PreEmptive::Analytics::WinRT::EndpointAttributeLimits::RI;

Shutdown Time Limits

ApplicationStop is blocking/synchronous by default on some platforms and asynchronous on others:

To keep from holding the application open and/or failing certification, there are configurable shutdown time limits.

By default, StopTimeout is 10 seconds and NeededTimeForSavingAtStop is 2 seconds. So, after 8 seconds have passed within ApplicationStop, it will get a signal that it needs to stop trying to transmit messages and save them to offline storage OR to fire and forget them before the process is destroyed.

These application stop behavior properties are contained within Configuration.StopBehavior on most platforms. It is just contained within Configuration on WinRT.

API Language Interoperability

Analytics can be added to applications created in multiple languages. The PreEmptive Analytics API is available for several languages, but only one can control the Application Start and Stop at a time. The API that starts first controls the scope of the application run and has APIs implemented in other language run in a subordinate fashion. This is done by setting the DefaultSession in the Configuration class. You can retrieve the generated session GUID by calling PAClient.GetActiveDefaultSession().

Session Extension Window

On some mobile platforms, it is desirable to not immediately stop an API instance when the application is put into the background. Session Extension is a feature that allows the API to only stop after a set amount of time of the target application being in the background. This is enabled by setting the SessionExtensionWindow property to something greater than 0. With this enabled, when the API receives an ApplicationStop, it will kick off a timer. If an ApplicationStart is received before this timer expires, the API will not be stopped. If an ApplicationStart is not received, however, the API will stop as normal. An example use case is if you have instrumented a phone application. If the user is actively using your application, checks a text message, and quickly switches back to your application, it is probably desirable for this to be considered one session, not two.

This feature also allows for the compensation of out-of-order application start and stop requests. If the API is already started and an extra ApplicationStart is received, followed soon after by an ApplicationStop, it is assumed that the calls are out of order and the stop will be ignored. This means the API won't stop from such a condition, and the sent data will continue to look like a single session. This feature is particularly useful for properly instrumenting a Xamarin Android application, since Android Activities have an inverted and difficult to work with life cycle.

When using this feature, you must be careful to avoid accidental message loss. You may want to do a session-extension enabled ApplicationStop when the user switches away from your application. However, when the operating system is about to terminate your application, it's not desirable to wait and see if the application will be brought back to the foreground. In this case it is desirable to use the immediate parameter of ApplicationStop to cause the API to ignore the session extension feature and instead immediately stop.

C# Example

client.ApplicationStop(immediate: true);

JavaScript Windows Store Example

client.applicationStop(null, null, true); //last parameter is `immediate`

C++ Windows Store Example

client->ApplicationStop(nullptr, nullptr, true); //last parameter is `immediate`

Currently, Session Extension is enabled by default and set to 60 seconds on the Xamarin Android. It is disabled by default on all other platforms.

Invalid Characters

The API automatically escapes all non-ASCII characters. It also discards characters which are non-escapable according to the XML specification. The API currently does not support characters which require more than 1 word (2 bytes) to represent in UTF-16 (codepoints greater than U+FFFF). These unsupported characters may be sent corrupted or discarded.

The Flow Controller

The first items we will look at in the Flow Controller are those controlling the in-memory queue size. This is where messages are held before being sent to the endpoint or to local storage. There are two controls:

When you set the QueueSize the HighWater property is automatically adjusted to be 1/3 less than the size. You may want to adjust these values if your application can send rapid bursts of messages.

The next two items control how often the API will try to flush the queue regardless of how many messages have accumulated.

Adjusting these values requires care. Having a MaximumInterval that is too large can cause the API to hang on to memory used to store messages - this can be exacerbated by having a large QueueSize. Setting it too small can steal time from your application.

The actual time between checks is determined by the FlowController and the rate at which your application is sending messages. This brings us to the next two properties:

Finally, there are two properties that let the FlowController handles transmission problems:

Controlling the Transmission Window

The Flow Controller uses the previously mentioned values to adjust how often it will empty the in memory queue of messages. Although this takes place on a background thread with a lower priority it still can happen at a time where your application is doing something critical. The API provides a way to control when the transmission can take place.

If you have places in your application where you need to prevent the API from sending messages you can use the Send Disabled feature:

C# Example

client.SetSendDisabled(true);
// Time critical code here
client.SetSendDisabled(false);

JavaScript Windows Store Example

client.setSendDisabled(true);
// Time critical code here
client.setSendDisabled(false);

C++ Windows Store Example

client->SetSendDisabled(true);
// Time critical code here
client->SetSendDisabled(false);

Another way to use Send Disabled is to only allow sending when your application is in an idle state, for example sitting at a menu screen. In this case you would want to start up the API in the disabled state, which you can do with the FlowController instance:

C# Example

var flowController = new FlowController();
flowController.SendDisabled = true;
client.ApplicationStart(null, null, flowController);
// When you enter an idle state
client.SetSendDisabled(false);
// When you are busy again
client.SetSendDisabled(true);

JavaScript Windows Store Example

var flowController = new analytics.FlowController();
flowController.sendDisabled = true;
client.applicationStart(null, null, flowController);
// When you enter an idle state
client.setSendDisabled(false);
// When you are busy again
client.setSendDisabled(true);

C++ Windows Store Example

auto flowController = ref new FlowController();
flowController.SendDisabled = true;
client->ApplicationStart(nullptr, nullptr, flowController);
// When you enter an idle state
client->SetSendDisabled(false);
// When you are busy again
client->SetSendDisabled(true);

The last way that you can control the sending of messages is by telling the API that it should flush any queued messages right away. This is done with the SendMessages method:

C# Example

client.SendMessages();

JavaScript Windows Store Example

client.sendMessages();

C++ Windows Store Example

client->SendMessages();

Time To Live

If you send many messages and it's a fairly rare occurrence that the user is connected to the internet, then it's possible that messages could consume an ever-increasing amount of disk space. The API addresses this problem by exposing a set of criteria that control how old messages may be deleted. There are two properties to configure this behavior:

Both of these conditions must be met before a message is eligible for deletion.

These properties are also retroactive. So, if you were to set these to very high values which impacted a user's experience(by using significant disk space), you could publish an update to your application with lower values for these properties and old messages qualifying with the new properties would begin to immediately be deleted

For ease of use, these properties exist in both Configuration and FlowController. See Interactions With Configuration for more details about this.

Batch Size Limits

Most servers have limitations on how large of a request they can process. Because of this, the API has support for splitting requests that would surprass the server's limits. If a single message is larger than this size, then it will be discarded.

This is configurable by using the MaximumBatchSize property on the Configuration or the FlowController instance. By default it is set to 4Mb - 4Kb. We leave 4K of extra space so that there is plenty of room for HTTP headers

This property is retroactive. So, if you had this value set to such a high value that the configured endpoint couldn't handle such a large request, you could publish an update to your application with appropriate values and the old message batches would then be split to conform to the new value.

Interactions With Configuration

It's not expected that most people will need all of the control that creating a FlowController allows. Because of this, many properties are duplicated among Configuration and FlowController, including:

This is so that creating a new FlowController instance isn't normally required for more common configuration options. If you do create a new FlowController instance however, then the properties from the Configuration instance are not used for these duplicated values. This is because the API also allow the creation of a custom flow controller using the IFlowController interface, which is disconnected from the default implementation and has these properties exposed at a more abstract level.

Here is an example of this behavior:

C# Example

config.Offline = true;
var flow = new FlowController();
flow.Offline = false; //This is the actual value that will be used!
var client = new PAClient(configuration);
client.ApplicationStart(null, null, null, flow); 
//the API will be "online" at this point, not "offline"

Client-Side Logging

The API has the ability to do client-side logging for debugging purposes. This is accomplished by passing a logger instance into the client constructor. You can either use the provided Logger implementations or create your own by implementing the ILogger interface. This allows you to handle the logging information in any way you want.

C# Example

var client = new PAClient(configuration, new Logger(LoggingLevel.Info));

JavaScript Windows Store Example

var client = new analytics.PAClient(configuration, 
    new Logger(analytics.LoggingLevel.info()));

C++ Windows Store Example

auto client = 
    ref new PAClient(configuration, ref new Logger(LoggingLevel::Info()));

The provided platform specific loggers output the logging information to a file (PALogFile.txt). This file is located in different places depending on the platform. For standard .NET applications, the file is placed in the same directory as the executable. On the phone platform, the file is placed in the root directory of isolated storage, and for WinRT it is in the LocalState directory where the package is installed.

Note that since JavaScript does not have the notion of classes, creating your own logger is not as straightforward as other WinRT languages. In order to implement your own logger for a JavaScript WinRT solution, create a Windows Runtime Component in another language, such as C#, and use that implementation in your JavaScript project.