From 84f3cf95ea9ad17ba5c0014ea35d0b8e98af56fe Mon Sep 17 00:00:00 2001 From: Vitali Zaidman Date: Fri, 14 Feb 2025 08:42:39 -0800 Subject: [PATCH] add event on debugger heartbeat and timeout (#49437) Summary: Pull Request resolved: https://github.com/facebook/react-native/pull/49437 * Log every heartbeat with the same sampling as CDP commands (`debugger_heartbeat`) * Log abandoned connections due to heartbeat timeout (`debugger_timeout`) Changelog: [General][Added] - add inspector proxy events for debugger heartbeat (sampled) and abandoned connections Reviewed By: robhogan Differential Revision: D69603217 fbshipit-source-id: f40721f5dc0cc0c33e71a0d29aa50ccf4e7fee3f --- .../src/inspector-proxy/InspectorProxy.js | 25 ++++++++++++++++--- .../dev-middleware/src/types/EventReporter.js | 10 ++++++++ 2 files changed, 31 insertions(+), 4 deletions(-) diff --git a/packages/dev-middleware/src/inspector-proxy/InspectorProxy.js b/packages/dev-middleware/src/inspector-proxy/InspectorProxy.js index 6d87cbaea8b..536bae1ccb3 100644 --- a/packages/dev-middleware/src/inspector-proxy/InspectorProxy.js +++ b/packages/dev-middleware/src/inspector-proxy/InspectorProxy.js @@ -317,6 +317,7 @@ export default class InspectorProxy implements InspectorProxyQueries { const pageId = query.page; const debuggerRelativeBaseUrl = getBaseUrlFromRequest(req) ?? this.#serverBaseUrl; + const appId = this.#devices.get(deviceId)?.getApp() || 'unknown'; if (deviceId == null || pageId == null) { throw new Error('Incorrect URL - must provide device and page IDs'); @@ -329,7 +330,7 @@ export default class InspectorProxy implements InspectorProxyQueries { this.#logger?.info('Connection to DevTools established.'); - this.#startHeartbeat(socket, DEBUGGER_HEARTBEAT_INTERVAL_MS); + this.#startHeartbeat(socket, DEBUGGER_HEARTBEAT_INTERVAL_MS, appId); device.handleDebuggerConnection(socket, pageId, { debuggerRelativeBaseUrl, @@ -357,9 +358,10 @@ export default class InspectorProxy implements InspectorProxyQueries { // where proxies may drop idle connections (e.g., VS Code tunnels). // // https://datatracker.ietf.org/doc/html/rfc6455#section-5.5.2 - #startHeartbeat(socket: WS, intervalMs: number) { + #startHeartbeat(socket: WS, intervalMs: number, appId: string) { let shouldSetTerminateTimeout = false; let terminateTimeout = null; + let latestPingMs = Date.now(); const pingTimeout: Timeout = setTimeout(() => { if (socket.readyState !== WS.OPEN) { @@ -369,6 +371,7 @@ export default class InspectorProxy implements InspectorProxyQueries { } shouldSetTerminateTimeout = true; + latestPingMs = Date.now(); socket.ping(() => { if (!shouldSetTerminateTimeout) { // Sometimes, this `sent` callback fires later than @@ -394,6 +397,11 @@ export default class InspectorProxy implements InspectorProxyQueries { this.#logger?.error( `Connection terminated with DevTools after not responding for ${MAX_PONG_LATENCY_MS / 1000} seconds.`, ); + this.#eventReporter?.logEvent({ + type: 'debugger_timeout', + duration: MAX_PONG_LATENCY_MS, + appId, + }); }, MAX_PONG_LATENCY_MS).unref(); }); }, intervalMs).unref(); @@ -404,8 +412,17 @@ export default class InspectorProxy implements InspectorProxyQueries { pingTimeout.refresh(); }; - socket.on('pong', onAnyMessageFromDebugger); - socket.on('message', onAnyMessageFromDebugger); + socket.on('pong', () => { + onAnyMessageFromDebugger(); + this.#eventReporter?.logEvent({ + type: 'debugger_heartbeat', + duration: Date.now() - latestPingMs, + appId, + }); + }); + socket.on('message', () => { + onAnyMessageFromDebugger(); + }); socket.on('close', (code: number, reason: string) => { this.#logger?.info( diff --git a/packages/dev-middleware/src/types/EventReporter.js b/packages/dev-middleware/src/types/EventReporter.js index 1564f338147..1c93da7b868 100644 --- a/packages/dev-middleware/src/types/EventReporter.js +++ b/packages/dev-middleware/src/types/EventReporter.js @@ -93,6 +93,16 @@ export type ReportableEvent = error: string, errorStack: string, ...DebuggerSessionIDs, + } + | { + type: 'debugger_heartbeat', + duration: number, + appId: string, + } + | { + type: 'debugger_timeout', + duration: number, + appId: string, }; /**