Error resistant data dump from `TimelinePanel` (#9079)

Error resistant data dump from `TimelinePanel` so we still have some data to dump if one of the pieces errors out.

Example error that can happen (as seen in https://github.com/matrix-org/element-web-rageshakes/issues/14197):

```
Uncaught TypeError: Cannot read properties of null (reading 'getEvents')
    at TimelineWindow.getEvents (timeline-window.ts:378:37)
    at TimelinePanel_TimelinePanel.onDumpDebugLogs (TimelinePanel.tsx:434:60)
    at Object.ID_727 (TimelinePanel.tsx:609:22)
    at MatrixDispatcher._invokeCallback (Dispatcher.js:198:1)
    at MatrixDispatcher.dispatch (Dispatcher.js:174:1)
    at sentryWrapped (helpers.js:77:1)
```
t3chguy/dedup-icons-17oct
Eric Eastwood 2022-07-22 11:38:44 -05:00 committed by GitHub
parent e694e87814
commit dfa844a035
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
1 changed files with 50 additions and 28 deletions

View File

@ -384,24 +384,28 @@ class TimelinePanel extends React.Component<IProps, IState> {
* every message change so instead we only log it out when asked. * every message change so instead we only log it out when asked.
*/ */
private onDumpDebugLogs = (): void => { private onDumpDebugLogs = (): void => {
const room = this.props.timelineSet.room; const room = this.props.timelineSet?.room;
// Get a list of the event IDs used in this TimelinePanel. // Get a list of the event IDs used in this TimelinePanel.
// This includes state and hidden events which we don't render // This includes state and hidden events which we don't render
const eventIdList = this.state.events.map((ev) => ev.getId()); const eventIdList = this.state?.events?.map((ev) => ev.getId());
// Get the list of actually rendered events seen in the DOM. // Get the list of actually rendered events seen in the DOM.
// This is useful to know for sure what's being shown on screen. // This is useful to know for sure what's being shown on screen.
// And we can suss out any corrupted React `key` problems. // And we can suss out any corrupted React `key` problems.
let renderedEventIds: string[]; let renderedEventIds: string[];
const messagePanel = this.messagePanel.current; try {
if (messagePanel) { const messagePanel = this.messagePanel.current;
const messagePanelNode = ReactDOM.findDOMNode(messagePanel) as Element; if (messagePanel) {
if (messagePanelNode) { const messagePanelNode = ReactDOM.findDOMNode(messagePanel) as Element;
const actuallyRenderedEvents = messagePanelNode.querySelectorAll('[data-event-id]'); if (messagePanelNode) {
renderedEventIds = [...actuallyRenderedEvents].map((renderedEvent) => { const actuallyRenderedEvents = messagePanelNode.querySelectorAll('[data-event-id]');
return renderedEvent.getAttribute('data-event-id'); renderedEventIds = [...actuallyRenderedEvents].map((renderedEvent) => {
}); return renderedEvent.getAttribute('data-event-id');
});
}
} }
} catch (err) {
logger.error(`onDumpDebugLogs: Failed to get the actual event ID's in the DOM`, err);
} }
// Get the list of events and threads for the room as seen by the // Get the list of events and threads for the room as seen by the
@ -413,26 +417,44 @@ class TimelinePanel extends React.Component<IProps, IState> {
const timelineSets = room.getTimelineSets(); const timelineSets = room.getTimelineSets();
const threadsTimelineSets = room.threadsTimelineSets; const threadsTimelineSets = room.threadsTimelineSets;
// Serialize all of the timelineSets and timelines in each set to their event IDs try {
serializedEventIdsFromTimelineSets = serializeEventIdsFromTimelineSets(timelineSets); // Serialize all of the timelineSets and timelines in each set to their event IDs
serializedEventIdsFromThreadsTimelineSets = serializeEventIdsFromTimelineSets(threadsTimelineSets); serializedEventIdsFromTimelineSets = serializeEventIdsFromTimelineSets(timelineSets);
serializedEventIdsFromThreadsTimelineSets = serializeEventIdsFromTimelineSets(threadsTimelineSets);
} catch (err) {
logger.error(`onDumpDebugLogs: Failed to serialize event IDs from timelinesets`, err);
}
// Serialize all threads in the room from theadId -> event IDs in the thread try {
room.getThreads().forEach((thread) => { // Serialize all threads in the room from theadId -> event IDs in the thread
serializedThreadsMap[thread.id] = { room.getThreads().forEach((thread) => {
events: thread.events.map(ev => ev.getId()), serializedThreadsMap[thread.id] = {
numTimelines: thread.timelineSet.getTimelines().length, events: thread.events.map(ev => ev.getId()),
liveTimeline: thread.timelineSet.getLiveTimeline().getEvents().length, numTimelines: thread.timelineSet.getTimelines().length,
prevTimeline: thread.timelineSet.getLiveTimeline().getNeighbouringTimeline(Direction.Backward) liveTimeline: thread.timelineSet.getLiveTimeline().getEvents().length,
?.getEvents().length, prevTimeline: thread.timelineSet.getLiveTimeline().getNeighbouringTimeline(Direction.Backward)
nextTimeline: thread.timelineSet.getLiveTimeline().getNeighbouringTimeline(Direction.Forward) ?.getEvents().length,
?.getEvents().length, nextTimeline: thread.timelineSet.getLiveTimeline().getNeighbouringTimeline(Direction.Forward)
}; ?.getEvents().length,
}); };
});
} catch (err) {
logger.error(`onDumpDebugLogs: Failed to serialize event IDs from the threads`, err);
}
} }
const timelineWindowEventIds = this.timelineWindow.getEvents().map(ev => ev.getId()); let timelineWindowEventIds: string[];
const pendingEvents = this.props.timelineSet.getPendingEvents().map(ev => ev.getId()); try {
timelineWindowEventIds = this.timelineWindow.getEvents().map(ev => ev.getId());
} catch (err) {
logger.error(`onDumpDebugLogs: Failed to get event IDs from the timelineWindow`, err);
}
let pendingEventIds: string[];
try {
pendingEventIds = this.props.timelineSet.getPendingEvents().map(ev => ev.getId());
} catch (err) {
logger.error(`onDumpDebugLogs: Failed to get pending event IDs`, err);
}
logger.debug( logger.debug(
`TimelinePanel(${this.context.timelineRenderingType}): Debugging info for ${room?.roomId}\n` + `TimelinePanel(${this.context.timelineRenderingType}): Debugging info for ${room?.roomId}\n` +
@ -444,7 +466,7 @@ class TimelinePanel extends React.Component<IProps, IState> {
`${JSON.stringify(serializedEventIdsFromThreadsTimelineSets)}\n` + `${JSON.stringify(serializedEventIdsFromThreadsTimelineSets)}\n` +
`\tserializedThreadsMap=${JSON.stringify(serializedThreadsMap)}\n` + `\tserializedThreadsMap=${JSON.stringify(serializedThreadsMap)}\n` +
`\ttimelineWindowEventIds(${timelineWindowEventIds.length})=${JSON.stringify(timelineWindowEventIds)}\n` + `\ttimelineWindowEventIds(${timelineWindowEventIds.length})=${JSON.stringify(timelineWindowEventIds)}\n` +
`\tpendingEvents(${pendingEvents.length})=${JSON.stringify(pendingEvents)}`, `\tpendingEventIds(${pendingEventIds.length})=${JSON.stringify(pendingEventIds)}`,
); );
}; };