-
Notifications
You must be signed in to change notification settings - Fork 6.8k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat(rtcstats): Integrate rtcstats (#6945)
* Integrate rtcstats * expcetion handling / clean up * order imports * config fix * remove mock amplitude handler * additional comments * lint fix * address code review * move rtcstats middleware * link to jitsi rtcstats package * address code review * address code review / add ws onclose handler * add display name / bump rtcstats version * resolve import error
- Loading branch information
1 parent
11fd536
commit 29805ed
Showing
11 changed files
with
259 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,111 @@ | ||
import rtcstatsInit from 'rtcstats/rtcstats'; | ||
import traceInit from 'rtcstats/trace-ws'; | ||
|
||
import { | ||
createRTCStatsTraceCloseEvent, | ||
sendAnalytics | ||
} from '../analytics'; | ||
|
||
import logger from './logger'; | ||
|
||
/** | ||
* Filter out RTCPeerConnection that are created by callstats.io. | ||
* | ||
* @param {*} config - Config object sent to the PC c'tor. | ||
* @returns {boolean} | ||
*/ | ||
function connectionFilter(config) { | ||
if (config && config.iceServers[0] && config.iceServers[0].urls) { | ||
for (const iceUrl of config.iceServers[0].urls) { | ||
if (iceUrl.indexOf('taas.callstats.io') >= 0) { | ||
return true; | ||
} | ||
} | ||
} | ||
} | ||
|
||
/** | ||
* Class that controls the rtcstats flow, because it overwrites and proxies global function it should only be | ||
* initialized once. | ||
*/ | ||
class RTCStats { | ||
/** | ||
* Initialize the rtcstats components. First off we initialize the trace, which is a wrapped websocket | ||
* that does the actual communication with the server. Secondly, the rtcstats component is initialized, | ||
* it overwrites GUM and PeerConnection global functions and adds a proxy over them used to capture stats. | ||
* Note, lib-jitsi-meet takes references to these methods before initializing so the init method needs to be | ||
* loaded before it does. | ||
* | ||
* @param {Object} options -. | ||
* @param {string} options.rtcstatsEndpoint - The Amplitude app key required. | ||
* @param {number} options.rtcstatsPollInterval - The getstats poll interval in ms. | ||
* @returns {void} | ||
*/ | ||
init(options) { | ||
this.handleTraceWSClose = this.handleTraceWSClose.bind(this); | ||
this.trace = traceInit(options.rtcstatsEndpoint, this.handleTraceWSClose); | ||
rtcstatsInit(this.trace, options.rtcstatsPollInterval, [ '' ], connectionFilter); | ||
this.initialized = true; | ||
} | ||
|
||
/** | ||
* Check whether or not the RTCStats is initialized. | ||
* | ||
* @returns {boolean} | ||
*/ | ||
isInitialized() { | ||
return this.initialized; | ||
} | ||
|
||
/** | ||
* Send identity data to rtcstats server, this will be reflected in the identity section of the stats dump. | ||
* It can be generally used to send additional metadata that might be relevant such as amplitude user data | ||
* or deployment specific information. | ||
* | ||
* @param {Object} identityData - Metadata object to send as identity. | ||
* @returns {void} | ||
*/ | ||
sendIdentityData(identityData) { | ||
this.trace && this.trace('identity', null, identityData); | ||
} | ||
|
||
/** | ||
* Connect to the rtcstats server instance. Stats (data obtained from getstats) won't be send until the | ||
* connect successfully initializes, however calls to GUM are recorded in an internal buffer even if not | ||
* connected and sent once it is established. | ||
* | ||
* @returns {void} | ||
*/ | ||
connect() { | ||
this.trace && this.trace.connect(); | ||
} | ||
|
||
/** | ||
* Self explanatory; closes the web socked connection. | ||
* Note, at the point of writing this documentation there was no method to reset the function overwrites, | ||
* thus even if the websocket is closed the global function proxies are still active but send no data, | ||
* this shouldn't influence the normal flow of the application. | ||
* | ||
* @returns {void} | ||
*/ | ||
close() { | ||
this.trace && this.trace.close(); | ||
} | ||
|
||
/** | ||
* The way rtcstats is currently designed the ws wouldn't normally be closed by the application logic but rather | ||
* by the page being closed/reloaded. Using this assumption any onclose event is most likely something abnormal | ||
* that happened on the ws. We then track this in order to determine how many rtcstats connection were closed | ||
* prematurely. | ||
* | ||
* @param {Object} closeEvent - Event sent by ws onclose. | ||
* @returns {void} | ||
*/ | ||
handleTraceWSClose(closeEvent) { | ||
logger.info('RTCStats trace ws closed', closeEvent); | ||
|
||
sendAnalytics(createRTCStatsTraceCloseEvent(closeEvent)); | ||
} | ||
} | ||
|
||
export default new RTCStats(); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
import './middleware'; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
// @flow | ||
|
||
import { getLogger } from '../base/logging/functions'; | ||
|
||
export default getLogger('features/rtcstats'); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,79 @@ | ||
// @flow | ||
|
||
import { getAmplitudeIdentity } from '../analytics'; | ||
import { | ||
CONFERENCE_JOINED | ||
} from '../base/conference'; | ||
import { LIB_WILL_INIT } from '../base/lib-jitsi-meet'; | ||
import { getLocalParticipant } from '../base/participants'; | ||
import { MiddlewareRegistry } from '../base/redux'; | ||
|
||
import RTCStats from './RTCStats'; | ||
import logger from './logger'; | ||
|
||
/** | ||
* Middleware which intercepts lib-jitsi-meet initialization and conference join in order init the | ||
* rtcstats-client. | ||
* | ||
* @param {Store} store - The redux store. | ||
* @returns {Function} | ||
*/ | ||
MiddlewareRegistry.register(store => next => action => { | ||
const state = store.getState(); | ||
const config = state['features/base/config']; | ||
const { analytics } = config; | ||
|
||
switch (action.type) { | ||
case LIB_WILL_INIT: { | ||
if (analytics.rtcstatsEndpoint) { | ||
// RTCStats "proxies" WebRTC functions such as GUM and RTCPeerConnection by rewriting the global | ||
// window functions. Because lib-jitsi-meet uses references to those functions that are taken on | ||
// init, we need to add these proxies before it initializes, otherwise lib-jitsi-meet will use the | ||
// original non proxy versions of these functions. | ||
try { | ||
// Default poll interval is 1000ms if not provided in the config. | ||
const pollInterval = analytics.rtcstatsPollInterval || 1000; | ||
|
||
// Initialize but don't connect to the rtcstats server wss, as it will start sending data for all | ||
// media calls made even before the conference started. | ||
RTCStats.init({ | ||
rtcstatsEndpoint: analytics.rtcstatsEndpoint, | ||
rtcstatsPollInterval: pollInterval | ||
}); | ||
} catch (error) { | ||
logger.error('Failed to initialize RTCStats: ', error); | ||
} | ||
} | ||
break; | ||
} | ||
case CONFERENCE_JOINED: { | ||
if (analytics.rtcstatsEndpoint && RTCStats.isInitialized()) { | ||
// Once the conference started connect to the rtcstats server and send data. | ||
try { | ||
RTCStats.connect(); | ||
|
||
const localParticipant = getLocalParticipant(state); | ||
|
||
// The current implementation of rtcstats-server is configured to send data to amplitude, thus | ||
// we add identity specific information so we can corelate on the amplitude side. If amplitude is | ||
// not configured an empty object will be sent. | ||
// The current configuration of the conference is also sent as metadata to rtcstats server. | ||
// This is done in order to facilitate queries based on different conference configurations. | ||
// e.g. Find all RTCPeerConnections that connect to a specific shard or were created in a | ||
// conference with a specific version. | ||
RTCStats.sendIdentityData({ | ||
...getAmplitudeIdentity(), | ||
...config, | ||
displayName: localParticipant?.name | ||
}); | ||
} catch (error) { | ||
// If the connection failed do not impact jitsi-meet just silently fail. | ||
logger.error('RTCStats connect failed with: ', error); | ||
} | ||
} | ||
break; | ||
} | ||
} | ||
|
||
return next(action); | ||
}); |