@webex/internal-plugin-metrics 3.0.0-bnr.5 → 3.0.0-next.10

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 (78) hide show
  1. package/.eslintrc.js +6 -0
  2. package/babel.config.js +3 -0
  3. package/dist/batcher.js +41 -3
  4. package/dist/batcher.js.map +1 -1
  5. package/dist/call-diagnostic/call-diagnostic-metrics-batcher.js +64 -0
  6. package/dist/call-diagnostic/call-diagnostic-metrics-batcher.js.map +1 -0
  7. package/dist/call-diagnostic/call-diagnostic-metrics-latencies.js +550 -0
  8. package/dist/call-diagnostic/call-diagnostic-metrics-latencies.js.map +1 -0
  9. package/dist/call-diagnostic/call-diagnostic-metrics.js +858 -0
  10. package/dist/call-diagnostic/call-diagnostic-metrics.js.map +1 -0
  11. package/dist/call-diagnostic/call-diagnostic-metrics.util.js +356 -0
  12. package/dist/call-diagnostic/call-diagnostic-metrics.util.js.map +1 -0
  13. package/dist/call-diagnostic/config.js +609 -0
  14. package/dist/call-diagnostic/config.js.map +1 -0
  15. package/dist/client-metrics-batcher.js +3 -3
  16. package/dist/client-metrics-batcher.js.map +1 -1
  17. package/dist/config.js +6 -9
  18. package/dist/config.js.map +1 -1
  19. package/dist/index.js +35 -2
  20. package/dist/index.js.map +1 -1
  21. package/dist/metrics.js +28 -22
  22. package/dist/metrics.js.map +1 -1
  23. package/dist/metrics.types.js +7 -0
  24. package/dist/metrics.types.js.map +1 -0
  25. package/dist/new-metrics.js +303 -0
  26. package/dist/new-metrics.js.map +1 -0
  27. package/dist/prelogin-metrics-batcher.js +81 -0
  28. package/dist/prelogin-metrics-batcher.js.map +1 -0
  29. package/dist/types/batcher.d.ts +5 -0
  30. package/dist/types/call-diagnostic/call-diagnostic-metrics-latencies.d.ts +237 -0
  31. package/dist/types/call-diagnostic/call-diagnostic-metrics.d.ts +425 -0
  32. package/dist/types/call-diagnostic/call-diagnostic-metrics.util.d.ts +103 -0
  33. package/dist/types/call-diagnostic/config.d.ts +178 -0
  34. package/dist/types/config.d.ts +18 -0
  35. package/dist/types/index.d.ts +15 -3
  36. package/dist/types/metrics.d.ts +1 -0
  37. package/dist/types/metrics.types.d.ts +107 -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 +26 -0
  42. package/dist/utils.js.map +1 -0
  43. package/jest.config.js +3 -0
  44. package/package.json +34 -10
  45. package/process +1 -0
  46. package/src/batcher.js +38 -0
  47. package/src/call-diagnostic/call-diagnostic-metrics-batcher.ts +72 -0
  48. package/src/call-diagnostic/call-diagnostic-metrics-latencies.ts +511 -0
  49. package/src/call-diagnostic/call-diagnostic-metrics.ts +925 -0
  50. package/src/call-diagnostic/call-diagnostic-metrics.util.ts +399 -0
  51. package/src/call-diagnostic/config.ts +685 -0
  52. package/src/client-metrics-batcher.js +1 -0
  53. package/src/config.js +1 -0
  54. package/src/index.ts +56 -0
  55. package/src/metrics.js +20 -16
  56. package/src/metrics.types.ts +179 -0
  57. package/src/new-metrics.ts +278 -0
  58. package/src/prelogin-metrics-batcher.ts +95 -0
  59. package/src/utils.ts +17 -0
  60. package/test/unit/spec/batcher.js +2 -0
  61. package/test/unit/spec/call-diagnostic/call-diagnostic-metrics-batcher.ts +469 -0
  62. package/test/unit/spec/call-diagnostic/call-diagnostic-metrics-latencies.ts +718 -0
  63. package/test/unit/spec/call-diagnostic/call-diagnostic-metrics.ts +2371 -0
  64. package/test/unit/spec/call-diagnostic/call-diagnostic-metrics.util.ts +637 -0
  65. package/test/unit/spec/client-metrics-batcher.js +2 -0
  66. package/test/unit/spec/metrics.js +76 -95
  67. package/test/unit/spec/new-metrics.ts +231 -0
  68. package/test/unit/spec/prelogin-metrics-batcher.ts +253 -0
  69. package/test/unit/spec/utils.ts +22 -0
  70. package/tsconfig.json +6 -0
  71. package/dist/call-diagnostic-events-batcher.js +0 -60
  72. package/dist/call-diagnostic-events-batcher.js.map +0 -1
  73. package/dist/internal-plugin-metrics.d.ts +0 -21
  74. package/dist/tsdoc-metadata.json +0 -11
  75. package/src/call-diagnostic-events-batcher.js +0 -62
  76. package/src/index.js +0 -17
  77. package/test/unit/spec/call-diagnostic-events-batcher.js +0 -195
  78. package/dist/types/{call-diagnostic-events-batcher.d.ts → call-diagnostic/call-diagnostic-metrics-batcher.d.ts} +1 -1
