@splitsoftware/splitio-commons 2.6.0 → 2.6.1-rc.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGES.txt +3 -0
- package/cjs/listeners/browser.js +1 -0
- package/cjs/logger/constants.js +4 -3
- package/cjs/logger/index.js +37 -18
- package/cjs/logger/messages/info.js +1 -0
- package/cjs/logger/sdkLogger.js +7 -0
- package/cjs/storages/inMemory/EventsCacheInMemory.js +1 -0
- package/cjs/storages/inMemory/ImpressionCountsCacheInMemory.js +1 -0
- package/cjs/storages/inMemory/ImpressionsCacheInMemory.js +1 -0
- package/cjs/storages/inMemory/TelemetryCacheInMemory.js +1 -0
- package/cjs/storages/inMemory/UniqueKeysCacheInMemory.js +1 -0
- package/cjs/storages/inMemory/UniqueKeysCacheInMemoryCS.js +1 -0
- package/cjs/sync/submitters/eventsSubmitter.js +2 -3
- package/cjs/sync/submitters/impressionCountsSubmitter.js +1 -1
- package/cjs/sync/submitters/impressionsSubmitter.js +2 -4
- package/cjs/sync/submitters/submitter.js +2 -1
- package/cjs/sync/submitters/telemetrySubmitter.js +3 -2
- package/cjs/sync/submitters/uniqueKeysSubmitter.js +2 -3
- package/cjs/utils/settingsValidation/logger/builtinLogger.js +4 -3
- package/cjs/utils/settingsValidation/logger/commons.js +12 -15
- package/cjs/utils/settingsValidation/logger/pluggableLogger.js +9 -6
- package/esm/listeners/browser.js +2 -1
- package/esm/logger/constants.js +1 -0
- package/esm/logger/index.js +37 -18
- package/esm/logger/messages/info.js +1 -0
- package/esm/logger/sdkLogger.js +7 -0
- package/esm/storages/inMemory/EventsCacheInMemory.js +1 -0
- package/esm/storages/inMemory/ImpressionCountsCacheInMemory.js +1 -0
- package/esm/storages/inMemory/ImpressionsCacheInMemory.js +1 -0
- package/esm/storages/inMemory/TelemetryCacheInMemory.js +1 -0
- package/esm/storages/inMemory/UniqueKeysCacheInMemory.js +1 -0
- package/esm/storages/inMemory/UniqueKeysCacheInMemoryCS.js +1 -0
- package/esm/sync/submitters/eventsSubmitter.js +2 -3
- package/esm/sync/submitters/impressionCountsSubmitter.js +1 -1
- package/esm/sync/submitters/impressionsSubmitter.js +2 -4
- package/esm/sync/submitters/submitter.js +2 -1
- package/esm/sync/submitters/telemetrySubmitter.js +3 -2
- package/esm/sync/submitters/uniqueKeysSubmitter.js +2 -3
- package/esm/utils/settingsValidation/logger/builtinLogger.js +4 -3
- package/esm/utils/settingsValidation/logger/commons.js +10 -14
- package/esm/utils/settingsValidation/logger/pluggableLogger.js +10 -7
- package/package.json +1 -1
- package/src/listeners/browser.ts +2 -1
- package/src/logger/constants.ts +1 -0
- package/src/logger/index.ts +36 -22
- package/src/logger/messages/info.ts +1 -0
- package/src/logger/sdkLogger.ts +7 -0
- package/src/logger/types.ts +4 -3
- package/src/storages/inMemory/EventsCacheInMemory.ts +1 -0
- package/src/storages/inMemory/ImpressionCountsCacheInMemory.ts +2 -0
- package/src/storages/inMemory/ImpressionsCacheInMemory.ts +1 -0
- package/src/storages/inMemory/TelemetryCacheInMemory.ts +2 -0
- package/src/storages/inMemory/UniqueKeysCacheInMemory.ts +3 -3
- package/src/storages/inMemory/UniqueKeysCacheInMemoryCS.ts +3 -3
- package/src/storages/types.ts +1 -0
- package/src/sync/submitters/eventsSubmitter.ts +2 -4
- package/src/sync/submitters/impressionCountsSubmitter.ts +1 -1
- package/src/sync/submitters/impressionsSubmitter.ts +2 -5
- package/src/sync/submitters/submitter.ts +1 -1
- package/src/sync/submitters/telemetrySubmitter.ts +3 -2
- package/src/sync/submitters/uniqueKeysSubmitter.ts +2 -3
- package/src/utils/settingsValidation/logger/builtinLogger.ts +5 -4
- package/src/utils/settingsValidation/logger/commons.ts +11 -11
- package/src/utils/settingsValidation/logger/pluggableLogger.ts +12 -8
- package/types/splitio.d.ts +26 -0
|
@@ -20,6 +20,7 @@ export function fromUniqueKeysCollector(uniqueKeys) {
|
|
|
20
20
|
var UniqueKeysCacheInMemory = /** @class */ (function () {
|
|
21
21
|
function UniqueKeysCacheInMemory(uniqueKeysQueueSize) {
|
|
22
22
|
if (uniqueKeysQueueSize === void 0) { uniqueKeysQueueSize = DEFAULT_CACHE_SIZE; }
|
|
23
|
+
this.name = 'unique keys';
|
|
23
24
|
this.uniqueTrackerSize = 0;
|
|
24
25
|
this.uniqueKeysTracker = {};
|
|
25
26
|
this.maxStorage = uniqueKeysQueueSize;
|
|
@@ -3,6 +3,7 @@ import { setToArray } from '../../utils/lang/sets';
|
|
|
3
3
|
var UniqueKeysCacheInMemoryCS = /** @class */ (function () {
|
|
4
4
|
function UniqueKeysCacheInMemoryCS(uniqueKeysQueueSize) {
|
|
5
5
|
if (uniqueKeysQueueSize === void 0) { uniqueKeysQueueSize = DEFAULT_CACHE_SIZE; }
|
|
6
|
+
this.name = 'unique keys';
|
|
6
7
|
this.uniqueTrackerSize = 0;
|
|
7
8
|
this.uniqueKeysTracker = {};
|
|
8
9
|
this.maxStorage = uniqueKeysQueueSize;
|
|
@@ -1,20 +1,19 @@
|
|
|
1
1
|
import { submitterFactory, firstPushWindowDecorator } from './submitter';
|
|
2
2
|
import { SUBMITTERS_PUSH_FULL_QUEUE } from '../../logger/constants';
|
|
3
|
-
var DATA_NAME = 'events';
|
|
4
3
|
/**
|
|
5
4
|
* Submitter that periodically posts tracked events
|
|
6
5
|
*/
|
|
7
6
|
export function eventsSubmitterFactory(params) {
|
|
8
7
|
var _a = params.settings, log = _a.log, eventsPushRate = _a.scheduler.eventsPushRate, eventsFirstPushWindow = _a.startup.eventsFirstPushWindow, postEventsBulk = params.splitApi.postEventsBulk, events = params.storage.events;
|
|
9
8
|
// don't retry events.
|
|
10
|
-
var submitter = submitterFactory(log, postEventsBulk, events, eventsPushRate
|
|
9
|
+
var submitter = submitterFactory(log, postEventsBulk, events, eventsPushRate);
|
|
11
10
|
// Set a timer for the first push window of events.
|
|
12
11
|
if (eventsFirstPushWindow > 0)
|
|
13
12
|
submitter = firstPushWindowDecorator(submitter, eventsFirstPushWindow);
|
|
14
13
|
// register events submitter to be executed when events cache is full
|
|
15
14
|
events.setOnFullQueueCb(function () {
|
|
16
15
|
if (submitter.isRunning()) {
|
|
17
|
-
log.info(SUBMITTERS_PUSH_FULL_QUEUE, [
|
|
16
|
+
log.info(SUBMITTERS_PUSH_FULL_QUEUE, [events.name]);
|
|
18
17
|
submitter.execute();
|
|
19
18
|
}
|
|
20
19
|
// If submitter is stopped (e.g., user consent declined or unknown, or app state offline), we don't send the data.
|
|
@@ -27,5 +27,5 @@ var IMPRESSIONS_COUNT_RATE = 1800000; // 30 minutes
|
|
|
27
27
|
export function impressionCountsSubmitterFactory(params) {
|
|
28
28
|
var log = params.settings.log, postTestImpressionsCount = params.splitApi.postTestImpressionsCount, impressionCounts = params.storage.impressionCounts;
|
|
29
29
|
// retry impressions counts only once.
|
|
30
|
-
return submitterFactory(log, postTestImpressionsCount, impressionCounts, IMPRESSIONS_COUNT_RATE,
|
|
30
|
+
return submitterFactory(log, postTestImpressionsCount, impressionCounts, IMPRESSIONS_COUNT_RATE, fromImpressionCountsCollector, 1);
|
|
31
31
|
}
|
|
@@ -1,14 +1,12 @@
|
|
|
1
1
|
import { groupBy, forOwn } from '../../utils/lang';
|
|
2
2
|
import { submitterFactory } from './submitter';
|
|
3
3
|
import { SUBMITTERS_PUSH_FULL_QUEUE } from '../../logger/constants';
|
|
4
|
-
var DATA_NAME = 'impressions';
|
|
5
4
|
/**
|
|
6
5
|
* Converts `impressions` data from cache into request payload.
|
|
7
6
|
*/
|
|
8
7
|
export function fromImpressionsCollector(sendLabels, data) {
|
|
9
8
|
var groupedByFeature = groupBy(data, 'feature');
|
|
10
9
|
var dto = [];
|
|
11
|
-
// using forOwn instead of for...in since the last also iterates over prototype enumerables
|
|
12
10
|
forOwn(groupedByFeature, function (value, name) {
|
|
13
11
|
dto.push({
|
|
14
12
|
f: name,
|
|
@@ -35,11 +33,11 @@ export function fromImpressionsCollector(sendLabels, data) {
|
|
|
35
33
|
export function impressionsSubmitterFactory(params) {
|
|
36
34
|
var _a = params.settings, log = _a.log, impressionsRefreshRate = _a.scheduler.impressionsRefreshRate, labelsEnabled = _a.core.labelsEnabled, postTestImpressionsBulk = params.splitApi.postTestImpressionsBulk, impressions = params.storage.impressions;
|
|
37
35
|
// retry impressions only once.
|
|
38
|
-
var syncTask = submitterFactory(log, postTestImpressionsBulk, impressions, impressionsRefreshRate,
|
|
36
|
+
var syncTask = submitterFactory(log, postTestImpressionsBulk, impressions, impressionsRefreshRate, fromImpressionsCollector.bind(undefined, labelsEnabled), 1);
|
|
39
37
|
// register impressions submitter to be executed when impressions cache is full
|
|
40
38
|
impressions.setOnFullQueueCb(function () {
|
|
41
39
|
if (syncTask.isRunning()) {
|
|
42
|
-
log.info(SUBMITTERS_PUSH_FULL_QUEUE, [
|
|
40
|
+
log.info(SUBMITTERS_PUSH_FULL_QUEUE, [impressions.name]);
|
|
43
41
|
syncTask.execute();
|
|
44
42
|
}
|
|
45
43
|
// If submitter is stopped (e.g., user consent declined or unknown, or app state offline), we don't send the data.
|
|
@@ -3,9 +3,10 @@ import { SUBMITTERS_PUSH, SUBMITTERS_PUSH_FAILS, SUBMITTERS_PUSH_RETRY } from '.
|
|
|
3
3
|
/**
|
|
4
4
|
* Base function to create submitters, such as ImpressionsSubmitter and EventsSubmitter
|
|
5
5
|
*/
|
|
6
|
-
export function submitterFactory(log, postClient, sourceCache, postRate,
|
|
6
|
+
export function submitterFactory(log, postClient, sourceCache, postRate, fromCacheToPayload, maxRetries, debugLogs // true for telemetry submitters
|
|
7
7
|
) {
|
|
8
8
|
if (maxRetries === void 0) { maxRetries = 0; }
|
|
9
|
+
var dataName = sourceCache.name;
|
|
9
10
|
var retries = 0;
|
|
10
11
|
var data;
|
|
11
12
|
function postData() {
|
|
@@ -53,6 +53,7 @@ export function getTelemetryConfigStats(mode, storageType) {
|
|
|
53
53
|
*/
|
|
54
54
|
export function telemetryCacheConfigAdapter(telemetry, settings) {
|
|
55
55
|
return {
|
|
56
|
+
name: 'telemetry config',
|
|
56
57
|
isEmpty: function () { return false; },
|
|
57
58
|
clear: function () { },
|
|
58
59
|
pop: function () {
|
|
@@ -102,7 +103,7 @@ export function telemetrySubmitterFactory(params) {
|
|
|
102
103
|
return; // No submitter created if telemetry cache is not defined
|
|
103
104
|
var settings = params.settings, _a = params.settings, log = _a.log, telemetryRefreshRate = _a.scheduler.telemetryRefreshRate, splitApi = params.splitApi, readiness = params.readiness, sdkReadinessManager = params.sdkReadinessManager;
|
|
104
105
|
var startTime = timer(now);
|
|
105
|
-
var submitter = firstPushWindowDecorator(submitterFactory(log, splitApi.postMetricsUsage, telemetry, telemetryRefreshRate,
|
|
106
|
+
var submitter = firstPushWindowDecorator(submitterFactory(log, splitApi.postMetricsUsage, telemetry, telemetryRefreshRate, undefined, 0, true), telemetryRefreshRate);
|
|
106
107
|
readiness.gate.once(SDK_READY_FROM_CACHE, function () {
|
|
107
108
|
telemetry.recordTimeUntilReadyFromCache(startTime());
|
|
108
109
|
});
|
|
@@ -111,7 +112,7 @@ export function telemetrySubmitterFactory(params) {
|
|
|
111
112
|
telemetry.recordTimeUntilReady(startTime());
|
|
112
113
|
// Post config data when the SDK is ready and if the telemetry submitter was started
|
|
113
114
|
if (submitter.isRunning()) {
|
|
114
|
-
var postMetricsConfigTask = submitterFactory(log, splitApi.postMetricsConfig, telemetryCacheConfigAdapter(telemetry, settings), 0,
|
|
115
|
+
var postMetricsConfigTask = submitterFactory(log, splitApi.postMetricsConfig, telemetryCacheConfigAdapter(telemetry, settings), 0, undefined, 0, true);
|
|
115
116
|
postMetricsConfigTask.execute();
|
|
116
117
|
}
|
|
117
118
|
});
|
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
import { SUBMITTERS_PUSH_FULL_QUEUE } from '../../logger/constants';
|
|
2
2
|
import { submitterFactory } from './submitter';
|
|
3
|
-
var DATA_NAME = 'unique keys';
|
|
4
3
|
var UNIQUE_KEYS_RATE = 900000; // 15 minutes
|
|
5
4
|
/**
|
|
6
5
|
* Submitter that periodically posts impression counts
|
|
@@ -9,11 +8,11 @@ export function uniqueKeysSubmitterFactory(params) {
|
|
|
9
8
|
var _a = params.settings, log = _a.log, key = _a.core.key, _b = params.splitApi, postUniqueKeysBulkCs = _b.postUniqueKeysBulkCs, postUniqueKeysBulkSs = _b.postUniqueKeysBulkSs, uniqueKeys = params.storage.uniqueKeys;
|
|
10
9
|
var isClientSide = key !== undefined;
|
|
11
10
|
var postUniqueKeysBulk = isClientSide ? postUniqueKeysBulkCs : postUniqueKeysBulkSs;
|
|
12
|
-
var syncTask = submitterFactory(log, postUniqueKeysBulk, uniqueKeys, UNIQUE_KEYS_RATE
|
|
11
|
+
var syncTask = submitterFactory(log, postUniqueKeysBulk, uniqueKeys, UNIQUE_KEYS_RATE);
|
|
13
12
|
// register unique keys submitter to be executed when uniqueKeys cache is full
|
|
14
13
|
uniqueKeys.setOnFullQueueCb(function () {
|
|
15
14
|
if (syncTask.isRunning()) {
|
|
16
|
-
log.info(SUBMITTERS_PUSH_FULL_QUEUE, [
|
|
15
|
+
log.info(SUBMITTERS_PUSH_FULL_QUEUE, [uniqueKeys.name]);
|
|
17
16
|
syncTask.execute();
|
|
18
17
|
}
|
|
19
18
|
// If submitter is stopped (e.g., user consent declined or unknown, or app state offline), we don't send the data.
|
|
@@ -32,11 +32,12 @@ else if (isLogLevelString(initialState)) {
|
|
|
32
32
|
* @returns a logger instance with the log level at `settings.debug`. If `settings.debug` is invalid or not provided, `initialLogLevel` is used.
|
|
33
33
|
*/
|
|
34
34
|
export function validateLogger(settings) {
|
|
35
|
-
var debug = settings.debug;
|
|
35
|
+
var debug = settings.debug, logger = settings.logger;
|
|
36
36
|
var logLevel = debug !== undefined ? getLogLevel(debug) : initialLogLevel;
|
|
37
37
|
var log = new Logger({ logLevel: logLevel || initialLogLevel }, allCodes);
|
|
38
|
-
|
|
38
|
+
log.setLogger(logger);
|
|
39
|
+
// if logLevel is undefined at this point, it means that settings `debug` value is invalid
|
|
39
40
|
if (!logLevel)
|
|
40
|
-
log._log(
|
|
41
|
+
log._log('error', 'Invalid Log Level - No changes to the logs will be applied.');
|
|
41
42
|
return log;
|
|
42
43
|
}
|
|
@@ -7,18 +7,14 @@ import { LogLevels, isLogLevelString } from '../../../logger';
|
|
|
7
7
|
* @returns LogLevel of the given debugValue or undefined if the provided value is invalid
|
|
8
8
|
*/
|
|
9
9
|
export function getLogLevel(debugValue) {
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
}
|
|
21
|
-
else {
|
|
22
|
-
return undefined;
|
|
23
|
-
}
|
|
10
|
+
return typeof debugValue === 'boolean' ?
|
|
11
|
+
debugValue ?
|
|
12
|
+
LogLevels.DEBUG :
|
|
13
|
+
LogLevels.NONE :
|
|
14
|
+
typeof debugValue === 'string' && isLogLevelString(debugValue) ?
|
|
15
|
+
debugValue :
|
|
16
|
+
undefined;
|
|
17
|
+
}
|
|
18
|
+
export function isLogger(log) {
|
|
19
|
+
return log !== null && typeof log === 'object' && typeof log.debug === 'function' && typeof log.info === 'function' && typeof log.warn === 'function' && typeof log.error === 'function';
|
|
24
20
|
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { Logger, LogLevels } from '../../../logger';
|
|
2
|
-
import { getLogLevel } from './commons';
|
|
3
|
-
function
|
|
4
|
-
return log
|
|
2
|
+
import { getLogLevel, isLogger } from './commons';
|
|
3
|
+
function isILogger(log) {
|
|
4
|
+
return isLogger(log) && typeof log.setLogLevel === 'function';
|
|
5
5
|
}
|
|
6
6
|
// By default it starts disabled.
|
|
7
7
|
var initialLogLevel = LogLevels.NONE;
|
|
@@ -13,16 +13,19 @@ var initialLogLevel = LogLevels.NONE;
|
|
|
13
13
|
* or one with NONE log level if `debug` is not defined or invalid.
|
|
14
14
|
*/
|
|
15
15
|
export function validateLogger(settings) {
|
|
16
|
-
var debug = settings.debug;
|
|
16
|
+
var debug = settings.debug, logger = settings.logger;
|
|
17
17
|
var logLevel = initialLogLevel;
|
|
18
18
|
if (debug !== undefined) {
|
|
19
|
-
if (
|
|
19
|
+
if (isILogger(debug)) {
|
|
20
|
+
debug.setLogger(logger);
|
|
20
21
|
return debug;
|
|
22
|
+
}
|
|
21
23
|
logLevel = getLogLevel(settings.debug);
|
|
22
24
|
}
|
|
23
25
|
var log = new Logger({ logLevel: logLevel || initialLogLevel });
|
|
24
|
-
|
|
26
|
+
log.setLogger(logger);
|
|
27
|
+
// `debug` value is invalid if logLevel is undefined at this point
|
|
25
28
|
if (!logLevel)
|
|
26
|
-
log._log(
|
|
29
|
+
log._log('error', 'Invalid `debug` value at config. Logs will be disabled.');
|
|
27
30
|
return log;
|
|
28
31
|
}
|
package/package.json
CHANGED
package/src/listeners/browser.ts
CHANGED
|
@@ -9,7 +9,7 @@ import { ISettings } from '../types';
|
|
|
9
9
|
import SplitIO from '../../types/splitio';
|
|
10
10
|
import { ImpressionsPayload } from '../sync/submitters/types';
|
|
11
11
|
import { objectAssign } from '../utils/lang/objectAssign';
|
|
12
|
-
import { CLEANUP_REGISTERING, CLEANUP_DEREGISTERING } from '../logger/constants';
|
|
12
|
+
import { CLEANUP_REGISTERING, CLEANUP_DEREGISTERING, SUBMITTERS_PUSH_PAGE_HIDDEN } from '../logger/constants';
|
|
13
13
|
import { ISyncManager } from '../sync/types';
|
|
14
14
|
import { isConsentGranted } from '../consent';
|
|
15
15
|
|
|
@@ -104,6 +104,7 @@ export class BrowserSignalListener implements ISignalListener {
|
|
|
104
104
|
if (!this._sendBeacon(url, dataPayload, extraMetadata)) {
|
|
105
105
|
postService(JSON.stringify(dataPayload)).catch(() => { }); // no-op to handle possible promise rejection
|
|
106
106
|
}
|
|
107
|
+
this.settings.log.debug(SUBMITTERS_PUSH_PAGE_HIDDEN, [cache.name]);
|
|
107
108
|
}
|
|
108
109
|
}
|
|
109
110
|
|
package/src/logger/constants.ts
CHANGED
|
@@ -55,6 +55,7 @@ export const IMPRESSIONS_TRACKER_SUCCESS = 121;
|
|
|
55
55
|
export const USER_CONSENT_UPDATED = 122;
|
|
56
56
|
export const USER_CONSENT_NOT_UPDATED = 123;
|
|
57
57
|
export const USER_CONSENT_INITIAL = 124;
|
|
58
|
+
export const SUBMITTERS_PUSH_PAGE_HIDDEN = 125;
|
|
58
59
|
|
|
59
60
|
export const ENGINE_VALUE_INVALID = 200;
|
|
60
61
|
export const ENGINE_VALUE_NO_ATTRIBUTES = 201;
|
package/src/logger/index.ts
CHANGED
|
@@ -2,6 +2,7 @@ import { objectAssign } from '../utils/lang/objectAssign';
|
|
|
2
2
|
import { ILoggerOptions, ILogger } from './types';
|
|
3
3
|
import { find, isObject } from '../utils/lang';
|
|
4
4
|
import SplitIO from '../../types/splitio';
|
|
5
|
+
import { isLogger } from '../utils/settingsValidation/logger/commons';
|
|
5
6
|
|
|
6
7
|
export const LogLevels: SplitIO.ILoggerAPI['LogLevel'] = {
|
|
7
8
|
DEBUG: 'DEBUG',
|
|
@@ -19,6 +20,13 @@ const LogLevelIndexes = {
|
|
|
19
20
|
NONE: 5
|
|
20
21
|
};
|
|
21
22
|
|
|
23
|
+
const DEFAULT_LOGGER: SplitIO.Logger = {
|
|
24
|
+
debug(msg) { console.log('[DEBUG] ' + msg); },
|
|
25
|
+
info(msg) { console.log('[INFO] ' + msg); },
|
|
26
|
+
warn(msg) { console.log('[WARN] ' + msg); },
|
|
27
|
+
error(msg) { console.log('[ERROR] ' + msg); }
|
|
28
|
+
};
|
|
29
|
+
|
|
22
30
|
export function isLogLevelString(str: string): str is SplitIO.LogLevel {
|
|
23
31
|
return !!find(LogLevels, (lvl: string) => str === lvl);
|
|
24
32
|
}
|
|
@@ -40,7 +48,6 @@ export function _sprintf(format: string = '', args: any[] = []): string {
|
|
|
40
48
|
const defaultOptions = {
|
|
41
49
|
prefix: 'splitio',
|
|
42
50
|
logLevel: LogLevels.NONE,
|
|
43
|
-
showLevel: true,
|
|
44
51
|
};
|
|
45
52
|
|
|
46
53
|
export class Logger implements ILogger {
|
|
@@ -48,6 +55,7 @@ export class Logger implements ILogger {
|
|
|
48
55
|
private options: Required<ILoggerOptions>;
|
|
49
56
|
private codes: Map<number, string>;
|
|
50
57
|
private logLevel: number;
|
|
58
|
+
private logger?: SplitIO.Logger;
|
|
51
59
|
|
|
52
60
|
constructor(options?: ILoggerOptions, codes?: Map<number, string>) {
|
|
53
61
|
this.options = objectAssign({}, defaultOptions, options);
|
|
@@ -60,23 +68,38 @@ export class Logger implements ILogger {
|
|
|
60
68
|
this.logLevel = LogLevelIndexes[logLevel];
|
|
61
69
|
}
|
|
62
70
|
|
|
71
|
+
setLogger(logger?: SplitIO.Logger) {
|
|
72
|
+
if (logger) {
|
|
73
|
+
if (isLogger(logger)) {
|
|
74
|
+
this.logger = logger;
|
|
75
|
+
// If custom logger is set, all logs are either enabled or disabled
|
|
76
|
+
if (this.logLevel !== LogLevelIndexes.NONE) this.setLogLevel(LogLevels.DEBUG);
|
|
77
|
+
return;
|
|
78
|
+
} else {
|
|
79
|
+
this.error('Invalid `logger` instance. It must be an object with `debug`, `info`, `warn` and `error` methods. Defaulting to `console.log`');
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
// unset
|
|
83
|
+
this.logger = undefined;
|
|
84
|
+
}
|
|
85
|
+
|
|
63
86
|
debug(msg: string | number, args?: any[]) {
|
|
64
|
-
if (this._shouldLog(LogLevelIndexes.DEBUG)) this._log(
|
|
87
|
+
if (this._shouldLog(LogLevelIndexes.DEBUG)) this._log('debug', msg, args);
|
|
65
88
|
}
|
|
66
89
|
|
|
67
90
|
info(msg: string | number, args?: any[]) {
|
|
68
|
-
if (this._shouldLog(LogLevelIndexes.INFO)) this._log(
|
|
91
|
+
if (this._shouldLog(LogLevelIndexes.INFO)) this._log('info', msg, args);
|
|
69
92
|
}
|
|
70
93
|
|
|
71
94
|
warn(msg: string | number, args?: any[]) {
|
|
72
|
-
if (this._shouldLog(LogLevelIndexes.WARN)) this._log(
|
|
95
|
+
if (this._shouldLog(LogLevelIndexes.WARN)) this._log('warn', msg, args);
|
|
73
96
|
}
|
|
74
97
|
|
|
75
98
|
error(msg: string | number, args?: any[]) {
|
|
76
|
-
if (this._shouldLog(LogLevelIndexes.ERROR)) this._log(
|
|
99
|
+
if (this._shouldLog(LogLevelIndexes.ERROR)) this._log('error', msg, args);
|
|
77
100
|
}
|
|
78
101
|
|
|
79
|
-
|
|
102
|
+
_log(method: keyof SplitIO.Logger, msg: string | number, args?: any[]) {
|
|
80
103
|
if (typeof msg === 'number') {
|
|
81
104
|
const format = this.codes.get(msg);
|
|
82
105
|
msg = format ? _sprintf(format, args) : `Message code ${msg}${args ? ', with args: ' + args.toString() : ''}`;
|
|
@@ -84,24 +107,15 @@ export class Logger implements ILogger {
|
|
|
84
107
|
if (args) msg = _sprintf(msg, args);
|
|
85
108
|
}
|
|
86
109
|
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
console.log(formattedText);
|
|
90
|
-
}
|
|
110
|
+
if (this.options.prefix) msg = this.options.prefix + ' => ' + msg;
|
|
91
111
|
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
result += '[' + level + ']' + (level === LogLevels.INFO || level === LogLevels.WARN ? ' ' : '') + ' ';
|
|
98
|
-
}
|
|
99
|
-
|
|
100
|
-
if (this.options.prefix) {
|
|
101
|
-
result += this.options.prefix + textPre;
|
|
112
|
+
if (this.logger) {
|
|
113
|
+
try {
|
|
114
|
+
this.logger[method](msg);
|
|
115
|
+
return;
|
|
116
|
+
} catch (e) { /* empty */ }
|
|
102
117
|
}
|
|
103
|
-
|
|
104
|
-
return result += text;
|
|
118
|
+
DEFAULT_LOGGER[method](msg);
|
|
105
119
|
}
|
|
106
120
|
|
|
107
121
|
private _shouldLog(level: number) {
|
|
@@ -25,6 +25,7 @@ export const codesInfo: [number, string][] = codesWarn.concat([
|
|
|
25
25
|
[c.SYNC_SPLITS_FETCH_RETRY, c.LOG_PREFIX_SYNC_SPLITS + 'Retrying download of feature flags #%s. Reason: %s'],
|
|
26
26
|
[c.SUBMITTERS_PUSH_FULL_QUEUE, c.LOG_PREFIX_SYNC_SUBMITTERS + 'Flushing full %s queue and resetting timer.'],
|
|
27
27
|
[c.SUBMITTERS_PUSH, c.LOG_PREFIX_SYNC_SUBMITTERS + 'Pushing %s.'],
|
|
28
|
+
[c.SUBMITTERS_PUSH_PAGE_HIDDEN, c.LOG_PREFIX_SYNC_SUBMITTERS + 'Flushing %s because page became hidden.'],
|
|
28
29
|
[c.STREAMING_REFRESH_TOKEN, c.LOG_PREFIX_SYNC_STREAMING + 'Refreshing streaming token in %s seconds, and connecting streaming in %s seconds.'],
|
|
29
30
|
[c.STREAMING_RECONNECT, c.LOG_PREFIX_SYNC_STREAMING + 'Attempting to reconnect streaming in %s seconds.'],
|
|
30
31
|
[c.STREAMING_CONNECTING, c.LOG_PREFIX_SYNC_STREAMING + 'Connecting streaming.'],
|
package/src/logger/sdkLogger.ts
CHANGED
|
@@ -30,6 +30,13 @@ export function createLoggerAPI(log: ILogger): SplitIO.ILoggerAPI {
|
|
|
30
30
|
* @param logLevel - Custom LogLevel value.
|
|
31
31
|
*/
|
|
32
32
|
setLogLevel,
|
|
33
|
+
/**
|
|
34
|
+
* Sets a custom logger for the SDK logs.
|
|
35
|
+
* @param logger - Custom logger.
|
|
36
|
+
*/
|
|
37
|
+
setLogger(logger?: ILogger) {
|
|
38
|
+
log.setLogger(logger);
|
|
39
|
+
},
|
|
33
40
|
/**
|
|
34
41
|
* Disables all the log levels.
|
|
35
42
|
*/
|
package/src/logger/types.ts
CHANGED
|
@@ -1,12 +1,13 @@
|
|
|
1
1
|
import SplitIO from '../../types/splitio';
|
|
2
2
|
|
|
3
3
|
export interface ILoggerOptions {
|
|
4
|
-
prefix?: string
|
|
5
|
-
logLevel?: SplitIO.LogLevel
|
|
6
|
-
showLevel?: boolean, // @TODO remove this param eventually since it is not being set `false` anymore
|
|
4
|
+
prefix?: string;
|
|
5
|
+
logLevel?: SplitIO.LogLevel;
|
|
7
6
|
}
|
|
8
7
|
|
|
9
8
|
export interface ILogger extends SplitIO.ILogger {
|
|
9
|
+
setLogger(logger?: SplitIO.Logger): void;
|
|
10
|
+
|
|
10
11
|
debug(msg: any): void;
|
|
11
12
|
debug(msg: string | number, args?: any[]): void;
|
|
12
13
|
|
|
@@ -3,6 +3,8 @@ import { DEFAULT_CACHE_SIZE } from '../inRedis/constants';
|
|
|
3
3
|
import { IImpressionCountsCacheSync } from '../types';
|
|
4
4
|
|
|
5
5
|
export class ImpressionCountsCacheInMemory implements IImpressionCountsCacheSync {
|
|
6
|
+
|
|
7
|
+
public name = 'impression counts';
|
|
6
8
|
protected cache: Record<string, number> = {};
|
|
7
9
|
private readonly maxStorage: number;
|
|
8
10
|
protected onFullQueue?: () => void;
|
|
@@ -3,6 +3,7 @@ import SplitIO from '../../../types/splitio';
|
|
|
3
3
|
|
|
4
4
|
export class ImpressionsCacheInMemory implements IImpressionsCacheSync {
|
|
5
5
|
|
|
6
|
+
public name = 'impressions';
|
|
6
7
|
private onFullQueue?: () => void;
|
|
7
8
|
private readonly maxQueue: number;
|
|
8
9
|
private queue: SplitIO.ImpressionDTO[];
|
|
@@ -25,6 +25,8 @@ export function shouldRecordTelemetry({ settings }: IStorageFactoryParams) {
|
|
|
25
25
|
|
|
26
26
|
export class TelemetryCacheInMemory implements ITelemetryCacheSync {
|
|
27
27
|
|
|
28
|
+
public name = 'telemetry stats';
|
|
29
|
+
|
|
28
30
|
constructor(private splits?: ISplitsCacheSync, private segments?: ISegmentsCacheSync, private largeSegments?: ISegmentsCacheSync) { }
|
|
29
31
|
|
|
30
32
|
// isEmpty flag
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { IUniqueKeysCacheSync } from '../types';
|
|
2
2
|
import { UniqueKeysPayloadSs } from '../../sync/submitters/types';
|
|
3
3
|
import { DEFAULT_CACHE_SIZE } from '../inRedis/constants';
|
|
4
4
|
import { setToArray } from '../../utils/lang/sets';
|
|
@@ -22,8 +22,8 @@ export function fromUniqueKeysCollector(uniqueKeys: { [featureName: string]: Set
|
|
|
22
22
|
return { keys: payload };
|
|
23
23
|
}
|
|
24
24
|
|
|
25
|
-
export class UniqueKeysCacheInMemory implements
|
|
26
|
-
|
|
25
|
+
export class UniqueKeysCacheInMemory implements IUniqueKeysCacheSync {
|
|
26
|
+
public name = 'unique keys';
|
|
27
27
|
protected onFullQueue?: () => void;
|
|
28
28
|
private readonly maxStorage: number;
|
|
29
29
|
private uniqueTrackerSize = 0;
|
|
@@ -1,10 +1,10 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { IUniqueKeysCacheSync } from '../types';
|
|
2
2
|
import { UniqueKeysPayloadCs } from '../../sync/submitters/types';
|
|
3
3
|
import { DEFAULT_CACHE_SIZE } from '../inRedis/constants';
|
|
4
4
|
import { setToArray } from '../../utils/lang/sets';
|
|
5
5
|
|
|
6
|
-
export class UniqueKeysCacheInMemoryCS implements
|
|
7
|
-
|
|
6
|
+
export class UniqueKeysCacheInMemoryCS implements IUniqueKeysCacheSync {
|
|
7
|
+
public name = 'unique keys';
|
|
8
8
|
private onFullQueue?: () => void;
|
|
9
9
|
private readonly maxStorage: number;
|
|
10
10
|
private uniqueTrackerSize = 0;
|
package/src/storages/types.ts
CHANGED
|
@@ -325,6 +325,7 @@ export interface IUniqueKeysCacheBase {
|
|
|
325
325
|
|
|
326
326
|
// API methods for sync recorder storages, used by submitters in standalone mode to pop data and post it to Split BE.
|
|
327
327
|
export interface IRecorderCacheSync<T> {
|
|
328
|
+
name: string,
|
|
328
329
|
// @TODO names are inconsistent with spec
|
|
329
330
|
/* Checks if cache is empty. Returns true if the cache was just created or cleared */
|
|
330
331
|
isEmpty(): boolean
|
|
@@ -2,8 +2,6 @@ import { submitterFactory, firstPushWindowDecorator } from './submitter';
|
|
|
2
2
|
import { SUBMITTERS_PUSH_FULL_QUEUE } from '../../logger/constants';
|
|
3
3
|
import { ISdkFactoryContextSync } from '../../sdkFactory/types';
|
|
4
4
|
|
|
5
|
-
const DATA_NAME = 'events';
|
|
6
|
-
|
|
7
5
|
/**
|
|
8
6
|
* Submitter that periodically posts tracked events
|
|
9
7
|
*/
|
|
@@ -16,7 +14,7 @@ export function eventsSubmitterFactory(params: ISdkFactoryContextSync) {
|
|
|
16
14
|
} = params;
|
|
17
15
|
|
|
18
16
|
// don't retry events.
|
|
19
|
-
let submitter = submitterFactory(log, postEventsBulk, events, eventsPushRate
|
|
17
|
+
let submitter = submitterFactory(log, postEventsBulk, events, eventsPushRate);
|
|
20
18
|
|
|
21
19
|
// Set a timer for the first push window of events.
|
|
22
20
|
if (eventsFirstPushWindow > 0) submitter = firstPushWindowDecorator(submitter, eventsFirstPushWindow);
|
|
@@ -24,7 +22,7 @@ export function eventsSubmitterFactory(params: ISdkFactoryContextSync) {
|
|
|
24
22
|
// register events submitter to be executed when events cache is full
|
|
25
23
|
events.setOnFullQueueCb(() => {
|
|
26
24
|
if (submitter.isRunning()) {
|
|
27
|
-
log.info(SUBMITTERS_PUSH_FULL_QUEUE, [
|
|
25
|
+
log.info(SUBMITTERS_PUSH_FULL_QUEUE, [events.name]);
|
|
28
26
|
submitter.execute();
|
|
29
27
|
}
|
|
30
28
|
// If submitter is stopped (e.g., user consent declined or unknown, or app state offline), we don't send the data.
|
|
@@ -40,5 +40,5 @@ export function impressionCountsSubmitterFactory(params: ISdkFactoryContextSync)
|
|
|
40
40
|
} = params;
|
|
41
41
|
|
|
42
42
|
// retry impressions counts only once.
|
|
43
|
-
return submitterFactory(log, postTestImpressionsCount, impressionCounts, IMPRESSIONS_COUNT_RATE,
|
|
43
|
+
return submitterFactory(log, postTestImpressionsCount, impressionCounts, IMPRESSIONS_COUNT_RATE, fromImpressionCountsCollector, 1);
|
|
44
44
|
}
|
|
@@ -5,8 +5,6 @@ import { ImpressionsPayload } from './types';
|
|
|
5
5
|
import { SUBMITTERS_PUSH_FULL_QUEUE } from '../../logger/constants';
|
|
6
6
|
import { ISdkFactoryContextSync } from '../../sdkFactory/types';
|
|
7
7
|
|
|
8
|
-
const DATA_NAME = 'impressions';
|
|
9
|
-
|
|
10
8
|
/**
|
|
11
9
|
* Converts `impressions` data from cache into request payload.
|
|
12
10
|
*/
|
|
@@ -14,7 +12,6 @@ export function fromImpressionsCollector(sendLabels: boolean, data: SplitIO.Impr
|
|
|
14
12
|
let groupedByFeature = groupBy(data, 'feature');
|
|
15
13
|
let dto: ImpressionsPayload = [];
|
|
16
14
|
|
|
17
|
-
// using forOwn instead of for...in since the last also iterates over prototype enumerables
|
|
18
15
|
forOwn(groupedByFeature, (value, name) => {
|
|
19
16
|
dto.push({
|
|
20
17
|
f: name, // Test Name
|
|
@@ -50,12 +47,12 @@ export function impressionsSubmitterFactory(params: ISdkFactoryContextSync) {
|
|
|
50
47
|
} = params;
|
|
51
48
|
|
|
52
49
|
// retry impressions only once.
|
|
53
|
-
const syncTask = submitterFactory(log, postTestImpressionsBulk, impressions, impressionsRefreshRate,
|
|
50
|
+
const syncTask = submitterFactory(log, postTestImpressionsBulk, impressions, impressionsRefreshRate, fromImpressionsCollector.bind(undefined, labelsEnabled), 1);
|
|
54
51
|
|
|
55
52
|
// register impressions submitter to be executed when impressions cache is full
|
|
56
53
|
impressions.setOnFullQueueCb(() => {
|
|
57
54
|
if (syncTask.isRunning()) {
|
|
58
|
-
log.info(SUBMITTERS_PUSH_FULL_QUEUE, [
|
|
55
|
+
log.info(SUBMITTERS_PUSH_FULL_QUEUE, [impressions.name]);
|
|
59
56
|
syncTask.execute();
|
|
60
57
|
}
|
|
61
58
|
// If submitter is stopped (e.g., user consent declined or unknown, or app state offline), we don't send the data.
|
|
@@ -13,12 +13,12 @@ export function submitterFactory<T>(
|
|
|
13
13
|
postClient: (body: string) => Promise<IResponse>,
|
|
14
14
|
sourceCache: IRecorderCacheSync<T>,
|
|
15
15
|
postRate: number,
|
|
16
|
-
dataName: string,
|
|
17
16
|
fromCacheToPayload?: (cacheData: T) => any,
|
|
18
17
|
maxRetries: number = 0,
|
|
19
18
|
debugLogs?: boolean // true for telemetry submitters
|
|
20
19
|
): ISyncTask<[], void> {
|
|
21
20
|
|
|
21
|
+
const dataName = sourceCache.name;
|
|
22
22
|
let retries = 0;
|
|
23
23
|
let data: any;
|
|
24
24
|
|
|
@@ -66,6 +66,7 @@ export function getTelemetryConfigStats(mode: SplitIO.SDKMode, storageType: stri
|
|
|
66
66
|
*/
|
|
67
67
|
export function telemetryCacheConfigAdapter(telemetry: ITelemetryCacheSync, settings: ISettings) {
|
|
68
68
|
return {
|
|
69
|
+
name: 'telemetry config',
|
|
69
70
|
isEmpty() { return false; },
|
|
70
71
|
clear() { },
|
|
71
72
|
|
|
@@ -124,7 +125,7 @@ export function telemetrySubmitterFactory(params: ISdkFactoryContextSync) {
|
|
|
124
125
|
submitterFactory(
|
|
125
126
|
log, splitApi.postMetricsUsage,
|
|
126
127
|
telemetry,
|
|
127
|
-
telemetryRefreshRate,
|
|
128
|
+
telemetryRefreshRate, undefined, 0, true
|
|
128
129
|
),
|
|
129
130
|
telemetryRefreshRate
|
|
130
131
|
);
|
|
@@ -139,7 +140,7 @@ export function telemetrySubmitterFactory(params: ISdkFactoryContextSync) {
|
|
|
139
140
|
|
|
140
141
|
// Post config data when the SDK is ready and if the telemetry submitter was started
|
|
141
142
|
if (submitter.isRunning()) {
|
|
142
|
-
const postMetricsConfigTask = submitterFactory(log, splitApi.postMetricsConfig, telemetryCacheConfigAdapter(telemetry, settings), 0,
|
|
143
|
+
const postMetricsConfigTask = submitterFactory(log, splitApi.postMetricsConfig, telemetryCacheConfigAdapter(telemetry, settings), 0, undefined, 0, true);
|
|
143
144
|
postMetricsConfigTask.execute();
|
|
144
145
|
}
|
|
145
146
|
});
|
|
@@ -2,7 +2,6 @@ import { SUBMITTERS_PUSH_FULL_QUEUE } from '../../logger/constants';
|
|
|
2
2
|
import { ISdkFactoryContextSync } from '../../sdkFactory/types';
|
|
3
3
|
import { submitterFactory } from './submitter';
|
|
4
4
|
|
|
5
|
-
const DATA_NAME = 'unique keys';
|
|
6
5
|
const UNIQUE_KEYS_RATE = 900000; // 15 minutes
|
|
7
6
|
|
|
8
7
|
/**
|
|
@@ -19,12 +18,12 @@ export function uniqueKeysSubmitterFactory(params: ISdkFactoryContextSync) {
|
|
|
19
18
|
const isClientSide = key !== undefined;
|
|
20
19
|
const postUniqueKeysBulk = isClientSide ? postUniqueKeysBulkCs : postUniqueKeysBulkSs;
|
|
21
20
|
|
|
22
|
-
const syncTask = submitterFactory(log, postUniqueKeysBulk, uniqueKeys, UNIQUE_KEYS_RATE
|
|
21
|
+
const syncTask = submitterFactory(log, postUniqueKeysBulk, uniqueKeys, UNIQUE_KEYS_RATE);
|
|
23
22
|
|
|
24
23
|
// register unique keys submitter to be executed when uniqueKeys cache is full
|
|
25
24
|
uniqueKeys.setOnFullQueueCb(() => {
|
|
26
25
|
if (syncTask.isRunning()) {
|
|
27
|
-
log.info(SUBMITTERS_PUSH_FULL_QUEUE, [
|
|
26
|
+
log.info(SUBMITTERS_PUSH_FULL_QUEUE, [uniqueKeys.name]);
|
|
28
27
|
syncTask.execute();
|
|
29
28
|
}
|
|
30
29
|
// If submitter is stopped (e.g., user consent declined or unknown, or app state offline), we don't send the data.
|