forked from zulip/zulip-mobile
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
webview: Better code for logging inbound events to Sentry.
See 523b307 (reverted in a recent commit) for an earlier attempt at this. It was done quickly to debug a problem with minimal delay; this is the better-organized version of it, informed by Greg's suggestions [1]. [1]: zulip#4180 (comment)
- Loading branch information
1 parent
b3df533
commit 64e1957
Showing
3 changed files
with
338 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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,154 @@ | ||
/* @flow strict-local */ | ||
import type { | ||
WebViewUpdateEvent as WebViewInboundEvent, | ||
WebViewUpdateEventContent as WebViewInboundEventContent, | ||
WebViewUpdateEventFetching as WebViewInboundEventFetching, | ||
WebViewUpdateEventTyping as WebViewInboundEventTyping, | ||
WebViewUpdateEventReady as WebViewInboundEventReady, | ||
WebViewUpdateEventMessagesRead as WebViewInboundEventMessagesRead, | ||
} from '../webViewHandleUpdates'; | ||
import type { JSONable } from '../../utils/jsonable'; | ||
import sendMessage from './sendMessage'; | ||
import { ensureUnreachable } from '../../types'; | ||
|
||
// TODO: Make exact (see note in jsonable.js). | ||
type Scrub<E: WebViewInboundEvent> = { [key: $Keys<E>]: JSONable }; | ||
|
||
type ScrubbedInboundEvent = | ||
| Scrub<WebViewInboundEventContent> | ||
| Scrub<WebViewInboundEventFetching> | ||
| Scrub<WebViewInboundEventTyping> | ||
| Scrub<WebViewInboundEventReady> | ||
| Scrub<WebViewInboundEventMessagesRead>; | ||
|
||
type ScrubbedInboundEventItem = {| | ||
timestamp: number, | ||
type: 'inbound', | ||
scrubbedEvent: ScrubbedInboundEvent, | ||
|}; | ||
|
||
/** | ||
* Grab interesting but not privacy-sensitive message-loading state. | ||
* | ||
* Takes the "content" from an inbound WebView event, an HTML string, | ||
* and returns the opening div#message-loading tag, so we know whether | ||
* it's visible. | ||
*/ | ||
const placeholdersDivTagFromContent = (content: string): string | null => { | ||
const match = new RegExp('<div id="message-loading" class="(?:hidden)?">').exec(content); | ||
return match !== null ? match[0] : null; | ||
}; | ||
|
||
export default class InboundEventLogger { | ||
_captureStartTime: number | void; | ||
_captureEndTime: number | void; | ||
_isCapturing: boolean; | ||
_capturedInboundEventItems: ScrubbedInboundEventItem[]; | ||
|
||
/** | ||
* Minimally transform an inbound event to remove sensitive data. | ||
*/ | ||
static scrubInboundEvent(event: WebViewInboundEvent): ScrubbedInboundEvent { | ||
// Don't spread the event (e.g., `...event`); instead, rebuild it. | ||
// That way, a new property, when added, won't automatically be | ||
// sent to Sentry un-scrubbed. | ||
switch (event.type) { | ||
case 'content': { | ||
return { | ||
type: event.type, | ||
scrollMessageId: event.scrollMessageId, | ||
auth: 'redacted', | ||
content: placeholdersDivTagFromContent(event.content), | ||
updateStrategy: event.updateStrategy, | ||
}; | ||
} | ||
case 'fetching': { | ||
return { | ||
type: event.type, | ||
showMessagePlaceholders: event.showMessagePlaceholders, | ||
fetchingOlder: event.fetchingOlder, | ||
fetchingNewer: event.fetchingNewer, | ||
}; | ||
} | ||
case 'typing': { | ||
return { | ||
type: event.type, | ||
content: placeholdersDivTagFromContent(event.content), | ||
}; | ||
} | ||
case 'ready': { | ||
return { | ||
type: event.type, | ||
}; | ||
} | ||
case 'read': { | ||
return { | ||
type: event.type, | ||
messageIds: event.messageIds, | ||
}; | ||
} | ||
default: { | ||
ensureUnreachable(event); | ||
return { | ||
type: event.type, | ||
}; | ||
} | ||
} | ||
} | ||
|
||
constructor() { | ||
this._isCapturing = false; | ||
this._capturedInboundEventItems = []; | ||
} | ||
|
||
startCapturing() { | ||
if (this._isCapturing) { | ||
throw new Error('InboundEventLogger: Tried to call startCapturing while already capturing.'); | ||
} else if (this._capturedInboundEventItems.length > 0) { | ||
throw new Error('InboundEventLogger: Tried to call startCapturing before resetting.'); | ||
} | ||
this._captureEndTime = undefined; | ||
this._isCapturing = true; | ||
this._captureStartTime = Date.now(); | ||
} | ||
|
||
stopCapturing() { | ||
if (!this._isCapturing) { | ||
throw new Error('InboundEventLogger: Tried to call stopCapturing while not capturing.'); | ||
} | ||
this._isCapturing = false; | ||
this._captureEndTime = Date.now(); | ||
} | ||
|
||
sendAndReset() { | ||
if (this._isCapturing) { | ||
throw new Error('InboundEventLogger: Tried to send captured events while still capturing.'); | ||
} | ||
sendMessage({ | ||
type: 'warn', | ||
details: { | ||
startTime: this._captureStartTime ?? null, | ||
endTime: this._captureEndTime ?? null, | ||
inboundEventItems: this._capturedInboundEventItems, | ||
}, | ||
}); | ||
this._captureStartTime = undefined; | ||
this._captureEndTime = undefined; | ||
this._capturedInboundEventItems = []; | ||
} | ||
|
||
maybeCaptureInboundEvent(event: WebViewInboundEvent) { | ||
if (this._isCapturing) { | ||
this._captureInboundEvent(event); | ||
} | ||
} | ||
|
||
_captureInboundEvent(event: WebViewInboundEvent) { | ||
const item = { | ||
type: 'inbound', | ||
timestamp: Date.now(), | ||
scrubbedEvent: InboundEventLogger.scrubInboundEvent(event), | ||
}; | ||
this._capturedInboundEventItems.push(item); | ||
} | ||
} |
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