Skip to content

Commit 0ed9262

Browse files
committed
feat(core): Add getGlobalScope() method
This scope lives in module scope and is applied to _all_ events.
1 parent 2d69912 commit 0ed9262

File tree

12 files changed

+605
-364
lines changed

12 files changed

+605
-364
lines changed

packages/core/src/globals.ts

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
import type { Scope } from '@sentry/types';
2+
3+
interface GlobalData {
4+
globalScope?: Scope;
5+
}
6+
7+
const GLOBAL_DATA: GlobalData = {};
8+
9+
/**
10+
* Get the global data.
11+
*/
12+
export function getGlobalData(): GlobalData {
13+
return GLOBAL_DATA;
14+
}
15+
16+
/**
17+
* Reset all global data.
18+
* Mostly useful for tests.
19+
*/
20+
export function clearGlobalData(): void {
21+
delete GLOBAL_DATA.globalScope;
22+
}

packages/core/src/index.ts

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,8 @@ export {
4242
} from './hub';
4343
export { makeSession, closeSession, updateSession } from './session';
4444
export { SessionFlusher } from './sessionflusher';
45-
export { Scope } from './scope';
45+
export { Scope, getGlobalScope } from './scope';
46+
export { clearGlobalData, getGlobalData } from './globals';
4647
export {
4748
notifyEventProcessors,
4849
// eslint-disable-next-line deprecation/deprecation
@@ -63,7 +64,7 @@ export {
6364
convertIntegrationFnToClass,
6465
} from './integration';
6566
export { FunctionToString, InboundFilters, LinkedErrors } from './integrations';
66-
export { applyScopeDataToEvent } from './utils/applyScopeDataToEvent';
67+
export { applyScopeDataToEvent, mergeScopeData } from './utils/applyScopeDataToEvent';
6768
export { prepareEvent } from './utils/prepareEvent';
6869
export { createCheckInEnvelope } from './checkin';
6970
export { hasTracingEnabled } from './utils/hasTracingEnabled';

packages/core/src/scope.ts

Lines changed: 30 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -23,11 +23,12 @@ import type {
2323
Transaction,
2424
User,
2525
} from '@sentry/types';
26-
import { arrayify, dateTimestampInSeconds, isPlainObject, uuid4 } from '@sentry/utils';
26+
import { dateTimestampInSeconds, isPlainObject, uuid4 } from '@sentry/utils';
2727

2828
import { getGlobalEventProcessors, notifyEventProcessors } from './eventProcessors';
29+
import { getGlobalData } from './globals';
2930
import { updateSession } from './session';
30-
import { applyScopeDataToEvent } from './utils/applyScopeDataToEvent';
31+
import { applyScopeDataToEvent, mergeScopeData } from './utils/applyScopeDataToEvent';
3132

3233
/**
3334
* Default value for maximum number of breadcrumbs added to an event.
@@ -457,7 +458,9 @@ export class Scope implements ScopeInterface {
457458
* @inheritDoc
458459
*/
459460
public getAttachments(): Attachment[] {
460-
return this._attachments;
461+
const data = this.getScopeData();
462+
463+
return data.attachments;
461464
}
462465

463466
/**
@@ -469,7 +472,7 @@ export class Scope implements ScopeInterface {
469472
}
470473

471474
/** @inheritDoc */
472-
public getScopeData(): ScopeData {
475+
public getPerScopeData(): ScopeData {
473476
const {
474477
_breadcrumbs,
475478
_attachments,
@@ -503,6 +506,16 @@ export class Scope implements ScopeInterface {
503506
};
504507
}
505508

509+
/** @inheritdoc */
510+
public getScopeData(): ScopeData {
511+
const data = getGlobalScope().getPerScopeData();
512+
const scopeData = this.getPerScopeData();
513+
514+
mergeScopeData(data, scopeData);
515+
516+
return data;
517+
}
518+
506519
/**
507520
* Applies data from the scope to the event and runs all event processors on it.
508521
*
@@ -570,6 +583,19 @@ export class Scope implements ScopeInterface {
570583
}
571584
}
572585

586+
/**
587+
* Get the global scope.
588+
* This scope is applied to _all_ events.
589+
*/
590+
export function getGlobalScope(): ScopeInterface {
591+
const globalData = getGlobalData();
592+
if (!globalData.globalScope) {
593+
globalData.globalScope = new Scope();
594+
}
595+
596+
return globalData.globalScope;
597+
}
598+
573599
function generatePropagationContext(): PropagationContext {
574600
return {
575601
traceId: uuid4(),

packages/core/src/utils/applyScopeDataToEvent.ts

Lines changed: 98 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,104 @@ export function applyScopeDataToEvent(event: Event, data: ScopeData): void {
2222
applySdkMetadataToEvent(event, sdkProcessingMetadata, propagationContext);
2323
}
2424

25+
/** Merge data of two scopes together. */
26+
export function mergeScopeData(data: ScopeData, mergeData: ScopeData): void {
27+
const {
28+
extra,
29+
tags,
30+
user,
31+
contexts,
32+
level,
33+
sdkProcessingMetadata,
34+
breadcrumbs,
35+
fingerprint,
36+
eventProcessors,
37+
attachments,
38+
propagationContext,
39+
transactionName,
40+
span,
41+
} = mergeData;
42+
43+
mergePropOverwrite(data, 'extra', extra);
44+
mergePropOverwrite(data, 'tags', tags);
45+
mergePropOverwrite(data, 'user', user);
46+
mergePropOverwrite(data, 'contexts', contexts);
47+
mergePropOverwrite(data, 'sdkProcessingMetadata', sdkProcessingMetadata);
48+
49+
if (level) {
50+
data.level = level;
51+
}
52+
53+
if (transactionName) {
54+
data.transactionName = transactionName;
55+
}
56+
57+
if (span) {
58+
data.span = span;
59+
}
60+
61+
if (breadcrumbs.length) {
62+
data.breadcrumbs = [...data.breadcrumbs, ...breadcrumbs];
63+
}
64+
65+
if (fingerprint.length) {
66+
data.fingerprint = [...data.fingerprint, ...fingerprint];
67+
}
68+
69+
if (eventProcessors.length) {
70+
data.eventProcessors = [...data.eventProcessors, ...eventProcessors];
71+
}
72+
73+
if (attachments.length) {
74+
data.attachments = [...data.attachments, ...attachments];
75+
}
76+
77+
data.propagationContext = { ...data.propagationContext, ...propagationContext };
78+
}
79+
80+
/**
81+
* Merge properties, overwriting existing keys.
82+
* Exported only for tests.
83+
*/
84+
export function mergePropOverwrite<
85+
Prop extends 'extra' | 'tags' | 'user' | 'contexts' | 'sdkProcessingMetadata',
86+
Data extends ScopeData | Event,
87+
>(data: Data, prop: Prop, mergeVal: Data[Prop]): void {
88+
if (mergeVal && Object.keys(mergeVal).length) {
89+
data[prop] = { ...data[prop], ...mergeVal };
90+
}
91+
}
92+
93+
/**
94+
* Merge properties, keeping existing keys.
95+
* Exported only for tests.
96+
*/
97+
export function mergePropKeep<
98+
Prop extends 'extra' | 'tags' | 'user' | 'contexts' | 'sdkProcessingMetadata',
99+
Data extends ScopeData | Event,
100+
>(data: Data, prop: Prop, mergeVal: Data[Prop]): void {
101+
if (mergeVal && Object.keys(mergeVal).length) {
102+
data[prop] = { ...mergeVal, ...data[prop] };
103+
}
104+
}
105+
106+
/** Exported only for tests */
107+
export function mergeArray<Prop extends 'breadcrumbs' | 'fingerprint'>(
108+
event: Event,
109+
prop: Prop,
110+
mergeVal: ScopeData[Prop],
111+
): void {
112+
const prevVal = event[prop];
113+
// If we are not merging any new values,
114+
// we only need to proceed if there was an empty array before (as we want to replace it with undefined)
115+
if (!mergeVal.length && (!prevVal || prevVal.length)) {
116+
return;
117+
}
118+
119+
const merged = [...(prevVal || []), ...mergeVal] as ScopeData[Prop];
120+
event[prop] = merged.length ? merged : undefined;
121+
}
122+
25123
function applyDataToEvent(event: Event, data: ScopeData): void {
26124
const { extra, tags, user, contexts, level, transactionName } = data;
27125

0 commit comments

Comments
 (0)