@splitsoftware/splitio-commons 2.5.1-rc.1 → 2.6.1-rc.0
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 +18 -0
- 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 +2 -1
- package/cjs/utils/settingsValidation/logger/commons.js +12 -15
- package/cjs/utils/settingsValidation/logger/pluggableLogger.js +7 -4
- package/esm/listeners/browser.js +2 -1
- package/esm/logger/constants.js +1 -0
- package/esm/logger/index.js +18 -0
- 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 +2 -1
- package/esm/utils/settingsValidation/logger/commons.js +10 -14
- package/esm/utils/settingsValidation/logger/pluggableLogger.js +8 -5
- package/package.json +1 -1
- package/src/listeners/browser.ts +2 -1
- package/src/logger/constants.ts +1 -0
- package/src/logger/index.ts +19 -0
- package/src/logger/messages/info.ts +1 -0
- package/src/logger/sdkLogger.ts +7 -0
- package/src/logger/types.ts +5 -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 +3 -2
- package/src/utils/settingsValidation/logger/commons.ts +11 -11
- package/src/utils/settingsValidation/logger/pluggableLogger.ts +10 -6
- package/types/splitio.d.ts +22 -0
|
@@ -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,9 +32,10 @@ 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
|
+
log.setLogger(logger);
|
|
38
39
|
// @ts-ignore // if logLevel is undefined at this point, it means that settings `debug` value is invalid
|
|
39
40
|
if (!logLevel)
|
|
40
41
|
log._log(LogLevels.ERROR, 'Invalid Log Level - No changes to the logs will be applied.');
|
|
@@ -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,14 +13,17 @@ 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 });
|
|
26
|
+
log.setLogger(logger);
|
|
24
27
|
// @ts-ignore // `debug` value is invalid if logLevel is undefined at this point
|
|
25
28
|
if (!logLevel)
|
|
26
29
|
log._log(LogLevels.ERROR, 'Invalid `debug` value at config. Logs will be disabled.');
|
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',
|
|
@@ -48,6 +49,7 @@ export class Logger implements ILogger {
|
|
|
48
49
|
private options: Required<ILoggerOptions>;
|
|
49
50
|
private codes: Map<number, string>;
|
|
50
51
|
private logLevel: number;
|
|
52
|
+
private logger?: SplitIO.Logger;
|
|
51
53
|
|
|
52
54
|
constructor(options?: ILoggerOptions, codes?: Map<number, string>) {
|
|
53
55
|
this.options = objectAssign({}, defaultOptions, options);
|
|
@@ -60,6 +62,15 @@ export class Logger implements ILogger {
|
|
|
60
62
|
this.logLevel = LogLevelIndexes[logLevel];
|
|
61
63
|
}
|
|
62
64
|
|
|
65
|
+
setLogger(logger?: SplitIO.Logger) {
|
|
66
|
+
if (!logger || isLogger(logger)) {
|
|
67
|
+
this.logger = logger;
|
|
68
|
+
} else {
|
|
69
|
+
this._log(LogLevels.ERROR, 'Invalid `logger` instance. It must be an object with `debug`, `info`, `warn` and `error` methods. Defaulting to `console.log`');
|
|
70
|
+
this.logger = undefined;
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
|
|
63
74
|
debug(msg: string | number, args?: any[]) {
|
|
64
75
|
if (this._shouldLog(LogLevelIndexes.DEBUG)) this._log(LogLevels.DEBUG, msg, args);
|
|
65
76
|
}
|
|
@@ -86,6 +97,14 @@ export class Logger implements ILogger {
|
|
|
86
97
|
|
|
87
98
|
const formattedText = this._generateLogMessage(level, msg);
|
|
88
99
|
|
|
100
|
+
// Do not break on custom logger errors
|
|
101
|
+
if (this.logger) {
|
|
102
|
+
try { // @ts-expect-error
|
|
103
|
+
this.logger[level.toLowerCase()](formattedText);
|
|
104
|
+
return;
|
|
105
|
+
} catch (e) { /* empty */ }
|
|
106
|
+
}
|
|
107
|
+
|
|
89
108
|
console.log(formattedText);
|
|
90
109
|
}
|
|
91
110
|
|
|
@@ -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,14 @@
|
|
|
1
1
|
import SplitIO from '../../types/splitio';
|
|
2
2
|
|
|
3
3
|
export interface ILoggerOptions {
|
|
4
|
-
prefix?: string
|
|
5
|
-
logLevel?: SplitIO.LogLevel
|
|
6
|
-
showLevel?: boolean
|
|
4
|
+
prefix?: string;
|
|
5
|
+
logLevel?: SplitIO.LogLevel;
|
|
6
|
+
showLevel?: boolean; // @TODO remove this param eventually since it is not being set `false` anymore
|
|
7
7
|
}
|
|
8
8
|
|
|
9
9
|
export interface ILogger extends SplitIO.ILogger {
|
|
10
|
+
setLogger(logger?: SplitIO.Logger): void;
|
|
11
|
+
|
|
10
12
|
debug(msg: any): void;
|
|
11
13
|
debug(msg: string | number, args?: any[]): void;
|
|
12
14
|
|
|
@@ -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.
|
|
@@ -40,12 +40,13 @@ if (/^(enabled?|on)/i.test(initialState)) {
|
|
|
40
40
|
* @param settings - user config object, with an optional `debug` property of type boolean or string log level.
|
|
41
41
|
* @returns a logger instance with the log level at `settings.debug`. If `settings.debug` is invalid or not provided, `initialLogLevel` is used.
|
|
42
42
|
*/
|
|
43
|
-
export function validateLogger(settings: { debug: unknown }): ILogger {
|
|
44
|
-
const { debug } = settings;
|
|
43
|
+
export function validateLogger(settings: { debug: unknown, logger?: SplitIO.Logger }): ILogger {
|
|
44
|
+
const { debug, logger } = settings;
|
|
45
45
|
|
|
46
46
|
const logLevel: SplitIO.LogLevel | undefined = debug !== undefined ? getLogLevel(debug) : initialLogLevel;
|
|
47
47
|
|
|
48
48
|
const log = new Logger({ logLevel: logLevel || initialLogLevel }, allCodes);
|
|
49
|
+
log.setLogger(logger);
|
|
49
50
|
|
|
50
51
|
// @ts-ignore // if logLevel is undefined at this point, it means that settings `debug` value is invalid
|
|
51
52
|
if (!logLevel) log._log(LogLevels.ERROR, 'Invalid Log Level - No changes to the logs will be applied.');
|
|
@@ -10,15 +10,15 @@ import SplitIO from '../../../../types/splitio';
|
|
|
10
10
|
* @returns LogLevel of the given debugValue or undefined if the provided value is invalid
|
|
11
11
|
*/
|
|
12
12
|
export function getLogLevel(debugValue: unknown): SplitIO.LogLevel | undefined {
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
13
|
+
return typeof debugValue === 'boolean' ?
|
|
14
|
+
debugValue ?
|
|
15
|
+
LogLevels.DEBUG :
|
|
16
|
+
LogLevels.NONE :
|
|
17
|
+
typeof debugValue === 'string' && isLogLevelString(debugValue) ?
|
|
18
|
+
debugValue :
|
|
19
|
+
undefined;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
export function isLogger(log: any): log is SplitIO.Logger {
|
|
23
|
+
return log !== null && typeof log === 'object' && typeof log.debug === 'function' && typeof log.info === 'function' && typeof log.warn === 'function' && typeof log.error === 'function';
|
|
24
24
|
}
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
import { Logger, LogLevels } from '../../../logger';
|
|
2
2
|
import { ILogger } from '../../../logger/types';
|
|
3
3
|
import SplitIO from '../../../../types/splitio';
|
|
4
|
-
import { getLogLevel } from './commons';
|
|
4
|
+
import { getLogLevel, isLogger } from './commons';
|
|
5
5
|
|
|
6
|
-
function
|
|
7
|
-
return log
|
|
6
|
+
function isILogger(log: any): log is ILogger {
|
|
7
|
+
return isLogger(log) && typeof (log as any).setLogLevel === 'function';
|
|
8
8
|
}
|
|
9
9
|
|
|
10
10
|
// By default it starts disabled.
|
|
@@ -17,16 +17,20 @@ let initialLogLevel = LogLevels.NONE;
|
|
|
17
17
|
* @returns a logger instance, that might be: the provided logger at `settings.debug`, or one with the given `debug` log level,
|
|
18
18
|
* or one with NONE log level if `debug` is not defined or invalid.
|
|
19
19
|
*/
|
|
20
|
-
export function validateLogger(settings: { debug: unknown }): ILogger {
|
|
21
|
-
const { debug } = settings;
|
|
20
|
+
export function validateLogger(settings: { debug: unknown, logger?: SplitIO.Logger }): ILogger {
|
|
21
|
+
const { debug, logger } = settings;
|
|
22
22
|
let logLevel: SplitIO.LogLevel | undefined = initialLogLevel;
|
|
23
23
|
|
|
24
24
|
if (debug !== undefined) {
|
|
25
|
-
if (
|
|
25
|
+
if (isILogger(debug)) {
|
|
26
|
+
debug.setLogger(logger);
|
|
27
|
+
return debug;
|
|
28
|
+
}
|
|
26
29
|
logLevel = getLogLevel(settings.debug);
|
|
27
30
|
}
|
|
28
31
|
|
|
29
32
|
const log = new Logger({ logLevel: logLevel || initialLogLevel });
|
|
33
|
+
log.setLogger(logger);
|
|
30
34
|
|
|
31
35
|
// @ts-ignore // `debug` value is invalid if logLevel is undefined at this point
|
|
32
36
|
if (!logLevel) log._log(LogLevels.ERROR, 'Invalid `debug` value at config. Logs will be disabled.');
|
package/types/splitio.d.ts
CHANGED
|
@@ -91,6 +91,10 @@ interface ISharedSettings {
|
|
|
91
91
|
* Do not change these settings unless you're working an advanced use case, like connecting to the Split proxy.
|
|
92
92
|
*/
|
|
93
93
|
urls?: SplitIO.UrlSettings;
|
|
94
|
+
/**
|
|
95
|
+
* Custom logger object. If not provided, the SDK will use the default `console.log` method for all log levels.
|
|
96
|
+
*/
|
|
97
|
+
logger?: SplitIO.Logger;
|
|
94
98
|
}
|
|
95
99
|
/**
|
|
96
100
|
* Common settings properties for SDKs with synchronous API (standalone and localhost modes).
|
|
@@ -587,6 +591,7 @@ declare namespace SplitIO {
|
|
|
587
591
|
telemetry: string;
|
|
588
592
|
};
|
|
589
593
|
readonly integrations?: IntegrationFactory[];
|
|
594
|
+
readonly logger?: Logger;
|
|
590
595
|
readonly debug: boolean | LogLevel | ILogger;
|
|
591
596
|
readonly version: string;
|
|
592
597
|
/**
|
|
@@ -617,6 +622,15 @@ declare namespace SplitIO {
|
|
|
617
622
|
* Log levels.
|
|
618
623
|
*/
|
|
619
624
|
type LogLevel = 'DEBUG' | 'INFO' | 'WARN' | 'ERROR' | 'NONE';
|
|
625
|
+
/**
|
|
626
|
+
* Custom logger interface.
|
|
627
|
+
*/
|
|
628
|
+
interface Logger {
|
|
629
|
+
debug(message: string): any;
|
|
630
|
+
info(message: string): any;
|
|
631
|
+
warn(message: string): any;
|
|
632
|
+
error(message: string): any;
|
|
633
|
+
}
|
|
620
634
|
/**
|
|
621
635
|
* Logger API
|
|
622
636
|
*/
|
|
@@ -631,8 +645,16 @@ declare namespace SplitIO {
|
|
|
631
645
|
disable(): void;
|
|
632
646
|
/**
|
|
633
647
|
* Sets a log level for the SDK logs.
|
|
648
|
+
*
|
|
649
|
+
* @param logLevel - The log level to set.
|
|
634
650
|
*/
|
|
635
651
|
setLogLevel(logLevel: LogLevel): void;
|
|
652
|
+
/**
|
|
653
|
+
* Sets a custom logger for the SDK logs.
|
|
654
|
+
*
|
|
655
|
+
* @param logger - The custom logger to set, or `undefined` to remove the custom logger and fall back to the default `console.log` method.
|
|
656
|
+
*/
|
|
657
|
+
setLogger(logger?: Logger): void;
|
|
636
658
|
/**
|
|
637
659
|
* Log level constants. Use this to pass them to setLogLevel function.
|
|
638
660
|
*/
|