diff --git a/packages/core/src/asyncContext.ts b/packages/core/src/asyncContext.ts deleted file mode 100644 index 82d336ded509..000000000000 --- a/packages/core/src/asyncContext.ts +++ /dev/null @@ -1,119 +0,0 @@ -import type { Integration } from '@sentry/types'; -import type { Scope } from '@sentry/types'; -import { GLOBAL_OBJ } from '@sentry/utils'; -import type { startInactiveSpan, startSpan, startSpanManual, suppressTracing, withActiveSpan } from './tracing/trace'; -import type { getActiveSpan } from './utils/spanUtils'; - -/** - * @private Private API with no semver guarantees! - * - * Strategy used to track async context. - */ -export interface AsyncContextStrategy { - /** - * Fork the isolation scope inside of the provided callback. - */ - withIsolationScope: (callback: (isolationScope: Scope) => T) => T; - - /** - * Fork the current scope inside of the provided callback. - */ - withScope: (callback: (isolationScope: Scope) => T) => T; - - /** - * Set the provided scope as the current scope inside of the provided callback. - */ - withSetScope: (scope: Scope, callback: (scope: Scope) => T) => T; - - /** - * Set the provided isolation as the current isolation scope inside of the provided callback. - */ - withSetIsolationScope: (isolationScope: Scope, callback: (isolationScope: Scope) => T) => T; - - /** - * Get the currently active scope. - */ - getCurrentScope: () => Scope; - - /** - * Get the currently active isolation scope. - */ - getIsolationScope: () => Scope; - - // OPTIONAL: Custom tracing methods - // These are used so that we can provide OTEL-based implementations - - /** Start an active span. */ - startSpan?: typeof startSpan; - - /** Start an inactive span. */ - startInactiveSpan?: typeof startInactiveSpan; - - /** Start an active manual span. */ - startSpanManual?: typeof startSpanManual; - - /** Get the currently active span. */ - getActiveSpan?: typeof getActiveSpan; - - /** Make a span the active span in the context of the callback. */ - withActiveSpan?: typeof withActiveSpan; - - /** Suppress tracing in the given callback, ensuring no spans are generated inside of it. */ - suppressTracing?: typeof suppressTracing; -} - -/** - * An object that contains a hub and maintains a scope stack. - * @hidden - */ -export interface Carrier { - __SENTRY__?: SentryCarrier; -} - -interface SentryCarrier { - acs?: AsyncContextStrategy; - /** - * Extra Hub properties injected by various SDKs - */ - integrations?: Integration[]; - extensions?: { - /** Extension methods for the hub, which are bound to the current Hub instance */ - // eslint-disable-next-line @typescript-eslint/ban-types - [key: string]: Function; - }; -} - -/** - * Returns the global shim registry. - * - * FIXME: This function is problematic, because despite always returning a valid Carrier, - * it has an optional `__SENTRY__` property, which then in turn requires us to always perform an unnecessary check - * at the call-site. We always access the carrier through this function, so we can guarantee that `__SENTRY__` is there. - **/ -export function getMainCarrier(): Carrier { - // This ensures a Sentry carrier exists - getSentryCarrier(GLOBAL_OBJ); - return GLOBAL_OBJ; -} - -/** - * @private Private API with no semver guarantees! - * - * Sets the global async context strategy - */ -export function setAsyncContextStrategy(strategy: AsyncContextStrategy | undefined): void { - // Get main carrier (global for every environment) - const registry = getMainCarrier(); - const sentry = getSentryCarrier(registry); - sentry.acs = strategy; -} - -/** Will either get the existing sentry carrier, or create a new one. */ -export function getSentryCarrier(carrier: Carrier): SentryCarrier { - if (!carrier.__SENTRY__) { - carrier.__SENTRY__ = { - extensions: {}, - }; - } - return carrier.__SENTRY__; -} diff --git a/packages/core/src/asyncContext/index.ts b/packages/core/src/asyncContext/index.ts new file mode 100644 index 000000000000..d13874bd854b --- /dev/null +++ b/packages/core/src/asyncContext/index.ts @@ -0,0 +1,31 @@ +import type { Carrier } from './../carrier'; +import { getMainCarrier, getSentryCarrier } from './../carrier'; +import { getStackAsyncContextStrategy } from './stackStrategy'; +import type { AsyncContextStrategy } from './types'; + +/** + * @private Private API with no semver guarantees! + * + * Sets the global async context strategy + */ +export function setAsyncContextStrategy(strategy: AsyncContextStrategy | undefined): void { + // Get main carrier (global for every environment) + const registry = getMainCarrier(); + const sentry = getSentryCarrier(registry); + sentry.acs = strategy; +} + +/** + * Get the current async context strategy. + * If none has been setup, the default will be used. + */ +export function getAsyncContextStrategy(carrier: Carrier): AsyncContextStrategy { + const sentry = getSentryCarrier(carrier); + + if (sentry.acs) { + return sentry.acs; + } + + // Otherwise, use the default one (stack) + return getStackAsyncContextStrategy(); +} diff --git a/packages/core/src/asyncContext/stackStrategy.ts b/packages/core/src/asyncContext/stackStrategy.ts new file mode 100644 index 000000000000..4ec7445d1b3e --- /dev/null +++ b/packages/core/src/asyncContext/stackStrategy.ts @@ -0,0 +1,178 @@ +import type { Client, Scope as ScopeInterface } from '@sentry/types'; +import { isThenable } from '@sentry/utils'; +import { getDefaultCurrentScope, getDefaultIsolationScope } from '../currentScopes'; +import { Scope } from '../scope'; + +import { getMainCarrier, getSentryCarrier } from './../carrier'; +import type { AsyncContextStrategy } from './types'; + +interface Layer { + client?: Client; + scope: ScopeInterface; +} + +/** + * This is an object that holds a stack of scopes. + */ +export class AsyncContextStack { + private readonly _stack: Layer[]; + private _isolationScope: ScopeInterface; + + public constructor(scope?: ScopeInterface, isolationScope?: ScopeInterface) { + let assignedScope; + if (!scope) { + assignedScope = new Scope(); + } else { + assignedScope = scope; + } + + let assignedIsolationScope; + if (!isolationScope) { + assignedIsolationScope = new Scope(); + } else { + assignedIsolationScope = isolationScope; + } + + this._stack = [{ scope: assignedScope }]; + this._isolationScope = assignedIsolationScope; + } + + /** + * Fork a scope for the stack. + */ + public withScope(callback: (scope: ScopeInterface) => T): T { + const scope = this._pushScope(); + + let maybePromiseResult: T; + try { + maybePromiseResult = callback(scope); + } catch (e) { + this._popScope(); + throw e; + } + + if (isThenable(maybePromiseResult)) { + // @ts-expect-error - isThenable returns the wrong type + return maybePromiseResult.then( + res => { + this._popScope(); + return res; + }, + e => { + this._popScope(); + throw e; + }, + ); + } + + this._popScope(); + return maybePromiseResult; + } + + /** + * Get the client of the stack. + */ + public getClient(): C | undefined { + return this.getStackTop().client as C; + } + + /** + * Returns the scope of the top stack. + */ + public getScope(): ScopeInterface { + return this.getStackTop().scope; + } + + /** + * Get the isolation scope for the stack. + */ + public getIsolationScope(): ScopeInterface { + return this._isolationScope; + } + + /** + * Returns the scope stack for domains or the process. + */ + public getStack(): Layer[] { + return this._stack; + } + + /** + * Returns the topmost scope layer in the order domain > local > process. + */ + public getStackTop(): Layer { + return this._stack[this._stack.length - 1]; + } + + /** + * Push a scope to the stack. + */ + private _pushScope(): ScopeInterface { + // We want to clone the content of prev scope + const scope = this.getScope().clone(); + this.getStack().push({ + client: this.getClient(), + scope, + }); + return scope; + } + + /** + * Pop a scope from the stack. + */ + private _popScope(): boolean { + if (this.getStack().length <= 1) return false; + return !!this.getStack().pop(); + } +} + +/** + * Get the global async context stack. + * This will be removed during the v8 cycle and is only here to make migration easier. + */ +function getAsyncContextStack(): AsyncContextStack { + const registry = getMainCarrier(); + const sentry = getSentryCarrier(registry) as { hub?: AsyncContextStack }; + + // If there's no hub, or its an old API, assign a new one + if (sentry.hub) { + return sentry.hub; + } + + sentry.hub = new AsyncContextStack(getDefaultCurrentScope(), getDefaultIsolationScope()); + return sentry.hub; +} + +function withScope(callback: (scope: ScopeInterface) => T): T { + return getAsyncContextStack().withScope(callback); +} + +function withSetScope(scope: ScopeInterface, callback: (scope: ScopeInterface) => T): T { + const hub = getAsyncContextStack() as AsyncContextStack; + return hub.withScope(() => { + hub.getStackTop().scope = scope; + return callback(scope); + }); +} + +function withIsolationScope(callback: (isolationScope: ScopeInterface) => T): T { + return getAsyncContextStack().withScope(() => { + return callback(getAsyncContextStack().getIsolationScope()); + }); +} + +/** + * Get the stack-based async context strategy. + */ +export function getStackAsyncContextStrategy(): AsyncContextStrategy { + return { + withIsolationScope, + withScope, + withSetScope, + withSetIsolationScope: (_isolationScope: ScopeInterface, callback: (isolationScope: ScopeInterface) => T) => { + return withIsolationScope(callback); + }, + getCurrentScope: () => getAsyncContextStack().getScope(), + getIsolationScope: () => getAsyncContextStack().getIsolationScope(), + }; +} diff --git a/packages/core/src/asyncContext/types.ts b/packages/core/src/asyncContext/types.ts new file mode 100644 index 000000000000..bd69c8e63e78 --- /dev/null +++ b/packages/core/src/asyncContext/types.ts @@ -0,0 +1,67 @@ +import type { Scope } from '@sentry/types'; +import type { + startInactiveSpan, + startSpan, + startSpanManual, + suppressTracing, + withActiveSpan, +} from './../tracing/trace'; +import type { getActiveSpan } from './../utils/spanUtils'; + +/** + * @private Private API with no semver guarantees! + * + * Strategy used to track async context. + */ +export interface AsyncContextStrategy { + /** + * Fork the isolation scope inside of the provided callback. + */ + withIsolationScope: (callback: (isolationScope: Scope) => T) => T; + + /** + * Fork the current scope inside of the provided callback. + */ + withScope: (callback: (isolationScope: Scope) => T) => T; + + /** + * Set the provided scope as the current scope inside of the provided callback. + */ + withSetScope: (scope: Scope, callback: (scope: Scope) => T) => T; + + /** + * Set the provided isolation as the current isolation scope inside of the provided callback. + */ + withSetIsolationScope: (isolationScope: Scope, callback: (isolationScope: Scope) => T) => T; + + /** + * Get the currently active scope. + */ + getCurrentScope: () => Scope; + + /** + * Get the currently active isolation scope. + */ + getIsolationScope: () => Scope; + + // OPTIONAL: Custom tracing methods + // These are used so that we can provide OTEL-based implementations + + /** Start an active span. */ + startSpan?: typeof startSpan; + + /** Start an inactive span. */ + startInactiveSpan?: typeof startInactiveSpan; + + /** Start an active manual span. */ + startSpanManual?: typeof startSpanManual; + + /** Get the currently active span. */ + getActiveSpan?: typeof getActiveSpan; + + /** Make a span the active span in the context of the callback. */ + withActiveSpan?: typeof withActiveSpan; + + /** Suppress tracing in the given callback, ensuring no spans are generated inside of it. */ + suppressTracing?: typeof suppressTracing; +} diff --git a/packages/core/src/carrier.ts b/packages/core/src/carrier.ts new file mode 100644 index 000000000000..215ed9673148 --- /dev/null +++ b/packages/core/src/carrier.ts @@ -0,0 +1,59 @@ +import type { Integration } from '@sentry/types'; +import { GLOBAL_OBJ } from '@sentry/utils'; +import type { AsyncContextStrategy } from './asyncContext/types'; + +/** + * An object that contains a hub and maintains a scope stack. + * @hidden + */ +export interface Carrier { + __SENTRY__?: SentryCarrier; +} + +interface SentryCarrier { + acs?: AsyncContextStrategy; +} + +/** + * An object that contains a hub and maintains a scope stack. + * @hidden + */ +export interface Carrier { + __SENTRY__?: SentryCarrier; +} + +interface SentryCarrier { + acs?: AsyncContextStrategy; + /** + * Extra Hub properties injected by various SDKs + */ + integrations?: Integration[]; + extensions?: { + /** Extension methods for the hub, which are bound to the current Hub instance */ + // eslint-disable-next-line @typescript-eslint/ban-types + [key: string]: Function; + }; +} + +/** + * Returns the global shim registry. + * + * FIXME: This function is problematic, because despite always returning a valid Carrier, + * it has an optional `__SENTRY__` property, which then in turn requires us to always perform an unnecessary check + * at the call-site. We always access the carrier through this function, so we can guarantee that `__SENTRY__` is there. + **/ +export function getMainCarrier(): Carrier { + // This ensures a Sentry carrier exists + getSentryCarrier(GLOBAL_OBJ); + return GLOBAL_OBJ; +} + +/** Will either get the existing sentry carrier, or create a new one. */ +export function getSentryCarrier(carrier: Carrier): SentryCarrier { + if (!carrier.__SENTRY__) { + carrier.__SENTRY__ = { + extensions: {}, + }; + } + return carrier.__SENTRY__; +} diff --git a/packages/core/src/currentScopes.ts b/packages/core/src/currentScopes.ts index e0b598c1ad39..86978759252b 100644 --- a/packages/core/src/currentScopes.ts +++ b/packages/core/src/currentScopes.ts @@ -1,10 +1,20 @@ import type { Scope } from '@sentry/types'; import type { Client } from '@sentry/types'; import { getGlobalSingleton } from '@sentry/utils'; -import { getMainCarrier } from './asyncContext'; -import { getAsyncContextStrategy } from './hub'; +import { getAsyncContextStrategy } from './asyncContext'; +import { getMainCarrier } from './carrier'; import { Scope as ScopeClass } from './scope'; +/** Get the default current scope. */ +export function getDefaultCurrentScope(): Scope { + return getGlobalSingleton('defaultCurrentScope', () => new ScopeClass()); +} + +/** Get the default isolation scope. */ +export function getDefaultIsolationScope(): Scope { + return getGlobalSingleton('defaultIsolationScope', () => new ScopeClass()); +} + /** * Get the currently active scope. */ diff --git a/packages/core/src/hub.ts b/packages/core/src/hub.ts deleted file mode 100644 index 1bd3ec7c64b6..000000000000 --- a/packages/core/src/hub.ts +++ /dev/null @@ -1,581 +0,0 @@ -/* eslint-disable max-lines */ -import type { - Breadcrumb, - BreadcrumbHint, - Client, - Event, - EventHint, - Extra, - Extras, - Hub as HubInterface, - Integration, - IntegrationClass, - Primitive, - Scope as ScopeInterface, - Session, - SessionContext, - SeverityLevel, - User, -} from '@sentry/types'; -import { getGlobalSingleton } from '@sentry/utils'; -import { GLOBAL_OBJ, consoleSandbox, dateTimestampInSeconds, isThenable, logger, uuid4 } from '@sentry/utils'; - -import type { AsyncContextStrategy, Carrier } from './asyncContext'; -import { getMainCarrier, getSentryCarrier } from './asyncContext'; -import { DEFAULT_ENVIRONMENT } from './constants'; -import { DEBUG_BUILD } from './debug-build'; -import { Scope } from './scope'; -import { closeSession, makeSession, updateSession } from './session'; -import { SDK_VERSION } from './version'; - -/** - * API compatibility version of this hub. - * - * WARNING: This number should only be increased when the global interface - * changes and new methods are introduced. - * - * @hidden - */ -export const API_VERSION = parseFloat(SDK_VERSION); - -/** - * Default maximum number of breadcrumbs added to an event. Can be overwritten - * with {@link Options.maxBreadcrumbs}. - */ -const DEFAULT_BREADCRUMBS = 100; - -/** - * A layer in the process stack. - * @hidden - */ -export interface Layer { - client?: Client; - scope: ScopeInterface; -} - -/** - * @inheritDoc - * @deprecated This class will be removed in v8 (tmp-deprecating so we're aware of where this is a problem) - */ -// eslint-disable-next-line deprecation/deprecation -export class Hub implements HubInterface { - /** Is a {@link Layer}[] containing the client and scope */ - private readonly _stack: Layer[]; - - private _isolationScope: ScopeInterface; - - /** - * Creates a new instance of the hub, will push one {@link Layer} into the - * internal stack on creation. - * - * @param client bound to the hub. - * @param scope bound to the hub. - * @param version number, higher number means higher priority. - * - * @deprecated Instantiation of Hub objects is deprecated and the constructor will be removed in version 8 of the SDK. - * - * If you are currently using the Hub for multi-client use like so: - * - * ``` - * // OLD - * const hub = new Hub(); - * hub.bindClient(client); - * makeMain(hub) - * ``` - * - * instead initialize the client as follows: - * - * ``` - * // NEW - * Sentry.withIsolationScope(() => { - * Sentry.setCurrentClient(client); - * client.init(); - * }); - * ``` - * - * If you are using the Hub to capture events like so: - * - * ``` - * // OLD - * const client = new Client(); - * const hub = new Hub(client); - * hub.captureException() - * ``` - * - * instead capture isolated events as follows: - * - * ``` - * // NEW - * const client = new Client(); - * const scope = new Scope(); - * scope.setClient(client); - * scope.captureException(); - * ``` - */ - public constructor( - client?: Client, - scope?: ScopeInterface, - isolationScope?: ScopeInterface, - private readonly _version: number = API_VERSION, - ) { - let assignedScope; - if (!scope) { - assignedScope = new Scope(); - assignedScope.setClient(client); - } else { - assignedScope = scope; - } - - let assignedIsolationScope; - if (!isolationScope) { - assignedIsolationScope = new Scope(); - assignedIsolationScope.setClient(client); - } else { - assignedIsolationScope = isolationScope; - } - - this._stack = [{ scope: assignedScope }]; - - if (client) { - // eslint-disable-next-line deprecation/deprecation - this.bindClient(client); - } - - this._isolationScope = assignedIsolationScope; - } - - /** - * This binds the given client to the current scope. - * @param client An SDK client (client) instance. - * - * @deprecated Use `initAndBind()` directly, or `setCurrentClient()` and/or `client.init()` instead. - */ - public bindClient(client?: Client): void { - // eslint-disable-next-line deprecation/deprecation - const top = this.getStackTop(); - top.client = client; - top.scope.setClient(client); - if (client) { - client.init(); - } - } - - /** - * @inheritDoc - * - * @deprecated Use `Sentry.withScope()` instead. - */ - public withScope(callback: (scope: ScopeInterface) => T): T { - const scope = this._pushScope(); - - let maybePromiseResult: T; - try { - maybePromiseResult = callback(scope); - } catch (e) { - this._popScope(); - throw e; - } - - if (isThenable(maybePromiseResult)) { - // @ts-expect-error - isThenable returns the wrong type - return maybePromiseResult.then( - res => { - this._popScope(); - return res; - }, - e => { - this._popScope(); - throw e; - }, - ); - } - - this._popScope(); - return maybePromiseResult; - } - - /** - * @inheritDoc - * - * @deprecated Use `Sentry.getClient()` instead. - */ - public getClient(): C | undefined { - // eslint-disable-next-line deprecation/deprecation - return this.getStackTop().client as C; - } - - /** - * Returns the scope of the top stack. - * - * @deprecated Use `Sentry.getCurrentScope()` instead. - */ - public getScope(): ScopeInterface { - // eslint-disable-next-line deprecation/deprecation - return this.getStackTop().scope; - } - - /** - * @deprecated Use `Sentry.getIsolationScope()` instead. - */ - public getIsolationScope(): ScopeInterface { - return this._isolationScope; - } - - /** - * Returns the scope stack for domains or the process. - * @deprecated This will be removed in v8. - */ - public getStack(): Layer[] { - return this._stack; - } - - /** - * Returns the topmost scope layer in the order domain > local > process. - * @deprecated This will be removed in v8. - */ - public getStackTop(): Layer { - return this._stack[this._stack.length - 1]; - } - - /** - * @inheritDoc - * - * @deprecated Use `Sentry.captureException()` instead. - */ - public captureException(exception: unknown, hint?: EventHint): string { - const eventId = hint && hint.event_id ? hint.event_id : uuid4(); - const syntheticException = new Error('Sentry syntheticException'); - // eslint-disable-next-line deprecation/deprecation - this.getScope().captureException(exception, { - originalException: exception, - syntheticException, - ...hint, - event_id: eventId, - }); - - return eventId; - } - - /** - * @inheritDoc - * - * @deprecated Use `Sentry.captureMessage()` instead. - */ - public captureMessage(message: string, level?: SeverityLevel, hint?: EventHint): string { - const eventId = hint && hint.event_id ? hint.event_id : uuid4(); - const syntheticException = new Error(message); - // eslint-disable-next-line deprecation/deprecation - this.getScope().captureMessage(message, level, { - originalException: message, - syntheticException, - ...hint, - event_id: eventId, - }); - - return eventId; - } - - /** - * @inheritDoc - * - * @deprecated Use `Sentry.captureEvent()` instead. - */ - public captureEvent(event: Event, hint?: EventHint): string { - const eventId = hint && hint.event_id ? hint.event_id : uuid4(); - // eslint-disable-next-line deprecation/deprecation - this.getScope().captureEvent(event, { ...hint, event_id: eventId }); - return eventId; - } - - /** - * @inheritDoc - * - * @deprecated Use `Sentry.addBreadcrumb()` instead. - */ - public addBreadcrumb(breadcrumb: Breadcrumb, hint?: BreadcrumbHint): void { - // eslint-disable-next-line deprecation/deprecation - const { client } = this.getStackTop(); - - if (!client) return; - - const { beforeBreadcrumb = null, maxBreadcrumbs = DEFAULT_BREADCRUMBS } = - (client.getOptions && client.getOptions()) || {}; - - if (maxBreadcrumbs <= 0) return; - - const timestamp = dateTimestampInSeconds(); - const mergedBreadcrumb = { timestamp, ...breadcrumb }; - const finalBreadcrumb = beforeBreadcrumb - ? (consoleSandbox(() => beforeBreadcrumb(mergedBreadcrumb, hint)) as Breadcrumb | null) - : mergedBreadcrumb; - - if (finalBreadcrumb === null) return; - - client.emit('beforeAddBreadcrumb', finalBreadcrumb, hint); - - // eslint-disable-next-line deprecation/deprecation - this.getIsolationScope().addBreadcrumb(finalBreadcrumb, maxBreadcrumbs); - } - - /** - * @inheritDoc - * @deprecated Use `Sentry.setUser()` instead. - */ - public setUser(user: User | null): void { - // eslint-disable-next-line deprecation/deprecation - this.getIsolationScope().setUser(user); - } - - /** - * @inheritDoc - * @deprecated Use `Sentry.setTags()` instead. - */ - public setTags(tags: { [key: string]: Primitive }): void { - // eslint-disable-next-line deprecation/deprecation - this.getIsolationScope().setTags(tags); - } - - /** - * @inheritDoc - * @deprecated Use `Sentry.setExtras()` instead. - */ - public setExtras(extras: Extras): void { - // eslint-disable-next-line deprecation/deprecation - this.getIsolationScope().setExtras(extras); - } - - /** - * @inheritDoc - * @deprecated Use `Sentry.setTag()` instead. - */ - public setTag(key: string, value: Primitive): void { - // eslint-disable-next-line deprecation/deprecation - this.getIsolationScope().setTag(key, value); - } - - /** - * @inheritDoc - * @deprecated Use `Sentry.setExtra()` instead. - */ - public setExtra(key: string, extra: Extra): void { - // eslint-disable-next-line deprecation/deprecation - this.getIsolationScope().setExtra(key, extra); - } - - /** - * @inheritDoc - * @deprecated Use `Sentry.setContext()` instead. - */ - // eslint-disable-next-line @typescript-eslint/no-explicit-any - public setContext(name: string, context: { [key: string]: any } | null): void { - // eslint-disable-next-line deprecation/deprecation - this.getIsolationScope().setContext(name, context); - } - - /** - * @inheritDoc - * @deprecated Use `Sentry.getClient().getIntegrationByName()` instead. - */ - public getIntegration(integration: IntegrationClass): T | null { - // eslint-disable-next-line deprecation/deprecation - const client = this.getClient(); - if (!client) return null; - try { - return client.getIntegrationByName(integration.id) || null; - } catch (_oO) { - DEBUG_BUILD && logger.warn(`Cannot retrieve integration ${integration.id} from the current Hub`); - return null; - } - } - - /** - * @inheritDoc - * - * @deprecated Use top level `captureSession` instead. - */ - public captureSession(endSession: boolean = false): void { - // both send the update and pull the session from the scope - if (endSession) { - // eslint-disable-next-line deprecation/deprecation - return this.endSession(); - } - - // only send the update - this._sendSessionUpdate(); - } - - /** - * @inheritDoc - * @deprecated Use top level `endSession` instead. - */ - public endSession(): void { - // eslint-disable-next-line deprecation/deprecation - const layer = this.getStackTop(); - const scope = layer.scope; - const session = scope.getSession(); - if (session) { - closeSession(session); - } - this._sendSessionUpdate(); - - // the session is over; take it off of the scope - scope.setSession(); - } - - /** - * @inheritDoc - * @deprecated Use top level `startSession` instead. - */ - public startSession(context?: SessionContext): Session { - // eslint-disable-next-line deprecation/deprecation - const { scope, client } = this.getStackTop(); - const { release, environment = DEFAULT_ENVIRONMENT } = (client && client.getOptions()) || {}; - - // Will fetch userAgent if called from browser sdk - const { userAgent } = GLOBAL_OBJ.navigator || {}; - - const session = makeSession({ - release, - environment, - user: scope.getUser(), - ...(userAgent && { userAgent }), - ...context, - }); - - // End existing session if there's one - const currentSession = scope.getSession && scope.getSession(); - if (currentSession && currentSession.status === 'ok') { - updateSession(currentSession, { status: 'exited' }); - } - // eslint-disable-next-line deprecation/deprecation - this.endSession(); - - // Afterwards we set the new session on the scope - scope.setSession(session); - - return session; - } - - /** - * Sends the current Session on the scope - */ - private _sendSessionUpdate(): void { - // eslint-disable-next-line deprecation/deprecation - const { scope, client } = this.getStackTop(); - - const session = scope.getSession(); - if (session && client && client.captureSession) { - client.captureSession(session); - } - } - - /** - * Push a scope to the stack. - */ - private _pushScope(): ScopeInterface { - // We want to clone the content of prev scope - // eslint-disable-next-line deprecation/deprecation - const scope = this.getScope().clone(); - // eslint-disable-next-line deprecation/deprecation - this.getStack().push({ - // eslint-disable-next-line deprecation/deprecation - client: this.getClient(), - scope, - }); - return scope; - } - - /** - * Pop a scope from the stack. - */ - private _popScope(): boolean { - // eslint-disable-next-line deprecation/deprecation - if (this.getStack().length <= 1) return false; - // eslint-disable-next-line deprecation/deprecation - return !!this.getStack().pop(); - } -} - -/** Get the default current scope. */ -export function getDefaultCurrentScope(): Scope { - return getGlobalSingleton('defaultCurrentScope', () => new Scope()); -} - -/** Get the default isolation scope. */ -export function getDefaultIsolationScope(): Scope { - return getGlobalSingleton('defaultIsolationScope', () => new Scope()); -} - -/** - * Get the global hub. - * This will be removed during the v8 cycle and is only here to make migration easier. - */ -// eslint-disable-next-line deprecation/deprecation -export function getGlobalHub(): HubInterface { - const registry = getMainCarrier(); - // eslint-disable-next-line deprecation/deprecation - const sentry = getSentryCarrier(registry) as { hub?: HubInterface }; - - // If there's no hub, or its an old API, assign a new one - if (sentry.hub) { - return sentry.hub; - } - - // eslint-disable-next-line deprecation/deprecation - sentry.hub = new Hub(undefined, getDefaultCurrentScope(), getDefaultIsolationScope()); - return sentry.hub; -} - -/** - * Get the current async context strategy. - * If none has been setup, the default will be used. - */ -export function getAsyncContextStrategy(carrier: Carrier): AsyncContextStrategy { - const sentry = getSentryCarrier(carrier); - - if (sentry.acs) { - return sentry.acs; - } - - // Otherwise, use the default one - return getHubStackAsyncContextStrategy(); -} - -function withScope(callback: (scope: ScopeInterface) => T): T { - // eslint-disable-next-line deprecation/deprecation - return getGlobalHub().withScope(callback); -} - -function withSetScope(scope: ScopeInterface, callback: (scope: ScopeInterface) => T): T { - // eslint-disable-next-line deprecation/deprecation - const hub = getGlobalHub() as Hub; - // eslint-disable-next-line deprecation/deprecation - return hub.withScope(() => { - // eslint-disable-next-line deprecation/deprecation - hub.getStackTop().scope = scope as Scope; - return callback(scope); - }); -} - -function withIsolationScope(callback: (isolationScope: ScopeInterface) => T): T { - // eslint-disable-next-line deprecation/deprecation - return getGlobalHub().withScope(() => { - // eslint-disable-next-line deprecation/deprecation - return callback(getGlobalHub().getIsolationScope()); - }); -} - -/* eslint-disable deprecation/deprecation */ -function getHubStackAsyncContextStrategy(): AsyncContextStrategy { - return { - withIsolationScope, - withScope, - withSetScope, - withSetIsolationScope: (_isolationScope: ScopeInterface, callback: (isolationScope: ScopeInterface) => T) => { - return withIsolationScope(callback); - }, - getCurrentScope: () => getGlobalHub().getScope(), - getIsolationScope: () => getGlobalHub().getIsolationScope(), - }; -} -/* eslint-enable deprecation/deprecation */ diff --git a/packages/core/src/index.ts b/packages/core/src/index.ts index cf3415302314..a863c6ee271d 100644 --- a/packages/core/src/index.ts +++ b/packages/core/src/index.ts @@ -1,6 +1,6 @@ export type { ClientClass } from './sdk'; -export type { Layer } from './hub'; -export type { AsyncContextStrategy, Carrier } from './asyncContext'; +export type { AsyncContextStrategy } from './asyncContext/types'; +export type { Carrier } from './carrier'; export type { OfflineStore, OfflineTransportOptions } from './transports/offline'; export type { ServerRuntimeClientOptions } from './server-runtime-client'; export type { RequestDataIntegrationOptions } from './integrations/requestdata'; @@ -29,10 +29,6 @@ export { captureSession, addEventProcessor, } from './exports'; -export { - getDefaultCurrentScope, - getDefaultIsolationScope, -} from './hub'; export { getCurrentScope, getIsolationScope, @@ -40,11 +36,11 @@ export { withScope, withIsolationScope, getClient, + getDefaultCurrentScope, + getDefaultIsolationScope, } from './currentScopes'; -export { - getMainCarrier, - setAsyncContextStrategy, -} from './asyncContext'; +export { setAsyncContextStrategy } from './asyncContext'; +export { getMainCarrier } from './carrier'; export { makeSession, closeSession, updateSession } from './session'; export { SessionFlusher } from './sessionflusher'; export { Scope } from './scope'; diff --git a/packages/core/src/sdk.ts b/packages/core/src/sdk.ts index ebe8f9a6ca22..85fdeae6e4d6 100644 --- a/packages/core/src/sdk.ts +++ b/packages/core/src/sdk.ts @@ -2,9 +2,9 @@ import type { Client, ClientOptions } from '@sentry/types'; import { consoleSandbox, logger } from '@sentry/utils'; import { getCurrentScope } from './currentScopes'; -import { getMainCarrier, getSentryCarrier } from './asyncContext'; +import type { AsyncContextStack } from './asyncContext/stackStrategy'; +import { getMainCarrier, getSentryCarrier } from './carrier'; import { DEBUG_BUILD } from './debug-build'; -import type { Hub } from './hub'; /** A class object that can instantiate Client objects. */ export type ClientClass = new (options: O) => F; @@ -55,11 +55,8 @@ export function setCurrentClient(client: Client): void { * @see {@link hub.ts getGlobalHub} */ function registerClientOnGlobalHub(client: Client): void { - // eslint-disable-next-line deprecation/deprecation - const sentryGlobal = getSentryCarrier(getMainCarrier()) as { hub?: Hub }; - // eslint-disable-next-line deprecation/deprecation + const sentryGlobal = getSentryCarrier(getMainCarrier()) as { hub?: AsyncContextStack }; if (sentryGlobal.hub && typeof sentryGlobal.hub.getStackTop === 'function') { - // eslint-disable-next-line deprecation/deprecation sentryGlobal.hub.getStackTop().client = client; } } diff --git a/packages/core/src/tracing/trace.ts b/packages/core/src/tracing/trace.ts index 791cae88a573..065403c78aed 100644 --- a/packages/core/src/tracing/trace.ts +++ b/packages/core/src/tracing/trace.ts @@ -1,11 +1,11 @@ import type { ClientOptions, Scope, SentrySpanArguments, Span, SpanTimeInput, StartSpanOptions } from '@sentry/types'; import { propagationContextFromHeaders } from '@sentry/utils'; -import type { AsyncContextStrategy } from '../asyncContext'; -import { getMainCarrier } from '../asyncContext'; +import type { AsyncContextStrategy } from '../asyncContext/types'; +import { getMainCarrier } from '../carrier'; import { getClient, getCurrentScope, getIsolationScope, withScope } from '../currentScopes'; -import { getAsyncContextStrategy } from '../hub'; +import { getAsyncContextStrategy } from '../asyncContext'; import { SEMANTIC_ATTRIBUTE_SENTRY_SAMPLE_RATE, SEMANTIC_ATTRIBUTE_SENTRY_SOURCE } from '../semanticAttributes'; import { handleCallbackErrors } from '../utils/handleCallbackErrors'; import { hasTracingEnabled } from '../utils/hasTracingEnabled'; diff --git a/packages/core/src/utils/spanUtils.ts b/packages/core/src/utils/spanUtils.ts index 3d06aa0d3204..b4fb587dd5d3 100644 --- a/packages/core/src/utils/spanUtils.ts +++ b/packages/core/src/utils/spanUtils.ts @@ -15,9 +15,9 @@ import { generateSentryTraceHeader, timestampInSeconds, } from '@sentry/utils'; -import { getMainCarrier } from '../asyncContext'; +import { getAsyncContextStrategy } from '../asyncContext'; +import { getMainCarrier } from '../carrier'; import { getCurrentScope } from '../currentScopes'; -import { getAsyncContextStrategy } from '../hub'; import { getMetricSummaryJsonForSpan, updateMetricSummaryOnSpan } from '../metrics/metric-summary'; import type { MetricType } from '../metrics/types'; import { SEMANTIC_ATTRIBUTE_SENTRY_OP, SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN } from '../semanticAttributes'; diff --git a/packages/core/test/lib/tracing/trace.test.ts b/packages/core/test/lib/tracing/trace.test.ts index b4b73307e174..110a90d01f6f 100644 --- a/packages/core/test/lib/tracing/trace.test.ts +++ b/packages/core/test/lib/tracing/trace.test.ts @@ -12,7 +12,7 @@ import { spanToJSON, withScope, } from '../../../src'; -import { getAsyncContextStrategy } from '../../../src/hub'; +import { getAsyncContextStrategy } from '../../../src/asyncContext'; import { SentrySpan, continueTrace,