@@ -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
@@ -70,33 +71,33 @@ class RNPerfMetrics {
7071 }
7172
7273 registerGlobalErrorReporting ( ) : void {
73- window . addEventListener ( 'error' , event => {
74- this . sendEvent ( {
75- eventName : 'Browser.UnhandledError' ,
76- params : {
77- type : 'error' ,
78- message : event . message ,
79- error : event . error instanceof Error ? event . error : null ,
80- }
81- } ) ;
82- } , { passive : true } ) ;
83-
84- window . addEventListener ( 'unhandledrejection' , event => {
85- let message : string ;
74+ // Indirection for `console` ensures minifier won't strip this out.
75+ const cons = globalThis . console ;
76+ const originalConsoleError = cons [ this . #CONSOLE_ERROR_METHOD] ;
77+ cons [ this . #CONSOLE_ERROR_METHOD] = ( ...args : unknown [ ] ) => {
8678 try {
87- message = String ( event . reason ) ;
88- } catch {
89- message = '[Promise was rejected without a serialisable reason]' ;
79+ const maybeError = args [ 0 ] ;
80+ const [ message , error ] = maybeWrapError ( '[RNPerfMetrics] console.error called' , maybeError ) ;
81+ this . sendEvent ( {
82+ eventName : 'Browser.ConsoleError' ,
83+ params : {
84+ message,
85+ error,
86+ }
87+ } ) ;
88+ } catch ( e ) {
89+ const [ message , error ] = maybeWrapError ( '[RNPerfMetrics] Error handling console.error' , e ) ;
90+ this . sendEvent ( {
91+ eventName : 'Browser.ConsoleError' ,
92+ params : {
93+ message,
94+ error,
95+ }
96+ } ) ;
97+ } finally {
98+ originalConsoleError . apply ( cons , args ) ;
9099 }
91- this . sendEvent ( {
92- eventName : 'Browser.UnhandledError' ,
93- params : {
94- type : 'rejectedPromise' ,
95- message,
96- error : event . reason instanceof Error ? event . reason : null ,
97- }
98- } ) ;
99- } , { passive : true } ) ;
100+ }
100101 }
101102
102103 setLaunchId ( launchId : string | null ) : void {
@@ -175,6 +176,16 @@ function maybeTruncateDeveloperResourceUrl(parsedURL: ParsedURL): string {
175176 return parsedURL . isHttpOrHttps ( ) ? url : `${ url . slice ( 0 , 100 ) } …(omitted ${ url . length - 100 } characters)` ;
176177}
177178
179+ function maybeWrapError ( baseMessage : string , error : unknown ) : [ string , Error ] {
180+ if ( error instanceof Error ) {
181+ const message = `${ baseMessage } : ${ error . message } `
182+ return [ message , error ] ;
183+ }
184+
185+ const message = `${ baseMessage } : ${ String ( error ) } ` ;
186+ return [ message , new Error ( message , { cause : error } ) ] ;
187+ }
188+
178189type CommonEventFields = Readonly < {
179190 timestamp : DOMHighResTimeStamp ,
180191 launchId : string | void | null ,
@@ -203,12 +214,11 @@ export type BrowserVisibilityChangeEvent = Readonly<{
203214 } > ,
204215} > ;
205216
206- export type UnhandledErrorEvent = Readonly < {
207- eventName : 'Browser.UnhandledError ' ,
217+ export type ConsoleErrorEvent = Readonly < {
218+ eventName : 'Browser.ConsoleError ' ,
208219 params : Readonly < {
209- type : 'error' | 'rejectedPromise' ,
210220 message : string ,
211- error : Error | null | undefined ,
221+ error : Error ,
212222 } > ,
213223} > ;
214224
@@ -238,7 +248,7 @@ export type DeveloperResourceLoadingFinishedEvent = Readonly<{
238248} > ;
239249
240250export type ReactNativeChromeDevToolsEvent = EntrypointLoadingStartedEvent | EntrypointLoadingFinishedEvent |
241- DebuggerReadyEvent | BrowserVisibilityChangeEvent | UnhandledErrorEvent | RemoteDebuggingTerminatedEvent |
251+ DebuggerReadyEvent | BrowserVisibilityChangeEvent | ConsoleErrorEvent | RemoteDebuggingTerminatedEvent |
242252 DeveloperResourceLoadingStartedEvent | DeveloperResourceLoadingFinishedEvent ;
243253
244254export type DecoratedReactNativeChromeDevToolsEvent = CommonEventFields & ReactNativeChromeDevToolsEvent ;
0 commit comments