Skip to content

WIP: POC to use orchestrion-js for instrumentation#20900

Open
mydea wants to merge 33 commits into
developfrom
experiment/orchestrionjs-auto-instrumentation
Open

WIP: POC to use orchestrion-js for instrumentation#20900
mydea wants to merge 33 commits into
developfrom
experiment/orchestrionjs-auto-instrumentation

Conversation

@mydea

@mydea mydea commented May 15, 2026

Copy link
Copy Markdown
Member

This is a WIP POC trying out usage of orchestrion-js for node SDK instrumentation.

  1. Built a general plan document outlining how this can/should work
  2. Implemented the generic utilities and building blocks needed
  3. Implemented a example integration for mysql package using the new pieces

Honestly it seems pretty straightforward... Usage for this POC is:

node --import @sentry/node/orchestrion app.mjs

And then

// app.mjs
import * as Sentry from '@sentry/node';

const client = Sentry.init({
 // regular setup...
  _experimentalUseOrchestrion: true,
});

// Split this way for better tree shaking
Sentry._experimentalSetupOrchestrion(client);

This will disable the otel instrumentation that is already converted to orchestrion (in this PR, only Mysql) and add the respective orchestrion-based integrations instead. The exact API here is WIP and really just geared towards experimentation, so could change, and it's easy to see how this would be easier in v11 with this being the default.

Some general benefits of this approach:

  1. preload becomes unnecessary as this approach generally behaves like preload - the --import script only registers the mappings for orchestrion, all actual code registering stuff etc. happens in Sentry.init(). This makes a bunch of things easier...
  2. Not tested here, but this should generally work exactly the same if you add the respective vite (and others in the future) plugin, allowing you to skip the --import. This also works when deploying to e.g. cloudflare etc. as long as one of the bundler plugins is used.
  3. The whole approach is much easier to reconcile with dual-system approaches where newer versions have native DC/TC support - just need to register different channel names mostly to get stuff working.

Comment thread yarn.lock Outdated
Comment thread yarn.lock Outdated
Comment thread packages/node/src/orchestrion/runtime/require-hook.cjs Outdated
@github-actions

github-actions Bot commented May 15, 2026

Copy link
Copy Markdown
Contributor

size-limit report 📦

