Skip to content

Commit

Permalink
feat(meetings): incremental log uploads
Browse files Browse the repository at this point in the history
  • Loading branch information
marcin-bazyl committed Dec 3, 2024
1 parent 602e8c6 commit bb2dd38
Show file tree
Hide file tree
Showing 8 changed files with 145 additions and 0 deletions.
1 change: 1 addition & 0 deletions packages/@webex/internal-plugin-support/src/config.js
Original file line number Diff line number Diff line change
Expand Up @@ -17,5 +17,6 @@ export default {
appType: '',
appVersion: '',
languageCode: '',
incrementalLogs: false,
},
};
4 changes: 4 additions & 0 deletions packages/@webex/internal-plugin-support/src/support.js
Original file line number Diff line number Diff line change
Expand Up @@ -107,6 +107,10 @@ const Support = WebexPlugin.extend({
return this.webex.upload(options);
})
.then((body) => {
if (this.config.incrementalLogs) {
this.webex.logger.clearBuffers();
}

if (userId && !body.userId) {
body.userId = userId;
}
Expand Down
14 changes: 14 additions & 0 deletions packages/@webex/plugin-logger/src/logger.js
Original file line number Diff line number Diff line change
Expand Up @@ -252,6 +252,20 @@ const Logger = WebexPlugin.extend({
return this.getCurrentLevel();
},

/**
* Clears the log buffers
*
* @instance
* @memberof Logger
* @public
* @returns {undefined}
*/
clearBuffers() {
this.clientBuffer = [];
this.sdkBuffer = [];
this.buffer = [];
},

/**
* Format logs (for upload)
*
Expand Down
1 change: 1 addition & 0 deletions packages/@webex/plugin-meetings/src/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -96,5 +96,6 @@ export default {
iceCandidatesGatheringTimeout: undefined,
backendIpv6NativeSupport: false,
reachabilityGetClusterTimeout: 5000,
logUploadIntervalMultiplicationFactor: 0, // if set to 0 or undefined, logs won't be uploaded periodically
},
};
65 changes: 65 additions & 0 deletions packages/@webex/plugin-meetings/src/meeting/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import jwtDecode from 'jwt-decode';
import {StatelessWebexPlugin} from '@webex/webex-core';
// @ts-ignore - Types not available for @webex/common
import {Defer} from '@webex/common';
import {safeSetTimeout, safeSetInterval} from '@webex/common-timers';
import {
ClientEvent,
ClientEventLeaveReason,
Expand Down Expand Up @@ -702,6 +703,8 @@ export default class Meeting extends StatelessWebexPlugin {
private iceCandidateErrors: Map<string, number>;
private iceCandidatesCount: number;
private rtcMetrics?: RtcMetrics;
private uploadLogsTimer?: ReturnType<typeof setTimeout>;
private logUploadIntervalIndex: number;

/**
* @param {Object} attrs
Expand Down Expand Up @@ -770,6 +773,8 @@ export default class Meeting extends StatelessWebexPlugin {
);
this.callStateForMetrics.correlationId = this.id;
}
this.logUploadIntervalIndex = 0;

/**
* @instance
* @type {String}
Expand Down Expand Up @@ -4060,6 +4065,65 @@ export default class Meeting extends StatelessWebexPlugin {
Trigger.trigger(this, options, EVENTS.REQUEST_UPLOAD_LOGS, this);
}

/**
* sets the timer for periodic log upload
* @returns {void}
*/
private setLogUploadTimer() {
// start with short timeouts and increase them later on so in case users have very long multi-hour meetings we don't get too fragmented logs
const LOG_UPLOAD_INTERVALS = [0.1, 1, 15, 15, 30, 30, 30, 60];

const delay =
1000 *
// @ts-ignore - config coming from registerPlugin
this.config.logUploadIntervalMultiplicationFactor *
LOG_UPLOAD_INTERVALS[this.logUploadIntervalIndex];

if (this.logUploadIntervalIndex < LOG_UPLOAD_INTERVALS.length - 1) {
this.logUploadIntervalIndex += 1;
}

this.uploadLogsTimer = safeSetTimeout(() => {
this.uploadLogsTimer = undefined;

this.uploadLogs();

// just as an extra precaution, to avoid uploading logs forever in case something goes wrong
// and the page remains opened, we stop it if there is no media connection
if (!this.mediaProperties.webrtcMediaConnection) {
return;
}

this.setLogUploadTimer();
}, delay);
}

/**
* Starts a periodic upload of logs
*
* @returns {undefined}
*/
public startPeriodicLogUpload() {
// @ts-ignore - config coming from registerPlugin
if (this.config.logUploadIntervalMultiplicationFactor && !this.uploadLogsTimer) {
this.logUploadIntervalIndex = 0;

this.setLogUploadTimer();
}
}

/**
* Stops the periodic upload of logs
*
* @returns {undefined}
*/
public stopPeriodicLogUpload() {
if (this.uploadLogsTimer) {
clearTimeout(this.uploadLogsTimer);
this.uploadLogsTimer = undefined;
}
}

/**
* Removes remote audio, video and share streams from class instance's mediaProperties
* @returns {undefined}
Expand Down Expand Up @@ -7247,6 +7311,7 @@ export default class Meeting extends StatelessWebexPlugin {

// We can log ReceiveSlot SSRCs only after the SDP exchange, so doing it here:
this.remoteMediaManager?.logAllReceiveSlots();
this.startPeriodicLogUpload();
} catch (error) {
LoggerProxy.logger.error(`${LOG_HEADER} failed to establish media connection: `, error);

Expand Down
1 change: 1 addition & 0 deletions packages/@webex/plugin-meetings/src/meeting/util.ts
Original file line number Diff line number Diff line change
Expand Up @@ -177,6 +177,7 @@ const MeetingUtil = {

cleanUp: (meeting) => {
meeting.getWebexObject().internal.device.meetingEnded();
meeting.stopPeriodicLogUpload();

meeting.breakouts.cleanUp();
meeting.simultaneousInterpretation.cleanUp();
Expand Down
57 changes: 57 additions & 0 deletions packages/@webex/plugin-meetings/test/unit/spec/meeting/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -2471,6 +2471,63 @@ describe('plugin-meetings', () => {
checkWorking();
});

it('should upload logs periodically', async () => {
const clock = sinon.useFakeTimers();

meeting.roap.doTurnDiscovery = sinon
.stub()
.resolves({turnServerInfo: undefined, turnDiscoverySkippedReason: undefined});

let logUploadCounter = 0;

TriggerProxy.trigger.callsFake((meetingObject, options, event) => {
if (
meetingObject === meeting &&
options.file === 'meeting/index' &&
options.function === 'uploadLogs' &&
event === 'REQUEST_UPLOAD_LOGS'
) {
logUploadCounter += 1;
}
});

meeting.config.logUploadIntervalMultiplicationFactor = 1;
meeting.meetingState = 'ACTIVE';

await meeting.addMedia({
mediaSettings: {},
});

const checkLogCounter = (delay, expectedCounter) => {
// first check that the counter is not increased just before the delay
clock.tick(delay - 50);
assert.equal(logUploadCounter, expectedCounter - 1);

// and now check that it has reached expected value after the delay
clock.tick(50);
assert.equal(logUploadCounter, expectedCounter);
};

checkLogCounter(100, 1);
checkLogCounter(1000, 2);
checkLogCounter(15000, 3);
checkLogCounter(15000, 4);
checkLogCounter(30000, 5);
checkLogCounter(30000, 6);
checkLogCounter(30000, 7);
checkLogCounter(60000, 8);
checkLogCounter(60000, 9);
checkLogCounter(60000, 10);

// simulate media connection being removed -> no more log uploads should happen
meeting.mediaProperties.webrtcMediaConnection = undefined;

clock.tick(60000);
assert.equal(logUploadCounter, 11);

clock.restore();
});

it('should attach the media and return promise when in the lobby if allowMediaInLobby is set', async () => {
meeting.roap.doTurnDiscovery = sinon
.stub()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ describe('plugin-meetings', () => {
meeting.cleanupLocalStreams = sinon.stub().returns(Promise.resolve());
meeting.closeRemoteStreams = sinon.stub().returns(Promise.resolve());
meeting.closePeerConnections = sinon.stub().returns(Promise.resolve());
meeting.stopPeriodicLogUpload = sinon.stub();

meeting.unsetRemoteStreams = sinon.stub();
meeting.unsetPeerConnections = sinon.stub();
Expand All @@ -64,6 +65,7 @@ describe('plugin-meetings', () => {
assert.calledOnce(meeting.cleanupLocalStreams);
assert.calledOnce(meeting.closeRemoteStreams);
assert.calledOnce(meeting.closePeerConnections);
assert.calledOnce(meeting.stopPeriodicLogUpload);

assert.calledOnce(meeting.unsetRemoteStreams);
assert.calledOnce(meeting.unsetPeerConnections);
Expand Down

0 comments on commit bb2dd38

Please sign in to comment.