Skip to content

Commit 4a5ba93

Browse files
authored
ref(core): Extract and reuse getCombinedScopeData helper (#18585)
Pre-work for #18160 We have multiple places in which we need to combine the `ScopeData` of global, isolation and current scopes, to apply this data to telemetry items (events, logs, metrics, soon also spansV2). Previously, we did this in-place or with helpers that were not re-used. This PR now unifies the various locations to one helper from core which can be reused everywhere. Closes #18586 (added automatically)
1 parent 995f788 commit 4a5ba93

File tree

8 files changed

+81
-60
lines changed

8 files changed

+81
-60
lines changed

packages/core/src/index.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -61,7 +61,7 @@ export {
6161
_INTERNAL_shouldSkipAiProviderWrapping,
6262
_INTERNAL_clearAiProviderSkips,
6363
} from './utils/ai/providerSkip';
64-
export { applyScopeDataToEvent, mergeScopeData } from './utils/applyScopeDataToEvent';
64+
export { applyScopeDataToEvent, mergeScopeData, getCombinedScopeData } from './utils/scopeData';
6565
export { prepareEvent } from './utils/prepareEvent';
6666
export type { ExclusiveEventHintOrCaptureContext } from './utils/prepareEvent';
6767
export { createCheckInEnvelope } from './checkin';

packages/core/src/logs/internal.ts

Lines changed: 3 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,13 @@
11
import { serializeAttributes } from '../attributes';
22
import { getGlobalSingleton } from '../carrier';
33
import type { Client } from '../client';
4-
import { getClient, getCurrentScope, getGlobalScope, getIsolationScope } from '../currentScopes';
4+
import { getClient, getCurrentScope, getIsolationScope } from '../currentScopes';
55
import { DEBUG_BUILD } from '../debug-build';
6-
import type { Scope, ScopeData } from '../scope';
76
import type { Integration } from '../types-hoist/integration';
87
import type { Log, SerializedLog } from '../types-hoist/log';
9-
import { mergeScopeData } from '../utils/applyScopeDataToEvent';
108
import { consoleSandbox, debug } from '../utils/debug-logger';
119
import { isParameterizedString } from '../utils/is';
10+
import { getCombinedScopeData } from '../utils/scopeData';
1211
import { _getSpanForScope } from '../utils/spanOnScope';
1312
import { timestampInSeconds } from '../utils/time';
1413
import { _getTraceInfoFromScope } from '../utils/trace-info';
@@ -98,7 +97,7 @@ export function _INTERNAL_captureLog(
9897
const {
9998
user: { id, email, username },
10099
attributes: scopeAttributes = {},
101-
} = getMergedScopeData(currentScope);
100+
} = getCombinedScopeData(getIsolationScope(), currentScope);
102101

103102
setLogAttribute(processedLogAttributes, 'user.id', id, false);
104103
setLogAttribute(processedLogAttributes, 'user.email', email, false);
@@ -212,20 +211,6 @@ export function _INTERNAL_getLogBuffer(client: Client): Array<SerializedLog> | u
212211
return _getBufferMap().get(client);
213212
}
214213

215-
/**
216-
* Get the scope data for the current scope after merging with the
217-
* global scope and isolation scope.
218-
*
219-
* @param currentScope - The current scope.
220-
* @returns The scope data.
221-
*/
222-
function getMergedScopeData(currentScope: Scope): ScopeData {
223-
const scopeData = getGlobalScope().getScopeData();
224-
mergeScopeData(scopeData, getIsolationScope().getScopeData());
225-
mergeScopeData(scopeData, currentScope.getScopeData());
226-
return scopeData;
227-
}
228-
229214
function _getBufferMap(): WeakMap<Client, Array<SerializedLog>> {
230215
// The reference to the Client <> LogBuffer map is stored on the carrier to ensure it's always the same
231216
return getGlobalSingleton('clientToLogBufferMap', () => new WeakMap<Client, Array<SerializedLog>>());

packages/core/src/metrics/internal.ts

Lines changed: 4 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,12 @@
11
import { getGlobalSingleton } from '../carrier';
22
import type { Client } from '../client';
3-
import { getClient, getCurrentScope, getGlobalScope, getIsolationScope } from '../currentScopes';
3+
import { getClient, getCurrentScope, getIsolationScope } from '../currentScopes';
44
import { DEBUG_BUILD } from '../debug-build';
5-
import type { Scope, ScopeData } from '../scope';
5+
import type { Scope } from '../scope';
66
import type { Integration } from '../types-hoist/integration';
77
import type { Metric, SerializedMetric, SerializedMetricAttributeValue } from '../types-hoist/metric';
8-
import { mergeScopeData } from '../utils/applyScopeDataToEvent';
98
import { debug } from '../utils/debug-logger';
9+
import { getCombinedScopeData } from '../utils/scopeData';
1010
import { _getSpanForScope } from '../utils/spanOnScope';
1111
import { timestampInSeconds } from '../utils/time';
1212
import { _getTraceInfoFromScope } from '../utils/trace-info';
@@ -130,7 +130,7 @@ function _enrichMetricAttributes(beforeMetric: Metric, client: Client, currentSc
130130
// Add user attributes
131131
const {
132132
user: { id, email, username },
133-
} = getMergedScopeData(currentScope);
133+
} = getCombinedScopeData(getIsolationScope(), currentScope);
134134
setMetricAttribute(processedMetricAttributes, 'user.id', id, false);
135135
setMetricAttribute(processedMetricAttributes, 'user.email', email, false);
136136
setMetricAttribute(processedMetricAttributes, 'user.name', username, false);
@@ -288,20 +288,6 @@ export function _INTERNAL_getMetricBuffer(client: Client): Array<SerializedMetri
288288
return _getBufferMap().get(client);
289289
}
290290