Path Size % Change Change
@sentry/browser 27.4 kB - -
@sentry/browser - with treeshaking flags 25.84 kB - -
@sentry/browser (incl. Tracing) 45.7 kB - -
@sentry/browser (incl. Tracing + Span Streaming) 47.94 kB - -
@sentry/browser (incl. Tracing, Profiling) 50.5 kB - -
@sentry/browser (incl. Tracing, Replay) 84.92 kB - -
@sentry/browser (incl. Tracing, Replay) - with treeshaking flags 74.53 kB - -
@sentry/browser (incl. Tracing, Replay with Canvas) 89.61 kB - -
@sentry/browser (incl. Tracing, Replay, Feedback) 102.3 kB - -
@sentry/browser (incl. Feedback) 44.56 kB - -
@sentry/browser (incl. sendFeedback) 32.2 kB - -
@sentry/browser (incl. FeedbackAsync) 37.31 kB - -
@sentry/browser (incl. Metrics) 28.47 kB - -
@sentry/browser (incl. Logs) 28.71 kB - -
@sentry/browser (incl. Metrics & Logs) 29.4 kB - -
@sentry/react 29.2 kB - -
@sentry/react (incl. Tracing) 48 kB - -
@sentry/vue 32.42 kB - -
@sentry/vue (incl. Tracing) 47.59 kB - -
@sentry/svelte 27.42 kB - -
CDN Bundle 29.79 kB - -
CDN Bundle (incl. Tracing) 48.2 kB - -
CDN Bundle (incl. Logs, Metrics) 31.33 kB - -
CDN Bundle (incl. Tracing, Logs, Metrics) 49.49 kB - -
CDN Bundle (incl. Replay, Logs, Metrics) 70.62 kB - -
CDN Bundle (incl. Tracing, Replay) 85.52 kB - -
CDN Bundle (incl. Tracing, Replay, Logs, Metrics) 86.77 kB - -
CDN Bundle (incl. Tracing, Replay, Feedback) 91.37 kB - -
CDN Bundle (incl. Tracing, Replay, Feedback, Logs, Metrics) 92.62 kB - -
CDN Bundle - uncompressed 88.59 kB - -
CDN Bundle (incl. Tracing) - uncompressed 145.8 kB - -
CDN Bundle (incl. Logs, Metrics) - uncompressed 93.29 kB - -
CDN Bundle (incl. Tracing, Logs, Metrics) - uncompressed 149.77 kB - -
CDN Bundle (incl. Replay, Logs, Metrics) - uncompressed 218.12 kB - -
CDN Bundle (incl. Tracing, Replay) - uncompressed 264.67 kB - -
CDN Bundle (incl. Tracing, Replay, Logs, Metrics) - uncompressed 268.63 kB - -
CDN Bundle (incl. Tracing, Replay, Feedback) - uncompressed 278.37 kB - -
CDN Bundle (incl. Tracing, Replay, Feedback, Logs, Metrics) - uncompressed 282.31 kB - -
@sentry/nextjs (client) 50.45 kB - -
@sentry/sveltekit (client) 46.12 kB - -
@sentry/core/server 76.08 kB - -
@sentry/core/browser 63.22 kB - -
@sentry/node-core 61.72 kB -0.01% -3 B 🔽
@sentry/node 130.91 kB +0.3% +390 B 🔺
@sentry/node - without tracing 74.37 kB +0.37% +268 B 🔺
@sentry/aws-serverless 86.62 kB +0.39% +332 B 🔺
@sentry/cloudflare (withSentry) - minified 173.69 kB - -
@sentry/cloudflare (withSentry) 433.85 kB - -
@sentry/node/import (ESM hook with diagnostics-channel injection) 69.88 kB added added
@sentry/node/light 50.77 kB added added

View base workflow run

Comment thread packages/server-utils/src/integrations/tracing-channel/mysql.ts
Comment thread packages/server-utils/src/integrations/tracing-channel/mysql.ts
@mydea

mydea commented May 15, 2026

Copy link
Copy Markdown
Member Author

Note: dependency warning stuff should be addressed when this is merged/released: apm-js-collab/code-transformer-bundler-plugins#2

Comment thread packages/node/src/orchestrion/runtime/import-hook.mjs Outdated
@mydea mydea force-pushed the experiment/orchestrionjs-auto-instrumentation branch from b1b6ed6 to 9e8b070 Compare May 18, 2026 07:29
Comment thread packages/server-utils/src/integrations/tracing-channel/mysql.ts
Comment thread dev-packages/node-integration-tests/suites/tracing/mysql/scenario-orchestrion.mjs Outdated
@mydea mydea force-pushed the experiment/orchestrionjs-auto-instrumentation branch from 9e8b070 to 26ccdf4 Compare May 18, 2026 09:03
Comment thread packages/node/src/orchestrion/setup.ts Outdated
@mydea mydea force-pushed the experiment/orchestrionjs-auto-instrumentation branch from 26ccdf4 to c8be420 Compare May 18, 2026 13:34
Comment thread packages/node/src/orchestrion/setup.ts Outdated
Comment thread packages/node/src/orchestrion/runtime/import-hook.mjs Outdated
Comment thread packages/node/src/orchestrion/runtime/import-hook.mjs Outdated
@mydea mydea force-pushed the experiment/orchestrionjs-auto-instrumentation branch from a38481b to c095626 Compare May 19, 2026 07:20
Comment thread packages/node/src/orchestrion/setup.ts Outdated
Comment thread packages/server-utils/src/integrations/tracing-channel/mysql.ts
Comment thread packages/node/src/orchestrion/runtime/import-hook.mjs Outdated
Comment thread packages/server-utils/src/orchestrion/detect.ts
Comment thread packages/node/src/orchestrion/bundler/vite.ts
Comment thread packages/node/src/orchestrion/runtime/require-hook.cjs Outdated
@mydea mydea force-pushed the experiment/orchestrionjs-auto-instrumentation branch from 5d957bf to 12ac21c Compare May 21, 2026 11:02
@mydea

