diff --git a/config.js b/config.js index b4ac9b2be..944988ed9 100644 --- a/config.js +++ b/config.js @@ -510,6 +510,9 @@ var config = { // ], }, + // Logs that should go be passed through the 'log' event if a handler is defined for it + // apiLogLevels: ['warn', 'log', 'error', 'info', 'debug'], + // Information about the jitsi-meet instance we are connecting to, including // the user region as seen by the server. deploymentInfo: { diff --git a/modules/API/API.js b/modules/API/API.js index df1b1facb..0b06fa2d9 100644 --- a/modules/API/API.js +++ b/modules/API/API.js @@ -696,6 +696,21 @@ class API { }); } + /** + * Notify external application (if API is enabled) that the an error has been logged. + * + * @param {string} logLevel - The message log level. + * @param {Array} args - Array of strings composing the log message. + * @returns {void} + */ + notifyLog(logLevel: string, args: Array) { + this._sendEvent({ + name: 'log', + logLevel, + args + }); + } + /** * Notify external application (if API is enabled) that the conference has * been joined. diff --git a/modules/API/external/external_api.js b/modules/API/external/external_api.js index b7ebebd24..339419f51 100644 --- a/modules/API/external/external_api.js +++ b/modules/API/external/external_api.js @@ -68,6 +68,7 @@ const events = { 'feedback-prompt-displayed': 'feedbackPromptDisplayed', 'filmstrip-display-changed': 'filmstripDisplayChanged', 'incoming-message': 'incomingMessage', + 'log': 'log', 'mic-error': 'micError', 'outgoing-message': 'outgoingMessage', 'participant-joined': 'participantJoined', @@ -543,6 +544,13 @@ export default class JitsiMeetExternalAPI extends EventEmitter { * the event and value - the listener. * Currently we support the following * events: + * {@code log} - receives event notifications whenever information has + * been logged and has a log level specified within {@code config.apiLogLevels}. + * The listener will receive object with the following structure: + * {{ + * logLevel: the message log level + * arguments: an array of strings that compose the actual log message + * }} * {@code incomingMessage} - receives event notifications about incoming * messages. The listener will receive object with the following structure: * {{ diff --git a/react/features/base/config/configWhitelist.js b/react/features/base/config/configWhitelist.js index 20c0fc3df..3345457ab 100644 --- a/react/features/base/config/configWhitelist.js +++ b/react/features/base/config/configWhitelist.js @@ -17,6 +17,7 @@ export default [ 'audioLevelsInterval', 'autoRecord', 'autoRecordToken', + 'apiLogLevels', 'avgRtpStatsN', /** diff --git a/react/features/base/logging/ExternalApiLogTransport.js b/react/features/base/logging/ExternalApiLogTransport.js new file mode 100644 index 000000000..500cf2380 --- /dev/null +++ b/react/features/base/logging/ExternalApiLogTransport.js @@ -0,0 +1,22 @@ +// @flow + +declare var APP: Object; + +/** + * Constructs a log transport object for use with external API. + * + * @param {Array} levels - The log levels forwarded to the external API. + + * @returns {Object} - The transport object. + */ +function buildTransport(levels: Array) { + return levels.reduce((logger, level) => { + logger[level] = (...args) => { + APP.API.notifyLog(level, args); + }; + + return logger; + }, {}); +} + +export default buildTransport; diff --git a/react/features/base/logging/middleware.js b/react/features/base/logging/middleware.js index 3ee648e3a..085958b88 100644 --- a/react/features/base/logging/middleware.js +++ b/react/features/base/logging/middleware.js @@ -11,6 +11,7 @@ import JitsiMeetJS, { import { MiddlewareRegistry } from '../redux'; import { isTestModeEnabled } from '../testing'; +import buildExternalApiLogTransport from './ExternalApiLogTransport'; import JitsiMeetInMemoryLogStorage from './JitsiMeetInMemoryLogStorage'; import JitsiMeetLogStorage from './JitsiMeetLogStorage'; import { SET_LOGGING_CONFIG } from './actionTypes'; @@ -141,6 +142,15 @@ function _initLogging({ dispatch, getState }, loggingConfig, isTestingEnabled) { const _logCollector = new Logger.LogCollector(new JitsiMeetLogStorage(getState)); + const { apiLogLevels } = getState()['features/base/config']; + + if (apiLogLevels && Array.isArray(apiLogLevels) && typeof APP === 'object') { + const transport = buildExternalApiLogTransport(apiLogLevels); + + Logger.addGlobalTransport(transport); + JitsiMeetJS.addGlobalLogTransport(transport); + } + Logger.addGlobalTransport(_logCollector); JitsiMeetJS.addGlobalLogTransport(_logCollector); dispatch(setLogCollector(_logCollector));