@webex/internal-plugin-metrics 3.0.0-beta.39 → 3.0.0-beta.391

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.
Files changed (73) hide show
  1. package/dist/batcher.js +40 -1
  2. package/dist/batcher.js.map +1 -1
  3. package/dist/call-diagnostic/call-diagnostic-metrics-batcher.js +65 -0
  4. package/dist/call-diagnostic/call-diagnostic-metrics-batcher.js.map +1 -0
  5. package/dist/call-diagnostic/call-diagnostic-metrics-latencies.js +486 -0
  6. package/dist/call-diagnostic/call-diagnostic-metrics-latencies.js.map +1 -0
  7. package/dist/call-diagnostic/call-diagnostic-metrics.js +860 -0
  8. package/dist/call-diagnostic/call-diagnostic-metrics.js.map +1 -0
  9. package/dist/call-diagnostic/call-diagnostic-metrics.util.js +363 -0
  10. package/dist/call-diagnostic/call-diagnostic-metrics.util.js.map +1 -0
  11. package/dist/call-diagnostic/config.js +627 -0
  12. package/dist/call-diagnostic/config.js.map +1 -0
  13. package/dist/client-metrics-batcher.js +2 -1
  14. package/dist/client-metrics-batcher.js.map +1 -1
  15. package/dist/config.js +2 -1
  16. package/dist/config.js.map +1 -1
  17. package/dist/index.js +33 -0
  18. package/dist/index.js.map +1 -1
  19. package/dist/metrics.js +27 -28
  20. package/dist/metrics.js.map +1 -1
  21. package/dist/metrics.types.js +7 -0
  22. package/dist/metrics.types.js.map +1 -0
  23. package/dist/new-metrics.js +300 -0
  24. package/dist/new-metrics.js.map +1 -0
  25. package/dist/prelogin-metrics-batcher.js +82 -0
  26. package/dist/prelogin-metrics-batcher.js.map +1 -0
  27. package/dist/types/batcher.d.ts +7 -0
  28. package/dist/types/call-diagnostic/call-diagnostic-metrics-batcher.d.ts +2 -0
  29. package/dist/types/call-diagnostic/call-diagnostic-metrics-latencies.d.ts +208 -0
  30. package/dist/types/call-diagnostic/call-diagnostic-metrics.d.ts +427 -0
  31. package/dist/types/call-diagnostic/call-diagnostic-metrics.util.d.ts +103 -0
  32. package/dist/types/call-diagnostic/config.d.ts +178 -0
  33. package/dist/types/client-metrics-batcher.d.ts +2 -0
  34. package/dist/types/config.d.ts +36 -0
  35. package/dist/types/index.d.ts +15 -0
  36. package/dist/types/metrics.d.ts +3 -0
  37. package/dist/types/metrics.types.d.ts +105 -0
  38. package/dist/types/new-metrics.d.ts +131 -0
  39. package/dist/types/prelogin-metrics-batcher.d.ts +2 -0
  40. package/dist/types/utils.d.ts +6 -0
  41. package/dist/utils.js +27 -0
  42. package/dist/utils.js.map +1 -0
  43. package/package.json +16 -8
  44. package/src/batcher.js +38 -0
  45. package/src/call-diagnostic/call-diagnostic-metrics-batcher.ts +72 -0
  46. package/src/call-diagnostic/call-diagnostic-metrics-latencies.ts +447 -0
  47. package/src/call-diagnostic/call-diagnostic-metrics.ts +919 -0
  48. package/src/call-diagnostic/call-diagnostic-metrics.util.ts +391 -0
  49. package/src/call-diagnostic/config.ts +685 -0
  50. package/src/client-metrics-batcher.js +1 -0
  51. package/src/config.js +1 -0
  52. package/src/index.ts +54 -0
  53. package/src/metrics.js +21 -24
  54. package/src/metrics.types.ts +168 -0
  55. package/src/new-metrics.ts +278 -0
  56. package/src/prelogin-metrics-batcher.ts +95 -0
  57. package/src/utils.ts +17 -0
  58. package/test/unit/spec/batcher.js +2 -0
  59. package/test/unit/spec/call-diagnostic/call-diagnostic-metrics-batcher.ts +453 -0
  60. package/test/unit/spec/call-diagnostic/call-diagnostic-metrics-latencies.ts +615 -0
  61. package/test/unit/spec/call-diagnostic/call-diagnostic-metrics.ts +2303 -0
  62. package/test/unit/spec/call-diagnostic/call-diagnostic-metrics.util.ts +628 -0
  63. package/test/unit/spec/client-metrics-batcher.js +2 -0
  64. package/test/unit/spec/metrics.js +74 -97
  65. package/test/unit/spec/new-metrics.ts +231 -0
  66. package/test/unit/spec/prelogin-metrics-batcher.ts +250 -0
  67. package/test/unit/spec/utils.ts +22 -0
  68. package/tsconfig.json +6 -0
  69. package/dist/call-diagnostic-events-batcher.js +0 -60
  70. package/dist/call-diagnostic-events-batcher.js.map +0 -1
  71. package/src/call-diagnostic-events-batcher.js +0 -62
  72. package/src/index.js +0 -17
  73. package/test/unit/spec/call-diagnostic-events-batcher.js +0 -195
@@ -0,0 +1,72 @@
1
+ import {uniqueId} from 'lodash';
2
+ import Batcher from '../batcher';
3
+ import {prepareDiagnosticMetricItem} from './call-diagnostic-metrics.util';
4
+ import {CALL_DIAGNOSTIC_LOG_IDENTIFIER} from './config';
5
+ import {generateCommonErrorMetadata} from '../utils';
6
+
7
+ const CallDiagnosticEventsBatcher = Batcher.extend({
8
+ namespace: 'Metrics',
9
+
10
+ /**
11
+ * Prepare item
12
+ * @param {any} item
13
+ * @returns {Promise<any>}
14
+ */
15
+ prepareItem(item) {
16
+ return Promise.resolve(prepareDiagnosticMetricItem(this.webex, item));
17
+ },
18
+
19
+ /**
20
+ * Prepare request, add time sensitive date etc.
21
+ * @param {any[]} queue
22
+ * @returns {Promise<any[]>}
23
+ */
24
+ prepareRequest(queue) {
25
+ // Add sent timestamp
26
+ queue.forEach((item) => {
27
+ item.eventPayload.originTime = item.eventPayload.originTime || {};
28
+ item.eventPayload.originTime.sent = new Date().toISOString();
29
+ });
30
+
31
+ return Promise.resolve(queue);
32
+ },
33
+
34
+ /**
35
+ *
36
+ * @param {any} payload
37
+ * @returns {Promise<any>}
38
+ */
39
+ submitHttpRequest(payload) {
40
+ const batchId = uniqueId('ca-batch-');
41
+
42
+ return this.webex
43
+ .request({
44
+ method: 'POST',
45
+ service: 'metrics',
46
+ resource: 'clientmetrics',
47
+ body: {
48
+ metrics: payload,
49
+ },
50
+ waitForServiceTimeout: this.webex.config.metrics.waitForServiceTimeout,
51
+ })
52
+ .then((res) => {
53
+ this.webex.logger.log(
54
+ CALL_DIAGNOSTIC_LOG_IDENTIFIER,
55
+ `CallDiagnosticEventsBatcher: @submitHttpRequest#${batchId}. Request successful.`
56
+ );
57
+
58
+ return res;
59
+ })
60
+ .catch((err) => {
61
+ this.webex.logger.error(
62
+ CALL_DIAGNOSTIC_LOG_IDENTIFIER,
63
+ `CallDiagnosticEventsBatcher: @submitHttpRequest#${batchId}. Request failed:`,
64
+ `error: ${generateCommonErrorMetadata(err)}`
65
+ );
66
+
67
+ return Promise.reject(err);
68
+ });
69
+ },
70
+ });
71
+
72
+ export default CallDiagnosticEventsBatcher;
@@ -0,0 +1,447 @@
1
+ /* eslint-disable class-methods-use-this */
2
+ /* eslint-disable valid-jsdoc */
3
+ import {WebexPlugin} from '@webex/webex-core';
4
+
5
+ import {MetricEventNames, PreComputedLatencies} from '../metrics.types';
6
+
7
+ // we only care about client event and feature event for now
8
+
9
+ /**
10
+ * @description Helper class to store latencies timestamp and to calculate various latencies for CA.
11
+ * @exports
12
+ * @class CallDiagnosticLatencies
13
+ */
14
+ export default class CallDiagnosticLatencies extends WebexPlugin {
15
+ latencyTimestamps: Map<MetricEventNames, number>;
16
+ precomputedLatencies: Map<PreComputedLatencies, number>;
17
+ // meetingId that the current latencies are for
18
+ private meetingId?: string;
19
+
20
+ /**
21
+ * @constructor
22
+ */
23
+ constructor(...args) {
24
+ super(...args);
25
+ this.latencyTimestamps = new Map();
26
+ this.precomputedLatencies = new Map();
27
+ }
28
+
29
+ /**
30
+ * Clear timestamps
31
+ */
32
+ public clearTimestamps() {
33
+ this.latencyTimestamps.clear();
34
+ }
35
+
36
+ /**
37
+ * Associate current latencies with a meeting id
38
+ * @param meetingId
39
+ */
40
+ private setMeetingId(meetingId: string) {
41
+ this.meetingId = meetingId;
42
+ }
43
+
44
+ /**
45
+ * Returns the meeting object associated with current latencies
46
+ * @returns meeting object
47
+ */
48
+ private getMeeting() {
49
+ if (this.meetingId) {
50
+ // @ts-ignore
51
+ return this.webex.meetings.meetingCollection.get(this.meetingId);
52
+ }
53
+
54
+ return undefined;
55
+ }
56
+
57
+ /**
58
+ * Store timestamp value
59
+ * @param key - key
60
+ * @param value -value
61
+ * @throws
62
+ * @returns
63
+ */
64
+ public saveTimestamp({
65
+ key,
66
+ value = new Date().getTime(),
67
+ options = {},
68
+ }: {
69
+ key: MetricEventNames;
70
+ value?: number;
71
+ options?: {meetingId?: string};
72
+ }) {
73
+ // save the meetingId so we can use the meeting object in latency calculations if needed
74
+ const {meetingId} = options;
75
+ if (meetingId) {
76
+ this.setMeetingId(meetingId);
77
+ }
78
+ // for some events we're only interested in the first timestamp not last
79
+ // as these events can happen multiple times
80
+ if (
81
+ key === 'client.media.rx.start' ||
82
+ key === 'client.media.tx.start' ||
83
+ key === 'internal.client.meetinginfo.request' ||
84
+ key === 'internal.client.meetinginfo.response'
85
+ ) {
86
+ this.saveFirstTimestampOnly(key, value);
87
+ } else {
88
+ this.latencyTimestamps.set(key, value);
89
+ }
90
+ }
91
+
92
+ /**
93
+ * Store precomputed latency value
94
+ * @param key - key
95
+ * @param value - value
96
+ * @param overwrite - overwrite existing value or add it
97
+ * @throws
98
+ * @returns
99
+ */
100
+ public saveLatency(key: PreComputedLatencies, value: number, overwrite = true) {
101
+ const existingValue = overwrite ? 0 : this.precomputedLatencies.get(key) || 0;
102
+ this.precomputedLatencies.set(key, value + existingValue);
103
+ }
104
+
105
+ /**
106
+ * Measure latency for a request
107
+ * @param key - key
108
+ * @param callback - callback for which you would like to measure latency
109
+ * @param overwrite - overwite existing value or add to it
110
+ * @returns
111
+ */
112
+ public measureLatency(
113
+ callback: () => Promise<any>,
114
+ key: PreComputedLatencies,
115
+ overwrite = false
116
+ ) {
117
+ const start = performance.now();
118
+
119
+ return callback().finally(() => {
120
+ this.saveLatency(key, performance.now() - start, overwrite);
121
+ });
122
+ }
123
+
124
+ /**
125
+ * Store only the first timestamp value for the given key
126
+ * @param key - key
127
+ * @param value -value
128
+ * @throws
129
+ * @returns
130
+ */
131
+ saveFirstTimestampOnly(key: MetricEventNames, value: number = new Date().getTime()) {
132
+ if (this.latencyTimestamps.has(key)) {
133
+ return;
134
+ }
135
+ this.latencyTimestamps.set(key, value);
136
+ }
137
+
138
+ /**
139
+ * Helper to calculate end - start
140
+ * @param a start
141
+ * @param b end
142
+ * @returns latency
143
+ */
144
+ public getDiffBetweenTimestamps(a: MetricEventNames, b: MetricEventNames) {
145
+ const start = this.latencyTimestamps.get(a);
146
+ const end = this.latencyTimestamps.get(b);
147
+ if (start && end) {
148
+ return end - start;
149
+ }
150
+
151
+ return undefined;
152
+ }
153
+
154
+ /**
155
+ * Meeting Info Request
156
+ * @note Meeting Info request happen not just in the join phase. CA requires
157
+ * metrics around meeting info request that are only part of join phase.
158
+ * This internal.* event is used to track the real timestamps
159
+ * (when the actual request/response happen). This is because the actual CA event is
160
+ * sent inside the join method on the meeting object based on some logic, but that's not exactly when
161
+ * those events are actually fired. The logic only confirms that they have happened, and we send them over.
162
+ * @returns - latency
163
+ */
164
+ public getMeetingInfoReqResp() {
165
+ return this.getDiffBetweenTimestamps(
166
+ 'internal.client.meetinginfo.request',
167
+ 'internal.client.meetinginfo.response'
168
+ );
169
+ }
170
+
171
+ /**
172
+ * Interstitial Time
173
+ * @returns - latency
174
+ */
175
+ public getShowInterstitialTime() {
176
+ return this.getDiffBetweenTimestamps(
177
+ 'client.interstitial-window.start-launch',
178
+ 'internal.client.interstitial-window.click.joinbutton'
179
+ );
180
+ }
181
+
182
+ /**
183
+ * Device Register Time
184
+ * @returns - latency
185
+ */
186
+ public getRegisterWDMDeviceJMT() {
187
+ return this.getDiffBetweenTimestamps(
188
+ 'internal.register.device.request',
189
+ 'internal.register.device.response'
190
+ );
191
+ }
192
+
193
+ /**
194
+ * Call Init Join Request
195
+ * @returns - latency
196
+ */
197
+ public getCallInitJoinReq() {
198
+ return this.getDiffBetweenTimestamps(
199
+ 'internal.client.interstitial-window.click.joinbutton',
200
+ 'client.locus.join.request'
201
+ );
202
+ }
203
+
204
+ /**
205
+ * Locus Join Request
206
+ * @returns - latency
207
+ */
208
+ public getJoinReqResp() {
209
+ return this.getDiffBetweenTimestamps('client.locus.join.request', 'client.locus.join.response');
210
+ }
211
+
212
+ /**
213
+ * Time taken to do turn discovery
214
+ * @returns - latency
215
+ */
216
+ public getTurnDiscoveryTime() {
217
+ return this.getDiffBetweenTimestamps(
218
+ 'internal.client.add-media.turn-discovery.start',
219
+ 'internal.client.add-media.turn-discovery.end'
220
+ );
221
+ }
222
+
223
+ /**
224
+ * Local SDP Generated Remote SDP REceived
225
+ * @returns - latency
226
+ */
227
+ public getLocalSDPGenRemoteSDPRecv() {
228
+ return this.getDiffBetweenTimestamps(
229
+ 'client.media-engine.local-sdp-generated',
230
+ 'client.media-engine.remote-sdp-received'
231
+ );
232
+ }
233
+
234
+ /**
235
+ * ICE Setup Time
236
+ * @returns - latency
237
+ */
238
+ public getICESetupTime() {
239
+ return this.getDiffBetweenTimestamps('client.ice.start', 'client.ice.end');
240
+ }
241
+
242
+ /**
243
+ * Audio ICE time
244
+ * @returns - latency
245
+ */
246
+ public getAudioICESetupTime() {
247
+ return this.getDiffBetweenTimestamps('client.ice.start', 'client.ice.end');
248
+ }
249
+
250
+ /**
251
+ * Video ICE Time
252
+ * @returns - latency
253
+ */
254
+ public getVideoICESetupTime() {
255
+ return this.getDiffBetweenTimestamps('client.ice.start', 'client.ice.end');
256
+ }
257
+
258
+ /**
259
+ * Share ICE Time
260
+ * @returns - latency
261
+ */
262
+ public getShareICESetupTime() {
263
+ return this.getDiffBetweenTimestamps('client.ice.start', 'client.ice.end');
264
+ }
265
+
266
+ /**
267
+ * Stay Lobby Time
268
+ * @returns - latency
269
+ */
270
+ public getStayLobbyTime() {
271
+ return this.getDiffBetweenTimestamps(
272
+ 'client.locus.join.response',
273
+ 'internal.host.meeting.participant.admitted'
274
+ );
275
+ }
276
+
277
+ /**
278
+ * Page JMT
279
+ * @returns - latency
280
+ */
281
+ public getPageJMT() {
282
+ return this.precomputedLatencies.get('internal.client.pageJMT') || undefined;
283
+ }
284
+
285
+ /**
286
+ * Download Time JMT
287
+ * @returns - latency
288
+ */
289
+ public getDownloadTimeJMT() {
290
+ return this.precomputedLatencies.get('internal.download.time') || undefined;
291
+ }
292
+
293
+ /**
294
+ * Click To Interstitial
295
+ * @returns - latency
296
+ */
297
+ public getClickToInterstitial() {
298
+ // for normal join (where green join button exists before interstitial, i.e reminder, space list etc)
299
+ if (this.latencyTimestamps.get('internal.client.meeting.click.joinbutton')) {
300
+ return this.getDiffBetweenTimestamps(
301
+ 'internal.client.meeting.click.joinbutton',
302
+ 'internal.client.meeting.interstitial-window.showed'
303
+ );
304
+ }
305
+
306
+ // for cross launch and guest flows
307
+ return this.precomputedLatencies.get('internal.click.to.interstitial') || undefined;
308
+ }
309
+
310
+ /**
311
+ * Interstitial To Join Ok
312
+ * @returns - latency
313
+ */
314
+ public getInterstitialToJoinOK() {
315
+ return this.getDiffBetweenTimestamps(
316
+ 'internal.client.interstitial-window.click.joinbutton',
317
+ 'client.locus.join.response'
318
+ );
319
+ }
320
+
321
+ /**
322
+ * Call Init To MediaEngineReady
323
+ * @returns - latency
324
+ */
325
+ public getCallInitMediaEngineReady() {
326
+ return this.getDiffBetweenTimestamps(
327
+ 'internal.client.interstitial-window.click.joinbutton',
328
+ 'client.media-engine.ready'
329
+ );
330
+ }
331
+
332
+ /**
333
+ * Interstitial To Media Ok
334
+ * @returns - latency
335
+ */
336
+ public getInterstitialToMediaOKJMT() {
337
+ const interstitialJoinClickTimestamp = this.latencyTimestamps.get(
338
+ 'internal.client.interstitial-window.click.joinbutton'
339
+ );
340
+
341
+ // get the first timestamp
342
+ const connectedMedia = this.latencyTimestamps.get('client.ice.end');
343
+
344
+ const lobbyTime = this.getStayLobbyTime() || 0;
345
+
346
+ if (interstitialJoinClickTimestamp && connectedMedia) {
347
+ return connectedMedia - interstitialJoinClickTimestamp - lobbyTime;
348
+ }
349
+
350
+ return undefined;
351
+ }
352
+
353
+ /**
354
+ * Total JMT
355
+ * @returns - latency
356
+ */
357
+ public getTotalJMT() {
358
+ const clickToInterstitial = this.getClickToInterstitial();
359
+ const interstitialToJoinOk = this.getInterstitialToJoinOK();
360
+
361
+ if (clickToInterstitial && interstitialToJoinOk) {
362
+ return clickToInterstitial + interstitialToJoinOk;
363
+ }
364
+
365
+ return undefined;
366
+ }
367
+
368
+ /**
369
+ * Join Conf JMT
370
+ * @returns - latency
371
+ */
372
+ public getJoinConfJMT() {
373
+ const joinReqResp = this.getJoinReqResp();
374
+ const ICESetupTime = this.getICESetupTime();
375
+
376
+ if (joinReqResp && ICESetupTime) {
377
+ return joinReqResp + ICESetupTime;
378
+ }
379
+
380
+ return undefined;
381
+ }
382
+
383
+ /**
384
+ * Total Media JMT
385
+ * @returns - latency
386
+ */
387
+ public getTotalMediaJMT() {
388
+ const clickToInterstitial = this.getClickToInterstitial();
389
+ const interstitialToJoinOk = this.getInterstitialToJoinOK();
390
+ const joinConfJMT = this.getJoinConfJMT();
391
+ const lobbyTime = this.getStayLobbyTime();
392
+
393
+ if (clickToInterstitial && interstitialToJoinOk && joinConfJMT) {
394
+ const totalMediaJMT = clickToInterstitial + interstitialToJoinOk + joinConfJMT;
395
+ if (this.getMeeting()?.allowMediaInLobby) {
396
+ return totalMediaJMT;
397
+ }
398
+
399
+ return totalMediaJMT - lobbyTime;
400
+ }
401
+
402
+ return undefined;
403
+ }
404
+
405
+ /**
406
+ * Client JMT
407
+ * @returns - latency
408
+ */
409
+ public getClientJMT() {
410
+ const interstitialToJoinOk = this.getInterstitialToJoinOK();
411
+ const joinConfJMT = this.getJoinConfJMT();
412
+
413
+ if (interstitialToJoinOk && joinConfJMT) {
414
+ return interstitialToJoinOk - joinConfJMT;
415
+ }
416
+
417
+ return undefined;
418
+ }
419
+
420
+ /**
421
+ * Audio setup delay receive
422
+ */
423
+ public getAudioJoinRespRxStart() {
424
+ return this.getDiffBetweenTimestamps('client.locus.join.response', 'client.media.rx.start');
425
+ }
426
+
427
+ /**
428
+ * Video setup delay receive
429
+ */
430
+ public getVideoJoinRespRxStart() {
431
+ return this.getDiffBetweenTimestamps('client.locus.join.response', 'client.media.rx.start');
432
+ }
433
+
434
+ /**
435
+ * Audio setup delay transmit
436
+ */
437
+ public getAudioJoinRespTxStart() {
438
+ return this.getDiffBetweenTimestamps('client.locus.join.response', 'client.media.tx.start');
439
+ }
440
+
441
+ /**
442
+ * Video setup delay transmit
443
+ */
444
+ public getVideoJoinRespTxStart() {
445
+ return this.getDiffBetweenTimestamps('client.locus.join.response', 'client.media.tx.start');
446
+ }
447
+ }