mydea commented May 21, 2026

Copy link
Copy Markdown
Member Author

I also added an e2e test app using the vite plugin, however this is failing, will need to wait on v0.2.0 of the plugins as this updates to the latest version of the code transformer, which we need here.

Comment thread packages/server-utils/src/orchestrion/detect.ts
@mydea mydea force-pushed the experiment/orchestrionjs-auto-instrumentation branch from 7d6f1f7 to 3de78da Compare May 26, 2026 09:40
Comment thread packages/node/src/orchestrion/runtime/import-hook.mjs Outdated
Comment thread dev-packages/e2e-tests/test-applications/node-express-orchestrion-cjs/src/app.js Dismissed
Comment thread dev-packages/e2e-tests/test-applications/node-express-orchestrion/src/app.mjs Dismissed
Comment thread packages/node/src/sdk/index.ts

@isaacs isaacs left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is looking very good!

My only concern/suggestion is that it should really not be node-specific. Deno and Bun are both supported by orchestrion now, so it'd be good to move this logic into a shared location so they can all benefit from it as we add instrumentations.

I'm not sure if it's best to (a) land this first, and then refactor this and the other @sentry/core/server stuff into @sentry-internal/server-utils, or (b) if we should bite that bullet first and then move this implementation on top of it. If we go with (a), then the move would be to land this now, and do server-utils as a second step. If (b), then it'd be good to get that done, and port this on top of it.

Comment thread packages/server-utils/src/integrations/tracing-channel/mysql.ts
Comment thread packages/node/src/orchestrion/runtime/import-hook.mjs Outdated
@mydea

mydea commented May 27, 2026

Copy link
Copy Markdown
Member Author

This is looking very good!

My only concern/suggestion is that it should really not be node-specific. Deno and Bun are both supported by orchestrion now, so it'd be good to move this logic into a shared location so they can all benefit from it as we add instrumentations.

I'm not sure if it's best to (a) land this first, and then refactor this and the other @sentry/core/server stuff into @sentry-internal/server-utils, or (b) if we should bite that bullet first and then move this implementation on top of it. If we go with (a), then the move would be to land this now, and do server-utils as a second step. If (b), then it'd be good to get that done, and port this on top of it.

jup, eventually this should go into a new server-utils package or similar I guess - we can move this up any time!

mydea and others added 25 commits June 12, 2026 14:04
Also, fix the `import ... from 'node:module'` in the import-hook.mjs,
which was broken.
The test was loading the import hook via `import`, which would get
converted to a `require()` when testing CommonJS. This, obviously, would
break, because the import loader is ESM only.

Update the test to register the import loader in the traditional way,
with `--import`, so that we can verify it covers both CJS and ESM.

Update the Orchestrion Plan so that the agents don't get confused when
the require hook is missing.
Also, disable the `Module.register()` path on Deno and Bun, because the
function is present but no-opped on those platforms.
Older versions of TS do not find this from package.json submodule exports.
The AI agents reviewing the code are concerned that we're not warning
in production, but that would be a bad idea in this case, so put a
comment to make the intent clearer.
@isaacs isaacs force-pushed the experiment/orchestrionjs-auto-instrumentation branch from 7b69e3b to d7addf6 Compare June 12, 2026 21:10
Comment thread packages/node/src/orchestrion/setup.ts Outdated
Polish the the orchestrion.js-based auto-instrumentation so the Node SDK
presents a minimal API surface that does not expose the internal
"orchestrion" term on the public API surface.

A single opt-in option now does the entire Orchestrion setup.

```ts
Sentry.init({
  dsn: '__DSN__',
  experimentalDiagnosticsChannelInjection: true,
})
```

Assuming that this is run in a user's local `--import` module (or,
failing that, before the instrumented modules are loaded), then the
appropriate hooks will be synchronously added, and Orchestrion
diagnostics_channel-based integrations will be used instead of the
legacy OTel integrations.

The previous three-step setup (`_experimentalUseOrchestrion` flag,
`--import @sentry/node/orchestrion`, plus a separate
`_experimentalSetupOrchestrion()` method call) is removed.

