diff --git a/bigbluebutton-html5/imports/api/connection-status/server/methods/addConnectionStatus.js b/bigbluebutton-html5/imports/api/connection-status/server/methods/addConnectionStatus.js index 9e58b49f7f3d0256a8c748bc7d6bf7bc5e9a71e9..1774f594b63082d16a29c245e1c6b5dd67c6ab12 100644 --- a/bigbluebutton-html5/imports/api/connection-status/server/methods/addConnectionStatus.js +++ b/bigbluebutton-html5/imports/api/connection-status/server/methods/addConnectionStatus.js @@ -1,14 +1,53 @@ import { check } from 'meteor/check'; +import Logger from '/imports/startup/server/logger'; import updateConnectionStatus from '/imports/api/connection-status/server/modifiers/updateConnectionStatus'; import { extractCredentials } from '/imports/api/common/server/helpers'; -export default function addConnectionStatus(level) { - check(level, String); +const STATS = Meteor.settings.public.stats; + +const logConnectionStatus = (meetingId, userId, status, type, value) => { + switch (status) { + case 'normal': + Logger.info(`Connection status updated: meetingId=${meetingId} userId=${userId} type=${type}`); + break; + case 'warning': + // Skip + break; + case 'danger': + case 'critical': + switch (type) { + case 'audio': + const { + jitter, + loss, + } = value; + Logger.info(`Connection status updated: meetingId=${meetingId} userId=${userId} jitter=${jitter} loss=${loss}`); + break; + case 'socket': + const { rtt } = value; + Logger.info(`Connection status updated: meetingId=${meetingId} userId=${userId} rtt=${rtt}`); + break; + default: + } + break; + default: + } +}; + +export default function addConnectionStatus(status, type, value) { + check(status, String); + check(type, String); + check(value, Object); const { meetingId, requesterUserId } = extractCredentials(this.userId); check(meetingId, String); check(requesterUserId, String); - updateConnectionStatus(meetingId, requesterUserId, level); + if (STATS.log) logConnectionStatus(meetingId, requesterUserId, status, type, value); + + // Avoid storing recoveries + if (status !== 'normal') { + updateConnectionStatus(meetingId, requesterUserId, status); + } } diff --git a/bigbluebutton-html5/imports/ui/components/connection-status/service.js b/bigbluebutton-html5/imports/ui/components/connection-status/service.js index 4345a61adfb29518b88b727d475260eeff394c7d..f3cec763a72b7c99dabd6477142a5447b7831345 100644 --- a/bigbluebutton-html5/imports/ui/components/connection-status/service.js +++ b/bigbluebutton-html5/imports/ui/components/connection-status/service.js @@ -4,7 +4,6 @@ import Users from '/imports/api/users'; import UsersPersistentData from '/imports/api/users-persistent-data'; import Auth from '/imports/ui/services/auth'; import Settings from '/imports/ui/services/settings'; -import Logger from '/imports/startup/client/logger'; import _ from 'lodash'; import { Session } from 'meteor/session'; import { notify } from '/imports/ui/services/notification'; @@ -12,12 +11,7 @@ import { makeCall } from '/imports/ui/services/api'; const STATS = Meteor.settings.public.stats; const NOTIFICATION = STATS.notification; -const STATS_LENGTH = STATS.length; const STATS_INTERVAL = STATS.interval; -const STATS_LOG = STATS.log; -const RTT_INTERVAL = STATS_LENGTH * STATS_INTERVAL; -// Set a bottom threshold to avoid log flooding -const RTT_LOG_THRESHOLD = STATS.rtt[STATS.level.indexOf('danger')]; const ROLE_MODERATOR = Meteor.settings.public.user.role_moderator; const intlMessages = defineMessages({ @@ -50,17 +44,17 @@ const getStats = () => { return STATS.level[stats]; }; -const setStats = (level = -1) => { +const setStats = (level = -1, type = 'recovery', value = {}) => { if (stats !== level) { stats = level; statsDep.changed(); - addConnectionStatus(level); + addConnectionStatus(level, type, value); } }; -const handleStats = (level) => { +const handleStats = (level, type, value) => { if (level > stats) { - setStats(level); + setStats(level, type, value); } }; @@ -71,9 +65,9 @@ const handleAudioStatsEvent = (event) => { let active = false; // From higher to lower for (let i = STATS.level.length - 1; i >= 0; i--) { - if (loss > STATS.loss[i] || jitter > STATS.jitter[i]) { + if (loss >= STATS.loss[i] || jitter >= STATS.jitter[i]) { active = true; - handleStats(i); + handleStats(i, 'audio', { loss, jitter }); break; } } @@ -89,9 +83,9 @@ const handleSocketStatsEvent = (event) => { let active = false; // From higher to lower for (let i = STATS.level.length - 1; i >= 0; i--) { - if (rtt > STATS.rtt[i]) { + if (rtt >= STATS.rtt[i]) { active = true; - handleStats(i); + handleStats(i, 'socket', { rtt }); break; } } @@ -108,26 +102,17 @@ const startStatsTimeout = () => { }, STATS.timeout); }; -const addConnectionStatus = (level) => { - if (level !== -1) makeCall('addConnectionStatus', STATS.level[level]); -}; +const addConnectionStatus = (level, type, value) => { + const status = level !== -1 ? STATS.level[level] : 'normal'; + + makeCall('addConnectionStatus', status, type, value); +} const fetchRoundTripTime = () => { const t0 = Date.now(); makeCall('voidConnection').then(() => { const tf = Date.now(); const rtt = tf - t0; - - if (STATS_LOG && rtt > RTT_LOG_THRESHOLD) { - Logger.info( - { - logCode: 'rtt', - extraInfo: { rtt }, - }, - 'Calculated round-trip time in milliseconds', - ); - } - const event = new CustomEvent('socketstats', { detail: { rtt } }); window.dispatchEvent(event); }); @@ -263,7 +248,7 @@ const startRoundTripTime = () => { stopRoundTripTime(); - roundTripTimeInterval = setInterval(fetchRoundTripTime, RTT_INTERVAL); + roundTripTimeInterval = setInterval(fetchRoundTripTime, STATS_INTERVAL); }; const stopRoundTripTime = () => { @@ -323,11 +308,9 @@ const notification = (level, intl) => { }; export default { - addConnectionStatus, getConnectionStatus, getStats, getHelp, - getLevel, isEnabled, notification, startRoundTripTime, diff --git a/bigbluebutton-html5/imports/utils/stats.js b/bigbluebutton-html5/imports/utils/stats.js index c4f81d02c867ff69918a1cf8d992ff77103169fb..69733cacb83284e11d487c0d4f1d29101dd3df3d 100644 --- a/bigbluebutton-html5/imports/utils/stats.js +++ b/bigbluebutton-html5/imports/utils/stats.js @@ -2,9 +2,9 @@ import logger from '/imports/startup/client/logger'; const STATS = Meteor.settings.public.stats; -const STATS_LENGTH = STATS.length; -const STATS_INTERVAL = STATS.interval; -const STATS_LOG = STATS.log; +// Probes done in an interval +const PROBES = 5; +const INTERVAL = STATS.interval / PROBES; const stop = callback => { logger.debug( @@ -47,7 +47,7 @@ const isActive = conn => { const collect = (conn, callback) => { let stats = []; - const monitor = (conn, stats, iteration) => { + const monitor = (conn, stats) => { if (!isActive(conn)) return stop(callback); conn.getStats().then(results => { @@ -77,13 +77,13 @@ const collect = (conn, callback) => { } stats.push(buildData(inboundRTP || remoteInboundRTP)); - while (stats.length > STATS_LENGTH) stats.shift(); + while (stats.length > PROBES) stats.shift(); const interval = calculateInterval(stats); - callback(buildResult(interval, iteration)); + callback(buildResult(interval)); } - setTimeout(monitor, STATS_INTERVAL, conn, stats, iteration + 1); + setTimeout(monitor, INTERVAL, conn, stats); }).catch(error => { logger.debug( { @@ -110,10 +110,9 @@ const buildData = inboundRTP => { }; }; -const buildResult = (interval, iteration) => { +const buildResult = (interval) => { const rate = calculateRate(interval.packets); return { - iteration: iteration, packets: { received: interval.packets.received, lost: interval.packets.lost @@ -130,7 +129,6 @@ const buildResult = (interval, iteration) => { const clearResult = () => { return { - iteration: 0, packets: { received: 0, lost: 0 @@ -178,30 +176,6 @@ const calculateMOS = (rate) => { return 1 + (0.035) * rate + (0.000007) * rate * (rate - 60) * (100 - rate); }; -const logResult = (id, result) => { - if (!STATS_LOG) return null; - - const { - iteration, - loss, - jitter - } = result; - // Avoiding messages flood - if (!iteration || iteration % STATS_LENGTH !== 0) return null; - - const duration = STATS_LENGTH * STATS_INTERVAL / 1000; - logger.debug( - { - logCode: 'stats_monitor_result', - extraInfo: { - id, - result - } - }, - `Stats result for the last ${duration} seconds: loss: ${loss}, jitter: ${jitter}.` - ); -}; - const monitorAudioConnection = conn => { if (!conn) return; @@ -211,7 +185,6 @@ const monitorAudioConnection = conn => { ); collect(conn, (result) => { - logResult('audio', result); const event = new CustomEvent('audiostats', { detail: result }); window.dispatchEvent(event); }); diff --git a/bigbluebutton-html5/private/config/settings.yml b/bigbluebutton-html5/private/config/settings.yml index 84ed767981d386885a3cf678040236ad787d9734..1957a4dccd175150516f20e49330c21b7436116b 100755 --- a/bigbluebutton-html5/private/config/settings.yml +++ b/bigbluebutton-html5/private/config/settings.yml @@ -433,8 +433,7 @@ public: sdpSemantics: 'unified-plan' stats: enabled: true - interval: 2000 - length: 5 + interval: 10000 timeout: 30000 log: false notification: