Skip to content

Commit

Permalink
feat(core): Add getGlobalScope() method
Browse files Browse the repository at this point in the history
This scope lives in module scope and is applied to _all_ events.
  • Loading branch information
mydea committed Dec 20, 2023
1 parent 2d69912 commit 0ed9262
Show file tree
Hide file tree
Showing 12 changed files with 605 additions and 364 deletions.
22 changes: 22 additions & 0 deletions packages/core/src/globals.ts
@@ -0,0 +1,22 @@
import type { Scope } from '@sentry/types';

interface GlobalData {
globalScope?: Scope;
}

const GLOBAL_DATA: GlobalData = {};

/**
* Get the global data.
*/
export function getGlobalData(): GlobalData {
return GLOBAL_DATA;
}

/**
* Reset all global data.
* Mostly useful for tests.
*/
export function clearGlobalData(): void {
delete GLOBAL_DATA.globalScope;
}
5 changes: 3 additions & 2 deletions packages/core/src/index.ts
Expand Up @@ -42,7 +42,8 @@ export {
} from './hub';
export { makeSession, closeSession, updateSession } from './session';
export { SessionFlusher } from './sessionflusher';
export { Scope } from './scope';
export { Scope, getGlobalScope } from './scope';
export { clearGlobalData, getGlobalData } from './globals';
export {
notifyEventProcessors,
// eslint-disable-next-line deprecation/deprecation
Expand All @@ -63,7 +64,7 @@ export {
convertIntegrationFnToClass,
} from './integration';
export { FunctionToString, InboundFilters, LinkedErrors } from './integrations';
export { applyScopeDataToEvent } from './utils/applyScopeDataToEvent';
export { applyScopeDataToEvent, mergeScopeData } from './utils/applyScopeDataToEvent';
export { prepareEvent } from './utils/prepareEvent';
export { createCheckInEnvelope } from './checkin';
export { hasTracingEnabled } from './utils/hasTracingEnabled';
Expand Down
34 changes: 30 additions & 4 deletions packages/core/src/scope.ts
Expand Up @@ -23,11 +23,12 @@ import type {
Transaction,
User,
} from '@sentry/types';
import { arrayify, dateTimestampInSeconds, isPlainObject, uuid4 } from '@sentry/utils';
import { dateTimestampInSeconds, isPlainObject, uuid4 } from '@sentry/utils';

import { getGlobalEventProcessors, notifyEventProcessors } from './eventProcessors';
import { getGlobalData } from './globals';
import { updateSession } from './session';
import { applyScopeDataToEvent } from './utils/applyScopeDataToEvent';
import { applyScopeDataToEvent, mergeScopeData } from './utils/applyScopeDataToEvent';

/**
* Default value for maximum number of breadcrumbs added to an event.
Expand Down Expand Up @@ -457,7 +458,9 @@ export class Scope implements ScopeInterface {
* @inheritDoc
*/
public getAttachments(): Attachment[] {
return this._attachments;
const data = this.getScopeData();

return data.attachments;
}

/**
Expand All @@ -469,7 +472,7 @@ export class Scope implements ScopeInterface {
}

/** @inheritDoc */
public getScopeData(): ScopeData {
public getPerScopeData(): ScopeData {
const {
_breadcrumbs,
_attachments,
Expand Down Expand Up @@ -503,6 +506,16 @@ export class Scope implements ScopeInterface {
};
}

/** @inheritdoc */
public getScopeData(): ScopeData {
const data = getGlobalScope().getPerScopeData();
const scopeData = this.getPerScopeData();

mergeScopeData(data, scopeData);

return data;
}

/**
* Applies data from the scope to the event and runs all event processors on it.
*
Expand Down Expand Up @@ -570,6 +583,19 @@ export class Scope implements ScopeInterface {
}
}

/**
* Get the global scope.
* This scope is applied to _all_ events.
*/
export function getGlobalScope(): ScopeInterface {
const globalData = getGlobalData();
if (!globalData.globalScope) {
globalData.globalScope = new Scope();
}

return globalData.globalScope;
}

function generatePropagationContext(): PropagationContext {
return {
traceId: uuid4(),
Expand Down
98 changes: 98 additions & 0 deletions packages/core/src/utils/applyScopeDataToEvent.ts
Expand Up @@ -22,6 +22,104 @@ export function applyScopeDataToEvent(event: Event, data: ScopeData): void {
applySdkMetadataToEvent(event, sdkProcessingMetadata, propagationContext);
}

/** Merge data of two scopes together. */
export function mergeScopeData(data: ScopeData, mergeData: ScopeData): void {
const {
extra,
tags,
user,
contexts,
level,
sdkProcessingMetadata,
breadcrumbs,
fingerprint,
eventProcessors,
attachments,
propagationContext,
transactionName,
span,
} = mergeData;

mergePropOverwrite(data, 'extra', extra);
mergePropOverwrite(data, 'tags', tags);
mergePropOverwrite(data, 'user', user);
mergePropOverwrite(data, 'contexts', contexts);
mergePropOverwrite(data, 'sdkProcessingMetadata', sdkProcessingMetadata);

if (level) {
data.level = level;
}

if (transactionName) {
data.transactionName = transactionName;
}

if (span) {
data.span = span;
}

if (breadcrumbs.length) {
data.breadcrumbs = [...data.breadcrumbs, ...breadcrumbs];
}

if (fingerprint.length) {
data.fingerprint = [...data.fingerprint, ...fingerprint];
}

if (eventProcessors.length) {
data.eventProcessors = [...data.eventProcessors, ...eventProcessors];
}

if (attachments.length) {
data.attachments = [...data.attachments, ...attachments];
}

data.propagationContext = { ...data.propagationContext, ...propagationContext };
}

/**
* Merge properties, overwriting existing keys.
* Exported only for tests.
*/
export function mergePropOverwrite<
Prop extends 'extra' | 'tags' | 'user' | 'contexts' | 'sdkProcessingMetadata',
Data extends ScopeData | Event,
>(data: Data, prop: Prop, mergeVal: Data[Prop]): void {
if (mergeVal && Object.keys(mergeVal).length) {
data[prop] = { ...data[prop], ...mergeVal };
}
}

/**
* Merge properties, keeping existing keys.
* Exported only for tests.
*/
export function mergePropKeep<
Prop extends 'extra' | 'tags' | 'user' | 'contexts' | 'sdkProcessingMetadata',
Data extends ScopeData | Event,
>(data: Data, prop: Prop, mergeVal: Data[Prop]): void {
if (mergeVal && Object.keys(mergeVal).length) {
data[prop] = { ...mergeVal, ...data[prop] };
}
}

/** Exported only for tests */
export function mergeArray<Prop extends 'breadcrumbs' | 'fingerprint'>(
event: Event,
prop: Prop,
mergeVal: ScopeData[Prop],
): void {
const prevVal = event[prop];
// If we are not merging any new values,
// we only need to proceed if there was an empty array before (as we want to replace it with undefined)
if (!mergeVal.length && (!prevVal || prevVal.length)) {
return;
}

const merged = [...(prevVal || []), ...mergeVal] as ScopeData[Prop];
event[prop] = merged.length ? merged : undefined;
}

function applyDataToEvent(event: Event, data: ScopeData): void {
const { extra, tags, user, contexts, level, transactionName } = data;

Expand Down

0 comments on commit 0ed9262

Please sign in to comment.