@@ -20,6 +20,7 @@ export function getInstance(): RNPerfMetrics {
2020
2121type UnsubscribeFn = ( ) => void ;
2222class 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+
178201type 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
240263export type ReactNativeChromeDevToolsEvent = EntrypointLoadingStartedEvent | EntrypointLoadingFinishedEvent |
241- DebuggerReadyEvent | BrowserVisibilityChangeEvent | UnhandledErrorEvent | RemoteDebuggingTerminatedEvent |
264+ DebuggerReadyEvent | BrowserVisibilityChangeEvent | BrowserErrorEvent | RemoteDebuggingTerminatedEvent |
242265 DeveloperResourceLoadingStartedEvent | DeveloperResourceLoadingFinishedEvent ;
243266
244267export type DecoratedReactNativeChromeDevToolsEvent = CommonEventFields & ReactNativeChromeDevToolsEvent ;
0 commit comments