From d76956afe39ad2069ed2b2a75d7d545d64e09d4e Mon Sep 17 00:00:00 2001 From: Alexey Karimov Date: Sat, 3 Oct 2026 09:57:27 +0500 Subject: [PATCH 1/2] Phase 9: check the capture review gate. The shipped filters still drop a line they cannot finish, and the deferred gaps do not pass that line through. Co-authored-by: Cursor --- docs/design/plans/2026-09-16-implementation-plan.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/design/plans/2026-09-16-implementation-plan.md b/docs/design/plans/2026-09-16-implementation-plan.md index 9f0ad58..dcd6db5 100644 --- a/docs/design/plans/2026-09-16-implementation-plan.md +++ b/docs/design/plans/2026-09-16-implementation-plan.md @@ -4037,7 +4037,7 @@ The phase with the most native↔JS round-tripping, hence the most device testin - [x] **9.5** `addBreadcrumb(crumb)` and `setBreadcrumbFilter(cb)`. Breadcrumbs are built through the SDK's exchange factory on both platforms, so the bridge constructs rather than forwards. Device: WOD_LX1 `AMRJCP4718402860` retained `breadcrumb-filter immediate 30dccd7246d8 REDACTED` and `breadcrumb-filter rewrite 30dccd7246d8 REDACTED` at level `info`, and the unsettled crumb was absent; simulator `6FA9B3E8-26C7-4232-AA2C-537D9DF32957` retained `breadcrumb-filter immediate e62610c1b82b REDACTED` and `breadcrumb-filter rewrite e62610c1b82b REDACTED` at level `info`, and the unsettled crumb was absent. The iPhone XS (KRSFT) was already running BareExample (pid 43281), so that run was not taken. - [x] **9.6** `addNetworkEvent` for stacks the SDK does not auto-instrument. WOD_LX1 `AMRJCP4718402860` retained `https://bugsee-e2e.invalid/add/89dc3cdd4491` (`mechanism` `react-native`, `type` `complete`) and omitted the hung url (the filter logged `hung nonce=89dc3cdd4491`). iOS builds a `BugseeNetworkEvent` (`eventWithID:`) because beta3's factory `createNetworkEvent` returns nil, then `addNetworkEvent:requiresFiltering:YES`. Simulator `6FA9B3E8-26C7-4232-AA2C-537D9DF32957` retained `https://bugsee-e2e.invalid/add/0b8a3f41b546` and omitted the hung url (filter logged `hung nonce=0b8a3f41b546`). iPhone XS KRSFT `345BA7FE-2C29-5722-892A-BFCB1FD34D0C` retained `https://bugsee-e2e.invalid/add/5013161d5890` and omitted the hung url (`hung nonce=5013161d5890`). Both events are `mechanism` `react-native`, `type` `complete`. `duration` stayed 90. - [x] **9.7** Dedup across the two console streams, *before* either filter runs — otherwise the user's callback runs twice on one line by two routes. Device test in **both** Debug and Release; RN only routes `console.*` through `RCTLog` under `__DEV__`, and Hermes release builds commonly strip console calls. After the one-shot stdout/stderr echo drop was restored, WOD_LX1 `AMRJCP4718402860` Debug retained one `BUGSEE_E2E dedup-line 9370533d0f4d #1` (source 98, level 3, `calls=1`, `dev=true`) and Release retained one `BUGSEE_E2E dedup-line 5269f19051b7 #1` (`dev=false`, `calls=1`). Simulator `6FA9B3E8-26C7-4232-AA2C-537D9DF32957` Debug retained one `BUGSEE_E2E dedup-line 5d34148a4685 #1` (source 98, level 3, `calls=1`, `dev=true`) and Release retained one `BUGSEE_E2E dedup-line dd8a7164a049 #1` (`dev=false`, `calls=1`). iPhone XS KRSFT was running BareExample, so that run was not taken. -- [ ] Review gate. +- [x] Review gate. Passed at `2b56960`: an unfinished log, network, or breadcrumb filter drops the line, the Android borrow stays 9s, the iOS data-request deadline stays 450ms, `patchXhr` and a JS `fetch` patch stay off, and console dedup still drops the native echo before the user callback. Two minors stay out: one Android message can accumulate more than 32 echo credits inside a single append, and `setLogFilter` turns the native filter on before the JS listener is subscribed (that gap drops the line). --- From a24f167bef1e41bd6e2fc4323edabc8962d2238a Mon Sep 17 00:00:00 2001 From: Alexey Karimov Date: Sat, 3 Oct 2026 10:14:31 +0500 Subject: [PATCH 2/2] Keep an iOS log-filter request from sitting unanswered until process death. Enabling the native filter before the JS listener is attached leaves that gap request in the pending table with no forget timer. Co-authored-by: Cursor --- .../plans/2026-09-16-implementation-plan.md | 2 +- .../src/logs/__tests__/filter.test.ts | 14 ++++++++++++++ packages/react-native/src/logs/filter.ts | 16 +++++++++------- 3 files changed, 24 insertions(+), 8 deletions(-) diff --git a/docs/design/plans/2026-09-16-implementation-plan.md b/docs/design/plans/2026-09-16-implementation-plan.md index dcd6db5..6014144 100644 --- a/docs/design/plans/2026-09-16-implementation-plan.md +++ b/docs/design/plans/2026-09-16-implementation-plan.md @@ -4037,7 +4037,7 @@ The phase with the most native↔JS round-tripping, hence the most device testin - [x] **9.5** `addBreadcrumb(crumb)` and `setBreadcrumbFilter(cb)`. Breadcrumbs are built through the SDK's exchange factory on both platforms, so the bridge constructs rather than forwards. Device: WOD_LX1 `AMRJCP4718402860` retained `breadcrumb-filter immediate 30dccd7246d8 REDACTED` and `breadcrumb-filter rewrite 30dccd7246d8 REDACTED` at level `info`, and the unsettled crumb was absent; simulator `6FA9B3E8-26C7-4232-AA2C-537D9DF32957` retained `breadcrumb-filter immediate e62610c1b82b REDACTED` and `breadcrumb-filter rewrite e62610c1b82b REDACTED` at level `info`, and the unsettled crumb was absent. The iPhone XS (KRSFT) was already running BareExample (pid 43281), so that run was not taken. - [x] **9.6** `addNetworkEvent` for stacks the SDK does not auto-instrument. WOD_LX1 `AMRJCP4718402860` retained `https://bugsee-e2e.invalid/add/89dc3cdd4491` (`mechanism` `react-native`, `type` `complete`) and omitted the hung url (the filter logged `hung nonce=89dc3cdd4491`). iOS builds a `BugseeNetworkEvent` (`eventWithID:`) because beta3's factory `createNetworkEvent` returns nil, then `addNetworkEvent:requiresFiltering:YES`. Simulator `6FA9B3E8-26C7-4232-AA2C-537D9DF32957` retained `https://bugsee-e2e.invalid/add/0b8a3f41b546` and omitted the hung url (filter logged `hung nonce=0b8a3f41b546`). iPhone XS KRSFT `345BA7FE-2C29-5722-892A-BFCB1FD34D0C` retained `https://bugsee-e2e.invalid/add/5013161d5890` and omitted the hung url (`hung nonce=5013161d5890`). Both events are `mechanism` `react-native`, `type` `complete`. `duration` stayed 90. - [x] **9.7** Dedup across the two console streams, *before* either filter runs — otherwise the user's callback runs twice on one line by two routes. Device test in **both** Debug and Release; RN only routes `console.*` through `RCTLog` under `__DEV__`, and Hermes release builds commonly strip console calls. After the one-shot stdout/stderr echo drop was restored, WOD_LX1 `AMRJCP4718402860` Debug retained one `BUGSEE_E2E dedup-line 9370533d0f4d #1` (source 98, level 3, `calls=1`, `dev=true`) and Release retained one `BUGSEE_E2E dedup-line 5269f19051b7 #1` (`dev=false`, `calls=1`). Simulator `6FA9B3E8-26C7-4232-AA2C-537D9DF32957` Debug retained one `BUGSEE_E2E dedup-line 5d34148a4685 #1` (source 98, level 3, `calls=1`, `dev=true`) and Release retained one `BUGSEE_E2E dedup-line dd8a7164a049 #1` (`dev=false`, `calls=1`). iPhone XS KRSFT was running BareExample, so that run was not taken. -- [x] Review gate. Passed at `2b56960`: an unfinished log, network, or breadcrumb filter drops the line, the Android borrow stays 9s, the iOS data-request deadline stays 450ms, `patchXhr` and a JS `fetch` patch stay off, and console dedup still drops the native echo before the user callback. Two minors stay out: one Android message can accumulate more than 32 echo credits inside a single append, and `setLogFilter` turns the native filter on before the JS listener is subscribed (that gap drops the line). +- [x] Review gate. Passed at `2b56960`: an unfinished log, network, or breadcrumb filter drops the line, the Android borrow stays 9s, the iOS data-request deadline stays 450ms, `patchXhr` and a JS `fetch` patch stay off, and console dedup still drops the native echo before the user callback. One minor stays out: one Android message can accumulate more than 32 echo credits inside a single append. `setLogFilter` now subscribes before it enables the native filter. --- diff --git a/packages/react-native/src/logs/__tests__/filter.test.ts b/packages/react-native/src/logs/__tests__/filter.test.ts index 80f0944..207cb0c 100644 --- a/packages/react-native/src/logs/__tests__/filter.test.ts +++ b/packages/react-native/src/logs/__tests__/filter.test.ts @@ -159,6 +159,20 @@ describe('setLogFilter', () => { expect(native.logFilterRequestSubscribeCallCount()).toBe(1); }); + it('subscribes once, and before the native filter is installed', () => { + const original = native.onLogFilterRequest.bind(native); + native.onLogFilterRequest = (listener) => { + expect(native.setLogFilterEnabled).not.toHaveBeenCalled(); + return original(listener); + }; + Bugsee.setLogFilter((line) => line); + expect(native.setLogFilterEnabled).toHaveBeenCalledWith(true); + Bugsee.setLogFilter((line) => line); + Bugsee.setLogFilter(); + Bugsee.setLogFilter((line) => line); + expect(native.logFilterRequestSubscribeCallCount()).toBe(1); + }); + it('rejects a non-function and keeps the filter already installed', async () => { Bugsee.setLogFilter((line) => `kept:${line}`); const set = Bugsee.setLogFilter as (callback?: unknown) => void; diff --git a/packages/react-native/src/logs/filter.ts b/packages/react-native/src/logs/filter.ts index 8ce837b..74b8fc3 100644 --- a/packages/react-native/src/logs/filter.ts +++ b/packages/react-native/src/logs/filter.ts @@ -22,9 +22,11 @@ let subscribed = false; * `undefined` or `null` clears it, which tells native to uninstall the filter * so lines are recorded without one. * - * The native request is subscribed to once and stays subscribed: clearing the - * callback stops native from asking, and a later callback must still be able - * to answer. + * The JS listener is subscribed before the native filter is installed, so a + * line filtered on the installing turn still has a listener. On iOS a request + * emitted in that gap stays in the pending table until process death. The + * subscription stays: clearing the callback stops native from asking, and a + * later callback must still be able to answer. */ export function setLogFilter(callback?: LogFilter | null): void { if (callback != null && typeof callback !== 'function') { @@ -32,12 +34,12 @@ export function setLogFilter(callback?: LogFilter | null): void { `Bugsee.setLogFilter requires a function, got ${typeof callback}`, ); } - current = callback ?? undefined; - NativeBugsee.setLogFilterEnabled(current !== undefined); - if (current !== undefined && !subscribed) { - subscribed = true; + if (callback != null && !subscribed) { NativeBugsee.onLogFilterRequest(onLogFilterRequest); + subscribed = true; } + current = callback ?? undefined; + NativeBugsee.setLogFilterEnabled(current !== undefined); } /**