diff --git a/packages/angular/src/tracing.ts b/packages/angular/src/tracing.ts index 88a2490c1d58..25ddc2fd8dcf 100644 --- a/packages/angular/src/tracing.ts +++ b/packages/angular/src/tracing.ts @@ -7,7 +7,7 @@ import type { ActivatedRouteSnapshot, Event, RouterState } from '@angular/router // eslint-disable-next-line @typescript-eslint/consistent-type-imports import { NavigationCancel, NavigationError, Router } from '@angular/router'; import { NavigationEnd, NavigationStart, ResolveEnd } from '@angular/router'; -import { WINDOW, getCurrentHub } from '@sentry/browser'; +import { WINDOW, getCurrentScope } from '@sentry/browser'; import type { Span, Transaction, TransactionContext } from '@sentry/types'; import { logger, stripUrlQueryAndFragment, timestampInSeconds } from '@sentry/utils'; import type { Observable } from 'rxjs'; @@ -50,14 +50,7 @@ export const instrumentAngularRouting = routingInstrumentation; * Grabs active transaction off scope */ export function getActiveTransaction(): Transaction | undefined { - const currentHub = getCurrentHub(); - - if (currentHub) { - const scope = currentHub.getScope(); - return scope.getTransaction(); - } - - return undefined; + return getCurrentScope().getTransaction(); } /** diff --git a/packages/angular/test/tracing.test.ts b/packages/angular/test/tracing.test.ts index e290850241c8..635c8847b9bf 100644 --- a/packages/angular/test/tracing.test.ts +++ b/packages/angular/test/tracing.test.ts @@ -21,16 +21,12 @@ jest.mock('@sentry/browser', () => { const original = jest.requireActual('@sentry/browser'); return { ...original, - getCurrentHub: () => { + getCurrentScope() { return { - getScope: () => { - return { - getTransaction: () => { - return transaction; - }, - }; + getTransaction: () => { + return transaction; }, - } as unknown as Hub; + }; }, }; }); diff --git a/packages/astro/src/server/meta.ts b/packages/astro/src/server/meta.ts index 4264be2733f5..7f1f544a19e6 100644 --- a/packages/astro/src/server/meta.ts +++ b/packages/astro/src/server/meta.ts @@ -1,5 +1,5 @@ import { getDynamicSamplingContextFromClient } from '@sentry/core'; -import type { Hub, Span } from '@sentry/types'; +import type { Client, Scope, Span } from '@sentry/types'; import { TRACEPARENT_REGEXP, dynamicSamplingContextToSentryBaggageHeader, @@ -22,9 +22,11 @@ import { * * @returns an object with the two serialized tags */ -export function getTracingMetaTags(span: Span | undefined, hub: Hub): { sentryTrace: string; baggage?: string } { - const scope = hub.getScope(); - const client = hub.getClient(); +export function getTracingMetaTags( + span: Span | undefined, + scope: Scope, + client: Client | undefined, +): { sentryTrace: string; baggage?: string } { const { dsc, sampled, traceId } = scope.getPropagationContext(); const transaction = span?.transaction; diff --git a/packages/astro/src/server/middleware.ts b/packages/astro/src/server/middleware.ts index 5e3b2de18622..7b4a02cceddf 100644 --- a/packages/astro/src/server/middleware.ts +++ b/packages/astro/src/server/middleware.ts @@ -1,12 +1,12 @@ import { captureException, continueTrace, - getCurrentHub, + getClient, getCurrentScope, runWithAsyncContext, startSpan, } from '@sentry/node'; -import type { Hub, Span } from '@sentry/types'; +import type { Client, Scope, Span } from '@sentry/types'; import { addNonEnumerableProperty, objectify, stripUrlQueryAndFragment } from '@sentry/utils'; import type { APIContext, MiddlewareResponseHandler } from 'astro'; @@ -69,7 +69,7 @@ export const handleRequest: (options?: MiddlewareOptions) => MiddlewareResponseH // if there is an active span, we know that this handle call is nested and hence // we don't create a new domain for it. If we created one, nested server calls would // create new transactions instead of adding a child span to the currently active span. - if (getCurrentHub().getScope().getSpan()) { + if (getCurrentScope().getSpan()) { return instrumentRequest(ctx, next, handlerOptions); } return runWithAsyncContext(() => { @@ -139,8 +139,8 @@ async function instrumentRequest( span.setHttpStatus(originalResponse.status); } - const hub = getCurrentHub(); - const client = hub.getClient(); + const scope = getCurrentScope(); + const client = getClient(); const contentType = originalResponse.headers.get('content-type'); const isPageloadRequest = contentType && contentType.startsWith('text/html'); @@ -163,7 +163,7 @@ async function instrumentRequest( start: async controller => { for await (const chunk of originalBody) { const html = typeof chunk === 'string' ? chunk : decoder.decode(chunk); - const modifiedHtml = addMetaTagToHead(html, hub, span); + const modifiedHtml = addMetaTagToHead(html, scope, client, span); controller.enqueue(new TextEncoder().encode(modifiedHtml)); } controller.close(); @@ -185,12 +185,12 @@ async function instrumentRequest( * This function optimistically assumes that the HTML coming in chunks will not be split * within the tag. If this still happens, we simply won't replace anything. */ -function addMetaTagToHead(htmlChunk: string, hub: Hub, span?: Span): string { +function addMetaTagToHead(htmlChunk: string, scope: Scope, client: Client, span?: Span): string { if (typeof htmlChunk !== 'string') { return htmlChunk; } - const { sentryTrace, baggage } = getTracingMetaTags(span, hub); + const { sentryTrace, baggage } = getTracingMetaTags(span, scope, client); const content = `\n${sentryTrace}\n${baggage}\n`; return htmlChunk.replace('', content); } diff --git a/packages/astro/test/server/meta.test.ts b/packages/astro/test/server/meta.test.ts index 6298f5f2a20b..279f36395107 100644 --- a/packages/astro/test/server/meta.test.ts +++ b/packages/astro/test/server/meta.test.ts @@ -10,22 +10,20 @@ const mockedSpan = { environment: 'production', }), }, -}; +} as any; -const mockedHub = { - getScope: () => ({ - getPropagationContext: () => ({ - traceId: '123', - }), +const mockedClient = {} as any; + +const mockedScope = { + getPropagationContext: () => ({ + traceId: '123', }), - getClient: () => ({}), -}; +} as any; describe('getTracingMetaTags', () => { it('returns the tracing tags from the span, if it is provided', () => { { - // @ts-expect-error - only passing a partial span object - const tags = getTracingMetaTags(mockedSpan, mockedHub); + const tags = getTracingMetaTags(mockedSpan, mockedScope, mockedClient); expect(tags).toEqual({ sentryTrace: '', @@ -35,10 +33,9 @@ describe('getTracingMetaTags', () => { }); it('returns propagationContext DSC data if no span is available', () => { - const tags = getTracingMetaTags(undefined, { - ...mockedHub, - // @ts-expect-error - only passing a partial scope object - getScope: () => ({ + const tags = getTracingMetaTags( + undefined, + { getPropagationContext: () => ({ traceId: '12345678901234567890123456789012', sampled: true, @@ -49,8 +46,9 @@ describe('getTracingMetaTags', () => { trace_id: '12345678901234567890123456789012', }, }), - }), - }); + } as any, + mockedClient, + ); expect(tags).toEqual({ sentryTrace: expect.stringMatching( @@ -73,7 +71,8 @@ describe('getTracingMetaTags', () => { toTraceparent: () => '12345678901234567890123456789012-1234567890123456-1', transaction: undefined, }, - mockedHub, + mockedScope, + mockedClient, ); expect(tags).toEqual({ @@ -93,10 +92,8 @@ describe('getTracingMetaTags', () => { toTraceparent: () => '12345678901234567890123456789012-1234567890123456-1', transaction: undefined, }, - { - ...mockedHub, - getClient: () => undefined, - }, + mockedScope, + undefined, ); expect(tags).toEqual({ diff --git a/packages/astro/test/server/middleware.test.ts b/packages/astro/test/server/middleware.test.ts index ef81d69214c5..5e56c6bd70ed 100644 --- a/packages/astro/test/server/middleware.test.ts +++ b/packages/astro/test/server/middleware.test.ts @@ -1,5 +1,6 @@ import * as SentryNode from '@sentry/node'; -import { vi } from 'vitest'; +import type { Client } from '@sentry/types'; +import { SpyInstance, vi } from 'vitest'; import { handleRequest, interpolateRouteFromUrlAndParams } from '../../src/server/middleware'; @@ -14,14 +15,17 @@ describe('sentryMiddleware', () => { const startSpanSpy = vi.spyOn(SentryNode, 'startSpan'); const getSpanMock = vi.fn(() => {}); - // @ts-expect-error only returning a partial hub here - vi.spyOn(SentryNode, 'getCurrentHub').mockImplementation(() => { - return { - getScope: () => ({ + const setUserMock = vi.fn(); + + beforeEach(() => { + vi.spyOn(SentryNode, 'getCurrentScope').mockImplementation(() => { + return { + setUser: setUserMock, + setPropagationContext: vi.fn(), getSpan: getSpanMock, - }), - getClient: () => ({}), - }; + } as any; + }); + vi.spyOn(SentryNode, 'getClient').mockImplementation(() => ({}) as Client); }); const nextResult = Promise.resolve(new Response(null, { status: 200, headers: new Headers() })); @@ -170,10 +174,6 @@ describe('sentryMiddleware', () => { }); it('attaches client IP and request headers if options are set', async () => { - const scope = { setUser: vi.fn(), setPropagationContext: vi.fn() }; - // @ts-expect-error, only passing a partial Scope object - const getCurrentScopeSpy = vi.spyOn(SentryNode, 'getCurrentScope').mockImplementation(() => scope); - const middleware = handleRequest({ trackClientIp: true, trackHeaders: true }); const ctx = { request: { @@ -192,8 +192,7 @@ describe('sentryMiddleware', () => { // @ts-expect-error, a partial ctx object is fine here await middleware(ctx, next); - expect(getCurrentScopeSpy).toHaveBeenCalledTimes(1); - expect(scope.setUser).toHaveBeenCalledWith({ ip_address: '192.168.0.1' }); + expect(setUserMock).toHaveBeenCalledWith({ ip_address: '192.168.0.1' }); expect(startSpanSpy).toHaveBeenCalledWith( expect.objectContaining({ diff --git a/packages/browser/src/eventbuilder.ts b/packages/browser/src/eventbuilder.ts index e361f1366cf3..6955fbfa26fe 100644 --- a/packages/browser/src/eventbuilder.ts +++ b/packages/browser/src/eventbuilder.ts @@ -1,4 +1,4 @@ -import { getCurrentHub } from '@sentry/core'; +import { getClient } from '@sentry/core'; import type { Event, EventHint, Exception, Severity, SeverityLevel, StackFrame, StackParser } from '@sentry/types'; import { addExceptionMechanism, @@ -48,8 +48,7 @@ export function eventFromPlainObject( syntheticException?: Error, isUnhandledRejection?: boolean, ): Event { - const hub = getCurrentHub(); - const client = hub.getClient(); + const client = getClient(); const normalizeDepth = client && client.getOptions().normalizeDepth; const event: Event = { diff --git a/packages/browser/src/profiling/utils.ts b/packages/browser/src/profiling/utils.ts index 3edb82e0b539..f2fdc5e4c10d 100644 --- a/packages/browser/src/profiling/utils.ts +++ b/packages/browser/src/profiling/utils.ts @@ -1,6 +1,6 @@ /* eslint-disable max-lines */ -import { DEFAULT_ENVIRONMENT, getClient, getCurrentHub } from '@sentry/core'; +import { DEFAULT_ENVIRONMENT, getClient } from '@sentry/core'; import type { DebugImage, Envelope, Event, EventEnvelope, StackFrame, StackParser, Transaction } from '@sentry/types'; import type { Profile, ThreadCpuProfile } from '@sentry/types/src/profiling'; import { GLOBAL_OBJ, browserPerformanceTimeOrigin, forEachEnvelopeItem, logger, uuid4 } from '@sentry/utils'; @@ -347,19 +347,10 @@ export function applyDebugMetadata(resource_paths: ReadonlyArray): Debug return []; } - const hub = getCurrentHub(); - if (!hub) { - return []; - } - const client = hub.getClient(); - if (!client) { - return []; - } - const options = client.getOptions(); - if (!options) { - return []; - } - const stackParser = options.stackParser; + const client = getClient(); + const options = client && client.getOptions(); + const stackParser = options && options.stackParser; + if (!stackParser) { return []; } diff --git a/packages/core/src/baseclient.ts b/packages/core/src/baseclient.ts index a6e0575a2e67..c5d9fff7df6f 100644 --- a/packages/core/src/baseclient.ts +++ b/packages/core/src/baseclient.ts @@ -48,7 +48,7 @@ import { import { getEnvelopeEndpointWithUrlEncodedAuth } from './api'; import { DEBUG_BUILD } from './debug-build'; import { createEventEnvelope, createSessionEnvelope } from './envelope'; -import { getCurrentHub } from './hub'; +import { getClient } from './exports'; import type { IntegrationIndex } from './integration'; import { setupIntegration, setupIntegrations } from './integration'; import { createMetricEnvelope } from './metrics/envelope'; @@ -870,7 +870,7 @@ function isTransactionEvent(event: Event): event is TransactionEvent { * This event processor will run for all events processed by this client. */ export function addEventProcessor(callback: EventProcessor): void { - const client = getCurrentHub().getClient(); + const client = getClient(); if (!client || !client.addEventProcessor) { return; diff --git a/packages/core/src/exports.ts b/packages/core/src/exports.ts index 6f71e7dfbccb..0e574c4853cc 100644 --- a/packages/core/src/exports.ts +++ b/packages/core/src/exports.ts @@ -202,9 +202,8 @@ export function startTransaction( * to create a monitor automatically when sending a check in. */ export function captureCheckIn(checkIn: CheckIn, upsertMonitorConfig?: MonitorConfig): string { - const hub = getCurrentHub(); - const scope = hub.getScope(); - const client = hub.getClient(); + const scope = getCurrentScope(); + const client = getClient(); if (!client) { DEBUG_BUILD && logger.warn('Cannot capture check-in. No client defined.'); } else if (!client.captureCheckIn) { diff --git a/packages/core/src/metrics/exports.ts b/packages/core/src/metrics/exports.ts index c27e76cf79b1..22a5e83ffb3d 100644 --- a/packages/core/src/metrics/exports.ts +++ b/packages/core/src/metrics/exports.ts @@ -2,7 +2,7 @@ import type { ClientOptions, MeasurementUnit, Primitive } from '@sentry/types'; import { logger } from '@sentry/utils'; import type { BaseClient } from '../baseclient'; import { DEBUG_BUILD } from '../debug-build'; -import { getCurrentHub } from '../hub'; +import { getClient, getCurrentScope } from '../exports'; import { COUNTER_METRIC_TYPE, DISTRIBUTION_METRIC_TYPE, GAUGE_METRIC_TYPE, SET_METRIC_TYPE } from './constants'; import { MetricsAggregator } from './integration'; import type { MetricType } from './types'; @@ -19,9 +19,8 @@ function addToMetricsAggregator( value: number | string, data: MetricData = {}, ): void { - const hub = getCurrentHub(); - const client = hub.getClient() as BaseClient; - const scope = hub.getScope(); + const client = getClient>(); + const scope = getCurrentScope(); if (client) { if (!client.metricsAggregator) { DEBUG_BUILD && diff --git a/packages/core/src/sessionflusher.ts b/packages/core/src/sessionflusher.ts index 0b0bc8455480..dac81b82336d 100644 --- a/packages/core/src/sessionflusher.ts +++ b/packages/core/src/sessionflusher.ts @@ -6,8 +6,7 @@ import type { SessionFlusherLike, } from '@sentry/types'; import { dropUndefinedKeys } from '@sentry/utils'; - -import { getCurrentHub } from './hub'; +import { getCurrentScope } from './exports'; type ReleaseHealthAttributes = { environment?: string; @@ -75,7 +74,7 @@ export class SessionFlusher implements SessionFlusherLike { if (!this._isEnabled) { return; } - const scope = getCurrentHub().getScope(); + const scope = getCurrentScope(); const requestSession = scope.getRequestSession(); if (requestSession && requestSession.status) { diff --git a/packages/core/src/tracing/trace.ts b/packages/core/src/tracing/trace.ts index 667bedaaef6c..f8c8f5d6cbe3 100644 --- a/packages/core/src/tracing/trace.ts +++ b/packages/core/src/tracing/trace.ts @@ -2,6 +2,7 @@ import type { TransactionContext } from '@sentry/types'; import { dropUndefinedKeys, isThenable, logger, tracingContextFromHeaders } from '@sentry/utils'; import { DEBUG_BUILD } from '../debug-build'; +import { getCurrentScope } from '../exports'; import type { Hub } from '../hub'; import { getCurrentHub } from '../hub'; import { hasTracingEnabled } from '../utils/hasTracingEnabled'; @@ -28,7 +29,7 @@ export function trace( const ctx = normalizeContext(context); const hub = getCurrentHub(); - const scope = hub.getScope(); + const scope = getCurrentScope(); const parentSpan = scope.getSpan(); const activeSpan = createChildSpanOrTransaction(hub, parentSpan, ctx); @@ -37,7 +38,7 @@ export function trace( function finishAndSetSpan(): void { activeSpan && activeSpan.finish(); - hub.getScope().setSpan(parentSpan); + scope.setSpan(parentSpan); } let maybePromiseResult: T; @@ -83,7 +84,7 @@ export function startSpan(context: TransactionContext, callback: (span: Span const ctx = normalizeContext(context); const hub = getCurrentHub(); - const scope = hub.getScope(); + const scope = getCurrentScope(); const parentSpan = scope.getSpan(); const activeSpan = createChildSpanOrTransaction(hub, parentSpan, ctx); @@ -91,7 +92,7 @@ export function startSpan(context: TransactionContext, callback: (span: Span function finishAndSetSpan(): void { activeSpan && activeSpan.finish(); - hub.getScope().setSpan(parentSpan); + scope.setSpan(parentSpan); } let maybePromiseResult: T; @@ -143,7 +144,7 @@ export function startSpanManual( const ctx = normalizeContext(context); const hub = getCurrentHub(); - const scope = hub.getScope(); + const scope = getCurrentScope(); const parentSpan = scope.getSpan(); const activeSpan = createChildSpanOrTransaction(hub, parentSpan, ctx); @@ -151,7 +152,7 @@ export function startSpanManual( function finishAndSetSpan(): void { activeSpan && activeSpan.finish(); - hub.getScope().setSpan(parentSpan); + scope.setSpan(parentSpan); } let maybePromiseResult: T; @@ -201,7 +202,7 @@ export function startInactiveSpan(context: TransactionContext): Span | undefined * Returns the currently active span. */ export function getActiveSpan(): Span | undefined { - return getCurrentHub().getScope().getSpan(); + return getCurrentScope().getSpan(); } export function continueTrace({ @@ -238,8 +239,7 @@ export function continueTrace( }, callback?: (transactionContext: Partial) => V, ): V | Partial { - const hub = getCurrentHub(); - const currentScope = hub.getScope(); + const currentScope = getCurrentScope(); const { traceparentData, dynamicSamplingContext, propagationContext } = tracingContextFromHeaders( sentryTrace, diff --git a/packages/e2e-tests/test-applications/node-express-app/src/app.ts b/packages/e2e-tests/test-applications/node-express-app/src/app.ts index fd83fcdfa23a..330a425cb494 100644 --- a/packages/e2e-tests/test-applications/node-express-app/src/app.ts +++ b/packages/e2e-tests/test-applications/node-express-app/src/app.ts @@ -35,7 +35,7 @@ app.get('/test-param/:param', function (req, res) { app.get('/test-transaction', async function (req, res) { const transaction = Sentry.startTransaction({ name: 'test-transaction', op: 'e2e-test' }); - Sentry.getCurrentHub().getScope().setSpan(transaction); + Sentry.getCurrentScope().setSpan(transaction); const span = transaction.startChild(); diff --git a/packages/feedback/src/util/sendFeedbackRequest.ts b/packages/feedback/src/util/sendFeedbackRequest.ts index b8ec16a15401..5e8e532ca58d 100644 --- a/packages/feedback/src/util/sendFeedbackRequest.ts +++ b/packages/feedback/src/util/sendFeedbackRequest.ts @@ -1,4 +1,4 @@ -import { createEventEnvelope, getCurrentHub } from '@sentry/core'; +import { createEventEnvelope, getClient, withScope } from '@sentry/core'; import type { FeedbackEvent, TransportMakeRequestResponse } from '@sentry/types'; import { FEEDBACK_API_SOURCE, FEEDBACK_WIDGET_SOURCE } from '../constants'; @@ -12,8 +12,7 @@ export async function sendFeedbackRequest( { feedback: { message, email, name, source, url } }: SendFeedbackData, { includeReplay = true }: SendFeedbackOptions = {}, ): Promise { - const hub = getCurrentHub(); - const client = hub.getClient(); + const client = getClient(); const transport = client && client.getTransport(); const dsn = client && client.getDsn(); @@ -35,7 +34,7 @@ export async function sendFeedbackRequest( }; return new Promise((resolve, reject) => { - hub.withScope(async scope => { + withScope(async scope => { // No use for breadcrumbs in feedback scope.clearBreadcrumbs(); @@ -49,12 +48,12 @@ export async function sendFeedbackRequest( event: baseEvent, }); - if (feedbackEvent === null) { + if (!feedbackEvent) { resolve(); return; } - if (client && client.emit) { + if (client.emit) { client.emit('beforeSendFeedback', feedbackEvent, { includeReplay: Boolean(includeReplay) }); } diff --git a/packages/feedback/src/widget/createWidget.ts b/packages/feedback/src/widget/createWidget.ts index 35f9fcf51f71..b5e414803121 100644 --- a/packages/feedback/src/widget/createWidget.ts +++ b/packages/feedback/src/widget/createWidget.ts @@ -1,4 +1,4 @@ -import { getCurrentHub } from '@sentry/core'; +import { getCurrentScope } from '@sentry/core'; import { logger } from '@sentry/utils'; import type { FeedbackFormData, FeedbackInternalOptions, FeedbackWidget } from '../types'; @@ -160,7 +160,7 @@ export function createWidget({ } const userKey = options.useSentryUser; - const scope = getCurrentHub().getScope(); + const scope = getCurrentScope(); const user = scope && scope.getUser(); dialog = Dialog({ diff --git a/packages/nextjs/src/client/routing/pagesRouterRoutingInstrumentation.ts b/packages/nextjs/src/client/routing/pagesRouterRoutingInstrumentation.ts index 91929f885ae0..a7c3d5bd2344 100644 --- a/packages/nextjs/src/client/routing/pagesRouterRoutingInstrumentation.ts +++ b/packages/nextjs/src/client/routing/pagesRouterRoutingInstrumentation.ts @@ -1,5 +1,5 @@ import type { ParsedUrlQuery } from 'querystring'; -import { getClient, getCurrentHub } from '@sentry/core'; +import { getClient, getCurrentScope } from '@sentry/core'; import { WINDOW } from '@sentry/react'; import type { Primitive, Transaction, TransactionContext, TransactionSource } from '@sentry/types'; import { @@ -124,7 +124,7 @@ export function pagesRouterInstrumentation( baggage, ); - getCurrentHub().getScope().setPropagationContext(propagationContext); + getCurrentScope().setPropagationContext(propagationContext); prevLocationName = route || globalObject.location.pathname; if (startTransactionOnPageLoad) { diff --git a/packages/nextjs/src/common/utils/edgeWrapperUtils.ts b/packages/nextjs/src/common/utils/edgeWrapperUtils.ts index 008f8629f3ab..afdf686499c5 100644 --- a/packages/nextjs/src/common/utils/edgeWrapperUtils.ts +++ b/packages/nextjs/src/common/utils/edgeWrapperUtils.ts @@ -1,4 +1,4 @@ -import { addTracingExtensions, captureException, flush, getCurrentHub, startTransaction } from '@sentry/core'; +import { addTracingExtensions, captureException, getCurrentScope, startTransaction } from '@sentry/core'; import type { Span } from '@sentry/types'; import { addExceptionMechanism, @@ -23,7 +23,7 @@ export function withEdgeWrapping( addTracingExtensions(); const req = args[0]; - const currentScope = getCurrentHub().getScope(); + const currentScope = getCurrentScope(); const prevSpan = currentScope.getSpan(); let span: Span | undefined; diff --git a/packages/nextjs/src/common/utils/wrapperUtils.ts b/packages/nextjs/src/common/utils/wrapperUtils.ts index fedb5ba6f3ff..5451b1264723 100644 --- a/packages/nextjs/src/common/utils/wrapperUtils.ts +++ b/packages/nextjs/src/common/utils/wrapperUtils.ts @@ -2,7 +2,7 @@ import type { IncomingMessage, ServerResponse } from 'http'; import { captureException, getActiveTransaction, - getCurrentHub, + getCurrentScope, runWithAsyncContext, startTransaction, } from '@sentry/core'; @@ -84,8 +84,7 @@ export function withTracedServerSideDataFetcher Pr ): (...params: Parameters) => Promise> { return async function (this: unknown, ...args: Parameters): Promise> { return runWithAsyncContext(async () => { - const hub = getCurrentHub(); - const scope = hub.getScope(); + const scope = getCurrentScope(); const previousSpan: Span | undefined = getTransactionFromRequest(req) ?? scope.getSpan(); let dataFetcherSpan; diff --git a/packages/nextjs/src/common/withServerActionInstrumentation.ts b/packages/nextjs/src/common/withServerActionInstrumentation.ts index eafcff7b9075..d87429ad528c 100644 --- a/packages/nextjs/src/common/withServerActionInstrumentation.ts +++ b/packages/nextjs/src/common/withServerActionInstrumentation.ts @@ -1,4 +1,11 @@ -import { addTracingExtensions, captureException, flush, getCurrentHub, runWithAsyncContext, trace } from '@sentry/core'; +import { + addTracingExtensions, + captureException, + getClient, + getCurrentScope, + runWithAsyncContext, + trace, +} from '@sentry/core'; import { logger, tracingContextFromHeaders } from '@sentry/utils'; import { DEBUG_BUILD } from './debug-build'; @@ -49,8 +56,7 @@ async function withServerActionInstrumentationImplementation> { addTracingExtensions(); return runWithAsyncContext(async () => { - const hub = getCurrentHub(); - const sendDefaultPii = hub.getClient()?.getOptions().sendDefaultPii; + const sendDefaultPii = getClient()?.getOptions().sendDefaultPii; let sentryTraceHeader; let baggageHeader; @@ -68,7 +74,7 @@ async function withServerActionInstrumentationImplementation { - const hub = getCurrentHub(); let transaction: Transaction | undefined; - const currentScope = hub.getScope(); - const options = hub.getClient()?.getOptions(); + const currentScope = getCurrentScope(); + const options = getClient()?.getOptions(); currentScope.setSDKProcessingMetadata({ request: req }); diff --git a/packages/nextjs/src/common/wrapAppGetInitialPropsWithSentry.ts b/packages/nextjs/src/common/wrapAppGetInitialPropsWithSentry.ts index 1974cf6c5a13..eddf7f4e25e4 100644 --- a/packages/nextjs/src/common/wrapAppGetInitialPropsWithSentry.ts +++ b/packages/nextjs/src/common/wrapAppGetInitialPropsWithSentry.ts @@ -1,4 +1,4 @@ -import { addTracingExtensions, getCurrentHub } from '@sentry/core'; +import { addTracingExtensions, getClient, getCurrentScope } from '@sentry/core'; import { dynamicSamplingContextToSentryBaggageHeader } from '@sentry/utils'; import type App from 'next/app'; @@ -32,8 +32,7 @@ export function wrapAppGetInitialPropsWithSentry(origAppGetInitialProps: AppGetI const { req, res } = context.ctx; const errorWrappedAppGetInitialProps = withErrorInstrumentation(wrappingTarget); - const hub = getCurrentHub(); - const options = hub.getClient()?.getOptions(); + const options = getClient()?.getOptions(); // Generally we can assume that `req` and `res` are always defined on the server: // https://nextjs.org/docs/api-reference/data-fetching/get-initial-props#context-object @@ -53,7 +52,7 @@ export function wrapAppGetInitialPropsWithSentry(origAppGetInitialProps: AppGetI }; } = await tracedGetInitialProps.apply(thisArg, args); - const requestTransaction = getTransactionFromRequest(req) ?? hub.getScope().getTransaction(); + const requestTransaction = getTransactionFromRequest(req) ?? getCurrentScope().getTransaction(); // Per definition, `pageProps` is not optional, however an increased amount of users doesn't seem to call // `App.getInitialProps(appContext)` in their custom `_app` pages which is required as per diff --git a/packages/nextjs/src/common/wrapErrorGetInitialPropsWithSentry.ts b/packages/nextjs/src/common/wrapErrorGetInitialPropsWithSentry.ts index a6444a5e3d60..0e4601886cee 100644 --- a/packages/nextjs/src/common/wrapErrorGetInitialPropsWithSentry.ts +++ b/packages/nextjs/src/common/wrapErrorGetInitialPropsWithSentry.ts @@ -1,4 +1,4 @@ -import { addTracingExtensions, getCurrentHub } from '@sentry/core'; +import { addTracingExtensions, getClient, getCurrentScope } from '@sentry/core'; import { dynamicSamplingContextToSentryBaggageHeader } from '@sentry/utils'; import type { NextPageContext } from 'next'; import type { ErrorProps } from 'next/error'; @@ -35,8 +35,7 @@ export function wrapErrorGetInitialPropsWithSentry( const { req, res } = context; const errorWrappedGetInitialProps = withErrorInstrumentation(wrappingTarget); - const hub = getCurrentHub(); - const options = hub.getClient()?.getOptions(); + const options = getClient()?.getOptions(); // Generally we can assume that `req` and `res` are always defined on the server: // https://nextjs.org/docs/api-reference/data-fetching/get-initial-props#context-object @@ -54,7 +53,7 @@ export function wrapErrorGetInitialPropsWithSentry( _sentryBaggage?: string; } = await tracedGetInitialProps.apply(thisArg, args); - const requestTransaction = getTransactionFromRequest(req) ?? hub.getScope().getTransaction(); + const requestTransaction = getTransactionFromRequest(req) ?? getCurrentScope().getTransaction(); if (requestTransaction) { errorGetInitialProps._sentryTraceData = requestTransaction.toTraceparent(); diff --git a/packages/nextjs/src/common/wrapGetInitialPropsWithSentry.ts b/packages/nextjs/src/common/wrapGetInitialPropsWithSentry.ts index 594ef451c385..510cbae5684c 100644 --- a/packages/nextjs/src/common/wrapGetInitialPropsWithSentry.ts +++ b/packages/nextjs/src/common/wrapGetInitialPropsWithSentry.ts @@ -1,4 +1,4 @@ -import { addTracingExtensions, getCurrentHub } from '@sentry/core'; +import { addTracingExtensions, getClient, getCurrentScope } from '@sentry/core'; import { dynamicSamplingContextToSentryBaggageHeader } from '@sentry/utils'; import type { NextPage } from 'next'; @@ -31,8 +31,7 @@ export function wrapGetInitialPropsWithSentry(origGetInitialProps: GetInitialPro const { req, res } = context; const errorWrappedGetInitialProps = withErrorInstrumentation(wrappingTarget); - const hub = getCurrentHub(); - const options = hub.getClient()?.getOptions(); + const options = getClient()?.getOptions(); // Generally we can assume that `req` and `res` are always defined on the server: // https://nextjs.org/docs/api-reference/data-fetching/get-initial-props#context-object @@ -50,7 +49,7 @@ export function wrapGetInitialPropsWithSentry(origGetInitialProps: GetInitialPro _sentryBaggage?: string; } = (await tracedGetInitialProps.apply(thisArg, args)) ?? {}; // Next.js allows undefined to be returned from a getInitialPropsFunction. - const requestTransaction = getTransactionFromRequest(req) ?? hub.getScope().getTransaction(); + const requestTransaction = getTransactionFromRequest(req) ?? getCurrentScope().getTransaction(); if (requestTransaction) { initialProps._sentryTraceData = requestTransaction.toTraceparent(); diff --git a/packages/nextjs/src/common/wrapGetServerSidePropsWithSentry.ts b/packages/nextjs/src/common/wrapGetServerSidePropsWithSentry.ts index 1e10518245b9..f93c7193418e 100644 --- a/packages/nextjs/src/common/wrapGetServerSidePropsWithSentry.ts +++ b/packages/nextjs/src/common/wrapGetServerSidePropsWithSentry.ts @@ -1,4 +1,4 @@ -import { addTracingExtensions, getCurrentHub } from '@sentry/core'; +import { addTracingExtensions, getClient, getCurrentScope } from '@sentry/core'; import { dynamicSamplingContextToSentryBaggageHeader } from '@sentry/utils'; import type { GetServerSideProps } from 'next'; @@ -32,8 +32,7 @@ export function wrapGetServerSidePropsWithSentry( const { req, res } = context; const errorWrappedGetServerSideProps = withErrorInstrumentation(wrappingTarget); - const hub = getCurrentHub(); - const options = hub.getClient()?.getOptions(); + const options = getClient()?.getOptions(); if (options?.instrumenter === 'sentry') { const tracedGetServerSideProps = withTracedServerSideDataFetcher(errorWrappedGetServerSideProps, req, res, { @@ -47,7 +46,7 @@ export function wrapGetServerSidePropsWithSentry( >); if (serverSideProps && 'props' in serverSideProps) { - const requestTransaction = getTransactionFromRequest(req) ?? hub.getScope().getTransaction(); + const requestTransaction = getTransactionFromRequest(req) ?? getCurrentScope().getTransaction(); if (requestTransaction) { serverSideProps.props._sentryTraceData = requestTransaction.toTraceparent(); diff --git a/packages/nextjs/src/common/wrapRouteHandlerWithSentry.ts b/packages/nextjs/src/common/wrapRouteHandlerWithSentry.ts index 79eaa78e3dff..1f294283c7d8 100644 --- a/packages/nextjs/src/common/wrapRouteHandlerWithSentry.ts +++ b/packages/nextjs/src/common/wrapRouteHandlerWithSentry.ts @@ -1,4 +1,4 @@ -import { addTracingExtensions, captureException, flush, getCurrentHub, runWithAsyncContext, trace } from '@sentry/core'; +import { addTracingExtensions, captureException, getCurrentScope, runWithAsyncContext, trace } from '@sentry/core'; import { tracingContextFromHeaders, winterCGHeadersToDict } from '@sentry/utils'; import { isRedirectNavigationError } from './nextNavigationErrorUtils'; @@ -20,14 +20,11 @@ export function wrapRouteHandlerWithSentry any>( return new Proxy(routeHandler, { apply: (originalFunction, thisArg, args) => { return runWithAsyncContext(async () => { - const hub = getCurrentHub(); - const currentScope = hub.getScope(); - const { traceparentData, dynamicSamplingContext, propagationContext } = tracingContextFromHeaders( sentryTraceHeader ?? headers?.get('sentry-trace') ?? undefined, baggageHeader ?? headers?.get('baggage'), ); - currentScope.setPropagationContext(propagationContext); + getCurrentScope().setPropagationContext(propagationContext); let res; try { diff --git a/packages/nextjs/src/common/wrapServerComponentWithSentry.ts b/packages/nextjs/src/common/wrapServerComponentWithSentry.ts index dade931bf074..9addbce2d589 100644 --- a/packages/nextjs/src/common/wrapServerComponentWithSentry.ts +++ b/packages/nextjs/src/common/wrapServerComponentWithSentry.ts @@ -1,7 +1,7 @@ import { addTracingExtensions, captureException, - getCurrentHub, + getCurrentScope, runWithAsyncContext, startTransaction, } from '@sentry/core'; @@ -28,9 +28,7 @@ export function wrapServerComponentWithSentry any> return new Proxy(appDirComponent, { apply: (originalFunction, thisArg, args) => { return runWithAsyncContext(() => { - const hub = getCurrentHub(); - const currentScope = hub.getScope(); - + const currentScope = getCurrentScope(); let maybePromiseResult; const completeHeadersDict: Record = context.headers diff --git a/packages/nextjs/src/edge/wrapApiHandlerWithSentry.ts b/packages/nextjs/src/edge/wrapApiHandlerWithSentry.ts index 46691b3cdce5..5a9398319ae2 100644 --- a/packages/nextjs/src/edge/wrapApiHandlerWithSentry.ts +++ b/packages/nextjs/src/edge/wrapApiHandlerWithSentry.ts @@ -1,4 +1,4 @@ -import { getCurrentHub } from '@sentry/core'; +import { getCurrentHub, getCurrentScope } from '@sentry/core'; import { withEdgeWrapping } from '../common/utils/edgeWrapperUtils'; import type { EdgeRouteHandler } from './types'; @@ -14,7 +14,7 @@ export function wrapApiHandlerWithSentry( apply: (wrappingTarget, thisArg, args: Parameters) => { const req = args[0]; - const activeSpan = getCurrentHub().getScope().getSpan(); + const activeSpan = getCurrentScope().getSpan(); const wrappedHandler = withEdgeWrapping(wrappingTarget, { spanDescription: diff --git a/packages/nextjs/src/server/index.ts b/packages/nextjs/src/server/index.ts index a7549506ae14..f2146a2cba38 100644 --- a/packages/nextjs/src/server/index.ts +++ b/packages/nextjs/src/server/index.ts @@ -1,8 +1,8 @@ import * as path from 'path'; -import { addTracingExtensions } from '@sentry/core'; +import { addTracingExtensions, getClient } from '@sentry/core'; import { RewriteFrames } from '@sentry/integrations'; import type { NodeOptions } from '@sentry/node'; -import { Integrations, getCurrentHub, getCurrentScope, init as nodeInit } from '@sentry/node'; +import { Integrations, getCurrentScope, init as nodeInit } from '@sentry/node'; import type { EventProcessor } from '@sentry/types'; import type { IntegrationWithExclusionOption } from '@sentry/utils'; import { addOrUpdateIntegration, escapeStringForRegex, logger } from '@sentry/utils'; @@ -117,8 +117,7 @@ export function init(options: NodeOptions): void { } function sdkAlreadyInitialized(): boolean { - const hub = getCurrentHub(); - return !!hub.getClient(); + return !!getClient(); } function addServerIntegrations(options: NodeOptions): void { diff --git a/packages/nextjs/test/config/wrappers.test.ts b/packages/nextjs/test/config/wrappers.test.ts index b6d29d5ecff2..95b003e4e14d 100644 --- a/packages/nextjs/test/config/wrappers.test.ts +++ b/packages/nextjs/test/config/wrappers.test.ts @@ -2,10 +2,10 @@ import type { IncomingMessage, ServerResponse } from 'http'; import * as SentryCore from '@sentry/core'; import { addTracingExtensions } from '@sentry/core'; +import type { Client } from '@sentry/types'; import { wrapGetInitialPropsWithSentry, wrapGetServerSidePropsWithSentry } from '../../src/common'; const startTransactionSpy = jest.spyOn(SentryCore, 'startTransaction'); -const originalGetCurrentHub = jest.requireActual('@sentry/node').getCurrentHub; // The wrap* functions require the hub to have tracing extensions. This is normally called by the NodeClient // constructor but the client isn't used in these tests. @@ -22,16 +22,11 @@ describe('data-fetching function wrappers', () => { res = { end: jest.fn() } as unknown as ServerResponse; jest.spyOn(SentryCore, 'hasTracingEnabled').mockReturnValue(true); - jest.spyOn(SentryCore, 'getCurrentHub').mockImplementation(() => { - const hub = originalGetCurrentHub(); - - hub.getClient = () => - ({ - getOptions: () => ({ instrumenter: 'sentry' }), - getDsn: () => {}, - }) as any; - - return hub; + jest.spyOn(SentryCore, 'getClient').mockImplementation(() => { + return { + getOptions: () => ({ instrumenter: 'sentry' }), + getDsn: () => {}, + } as Client; }); }); diff --git a/packages/node/src/anr/index.ts b/packages/node/src/anr/index.ts index 84a3ebe52920..32117f21372b 100644 --- a/packages/node/src/anr/index.ts +++ b/packages/node/src/anr/index.ts @@ -1,9 +1,9 @@ import { spawn } from 'child_process'; -import { getClient, makeSession, updateSession } from '@sentry/core'; +import { getClient, getCurrentScope, makeSession, updateSession } from '@sentry/core'; import type { Event, Session, StackFrame } from '@sentry/types'; import { logger, watchdogTimer } from '@sentry/utils'; -import { addEventProcessor, captureEvent, flush, getCurrentHub } from '..'; +import { addEventProcessor, captureEvent, flush } from '..'; import { captureStackTrace } from './debugger'; const DEFAULT_INTERVAL = 50; @@ -91,8 +91,6 @@ function startChildProcess(options: Options): void { logger.log(`[ANR] ${message}`, ...args); } - const hub = getCurrentHub(); - try { const env = { ...process.env }; env.SENTRY_ANR_CHILD_PROCESS = 'true'; @@ -112,7 +110,7 @@ function startChildProcess(options: Options): void { const timer = setInterval(() => { try { - const currentSession = hub.getScope()?.getSession(); + const currentSession = getCurrentScope()?.getSession(); // We need to copy the session object and remove the toJSON method so it can be sent to the child process // serialized without making it a SerializedSession const session = currentSession ? { ...currentSession, toJSON: undefined } : undefined; @@ -126,7 +124,7 @@ function startChildProcess(options: Options): void { child.on('message', (msg: string) => { if (msg === 'session-ended') { log('ANR event sent from child process. Clearing session in this process.'); - hub.getScope()?.setSession(undefined); + getCurrentScope()?.setSession(undefined); } }); diff --git a/packages/node/src/handlers.ts b/packages/node/src/handlers.ts index 35f1a90190c3..832d87139f83 100644 --- a/packages/node/src/handlers.ts +++ b/packages/node/src/handlers.ts @@ -5,7 +5,7 @@ import { continueTrace, flush, getClient, - getCurrentHub, + getCurrentScope, hasTracingEnabled, runWithAsyncContext, startTransaction, @@ -44,8 +44,7 @@ export function tracingHandler(): ( res: http.ServerResponse, next: (error?: any) => void, ): void { - const hub = getCurrentHub(); - const options = hub.getClient()?.getOptions(); + const options = getClient()?.getOptions(); if ( !options || @@ -86,7 +85,7 @@ export function tracingHandler(): ( ); // We put the transaction on the scope so users can attach children to it - hub.getScope().setSpan(transaction); + getCurrentScope().setSpan(transaction); // We also set __sentry_transaction on the response so people can grab the transaction there to add // spans to it later. @@ -149,15 +148,14 @@ export function requestHandler( // TODO (v8): Get rid of this const requestDataOptions = convertReqHandlerOptsToAddReqDataOpts(options); - const currentHub = getCurrentHub(); - const client = currentHub.getClient(); + const client = getClient(); // Initialise an instance of SessionFlusher on the client when `autoSessionTracking` is enabled and the // `requestHandler` middleware is used indicating that we are running in SessionAggregates mode if (client && isAutoSessionTrackingEnabled(client)) { client.initSessionFlusher(); // If Scope contains a Single mode Session, it is removed in favor of using Session Aggregates mode - const scope = currentHub.getScope(); + const scope = getCurrentScope(); if (scope.getSession()) { scope.setSession(); } @@ -183,22 +181,21 @@ export function requestHandler( }; } runWithAsyncContext(() => { - const currentHub = getCurrentHub(); - const scope = currentHub.getScope(); + const scope = getCurrentScope(); scope.setSDKProcessingMetadata({ request: req, // TODO (v8): Stop passing this requestDataOptionsFromExpressHandler: requestDataOptions, }); - const client = currentHub.getClient(); + const client = getClient(); if (isAutoSessionTrackingEnabled(client)) { // Set `status` of `RequestSession` to Ok, at the beginning of the request scope.setRequestSession({ status: 'ok' }); } res.once('finish', () => { - const client = currentHub.getClient(); + const client = getClient(); if (isAutoSessionTrackingEnabled(client)) { setImmediate(() => { // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access @@ -328,9 +325,8 @@ interface TrpcMiddlewareArguments { */ export function trpcMiddleware(options: SentryTrpcMiddlewareOptions = {}) { return function ({ path, type, next, rawInput }: TrpcMiddlewareArguments): T { - const hub = getCurrentHub(); - const clientOptions = hub.getClient()?.getOptions(); - const sentryTransaction = hub.getScope().getTransaction(); + const clientOptions = getClient()?.getOptions(); + const sentryTransaction = getCurrentScope().getTransaction(); if (sentryTransaction) { sentryTransaction.setName(`trpc/${path}`, 'route'); diff --git a/packages/node/src/integrations/http.ts b/packages/node/src/integrations/http.ts index 407343a96770..b61d34574457 100644 --- a/packages/node/src/integrations/http.ts +++ b/packages/node/src/integrations/http.ts @@ -1,6 +1,7 @@ import type * as http from 'http'; import type * as https from 'https'; import type { Hub } from '@sentry/core'; +import { getClient, getCurrentScope } from '@sentry/core'; import { getCurrentHub, getDynamicSamplingContextFromClient, isSentryRequestUrl } from '@sentry/core'; import type { DynamicSamplingContext, @@ -243,8 +244,7 @@ function _createWrappedRequestMethodFactory( return originalRequestMethod.apply(httpModule, requestArgs); } - const hub = getCurrentHub(); - const scope = hub.getScope(); + const scope = getCurrentScope(); const parentSpan = scope.getSpan(); const data = getRequestSpanData(requestUrl, requestOptions); @@ -264,7 +264,7 @@ function _createWrappedRequestMethodFactory( const dynamicSamplingContext = requestSpan?.transaction?.getDynamicSamplingContext(); addHeadersToRequestOptions(requestOptions, requestUrl, sentryTraceHeader, dynamicSamplingContext); } else { - const client = hub.getClient(); + const client = getClient(); const { traceId, sampled, dsc } = scope.getPropagationContext(); const sentryTraceHeader = generateSentryTraceHeader(traceId, undefined, sampled); const dynamicSamplingContext = diff --git a/packages/node/src/integrations/undici/index.ts b/packages/node/src/integrations/undici/index.ts index 50e8dd0b30fb..7681a26ba7ca 100644 --- a/packages/node/src/integrations/undici/index.ts +++ b/packages/node/src/integrations/undici/index.ts @@ -1,4 +1,4 @@ -import { getCurrentHub, getDynamicSamplingContextFromClient, isSentryRequestUrl } from '@sentry/core'; +import { getCurrentHub, getCurrentScope, getDynamicSamplingContextFromClient, isSentryRequestUrl } from '@sentry/core'; import type { EventProcessor, Integration, Span } from '@sentry/types'; import { LRUMap, @@ -147,7 +147,7 @@ export class Undici implements Integration { } const clientOptions = client.getOptions(); - const scope = hub.getScope(); + const scope = getCurrentScope(); const parentSpan = scope.getSpan(); diff --git a/packages/node/src/sdk.ts b/packages/node/src/sdk.ts index 5ef42128d9ab..07fd3f8b024a 100644 --- a/packages/node/src/sdk.ts +++ b/packages/node/src/sdk.ts @@ -1,7 +1,9 @@ /* eslint-disable max-lines */ import { Integrations as CoreIntegrations, + getClient, getCurrentHub, + getCurrentScope, getIntegrationsToSetup, getMainCarrier, initAndBind, @@ -182,7 +184,7 @@ export function init(options: NodeOptions = {}): void { updateScopeFromEnvVariables(); if (options.spotlight) { - const client = getCurrentHub().getClient(); + const client = getClient(); if (client && client.addIntegration) { // force integrations to be setup even if no DSN was set client.setupIntegrations(true); @@ -277,6 +279,6 @@ function updateScopeFromEnvVariables(): void { const sentryTraceEnv = process.env.SENTRY_TRACE; const baggageEnv = process.env.SENTRY_BAGGAGE; const { propagationContext } = tracingContextFromHeaders(sentryTraceEnv, baggageEnv); - getCurrentHub().getScope().setPropagationContext(propagationContext); + getCurrentScope().setPropagationContext(propagationContext); } } diff --git a/packages/node/test/handlers.test.ts b/packages/node/test/handlers.test.ts index 37faef621907..cf6dab4d9338 100644 --- a/packages/node/test/handlers.test.ts +++ b/packages/node/test/handlers.test.ts @@ -458,10 +458,11 @@ describe('tracingHandler', () => { const hub = new sentryCore.Hub(new NodeClient(options)); jest.spyOn(sentryCore, 'getCurrentHub').mockReturnValue(hub); + jest.spyOn(sentryCore, 'getCurrentScope').mockImplementation(() => hub.getScope()); sentryTracingMiddleware(req, res, next); - const transaction = sentryCore.getCurrentHub().getScope().getTransaction(); + const transaction = sentryCore.getCurrentScope().getTransaction(); expect(transaction?.metadata.request).toEqual(req); }); diff --git a/packages/node/test/integrations/http.test.ts b/packages/node/test/integrations/http.test.ts index 3a147af422dd..1bfafd5c256d 100644 --- a/packages/node/test/integrations/http.test.ts +++ b/packages/node/test/integrations/http.test.ts @@ -44,6 +44,8 @@ describe('tracing', () => { }); jest.spyOn(sentryCore, 'getCurrentHub').mockReturnValue(hub); + jest.spyOn(sentryCore, 'getCurrentScope').mockImplementation(() => hub.getScope()); + jest.spyOn(sentryCore, 'getClient').mockReturnValue(hub.getClient()); const transaction = hub.startTransaction({ name: 'dogpark', @@ -67,7 +69,8 @@ describe('tracing', () => { }); const hub = new Hub(new NodeClient(options)); jest.spyOn(sentryCore, 'getCurrentHub').mockReturnValue(hub); - + jest.spyOn(sentryCore, 'getCurrentScope').mockImplementation(() => hub.getScope()); + jest.spyOn(sentryCore, 'getClient').mockReturnValue(hub.getClient()); return hub; } @@ -236,11 +239,7 @@ describe('tracing', () => { const baggageHeader = request.getHeader('baggage') as string; const parts = sentryTraceHeader.split('-'); - expect(parts.length).toEqual(3); - expect(parts[0]).toEqual('86f39e84263a4de99c326acab3bfe3bd'); - expect(parts[1]).toEqual(expect.any(String)); - expect(parts[2]).toEqual('1'); - + expect(parts).toEqual(['86f39e84263a4de99c326acab3bfe3bd', expect.any(String), '1']); expect(baggageHeader).toEqual('sentry-trace_id=86f39e84263a4de99c326acab3bfe3bd,sentry-public_key=test-public-key'); }); @@ -355,7 +354,9 @@ describe('tracing', () => { const hub = new Hub(); - jest.spyOn(sentryCore, 'getCurrentHub').mockImplementation(() => hub); + jest.spyOn(sentryCore, 'getCurrentHub').mockReturnValue(hub); + jest.spyOn(sentryCore, 'getCurrentScope').mockImplementation(() => hub.getScope()); + jest.spyOn(sentryCore, 'getClient').mockReturnValue(hub.getClient()); const client = new NodeClient(options); jest.spyOn(hub, 'getClient').mockImplementation(() => client); @@ -380,6 +381,10 @@ describe('tracing', () => { const hub = createHub({ shouldCreateSpanForRequest: () => false }); + jest.spyOn(sentryCore, 'getCurrentHub').mockReturnValue(hub); + jest.spyOn(sentryCore, 'getCurrentScope').mockImplementation(() => hub.getScope()); + jest.spyOn(sentryCore, 'getClient').mockReturnValue(hub.getClient()); + httpIntegration.setupOnce( () => undefined, () => hub, @@ -485,6 +490,10 @@ describe('tracing', () => { const hub = createHub(); + jest.spyOn(sentryCore, 'getCurrentHub').mockReturnValue(hub); + jest.spyOn(sentryCore, 'getCurrentScope').mockImplementation(() => hub.getScope()); + jest.spyOn(sentryCore, 'getClient').mockReturnValue(hub.getClient()); + httpIntegration.setupOnce( () => undefined, () => hub, diff --git a/packages/remix/src/index.server.ts b/packages/remix/src/index.server.ts index 1c1c0ffee072..0600eb625b7f 100644 --- a/packages/remix/src/index.server.ts +++ b/packages/remix/src/index.server.ts @@ -1,5 +1,6 @@ import type { NodeOptions } from '@sentry/node'; -import { getCurrentHub, getCurrentScope, init as nodeInit } from '@sentry/node'; +import { getClient } from '@sentry/node'; +import { getCurrentScope, init as nodeInit } from '@sentry/node'; import { logger } from '@sentry/utils'; import { DEBUG_BUILD } from './utils/debug-build'; @@ -68,8 +69,7 @@ export { wrapExpressCreateRequestHandler } from './utils/serverAdapters/express' export type { SentryMetaArgs } from './utils/types'; function sdkAlreadyInitialized(): boolean { - const hub = getCurrentHub(); - return !!hub.getClient(); + return !!getClient(); } /** Initializes Sentry Remix SDK on Node. */ diff --git a/packages/remix/src/utils/instrumentServer.ts b/packages/remix/src/utils/instrumentServer.ts index 7e98dc123858..816410fd75f9 100644 --- a/packages/remix/src/utils/instrumentServer.ts +++ b/packages/remix/src/utils/instrumentServer.ts @@ -1,5 +1,5 @@ /* eslint-disable max-lines */ -import { getActiveTransaction, hasTracingEnabled, runWithAsyncContext } from '@sentry/core'; +import { getActiveTransaction, getClient, getCurrentScope, hasTracingEnabled, runWithAsyncContext } from '@sentry/core'; import type { Hub } from '@sentry/node'; import { captureException, getCurrentHub } from '@sentry/node'; import type { Transaction, TransactionSource, WrappedFunction } from '@sentry/types'; @@ -225,7 +225,7 @@ function makeWrappedDataFunction( return async function (this: unknown, args: DataFunctionArgs): Promise { let res: Response | AppData; const activeTransaction = getActiveTransaction(); - const currentScope = getCurrentHub().getScope(); + const currentScope = getCurrentScope(); try { const span = activeTransaction?.startChild({ @@ -280,7 +280,7 @@ function getTraceAndBaggage(): { sentryBaggage?: string; } { const transaction = getActiveTransaction(); - const currentScope = getCurrentHub().getScope(); + const currentScope = getCurrentScope(); if (isNodeEnv() && hasTracingEnabled()) { const span = currentScope.getSpan(); @@ -421,8 +421,8 @@ function wrapRequestHandler(origRequestHandler: RequestHandler, build: ServerBui return async function (this: unknown, request: RemixRequest, loadContext?: unknown): Promise { return runWithAsyncContext(async () => { const hub = getCurrentHub(); - const options = hub.getClient()?.getOptions(); - const scope = hub.getScope(); + const options = getClient()?.getOptions(); + const scope = getCurrentScope(); let normalizedRequest: Record = request; diff --git a/packages/remix/src/utils/serverAdapters/express.ts b/packages/remix/src/utils/serverAdapters/express.ts index ab638866ffd4..b60b74a8e0ff 100644 --- a/packages/remix/src/utils/serverAdapters/express.ts +++ b/packages/remix/src/utils/serverAdapters/express.ts @@ -1,4 +1,4 @@ -import { getCurrentHub, hasTracingEnabled } from '@sentry/core'; +import { getClient, getCurrentHub, getCurrentScope, hasTracingEnabled } from '@sentry/core'; import { flush } from '@sentry/node'; import type { Transaction } from '@sentry/types'; import { extractRequestData, isString, logger } from '@sentry/utils'; @@ -59,8 +59,8 @@ function wrapExpressRequestHandler( const request = extractRequestData(req); const hub = getCurrentHub(); - const options = hub.getClient()?.getOptions(); - const scope = hub.getScope(); + const options = getClient()?.getOptions(); + const scope = getCurrentScope(); scope.setSDKProcessingMetadata({ request }); diff --git a/packages/replay/src/replay.ts b/packages/replay/src/replay.ts index 8e1740845c7b..0085c44c6eb8 100644 --- a/packages/replay/src/replay.ts +++ b/packages/replay/src/replay.ts @@ -1,7 +1,7 @@ /* eslint-disable max-lines */ // TODO: We might want to split this file up import { EventType, record } from '@sentry-internal/rrweb'; -import { captureException, getClient, getCurrentHub } from '@sentry/core'; -import type { Event as SentryEvent, ReplayRecordingMode, Transaction } from '@sentry/types'; +import { captureException, getClient, getCurrentScope } from '@sentry/core'; +import type { ReplayRecordingMode, Transaction } from '@sentry/types'; import { logger } from '@sentry/utils'; import { @@ -698,7 +698,7 @@ export class ReplayContainer implements ReplayContainerInterface { * This is only available if performance is enabled, and if an instrumented router is used. */ public getCurrentRoute(): string | undefined { - const lastTransaction = this.lastTransaction || getCurrentHub().getScope().getTransaction(); + const lastTransaction = this.lastTransaction || getCurrentScope().getTransaction(); if (!lastTransaction || !['route', 'custom'].includes(lastTransaction.metadata.source)) { return undefined; } diff --git a/packages/replay/src/util/addGlobalListeners.ts b/packages/replay/src/util/addGlobalListeners.ts index fac2b278e666..1824e1fa606c 100644 --- a/packages/replay/src/util/addGlobalListeners.ts +++ b/packages/replay/src/util/addGlobalListeners.ts @@ -1,5 +1,6 @@ import type { BaseClient } from '@sentry/core'; -import { addEventProcessor, getClient, getCurrentHub } from '@sentry/core'; +import { getCurrentScope } from '@sentry/core'; +import { addEventProcessor, getClient } from '@sentry/core'; import type { Client, DynamicSamplingContext } from '@sentry/types'; import { addClickKeypressInstrumentationHandler, addHistoryInstrumentationHandler } from '@sentry/utils'; @@ -17,7 +18,7 @@ import type { ReplayContainer } from '../types'; */ export function addGlobalListeners(replay: ReplayContainer): void { // Listeners from core SDK // - const scope = getCurrentHub().getScope(); + const scope = getCurrentScope(); const client = getClient(); scope.addScopeListener(handleScopeListener(replay)); diff --git a/packages/replay/src/util/sendReplayRequest.ts b/packages/replay/src/util/sendReplayRequest.ts index 49710916fcb1..c030ea1f8c2f 100644 --- a/packages/replay/src/util/sendReplayRequest.ts +++ b/packages/replay/src/util/sendReplayRequest.ts @@ -1,4 +1,4 @@ -import { getCurrentHub } from '@sentry/core'; +import { getClient, getCurrentScope } from '@sentry/core'; import type { ReplayEvent, TransportMakeRequestResponse } from '@sentry/types'; import type { RateLimits } from '@sentry/utils'; import { isRateLimited, updateRateLimits } from '@sentry/utils'; @@ -30,9 +30,8 @@ export async function sendReplayRequest({ const { urls, errorIds, traceIds, initialTimestamp } = eventContext; - const hub = getCurrentHub(); - const client = hub.getClient(); - const scope = hub.getScope(); + const client = getClient(); + const scope = getCurrentScope(); const transport = client && client.getTransport(); const dsn = client && client.getDsn(); diff --git a/packages/serverless/src/awslambda.ts b/packages/serverless/src/awslambda.ts index 1da44ed1b47c..aff1e58675f5 100644 --- a/packages/serverless/src/awslambda.ts +++ b/packages/serverless/src/awslambda.ts @@ -305,7 +305,7 @@ export function wrapHandler( sentryTrace, baggage, ); - hub.getScope().setPropagationContext(propagationContext); + Sentry.getCurrentScope().setPropagationContext(propagationContext); transaction = hub.startTransaction({ name: context.functionName, diff --git a/packages/serverless/src/awsservices.ts b/packages/serverless/src/awsservices.ts index 699ae9c40ab5..33a3a25b8689 100644 --- a/packages/serverless/src/awsservices.ts +++ b/packages/serverless/src/awsservices.ts @@ -1,4 +1,4 @@ -import { getCurrentHub } from '@sentry/node'; +import { getCurrentScope } from '@sentry/node'; import type { Integration, Span } from '@sentry/types'; import { fill } from '@sentry/utils'; // 'aws-sdk/global' import is expected to be type-only so it's erased in the final .js file. @@ -57,7 +57,7 @@ function wrapMakeRequest( ): MakeRequestFunction { return function (this: TService, operation: string, params?: GenericParams, callback?: MakeRequestCallback) { let span: Span | undefined; - const scope = getCurrentHub().getScope(); + const scope = getCurrentScope(); const transaction = scope.getTransaction(); const req = orig.call(this, operation, params); req.on('afterBuild', () => { diff --git a/packages/serverless/src/google-cloud-grpc.ts b/packages/serverless/src/google-cloud-grpc.ts index 8dfbcc092cb4..d475d9b3b421 100644 --- a/packages/serverless/src/google-cloud-grpc.ts +++ b/packages/serverless/src/google-cloud-grpc.ts @@ -1,5 +1,5 @@ import type { EventEmitter } from 'events'; -import { getCurrentHub } from '@sentry/node'; +import { getCurrentScope } from '@sentry/node'; import type { Integration, Span } from '@sentry/types'; import { fill } from '@sentry/utils'; @@ -108,7 +108,7 @@ function fillGrpcFunction(stub: Stub, serviceIdentifier: string, methodName: str return ret; } let span: Span | undefined; - const scope = getCurrentHub().getScope(); + const scope = getCurrentScope(); const transaction = scope.getTransaction(); if (transaction) { span = transaction.startChild({ diff --git a/packages/serverless/src/google-cloud-http.ts b/packages/serverless/src/google-cloud-http.ts index d3ef8646eab7..f9eb9a6cc3cd 100644 --- a/packages/serverless/src/google-cloud-http.ts +++ b/packages/serverless/src/google-cloud-http.ts @@ -1,7 +1,7 @@ // '@google-cloud/common' import is expected to be type-only so it's erased in the final .js file. // When TypeScript compiler is upgraded, use `import type` syntax to explicitly assert that we don't want to load a module here. import type * as common from '@google-cloud/common'; -import { getCurrentHub } from '@sentry/node'; +import { getCurrentScope } from '@sentry/node'; import type { Integration, Span } from '@sentry/types'; import { fill } from '@sentry/utils'; @@ -52,7 +52,7 @@ export class GoogleCloudHttp implements Integration { function wrapRequestFunction(orig: RequestFunction): RequestFunction { return function (this: common.Service, reqOpts: RequestOptions, callback: ResponseCallback): void { let span: Span | undefined; - const scope = getCurrentHub().getScope(); + const scope = getCurrentScope(); const transaction = scope.getTransaction(); if (transaction) { const httpMethod = reqOpts.method || 'GET'; diff --git a/packages/svelte/src/performance.ts b/packages/svelte/src/performance.ts index 2230db18f9a4..0afd5250a06f 100644 --- a/packages/svelte/src/performance.ts +++ b/packages/svelte/src/performance.ts @@ -1,4 +1,4 @@ -import { getCurrentHub } from '@sentry/browser'; +import { getCurrentScope } from '@sentry/browser'; import type { Span, Transaction } from '@sentry/types'; import { afterUpdate, beforeUpdate, onMount } from 'svelte'; import { current_component } from 'svelte/internal'; @@ -92,5 +92,5 @@ function recordUpdateSpans(componentName: string, initSpan?: Span): void { } function getActiveTransaction(): Transaction | undefined { - return getCurrentHub().getScope().getTransaction(); + return getCurrentScope().getTransaction(); } diff --git a/packages/svelte/test/performance.test.ts b/packages/svelte/test/performance.test.ts index aabf4462e8cb..e872ee7a283d 100644 --- a/packages/svelte/test/performance.test.ts +++ b/packages/svelte/test/performance.test.ts @@ -22,18 +22,12 @@ jest.mock('@sentry/core', () => { const original = jest.requireActual('@sentry/core'); return { ...original, - getCurrentHub(): { - getScope(): Scope; - } { + getCurrentScope(): Scope { return { - getScope(): any { - return { - getTransaction: () => { - return returnUndefinedTransaction ? undefined : testTransaction; - }, - }; + getTransaction: () => { + return returnUndefinedTransaction ? undefined : testTransaction; }, - }; + } as Scope; }, }; }); diff --git a/packages/sveltekit/src/server/handle.ts b/packages/sveltekit/src/server/handle.ts index 3b16f659f6e0..7f9f581ca3c3 100644 --- a/packages/sveltekit/src/server/handle.ts +++ b/packages/sveltekit/src/server/handle.ts @@ -1,6 +1,7 @@ /* eslint-disable @sentry-internal/sdk/no-optional-chaining */ import type { Span } from '@sentry/core'; -import { getActiveTransaction, getCurrentHub, runWithAsyncContext, startSpan } from '@sentry/core'; +import { getCurrentScope } from '@sentry/core'; +import { getActiveTransaction, runWithAsyncContext, startSpan } from '@sentry/core'; import { captureException } from '@sentry/node'; import { dynamicSamplingContextToSentryBaggageHeader, objectify } from '@sentry/utils'; import type { Handle, ResolveOptions } from '@sveltejs/kit'; @@ -102,7 +103,7 @@ export function sentryHandle(handlerOptions?: SentryHandleOptions): Handle { // if there is an active transaction, we know that this handle call is nested and hence // we don't create a new domain for it. If we created one, nested server calls would // create new transactions instead of adding a child span to the currently active span. - if (getCurrentHub().getScope().getSpan()) { + if (getCurrentScope().getSpan()) { return instrumentHandle(input, options); } return runWithAsyncContext(() => { @@ -122,7 +123,7 @@ async function instrumentHandle( } const { dynamicSamplingContext, traceparentData, propagationContext } = getTracePropagationData(event); - getCurrentHub().getScope().setPropagationContext(propagationContext); + getCurrentScope().setPropagationContext(propagationContext); try { const resolveResult = await startSpan( diff --git a/packages/sveltekit/src/server/load.ts b/packages/sveltekit/src/server/load.ts index c902fe4376d6..5d0cd3c1cb90 100644 --- a/packages/sveltekit/src/server/load.ts +++ b/packages/sveltekit/src/server/load.ts @@ -1,5 +1,5 @@ /* eslint-disable @sentry-internal/sdk/no-optional-chaining */ -import { getCurrentHub, startSpan } from '@sentry/core'; +import { getCurrentScope, startSpan } from '@sentry/core'; import { captureException } from '@sentry/node'; import type { TransactionContext } from '@sentry/types'; import { addNonEnumerableProperty, objectify } from '@sentry/utils'; @@ -130,7 +130,7 @@ export function wrapServerLoadWithSentry any>(origSe const routeId = event.route && (Object.getOwnPropertyDescriptor(event.route, 'id')?.value as string | undefined); const { dynamicSamplingContext, traceparentData, propagationContext } = getTracePropagationData(event); - getCurrentHub().getScope().setPropagationContext(propagationContext); + getCurrentScope().setPropagationContext(propagationContext); const traceLoadContext: TransactionContext = { op: 'function.sveltekit.server.load', diff --git a/packages/tracing-internal/src/browser/request.ts b/packages/tracing-internal/src/browser/request.ts index b45e15679805..ab2f73b127f0 100644 --- a/packages/tracing-internal/src/browser/request.ts +++ b/packages/tracing-internal/src/browser/request.ts @@ -1,5 +1,5 @@ /* eslint-disable max-lines */ -import { getCurrentHub, getDynamicSamplingContextFromClient, hasTracingEnabled } from '@sentry/core'; +import { getClient, getCurrentScope, getDynamicSamplingContextFromClient, hasTracingEnabled } from '@sentry/core'; import type { HandlerDataXhr, SentryWrappedXMLHttpRequest, Span } from '@sentry/types'; import { BAGGAGE_HEADER_NAME, @@ -264,8 +264,7 @@ export function xhrCallback( return undefined; } - const hub = getCurrentHub(); - const scope = hub.getScope(); + const scope = getCurrentScope(); const parentSpan = scope.getSpan(); const span = @@ -294,7 +293,7 @@ export function xhrCallback( const sentryBaggageHeader = dynamicSamplingContextToSentryBaggageHeader(dynamicSamplingContext); setHeaderOnXhr(xhr, span.toTraceparent(), sentryBaggageHeader); } else { - const client = hub.getClient(); + const client = getClient(); const { traceId, sampled, dsc } = scope.getPropagationContext(); const sentryTraceHeader = generateSentryTraceHeader(traceId, undefined, sampled); const dynamicSamplingContext = diff --git a/packages/tracing-internal/src/common/fetch.ts b/packages/tracing-internal/src/common/fetch.ts index 9c45da8adfa3..63f7f8ede721 100644 --- a/packages/tracing-internal/src/common/fetch.ts +++ b/packages/tracing-internal/src/common/fetch.ts @@ -1,4 +1,4 @@ -import { getCurrentHub, getDynamicSamplingContextFromClient, hasTracingEnabled } from '@sentry/core'; +import { getClient, getCurrentScope, getDynamicSamplingContextFromClient, hasTracingEnabled } from '@sentry/core'; import type { Client, HandlerDataFetch, Scope, Span, SpanOrigin } from '@sentry/types'; import { BAGGAGE_HEADER_NAME, @@ -65,9 +65,8 @@ export function instrumentFetchRequest( return undefined; } - const hub = getCurrentHub(); - const scope = hub.getScope(); - const client = hub.getClient(); + const scope = getCurrentScope(); + const client = getClient(); const parentSpan = scope.getSpan(); const { method, url } = handlerData.fetchData; diff --git a/packages/vercel-edge/src/integrations/wintercg-fetch.ts b/packages/vercel-edge/src/integrations/wintercg-fetch.ts index ded26ed21a9d..7c75308c72fe 100644 --- a/packages/vercel-edge/src/integrations/wintercg-fetch.ts +++ b/packages/vercel-edge/src/integrations/wintercg-fetch.ts @@ -1,5 +1,5 @@ import { instrumentFetchRequest } from '@sentry-internal/tracing'; -import { getCurrentHub, isSentryRequestUrl } from '@sentry/core'; +import { getClient, getCurrentHub, isSentryRequestUrl } from '@sentry/core'; import type { FetchBreadcrumbData, FetchBreadcrumbHint, HandlerDataFetch, Integration, Span } from '@sentry/types'; import { LRUMap, addFetchInstrumentationHandler, stringMatchesSomePattern } from '@sentry/utils'; @@ -74,8 +74,7 @@ export class WinterCGFetch implements Integration { /** Decides whether to attach trace data to the outgoing fetch request */ private _shouldAttachTraceData(url: string): boolean { - const hub = getCurrentHub(); - const client = hub.getClient(); + const client = getClient(); if (!client) { return false; diff --git a/packages/vercel-edge/test/wintercg-fetch.test.ts b/packages/vercel-edge/test/wintercg-fetch.test.ts index 22bd960defdf..d35aaa64f35c 100644 --- a/packages/vercel-edge/test/wintercg-fetch.test.ts +++ b/packages/vercel-edge/test/wintercg-fetch.test.ts @@ -29,6 +29,8 @@ const fakeHubInstance = new FakeHub( ); jest.spyOn(sentryCore, 'getCurrentHub').mockImplementation(() => fakeHubInstance); +jest.spyOn(sentryCore, 'getCurrentScope').mockImplementation(() => fakeHubInstance.getScope()); +jest.spyOn(sentryCore, 'getClient').mockImplementation(() => fakeHubInstance.getClient()); const addFetchInstrumentationHandlerSpy = jest.spyOn(sentryUtils, 'addFetchInstrumentationHandler'); const instrumentFetchRequestSpy = jest.spyOn(internalTracing, 'instrumentFetchRequest'); diff --git a/packages/vue/src/tracing.ts b/packages/vue/src/tracing.ts index 82000b9799a4..ef509dcdb406 100644 --- a/packages/vue/src/tracing.ts +++ b/packages/vue/src/tracing.ts @@ -1,4 +1,4 @@ -import { getCurrentHub } from '@sentry/browser'; +import { getCurrentHub, getCurrentScope } from '@sentry/browser'; import type { Span, Transaction } from '@sentry/types'; import { logger, timestampInSeconds } from '@sentry/utils'; @@ -34,7 +34,7 @@ const HOOKS: { [key in Operation]: Hook[] } = { /** Grabs active transaction off scope, if any */ export function getActiveTransaction(): Transaction | undefined { - return getCurrentHub().getScope().getTransaction(); + return getCurrentScope().getTransaction(); } /** Finish top-level span and activity with a debounce configured using `timeout` option */