diff --git a/packages/browser/src/integrations/globalhandlers.ts b/packages/browser/src/integrations/globalhandlers.ts index 0c3f3be60e2d..079ef6083212 100644 --- a/packages/browser/src/integrations/globalhandlers.ts +++ b/packages/browser/src/integrations/globalhandlers.ts @@ -1,6 +1,6 @@ /* eslint-disable @typescript-eslint/no-unsafe-member-access */ -import { getCurrentHub } from '@sentry/core'; -import type { Event, Hub, Integration, Primitive, StackParser } from '@sentry/types'; +import { captureEvent, getClient } from '@sentry/core'; +import type { Client, Event, Integration, Primitive, StackParser } from '@sentry/types'; import { addGlobalErrorInstrumentationHandler, addGlobalUnhandledRejectionInstrumentationHandler, @@ -36,12 +36,6 @@ export class GlobalHandlers implements Integration { /** JSDoc */ private readonly _options: GlobalHandlersIntegrations; - /** - * Stores references functions to installing handlers. Will set to undefined - * after they have been run so that they are not used twice. - */ - private _installFunc: Record void) | undefined>; - /** JSDoc */ public constructor(options?: GlobalHandlersIntegrations) { this.name = GlobalHandlers.id; @@ -50,43 +44,36 @@ export class GlobalHandlers implements Integration { onunhandledrejection: true, ...options, }; - - this._installFunc = { - onerror: _installGlobalOnErrorHandler, - onunhandledrejection: _installGlobalOnUnhandledRejectionHandler, - }; } /** * @inheritDoc */ public setupOnce(): void { Error.stackTraceLimit = 50; - const options = this._options; - - // We can disable guard-for-in as we construct the options object above + do checks against - // `this._installFunc` for the property. - // eslint-disable-next-line guard-for-in - for (const key in options) { - const installFunc = this._installFunc[key as GlobalHandlersIntegrationsOptionKeys]; - if (installFunc && options[key as GlobalHandlersIntegrationsOptionKeys]) { - globalHandlerLog(key); - installFunc(); - this._installFunc[key as GlobalHandlersIntegrationsOptionKeys] = undefined; - } + } + + /** @inheritdoc */ + public setup(client: Client): void { + if (this._options.onerror) { + _installGlobalOnErrorHandler(client); + globalHandlerLog('onerror'); + } + if (this._options.onunhandledrejection) { + _installGlobalOnUnhandledRejectionHandler(client); + globalHandlerLog('onunhandledrejection'); } } } -function _installGlobalOnErrorHandler(): void { +function _installGlobalOnErrorHandler(client: Client): void { addGlobalErrorInstrumentationHandler(data => { - const [hub, stackParser, attachStacktrace] = getHubAndOptions(); - if (!hub.getIntegration(GlobalHandlers)) { + const { stackParser, attachStacktrace } = getOptions(); + + if (getClient() !== client || shouldIgnoreOnError()) { return; } + const { msg, url, line, column, error } = data; - if (shouldIgnoreOnError()) { - return; - } const event = error === undefined && isString(msg) @@ -100,7 +87,7 @@ function _installGlobalOnErrorHandler(): void { event.level = 'error'; - hub.captureEvent(event, { + captureEvent(event, { originalException: error, mechanism: { handled: false, @@ -110,15 +97,12 @@ function _installGlobalOnErrorHandler(): void { }); } -function _installGlobalOnUnhandledRejectionHandler(): void { +function _installGlobalOnUnhandledRejectionHandler(client: Client): void { addGlobalUnhandledRejectionInstrumentationHandler(e => { - const [hub, stackParser, attachStacktrace] = getHubAndOptions(); - if (!hub.getIntegration(GlobalHandlers)) { - return; - } + const { stackParser, attachStacktrace } = getOptions(); - if (shouldIgnoreOnError()) { - return true; + if (getClient() !== client || shouldIgnoreOnError()) { + return; } const error = _getUnhandledRejectionError(e as unknown); @@ -129,15 +113,13 @@ function _installGlobalOnUnhandledRejectionHandler(): void { event.level = 'error'; - hub.captureEvent(event, { + captureEvent(event, { originalException: error, mechanism: { handled: false, type: 'onunhandledrejection', }, }); - - return; }); } @@ -258,12 +240,11 @@ function globalHandlerLog(type: string): void { DEBUG_BUILD && logger.log(`Global Handler attached: ${type}`); } -function getHubAndOptions(): [Hub, StackParser, boolean | undefined] { - const hub = getCurrentHub(); - const client = hub.getClient(); +function getOptions(): { stackParser: StackParser; attachStacktrace?: boolean } { + const client = getClient(); const options = (client && client.getOptions()) || { stackParser: () => [], attachStacktrace: false, }; - return [hub, options.stackParser, options.attachStacktrace]; + return options; } diff --git a/packages/browser/src/profiling/integration.ts b/packages/browser/src/profiling/integration.ts index 326af29492cf..5173705feaa6 100644 --- a/packages/browser/src/profiling/integration.ts +++ b/packages/browser/src/profiling/integration.ts @@ -1,4 +1,5 @@ -import type { EventEnvelope, EventProcessor, Hub, Integration, Transaction } from '@sentry/types'; +import { getCurrentScope } from '@sentry/core'; +import type { Client, EventEnvelope, EventProcessor, Hub, Integration, Transaction } from '@sentry/types'; import type { Profile } from '@sentry/types/src/profiling'; import { logger } from '@sentry/utils'; @@ -29,6 +30,7 @@ export class BrowserProfilingIntegration implements Integration { public readonly name: string; + /** @deprecated This is never set. */ public getCurrentHub?: () => Hub; public constructor() { @@ -38,12 +40,13 @@ export class BrowserProfilingIntegration implements Integration { /** * @inheritDoc */ - public setupOnce(_addGlobalEventProcessor: (callback: EventProcessor) => void, getCurrentHub: () => Hub): void { - this.getCurrentHub = getCurrentHub; + public setupOnce(_addGlobalEventProcessor: (callback: EventProcessor) => void, _getCurrentHub: () => Hub): void { + // noop + } - const hub = this.getCurrentHub(); - const client = hub.getClient(); - const scope = hub.getScope(); + /** @inheritdoc */ + public setup(client: Client): void { + const scope = getCurrentScope(); const transaction = scope.getTransaction(); @@ -53,67 +56,68 @@ export class BrowserProfilingIntegration implements Integration { } } - if (client && typeof client.on === 'function') { - client.on('startTransaction', (transaction: Transaction) => { - if (shouldProfileTransaction(transaction)) { - startProfileForTransaction(transaction); + if (typeof client.on !== 'function') { + logger.warn('[Profiling] Client does not support hooks, profiling will be disabled'); + return; + } + + client.on('startTransaction', (transaction: Transaction) => { + if (shouldProfileTransaction(transaction)) { + startProfileForTransaction(transaction); + } + }); + + client.on('beforeEnvelope', (envelope): void => { + // if not profiles are in queue, there is nothing to add to the envelope. + if (!getActiveProfilesCount()) { + return; + } + + const profiledTransactionEvents = findProfiledTransactionsFromEnvelope(envelope); + if (!profiledTransactionEvents.length) { + return; + } + + const profilesToAddToEnvelope: Profile[] = []; + + for (const profiledTransaction of profiledTransactionEvents) { + const context = profiledTransaction && profiledTransaction.contexts; + const profile_id = context && context['profile'] && context['profile']['profile_id']; + const start_timestamp = context && context['profile'] && context['profile']['start_timestamp']; + + if (typeof profile_id !== 'string') { + DEBUG_BUILD && logger.log('[Profiling] cannot find profile for a transaction without a profile context'); + continue; } - }); - client.on('beforeEnvelope', (envelope): void => { - // if not profiles are in queue, there is nothing to add to the envelope. - if (!getActiveProfilesCount()) { - return; + if (!profile_id) { + DEBUG_BUILD && logger.log('[Profiling] cannot find profile for a transaction without a profile context'); + continue; } - const profiledTransactionEvents = findProfiledTransactionsFromEnvelope(envelope); - if (!profiledTransactionEvents.length) { - return; + // Remove the profile from the transaction context before sending, relay will take care of the rest. + if (context && context['profile']) { + delete context.profile; } - const profilesToAddToEnvelope: Profile[] = []; - - for (const profiledTransaction of profiledTransactionEvents) { - const context = profiledTransaction && profiledTransaction.contexts; - const profile_id = context && context['profile'] && context['profile']['profile_id']; - const start_timestamp = context && context['profile'] && context['profile']['start_timestamp']; - - if (typeof profile_id !== 'string') { - DEBUG_BUILD && logger.log('[Profiling] cannot find profile for a transaction without a profile context'); - continue; - } - - if (!profile_id) { - DEBUG_BUILD && logger.log('[Profiling] cannot find profile for a transaction without a profile context'); - continue; - } - - // Remove the profile from the transaction context before sending, relay will take care of the rest. - if (context && context['profile']) { - delete context.profile; - } - - const profile = takeProfileFromGlobalCache(profile_id); - if (!profile) { - DEBUG_BUILD && logger.log(`[Profiling] Could not retrieve profile for transaction: ${profile_id}`); - continue; - } - - const profileEvent = createProfilingEvent( - profile_id, - start_timestamp as number | undefined, - profile, - profiledTransaction as ProfiledEvent, - ); - if (profileEvent) { - profilesToAddToEnvelope.push(profileEvent); - } + const profile = takeProfileFromGlobalCache(profile_id); + if (!profile) { + DEBUG_BUILD && logger.log(`[Profiling] Could not retrieve profile for transaction: ${profile_id}`); + continue; } - addProfilesToEnvelope(envelope as EventEnvelope, profilesToAddToEnvelope); - }); - } else { - logger.warn('[Profiling] Client does not support hooks, profiling will be disabled'); - } + const profileEvent = createProfilingEvent( + profile_id, + start_timestamp as number | undefined, + profile, + profiledTransaction as ProfiledEvent, + ); + if (profileEvent) { + profilesToAddToEnvelope.push(profileEvent); + } + } + + addProfilesToEnvelope(envelope as EventEnvelope, profilesToAddToEnvelope); + }); } } diff --git a/packages/nextjs/src/common/wrapGenerationFunctionWithSentry.ts b/packages/nextjs/src/common/wrapGenerationFunctionWithSentry.ts index 80f7d62cc447..3acaa849ff79 100644 --- a/packages/nextjs/src/common/wrapGenerationFunctionWithSentry.ts +++ b/packages/nextjs/src/common/wrapGenerationFunctionWithSentry.ts @@ -2,7 +2,7 @@ import { addTracingExtensions, captureException, continueTrace, - getCurrentHub, + getClient, getCurrentScope, runWithAsyncContext, trace, @@ -34,7 +34,7 @@ export function wrapGenerationFunctionWithSentry a } let data: Record | undefined = undefined; - if (getCurrentHub().getClient()?.getOptions().sendDefaultPii) { + if (getClient()?.getOptions().sendDefaultPii) { const props: unknown = args[0]; const params = props && typeof props === 'object' && 'params' in props ? props.params : undefined; const searchParams =