-
Notifications
You must be signed in to change notification settings - Fork 7.5k
feat(logging): start collecting logs at appMount #16338
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Open
andrei-gavrilescu
wants to merge
1
commit into
jitsi:master
Choose a base branch
from
andrei-gavrilescu:feat/earlier-log-collection
base: master
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
+127
−47
Open
Changes from all commits
Commits
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or 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 | ||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -71,11 +71,13 @@ | |||||||||||||||
| * @returns {Object} The new state that is the result of the reduction of the | ||||||||||||||||
| * specified {@code action}. | ||||||||||||||||
| */ | ||||||||||||||||
| function _appWillMount({ getState }: IStore, next: Function, action: AnyAction) { | ||||||||||||||||
| const { config } = getState()['features/base/logging']; | ||||||||||||||||
| function _appWillMount(store: IStore, next: Function, action: AnyAction) { | ||||||||||||||||
| const { config } = store.getState()['features/base/logging']; | ||||||||||||||||
|
|
||||||||||||||||
| _setLogLevels(Logger, config); | ||||||||||||||||
|
|
||||||||||||||||
| _initEarlyLogging(store); | ||||||||||||||||
|
|
||||||||||||||||
| // FIXME Until the logic of conference.js is rewritten into the React | ||||||||||||||||
| // app we, JitsiMeetJS.init is to not be used for the React app. | ||||||||||||||||
| // Consequently, LIB_WILL_INIT will not be dispatched. In the meantime, do | ||||||||||||||||
|
|
@@ -85,6 +87,32 @@ | |||||||||||||||
| return next(action); | ||||||||||||||||
| } | ||||||||||||||||
|
|
||||||||||||||||
| /** | ||||||||||||||||
| * Create the LogCollector and register it as the global log transport. It | ||||||||||||||||
| * is done early to capture as much logs as possible. Captured logs will be | ||||||||||||||||
| * cached, before the JitsiMeetLogStorage gets ready (RTCStats trace is | ||||||||||||||||
| * available). | ||||||||||||||||
| * | ||||||||||||||||
| * @param {Store} store - The Redux store in which context the early logging is to be | ||||||||||||||||
| * initialized. | ||||||||||||||||
| * @private | ||||||||||||||||
| * @returns {void} | ||||||||||||||||
| */ | ||||||||||||||||
| function _initEarlyLogging({ dispatch, getState }: IStore) { | ||||||||||||||||
| const { logCollector } = getState()['features/base/logging']; | ||||||||||||||||
|
|
||||||||||||||||
| if (!logCollector) { | ||||||||||||||||
| // Create the LogCollector with minimal configuration | ||||||||||||||||
| // Use default values since config is not available yet | ||||||||||||||||
| const _logCollector = new Logger.LogCollector(new JitsiMeetLogStorage(getState), {}); | ||||||||||||||||
|
|
||||||||||||||||
| Logger.addGlobalTransport(_logCollector as any); | ||||||||||||||||
| _logCollector.start(); | ||||||||||||||||
|
|
||||||||||||||||
| dispatch(setLogCollector(_logCollector)); | ||||||||||||||||
| } | ||||||||||||||||
| } | ||||||||||||||||
|
|
||||||||||||||||
| /** | ||||||||||||||||
| * Starts the log collector, after {@link CONFERENCE_JOINED} action is reduced. | ||||||||||||||||
| * | ||||||||||||||||
|
|
@@ -143,58 +171,109 @@ | |||||||||||||||
| */ | ||||||||||||||||
| function _initLogging({ dispatch, getState }: IStore, | ||||||||||||||||
| loggingConfig: any, isTestingEnabled: boolean) { | ||||||||||||||||
| const { logCollector } = getState()['features/base/logging']; | ||||||||||||||||
| const state = getState(); | ||||||||||||||||
| const { analytics: { rtcstatsLogFlushSizeBytes } = {}, apiLogLevels } = state['features/base/config']; | ||||||||||||||||
| const { logCollector } = state['features/base/logging']; | ||||||||||||||||
| const { disableLogCollector } = loggingConfig; | ||||||||||||||||
|
|
||||||||||||||||
| // Create the LogCollector and register it as the global log transport. It | ||||||||||||||||
| // is done early to capture as much logs as possible. Captured logs will be | ||||||||||||||||
| // cached, before the JitsiMeetLogStorage gets ready (RTCStats trace is | ||||||||||||||||
| // available). | ||||||||||||||||
| if (!logCollector && !loggingConfig.disableLogCollector) { | ||||||||||||||||
| const { apiLogLevels, analytics: { rtcstatsLogFlushSizeBytes } = {} } = getState()['features/base/config']; | ||||||||||||||||
|
|
||||||||||||||||
| // The smaller the flush size the smaller the chance of losing logs, but | ||||||||||||||||
| // the more often the logs will be sent to the server, by default the LogCollector | ||||||||||||||||
| // will set once the logs reach 10KB or 30 seconds have passed since the last flush, | ||||||||||||||||
| // this means if something happens between that interval and the logs don't get flushed | ||||||||||||||||
| // they will be lost, for instance the meeting tab is closed, the browser crashes, | ||||||||||||||||
| // an uncaught exception happens, etc. | ||||||||||||||||
| // If undefined is passed the default values will be used, | ||||||||||||||||
| const _logCollector = new Logger.LogCollector(new JitsiMeetLogStorage(getState), { | ||||||||||||||||
| maxEntryLength: rtcstatsLogFlushSizeBytes | ||||||||||||||||
| }); | ||||||||||||||||
|
|
||||||||||||||||
| if (apiLogLevels && Array.isArray(apiLogLevels) && typeof APP === 'object') { | ||||||||||||||||
| const transport = buildExternalApiLogTransport(apiLogLevels); | ||||||||||||||||
|
|
||||||||||||||||
| Logger.addGlobalTransport(transport); | ||||||||||||||||
| JitsiMeetJS.addGlobalLogTransport(transport); | ||||||||||||||||
| } | ||||||||||||||||
|
|
||||||||||||||||
| Logger.addGlobalTransport(_logCollector); | ||||||||||||||||
| if (disableLogCollector) { | ||||||||||||||||
| _disableLogCollector(logCollector, dispatch); | ||||||||||||||||
|
|
||||||||||||||||
| _logCollector.start(); | ||||||||||||||||
| return; | ||||||||||||||||
| } | ||||||||||||||||
|
|
||||||||||||||||
| dispatch(setLogCollector(_logCollector)); | ||||||||||||||||
| _setupExternalApiTransport(apiLogLevels); | ||||||||||||||||
| _setupDebugLogging(isTestingEnabled); | ||||||||||||||||
| _configureLogCollector(logCollector, rtcstatsLogFlushSizeBytes); | ||||||||||||||||
| } | ||||||||||||||||
|
|
||||||||||||||||
| // The JitsiMeetInMemoryLogStorage can not be accessed on mobile through | ||||||||||||||||
| // the 'executeScript' method like it's done in torture tests for WEB. | ||||||||||||||||
| if (isTestingEnabled && typeof APP === 'object') { | ||||||||||||||||
| APP.debugLogs = new JitsiMeetInMemoryLogStorage(); | ||||||||||||||||
| const debugLogCollector = new Logger.LogCollector( | ||||||||||||||||
| APP.debugLogs, { storeInterval: 1000 }); | ||||||||||||||||
|
|
||||||||||||||||
| Logger.addGlobalTransport(debugLogCollector); | ||||||||||||||||
| JitsiMeetJS.addGlobalLogTransport(debugLogCollector); | ||||||||||||||||
| debugLogCollector.start(); | ||||||||||||||||
| } | ||||||||||||||||
| } else if (logCollector && loggingConfig.disableLogCollector) { | ||||||||||||||||
| Logger.removeGlobalTransport(logCollector); | ||||||||||||||||
| JitsiMeetJS.removeGlobalLogTransport(logCollector); | ||||||||||||||||
| logCollector.stop(); | ||||||||||||||||
| dispatch(setLogCollector(undefined)); | ||||||||||||||||
| /** | ||||||||||||||||
| * Sets up external API log transport if configured. | ||||||||||||||||
| * | ||||||||||||||||
| * @param {Array} apiLogLevels - The API log levels configuration. | ||||||||||||||||
| * @private | ||||||||||||||||
| * @returns {void} | ||||||||||||||||
| */ | ||||||||||||||||
| function _setupExternalApiTransport(apiLogLevels: any) { | ||||||||||||||||
| if (apiLogLevels && Array.isArray(apiLogLevels) && typeof APP === 'object') { | ||||||||||||||||
| const transport = buildExternalApiLogTransport(apiLogLevels); | ||||||||||||||||
|
|
||||||||||||||||
| Logger.addGlobalTransport(transport); | ||||||||||||||||
| JitsiMeetJS.addGlobalLogTransport(transport); | ||||||||||||||||
| } | ||||||||||||||||
| } | ||||||||||||||||
|
|
||||||||||||||||
| /** | ||||||||||||||||
| * Sets up debug logging for testing mode. | ||||||||||||||||
| * | ||||||||||||||||
| * @param {boolean} isTestingEnabled - Whether testing mode is enabled. | ||||||||||||||||
| * @private | ||||||||||||||||
| * @returns {void} | ||||||||||||||||
| */ | ||||||||||||||||
| function _setupDebugLogging(isTestingEnabled: boolean) { | ||||||||||||||||
| // The JitsiMeetInMemoryLogStorage cannot be accessed on mobile through | ||||||||||||||||
| // the 'executeScript' method like it's done in torture tests for WEB. | ||||||||||||||||
| if (!isTestingEnabled || typeof APP !== 'object') { | ||||||||||||||||
| return; | ||||||||||||||||
| } | ||||||||||||||||
|
|
||||||||||||||||
| APP.debugLogs = new JitsiMeetInMemoryLogStorage(); | ||||||||||||||||
| const debugLogCollector = new Logger.LogCollector( | ||||||||||||||||
| APP.debugLogs, | ||||||||||||||||
| { storeInterval: 1000 } | ||||||||||||||||
| ); | ||||||||||||||||
|
|
||||||||||||||||
| Logger.addGlobalTransport(debugLogCollector); | ||||||||||||||||
| JitsiMeetJS.addGlobalLogTransport(debugLogCollector); | ||||||||||||||||
| debugLogCollector.start(); | ||||||||||||||||
| } | ||||||||||||||||
|
|
||||||||||||||||
| /** | ||||||||||||||||
| * Configures the existing log collector with flush size settings. | ||||||||||||||||
| * | ||||||||||||||||
| * @param {Object} logCollector - The log collector instance. | ||||||||||||||||
| * @param {number|undefined} rtcstatsLogFlushSizeBytes - The flush size in bytes. | ||||||||||||||||
| * @private | ||||||||||||||||
| * @returns {void} | ||||||||||||||||
| */ | ||||||||||||||||
| function _configureLogCollector(logCollector: any, rtcstatsLogFlushSizeBytes: number | undefined) { | ||||||||||||||||
|
|
||||||||||||||||
| // Log collector should be available at this point, as we initialize it early at appMount in | ||||||||||||||||
| // order for it to capture and cache as many logs as possible with it's default behavior. | ||||||||||||||||
| // We then update it's settings here after the config is in it's final form. | ||||||||||||||||
| // Data will only be sent to the server once the RTCStats trace is available, if it's available. | ||||||||||||||||
| if (!logCollector) { | ||||||||||||||||
| return; | ||||||||||||||||
| } | ||||||||||||||||
|
|
||||||||||||||||
| // The smaller the flush size, the smaller the chance of losing logs, but | ||||||||||||||||
| // the more often the logs will be sent to the server. By default, the LogCollector | ||||||||||||||||
| // will flush once the logs reach 10KB or 30 seconds have passed since the last flush. | ||||||||||||||||
| // This means if something happens between that interval and the logs don't get flushed, | ||||||||||||||||
| // they will be lost (e.g., meeting tab is closed, browser crashes, uncaught exception). | ||||||||||||||||
| // If undefined is passed, the default values will be used. | ||||||||||||||||
| logCollector.maxEntryLength = rtcstatsLogFlushSizeBytes; | ||||||||||||||||
|
||||||||||||||||
| logCollector.maxEntryLength = rtcstatsLogFlushSizeBytes; | |
| if (typeof logCollector.setMaxEntryLength === 'function') { | |
| logCollector.setMaxEntryLength(rtcstatsLogFlushSizeBytes); | |
| } else { | |
| // Fallback to direct assignment if setter is not available. | |
| logCollector.maxEntryLength = rtcstatsLogFlushSizeBytes; | |
| } |
This file contains hidden or 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
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The condition logic has been inverted. The original code checked
if (isTestingEnabled && typeof APP === 'object')but now it returns early when either condition is false, which means the debug logging setup will never execute.