@@ -0,0 +1,511 @@
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
+ this.precomputedLatencies.clear();
35
+ }
36
+
37
+ /**
38
+ * Associate current latencies with a meeting id
39
+ * @param meetingId
40
+ */
41
+ private setMeetingId(meetingId: string) {
42
+ this.meetingId = meetingId;
43
+ }
44
+
45
+ /**
46
+ * Returns the meeting object associated with current latencies
47
+ * @returns meeting object
48
+ */
49
+ private getMeeting() {
50
+ if (this.meetingId) {
51
+ // @ts-ignore
52
+ return this.webex.meetings.meetingCollection.get(this.meetingId);
53
+ }
54
+
55
+ return undefined;
56
+ }
57
+
58
+ /**
59
+ * Store timestamp value
60
+ * @param key - key
61
+ * @param value - value
62
+ * @param options - store options
63
+ * @throws
64
+ * @returns
65
+ */
66
+ public saveTimestamp({
67
+ key,
68
+ value = new Date().getTime(),
69
+ options = {},
70
+ }: {
71
+ key: MetricEventNames;
72
+ value?: number;
73
+ options?: {meetingId?: string};
74
+ }) {
75
+ // save the meetingId so we can use the meeting object in latency calculations if needed
76
+ const {meetingId} = options;
77
+ if (meetingId) {
78
+ this.setMeetingId(meetingId);
79
+ }
80
+ // for some events we're only interested in the first timestamp not last
81
+ // as these events can happen multiple times
82
+ if (
83
+ key === 'client.media.rx.start' ||
84
+ key === 'client.media.tx.start' ||
85
+ key === 'internal.client.meetinginfo.request' ||
86
+ key === 'internal.client.meetinginfo.response'
87
+ ) {
88
+ this.saveFirstTimestampOnly(key, value);
89
+ } else {
90
+ this.latencyTimestamps.set(key, value);
91
+ }
92
+ }
93
+
94
+ /**
95
+ * Store precomputed latency value
96
+ * @param key - key
97
+ * @param value - value
98
+ * @param accumulate - when it is true, it overwrites existing value with sum of the current value and the new measurement otherwise just store the new measurement
99
+ * @throws
100
+ * @returns
101
+ */
102
+ public saveLatency(key: PreComputedLatencies, value: number, accumulate = false) {
103
+ const existingValue = accumulate ? this.precomputedLatencies.get(key) || 0 : 0;
104
+ this.precomputedLatencies.set(key, value + existingValue);
105
+ }
106
+
107
+ /**
108
+ * Measure latency for a request
109
+ * @param callback - callback for which you would like to measure latency
110
+ * @param key - key
111
+ * @param accumulate - when it is true, it overwrites existing value with sum of the current value and the new measurement otherwise just store the new measurement
112
+ * @returns
113
+ */
114
+ public measureLatency(
115
+ callback: () => Promise<unknown>,
116
+ key: PreComputedLatencies,
117
+ accumulate = false
118
+ ) {
119
+ const start = performance.now();
120
+
121
+ return callback().finally(() => {
122
+ this.saveLatency(key, performance.now() - start, accumulate);
123
+ });
124
+ }
125
+
126
+ /**
127
+ * Store only the first timestamp value for the given key
128
+ * @param key - key
129
+ * @param value -value
130
+ * @throws
131
+ * @returns
132
+ */
133
+ saveFirstTimestampOnly(key: MetricEventNames, value: number = new Date().getTime()) {
134
+ if (this.latencyTimestamps.has(key)) {
135
+ return;
136
+ }
137
+ this.latencyTimestamps.set(key, value);
138
+ }
139
+
140
+ /**
141
+ * Helper to calculate end - start
142
+ * @param a start
143
+ * @param b end
144
+ * @returns latency
145
+ */
146
+ public getDiffBetweenTimestamps(a: MetricEventNames, b: MetricEventNames) {
147
+ const start = this.latencyTimestamps.get(a);
148
+ const end = this.latencyTimestamps.get(b);
149
+ if (start && end) {
150
+ return end - start;
151
+ }
152
+
153
+ return undefined;
154
+ }
155
+
156
+ /**
157
+ * Meeting Info Request
158
+ * @note Meeting Info request happen not just in the join phase. CA requires
159
+ * metrics around meeting info request that are only part of join phase.
160
+ * This internal.* event is used to track the real timestamps
161
+ * (when the actual request/response happen). This is because the actual CA event is
162
+ * sent inside the join method on the meeting object based on some logic, but that's not exactly when
163
+ * those events are actually fired. The logic only confirms that they have happened, and we send them over.
164
+ * @returns - latency
165
+ */
166
+ public getMeetingInfoReqResp() {
167
+ return this.getDiffBetweenTimestamps(
168
+ 'internal.client.meetinginfo.request',
169
+ 'internal.client.meetinginfo.response'
170
+ );
171
+ }
172
+
173
+ /**
174
+ * Interstitial Time
175
+ * @returns - latency
176
+ */
177
+ public getShowInterstitialTime() {
178
+ return this.getDiffBetweenTimestamps(
179
+ 'client.interstitial-window.start-launch',
180
+ 'internal.client.interstitial-window.click.joinbutton'
181
+ );
182
+ }
183
+
184
+ /**
185
+ * getU2CTime
186
+ * @returns - latency
187
+ */
188
+ public getU2CTime() {
189
+ const u2cLatency = this.precomputedLatencies.get('internal.get.u2c.time');
190
+
191
+ return u2cLatency ? Math.floor(u2cLatency) : undefined;
192
+ }
193
+
194
+ /**
195
+ * Device Register Time
196
+ * @returns - latency
197
+ */
198
+ public getRegisterWDMDeviceJMT() {
199
+ return this.getDiffBetweenTimestamps(
200
+ 'internal.register.device.request',
201
+ 'internal.register.device.response'
202
+ );
203
+ }
204
+
205
+ /**
206
+ * Call Init Join Request
207
+ * @returns - latency
208
+ */
209
+ public getCallInitJoinReq() {
210
+ return this.getDiffBetweenTimestamps(
211
+ 'internal.client.interstitial-window.click.joinbutton',
212
+ 'client.locus.join.request'
213
+ );
214
+ }
215
+
216
+ /**
217
+ * Locus Join Request
218
+ * @returns - latency
219
+ */
220
+ public getJoinReqResp() {
221
+ return this.getDiffBetweenTimestamps('client.locus.join.request', 'client.locus.join.response');
222
+ }
223
+
224
+ /**
225
+ * Time taken to do turn discovery
226
+ * @returns - latency
227
+ */
228
+ public getTurnDiscoveryTime() {
229
+ return this.getDiffBetweenTimestamps(
230
+ 'internal.client.add-media.turn-discovery.start',
231
+ 'internal.client.add-media.turn-discovery.end'
232
+ );
233
+ }
234
+
235
+ /**
236
+ * Local SDP Generated Remote SDP REceived
237
+ * @returns - latency
238
+ */
239
+ public getLocalSDPGenRemoteSDPRecv() {
240
+ return this.getDiffBetweenTimestamps(
241
+ 'client.media-engine.local-sdp-generated',
242
+ 'client.media-engine.remote-sdp-received'
243
+ );
244
+ }
245
+
246
+ /**
247
+ * ICE Setup Time
248
+ * @returns - latency
249
+ */
250
+ public getICESetupTime() {
251
+ return this.getDiffBetweenTimestamps('client.ice.start', 'client.ice.end');
252
+ }
253
+
254
+ /**
255
+ * Audio ICE time
256
+ * @returns - latency
257
+ */
258
+ public getAudioICESetupTime() {
259
+ return this.getDiffBetweenTimestamps('client.ice.start', 'client.ice.end');
260
+ }
261
+
262
+ /**
263
+ * Video ICE Time
264
+ * @returns - latency
265
+ */
266
+ public getVideoICESetupTime() {
267
+ return this.getDiffBetweenTimestamps('client.ice.start', 'client.ice.end');
268
+ }
269
+
270
+ /**
271
+ * Share ICE Time
272
+ * @returns - latency
273
+ */
274
+ public getShareICESetupTime() {
275
+ return this.getDiffBetweenTimestamps('client.ice.start', 'client.ice.end');
276
+ }
277
+
278
+ /**
279
+ * Stay Lobby Time
280
+ * @returns - latency
281
+ */
282
+ public getStayLobbyTime() {
283
+ return this.getDiffBetweenTimestamps(
284
+ 'client.locus.join.response',
285
+ 'internal.host.meeting.participant.admitted'
286
+ );
287
+ }
288
+
289
+ /**
290
+ * Page JMT
291
+ * @returns - latency
292
+ */
293
+ public getPageJMT() {
294
+ return this.precomputedLatencies.get('internal.client.pageJMT') || undefined;
295
+ }
296
+
297
+ /**
298
+ * Download Time JMT
299
+ * @returns - latency
300
+ */
301
+ public getDownloadTimeJMT() {
302
+ return this.precomputedLatencies.get('internal.download.time') || undefined;
303
+ }
304
+
305
+ /**
306
+ * Click To Interstitial
307
+ * @returns - latency
308
+ */
309
+ public getClickToInterstitial() {
310
+ // for normal join (where green join button exists before interstitial, i.e reminder, space list etc)
311
+ if (this.latencyTimestamps.get('internal.client.meeting.click.joinbutton')) {
312
+ return this.getDiffBetweenTimestamps(
313
+ 'internal.client.meeting.click.joinbutton',
314
+ 'internal.client.meeting.interstitial-window.showed'
315
+ );
316
+ }
317
+
318
+ // for cross launch and guest flows
319
+ return this.precomputedLatencies.get('internal.click.to.interstitial') || undefined;
320
+ }
321
+
322
+ /**
323
+ * Interstitial To Join Ok
324
+ * @returns - latency
325
+ */
326
+ public getInterstitialToJoinOK() {
327
+ return this.getDiffBetweenTimestamps(
328
+ 'internal.client.interstitial-window.click.joinbutton',
329
+ 'client.locus.join.response'
330
+ );
331
+ }
332
+
333
+ /**
334
+ * Call Init To MediaEngineReady
335
+ * @returns - latency
336
+ */
337
+ public getCallInitMediaEngineReady() {
338
+ return this.getDiffBetweenTimestamps(
339
+ 'internal.client.interstitial-window.click.joinbutton',
340
+ 'client.media-engine.ready'
341
+ );
342
+ }
343
+
344
+ /**
345
+ * Interstitial To Media Ok
346
+ * @returns - latency
347
+ */
348
+ public getInterstitialToMediaOKJMT() {
349
+ const interstitialJoinClickTimestamp = this.latencyTimestamps.get(
350
+ 'internal.client.interstitial-window.click.joinbutton'
351
+ );
352
+
353
+ // get the first timestamp
354
+ const connectedMedia = this.latencyTimestamps.get('client.ice.end');
355
+
356
+ const lobbyTime = this.getStayLobbyTime() || 0;
357
+
358
+ if (interstitialJoinClickTimestamp && connectedMedia) {
359
+ return connectedMedia - interstitialJoinClickTimestamp - lobbyTime;
360
+ }
361
+
362
+ return undefined;
363
+ }
364
+
365
+ /**
366
+ * Total JMT
367
+ * @returns - latency
368
+ */
369
+ public getTotalJMT() {
370
+ const clickToInterstitial = this.getClickToInterstitial();
371
+ const interstitialToJoinOk = this.getInterstitialToJoinOK();
372
+
373
+ if (clickToInterstitial && interstitialToJoinOk) {
374
+ return clickToInterstitial + interstitialToJoinOk;
375
+ }
376
+
377
+ return undefined;
378
+ }
379
+
380
+ /**
381
+ * Join Conf JMT
382
+ * @returns - latency
383
+ */
384
+ public getJoinConfJMT() {
385
+ const joinReqResp = this.getJoinReqResp();
386
+ const ICESetupTime = this.getICESetupTime();
387
+
388
+ if (joinReqResp && ICESetupTime) {
389
+ return joinReqResp + ICESetupTime;
390
+ }
391
+
392
+ return undefined;
393
+ }
394
+
395
+ /**
396
+ * Total Media JMT
397
+ * @returns - latency
398
+ */
399
+ public getTotalMediaJMT() {
400
+ const clickToInterstitial = this.getClickToInterstitial();
401
+ const interstitialToJoinOk = this.getInterstitialToJoinOK();
402
+ const joinConfJMT = this.getJoinConfJMT();
403
+ const lobbyTime = this.getStayLobbyTime();
404
+
405
+ if (clickToInterstitial && interstitialToJoinOk && joinConfJMT) {
406
+ const totalMediaJMT = clickToInterstitial + interstitialToJoinOk + joinConfJMT;
407
+ if (this.getMeeting()?.allowMediaInLobby) {
408
+ return totalMediaJMT;
409
+ }
410
+
411
+ return totalMediaJMT - lobbyTime;
412
+ }
413
+
414
+ return undefined;
415
+ }
416
+
417
+ /**
418
+ * Client JMT
419
+ * @returns - latency
420
+ */
421
+ public getClientJMT() {
422
+ const interstitialToJoinOk = this.getInterstitialToJoinOK();
423
+ const joinConfJMT = this.getJoinConfJMT();
424
+
425
+ if (interstitialToJoinOk && joinConfJMT) {
426
+ return interstitialToJoinOk - joinConfJMT;
427
+ }
428
+
429
+ return undefined;
430
+ }
431
+
432
+ /**
433
+ * Audio setup delay receive
434
+ */
435
+ public getAudioJoinRespRxStart() {
436
+ return this.getDiffBetweenTimestamps('client.locus.join.response', 'client.media.rx.start');
437
+ }
438
+
439
+ /**
440
+ * Video setup delay receive
441
+ */
442
+ public getVideoJoinRespRxStart() {
443
+ return this.getDiffBetweenTimestamps('client.locus.join.response', 'client.media.rx.start');
444
+ }
445
+
446
+ /**
447
+ * Total latency for all get cluster request.
448
+ */
449
+ public getReachabilityClustersReqResp() {
450
+ const reachablityClusterReqResp = this.precomputedLatencies.get('internal.get.cluster.time');
451
+
452
+ return reachablityClusterReqResp ? Math.floor(reachablityClusterReqResp) : undefined;
453
+ }
454
+
455
+ /**
456
+ * Audio setup delay transmit
457
+ */
458
+ public getAudioJoinRespTxStart() {
459
+ return this.getDiffBetweenTimestamps('client.locus.join.response', 'client.media.tx.start');
460
+ }
461
+
462
+ /**
463
+ * Video setup delay transmit
464
+ */
465
+ public getVideoJoinRespTxStart() {
466
+ return this.getDiffBetweenTimestamps('client.locus.join.response', 'client.media.tx.start');
467
+ }
468
+
469
+ /**
470
+ * Total latency for all exchange ci token.
471
+ */
472
+ public getExchangeCITokenJMT() {
473
+ const exchangeCITokenJMT = this.precomputedLatencies.get('internal.exchange.ci.token.time');
474
+
475
+ return exchangeCITokenJMT ? Math.floor(exchangeCITokenJMT) : undefined;
476
+ }
477
+
478
+ /**
479
+ * Total latency for all refresh captcha requests.
480
+ */
481
+ public getRefreshCaptchaReqResp() {
482
+ const refreshCaptchaReqResp = this.precomputedLatencies.get('internal.refresh.captcha.time');
483
+
484
+ return refreshCaptchaReqResp ? Math.floor(refreshCaptchaReqResp) : undefined;
485
+ }
486
+
487
+ /**
488
+ * Get the latency for downloading intelligence models.
489
+ * @returns - latency
490
+ */
491
+ public getDownloadIntelligenceModelsReqResp() {
492
+ const downloadIntelligenceModelsReqResp = this.precomputedLatencies.get(
493
+ 'internal.api.fetch.intelligence.models'
494
+ );
495
+
496
+ return downloadIntelligenceModelsReqResp
497
+ ? Math.floor(downloadIntelligenceModelsReqResp)
498
+ : undefined;
499
+ }
500
+
501
+ /**
502
+ * Get the total latency for all other app API requests.
503
+ * Excludes meeting info, because it's measured separately.
504
+ * @returns - latency
505
+ */
506
+ public getOtherAppApiReqResp() {
507
+ const otherAppApiJMT = this.precomputedLatencies.get('internal.other.app.api.time');
508
+
509
+ return otherAppApiJMT > 0 ? Math.floor(otherAppApiJMT) : undefined;
510
+ }
511
+ }