Skip to content

Commit 134a66a

Browse files
authored
feat(node): Include global scope for eventLoopBlockIntegration (#20108)
- Closes getsentry/sentry-electron#1320 Global scope is not captured by the native module through the `AsyncLocalStorage` so this PR sends that via the polling mechanism.
1 parent 68e78ab commit 134a66a

File tree

5 files changed

+33
-11
lines changed

5 files changed

+33
-11
lines changed

dev-packages/node-integration-tests/suites/thread-blocked-native/isolated.mjs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,8 @@ const fns = [
2424
neverResolve,
2525
];
2626

27+
Sentry.getGlobalScope().setUser({ email: 'something@gmail.com' });
28+
2729
setTimeout(() => {
2830
for (let id = 0; id < 10; id++) {
2931
Sentry.withIsolationScope(async () => {

dev-packages/node-integration-tests/suites/thread-blocked-native/test.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -249,7 +249,7 @@ describe('Thread Blocked Native', { timeout: 30_000 }, () => {
249249
message: 'Starting task 5',
250250
},
251251
],
252-
user: { id: 5 },
252+
user: { id: 5, email: 'something@gmail.com' },
253253
threads: {
254254
values: [
255255
{

packages/node-native/src/common.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import type { Contexts, DsnComponents, Primitive, SdkMetadata, Session } from '@sentry/core';
1+
import type { Contexts, DsnComponents, Primitive, ScopeData, SdkMetadata, Session } from '@sentry/core';
22

33
export const POLL_RATIO = 2;
44

@@ -40,5 +40,6 @@ export interface WorkerStartData extends ThreadBlockedIntegrationOptions {
4040

4141
export interface ThreadState {
4242
session: Session | undefined;
43+
scope: ScopeData;
4344
debugImages: Record<string, string>;
4445
}

packages/node-native/src/event-loop-block-integration.ts

Lines changed: 20 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,18 @@ import type {
88
EventHint,
99
Integration,
1010
IntegrationFn,
11+
ScopeData,
12+
} from '@sentry/core';
13+
import {
14+
debug,
15+
defineIntegration,
16+
getClient,
17+
getCurrentScope,
18+
getFilenameToDebugIdMap,
19+
getGlobalScope,
20+
getIsolationScope,
21+
mergeScopeData,
1122
} from '@sentry/core';
12-
import { debug, defineIntegration, getClient, getFilenameToDebugIdMap, getIsolationScope } from '@sentry/core';
1323
import type { NodeClient } from '@sentry/node';
1424
import { registerThread, threadPoll } from '@sentry-internal/node-native-stacktrace';
1525
import type { ThreadBlockedIntegrationOptions, WorkerStartData } from './common';
@@ -37,6 +47,13 @@ async function getContexts(client: NodeClient): Promise<Contexts> {
3747
return event?.contexts || {};
3848
}
3949

50+
function getLocalScopeData(): ScopeData {
51+
const globalScope = getGlobalScope().getScopeData();
52+
const currentScope = getCurrentScope().getScopeData();
53+
mergeScopeData(globalScope, currentScope);
54+
return globalScope;
55+
}
56+
4057
type IntegrationInternal = { start: () => void; stop: () => void };
4158

4259
function poll(enabled: boolean, clientOptions: ClientOptions): void {
@@ -45,8 +62,9 @@ function poll(enabled: boolean, clientOptions: ClientOptions): void {
4562
// We need to copy the session object and remove the toJSON method so it can be sent to the worker
4663
// serialized without making it a SerializedSession
4764
const session = currentSession ? { ...currentSession, toJSON: undefined } : undefined;
65+
const scope = getLocalScopeData();
4866
// message the worker to tell it the main event loop is still running
49-
threadPoll(enabled, { session, debugImages: getFilenameToDebugIdMap(clientOptions.stackParser) });
67+
threadPoll(enabled, { session, scope, debugImages: getFilenameToDebugIdMap(clientOptions.stackParser) });
5068
} catch {
5169
// we ignore all errors
5270
}

packages/node-native/src/event-loop-block-watchdog.ts

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,6 @@ import type { ThreadState, WorkerStartData } from './common';
2121
import { POLL_RATIO } from './common';
2222

2323
type CurrentScopes = {
24-
scope: Scope;
2524
isolationScope: Scope;
2625
};
2726

@@ -275,14 +274,16 @@ async function sendBlockEvent(crashedThreadId: string): Promise<void> {
275274
...getExceptionAndThreads(crashedThreadId, threads),
276275
};
277276

278-
const asyncState = threads[crashedThreadId]?.asyncState;
279-
if (asyncState) {
280-
// We need to rehydrate the scopes from the serialized objects so we can call getScopeData()
281-
const scope = Object.assign(new Scope(), asyncState.scope).getScopeData();
282-
const isolationScope = Object.assign(new Scope(), asyncState.isolationScope).getScopeData();
277+
const scope = crashedThread.pollState?.scope
278+
? new Scope().update(crashedThread.pollState.scope).getScopeData()
279+
: new Scope().getScopeData();
280+
281+
if (crashedThread?.asyncState?.isolationScope) {
282+
// We need to rehydrate the scope from the serialized object with properties beginning with _user, etc
283+
const isolationScope = Object.assign(new Scope(), crashedThread.asyncState.isolationScope).getScopeData();
283284
mergeScopeData(scope, isolationScope);
284-
applyScopeToEvent(event, scope);
285285
}
286+
applyScopeToEvent(event, scope);
286287

287288
const allDebugImages: Record<string, string> = Object.values(threads).reduce((acc, threadState) => {
288289
return { ...acc, ...threadState.pollState?.debugImages };

0 commit comments

Comments
 (0)