291-
/**
292-
* Get the scope data for the current scope after merging with the
293-
* global scope and isolation scope.
294-
*
295-
* @param currentScope - The current scope.
296-
* @returns The scope data.
297-
*/
298-
function getMergedScopeData(currentScope: Scope): ScopeData {
299-
const scopeData = getGlobalScope().getScopeData();
300-
mergeScopeData(scopeData, getIsolationScope().getScopeData());
301-
mergeScopeData(scopeData, currentScope.getScopeData());
302-
return scopeData;
303-
}
304-
305291
function _getBufferMap(): WeakMap<Client, Array<SerializedMetric>> {
306292
// The reference to the Client <> MetricBuffer map is stored on the carrier to ensure it's always the same
307293
return getGlobalSingleton('clientToMetricBufferMap', () => new WeakMap<Client, Array<SerializedMetric>>());

packages/core/src/utils/prepareEvent.ts

Lines changed: 2 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,15 @@
11
import type { Client } from '../client';
22
import { DEFAULT_ENVIRONMENT } from '../constants';
3-
import { getGlobalScope } from '../currentScopes';
43
import { notifyEventProcessors } from '../eventProcessors';
54
import type { CaptureContext, ScopeContext } from '../scope';
65
import { Scope } from '../scope';
76
import type { Event, EventHint } from '../types-hoist/event';
87
import type { ClientOptions } from '../types-hoist/options';
98
import type { StackParser } from '../types-hoist/stacktrace';
10-
import { applyScopeDataToEvent, mergeScopeData } from './applyScopeDataToEvent';
119
import { getFilenameToDebugIdMap } from './debug-ids';
1210
import { addExceptionMechanism, uuid4 } from './misc';
1311
import { normalize } from './normalize';
12+
import { applyScopeDataToEvent, getCombinedScopeData } from './scopeData';
1413
import { truncate } from './string';
1514
import { dateTimestampInSeconds } from './time';
1615

@@ -79,17 +78,7 @@ export function prepareEvent(
7978
// This should be the last thing called, since we want that
8079
// {@link Scope.addEventProcessor} gets the finished prepared event.
8180
// Merge scope data together
82-
const data = getGlobalScope().getScopeData();
83-
84-
if (isolationScope) {
85-
const isolationData = isolationScope.getScopeData();
86-
mergeScopeData(data, isolationData);
87-
}
88-
89-
if (finalScope) {
90-
const finalScopeData = finalScope.getScopeData();
91-
mergeScopeData(data, finalScopeData);
92-
}
81+
const data = getCombinedScopeData(isolationScope, finalScope);
9382

9483
const attachments = [...(hint.attachments || []), ...data.attachments];
9584
if (attachments.length) {

packages/core/src/utils/applyScopeDataToEvent.ts renamed to packages/core/src/utils/scopeData.ts

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
1-
import type { ScopeData } from '../scope';
1+
import { getGlobalScope } from '../currentScopes';
2+
import type { Scope, ScopeData } from '../scope';
23
import { getDynamicSamplingContextFromSpan } from '../tracing/dynamicSamplingContext';
34
import type { Breadcrumb } from '../types-hoist/breadcrumb';
45
import type { Event } from '../types-hoist/event';
@@ -113,6 +114,20 @@ export function mergeArray<Prop extends 'breadcrumbs' | 'fingerprint'>(
113114
event[prop] = merged.length ? merged : undefined;
114115
}
115116

117+
/**
118+
* Get the scope data for the current scope after merging with the
119+
* global scope and isolation scope.
120+
*
121+
* @param currentScope - The current scope.
122+
* @returns The scope data.
123+
*/
124+
export function getCombinedScopeData(isolationScope: Scope | undefined, currentScope: Scope | undefined): ScopeData {
125+
const scopeData = getGlobalScope().getScopeData();
126+
isolationScope && mergeScopeData(scopeData, isolationScope.getScopeData());
127+
currentScope && mergeScopeData(scopeData, currentScope.getScopeData());
128+
return scopeData;
129+
}
130+
116131
function applyDataToEvent(event: Event, data: ScopeData): void {
117132
const { extra, tags, user, contexts, level, transactionName } = data;
118133

packages/core/test/lib/scope.test.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ import {
1010
import { Scope } from '../../src/scope';
1111
import type { Breadcrumb } from '../../src/types-hoist/breadcrumb';
1212
import type { Event } from '../../src/types-hoist/event';
13-
import { applyScopeDataToEvent } from '../../src/utils/applyScopeDataToEvent';
13+
import { applyScopeDataToEvent } from '../../src/utils/scopeData';
1414
import { getDefaultTestClientOptions, TestClient } from '../mocks/client';
1515
import { clearGlobalScope } from '../testutils';
1616

packages/core/test/lib/utils/applyScopeDataToEvent.test.ts renamed to packages/core/test/lib/utils/scopeData.test.ts

Lines changed: 52 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,18 @@
1-
import { describe, expect, it } from 'vitest';
1+
import { describe, expect, it, vi } from 'vitest';
22
import type { ScopeData } from '../../../src';
3-
import { startInactiveSpan } from '../../../src';
3+
import { Scope, startInactiveSpan } from '../../../src';
4+
import * as currentScopes from '../../../src/currentScopes';
45
import type { Attachment } from '../../../src/types-hoist/attachment';
56
import type { Breadcrumb } from '../../../src/types-hoist/breadcrumb';
67
import type { Event, EventType } from '../../../src/types-hoist/event';
78
import type { EventProcessor } from '../../../src/types-hoist/eventprocessor';
89
import {
910
applyScopeDataToEvent,
11+
getCombinedScopeData,
1012
mergeAndOverwriteScopeData,
1113
mergeArray,
1214
mergeScopeData,
13-
} from '../../../src/utils/applyScopeDataToEvent';
15+
} from '../../../src/utils/scopeData';
1416

1517
describe('mergeArray', () => {
1618
it.each([
@@ -353,3 +355,50 @@ describe('applyScopeDataToEvent', () => {
353355
},
354356
);
355357
});
358+
359+
describe('getCombinedScopeData', () => {
360+
const globalScope = new Scope();
361+
const isolationScope = new Scope();
362+
const currentScope = new Scope();
363+
364+
it('returns the combined scope data with correct precedence', () => {
365+
globalScope.setTag('foo', 'bar');
366+
globalScope.setTag('dogs', 'boring');
367+
globalScope.setTag('global', 'global');
368+
369+
isolationScope.setTag('dogs', 'great');
370+
isolationScope.setTag('foo', 'nope');
371+
isolationScope.setTag('isolation', 'isolation');
372+
373+
currentScope.setTag('foo', 'baz');
374+
currentScope.setTag('current', 'current');
375+
376+
vi.spyOn(currentScopes, 'getGlobalScope').mockReturnValue(globalScope);
377+
378+
expect(getCombinedScopeData(isolationScope, currentScope)).toEqual({
379+
attachments: [],
380+
attributes: {},
381+
breadcrumbs: [],
382+
contexts: {},
383+
eventProcessors: [],
384+
extra: {},
385+
fingerprint: [],
386+
level: undefined,
387+
propagationContext: {
388+
sampleRand: expect.any(Number),
389+
traceId: expect.any(String),
390+
},
391+
sdkProcessingMetadata: {},
392+
span: undefined,
393+
tags: {
394+
current: 'current',
395+
global: 'global',
396+
isolation: 'isolation',
397+
foo: 'baz',
398+
dogs: 'great',
399+
},
400+
transactionName: undefined,
401+
user: {},
402+
});
403+
});
404+
});

packages/node-core/src/integrations/anr/index.ts

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -5,12 +5,11 @@ import {
55
debug,
66
defineIntegration,
77
getClient,
8+
getCombinedScopeData,
89
getCurrentScope,
910
getFilenameToDebugIdMap,
10-
getGlobalScope,
1111
getIsolationScope,
1212
GLOBAL_OBJ,
13-
mergeScopeData,
1413
} from '@sentry/core';
1514
import { NODE_VERSION } from '../../nodeVersion';
1615
import type { NodeClient } from '../../sdk/client';
@@ -35,9 +34,7 @@ function globalWithScopeFetchFn(): typeof GLOBAL_OBJ & { __SENTRY_GET_SCOPES__?:
3534

3635
/** Fetches merged scope data */
3736
function getScopeData(): ScopeData {
38-
const scope = getGlobalScope().getScopeData();
39-
mergeScopeData(scope, getIsolationScope().getScopeData());
40-
mergeScopeData(scope, getCurrentScope().getScopeData());
37+
const scope = getCombinedScopeData(getIsolationScope(), getCurrentScope());
4138

4239
// We remove attachments because they likely won't serialize well as json
4340
scope.attachments = [];

0 commit comments

Comments
 (0)