浏览代码

feat(metrics): related to conference.init execution.

Adds logs and analytics events with time measurements for the different stages of conference.init execution.
factor2
Hristo Terezov 1年前
父节点
当前提交
61a0247f30

+ 23
- 4
conference.js 查看文件

136
     isUserInteractionRequiredForUnmute
136
     isUserInteractionRequiredForUnmute
137
 } from './react/features/base/tracks/functions';
137
 } from './react/features/base/tracks/functions';
138
 import { downloadJSON } from './react/features/base/util/downloadJSON';
138
 import { downloadJSON } from './react/features/base/util/downloadJSON';
139
+import { getJitsiMeetGlobalNSConnectionTimes } from './react/features/base/util/helpers';
139
 import { openLeaveReasonDialog } from './react/features/conference/actions.web';
140
 import { openLeaveReasonDialog } from './react/features/conference/actions.web';
140
 import { showDesktopPicker } from './react/features/desktop-picker/actions';
141
 import { showDesktopPicker } from './react/features/desktop-picker/actions';
141
 import { appendSuffix } from './react/features/display-name/functions';
142
 import { appendSuffix } from './react/features/display-name/functions';
413
      * without any audio tracks.
414
      * without any audio tracks.
414
      * @param {boolean} options.startWithVideoMuted - will start the conference
415
      * @param {boolean} options.startWithVideoMuted - will start the conference
415
      * without any video tracks.
416
      * without any video tracks.
417
+     * @param {boolean} recordTimeMetrics - If true time metrics will be recorded.
416
      * @returns {Promise<JitsiLocalTrack[]>, Object}
418
      * @returns {Promise<JitsiLocalTrack[]>, Object}
417
      */
419
      */
418
-    createInitialLocalTracks(options = {}) {
420
+    createInitialLocalTracks(options = {}, recordTimeMetrics = false) {
419
         const errors = {};
421
         const errors = {};
420
 
422
 
421
         // Always get a handle on the audio input device so that we have statistics (such as "No audio input" or
423
         // Always get a handle on the audio input device so that we have statistics (such as "No audio input" or
487
                 devices: initialDevices,
489
                 devices: initialDevices,
488
                 timeout,
490
                 timeout,
489
                 firePermissionPromptIsShownEvent: true
491
                 firePermissionPromptIsShownEvent: true
490
-            })).then(({ tracks, errors: pErrors }) => {
492
+            }, recordTimeMetrics)).then(({ tracks, errors: pErrors }) => {
491
                 Object.assign(errors, pErrors);
493
                 Object.assign(errors, pErrors);
492
 
494
 
493
                 return tracks;
495
                 return tracks;
571
             startWithAudioMuted: getStartWithAudioMuted(state) || isUserInteractionRequiredForUnmute(state),
573
             startWithAudioMuted: getStartWithAudioMuted(state) || isUserInteractionRequiredForUnmute(state),
572
             startWithVideoMuted: getStartWithVideoMuted(state) || isUserInteractionRequiredForUnmute(state)
574
             startWithVideoMuted: getStartWithVideoMuted(state) || isUserInteractionRequiredForUnmute(state)
573
         };
575
         };
576
+        const connectionTimes = getJitsiMeetGlobalNSConnectionTimes();
577
+        const startTime = window.performance.now();
574
 
578
 
575
-        logger.debug(`Executed conference.init with roomName: ${roomName}`);
579
+        connectionTimes['conference.init.start'] = startTime;
580
+
581
+        logger.debug(`Executed conference.init with roomName: ${roomName} (performance.now=${startTime})`);
576
 
582
 
577
         this.roomName = roomName;
583
         this.roomName = roomName;
578
 
584
 
605
             return localTracks;
611
             return localTracks;
606
         };
612
         };
607
         const { dispatch, getState } = APP.store;
613
         const { dispatch, getState } = APP.store;
608
-        const { tryCreateLocalTracks, errors } = this.createInitialLocalTracks(initialOptions);
614
+        const createLocalTracksStart = window.performance.now();
615
+
616
+        connectionTimes['conference.init.createLocalTracks.start'] = createLocalTracksStart;
617
+
618
+        logger.debug(`(TIME) createInitialLocalTracks: ${createLocalTracksStart} `);
619
+
620
+        const { tryCreateLocalTracks, errors } = this.createInitialLocalTracks(initialOptions, true);
609
 
