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
This commit is contained in:
Vitali Zaidman
2025-02-14 08:42:39 -08:00
committed by Facebook GitHub Bot
parent 0aa89e9f1c
commit 84f3cf95ea
2 changed files with 31 additions and 4 deletions
@@ -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(
@@ -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,
};
/**