@webex/internal-plugin-metrics 3.12.0-next.5 → 3.12.0-next.51
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/README.md +274 -0
- package/dist/automated-user.js +19 -0
- package/dist/automated-user.js.map +1 -0
- package/dist/batcher.js +3 -0
- package/dist/batcher.js.map +1 -1
- package/dist/call-diagnostic/call-diagnostic-metrics-latencies.js +635 -62
- package/dist/call-diagnostic/call-diagnostic-metrics-latencies.js.map +1 -1
- package/dist/call-diagnostic/call-diagnostic-metrics.js +188 -78
- package/dist/call-diagnostic/call-diagnostic-metrics.js.map +1 -1
- package/dist/call-diagnostic/call-diagnostic-metrics.util.js +5 -0
- package/dist/call-diagnostic/call-diagnostic-metrics.util.js.map +1 -1
- package/dist/call-diagnostic/config.js +16 -3
- package/dist/call-diagnostic/config.js.map +1 -1
- package/dist/config.js +8 -0
- package/dist/config.js.map +1 -1
- package/dist/index.js +25 -1
- package/dist/index.js.map +1 -1
- package/dist/metrics.js +118 -5
- package/dist/metrics.js.map +1 -1
- package/dist/metrics.types.js +4 -0
- package/dist/metrics.types.js.map +1 -1
- package/dist/network-telemetry.js +610 -0
- package/dist/network-telemetry.js.map +1 -0
- package/dist/new-metrics.js +63 -17
- package/dist/new-metrics.js.map +1 -1
- package/dist/privacy-and-security-permission-enricher/constants.js +18 -0
- package/dist/privacy-and-security-permission-enricher/constants.js.map +1 -0
- package/dist/privacy-and-security-permission-enricher/index.js +143 -0
- package/dist/privacy-and-security-permission-enricher/index.js.map +1 -0
- package/dist/privacy-and-security-permission-enricher/types.js +7 -0
- package/dist/privacy-and-security-permission-enricher/types.js.map +1 -0
- package/dist/privacy-and-security-permission-enricher/utils.js +71 -0
- package/dist/privacy-and-security-permission-enricher/utils.js.map +1 -0
- package/dist/types/automated-user.d.ts +2 -0
- package/dist/types/call-diagnostic/call-diagnostic-metrics-latencies.d.ts +208 -5
- package/dist/types/call-diagnostic/call-diagnostic-metrics.d.ts +73 -14
- package/dist/types/call-diagnostic/config.d.ts +4 -0
- package/dist/types/config.d.ts +9 -0
- package/dist/types/index.d.ts +5 -3
- package/dist/types/metrics.types.d.ts +7 -2
- package/dist/types/network-telemetry.d.ts +108 -0
- package/dist/types/new-metrics.d.ts +17 -1
- package/dist/types/privacy-and-security-permission-enricher/constants.d.ts +6 -0
- package/dist/types/privacy-and-security-permission-enricher/index.d.ts +40 -0
- package/dist/types/privacy-and-security-permission-enricher/types.d.ts +14 -0
- package/dist/types/privacy-and-security-permission-enricher/utils.d.ts +5 -0
- package/dist/types/unhandled-exception-telemetry/index.d.ts +60 -0
- package/dist/types/unhandled-exception-telemetry/utils.d.ts +6 -0
- package/dist/unhandled-exception-telemetry/index.js +330 -0
- package/dist/unhandled-exception-telemetry/index.js.map +1 -0
- package/dist/unhandled-exception-telemetry/utils.js +105 -0
- package/dist/unhandled-exception-telemetry/utils.js.map +1 -0
- package/package.json +12 -11
- package/src/automated-user.ts +16 -0
- package/src/batcher.js +4 -0
- package/src/call-diagnostic/call-diagnostic-metrics-latencies.ts +781 -71
- package/src/call-diagnostic/call-diagnostic-metrics.ts +119 -16
- package/src/call-diagnostic/call-diagnostic-metrics.util.ts +5 -0
- package/src/call-diagnostic/config.ts +14 -0
- package/src/config.js +8 -0
- package/src/index.ts +16 -0
- package/src/metrics.js +123 -5
- package/src/metrics.types.ts +25 -3
- package/src/network-telemetry.ts +757 -0
- package/src/new-metrics.ts +56 -6
- package/src/privacy-and-security-permission-enricher/constants.ts +33 -0
- package/src/privacy-and-security-permission-enricher/index.ts +142 -0
- package/src/privacy-and-security-permission-enricher/types.ts +21 -0
- package/src/privacy-and-security-permission-enricher/utils.ts +82 -0
- package/src/unhandled-exception-telemetry/index.ts +403 -0
- package/src/unhandled-exception-telemetry/utils.ts +101 -0
- package/test/unit/spec/automated-user.ts +43 -0
- package/test/unit/spec/batcher.js +43 -0
- package/test/unit/spec/call-diagnostic/call-diagnostic-metrics-batcher.ts +14 -2
- package/test/unit/spec/call-diagnostic/call-diagnostic-metrics-latencies.ts +1424 -293
- package/test/unit/spec/call-diagnostic/call-diagnostic-metrics.ts +906 -159
- package/test/unit/spec/call-diagnostic/call-diagnostic-metrics.util.ts +27 -0
- package/test/unit/spec/metrics.js +14 -5
- package/test/unit/spec/network-telemetry.ts +610 -0
- package/test/unit/spec/new-metrics.ts +203 -50
- package/test/unit/spec/prelogin-metrics-batcher.ts +71 -36
- package/test/unit/spec/privacy-and-security-permission-enricher.ts +318 -0
- package/test/unit/spec/unhandled-exception-telemetry/utils.ts +109 -0
- package/test/unit/spec/unhandled-exception-telemetry.ts +688 -0
|
@@ -6,6 +6,7 @@ import uuid from 'uuid';
|
|
|
6
6
|
import {merge} from 'lodash';
|
|
7
7
|
import {StatelessWebexPlugin} from '@webex/webex-core';
|
|
8
8
|
import {getOSNameInternal} from '../metrics';
|
|
9
|
+
import * as AutomatedUserUtils from '../automated-user';
|
|
9
10
|
|
|
10
11
|
import {
|
|
11
12
|
anonymizeIPAddress,
|
|
@@ -99,7 +100,12 @@ export default class CallDiagnosticMetrics extends StatelessWebexPlugin {
|
|
|
99
100
|
// @ts-ignore
|
|
100
101
|
private preLoginMetricsBatcher: PreLoginMetricsBatcher;
|
|
101
102
|
|
|
102
|
-
|
|
103
|
+
// lazy getter to avoid @ts-ignore on every call site
|
|
104
|
+
private get logger(): any {
|
|
105
|
+
// @ts-ignore
|
|
106
|
+
return this.webex.logger;
|
|
107
|
+
}
|
|
108
|
+
|
|
103
109
|
private hasLoggedBrowserSerial: boolean;
|
|
104
110
|
private device: any;
|
|
105
111
|
private delayedClientEvents: DelayedClientEvent[] = [];
|
|
@@ -108,6 +114,8 @@ export default class CallDiagnosticMetrics extends StatelessWebexPlugin {
|
|
|
108
114
|
private isMercuryConnected = false;
|
|
109
115
|
private eventLimitTracker: Map<string, number> = new Map();
|
|
110
116
|
private eventLimitWarningsLogged: Set<string> = new Set();
|
|
117
|
+
private isTelemetryOptOutManual = false;
|
|
118
|
+
private isTelemetryOptOutAutomatic = false;
|
|
111
119
|
|
|
112
120
|
// the default validator before piping an event to the batcher
|
|
113
121
|
// this function can be overridden by the user
|
|
@@ -124,8 +132,6 @@ export default class CallDiagnosticMetrics extends StatelessWebexPlugin {
|
|
|
124
132
|
constructor(...args) {
|
|
125
133
|
super(...args);
|
|
126
134
|
// @ts-ignore
|
|
127
|
-
this.logger = this.webex.logger;
|
|
128
|
-
// @ts-ignore
|
|
129
135
|
this.callDiagnosticEventsBatcher = new CallDiagnosticEventsBatcher({}, {parent: this.webex});
|
|
130
136
|
// @ts-ignore
|
|
131
137
|
this.preLoginMetricsBatcher = new PreLoginMetricsBatcher({}, {parent: this.webex});
|
|
@@ -145,6 +151,58 @@ export default class CallDiagnosticMetrics extends StatelessWebexPlugin {
|
|
|
145
151
|
return null;
|
|
146
152
|
}
|
|
147
153
|
|
|
154
|
+
/**
|
|
155
|
+
* Returns the user activation state reported from the browser's navigator.userActivation API
|
|
156
|
+
* @returns object with hasBeenActive and isActive booleans, or undefined if unavailable
|
|
157
|
+
*/
|
|
158
|
+
getUserActivation(): {hasBeenActive: boolean; isActive: boolean} | undefined {
|
|
159
|
+
const userActivation =
|
|
160
|
+
typeof navigator !== 'undefined'
|
|
161
|
+
? (navigator as {userActivation?: {hasBeenActive: boolean; isActive: boolean}})
|
|
162
|
+
.userActivation
|
|
163
|
+
: undefined;
|
|
164
|
+
|
|
165
|
+
if (userActivation) {
|
|
166
|
+
return {
|
|
167
|
+
hasBeenActive: userActivation.hasBeenActive,
|
|
168
|
+
isActive: userActivation.isActive,
|
|
169
|
+
};
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
return undefined;
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
/**
|
|
176
|
+
* Returns the telemetryOptOut value of the current user
|
|
177
|
+
* @returns one of 'manual', 'automatic', undefined
|
|
178
|
+
*/
|
|
179
|
+
public getTelemetryOptOut() {
|
|
180
|
+
if (this.isTelemetryOptOutManual) {
|
|
181
|
+
return 'manual';
|
|
182
|
+
}
|
|
183
|
+
if (this.isTelemetryOptOutAutomatic) {
|
|
184
|
+
return 'automatic';
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
return undefined;
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
/**
|
|
191
|
+
* Sets the manual telemetry opt-out status for the current user
|
|
192
|
+
* @param value - boolean value indicating manual telemetry opt-out status
|
|
193
|
+
*/
|
|
194
|
+
public setIsTelemetryOptOutManual(value: boolean) {
|
|
195
|
+
this.isTelemetryOptOutManual = value;
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
/**
|
|
199
|
+
* Sets the automatic telemetry opt-out status for the current user
|
|
200
|
+
* @param value - boolean value indicating automatic telemetry opt-out status
|
|
201
|
+
*/
|
|
202
|
+
public setIsTelemetryOptOutAutomatic(value: boolean) {
|
|
203
|
+
this.isTelemetryOptOutAutomatic = value;
|
|
204
|
+
}
|
|
205
|
+
|
|
148
206
|
/**
|
|
149
207
|
* Returns if the meeting has converged architecture enabled
|
|
150
208
|
* @param options.meetingId
|
|
@@ -190,6 +248,11 @@ export default class CallDiagnosticMetrics extends StatelessWebexPlugin {
|
|
|
190
248
|
|
|
191
249
|
// if ConvergedArchitecture enable and isConvergedWebinarWebcast -- then webcast
|
|
192
250
|
if (meetingInfo?.enableConvergedArchitecture && meetingInfo?.enableEvent) {
|
|
251
|
+
// if enableConvergedWebinarLargeScale - then large scale webinar
|
|
252
|
+
if (meetingInfo?.enableConvergedWebinarLargeScale) {
|
|
253
|
+
return WEBEX_SUB_SERVICE_TYPES.LARGE_SCALE_WEBINAR;
|
|
254
|
+
}
|
|
255
|
+
|
|
193
256
|
return meetingInfo?.isConvergedWebinarWebcast
|
|
194
257
|
? WEBEX_SUB_SERVICE_TYPES.WEBCAST
|
|
195
258
|
: WEBEX_SUB_SERVICE_TYPES.WEBINAR;
|
|
@@ -213,12 +276,12 @@ export default class CallDiagnosticMetrics extends StatelessWebexPlugin {
|
|
|
213
276
|
getOrigin(options: GetOriginOptions, meetingId?: string) {
|
|
214
277
|
const defaultClientType: ClientType =
|
|
215
278
|
// @ts-ignore
|
|
216
|
-
this.webex.meetings
|
|
279
|
+
this.webex.meetings?.config?.metrics?.clientType;
|
|
217
280
|
const defaultSubClientType: SubClientType =
|
|
218
281
|
// @ts-ignore
|
|
219
|
-
this.webex.meetings
|
|
282
|
+
this.webex.meetings?.config?.metrics?.subClientType;
|
|
220
283
|
// @ts-ignore
|
|
221
|
-
const providedClientVersion: string = this.webex.meetings
|
|
284
|
+
const providedClientVersion: string = this.webex.meetings?.config?.metrics?.clientVersion;
|
|
222
285
|
// @ts-ignore
|
|
223
286
|
const defaultSDKClientVersion = `${CLIENT_NAME}/${this.webex.version}`;
|
|
224
287
|
|
|
@@ -229,6 +292,18 @@ export default class CallDiagnosticMetrics extends StatelessWebexPlugin {
|
|
|
229
292
|
versionMetadata = extractVersionMetadata(providedClientVersion);
|
|
230
293
|
}
|
|
231
294
|
|
|
295
|
+
// Browser details and the support verdict can all be supplied by the host client, which is able
|
|
296
|
+
// to resolve wrapper browsers (Electron, WebOS) to the engine that actually determines
|
|
297
|
+
// capability. The browser fields fall back to the SDK's own detection when not supplied, the
|
|
298
|
+
// same way clientVersion falls back to the SDK version.
|
|
299
|
+
const {
|
|
300
|
+
browser: providedBrowser,
|
|
301
|
+
browserVersion: providedBrowserVersion,
|
|
302
|
+
isSupportedBrowserFamily,
|
|
303
|
+
isOutdatedBrowserVersion,
|
|
304
|
+
// @ts-ignore
|
|
305
|
+
} = this.webex.meetings?.config?.metrics ?? {};
|
|
306
|
+
|
|
232
307
|
if (!this.hasLoggedBrowserSerial) {
|
|
233
308
|
this.logger.log(
|
|
234
309
|
CALL_DIAGNOSTIC_LOG_IDENTIFIER,
|
|
@@ -258,25 +333,27 @@ export default class CallDiagnosticMetrics extends StatelessWebexPlugin {
|
|
|
258
333
|
...versionMetadata,
|
|
259
334
|
publicNetworkPrefix:
|
|
260
335
|
// @ts-ignore
|
|
261
|
-
anonymizeIPAddress(this.webex.meetings
|
|
336
|
+
anonymizeIPAddress(this.webex.meetings?.geoHintInfo?.clientAddress) || undefined,
|
|
262
337
|
localNetworkPrefix:
|
|
263
338
|
anonymizeIPAddress(
|
|
264
339
|
// @ts-ignore
|
|
265
|
-
this.webex.meetings
|
|
266
|
-
|
|
340
|
+
this.webex.meetings?.meetingCollection
|
|
341
|
+
?.get(meetingId)
|
|
267
342
|
?.statsAnalyzer?.getLocalIpAddress()
|
|
268
343
|
) || undefined,
|
|
269
344
|
osVersion: getOSVersion() || 'unknown',
|
|
270
345
|
subClientType: options?.subClientType || defaultSubClientType,
|
|
271
346
|
os: getOSNameInternal(),
|
|
272
|
-
browser: getBrowserName(),
|
|
273
|
-
browserVersion: getBrowserVersion(),
|
|
347
|
+
browser: providedBrowser || getBrowserName(),
|
|
348
|
+
browserVersion: providedBrowserVersion || getBrowserVersion(),
|
|
349
|
+
...(isSupportedBrowserFamily === undefined ? {} : {isSupportedBrowserFamily}),
|
|
350
|
+
...(isOutdatedBrowserVersion === undefined ? {} : {isOutdatedBrowserVersion}),
|
|
274
351
|
},
|
|
275
352
|
};
|
|
276
353
|
|
|
277
354
|
if (meetingId) {
|
|
278
355
|
// @ts-ignore
|
|
279
|
-
const meeting = this.webex.meetings
|
|
356
|
+
const meeting = this.webex.meetings?.getBasicMeetingInformation(meetingId);
|
|
280
357
|
if (meeting?.environment) {
|
|
281
358
|
origin.environment = meeting.environment;
|
|
282
359
|
}
|
|
@@ -308,6 +385,20 @@ export default class CallDiagnosticMetrics extends StatelessWebexPlugin {
|
|
|
308
385
|
throw new Error("ClientType and SubClientType can't be undefined");
|
|
309
386
|
}
|
|
310
387
|
|
|
388
|
+
/**
|
|
389
|
+
* Returns the signed-in user's CI ID derived from credentials tokens.
|
|
390
|
+
* Used as a fallback when no device is registered.
|
|
391
|
+
* @returns the userId, or undefined if it can't be determined
|
|
392
|
+
*/
|
|
393
|
+
private getUserIdFromCredentials(): string | undefined {
|
|
394
|
+
try {
|
|
395
|
+
// @ts-ignore
|
|
396
|
+
return this.webex.credentials.getUserId();
|
|
397
|
+
} catch {
|
|
398
|
+
return undefined;
|
|
399
|
+
}
|
|
400
|
+
}
|
|
401
|
+
|
|
311
402
|
/**
|
|
312
403
|
* Gather identifier details for call diagnostic payload.
|
|
313
404
|
* @throws Error if initialization fails.
|
|
@@ -352,7 +443,6 @@ export default class CallDiagnosticMetrics extends StatelessWebexPlugin {
|
|
|
352
443
|
const {device} = this;
|
|
353
444
|
const {installationId} = device?.config || {};
|
|
354
445
|
|
|
355
|
-
identifiers.userId = device?.userId || preLoginId;
|
|
356
446
|
identifiers.deviceId = device?.url;
|
|
357
447
|
identifiers.orgId = device?.orgId;
|
|
358
448
|
// @ts-ignore
|
|
@@ -363,6 +453,10 @@ export default class CallDiagnosticMetrics extends StatelessWebexPlugin {
|
|
|
363
453
|
}
|
|
364
454
|
}
|
|
365
455
|
|
|
456
|
+
// Prefer the device's userId, but fall back to the signed-in user's ID from
|
|
457
|
+
// credentials when no device is registered, then to the pre-login ID.
|
|
458
|
+
identifiers.userId = this.device?.userId || this.getUserIdFromCredentials() || preLoginId;
|
|
459
|
+
|
|
366
460
|
if (meeting?.locusInfo?.fullState) {
|
|
367
461
|
identifiers.locusUrl = meeting.locusUrl;
|
|
368
462
|
identifiers.locusId = meeting.locusUrl && meeting.locusUrl.split('/').pop();
|
|
@@ -426,7 +520,7 @@ export default class CallDiagnosticMetrics extends StatelessWebexPlugin {
|
|
|
426
520
|
sent: 'not_defined_yet',
|
|
427
521
|
},
|
|
428
522
|
// @ts-ignore
|
|
429
|
-
senderCountryCode: this.webex.meetings
|
|
523
|
+
senderCountryCode: this.webex.meetings?.geoHintInfo?.countryCode,
|
|
430
524
|
event: eventData,
|
|
431
525
|
};
|
|
432
526
|
|
|
@@ -602,6 +696,7 @@ export default class CallDiagnosticMetrics extends StatelessWebexPlugin {
|
|
|
602
696
|
mediaEngineSoftwareVersion: getOSVersion() || 'unknown',
|
|
603
697
|
startTime: new Date().toISOString(),
|
|
604
698
|
},
|
|
699
|
+
webexSubServiceType: this.getSubServiceType(meeting),
|
|
605
700
|
};
|
|
606
701
|
|
|
607
702
|
// merge any new properties, or override existing ones
|
|
@@ -984,7 +1079,7 @@ export default class CallDiagnosticMetrics extends StatelessWebexPlugin {
|
|
|
984
1079
|
sessionCorrelationId,
|
|
985
1080
|
});
|
|
986
1081
|
|
|
987
|
-
// create common event object
|
|
1082
|
+
// create common event object structure
|
|
988
1083
|
const commonEventObject = {
|
|
989
1084
|
name,
|
|
990
1085
|
canProceed: true,
|
|
@@ -997,6 +1092,7 @@ export default class CallDiagnosticMetrics extends StatelessWebexPlugin {
|
|
|
997
1092
|
'loginType' in meeting.callStateForMetrics
|
|
998
1093
|
? meeting.callStateForMetrics.loginType
|
|
999
1094
|
: this.getCurLoginType(),
|
|
1095
|
+
telemetryOptOut: this.getTelemetryOptOut(),
|
|
1000
1096
|
isConvergedArchitectureEnabled: this.getIsConvergedArchitectureEnabled({
|
|
1001
1097
|
meetingId,
|
|
1002
1098
|
}),
|
|
@@ -1005,6 +1101,9 @@ export default class CallDiagnosticMetrics extends StatelessWebexPlugin {
|
|
|
1005
1101
|
webexSubServiceType: this.getSubServiceType(meeting),
|
|
1006
1102
|
// @ts-ignore
|
|
1007
1103
|
webClientPreload: this.webex.meetings?.config?.metrics?.webClientPreload,
|
|
1104
|
+
isVipMeeting: meeting?.meetingInfo?.vipmeeting || false,
|
|
1105
|
+
isAutomatedUser: AutomatedUserUtils.isAutomatedUser(),
|
|
1106
|
+
userActivation: this.getUserActivation(),
|
|
1008
1107
|
};
|
|
1009
1108
|
|
|
1010
1109
|
const joinFlowVersion = options.joinFlowVersion ?? meeting.callStateForMetrics?.joinFlowVersion;
|
|
@@ -1124,8 +1223,11 @@ export default class CallDiagnosticMetrics extends StatelessWebexPlugin {
|
|
|
1124
1223
|
isMercuryConnected: this.isMercuryConnected,
|
|
1125
1224
|
},
|
|
1126
1225
|
loginType: this.getCurLoginType(),
|
|
1226
|
+
telemetryOptOut: this.getTelemetryOptOut(),
|
|
1127
1227
|
// @ts-ignore
|
|
1128
1228
|
webClientPreload: this.webex.meetings?.config?.metrics?.webClientPreload,
|
|
1229
|
+
isAutomatedUser: AutomatedUserUtils.isAutomatedUser(),
|
|
1230
|
+
userActivation: this.getUserActivation(),
|
|
1129
1231
|
};
|
|
1130
1232
|
|
|
1131
1233
|
if (options.joinFlowVersion) {
|
|
@@ -1335,6 +1437,7 @@ export default class CallDiagnosticMetrics extends StatelessWebexPlugin {
|
|
|
1335
1437
|
eventPayload: event,
|
|
1336
1438
|
type: ['diagnostic-event'],
|
|
1337
1439
|
};
|
|
1440
|
+
|
|
1338
1441
|
this.preLoginMetricsBatcher.savePreLoginId(preLoginId);
|
|
1339
1442
|
|
|
1340
1443
|
return this.preLoginMetricsBatcher.request(finalEvent);
|
|
@@ -1382,7 +1485,7 @@ export default class CallDiagnosticMetrics extends StatelessWebexPlugin {
|
|
|
1382
1485
|
},
|
|
1383
1486
|
headers: {},
|
|
1384
1487
|
// @ts-ignore
|
|
1385
|
-
waitForServiceTimeout: this.webex.internal.metrics
|
|
1488
|
+
waitForServiceTimeout: this.webex.internal.metrics?.config?.waitForServiceTimeout,
|
|
1386
1489
|
};
|
|
1387
1490
|
|
|
1388
1491
|
if (options.preLoginId) {
|
|
@@ -390,6 +390,11 @@ export const prepareDiagnosticMetricItem = (webex: any, item: any) => {
|
|
|
390
390
|
|
|
391
391
|
item.eventPayload.origin = Object.assign(origin, item.eventPayload.origin);
|
|
392
392
|
|
|
393
|
+
// Mark call milestones in logs for easier filtering and analysis
|
|
394
|
+
if (eventName) {
|
|
395
|
+
webex.logger.log('Milestone,CallDiagnostic', eventName);
|
|
396
|
+
}
|
|
397
|
+
|
|
393
398
|
webex.logger.log(
|
|
394
399
|
`CallDiagnosticLatencies,prepareDiagnosticMetricItem: ${JSON.stringify({
|
|
395
400
|
latencies: Object.fromEntries(cdl.latencyTimestamps),
|
|
@@ -26,6 +26,7 @@ export const WEBEX_SUB_SERVICE_TYPES: Record<string, ClientSubServiceType> = {
|
|
|
26
26
|
SCHEDULED_MEETING: 'ScheduledMeeting',
|
|
27
27
|
WEBINAR: 'Webinar',
|
|
28
28
|
WEBCAST: 'Webcast',
|
|
29
|
+
LARGE_SCALE_WEBINAR: 'LargeScaleWebinar',
|
|
29
30
|
};
|
|
30
31
|
|
|
31
32
|
// Found in https://developer.mozilla.org/en-US/docs/Web/API/MediaDevices/getUserMedia
|
|
@@ -136,6 +137,7 @@ export const ERROR_DESCRIPTIONS = {
|
|
|
136
137
|
WEBRTC_API_NOT_AVAILABLE: 'WebrtcApiNotAvailableError',
|
|
137
138
|
WDM_RESTRICTED_REGION: 'WdmRestrictedRegion',
|
|
138
139
|
USER_NOT_ALLOWED_JOIN_WEBINAR: 'UserNotAllowedJoinWebinar',
|
|
140
|
+
INVALID_MEETING_INFO: 'InvalidMeetingInfo',
|
|
139
141
|
};
|
|
140
142
|
|
|
141
143
|
export const SERVICE_ERROR_CODES_TO_CLIENT_ERROR_CODES_MAP = {
|
|
@@ -146,6 +148,8 @@ export const SERVICE_ERROR_CODES_TO_CLIENT_ERROR_CODES_MAP = {
|
|
|
146
148
|
99002: 4100,
|
|
147
149
|
// Cannot find the data. Unkown meeting.
|
|
148
150
|
99009: 4100,
|
|
151
|
+
// The input parameters contain invalid item
|
|
152
|
+
99019: 4105,
|
|
149
153
|
// Meeting is not allow to cross env
|
|
150
154
|
58500: 4100,
|
|
151
155
|
// Input parameters contain invalit item
|
|
@@ -201,6 +205,8 @@ export const SERVICE_ERROR_CODES_TO_CLIENT_ERROR_CODES_MAP = {
|
|
|
201
205
|
423005: 4005,
|
|
202
206
|
// Wrong password or host key with too many requests
|
|
203
207
|
423006: 4005,
|
|
208
|
+
// PanelistPasswordError too many time,please input captcha code
|
|
209
|
+
423008: 4005,
|
|
204
210
|
// PasswordError with right captcha, please input captcha code
|
|
205
211
|
423010: 4005,
|
|
206
212
|
// PasswordOrHostKeyError with right captcha, please input captcha code
|
|
@@ -227,6 +233,8 @@ export const SERVICE_ERROR_CODES_TO_CLIENT_ERROR_CODES_MAP = {
|
|
|
227
233
|
403003: 4101,
|
|
228
234
|
// Attendee email is required
|
|
229
235
|
403030: 4101,
|
|
236
|
+
// webinar need login when un-invited attendee join
|
|
237
|
+
403106: 4104,
|
|
230
238
|
|
|
231
239
|
// ---- Locus ------
|
|
232
240
|
// FREE_USER_MAX_PARTICIPANTS_EXCEEDED
|
|
@@ -696,6 +704,12 @@ export const CLIENT_ERROR_CODE_TO_ERROR_PAYLOAD: Record<number, Partial<ClientEv
|
|
|
696
704
|
category: 'expected',
|
|
697
705
|
fatal: true,
|
|
698
706
|
},
|
|
707
|
+
4105: {
|
|
708
|
+
errorDescription: ERROR_DESCRIPTIONS.INVALID_MEETING_INFO,
|
|
709
|
+
category: 'expected',
|
|
710
|
+
fatal: false,
|
|
711
|
+
shownToUser: true,
|
|
712
|
+
},
|
|
699
713
|
2729: {
|
|
700
714
|
errorDescription: ERROR_DESCRIPTIONS.NO_MEDIA_FOUND,
|
|
701
715
|
category: 'expected',
|
package/src/config.js
CHANGED
|
@@ -15,10 +15,18 @@ export default {
|
|
|
15
15
|
},
|
|
16
16
|
metrics: {
|
|
17
17
|
appType: inBrowser ? 'browser' : 'nodejs',
|
|
18
|
+
unhandledExceptionTelemetry: {
|
|
19
|
+
enabled: false,
|
|
20
|
+
},
|
|
21
|
+
networkTelemetry: {
|
|
22
|
+
enabled: false,
|
|
23
|
+
intervalMs: 10 * 60 * 1_000,
|
|
24
|
+
},
|
|
18
25
|
batcherWait: 500,
|
|
19
26
|
batcherMaxCalls: 50,
|
|
20
27
|
batcherMaxWait: 1500,
|
|
21
28
|
batcherRetryPlateau: 32000,
|
|
29
|
+
batcherRetryOnNetworkError: true,
|
|
22
30
|
waitForServiceTimeout: 30,
|
|
23
31
|
},
|
|
24
32
|
};
|
package/src/index.ts
CHANGED
|
@@ -2,12 +2,15 @@
|
|
|
2
2
|
* Copyright (c) 2015-2020 Cisco Systems, Inc. See LICENSE file.
|
|
3
3
|
*/
|
|
4
4
|
|
|
5
|
+
/* eslint-disable valid-jsdoc */
|
|
6
|
+
|
|
5
7
|
import {registerInternalPlugin} from '@webex/webex-core';
|
|
6
8
|
|
|
7
9
|
import Metrics from './metrics';
|
|
8
10
|
import config from './config';
|
|
9
11
|
import NewMetrics from './new-metrics';
|
|
10
12
|
import * as Utils from './utils';
|
|
13
|
+
import * as AutomatedUserUtils from './automated-user';
|
|
11
14
|
import {
|
|
12
15
|
ClientEvent,
|
|
13
16
|
ClientEventLeaveReason,
|
|
@@ -19,6 +22,8 @@ import {
|
|
|
19
22
|
SubmitMQE,
|
|
20
23
|
PreComputedLatencies,
|
|
21
24
|
SubmitFeatureEvent,
|
|
25
|
+
LocusSyncLatencyEventName,
|
|
26
|
+
PrivacyAndSecurityPermission,
|
|
22
27
|
} from './metrics.types';
|
|
23
28
|
import * as CALL_DIAGNOSTIC_CONFIG from './call-diagnostic/config';
|
|
24
29
|
import * as CallDiagnosticUtils from './call-diagnostic/call-diagnostic-metrics.util';
|
|
@@ -32,6 +37,13 @@ import PreLoginMetrics from './prelogin-metrics';
|
|
|
32
37
|
|
|
33
38
|
registerInternalPlugin('metrics', Metrics, {
|
|
34
39
|
config,
|
|
40
|
+
/**
|
|
41
|
+
* Stops network telemetry before SDK logout.
|
|
42
|
+
* @returns
|
|
43
|
+
*/
|
|
44
|
+
onBeforeLogout() {
|
|
45
|
+
return this.stopNetworkTelemetry();
|
|
46
|
+
},
|
|
35
47
|
});
|
|
36
48
|
|
|
37
49
|
registerInternalPlugin('newMetrics', NewMetrics, {
|
|
@@ -43,6 +55,7 @@ export {default, getOSNameInternal} from './metrics';
|
|
|
43
55
|
export {
|
|
44
56
|
config,
|
|
45
57
|
CALL_DIAGNOSTIC_CONFIG,
|
|
58
|
+
AutomatedUserUtils,
|
|
46
59
|
NewMetrics,
|
|
47
60
|
Utils,
|
|
48
61
|
CallDiagnosticUtils,
|
|
@@ -54,6 +67,7 @@ export {
|
|
|
54
67
|
RtcMetrics,
|
|
55
68
|
PreLoginMetrics,
|
|
56
69
|
};
|
|
70
|
+
export {isAutomatedUser, isAutomatedUserAgent} from './automated-user';
|
|
57
71
|
export type {
|
|
58
72
|
ClientEvent,
|
|
59
73
|
ClientEventLeaveReason,
|
|
@@ -65,4 +79,6 @@ export type {
|
|
|
65
79
|
SubmitBusinessEvent,
|
|
66
80
|
PreComputedLatencies,
|
|
67
81
|
SubmitFeatureEvent,
|
|
82
|
+
LocusSyncLatencyEventName,
|
|
83
|
+
PrivacyAndSecurityPermission,
|
|
68
84
|
};
|
package/src/metrics.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
/* eslint-disable default-param-last */
|
|
2
|
+
/* eslint-disable valid-jsdoc */
|
|
2
3
|
|
|
3
4
|
/*!
|
|
4
5
|
* Copyright (c) 2015-2020 Cisco Systems, Inc. See LICENSE file.
|
|
@@ -11,6 +12,7 @@ import {OS_NAME, OSMap, CLIENT_NAME} from './config';
|
|
|
11
12
|
import Batcher from './batcher';
|
|
12
13
|
import ClientMetricsBatcher from './client-metrics-batcher';
|
|
13
14
|
import ClientMetricsPreloginBatcher from './client-metrics-prelogin-batcher';
|
|
15
|
+
import {createNetworkTelemetryCollector} from './network-telemetry';
|
|
14
16
|
|
|
15
17
|
const {getOSName, getOSVersion, getBrowserName, getBrowserVersion} = BrowserDetection();
|
|
16
18
|
|
|
@@ -43,6 +45,119 @@ const Metrics = WebexPlugin.extend({
|
|
|
43
45
|
|
|
44
46
|
namespace: 'Metrics',
|
|
45
47
|
|
|
48
|
+
/**
|
|
49
|
+
* Registers SDK request outcome monitoring.
|
|
50
|
+
* @param args
|
|
51
|
+
* @returns
|
|
52
|
+
*/
|
|
53
|
+
initialize(...args) {
|
|
54
|
+
WebexPlugin.prototype.initialize.call(this, ...args);
|
|
55
|
+
|
|
56
|
+
// Metrics may also be constructed as a standalone plugin. Request events are
|
|
57
|
+
// available only when it is attached to a Webex instance.
|
|
58
|
+
if (!this.parent && !this.collection) {
|
|
59
|
+
return;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
const initializeNetworkTelemetry = () => {
|
|
63
|
+
if (
|
|
64
|
+
this.networkTelemetry ||
|
|
65
|
+
this.webex?.config?.metrics?.networkTelemetry?.enabled !== true
|
|
66
|
+
) {
|
|
67
|
+
return;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
this.networkTelemetry = createNetworkTelemetryCollector({
|
|
71
|
+
intervalMs: this.webex.config.metrics.networkTelemetry.intervalMs,
|
|
72
|
+
submitMetric: this.submitNetworkTelemetryMetric.bind(this),
|
|
73
|
+
onSubmissionFailure: this.handleNetworkTelemetrySubmissionFailure.bind(this),
|
|
74
|
+
});
|
|
75
|
+
this.listenTo(this.webex, 'request:start', this.recordNetworkRequestStart);
|
|
76
|
+
this.listenTo(this.webex, 'request:success', this.recordNetworkRequestSuccess);
|
|
77
|
+
this.listenTo(this.webex, 'request:failure', this.recordNetworkRequestFailure);
|
|
78
|
+
};
|
|
79
|
+
|
|
80
|
+
// Ampersand initializes children before WebexCore.initialize() populates
|
|
81
|
+
// webex.config. Run immediately for already-configured hosts (including
|
|
82
|
+
// MockWebex), and listen for the real core's post-initialization notification.
|
|
83
|
+
this.listenToOnce(this.webex, 'change:config', initializeNetworkTelemetry);
|
|
84
|
+
initializeNetworkTelemetry();
|
|
85
|
+
},
|
|
86
|
+
|
|
87
|
+
/**
|
|
88
|
+
* Records an SDK request being sent in the current reporting window.
|
|
89
|
+
* @param options
|
|
90
|
+
* @returns
|
|
91
|
+
*/
|
|
92
|
+
recordNetworkRequestStart(options) {
|
|
93
|
+
this.networkTelemetry.recordRequest(options);
|
|
94
|
+
},
|
|
95
|
+
|
|
96
|
+
/**
|
|
97
|
+
* Records a successful SDK request for the current reporting window.
|
|
98
|
+
* @param options
|
|
99
|
+
* @returns
|
|
100
|
+
*/
|
|
101
|
+
recordNetworkRequestSuccess(options) {
|
|
102
|
+
this.networkTelemetry.recordResponse(options);
|
|
103
|
+
},
|
|
104
|
+
|
|
105
|
+
/**
|
|
106
|
+
* Records a failed SDK request for the current reporting window.
|
|
107
|
+
* @param options
|
|
108
|
+
* @param reason
|
|
109
|
+
* @returns
|
|
110
|
+
*/
|
|
111
|
+
recordNetworkRequestFailure(options, reason) {
|
|
112
|
+
this.networkTelemetry.recordFailure(options, reason);
|
|
113
|
+
},
|
|
114
|
+
|
|
115
|
+
/**
|
|
116
|
+
* Submits the current network telemetry window immediately.
|
|
117
|
+
* @returns
|
|
118
|
+
*/
|
|
119
|
+
flushNetworkTelemetry() {
|
|
120
|
+
if (!this.networkTelemetry) {
|
|
121
|
+
return Promise.resolve();
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
return this.networkTelemetry.flush();
|
|
125
|
+
},
|
|
126
|
+
|
|
127
|
+
/**
|
|
128
|
+
* Submits a completed network telemetry window through client metrics.
|
|
129
|
+
* @param name
|
|
130
|
+
* @param properties
|
|
131
|
+
* @returns
|
|
132
|
+
*/
|
|
133
|
+
submitNetworkTelemetryMetric(name, properties) {
|
|
134
|
+
return this.submitClientMetrics(name, properties);
|
|
135
|
+
},
|
|
136
|
+
|
|
137
|
+
/**
|
|
138
|
+
* Logs a network telemetry submission failure without affecting SDK requests.
|
|
139
|
+
* @returns
|
|
140
|
+
*/
|
|
141
|
+
handleNetworkTelemetrySubmissionFailure() {
|
|
142
|
+
this.webex.logger?.warn('metrics: failed to submit network request summary telemetry');
|
|
143
|
+
},
|
|
144
|
+
|
|
145
|
+
/**
|
|
146
|
+
* Stops network telemetry collection and submission.
|
|
147
|
+
* @returns
|
|
148
|
+
*/
|
|
149
|
+
stopNetworkTelemetry() {
|
|
150
|
+
if (!this.networkTelemetry) {
|
|
151
|
+
return Promise.resolve();
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
this.stopListening(this.webex, 'request:start', this.recordNetworkRequestStart);
|
|
155
|
+
this.stopListening(this.webex, 'request:success', this.recordNetworkRequestSuccess);
|
|
156
|
+
this.stopListening(this.webex, 'request:failure', this.recordNetworkRequestFailure);
|
|
157
|
+
|
|
158
|
+
return this.networkTelemetry.stop();
|
|
159
|
+
},
|
|
160
|
+
|
|
46
161
|
submit(key, value) {
|
|
47
162
|
return this.batcher.request({key, ...value});
|
|
48
163
|
},
|
|
@@ -60,17 +175,20 @@ const Metrics = WebexPlugin.extend({
|
|
|
60
175
|
const payload = {metricName: eventName};
|
|
61
176
|
// @ts-ignore
|
|
62
177
|
const providedClientVersion = this.webex.meetings?.config?.metrics?.clientVersion;
|
|
178
|
+
const {appName, appVersion} = this.webex.config;
|
|
179
|
+
// Browser location is unavailable when the SDK runs in Node.js.
|
|
180
|
+
// eslint-disable-next-line no-undef
|
|
181
|
+
const browserLocation = typeof window !== 'undefined' ? window.location : undefined;
|
|
63
182
|
|
|
64
183
|
payload.tags = {
|
|
65
184
|
...props.tags,
|
|
185
|
+
app_name: appName ?? 'unknown',
|
|
186
|
+
app_url: browserLocation?.origin || browserLocation?.hostname || 'non-browser',
|
|
187
|
+
app_version: appVersion ?? 'unknown',
|
|
66
188
|
browser: getBrowserName(),
|
|
67
189
|
os: getOSNameInternal(),
|
|
68
190
|
appVersion: providedClientVersion,
|
|
69
|
-
|
|
70
|
-
// Node does not like this so we need to check if it exists or not
|
|
71
|
-
// eslint-disable-next-line no-undef
|
|
72
|
-
domain:
|
|
73
|
-
typeof window !== 'undefined' ? window.location.hostname || 'non-browser' : 'non-browser', // Check what else we could measure
|
|
191
|
+
domain: browserLocation?.hostname || 'non-browser',
|
|
74
192
|
};
|
|
75
193
|
|
|
76
194
|
payload.fields = {
|
package/src/metrics.types.ts
CHANGED
|
@@ -23,6 +23,15 @@ export type ClientUserNameInput = NonNullable<RawClientEvent['userNameInput']>;
|
|
|
23
23
|
|
|
24
24
|
export type ClientEmailInput = NonNullable<RawClientEvent['emailInput']>;
|
|
25
25
|
|
|
26
|
+
export type PrivacyAndSecurityPermission = NonNullable<
|
|
27
|
+
RawClientEvent['privacyAndSecurityPermission']
|
|
28
|
+
>;
|
|
29
|
+
|
|
30
|
+
export type PrivacyAndSecurityPermissionResource = keyof PrivacyAndSecurityPermission;
|
|
31
|
+
|
|
32
|
+
export type PrivacyAndSecurityPermissionState =
|
|
33
|
+
PrivacyAndSecurityPermission[PrivacyAndSecurityPermissionResource];
|
|
34
|
+
|
|
26
35
|
export type BrowserLaunchMethodType = NonNullable<
|
|
27
36
|
RawEvent['origin']['clientInfo']
|
|
28
37
|
>['browserLaunchMethod'];
|
|
@@ -149,6 +158,17 @@ export type SubmitMQEOptions = {
|
|
|
149
158
|
globalMeetingId?: string;
|
|
150
159
|
};
|
|
151
160
|
|
|
161
|
+
export const LOCUS_SYNC_LATENCY_EVENT_NAMES = [
|
|
162
|
+
'internal.client.locus.sync.start',
|
|
163
|
+
'internal.client.locus.hashtree.request',
|
|
164
|
+
'internal.client.locus.hashtree.response',
|
|
165
|
+
'internal.client.locus.sync.request',
|
|
166
|
+
'internal.client.locus.sync.response',
|
|
167
|
+
'internal.client.locus.sync.message.received',
|
|
168
|
+
] as const;
|
|
169
|
+
|
|
170
|
+
export type LocusSyncLatencyEventName = (typeof LOCUS_SYNC_LATENCY_EVENT_NAMES)[number];
|
|
171
|
+
|
|
152
172
|
export type InternalEvent = {
|
|
153
173
|
name:
|
|
154
174
|
| 'internal.client.meetinginfo.request'
|
|
@@ -156,14 +176,14 @@ export type InternalEvent = {
|
|
|
156
176
|
| 'internal.register.device.request'
|
|
157
177
|
| 'internal.register.device.response'
|
|
158
178
|
| 'internal.reset.join.latencies'
|
|
159
|
-
| 'internal.client.meeting.click.joinbutton'
|
|
160
179
|
| 'internal.host.meeting.participant.admitted'
|
|
161
180
|
| 'internal.client.meeting.interstitial-window.showed'
|
|
162
181
|
| 'internal.client.interstitial-window.click.joinbutton'
|
|
163
182
|
| 'internal.client.add-media.turn-discovery.start'
|
|
164
183
|
| 'internal.client.add-media.turn-discovery.end'
|
|
165
184
|
| 'internal.client.share.initiated'
|
|
166
|
-
| 'internal.client.share.stopped'
|
|
185
|
+
| 'internal.client.share.stopped'
|
|
186
|
+
| LocusSyncLatencyEventName;
|
|
167
187
|
|
|
168
188
|
payload?: never;
|
|
169
189
|
options?: never;
|
|
@@ -319,12 +339,14 @@ export type PreComputedLatencies =
|
|
|
319
339
|
| 'internal.get.cluster.time'
|
|
320
340
|
| 'internal.click.to.interstitial'
|
|
321
341
|
| 'internal.click.to.interstitial.with.user.delay'
|
|
342
|
+
| 'internal.click.to.interstitial.for.client.jmt'
|
|
322
343
|
| 'internal.refresh.captcha.time'
|
|
323
344
|
| 'internal.exchange.ci.token.time'
|
|
324
345
|
| 'internal.get.u2c.time'
|
|
325
346
|
| 'internal.call.init.join.req'
|
|
326
347
|
| 'internal.other.app.api.time'
|
|
327
|
-
| 'internal.api.fetch.intelligence.models'
|
|
348
|
+
| 'internal.api.fetch.intelligence.models'
|
|
349
|
+
| 'internal.client.locus.sync.random.backoff';
|
|
328
350
|
|
|
329
351
|
export interface IdType {
|
|
330
352
|
meetingId?: string;
|