Skip to content

Commit 300820f

Browse files
committed
feat(server-utils): Capture and log orchestrion stats
1 parent c85347c commit 300820f

5 files changed

Lines changed: 78 additions & 69 deletions

File tree

packages/core/src/utils/worldwide.ts

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,15 @@ export type InternalGlobal = {
5555
_sentryModuleMetadata?: Record<string, any>;
5656
_sentryEsmLoaderHookRegistered?: boolean;
5757
_sentryWrappedDepth?: number;
58+
/**
59+
* Orchestrion bundler and runtime detection.
60+
*/
61+
__SENTRY_ORCHESTRION__?: {
62+
/** Empty array signifies runtime hooked */
63+
runtime?: string[];
64+
/** Empty array signifies bundler plugin ran */
65+
bundler?: string[];
66+
};
5867
} & Carrier;
5968

6069
/** Get's the global object for the current JavaScript runtime */

packages/server-utils/package.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -93,9 +93,9 @@
9393
"access": "public"
9494
},
9595
"dependencies": {
96-
"@apm-js-collab/code-transformer-bundler-plugins": "^0.5.0",
96+
"@apm-js-collab/code-transformer-bundler-plugins": "^0.6.0",
9797
"@apm-js-collab/code-transformer": "^0.18.0",
98-
"@apm-js-collab/tracing-hooks": "^0.11.0",
98+
"@apm-js-collab/tracing-hooks": "^0.12.0",
9999
"@sentry/conventions": "^0.15.1",
100100
"@sentry/core": "10.65.0",
101101
"magic-string": "~0.30.0"
Lines changed: 24 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,6 @@
1-
import { debug } from '@sentry/core';
1+
import { debug, GLOBAL_OBJ } from '@sentry/core';
22
import { DEBUG_BUILD } from '../debug-build';
33

4-
declare global {
5-
// eslint-disable-next-line no-var
6-
var __SENTRY_ORCHESTRION__: { runtime?: boolean; bundler?: boolean } | undefined;
7-
}
8-
94
/**
105
* Whether orchestrion has injected the diagnostics channels into this process,
116
* either by the runtime `--import` hook / init-time registration (`runtime`)
@@ -16,40 +11,43 @@ declare global {
1611
* will ever publish to those channels.
1712
*/
1813
export function isOrchestrionInjected(): boolean {
19-
const marker = globalThis.__SENTRY_ORCHESTRION__;
20-
return !!(marker?.runtime || marker?.bundler);
14+
return !!GLOBAL_OBJ.__SENTRY_ORCHESTRION__;
2115
}
2216

2317
/**
2418
* Verifies that the diagnostics channels have been injected either by the
2519
* runtime `--import` hook (or init-time registration), a bundler plugin, or
26-
* both, and warns if not.
20+
* both, and warns if not. When at least one injector is active, logs for each
21+
* mechanism whether it hooked (a defined array, even empty, means it did) and
22+
* which libraries it injected.
2723
*
2824
* Both injectors being active at once is fine: they operate on disjoint module
2925
* sets (a module is either loaded through Node's loader and transformed by the
3026
* runtime hook, or inlined by the bundler and transformed by the plugin), so
3127
* a single module can't be double-wrapped. A hybrid setup, with some deps
3228
* external and runtime-instrumented, others bundled and plugin-instrumented,
3329
* is fine.
34-
*
35-
* Note: intentionally does NOT warn in production, only in debug builds,
36-
* because production warnings are reserved for truly critical issues.
3730
*/
3831
export function detectOrchestrionSetup(): void {
39-
if (!DEBUG_BUILD) return;
32+
const { runtime, bundler } = GLOBAL_OBJ.__SENTRY_ORCHESTRION__ ?? {};
4033

41-
const marker = globalThis.__SENTRY_ORCHESTRION__;
42-
const runtime = !!marker?.runtime;
43-
const bundler = !!marker?.bundler;
44-
45-
DEBUG_BUILD && debug.log(`[orchestrion] detect: runtime=${runtime} bundler=${bundler}`);
46-
47-
if (!isOrchestrionInjected()) {
48-
DEBUG_BUILD &&
49-
debug.warn(
50-
'[Sentry] No diagnostics-channel injection detected. Channel-based integrations ' +
51-
'(mysql, …) will not record spans. Make sure the diagnostics channels are injected ' +
52-
'via the runtime `--import` hook or a bundler plugin before the instrumented modules load.',
53-
);
34+
if (!runtime && !bundler) {
35+
debug.warn(
36+
'[Sentry] No diagnostics-channel injection detected. Channel-based integrations ' +
37+
'will not record spans. Make sure the diagnostics channels are injected ' +
38+
'via the runtime `--import` hook or a bundler plugin before the instrumented modules load.',
39+
);
40+
return;
5441
}
42+
43+
debug.log(
44+
runtime
45+
? `[Sentry] Runtime hook registered, injected libraries=${JSON.stringify(runtime)}`
46+
: '[Sentry] Runtime hook not registered',
47+
);
48+
debug.log(
49+
bundler
50+
? `[Sentry] Bundler plugin ran, injected libraries=${JSON.stringify(bundler)}`
51+
: '[Sentry] Bundler plugin did not run',
52+
);
5553
}

packages/server-utils/src/orchestrion/runtime/register.ts

Lines changed: 28 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,23 @@
1-
import { debug } from '@sentry/core';
1+
import { debug, GLOBAL_OBJ } from '@sentry/core';
22
import { createRequire } from 'node:module';
33
import * as Module from 'node:module';
44
import { pathToFileURL } from 'node:url';
55
import { DEBUG_BUILD } from '../../debug-build';
66
import { SENTRY_INSTRUMENTATIONS } from '../config';
7+
import type { InstrumentationConfig } from '@apm-js-collab/code-transformer';
8+
import type { register } from 'node:module';
9+
10+
type TracingHooksSync = {
11+
initialize: (opts: { instrumentations: InstrumentationConfig[] }) => void;
12+
resolve: Function;
13+
load: Function;
14+
setDiagnosticsHook: (callback: (event: { url: string; moduleName: string; error: Error }) => void) => void;
15+
};
16+
17+
type NodeModule = {
18+
registerHooks?: (options: unknown) => { deregister: () => void };
19+
register?: typeof register;
20+
};
721

822
export interface RegisterDiagnosticsChannelInjectionOptions {
923
/**
@@ -17,11 +31,6 @@ export interface RegisterDiagnosticsChannelInjectionOptions {
1731
tracingHooksDir?: string;
1832
}
1933

20-
declare global {
21-
// eslint-disable-next-line no-var
22-
var __SENTRY_ORCHESTRION__: { runtime?: boolean; bundler?: boolean } | undefined;
23-
}
24-
2534
/** `Module.registerHooks` only became stable in Node 24.13 / 25.1 and Deno 2.8. */
2635
function hasStableSyncModuleHooks(denoVersionString: string | undefined): boolean {
2736
const parseVersion = (v: string): number[] => v.split('.').map(n => parseInt(n, 10));
@@ -47,15 +56,9 @@ function hasStableSyncModuleHooks(denoVersionString: string | undefined): boolea
4756
*
4857
* Libraries imported *after* this call publish the `tracingChannel` events that
4958
* the channel-based integrations subscribe to.
50-
*
51-
* Idempotent via `globalThis.__SENTRY_ORCHESTRION__` — a no-op if the runtime
52-
* `--import` hook or a bundler plugin already injected the channels.
5359
*/
5460
export function registerDiagnosticsChannelInjection(options?: RegisterDiagnosticsChannelInjectionOptions): void {
55-
const g = (globalThis.__SENTRY_ORCHESTRION__ ??= {});
56-
57-
// Already injected (runtime --import hook or bundler plugin) — nothing to do.
58-
if (g.runtime || g.bundler) {
61+
if (GLOBAL_OBJ?.__SENTRY_ORCHESTRION__?.runtime) {
5962
return;
6063
}
6164

@@ -89,10 +92,7 @@ export function registerDiagnosticsChannelInjection(options?: RegisterDiagnostic
8992

9093
// `Module.registerHooks` / `Module.register` are newer than the @types/node
9194
// we build against, hence the cast.
92-
const mod = Module as unknown as {
93-
registerHooks?: (hooks: unknown) => void;
94-
register?: (specifier: string, options: unknown) => void;
95-
};
95+
const mod = Module as NodeModule;
9696

9797
// runs both at `--import` time and (synchronously) inside `Sentry.init()`,
9898
// so an unguarded throw would either abort startup or make `init()` throw.
@@ -105,15 +105,18 @@ export function registerDiagnosticsChannelInjection(options?: RegisterDiagnostic
105105
// We require() the module here so that we can synchronously load it,
106106
// including from a CommonJS Sentry build, without bundlers pulling in.
107107
// All versions in stableSyncHooks support this.
108-
const { initialize, resolve, load } = (
108+
const { initialize, resolve, load, setDiagnosticsHook } = (
109109
requireFromHooksDir
110110
? requireFromHooksDir(`${tracingHooksDir}/hook-sync.mjs`)
111111
: nodeRequire('@apm-js-collab/tracing-hooks/hook-sync.mjs')
112-
) as {
113-
initialize: (opts: { instrumentations: unknown }) => void;
114-
resolve: unknown;
115-
load: unknown;
116-
};
112+
) as TracingHooksSync;
113+
114+
setDiagnosticsHook(event => {
115+
GLOBAL_OBJ.__SENTRY_ORCHESTRION__ = GLOBAL_OBJ.__SENTRY_ORCHESTRION__ || {};
116+
GLOBAL_OBJ.__SENTRY_ORCHESTRION__.runtime = GLOBAL_OBJ.__SENTRY_ORCHESTRION__.runtime || [];
117+
GLOBAL_OBJ.__SENTRY_ORCHESTRION__.runtime.push(event.moduleName);
118+
});
119+
117120
initialize({ instrumentations: SENTRY_INSTRUMENTATIONS });
118121
mod.registerHooks({ resolve, load });
119122
DEBUG_BUILD && debug.log('[orchestrion] registered diagnostics-channel injection via Module.registerHooks()');
@@ -161,5 +164,6 @@ export function registerDiagnosticsChannelInjection(options?: RegisterDiagnostic
161164
return;
162165
}
163166

164-
g.runtime = true;
167+
GLOBAL_OBJ.__SENTRY_ORCHESTRION__ = GLOBAL_OBJ.__SENTRY_ORCHESTRION__ || {};
168+
GLOBAL_OBJ.__SENTRY_ORCHESTRION__.runtime = GLOBAL_OBJ.__SENTRY_ORCHESTRION__.runtime || [];
165169
}

yarn.lock

Lines changed: 15 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -414,6 +414,16 @@
414414
magic-string "^0.30.21"
415415
module-details-from-path "^1.0.4"
416416

417+
"@apm-js-collab/code-transformer-bundler-plugins@^0.6.0":
418+
version "0.6.0"
419+
resolved "https://registry.yarnpkg.com/@apm-js-collab/code-transformer-bundler-plugins/-/code-transformer-bundler-plugins-0.6.0.tgz#ed64bae9e1871366eadc1ef6dabc9ee6ccc53e57"
420+
integrity sha512-Hys7LDskIB/BNrd87GAfYWHRE3mWgcPYonK4W9uxjho4N0JnPKk2YQyOLlqmkyVhj2UzwLRpmbJ4D6uR9O7wyw==
421+
dependencies:
422+
"@apm-js-collab/code-transformer" "^0.18.0"
423+
es-module-lexer "^2.1.0"
424+
magic-string "^0.30.21"
425+
module-details-from-path "^1.0.4"
426+
417427
"@apm-js-collab/code-transformer@^0.15.0":
418428
version "0.15.0"
419429
resolved "https://registry.yarnpkg.com/@apm-js-collab/code-transformer/-/code-transformer-0.15.0.tgz#a3a1b6c7b92db16f8277636b4a72a1626e2fa52a"
@@ -426,18 +436,6 @@
426436
semifies "^1.0.0"
427437
source-map "^0.6.0"
428438

429-
"@apm-js-collab/code-transformer@^0.16.0":
430-
version "0.16.0"
431-
resolved "https://registry.yarnpkg.com/@apm-js-collab/code-transformer/-/code-transformer-0.16.0.tgz#f49f3f88839907aea86a23c0a8102ad5038b8273"
432-
integrity sha512-J3YRXRsxr/d48E7iDOAyVLrqQMCGU1iHWxXPKq7EeXQTRDDJ50piOfQNqxsM7u4XogJlirXvLHIznr0T33HTKw==
433-
dependencies:
434-
"@types/estree" "^1.0.8"
435-
astring "^1.9.0"
436-
esquery "^1.7.0"
437-
meriyah "^6.1.4"
438-
semifies "^1.0.0"
439-
source-map "^0.6.0"
440-
441439
"@apm-js-collab/code-transformer@^0.18.0":
442440
version "0.18.0"
443441
resolved "https://registry.yarnpkg.com/@apm-js-collab/code-transformer/-/code-transformer-0.18.0.tgz#722972c05f04bc37f4bbd5067f42ffd7a990eee9"
@@ -450,12 +448,12 @@
450448
semifies "^1.0.0"
451449
source-map "^0.6.0"
452450

453-
"@apm-js-collab/tracing-hooks@^0.11.0":
454-
version "0.11.0"
455-
resolved "https://registry.yarnpkg.com/@apm-js-collab/tracing-hooks/-/tracing-hooks-0.11.0.tgz#4c9c378695d65e90574893c1faba3c13b5bdbd14"
456-
integrity sha512-5hWEcCGF4hcNh9lyB70p58pXn4HyUAVCad44wK6j110Ky+ivG19TfKHLB2aIDgItabCj6VPZm0DMAMNRnJDNBw==
451+
"@apm-js-collab/tracing-hooks@^0.12.0":
452+
version "0.12.0"
453+
resolved "https://registry.yarnpkg.com/@apm-js-collab/tracing-hooks/-/tracing-hooks-0.12.0.tgz#b65505d1d075fc8d8f4fa1973c37eaa9a9975b3f"
454+
integrity sha512-U1cDbHOFbeToq5VWNcroBtQpz3hfH39uLkzJ5lorBFVNhHbTNj5MQvP+jODDwxBkG6A/agbQelG15rEoDgnjWg==
457455
dependencies:
458-
"@apm-js-collab/code-transformer" "^0.16.0"
456+
"@apm-js-collab/code-transformer" "^0.18.0"
459457
debug "^4.4.1"
460458
module-details-from-path "^1.0.4"
461459

0 commit comments

Comments
 (0)