NodeJS

This documentation is for the Countly NodeJS SDK version 26.8.X. The SDK source code repository can be found here.

Countly NodeJS SDK runs with the following Node.js versions and up:

Node Versions
^26 ^24 ^22 ^20 ^18 ^16 ^14 ^12 ^10.12

To examine the example integrations, please have a look here.

Adding the SDK to the project

You can reach Countly NodeJS SDK npm package here. To add it to your project, you would use a command similar to these with your preferred package manager:

npm yarn
npm install countly-sdk-nodejs

After this, you can import and use Countly as you wish inside your project as described below.

SDK Integration

Minimal Setup

Wherever you want to integrate Countly NodeJS SDK, you should import 'countly-sdk-nodejs' and initialize Countly. Here, you would also need to provide your application key and server URL. Please check here for more information on how to acquire your application key (APP_KEY) and server URL.

Example basic setup would look like this:

var Countly = require('countly-sdk-nodejs');

Countly.init({
  app_key: "YOUR-APP-KEY",
  url: "https://your_server_url/"
});

If you are in doubt about the correctness of your Countly SDK integration you can learn about the verification methods from here.

SDK Data Storage

The SDK stores the device ID and the data it has not sent yet, such as requests, events, and SDK behavior settings, as JSON files in a .countly-storage folder in your app's working directory. The working directory is the folder your app is started from. Add .countly-storage/ to your .gitignore file.

To store the data in another folder, set storage_path during the initialization. A relative path is resolved against the app's working directory:

var Countly = require('countly-sdk-nodejs');

Countly.init({
  app_key: "YOUR-APP-KEY",
  url: "https://your_server_url/",
  // by default it is ".countly-storage" in the app's working directory
  storage_path: "/var/lib/my-app/countly"
});

Command line tools that are started from different folders, and apps that run in containers, should set an absolute storage_path, for example a folder in the user's home folder or on a mounted volume. Otherwise, each folder or container keeps its own device ID.

If the storage folder is not writable, for example on a read-only file system such as AWS Lambda, the SDK prints a warning, also when logging is off. The device ID and the unsent data then do not survive a restart. Use a writable folder such as /tmp, or the memory storage type (storage_type). The SDK also prints a warning when two Countly instances in the same process use the same storage folder.

SDK versions up to 24.10.4 stored their data in a data folder inside the SDK package, which every reinstall or upgrade of the package deleted. They also resolved a relative storage_path against the SDK's own folder. At its first start, the SDK moves the data from the old location to the new folder and keeps the old files with a .migrated suffix. If the new folder is not writable, the SDK keeps using the old location.

CountlyBulk stores its data in .countly-storage/bulk/<app_key>, unless you set storage_path.

Sending Data Before the Process Ends

The SDK sends the recorded data in the background. It keeps your Node.js process running only while it has data to send and the server answers, so a script ends by itself once its data is delivered. Data that cannot be sent yet, for example because the server cannot be reached, stays stored and is sent at the next start.

To send all recorded data right away, call flush, for example at the end of a script, a scheduled job, or a serverless function. It returns a promise that resolves with true when everything recorded so far is delivered, and with false when something is still waiting, for example because the server could not be reached or the time limit passed. The time limit is an optional argument, in seconds:

Countly.add_event({ key: "backup_finished" });

// wait at most 5 seconds
Countly.flush(5).then(function(delivered) {
  console.log("Countly data delivered:", delivered);
});

In a long-running server, call it in your shutdown handler before process.exit():

process.on("SIGTERM", async function() {
  Countly.end_session();
  await Countly.flush(5);
  process.exit(0);
});

Instead of using the promise, you can pass a callback as the last argument. It receives the same value.

Call flush when your app ends, not after every recorded item. It sends right away, also right after a failed request and while the SDK pauses sending because the server answered slowly.

SDK Logging

If you encounter a problem or want to see if everything is working smoothly just turning on the logs during the initialization is all you really need. You can do so by setting the debug flag as true, during the init. This way you can see the inner workings of the Countly from your console.

var Countly = require('countly-sdk-nodejs');

Countly.init({
  app_key: "YOUR-APP-KEY",
  url: "https://your_server_url/",
  debug: true
});

But sometimes you might want to see the logs only for a small time frame or some particular operation. In those situations, you can simply use setLoggingEnabled function to turn the logs on or off as you wish, just like this:

//to turn on the logs
Countly.setLoggingEnabled(true);

//some code in between
//<...>

//to turn off the logs
Countly.setLoggingEnabled(false);

Crash Reporting

Countly also provides a way to track NodeJS errors on your server.

To automatically capture and report JavaScript errors on your server, call the following function:

Countly.track_errors()

An uncaught exception is then recorded as a fatal crash. The SDK stores the crash right away and ends the process with exit code 1, as Node.js does without the SDK. The crash is sent at the next start.

An unhandled promise rejection is recorded as a non-fatal error, and the process keeps running. Without the SDK, Node.js 15 and newer end the process on an unhandled rejection.

You can additionally, add more segments or properties/values to track with error reports, by providing an object with key/values to add to error reports.

Countly.track_errors({
  "facebook_sdk": "2.3",
  "jquery": "1.8"
})

Apart from reporting unhandled errors automatically, you can also report handled exceptions to server too, so you can figure out how and even if you need to handle them later on. And optionally you can again provide custom segments to be used in the report (or use the ones provided with track_error method as default ones).

Countly.log_error(error, segments);

try{
  //do something here
}
catch(ex){
  //report error to Countly
  Countly.log_error(ex);
}

To better understand what happened prior to getting an error, you can leave out breadcrumbs through out the code, on different actions. This breadcrumb will be then combined in single log and reported to server too.

Countly.add_log("user clicked button a");

Events

Recording Events

An event is a way to track any custom actions or other data you want to track from your app. You can also provide segments to be able to view breakdown of action by provided segment values.

An event is a JavaScript object with these keys:

  • key: the name of the event (mandatory)
  • count: number of events (default: 1)
  • sum: sum to report with the event (optional)
  • dur: duration to report with the event (optional)
  • segmentation: an object with key/value pairs to report with the event as segments (optional). BigInt values are sent as text.

Here is an example of adding an event with all possible properties:

Countly.add_event({
  "key": "click",
  "count": 1,
  "sum": 1.5,
  "dur": 30,
  "segmentation": {
    "key1": "value1",
    "key2": "value2"
  }
});

Data passed should be in UTF-8

All data passed to Countly instance via SDK or API should be in UTF-8.

Timed Events

You can report time or duration with every event by providing dur property of the events object. But if you want, you can also let the SDK track the duration of some specific event for you, with the start_event and end_event methods.

First, you can start tracking event time by providing name of the event (which later on will be used as key for event object)

Countly.start_event("timedEvent")

Countly will internally mark the start of event and will wait until you end event with end_event method, setting up dur property based on how much time has passed since start_event for same event name was called.

//end event
Countly.end_event("timedEvent")

//or end event with additional data
Countly.end_event({
  "key": "timedEvent",
  "count": 1,
  "sum": 1.5,
  "segmentation": {
    "key1": "value1",
    "key2": "value2"
  }
});

Sessions

Manual Sessions

The SDK does not track sessions automatically. Use the methods below to begin, extend, and end sessions yourself.

If noHeartBeat is true, the SDK does not extend the session automatically, and you need to extend it yourself.

Countly.begin_session(noHeartBeat);

Extending a Session

By default (if noHeartBeat was not provided in begin_session) the SDK extends the session itself. If you chose not to, you can extend it with this method and provide the seconds since the last begin_session or session_duration call, whichever was the last one.

Countly.session_duration(sec)

Ending a Session

When a visitor leaves your app, end their session with this method, optionally providing the number of seconds since the last begin_session or session_duration call, whichever was the last one.

Countly.end_session(sec)

View Tracking

This method allows you to track different parts of your application, called views. You can track how much time is spent on each part of the application.

Countly.track_view("viewname");

And optionally, as the second parameter, you can provide view segments (key/value pairs) to track together with the view. There is a list of reserved segment keys that should not be used:

  • start
  • visit
  • bounce
  • end
  • name
  • domain
  • view
  • segment
  • platform
//Provide view segments
Countly.track_view("viewname", {theme:"red", mode:"fullscreen"});

Device ID Management

Retrieving Current Device ID

Countly offers a convenience method (get_device_id) for you to get the current user's device ID:

var id = Countly.get_device_id();

SDK records the type of an ID. These types are:

  • DEVELOPER_SUPPLIED
  • SDK_GENERATED

DEVELOPER_SUPPLIED device ID means this ID was assigned by your internal logic. SDK_GENERATED device ID means the ID was generated randomly by the SDK.

You can get the device ID type of a user by calling the get_device_id_type function:

var idType = Countly.get_device_id_type();

You can use the DeviceIdType enums to evaluate the device ID type you retrieved:

var idType = Countly.get_device_id_type();
if (idType === Countly.DeviceIdType.SDK_GENERATED) {
  // ...do something
}

Changing Device ID

You can change the device ID of a user with set_id method:

Countly.set_id("newId");

This method's effect on the server will be different according to the type of the current ID stored in the SDK at the time you call it:

  • If current stored ID is SDK_GENERATED then in the server all the information recorded for that device ID will be merged to the new ID you provide and old user with the SDK_GENERATED ID will be erased.
  • If the current stored ID is DEVELOPER_SUPPLIED then in the server it will also create a new user with this new ID if it does not exist.

If you need a more complicated logic or using the SDK version 24.10.0 and below then you will need to use this method mentioned here instead.

NOTE: The call will reject invalid device ID values. A valid value is not null, not undefined, of type string and is not an empty string.

Remote Config

Remote Config feature enables you to fetch data that you have created in your server. Depending on the conditions you have set, you can fetch data from your server for the specific users that fits those conditions and process the Remote Config data in anyway you want. Whether to change the background color of your site to showing a certain message, the possibilities are virtually endless. For more information on Remote Config please check here.

Automatic Remote Config

Automatic Remote Config functionality is disabled by default and needs to be explicitly enabled. When automatic Remote Config is enabled, the SDK will try to fetch it upon some specific triggers. For example, after SDK initialization, changing device ID.

You may enable this feature by providing to the remote_config flag a callback function or by setting it to true while initializing the SDK.

If you provide a callback, the callback will be called when the Remote Config is initially loaded and when it is reloaded if you change the device_id. This callback should have two parameters, first is for error, and second is for the Remote Config object.

// in your Countly init script
Countly.init({
  app_key:"YOUR_APP_KEY",
  url: "https://try.count.ly",
  debug: true,
  remote_config: true 
});

// OR

// provide a callback to be notified when configs are loaded
Countly.init({
  app_key:"YOUR_APP_KEY",
  url: "https://try.count.ly",
  debug: true,
  remote_config: function(err, remoteConfigs){
    if (!err) {
      //we have our remoteConfigs here
      console.log(remoteConfigs);
    }
  }
});

Manual Remote Config

If you want, you can manually fetch the Remote Config in order to receive the latest value anytime after the initialization. To do so you have to use the fetch_remote_config call. This method is also used for reloading the values for updating them according to the latest changes you made on your server.

By using this method, you can simply load the entire object or load some specific keys or omit some specific keys in order to decrease the amount of data transfer needed, assuming the values for some of the keys are large. This call will automatically save the fetched keys internally.

Fetch All Keys

Here you so not need to provide any parameters to the call but providing a callback is the recommended practice. This callback should have two parameters, first is for error, and second is for the Remote Config object.

// load the whole configuration object with a callback
Countly.fetch_remote_config(function(err, remoteConfigs){
  if (!err) {
    console.log(remoteConfigs);
    // or do something else here if you want with remoteConfigs object
  }
});

// or whole configuration object with no params
Countly.fetch_remote_config();

Fetch Specific Keys

Here the keys should be provided as string values in an array, as the first parameter in fetch_remote_config call. You can provide a callback function as a second parameter. This callback should have two parameters, first is for error, and second is for the Remote Config object.

// load specific keys only, as `key1` and `key2`
Countly.fetch_remote_config(["key1","key2"], function(err, remoteConfigs){
  if (!err) {
    console.log(remoteConfigs);
    // or do something else here if you want with remoteConfigs object
  }
});

Fetch All Except Specific Keys

Here the first parameter should be set to 'null' or 'undefined' and the keys that you want to omit must be provided as the second parameter as an array of keys as string. As a third parameter you can provide a callback function. This callback should have two parameters, first is for error, and second is for the Remote Config object.

// load all key values except specific keys, as `key1` and `key2'
Countly.fetch_remote_config(null, ["key1","key2"], function(err, remoteConfigs){
  if (!err) {
    console.log(remoteConfigs);
    // or do something else here if you want with remoteConfigs object
  }
});

Accessing Remote Config Values

You may call get_remote_config each time you would like to receive the Remote Config object of a value for a specific key or all keys from your local storage.

This method should be called once the Remote Config have been successfully loaded, or it will simply return an empty object or undefined values.

//get whole Remote Config object
var remoteConfig = Countly.get_remote_config();

//or get value for specific key like 'test'
var test = Countly.get_remote_config("test");

A/B Testing

While fetching Remote Config, the SDK will automatically enroll the user to A/B testing. For more information on A/B testing please check here.

Consent

If consents are enabled, to fetch the Remote Config data you have to provide the 'remote-config' consent for this feature to work.

User Feedback

If there is any way you can get some user feedback, there is not a simple method to report collected data to Countly.

//user feedback
Countly.report_feedback({
  widget_id:"1234567890",
  contactMe: true,
  rating: 5,
  email: "user@domain.com",
  comment: "Very good"
});

User Profiles

User Profiles is a Countly Enterprise plugin and built-in Flex.

Setting User Properties

If you have any details about the user/visitor, you can provide that information to Countly. This will allow you to track each and every specific user on the "User Profiles" tab.

If a parameter is set as an empty string, it will be deleted on the server side.

The list of possible parameters you can pass is:

Countly.user_details({
  "name": "Arturs Sosins",
  "username": "ar2rsawseen",
  "email": "test@test.com",
  "organization": "Countly",
  "phone": "+37112345678",
  //Web URL pointing to user picture
  "picture": "https://pbs.twimg.com/profile_images/1442562237/012_n_400x400.jpg", 
  "gender": "M",
  "byear": 1987, //birth year
  "custom":{
    "key1":"value1",
    "key2":"value2",
    ...
  }
 });

Modifying Data

Additionally, you can manipulate custom data values in different ways, like incrementing the current value on the server or storing an array of values under the same property.

Below is the list of available methods:

Countly.userData.set(key, value) //set custom property
Countly.userData.set_once(key, value) //set custom property only if property does not exist
Countly.userData.increment(key) //increment value in key by one
Countly.userData.increment_by(key, value) //increment value in key by provided value
Countly.userData.multiply(key, value) //multiply value in key by provided value
Countly.userData.max(key, value) //save max value between current and provided
Countly.userData.min(key, value) //save min value between current and provided
Countly.userData.push(key, value) //add value to key as array element
Countly.userData.push_unique(key, value) //add value to key as array element, but only store unique values in array
Countly.userData.pull(key, value) //remove value from array under property with key as name
Countly.userData.save() //send userData to server

Application Performance Monitoring

You can report a trace using report_trace method. Contents of it depend on which trace you report.

Here is an example of how to report network trace:

//report network trace
Countly.report_trace({
  type: "network", //device or network
  name: "/some/endpoint", //use name to identify trace and group them by
  stz: 1234567890123, //start timestamp in miliseconds
  etz: 1234567890123, //end timestamp in miliseconds
  app_metrics: {
    response_time: 1000,
    response_code: 200,
    response_payload_size: 12345,
    request_payload_size: 5432
  }
});

 And here is an example of device trace:

//user built in method to report app start time
Countly.report_app_start();

//or report device trace manually
Countly.report_trace({
  type: "device", //device or network
  name: "My App", //use name to identify trace and group them by
  stz: 1234567890123, //start timestamp in miliseconds
  etz: 1234567890123, //end timestamp in miliseconds
  app_metrics: {
    duration: 1000,
  }
});

Or you can report any custom traces to provide duration:

//or report device trace manually
Countly.report_trace({
  type: "device", //device or network
  name: "Some process we launched", //use name to identify trace and group them by
  stz: 1234567890123, //start timestamp in miliseconds
  etz: 1234567890123, //end timestamp in miliseconds
  app_metrics: {
    duration: 1000,
  }
});

User Consent

This section describes how to set up GDPR compliant consent management with the Countly NodeJS SDK.

When consent management is enabled, each feature of the SDK works only after consent for it is given. The SDK does not store the consent state, so your app stores the user's choices and gives them to the SDK again at every start.

Feature Names

The Countly.features property lists the features that need consent:

  • sessions: allows sessions to be tracked
  • events: allows your events to be sent to the server
  • views: allows views to be tracked
  • crashes: allows errors, crashes, and breadcrumbs to be recorded
  • attribution: allows conversions to be reported
  • users: allows user details and custom properties to be sent
  • location: allows the country code, city, and IP address to be sent
  • star-rating: allows star ratings to be reported
  • apm: allows performance traces to be recorded
  • feedback: allows feedback to be reported
  • remote-config: allows remote config values to be downloaded

Setup During Init

To enable consent management, pass true to the require_consent flag during the SDK initialization. The SDK then tracks nothing until consent for a feature is given. SDK Manager can turn the requirement on or off, as described in the SDK Behavior Settings section.

Countly.init({
  app_key: "YOUR-APP-KEY",
  url: "https://your_server_url/",
  require_consent: true
});

Changing Consent

When the user chooses what may be tracked, store their choice and give it to the SDK with add_consent at every start. You can pass one feature or an array of features. When the user changes their mind, call add_consent or remove_consent. The SDK reports both to the server.

// give consent for one feature or several
Countly.add_consent("sessions");
Countly.add_consent(["events", "views", "crashes"]);

// remove consent
Countly.remove_consent("events");

If you call begin_session or track_view before the matching consent is given, the SDK keeps the last such call and runs it once the consent is given. A consent change made right before a device ID change or a flush call is sent right away, under the device ID of the user who made it.

When the device ID is changed without merging, all given consent is removed, as described in the Extended Device ID Management section.

Feature Groups

You can group features under your own name with group_features, and then give or remove consent for the whole group at once:

Countly.group_features({
  activity: ["sessions", "events", "views"],
  info: ["attribution", "users"]
});

// gives consent for sessions, events, and views
Countly.add_consent("activity");

Checking Consent

To check whether consent is given for a feature or a group, call check_consent. To check whether any consent is given at all, call check_any_consent.

var eventsAllowed = Countly.check_consent("events");
var anyAllowed = Countly.check_any_consent();

Security and Privacy

Parameter Tamper Protection

You can set an optional salt that the SDK uses to calculate a checksum of the request data. The SDK sends the checksum with each request, in the checksum256 field. Set exactly the same salt on the Countly Server (Management > Applications > Salt for checksum). When the salt is set on the server, every request is checked for a valid checksum before it is processed.

Countly.init({
  app_key: "YOUR-APP-KEY",
  url: "https://your_server_url/",
  salt: "your_salt"
});

Do not set a salt for an app that receives data from CountlyBulk. Countly servers do not process bulk data for apps that have a salt, and CountlyBulk prints a warning when salt is set.

Other Features and Notes

SDK Config Parameters Explained

Here are the properties you can set up on Countly initialization:

  • app_key: Mandatory! The App Key for your app which created in Countly.
  • url: Mandatory! Countly server URL. You must use your server URL here!
  • device_id: To identify a visitor, SDK will auto-generate if not provided!
  • clear_stored_device_id: Erases the device ID stored by an earlier run, so that the device_id you provide is used (default: false).
  • app_version: Version of your app or website.
  • country_code: Country code for your visitor.
  • city: Name of the city of your visitor.
  • ip_address: IP address of your visitor.
  • debug: Output debug info into the console (default: false).
  • interval: Set an interval how often to check if there is any data to report and report it (default: 500 ms).
  • fail_timeout: Set time in seconds to wait after a failed connection to the server (default: 60 seconds).
  • request_timeout: Time in seconds to wait for the server's answer. After that, the request counts as failed and is sent again later (default: 30 seconds, minimum: 1 second).
  • session_update: How often in seconds should session be extended (default: 60 seconds).
  • max_events: Maximum amount of events to send in one batch (default: 100).
  • queue_size: Maximum amount of requests to store while they cannot be sent (default: 1000).
  • force_post: Force using the post method for all requests (default: false)
  • salt: Secret used to add a checksum to every request. See the Parameter Tamper Protection section above.
  • storage_path: Where SDK would store data, including id, queues, etc. A relative path is resolved against the app's working directory (default: ".countly-storage").
  • storage_type: Determines which storage type will be applied. Built-in storage type options are:

    • File Storage: Countly.StorageTypes.FILE
    • Memory Storage: Countly.StorageTypes.MEMORY

    If custom_storage_method will be used, do not use the storage type. If the user did not set the storage type, SDK applies the default. Default is: Countly.StorageTypes.FILE

  • custom_storage_method: User-given storage methods that can be used instead of the default File Storage or the optional Memory Storage methods.

    • If no storage_path is provided with the custom method, the storage path will be the default path! (default: ".countly-storage")
    • The object must contain storeGet, storeSet, and storeRemove functions!

    Before applying this configuration option, read this section of the document to get more information about providing your storage methods

  • require_consent: pass true if you are implementing GDPR compatible consent management. It would prevent running any functionality without proper consent (default: false).
  • remote_config: Enable automatic remote config fetching, provide callback function to be notified when fetching done (default: false).
  • http_options: Function to get http options by reference and overwrite them, before running each request.
  • max_breadcrumb_count: Maximum amount of breadcrumbs to store for crash logs (default: 100)
  • metrics: Provide metrics override or custom metrics for this user. For more information on the specific metric keys used by Countly, check here.
  • standalone_worker: In a cluster worker, send the worker's data directly instead of handing it to the main process. Needed when the main process does not run the SDK, for example in PM2 cluster mode (default: false). See the Clusters and PM2 section below.
  • behavior_settings: SDK behavior settings to use until the settings have been fetched from the server once, in the format SDK Manager exports. See the SDK Behavior Settings section below.
  • disable_sdk_behavior_settings_updates: Do not fetch the SDK behavior settings from the server (default: false).
  • disable_health_check: Do not send the health check at start (default: false).
  • disable_backoff_mechanism: Never pause sending when the server answers slowly (default: false).

Setting up properties in Countly NodeJS SDK is as follows

Countly.init({
  debug:false,
  app_key:"YOUR_APP_KEY",
  device_id:"1234-1234-1234-1234",
  url: "https://your.server.ly",
  app_version: "1.2",
  country_code: "LV",
  city: "Riga",
  storage_type: Countly.StorageTypes.MEMORY,
  ip_address: "83.140.15.1",
  http_options: function(options){
    options.headers["user-agent"] = "Test";
  },
  metrics:{
    _os: "Ubuntu",
    _os_version: "16.04",
    _device: "aws-server"
  }
});

Example Integrations

apm_example is a simple APM integration.
bulk_import_example is a simple bulk import example.
example covers most of the functionalities.
multi-process is a simple demonstration for multi-instancing the Countly SDK.

SDK Internal Limits

If values or keys provided by the user exceeds certain internal limits, they will be truncated. Please have a look here for the list of properties effected by these limits.

These limits apply to the data you record. Countly's own events, such as views, star ratings, and feedback, keep their keys and segmentation, so that the server recognizes them. The segmentation you give to track_view is still limited.

You can override these internal limits during initialization. SDK Manager can change them as well, as described in the SDK Behavior Settings section below.

Key Length

max_key_length - 128 chars by default. Keys that exceed this limit will be truncated.

Countly.init({
  app_key:"YOUR_APP_KEY",
  url: "YOUR_SERVER_URL",
  max_key_length: 50
});

Value Size

max_value_size - 256 chars by default. Values that exceed this limit will be truncated.

Countly.init({
  app_key:"YOUR_APP_KEY",
  url: "YOUR_SERVER_URL",
  max_value_size: 12
});

Segmentation Values

max_segmentation_values - 100 dev entries by default. Key/value pairs that exceed this limit in a single event will be removed.

Countly.init({
  app_key:"YOUR_APP_KEY",
  url: "YOUR_SERVER_URL",
  max_segmentation_values: 67
});

Breadcrumb Count

max_breadcrumb_count - 100 entries by default. If the limit is exceeded, the oldest entry will be removed from stored breadcrumbs.

Countly.init({
  app_key:"YOUR_APP_KEY",
  url: "YOUR_SERVER_URL",
  max_breadcrumb_count: 45
});

Stack Trace Lines Per Thread

max_stack_trace_lines_per_thread - 30 lines by default. Crash stack trace lines that exceed this limit (per thread) will be removed.

Countly.init({
  app_key:"YOUR_APP_KEY",
  url: "YOUR_SERVER_URL",
  max_stack_trace_lines_per_thread: 23
});

Stack Trace Line Length

max_stack_trace_line_length - 200 chars by default. Crash stack trace lines that exceed this limit will be truncated.

Countly.init({
  app_key:"YOUR_APP_KEY",
  url: "YOUR_SERVER_URL",
  max_stack_trace_line_length: 10
});

Setting Maximum Request Queue Size

When you initialize Countly, you can specify a value for the queue_size flag. This flag limits the number of requests that can be stored in the request queue when the Countly server is unavailable or experiencing connection problems.

If the server is down, requests sent to it will be queued on the device. If the number of queued requests becomes excessive, it can cause problems with delivering the requests to the server, and can also take up valuable storage space on the device. To prevent this from happening, the queue_size flag limits the number of requests that can be stored in the queue.

If the number of requests in the queue reaches the queue_size limit, the oldest requests in the queue will be dropped, and the newest requests will take their place. This ensures that the queue does not become too large, and that the most recent requests are prioritized for delivery. Raw requests made with request count towards this limit as well.

If you do not specify a value for the queue_size flag, the default setting of 1,000 will be used. SDK Manager can change it with Request Queue Size.

Countly.init({
  app_key:"YOUR_APP_KEY",
  url: "https://try.count.ly",
  queue_size: 5000
});

Request Timeout Duration

A request fails when the server sends nothing for 30 seconds, or when the connection closes before the whole answer arrives. The SDK then sends the request again later. To change the time, set request_timeout in seconds. The minimum is one second. The option works the same way in CountlyBulk.

Countly.init({
  app_key: "YOUR-APP-KEY",
  url: "https://your_server_url/",
  request_timeout: 60
});

Attribution

When using Countly attribution analytics, you can also report conversion to Countly server, for e.g., when visitor purchased something or registered.

Note: that conversion for each user may be reported only once, all other conversions will be ignored for this same user

//or provide campaign id yourself
Countly.report_conversion("MyCampaignID");

Make Direct Request

If you are switching between users a lot, or changing some other data, which is hard to handle over multiple processes, etc. You can simply make a raw request with all possible SDK parameters described in API reference

Countly.request({
  app_key:"somekey", 
  device_id:"someid", 
  events:"[{'key':'val','count':1}]", 
  metrics:"{'_os':'Linux'}",
  begin_session:1
});

The SDK sends these values as they are, without applying the internal limits, and the request counts towards the request queue size.

Providing Custom Storage Methods

Countly NodeJS SDK permits the user to use their storage methods. To replace the SDK's built-in storage options (File and Memory) during configuration, you can provide preferred storage methods within an object to the custom_storage_method parameter.