621
 
610
         tryCreateLocalTracks.then(async tr => {
622
         tryCreateLocalTracks.then(async tr => {
623
+            const createLocalTracksEnd = window.performance.now();
624
+
625
+            connectionTimes['conference.init.createLocalTracks.end'] = createLocalTracksEnd;
626
+            logger.debug(`(TIME) createInitialLocalTracks finished: ${createLocalTracksEnd} `);
611
             const tracks = handleInitialTracks(initialOptions, tr);
627
             const tracks = handleInitialTracks(initialOptions, tr);
612
 
628
 
613
             this._initDeviceList(true);
629
             this._initDeviceList(true);
621
                 // which will guarantee us that the local tracks are added to redux before we proceed.
637
                 // which will guarantee us that the local tracks are added to redux before we proceed.
622
                 initPrejoin(tracks, errors, dispatch);
638
                 initPrejoin(tracks, errors, dispatch);
623
 
639
 
640
+                connectionTimes['conference.init.end'] = window.performance.now();
641
+
624
                 // resolve the initialGUMPromise in case connect have finished so that we can proceed to join.
642
                 // resolve the initialGUMPromise in case connect have finished so that we can proceed to join.
625
                 if (initialGUMPromise) {
643
                 if (initialGUMPromise) {
626
                     logger.debug('Resolving the initialGUM promise! (prejoinVisible=true)');
644
                     logger.debug('Resolving the initialGUM promise! (prejoinVisible=true)');
639
                 APP.store.dispatch(displayErrorsForCreateInitialLocalTracks(errors));
657
                 APP.store.dispatch(displayErrorsForCreateInitialLocalTracks(errors));
640
                 setGUMPendingStateOnFailedTracks(tracks, APP.store.dispatch);
658
                 setGUMPendingStateOnFailedTracks(tracks, APP.store.dispatch);
641
 
659
 
660
+                connectionTimes['conference.init.end'] = window.performance.now();
642
                 if (initialGUMPromise) {
661
                 if (initialGUMPromise) {
643
                     logger.debug('Resolving the initialGUM promise!');
662
                     logger.debug('Resolving the initialGUM promise!');
644
                     initialGUMPromise.resolve({
663
                     initialGUMPromise.resolve({

+ 6
- 0
react/features/base/devices/actions.web.ts 查看文件

75
         const deviceLabels = getDevicesFromURL(getState());
75
         const deviceLabels = getDevicesFromURL(getState());
76
         let updateSettingsPromise;
76
         let updateSettingsPromise;
77
 
77
 
78
+        logger.debug(`(TIME) configureInitialDevices: deviceLabels=${
79
+            Boolean(deviceLabels)}, performance.now=${window.performance.now()}`);
80
+
78
         if (deviceLabels) {
81
         if (deviceLabels) {
79
             updateSettingsPromise = dispatch(getAvailableDevices()).then(() => {
82
             updateSettingsPromise = dispatch(getAvailableDevices()).then(() => {
80
                 const state = getState();
83
                 const state = getState();
127
             .then(() => {
130
             .then(() => {
128
                 const userSelectedAudioOutputDeviceId = getUserSelectedOutputDeviceId(getState());
131
                 const userSelectedAudioOutputDeviceId = getUserSelectedOutputDeviceId(getState());
129
 
132
 
133
+                logger.debug(`(TIME) configureInitialDevices -> setAudioOutputDeviceId: performance.now=${
134
+                    window.performance.now()}`);
135
+
130
                 return setAudioOutputDeviceId(userSelectedAudioOutputDeviceId, dispatch)
136
                 return setAudioOutputDeviceId(userSelectedAudioOutputDeviceId, dispatch)
131
                     .catch(ex => logger.warn(`Failed to set audio output device.
137
                     .catch(ex => logger.warn(`Failed to set audio output device.
132
                         Default audio output device will be used instead ${ex}`));
138
                         Default audio output device will be used instead ${ex}`));

+ 3
- 2
react/features/base/tracks/actions.web.ts 查看文件

352
  * Creates the initial audio/video tracks.
352
  * Creates the initial audio/video tracks.
353
  *
353
  *
354
  * @param {ICreateInitialTracksOptions} options - Options for creating the audio/video tracks.
354
  * @param {ICreateInitialTracksOptions} options - Options for creating the audio/video tracks.
355
+ * @param {boolean} recordTimeMetrics - If true time metrics will be recorded.
355
  * @returns {Function}
356
  * @returns {Function}
356
  */
357
  */
357
-export function createInitialAVTracks(options: ICreateInitialTracksOptions) {
358
+export function createInitialAVTracks(options: ICreateInitialTracksOptions, recordTimeMetrics = false) {
358
     return (dispatch: IStore['dispatch'], _getState: IStore['getState']) => {
359
     return (dispatch: IStore['dispatch'], _getState: IStore['getState']) => {
359
         const {
360
         const {
360
             devices,
361
             devices,
364
 
365
 
365
         dispatch(gumPending(devices, IGUMPendingState.PENDING_UNMUTE));
366
         dispatch(gumPending(devices, IGUMPendingState.PENDING_UNMUTE));
366
 
367
 
367
-        return createLocalTracksF(options).then(tracks => {
368
+        return createLocalTracksF(options, undefined, recordTimeMetrics).then(tracks => {
368
             return {
369
             return {
369
                 errors: {} as IInitialTracksErrors,
370
                 errors: {} as IInitialTracksErrors,
370
                 tracks
371
                 tracks

+ 7
- 1
react/features/base/tracks/functions.web.ts 查看文件

11
     getUserSelectedCameraDeviceId,
11
     getUserSelectedCameraDeviceId,
12
     getUserSelectedMicDeviceId
12
     getUserSelectedMicDeviceId
13
 } from '../settings/functions.web';
13
 } from '../settings/functions.web';
14
+import { getJitsiMeetGlobalNSConnectionTimes } from '../util/helpers';
14
 
15
 
15
 import { getCameraFacingMode } from './functions.any';
16
 import { getCameraFacingMode } from './functions.any';
16
 import loadEffects from './loadEffects';
17
 import loadEffects from './loadEffects';
36
  * corresponding event.
37
  * corresponding event.
37
  * @param {IStore} store - The redux store in the context of which the function
38
  * @param {IStore} store - The redux store in the context of which the function
38
  * is to execute and from which state such as {@code config} is to be retrieved.
39
  * is to execute and from which state such as {@code config} is to be retrieved.
40
+ * @param {boolean} recordTimeMetrics - If true time metrics will be recorded.
39
  * @returns {Promise<JitsiLocalTrack[]>}
41
  * @returns {Promise<JitsiLocalTrack[]>}
40
  */
42
  */
41
-export function createLocalTracksF(options: ITrackOptions = {}, store?: IStore) {
43
+export function createLocalTracksF(options: ITrackOptions = {}, store?: IStore, recordTimeMetrics = false) {
42
     let { cameraDeviceId, micDeviceId } = options;
44
     let { cameraDeviceId, micDeviceId } = options;
43
     const {
45
     const {
44
         desktopSharingSourceDevice,
46
         desktopSharingSourceDevice,
69
 
71
 
70
     return (
72
     return (
71
         loadEffects(store).then((effectsArray: Object[]) => {
73
         loadEffects(store).then((effectsArray: Object[]) => {
74
+            if (recordTimeMetrics) {
75
+                getJitsiMeetGlobalNSConnectionTimes()['trackEffects.loaded'] = window.performance.now();
76
+            }
77
+
72
             // Filter any undefined values returned by Promise.resolve().
78
             // Filter any undefined values returned by Promise.resolve().
73
             const effects = effectsArray.filter(effect => Boolean(effect));
79
             const effects = effectsArray.filter(effect => Boolean(effect));
74
 
80
 

+ 8
- 1
react/features/base/tracks/loadEffects.web.ts 查看文件

11
  * @returns {Promise} - A Promise which resolves when all effects are created.
11
  * @returns {Promise} - A Promise which resolves when all effects are created.
12
  */
12
  */
13
 export default function loadEffects(store: IStore): Promise<any> {
13
 export default function loadEffects(store: IStore): Promise<any> {
14
+    const start = window.performance.now();
14
     const state = store.getState();
15
     const state = store.getState();
15
     const virtualBackground = state['features/virtual-background'];
16
     const virtualBackground = state['features/virtual-background'];
16
     const noiseSuppression = state['features/noise-suppression'];
17
     const noiseSuppression = state['features/noise-suppression'];
30
         ? Promise.resolve(new NoiseSuppressionEffect(nsOptions))
31
         ? Promise.resolve(new NoiseSuppressionEffect(nsOptions))
31
         : Promise.resolve();
32
         : Promise.resolve();
32
 
33
 
33
-    return Promise.all([ backgroundPromise, noiseSuppressionPromise ]);
34
+    return Promise.all([ backgroundPromise, noiseSuppressionPromise ]).then(effectsArray => {
35
+        const end = window.performance.now();
36
+
37
+        logger.debug(`(TIME) loadEffects() start=${start}, end=${end}, time=${end - start}`);
38
+
39
+        return effectsArray;
40
+    });
34
 }
41
 }

+ 15
- 0
react/features/base/util/helpers.ts 查看文件

100
     return window.JitsiMeetJS.app;
100
     return window.JitsiMeetJS.app;
101
 }
101
 }
102
 
102
 
103
+/**
104
+ * Returns the object that stores the connection times.
105
+ *
106
+ * @returns {Object} - The object that stores the connection times.
107
+ */
108
+export function getJitsiMeetGlobalNSConnectionTimes() {
109
+    const globalNS = getJitsiMeetGlobalNS();
110
+
111
+    if (!globalNS.connectionTimes) {
112
+        globalNS.connectionTimes = {};
113
+    }
114
+
115
+    return globalNS.connectionTimes;
116
+}
117
+
103
 /**
118
 /**
104
  * Prints the error and reports it to the global error handler.
119
  * Prints the error and reports it to the global error handler.
105
  *
120
  *

+ 22
- 2
react/features/conference/actions.web.ts 查看文件

1
 import { IStore } from '../app/types';
1
 import { IStore } from '../app/types';
2
 import { configureInitialDevices, getAvailableDevices } from '../base/devices/actions.web';
2
 import { configureInitialDevices, getAvailableDevices } from '../base/devices/actions.web';
3
 import { openDialog } from '../base/dialog/actions';
3
 import { openDialog } from '../base/dialog/actions';
4
+import { getJitsiMeetGlobalNSConnectionTimes } from '../base/util/helpers';
4
 import { getBackendSafeRoomName } from '../base/util/uri';
5
 import { getBackendSafeRoomName } from '../base/util/uri';
5
 
6
 
6
 import { DISMISS_CALENDAR_NOTIFICATION } from './actionTypes';
7
 import { DISMISS_CALENDAR_NOTIFICATION } from './actionTypes';
37
 /**
38
 /**
38
  * Setups initial devices. Makes sure we populate availableDevices list before configuring.
39
  * Setups initial devices. Makes sure we populate availableDevices list before configuring.
39
  *
40
  *
41
+ * @param {boolean} recordTimeMetrics - If true, an analytics time metrics will be sent.
40
  * @returns {Promise<any>}
42
  * @returns {Promise<any>}
41
  */
43
  */
42
-export function setupInitialDevices() {
44
+export function setupInitialDevices(recordTimeMetrics = false) {
43
     return async (dispatch: IStore['dispatch']) => {
45
     return async (dispatch: IStore['dispatch']) => {
46
+        if (recordTimeMetrics) {
47
+            getJitsiMeetGlobalNSConnectionTimes()['setupInitialDevices.start'] = window.performance.now();
48
+        }
49
+
44
         await dispatch(getAvailableDevices());
50
         await dispatch(getAvailableDevices());
51
+
52
+        if (recordTimeMetrics) {
53
+            getJitsiMeetGlobalNSConnectionTimes()['setupInitialDevices.getAD.finished'] = window.performance.now();
54
+        }
55
+
45
         await dispatch(configureInitialDevices());
56
         await dispatch(configureInitialDevices());
57
+
58
+        const now = window.performance.now();
59
+
60
+        if (recordTimeMetrics) {
61
+            getJitsiMeetGlobalNSConnectionTimes()['setupInitialDevices.end'] = now;
62
+        }
63
+        logger.debug(`(TIME) setupInitialDevices finished: ${now}`);
46
     };
64
     };
47
 }
65
 }
48
 
66
 
55
  */
73
  */
56
 export function init(shouldDispatchConnect: boolean) {
74
 export function init(shouldDispatchConnect: boolean) {
57
     return (dispatch: IStore['dispatch'], getState: IStore['getState']) => {
75
     return (dispatch: IStore['dispatch'], getState: IStore['getState']) => {
76
+        logger.debug(`(TIME) init action dispatched: ${window.performance.now()}`);
77
+
58
         const room = getBackendSafeRoomName(getState()['features/base/conference'].room);
78
         const room = getBackendSafeRoomName(getState()['features/base/conference'].room);
59
 
79
 
60
         // XXX For web based version we use conference initialization logic
80
         // XXX For web based version we use conference initialization logic
61
         // from the old app (at the moment of writing).
81
         // from the old app (at the moment of writing).
62
-        return dispatch(setupInitialDevices()).then(
82
+        return dispatch(setupInitialDevices(true)).then(
63
             () => APP.conference.init({
83
             () => APP.conference.init({
64
                 roomName: room,
84
                 roomName: room,
65
                 shouldDispatchConnect
85
                 shouldDispatchConnect

+ 5
- 6
react/index.web.js 查看文件

4
 import { App } from './features/app/components/App.web';
4
 import { App } from './features/app/components/App.web';
5
 import { getLogger } from './features/base/logging/functions';
5
 import { getLogger } from './features/base/logging/functions';
6
 import Platform from './features/base/react/Platform.web';
6
 import Platform from './features/base/react/Platform.web';
7
-import { getJitsiMeetGlobalNS } from './features/base/util/helpers';
7
+import { getJitsiMeetGlobalNS, getJitsiMeetGlobalNSConnectionTimes } from './features/base/util/helpers';
8
 import DialInSummaryApp from './features/invite/components/dial-in-summary/web/DialInSummaryApp';
8
 import DialInSummaryApp from './features/invite/components/dial-in-summary/web/DialInSummaryApp';
9
 import PrejoinApp from './features/prejoin/components/web/PrejoinApp';
9
 import PrejoinApp from './features/prejoin/components/web/PrejoinApp';
10
 import WhiteboardApp from './features/whiteboard/components/web/WhiteboardApp';
10
 import WhiteboardApp from './features/whiteboard/components/web/WhiteboardApp';
45
 }
45
 }
46
 
46
 
47
 const globalNS = getJitsiMeetGlobalNS();
47
 const globalNS = getJitsiMeetGlobalNS();
48
+const connectionTimes = getJitsiMeetGlobalNSConnectionTimes();
48
 
49
 
49
 // Used for automated performance tests.
50
 // Used for automated performance tests.
50
-globalNS.connectionTimes = {
51
-    'index.loaded': window.indexLoadedTime
52
-};
51
+connectionTimes['index.loaded'] = window.indexLoadedTime;
53
 
52
 
54
 window.addEventListener('load', () => {
53
 window.addEventListener('load', () => {
55
-    globalNS.connectionTimes['window.loaded'] = window.loadedEventTime;
54
+    connectionTimes['window.loaded'] = window.loadedEventTime;
56
 });
55
 });
57
 
56
 
58
 document.addEventListener('DOMContentLoaded', () => {
57
 document.addEventListener('DOMContentLoaded', () => {
59
     const now = window.performance.now();
58
     const now = window.performance.now();
60
 
59
 
61
-    globalNS.connectionTimes['document.ready'] = now;
60
+    connectionTimes['document.ready'] = now;
62
     logger.log('(TIME) document ready:\t', now);
61
     logger.log('(TIME) document ready:\t', now);
63
 });
62
 });
64
 
63
 

正在加载...
取消
保存