The channel-injection hooks are registered synchronously (mirroring
`esmLoader.ts`: `Module.register(...)` + `ModulePatch` on Node <24.13,
`Module.registerHooks` on newer / Deno 2.8+), so they are in place
before the app's own `import`s resolve. A dynamic `import()` would have
raced module loading.

The `@sentry/node/import` loader hook script injects the channels
**unconditionally**, as the presence of the channels is harmless when
they are not being used. They are only *subscribed* to if the opt-in
flag is set.

The version detection is moved to a single source of truth in
`server-utils/orchestrion/runtime/register.ts`, so that it can be
initialized synchronously as part of `Sentry.init()`, or in the
`import-hook.mjs` loader.

All references to `orchestrion` are removed from the public API surface.
The term remains in `server-utils`, of course, as this is an internal
package designed to house shared implementation details across
server-side JS platforms.

The `@sentry/node/orchestrion/vite` subpath export is removed, as that
really isn't used by anything, and was just a pass-through for
`@sentry/server-utils/orchestrion/vite` anyway. The Bun and Edge
computing SDKs will use this directly to instrument using Orchestrion.

@cursor cursor Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cursor Bugbot has reviewed your changes and found 2 potential issues.

Fix All in Cursor

❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.

Reviewed by Cursor Bugbot for commit e27b2e9. Configure here.

'`node --import ./instrument.mjs app.js`), or inject the channels with ' +
'`node --import @sentry/node/import app.js`.',
);
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Late init misreports injection success

Medium Severity

After registerDiagnosticsChannelInjection() succeeds, __SENTRY_ORCHESTRION__.runtime is set even when instrumented modules such as mysql were already loaded, so hooks never transform them. The DEBUG-only warning only runs when that marker is missing, so the late-init case described in the function comment never triggers and MySQL spans can fail silently.

Additional Locations (1)
Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit e27b2e9. Configure here.

// possible (before the app imports its instrumented modules) when opted in.
if (options.experimentalDiagnosticsChannelInjection) {
installDiagnosticsChannelInjection();
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Channel mysql ignores tracing disabled

Medium Severity

With experimentalDiagnosticsChannelInjection: true, the SDK always installs channel injection hooks and adds the channel-based Mysql integration, even when hasSpansEnabled is false. The default OTel Mysql integration is only registered when span recording is enabled, so this path can instrument queries when tracing was intentionally turned off.

Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit e27b2e9. Configure here.

Comment on lines +52 to +54
'`node --import ./instrument.mjs app.js`), or inject the channels with ' +
'`node --import @sentry/node/import app.js`.',
);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Bug: The warning in installDiagnosticsChannelInjection() for late Sentry.init() calls is broken. It incorrectly checks for registration success instead of timing, so it never fires when modules are imported before initialization.
Severity: MEDIUM

Suggested Fix

The warning logic should be changed to detect if modules were already loaded before the diagnostics channel hooks were registered, rather than just checking if the registration function succeeded. This might involve setting a flag earlier in the application lifecycle (e.g., via --import) and checking its state within Sentry.init() to determine if initialization is happening too late.

Prompt for AI Agent
Review the code at the location below. A potential bug has been identified by an AI
agent. Verify if this is a real issue. If it is, propose a fix; if not, explain why it's
not valid.

Location: packages/node/src/sdk/diagnosticsChannelInjection.ts#L52-L54

Potential issue: The warning logic in `installDiagnosticsChannelInjection()` is flawed.
It is intended to alert developers if `Sentry.init()` is called after instrumented
modules have already been imported, which would cause instrumentation to fail. However,
the check `!marker?.runtime && !marker?.bundler` will never trigger in this scenario.
This is because `registerDiagnosticsChannelInjection()` sets `marker.runtime = true`
upon successful hook registration, regardless of whether it was too late. As a result,
developers who misconfigure their application by initializing Sentry after importing a
module like `mysql` will not receive the intended debug warning, leading to silent
instrumentation failure.

Also affects:

  • packages/server-utils/src/orchestrion/runtime/register.ts:98~98

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants