diff --git a/CHANGELOG.md b/CHANGELOG.md
index 80797e14..94ba33d1 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,5 +1,6 @@
## X.X.X
- Added a new flag, 'loadAPMScriptsAsync', which can load the APM related scripts automatically for Async implementations
+- Adding SDK health check requests after init
- Adding remaining request queue size information to every request
- Fixed a bug where unnecessary device ID merge information was sent to the server
diff --git a/cypress/integration/health_check.js b/cypress/integration/health_check.js
new file mode 100644
index 00000000..bb570df8
--- /dev/null
+++ b/cypress/integration/health_check.js
@@ -0,0 +1,37 @@
+/* eslint-disable require-jsdoc */
+var Countly = require("../../lib/countly");
+var hp = require("../support/helper");
+
+function initMain() {
+ Countly.init({
+ app_key: "YOUR_APP_KEY",
+ url: "https://try.count.ly",
+ test_mode: true
+ });
+}
+
+describe("Health Check tests ", () => {
+ it("Check if health check is sent at the beginning", () => {
+ hp.haltAndClearStorage(() => {
+ initMain();
+ cy.intercept("GET", "https://try.count.ly/i?**").as("getXhr");
+ cy.wait("@getXhr").then((xhr) => {
+ const url = new URL(xhr.request.url);
+
+ // Test the 'hc' parameter
+ const hcParam = url.searchParams.get("hc");
+ const hcParamObj = JSON.parse(hcParam);
+ expect(hcParamObj).to.eql({ el: 0, wl: 0, sc: -1, em: "\"\"" });
+
+ // Test the 'metrics' parameter
+ const metricsParam = url.searchParams.get("metrics");
+ expect(metricsParam).to.equal("{\"_app_version\":\"0.0\",\"_ua\":\"abcd\"}");
+
+ // check nothing in the request queue
+ cy.fetch_local_request_queue().then((rq) => {
+ expect(rq.length).to.equal(0);
+ });
+ });
+ });
+ });
+});
diff --git a/lib/countly.js b/lib/countly.js
index ee05723f..fe6cad83 100644
--- a/lib/countly.js
+++ b/lib/countly.js
@@ -164,6 +164,16 @@
BOOMERANG_SRC: "https://cdn.jsdelivr.net/npm/countly-sdk-web@latest/plugin/boomerang/boomerang.min.js",
CLY_BOOMERANG_SRC: "https://cdn.jsdelivr.net/npm/countly-sdk-web@latest/plugin/boomerang/countly_boomerang.js",
};
+
+ /**
+ * Health check counters' local storage keys
+ */
+ var healthCheckCounterEnum = Object.freeze({
+ errorCount: "cly_hc_error_count",
+ warningCount: "cly_hc_warning_count",
+ statusCode: "cly_hc_status_code",
+ errorMessage: "cly_hc_error_message",
+ });
/**
* Async api queue, push commands here to be executed when script is loaded or after
* @example
Add command as array
@@ -295,6 +305,10 @@
this.maxStackTraceLinesPerThread = getConfig("max_stack_trace_lines_per_thread", ob, configurationDefaultValues.MAX_STACKTRACE_LINES_PER_THREAD);
this.maxStackTraceLineLength = getConfig("max_stack_trace_line_length", ob, configurationDefaultValues.MAX_STACKTRACE_LINE_LENGTH);
this.heatmapWhitelist = getConfig("heatmap_whitelist", ob, []);
+ self.hcErrorCount = getValueFromStorage(healthCheckCounterEnum.errorCount) || 0;
+ self.hcWarningCount = getValueFromStorage(healthCheckCounterEnum.warningCount) || 0;
+ self.hcStatusCode = getValueFromStorage(healthCheckCounterEnum.statusCode) || -1;
+ self.hcErrorMessage = getValueFromStorage(healthCheckCounterEnum.errorMessage) || "";
if (maxCrashLogs && !this.maxBreadcrumbCount) {
this.maxBreadcrumbCount = maxCrashLogs;
@@ -3756,6 +3770,13 @@
if (last - lastBeat > sessionUpdate) {
self.session_duration(last - lastBeat);
lastBeat = last;
+ // save health check logging counters if there are any
+ if (self.hcErrorCount > 0) {
+ setValueInStorage(healthCheckCounterEnum.errorCount, self.hcErrorCount);
+ }
+ if (self.hcWarningCount > 0) {
+ setValueInStorage(healthCheckCounterEnum.warningCount, self.hcWarningCount);
+ }
}
}
@@ -3941,10 +3962,12 @@
if (level === logLevelEnums.ERROR) {
// eslint-disable-next-line no-console
console.error(log);
+ HealthCheck.incrementErrorCount();
}
else if (level === logLevelEnums.WARNING) {
// eslint-disable-next-line no-console
console.warn(log);
+ HealthCheck.incrementWarningCount();
}
else if (level === logLevelEnums.INFO) {
// eslint-disable-next-line no-console
@@ -4019,8 +4042,11 @@
}
else {
log(logLevelEnums.ERROR, functionName + " Failed Server XML HTTP request, ", this.status);
+ if (functionName === "send_request_queue") {
+ HealthCheck.saveRequestCounters(this.status, this.responseText);
+ }
if (typeof callback === "function") {
- callback(true, params);
+ callback(true, params, this.status, this.responseText);
}
}
}
@@ -4491,7 +4517,101 @@
}
};
+ /**
+ * Health Check Interface:
+ * {sendInstantHCRequest} Sends instant health check request
+ * {resetAndSaveCounters} Resets and saves health check counters
+ * {incrementErrorCount} Increments health check error count
+ * {incrementWarningCount} Increments health check warning count
+ * {resetCounters} Resets health check counters
+ * {saveRequestCounters} Saves health check request counters
+ */
+ var HealthCheck = {};
+ HealthCheck.sendInstantHCRequest = sendInstantHCRequest;
+ HealthCheck.resetAndSaveCounters = resetAndSaveCounters;
+ HealthCheck.incrementErrorCount = incrementErrorCount;
+ HealthCheck.incrementWarningCount = incrementWarningCount;
+ HealthCheck.resetCounters = resetCounters;
+ HealthCheck.saveRequestCounters = saveRequestCounters;
+ /**
+ * Increments health check error count
+ */
+ function incrementErrorCount() {
+ self.hcErrorCount++;
+ }
+ /**
+ * Increments health check warning count
+ */
+ function incrementWarningCount() {
+ self.hcWarningCount++;
+ }
+ /**
+ * Resets health check counters
+ */
+ function resetCounters() {
+ self.hcErrorCount = 0;
+ self.hcWarningCount = 0;
+ self.hcStatusCode = -1;
+ self.hcErrorMessage = "";
+ }
+ /**
+ * Sets and saves the status code and error message counters
+ * @param {number} status - response status code of the request
+ * @param {string} responseText - response text of the request
+ */
+ function saveRequestCounters(status, responseText) {
+ self.hcStatusCode = status;
+ self.hcErrorMessage = responseText;
+ setValueInStorage(healthCheckCounterEnum.statusCode, self.hcStatusCode);
+ setValueInStorage(healthCheckCounterEnum.errorMessage, self.hcErrorMessage);
+ }
+ /**
+ * Resets and saves health check counters
+ */
+ function resetAndSaveCounters() {
+ HealthCheck.resetCounters();
+ setValueInStorage(healthCheckCounterEnum.errorCount, self.hcErrorCount);
+ setValueInStorage(healthCheckCounterEnum.warningCount, self.hcWarningCount);
+ setValueInStorage(healthCheckCounterEnum.statusCode, self.hcStatusCode);
+ setValueInStorage(healthCheckCounterEnum.errorMessage, self.hcErrorMessage);
+ }
+ /**
+ * Countly health check request sender
+ */
+ function sendInstantHCRequest() {
+ // truncate error message to 1000 characters
+ var curbedMessage = truncateSingleValue(self.hcErrorMessage, 1000, "healthCheck", log);
+ // prepare hc object
+ var hc = {
+ el: self.hcErrorCount,
+ wl: self.hcWarningCount,
+ sc: self.hcStatusCode,
+ em: JSON.stringify(curbedMessage)
+ };
+ // prepare request
+ var request = {
+ hc: JSON.stringify(hc),
+ metrics: JSON.stringify({ _app_version: self.app_version })
+ };
+ // add common request params
+ prepareRequest(request);
+ // send request
+ sendXmlHttpRequest("healthCheck", self.url + apiPath, request, function(err, params, status, responseText) {
+ if (err) {
+ log(logLevelEnums.ERROR, "healthCheck, An error occurred. Status: [" + status + "], response: [" + responseText + "]");
+ }
+ else {
+ log(logLevelEnums.INFO, "healthCheck, Request was successful. Status: [" + status + "], response: [" + responseText + "]");
+ // reset and save health check counters if request was successful
+ HealthCheck.resetAndSaveCounters();
+ }
+ }, true);
+ }
+
+ // initialize Countly Class
this.initialize();
+ // send instant health check request
+ HealthCheck.sendInstantHCRequest();
};
/**