Skip to content

Commit 06a25f7

Browse files
authored
Capture console errors; not just unhandled ones (#80)
1 parent fa5df76 commit 06a25f7

File tree

1 file changed

+39
-16
lines changed

1 file changed

+39
-16
lines changed

front_end/core/host/RNPerfMetrics.ts

Lines changed: 39 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ export function getInstance(): RNPerfMetrics {
2020

2121
type UnsubscribeFn = () => void;
2222
class RNPerfMetrics {
23+
readonly #CONSOLE_ERROR_METHOD = 'error';
2324
#listeners: Set<RNReliabilityEventListener> = new Set();
2425
#launchId: string|null = null;
2526

@@ -71,32 +72,44 @@ class RNPerfMetrics {
7172

7273
registerGlobalErrorReporting(): void {
7374
window.addEventListener('error', event => {
75+
const [message, error] = maybeWrapError(`[RNPerfMetrics] uncaught error: ${event.message}`, event.error);
7476
this.sendEvent({
75-
eventName: 'Browser.UnhandledError',
77+
eventName: 'Browser.Error',
7678
params: {
7779
type: 'error',
78-
message: event.message,
79-
error: event.error instanceof Error ? event.error : null,
80+
message,
81+
error,
8082
}
8183
});
8284
}, {passive: true});
8385

8486
window.addEventListener('unhandledrejection', event => {
85-
let message: string;
86-
try {
87-
message = String(event.reason);
88-
} catch {
89-
message = '[Promise was rejected without a serialisable reason]';
90-
}
87+
const [message, error] = maybeWrapError(`[RNPerfMetrics] unhandled promise rejection`, event.reason);
9188
this.sendEvent({
92-
eventName: 'Browser.UnhandledError',
89+
eventName: 'Browser.Error',
9390
params: {
9491
type: 'rejectedPromise',
9592
message,
96-
error: event.reason instanceof Error ? event.reason : null,
93+
error,
9794
}
9895
});
9996
}, {passive: true});
97+
98+
// Indirection for `console` ensures minifier won't strip this out.
99+
const cons = globalThis.console;
100+
const originalConsoleError = cons[this.#CONSOLE_ERROR_METHOD];
101+
cons[this.#CONSOLE_ERROR_METHOD] = (...args: unknown[]) => {
102+
try {
103+
const maybeError = args[0];
104+
const [message, error] = maybeWrapError('[RNPerfMetrics] console.error', maybeError);
105+
this.sendEvent({eventName: 'Browser.Error', params: {message, error, type: 'consoleError'}});
106+
} catch (e) {
107+
const [message, error] = maybeWrapError('[RNPerfMetrics] Error handling console.error', e);
108+
this.sendEvent({eventName: 'Browser.Error', params: {message, error, type: 'consoleError'}});
109+
} finally {
110+
originalConsoleError.apply(cons, args);
111+
}
112+
}
100113
}
101114

102115
setLaunchId(launchId: string|null): void {
@@ -175,6 +188,16 @@ function maybeTruncateDeveloperResourceUrl(parsedURL: ParsedURL): string {
175188
return parsedURL.isHttpOrHttps() ? url : `${url.slice(0, 100)} …(omitted ${url.length - 100} characters)`;
176189
}
177190

191+
function maybeWrapError(baseMessage: string, error: unknown): [string, Error] {
192+
if (error instanceof Error) {
193+
const message = `${baseMessage}: ${error.message}`
194+
return [message, error];
195+
}
196+
197+
const message = `${baseMessage}: ${String(error)}`;
198+
return [message, new Error(message, {cause: error})];
199+
}
200+
178201
type CommonEventFields = Readonly<{
179202
timestamp: DOMHighResTimeStamp,
180203
launchId: string | void | null,
@@ -203,12 +226,12 @@ export type BrowserVisibilityChangeEvent = Readonly<{
203226
}>,
204227
}>;
205228

206-
export type UnhandledErrorEvent = Readonly<{
207-
eventName: 'Browser.UnhandledError',
229+
export type BrowserErrorEvent = Readonly<{
230+
eventName: 'Browser.Error',
208231
params: Readonly<{
209-
type: 'error' | 'rejectedPromise',
210232
message: string,
211-
error: Error | null | undefined,
233+
error: Error,
234+
type: 'error' | 'rejectedPromise' | 'consoleError',
212235
}>,
213236
}>;
214237

@@ -238,7 +261,7 @@ export type DeveloperResourceLoadingFinishedEvent = Readonly<{
238261
}>;
239262

240263
export type ReactNativeChromeDevToolsEvent = EntrypointLoadingStartedEvent|EntrypointLoadingFinishedEvent|
241-
DebuggerReadyEvent|BrowserVisibilityChangeEvent|UnhandledErrorEvent|RemoteDebuggingTerminatedEvent|
264+
DebuggerReadyEvent|BrowserVisibilityChangeEvent|BrowserErrorEvent|RemoteDebuggingTerminatedEvent|
242265
DeveloperResourceLoadingStartedEvent|DeveloperResourceLoadingFinishedEvent;
243266

244267
export type DecoratedReactNativeChromeDevToolsEvent = CommonEventFields&ReactNativeChromeDevToolsEvent;

0 commit comments

Comments
 (0)