The custom object must contain the storeSet, storeGet, and storeRemove functions for the SDK to accept the user-given methods and function correctly!

Objects containing the methods with correct naming will be evaluated as valid objects, and the SDK will use the provided custom methods as internal storage methods.

While evaluating objects, SDK checks for, if:

  • an object is provided
  • that object has the valid keys (storeSet, storeGet, storeRemove)
  • the values are functions

In cases where an invalid object is provided as the custom method, SDK will ignore the provided object and switch back to the default storage type, File storage.

Keep in mind that passing the internal validity check for the methods does not guarantee that the custom logic applied is correct. You must ensure that your custom implementation functions properly and that the SDK runs without issues when integrated with the custom methods.

If custom storage methods are provided, do not fill storage_type parameters during configuration.

If, during configuration, storage_path parameter is not provided, SDK will use the default storage path, a .countly-storage folder in the app's working directory. This parameter must be provided to use custom methods with other desired paths!

Custom Storage Object must contain the 3 main methods SDK uses internally;

  • storeSet(key, value, callback) - must save the provided key/value pair into the storage. Callback is a function that is invoked to indicate the result of the operation after the operation is complete or failed. Parameters:
    • key(string) - the key to associate with the value.
    • value(null | boolean | number | string | object) - the value to store (can be null, primitive, or an object).
    • callback(function) - after the value is stored under the given key, you should call the callback if given, with the error object of the failed operation or with null if the operation succeeds.
  • storeGet(key, def) - must return the value for the provided key. If the value for the key does not exist, it should return the def value. Parameters:
    • key(string) - the key that's associated with the value.
    • def(null | boolean | number | string | object) - default value to use if it doesn't set (can be null, primitive, or an object).
  • storeRemove(key) - must remove the key and value from the storage for the provided key. Parameters:
    • key(string) - the key that's associated with the value to remove.

Custom Storage Method object should be like this:

const customStorageMethods = {
    /**
     * @example
     * customStorageMethods.storeSet('myKey', 'myValue', function(error) {
     *   if (error) {
     *     console.error('Failed to store value:', error);
     *   } else {
     *     console.log('Value stored successfully.');
     *   }
     * });
     */
    storeSet: function(key, value, callback) {
        // your storeSet method
    },
    /**
     * @example
     * let value = customStorageMethods.storeGet('myKey', 'defaultValue');
     * console.log('Retrieved value:', value);
     */
    storeGet: function(key, def) {
        // your storeGet method
    },
    /**
     * @example
     * customStorageMethods.storeRemove('myKey');
     */
    storeRemove: function(key) {
        // your storeRemove method
    },
};

Extended Device ID Management

In some cases you may want to change the ID of the user/device that you provided or Countly generated automatically, for example, when user was changed.

Countly.change_id("myNewId");

If device ID is changed without merging and consent was enabled, all previously given consent will be removed. This means that all features will cease to function until new consent has been given again for that new device ID.

When you change the device ID without merging, the SDK first sends the previous user's open view duration and unsaved custom property changes under the previous device ID. It also clears the stored remote config values, the crash breadcrumbs, and the calls waiting for consent, so that nothing of the previous user carries over to the new one. Remote config values that were still downloading for the previous user are ignored, and the fetch_remote_config callback receives an error. An empty device ID is ignored with a warning.

In some cases, you may also need to change user's device ID in a way, that server will merge data of both user IDs (existing and new ID you provided) on the server, for e.g., when user used website without authenticating and have recorded some data, and then authenticated and you want to change ID to your internal id of this user, to keep tracking it across multiple devices.

This call will merge any data recorded for current ID and save it as user with new provided ID.

Countly.change_id("myNewId", true);

Clusters and PM2

In a Node.js cluster, the workers hand their data to the main process, which sends it, so the main process must run the SDK too. When it does not, for example in PM2 cluster mode, a worker prints a warning, also when logging is off, and nothing it records is sent. In that case, set standalone_worker so that each worker sends its own data:

Countly.init({
  app_key: "YOUR-APP-KEY",
  url: "https://your_server_url/",
  standalone_worker: true,
  device_id: "my-service"
});

Each worker then keeps its data in its own worker-<number> folder inside the storage folder. The folder is numbered by PM2's instance number, or else by the cluster worker ID. The cluster worker ID changes when a worker restarts, so pass a device_id to keep one device ID.

The workers and the main process exchange messages that have a cly property. Your own message handlers in the main process and in workers receive these messages as well, so ignore messages with a cly property. In a worker that hands its data to the main process, flush resolves false, because the main process sends the data.

SDK Behavior Settings

The SDK follows the SDK behavior settings set for your app in SDK Manager. In the Sidebar of Countly, go to Utilities > SDK Manager. The SDK fetches the settings at start, every four hours by default, and after a device ID change. It stores them, so they also apply from the next start on, before the server answers.

