Skip to content

Commit a985487

Browse files
authored
feat(core): Streamline custom hub creation for node-experimental (#10555)
This is an intermediate step to streamline what we need to customize in OTEL. This should make it easier to slowly remove customizations there. The main change is that you can set a `createHub` method on the main carrier, which will be used if set. This way, all the code we have to duplicate in node-experimental right now just to create the correct `OpenTelemetryHub` can mostly go away. We will remove this again during then v8 cycle.
1 parent ca6010b commit a985487

18 files changed

+148
-198
lines changed

packages/core/src/hub.ts

Lines changed: 72 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -19,15 +19,7 @@ import type {
1919
TransactionContext,
2020
User,
2121
} from '@sentry/types';
22-
import {
23-
GLOBAL_OBJ,
24-
consoleSandbox,
25-
dateTimestampInSeconds,
26-
getGlobalSingleton,
27-
isThenable,
28-
logger,
29-
uuid4,
30-
} from '@sentry/utils';
22+
import { GLOBAL_OBJ, consoleSandbox, dateTimestampInSeconds, isThenable, logger, uuid4 } from '@sentry/utils';
3123

3224
import { DEFAULT_ENVIRONMENT } from './constants';
3325
import { DEBUG_BUILD } from './debug-build';
@@ -86,21 +78,41 @@ export interface Layer {
8678
* @hidden
8779
*/
8880
export interface Carrier {
89-
__SENTRY__?: {
90-
hub?: Hub;
91-
acs?: AsyncContextStrategy;
92-
/**
93-
* Extra Hub properties injected by various SDKs
94-
*/
95-
integrations?: Integration[];
96-
extensions?: {
97-
/** Extension methods for the hub, which are bound to the current Hub instance */
98-
// eslint-disable-next-line @typescript-eslint/ban-types
99-
[key: string]: Function;
100-
};
81+
__SENTRY__?: SentryCarrier;
82+
}
83+
84+
type CreateHub = (...options: ConstructorParameters<typeof Hub>) => Hub;
85+
86+
interface SentryCarrier {
87+
hub?: Hub;
88+
createHub?: CreateHub;
89+
acs?: AsyncContextStrategy;
90+
/**
91+
* Extra Hub properties injected by various SDKs
92+
*/
93+
integrations?: Integration[];
94+
extensions?: {
95+
/** Extension methods for the hub, which are bound to the current Hub instance */
96+
// eslint-disable-next-line @typescript-eslint/ban-types
97+
[key: string]: Function;
10198
};
10299
}
103100

101+
/**
102+
* Create a hub. If a custom `createHub` is registered on the main carrier, use that instead.
103+
* This only exists to make POTEL migration easier.
104+
*/
105+
function createHub(...options: ConstructorParameters<typeof Hub>): Hub {
106+
const carrier = getMainCarrier();
107+
const sentry = getSentryCarrier(carrier);
108+
109+
if (sentry.createHub) {
110+
return sentry.createHub(...options);
111+
}
112+
113+
return new Hub(...options);
114+
}
115+
104116
/**
105117
* @inheritDoc
106118
*/
@@ -663,8 +675,8 @@ Sentry.init({...});
663675
// eslint-disable-next-line @typescript-eslint/no-explicit-any
664676
private _callExtensionMethod<T>(method: string, ...args: any[]): T {
665677
const carrier = getMainCarrier();
666-
const sentry = carrier.__SENTRY__;
667-
if (sentry && sentry.extensions && typeof sentry.extensions[method] === 'function') {
678+
const sentry = getSentryCarrier(carrier);
679+
if (sentry.extensions && typeof sentry.extensions[method] === 'function') {
668680
return sentry.extensions[method].apply(this, args);
669681
}
670682
DEBUG_BUILD && logger.warn(`Extension method ${method} couldn't be found, doing nothing.`);
@@ -679,10 +691,8 @@ Sentry.init({...});
679691
* at the call-site. We always access the carrier through this function, so we can guarantee that `__SENTRY__` is there.
680692
**/
681693
export function getMainCarrier(): Carrier {
682-
GLOBAL_OBJ.__SENTRY__ = GLOBAL_OBJ.__SENTRY__ || {
683-
extensions: {},
684-
hub: undefined,
685-
};
694+
// This ensures a Sentry carrier exists
695+
getSentryCarrier(GLOBAL_OBJ);
686696
return GLOBAL_OBJ;
687697
}
688698

@@ -711,18 +721,19 @@ export function makeMain(hub: Hub): Hub {
711721
*/
712722
export function getCurrentHub(): Hub {
713723
// Get main carrier (global for every environment)
714-
const registry = getMainCarrier();
724+
const carrier = getMainCarrier();
725+
const sentry = getSentryCarrier(carrier);
715726

716-
if (registry.__SENTRY__ && registry.__SENTRY__.acs) {
717-
const hub = registry.__SENTRY__.acs.getCurrentHub();
727+
if (sentry.acs) {
728+
const hub = sentry.acs.getCurrentHub();
718729

719730
if (hub) {
720731
return hub;
721732
}
722733
}
723734

724735
// Return hub that lives on a global object
725-
return getGlobalHub(registry);
736+
return getGlobalHub();
726737
}
727738

728739
/**
@@ -735,15 +746,17 @@ export function getIsolationScope(): Scope {
735746
return getCurrentHub().getIsolationScope();
736747
}
737748

738-
function getGlobalHub(registry: Carrier = getMainCarrier()): Hub {
749+
function getGlobalHub(): Hub {
750+
const registry = getMainCarrier();
751+
739752
// If there's no hub, or its an old API, assign a new one
740753

741754
if (
742755
!hasHubOnCarrier(registry) ||
743756
// eslint-disable-next-line deprecation/deprecation
744757
getHubFromCarrier(registry).isOlderThan(API_VERSION)
745758
) {
746-
setHubOnCarrier(registry, new Hub());
759+
setHubOnCarrier(registry, createHub());
747760
}
748761

749762
// Return hub that lives on a global object
@@ -768,7 +781,7 @@ export function ensureHubOnCarrier(carrier: Carrier, parent: Hub = getGlobalHub(
768781
const scope = parent.getScope();
769782
// eslint-disable-next-line deprecation/deprecation
770783
const isolationScope = parent.getIsolationScope();
771-
setHubOnCarrier(carrier, new Hub(client, scope.clone(), isolationScope.clone()));
784+
setHubOnCarrier(carrier, createHub(client, scope.clone(), isolationScope.clone()));
772785
}
773786
}
774787

@@ -780,8 +793,8 @@ export function ensureHubOnCarrier(carrier: Carrier, parent: Hub = getGlobalHub(
780793
export function setAsyncContextStrategy(strategy: AsyncContextStrategy | undefined): void {
781794
// Get main carrier (global for every environment)
782795
const registry = getMainCarrier();
783-
registry.__SENTRY__ = registry.__SENTRY__ || {};
784-
registry.__SENTRY__.acs = strategy;
796+
const sentry = getSentryCarrier(registry);
797+
sentry.acs = strategy;
785798
}
786799

787800
/**
@@ -793,9 +806,10 @@ export function setAsyncContextStrategy(strategy: AsyncContextStrategy | undefin
793806
*/
794807
export function runWithAsyncContext<T>(callback: () => T, options: RunWithAsyncContextOptions = {}): T {
795808
const registry = getMainCarrier();
809+
const sentry = getSentryCarrier(registry);
796810

797-
if (registry.__SENTRY__ && registry.__SENTRY__.acs) {
798-
return registry.__SENTRY__.acs.runWithAsyncContext(callback, options);
811+
if (sentry.acs) {
812+
return sentry.acs.runWithAsyncContext(callback, options);
799813
}
800814

801815
// if there was no strategy, fallback to just calling the callback
@@ -807,7 +821,7 @@ export function runWithAsyncContext<T>(callback: () => T, options: RunWithAsyncC
807821
* @param carrier object
808822
*/
809823
function hasHubOnCarrier(carrier: Carrier): boolean {
810-
return !!(carrier && carrier.__SENTRY__ && carrier.__SENTRY__.hub);
824+
return !!getSentryCarrier(carrier).hub;
811825
}
812826

813827
/**
@@ -817,7 +831,12 @@ function hasHubOnCarrier(carrier: Carrier): boolean {
817831
* @hidden
818832
*/
819833
export function getHubFromCarrier(carrier: Carrier): Hub {
820-
return getGlobalSingleton<Hub>('hub', () => new Hub(), carrier);
834+
const sentry = getSentryCarrier(carrier);
835+
if (!sentry.hub) {
836+
sentry.hub = createHub();
837+
}
838+
839+
return sentry.hub;
821840
}
822841

823842
/**
@@ -828,7 +847,18 @@ export function getHubFromCarrier(carrier: Carrier): Hub {
828847
*/
829848
export function setHubOnCarrier(carrier: Carrier, hub: Hub): boolean {
830849
if (!carrier) return false;
831-
const __SENTRY__ = (carrier.__SENTRY__ = carrier.__SENTRY__ || {});
832-
__SENTRY__.hub = hub;
850+
const sentry = getSentryCarrier(carrier);
851+
sentry.hub = hub;
833852
return true;
834853
}
854+
855+
/** Will either get the existing sentry carrier, or create a new one. */
856+
function getSentryCarrier(carrier: Carrier): SentryCarrier {
857+
if (!carrier.__SENTRY__) {
858+
carrier.__SENTRY__ = {
859+
extensions: {},
860+
hub: undefined,
861+
};
862+
}
863+
return carrier.__SENTRY__;
864+
}

packages/opentelemetry/src/contextManager.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
import type { Context, ContextManager } from '@opentelemetry/api';
22
import type { Carrier, Hub } from '@sentry/core';
3+
import { getCurrentHub, getHubFromCarrier } from '@sentry/core';
34
import { ensureHubOnCarrier } from '@sentry/core';
45

5-
import { getCurrentHub, getHubFromCarrier } from './custom/hub';
66
import { setHubOnContext } from './utils/contextData';
77

88
function createNewHub(parent: Hub | undefined): Hub {
@@ -46,6 +46,7 @@ export function wrapContextManagerClass<ContextManagerInstance extends ContextMa
4646
thisArg?: ThisParameterType<F>,
4747
...args: A
4848
): ReturnType<F> {
49+
// eslint-disable-next-line deprecation/deprecation
4950
const existingHub = getCurrentHub();
5051
const newHub = createNewHub(existingHub);
5152

0 commit comments

Comments
 (0)