A setting set in SDK Manager takes precedence over the matching init option. When the setting is removed from SDK Manager, the init option or the default applies again. For more information on how the settings work, see How SDKs Work.

Availability

All of the settings below need Countly 26.01 or later. Countly 25.03 offers Allow Tracking and Allow Networking. On older servers, the SDK keeps its init options and defaults.

The Node.js SDK follows these settings:

  • Allow Tracking: when it is turned off, the SDK records nothing new, including consent changes and request calls. Data recorded before is still sent.
  • Allow Networking: when it is turned off, the SDK keeps new data stored and sends it once networking is turned on again. Meanwhile, flush resolves false right away, and fetch_remote_config calls its callback with an error.
  • Allow Session Tracking, Allow View Tracking, Allow Custom Event Tracking, Allow Crash Tracking, and Allow Location Tracking, each on its own.
  • Require Consent: SDK Manager can make consent required, or lift a requirement set with require_consent. When SDK Manager does not set it, require_consent applies.
  • Request Queue Size, Event Queue Size, and Session Update Interval.
  • The SDK limits: Max Key Length, Max Value Size, Max Number of Segments, Max Breadcrumb Count, Max Trace Line Per Thread, and Max Trace Length Per Line.
  • Request Drop Age: queued requests older than this number of hours are dropped instead of sent, and a flush call waiting for them resolves false. It is off by default.
  • The backoff settings described in the Backoff Mechanism section below, and SDK Behavior Update Interval.

The settings request is sent also when consent is required and not given, and while networking is turned off, so that a change in SDK Manager always reaches the SDK.

Providing Settings

Until the SDK has fetched the settings once, it uses its init options. To apply settings from the very first start, for example for a process that must not send anything until SDK Manager allows it, download them with the Download Config button in SDK Manager, which saves an sdk-config.json file. Then pass its content with behavior_settings. Settings fetched and stored from the server take precedence over them.

Countly.init({
  app_key: "YOUR-APP-KEY",
  url: "https://your_server_url/",
  // the file saved with Download Config
  behavior_settings: require("./sdk-config.json")
});

You can also write the settings yourself, in the same format, for example { c: { tracking: false } }.

Disabling Updates

To keep the SDK from fetching the settings, set disable_sdk_behavior_settings_updates. Settings stored earlier and settings passed with behavior_settings still apply.

Countly.init({
  app_key: "YOUR-APP-KEY",
  url: "https://your_server_url/",
  disable_sdk_behavior_settings_updates: true
});

SDK Health Check

Once per start, after fetching the SDK behavior settings, the SDK sends a health check. It reports the number of errors and warnings the SDK logged, also when logging is off, and the status code and answer of the last request the server refused. It also reports how often sending paused because the server answered slowly, and the app version.

You can see the results in the Health Check tab of SDK Manager, which helps you find out why data is missing. The SDK keeps the numbers until the server confirms them. The health check is sent also when consent is required and not given, and not while networking is turned off. For more information, see How SDKs Work.

To turn the health check off, set disable_health_check:

Countly.init({
  app_key: "YOUR-APP-KEY",
  url: "https://your_server_url/",
  disable_health_check: true
});

Backoff Mechanism

When the server needs 10 seconds or more to answer a request, the SDK pauses sending for 60 seconds, so that a busy server can recover. It pauses only when its request queue is at most half full and the request was recorded within the last 24 hours, so a large backlog is still sent without pauses. flush does not wait for a pause.

SDK Manager can change these values with Backoff Timeout Limit, Backoff Requests Queue Percentage, Backoff Requests Age, and Backoff Delay, or turn the mechanism off with Enable Backoff Mechanism. For a detailed explanation, see A Deeper Look at SDK Concepts.

To never pause, whatever the SDK Manager settings are, set disable_backoff_mechanism:

Countly.init({
  app_key: "YOUR-APP-KEY",
  url: "https://your_server_url/",
  disable_backoff_mechanism: true
});

FAQ

Where Does the SDK Store the Data?

In a .countly-storage folder in your app's working directory, unless you set storage_path, use the memory storage type, or provide your own storage methods. The SDK Data Storage section above describes the details.

What Information is Collected by the SDK?

The data that SDKs gather to carry out their tasks and implement the necessary functionalities is mentioned here

At start, the SDK also sends an SDK behavior settings request and a health check. Both carry the app key, the device ID and its type, the SDK name and version, the app version, and the time of the request. The health check also carries the SDK's own error and warning counts, and the status code and answer of the last request the server refused.

Was this page helpful?
Reach out to us for any other questions.
Helpful?

Looking For More Help?