diff --git a/CHANGELOG.md b/CHANGELOG.md
index 7a8f1649628b..86436b9158be 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -6,6 +6,14 @@
Work in this release was contributed by @psh4607, @trinitiwowka, @nehaprasad-dev, and @JealousGx. Thank you for your contributions!
+- feat(deno)!: Rename several default integrations to match the other SDKs ([#22404](https://github.com/getsentry/sentry-javascript/pull/22404)). The `deno*Integration` exports are kept as deprecated aliases. If you were relying on the names (for example, to disable them), then note that these have changed:
+ - `DenoAmqplib` => `Amqplib`
+ - `DenoKoa` => `Koa`
+ - `DenoMongodb` => `Mongodb`
+ - `DenoMongoose` => `Mongoose`
+ - `DenoMysql` => `Mysql`
+ - `DenoPostgres` => `Postgres`
+
## 10.67.0
### Important Changes
diff --git a/MIGRATION.md b/MIGRATION.md
index e2d20c5f5118..7e8737512c10 100644
--- a/MIGRATION.md
+++ b/MIGRATION.md
@@ -527,6 +527,19 @@ The `childProcessIntegration` was split into a `childProcessIntegration` (for `c
> **TODO(v11):** Document how the two integrations are configured and what users who customized
> `childProcessIntegration` need to change.
+### Deno default integrations renamed to match the other SDKs
+
+Affected SDKs: `@sentry/deno`.
+
+Several default integrations were renamed to match the names used by the other SDKs. The old `deno*Integration` exports are kept as deprecated aliases. If you relied on the old names (for example, to disable an integration), update them:
+
+- `DenoAmqplib` => `Amqplib`
+- `DenoKoa` => `Koa`
+- `DenoMongodb` => `Mongodb`
+- `DenoMongoose` => `Mongoose`
+- `DenoMysql` => `Mysql`
+- `DenoPostgres` => `Postgres`
+
## 6. Type Changes
- Several public types that used `any` now use `unknown` — including `StackFrame`, `SamplingContext`,
diff --git a/dev-packages/deno-integration-tests/suites/direct-client-acs/scenario.mjs b/dev-packages/deno-integration-tests/suites/direct-client-acs/scenario.mjs
new file mode 100644
index 000000000000..beec4d932d13
--- /dev/null
+++ b/dev-packages/deno-integration-tests/suites/direct-client-acs/scenario.mjs
@@ -0,0 +1,52 @@
+// Spawned by test.ts via `deno run`, in a fresh process so nothing else has
+// installed the AsyncLocalStorage context strategy.
+//
+// This builds a `DenoClient` DIRECTLY — `new DenoClient(...)` + `client.init()`
+// instead of calling `Sentry.init()`, then drives the mysql orchestrion channel
+// The mysql subscriber only binds once the ALS context strategy is installed
+// (it waits for the tracing-channel binding), so a nested db span here proves
+// `DenoClient.init()` installs that strategy on the direct-construction path.
+// Without it, the subscriber never binds and no span is produced.
+import { createStackParser, nodeStackLineParser } from '@sentry/core';
+import { DenoClient, getCurrentScope, getDefaultIntegrations, startSpan } from '@sentry/deno';
+import { tracingChannel } from 'node:diagnostics_channel';
+
+let nested = false;
+
+const client = new DenoClient({
+ dsn: 'https://username@domain/123',
+ tracesSampleRate: 1,
+ integrations: getDefaultIntegrations({}),
+ stackParser: createStackParser(nodeStackLineParser()),
+ beforeSendTransaction(event) {
+ const spans = event.spans ?? [];
+ if (spans.some(s => s.op === 'db' && s.data?.['sentry.origin'] === 'auto.db.orchestrion.mysql')) {
+ nested = true;
+ }
+ return null;
+ },
+ transport: () => ({ send: () => Promise.resolve({}), flush: () => Promise.resolve(true) }),
+});
+
+client.init();
+getCurrentScope().setClient(client);
+
+const channel = tracingChannel('orchestrion:mysql:query');
+const ctx = {
+ arguments: ['SELECT 1 AS solution'],
+ self: { config: { host: '127.0.0.1', port: 3306, database: 'mydb', user: 'root' } },
+};
+
+startSpan({ name: 'parent', op: 'test' }, () => {
+ channel.start.runStores(ctx, () => {
+ channel.end.publish(ctx);
+ });
+ channel.asyncStart.runStores(ctx, () => {
+ channel.asyncEnd.publish(ctx);
+ });
+});
+
+await client.flush(2000);
+
+// eslint-disable-next-line no-console
+console.log(`SCENARIO nested=${nested}`);
diff --git a/dev-packages/deno-integration-tests/suites/direct-client-acs/test.ts b/dev-packages/deno-integration-tests/suites/direct-client-acs/test.ts
new file mode 100644
index 000000000000..018e2f8bd594
--- /dev/null
+++ b/dev-packages/deno-integration-tests/suites/direct-client-acs/test.ts
@@ -0,0 +1,38 @@
+//
+
+import { assert } from 'https://deno.land/std@0.212.0/assert/assert.ts';
+import { assertEquals } from 'https://deno.land/std@0.212.0/assert/assert_equals.ts';
+
+// A directly-constructed `DenoClient` (no `Sentry.init()`) is a supported path.
+// The SDK's own tests use it. It must still install the AsyncLocalStorage
+// context strategy, which the channel integrations depend on. We run it in a
+// fresh process so no prior `init()` has installed the strategy already, then
+// assert a nested mysql span appears (see scenario.mjs for why that proves the
+// strategy was installed by `client.init()`).
+Deno.test('DenoClient.init installs the AsyncLocalStorage strategy on the direct-construction path', async () => {
+ const scenario = new URL('./scenario.mjs', import.meta.url);
+
+ // The package root — where `node_modules` (and thus `@sentry/deno`) resolves
+ // for the spawned `deno run`.
+ const cwd = new URL('../../', import.meta.url);
+
+ const command = new Deno.Command('deno', {
+ args: ['run', '--allow-all', scenario.pathname],
+ cwd: cwd.pathname,
+ stdout: 'piped',
+ stderr: 'piped',
+ });
+
+ const { code, stdout, stderr } = await command.output();
+ const out = new TextDecoder().decode(stdout);
+ const err = new TextDecoder().decode(stderr);
+
+ assertEquals(code, 0, `scenario exited ${code}\nstdout:\n${out}\nstderr:\n${err}`);
+
+ const line = out.split('\n').find(l => l.startsWith('SCENARIO')) ?? '';
+ assert(line, `no SCENARIO line in output:\n${out}\nstderr:\n${err}`);
+ assert(
+ line.includes('nested=true'),
+ `expected a nested mysql span via the direct client path (ACS must be installed by client.init), got: ${line}`,
+ );
+});
diff --git a/dev-packages/deno-integration-tests/suites/orchestrion-amqplib/test.ts b/dev-packages/deno-integration-tests/suites/orchestrion-amqplib/test.ts
new file mode 100644
index 000000000000..a713c83e6327
--- /dev/null
+++ b/dev-packages/deno-integration-tests/suites/orchestrion-amqplib/test.ts
@@ -0,0 +1,110 @@
+//
+
+import { tracingChannel } from 'node:diagnostics_channel';
+import type { TransactionEvent } from '@sentry/core';
+import type { DenoClient } from '@sentry/deno';
+import { getCurrentScope, getGlobalScope, getIsolationScope, init, startSpan } from '@sentry/deno';
+import { assert } from 'https://deno.land/std@0.212.0/assert/assert.ts';
+import { assertExists } from 'https://deno.land/std@0.212.0/assert/assert_exists.ts';
+import { assertEquals } from 'https://deno.land/std@0.212.0/assert/assert_equals.ts';
+
+function resetGlobals(): void {
+ getCurrentScope().clear();
+ getCurrentScope().setClient(undefined);
+ getIsolationScope().clear();
+ getGlobalScope().clear();
+}
+
+/** See deno-redis.test.ts — same sink shape, deduped for clarity. */
+function transactionSink(): {
+ beforeSendTransaction: (event: TransactionEvent) => null;
+ waitFor: (predicate: (event: TransactionEvent) => boolean) => Promise;
+} {
+ const transactions: TransactionEvent[] = [];
+ const waiters: { predicate: (e: TransactionEvent) => boolean; resolve: (e: TransactionEvent) => void }[] = [];
+ return {
+ beforeSendTransaction(event) {
+ transactions.push(event);
+ for (let i = waiters.length - 1; i >= 0; i--) {
+ const w = waiters[i]!;
+ if (w.predicate(event)) {
+ waiters.splice(i, 1);
+ w.resolve(event);
+ }
+ }
+ return null;
+ },
+ waitFor(predicate) {
+ const already = transactions.find(predicate);
+ if (already) return Promise.resolve(already);
+ return new Promise(resolve => {
+ waiters.push({ predicate, resolve });
+ });
+ },
+ };
+}
+
+function withTimeout(p: Promise, ms: number, what: string): Promise {
+ let timer: ReturnType | undefined;
+ const timeout = new Promise((_, reject) => {
+ timer = setTimeout(() => reject(new Error(`Timed out waiting for ${what} after ${ms}ms`)), ms);
+ });
+ return Promise.race([p, timeout]).finally(() => {
+ if (timer !== undefined) clearTimeout(timer);
+ });
+}
+
+Deno.test('amqplib instrumentation: included in default integrations (Deno 2.8.0+)', () => {
+ resetGlobals();
+ const client = init({ dsn: 'https://username@domain/123' }) as DenoClient;
+ const names = client.getOptions().integrations.map(i => i.name);
+ assert(names.includes('Amqplib'), `Amqplib should be in defaults, got ${names.join(', ')}`);
+});
+
+// Exercises the SDK path end-to-end: `init()` installs the AsyncLocalStorage
+// context strategy and wires the default `amqplibChannelIntegration` (which
+// subscribes to the channel), and we drive the `orchestrion:amqplib:publish`
+// channel manually — the same events the orchestrion transform publishes around
+// `Channel.prototype.publish` — so no live broker is needed. Asserting a nested
+// producer `message` span proves the subscriber, the emitted attributes, AND the
+// context-strategy wiring all work.
+Deno.test('amqplib instrumentation: orchestrion:amqplib:publish channel produces a nested message span', async () => {
+ resetGlobals();
+ const sink = transactionSink();
+ init({
+ dsn: 'https://username@domain/123',
+ tracesSampleRate: 1,
+ beforeSendTransaction: sink.beforeSendTransaction,
+ });
+
+ const channel = tracingChannel('orchestrion:amqplib:publish');
+
+ // `publish(exchange, routingKey, content, options)`; `self.connection` carries
+ // the server product used for `messaging.system`.
+ const ctx = {
+ self: { connection: { serverProperties: { product: 'RabbitMQ' } } },
+ arguments: ['my-exchange', 'my.routing.key', new Uint8Array(), { messageId: 'msg-1' }],
+ };
+
+ startSpan({ name: 'parent', op: 'test' }, () => {
+ channel.start.runStores(ctx, () => {
+ channel.end.publish(ctx);
+ });
+ channel.asyncStart.runStores(ctx, () => {
+ channel.asyncEnd.publish(ctx);
+ });
+ });
+
+ const parent = await withTimeout(
+ sink.waitFor(t => t.transaction === 'parent'),
+ 5000,
+ "'parent' transaction",
+ );
+
+ const publishSpan = parent.spans?.find(s => s.op === 'message');
+ assertExists(publishSpan, `expected a message child span, got ops: ${parent.spans?.map(s => s.op).join(', ')}`);
+ assertEquals(publishSpan!.description, 'publish my-exchange');
+ assertEquals(publishSpan!.data?.['messaging.destination.name'], 'my-exchange');
+ assertEquals(publishSpan!.data?.['messaging.system'], 'rabbitmq');
+ assertEquals(publishSpan!.data?.['sentry.origin'], 'auto.amqplib.orchestrion.publisher');
+});
diff --git a/dev-packages/deno-integration-tests/suites/orchestrion-koa/test.ts b/dev-packages/deno-integration-tests/suites/orchestrion-koa/test.ts
new file mode 100644
index 000000000000..8c1564b84853
--- /dev/null
+++ b/dev-packages/deno-integration-tests/suites/orchestrion-koa/test.ts
@@ -0,0 +1,103 @@
+//
+
+import { tracingChannel } from 'node:diagnostics_channel';
+import type { TransactionEvent } from '@sentry/core';
+import type { DenoClient } from '@sentry/deno';
+import { getCurrentScope, getGlobalScope, getIsolationScope, init, startSpan } from '@sentry/deno';
+import { assert } from 'https://deno.land/std@0.212.0/assert/assert.ts';
+import { assertExists } from 'https://deno.land/std@0.212.0/assert/assert_exists.ts';
+import { assertEquals } from 'https://deno.land/std@0.212.0/assert/assert_equals.ts';
+
+function resetGlobals(): void {
+ getCurrentScope().clear();
+ getCurrentScope().setClient(undefined);
+ getIsolationScope().clear();
+ getGlobalScope().clear();
+}
+
+/** See deno-redis.test.ts — same sink shape, deduped for clarity. */
+function transactionSink(): {
+ beforeSendTransaction: (event: TransactionEvent) => null;
+ waitFor: (predicate: (event: TransactionEvent) => boolean) => Promise;
+} {
+ const transactions: TransactionEvent[] = [];
+ const waiters: { predicate: (e: TransactionEvent) => boolean; resolve: (e: TransactionEvent) => void }[] = [];
+ return {
+ beforeSendTransaction(event) {
+ transactions.push(event);
+ for (let i = waiters.length - 1; i >= 0; i--) {
+ const w = waiters[i]!;
+ if (w.predicate(event)) {
+ waiters.splice(i, 1);
+ w.resolve(event);
+ }
+ }
+ return null;
+ },
+ waitFor(predicate) {
+ const already = transactions.find(predicate);
+ if (already) return Promise.resolve(already);
+ return new Promise(resolve => {
+ waiters.push({ predicate, resolve });
+ });
+ },
+ };
+}
+
+function withTimeout(p: Promise, ms: number, what: string): Promise {
+ let timer: ReturnType | undefined;
+ const timeout = new Promise((_, reject) => {
+ timer = setTimeout(() => reject(new Error(`Timed out waiting for ${what} after ${ms}ms`)), ms);
+ });
+ return Promise.race([p, timeout]).finally(() => {
+ if (timer !== undefined) clearTimeout(timer);
+ });
+}
+
+Deno.test('koa instrumentation: included in default integrations (Deno 2.8.0+)', () => {
+ resetGlobals();
+ const client = init({ dsn: 'https://username@domain/123' }) as DenoClient;
+ const names = client.getOptions().integrations.map(i => i.name);
+ assert(names.includes('Koa'), `Koa should be in defaults, got ${names.join(', ')}`);
+});
+
+// Exercises the SDK path end-to-end. Unlike the db integrations, koa's channel
+// doesn't build a span directly: its `start` handler wraps the registered
+// middleware (arg 0) in a span-creating proxy, and the span opens when that
+// middleware later runs under an active span. So we publish `orchestrion:koa:use`
+// with a middleware, then invoke the wrapped middleware inside a parent span —
+// the same shape `app.use(fn)` then a request produces. Asserting a nested
+// `middleware.koa` span proves the subscriber and context wiring work.
+Deno.test('koa instrumentation: orchestrion:koa:use channel wraps middleware into a span', async () => {
+ resetGlobals();
+ const sink = transactionSink();
+ init({
+ dsn: 'https://username@domain/123',
+ tracesSampleRate: 1,
+ beforeSendTransaction: sink.beforeSendTransaction,
+ });
+
+ function myMiddleware(_context: unknown, next: () => Promise): Promise {
+ return next();
+ }
+
+ // Publishing `start` runs the subscriber, which patches `arguments[0]` in place.
+ const ctx = { arguments: [myMiddleware] as unknown[] };
+ tracingChannel('orchestrion:koa:use').start.publish(ctx);
+ const wrappedMiddleware = ctx.arguments[0] as typeof myMiddleware;
+
+ await startSpan({ name: 'parent', op: 'test' }, async () => {
+ await wrappedMiddleware({}, () => Promise.resolve());
+ });
+
+ const parent = await withTimeout(
+ sink.waitFor(t => t.transaction === 'parent'),
+ 5000,
+ "'parent' transaction",
+ );
+
+ const koaSpan = parent.spans?.find(s => s.op === 'middleware.koa');
+ assertExists(koaSpan, `expected a middleware.koa child span, got ops: ${parent.spans?.map(s => s.op).join(', ')}`);
+ assertEquals(koaSpan!.description, 'myMiddleware');
+ assertEquals(koaSpan!.data?.['sentry.origin'], 'auto.http.orchestrion.koa');
+});
diff --git a/dev-packages/deno-integration-tests/suites/orchestrion-mongo/test.ts b/dev-packages/deno-integration-tests/suites/orchestrion-mongo/test.ts
new file mode 100644
index 000000000000..112c8e2ed74c
--- /dev/null
+++ b/dev-packages/deno-integration-tests/suites/orchestrion-mongo/test.ts
@@ -0,0 +1,117 @@
+//
+
+import { tracingChannel } from 'node:diagnostics_channel';
+import type { TransactionEvent } from '@sentry/core';
+import type { DenoClient } from '@sentry/deno';
+import { getCurrentScope, getGlobalScope, getIsolationScope, init, startSpan } from '@sentry/deno';
+import { assert } from 'https://deno.land/std@0.212.0/assert/assert.ts';
+import { assertEquals } from 'https://deno.land/std@0.212.0/assert/assert_equals.ts';
+import { assertExists } from 'https://deno.land/std@0.212.0/assert/assert_exists.ts';
+
+function resetGlobals(): void {
+ getCurrentScope().clear();
+ getCurrentScope().setClient(undefined);
+ getIsolationScope().clear();
+ getGlobalScope().clear();
+}
+
+/** See deno-redis.test.ts — same sink shape, deduped for clarity. */
+function transactionSink(): {
+ beforeSendTransaction: (event: TransactionEvent) => null;
+ waitFor: (predicate: (event: TransactionEvent) => boolean) => Promise;
+} {
+ const transactions: TransactionEvent[] = [];
+ const waiters: { predicate: (e: TransactionEvent) => boolean; resolve: (e: TransactionEvent) => void }[] = [];
+ return {
+ beforeSendTransaction(event) {
+ transactions.push(event);
+ for (let i = waiters.length - 1; i >= 0; i--) {
+ const w = waiters[i]!;
+ if (w.predicate(event)) {
+ waiters.splice(i, 1);
+ w.resolve(event);
+ }
+ }
+ return null;
+ },
+ waitFor(predicate) {
+ const already = transactions.find(predicate);
+ if (already) return Promise.resolve(already);
+ return new Promise(resolve => {
+ waiters.push({ predicate, resolve });
+ });
+ },
+ };
+}
+
+function withTimeout(p: Promise, ms: number, what: string): Promise {
+ let timer: ReturnType | undefined;
+ const timeout = new Promise((_, reject) => {
+ timer = setTimeout(() => reject(new Error(`Timed out waiting for ${what} after ${ms}ms`)), ms);
+ });
+ return Promise.race([p, timeout]).finally(() => {
+ if (timer !== undefined) clearTimeout(timer);
+ });
+}
+
+Deno.test('mongodb instrumentation: included in default integrations (Deno 2.8.0+)', () => {
+ resetGlobals();
+ const client = init({ dsn: 'https://username@domain/123' }) as DenoClient;
+ const names = client.getOptions().integrations.map(i => i.name);
+ assert(names.includes('Mongo'), `Mongo should be in defaults, got ${names.join(', ')}`);
+});
+
+// Exercises the SDK path end-to-end: `init()` installs the AsyncLocalStorage
+// context strategy and wires the default `mongodbChannelIntegration` (which
+// subscribes to the channel), and we drive the `orchestrion:mongodb:command`
+// channel manually — the same events the orchestrion transform publishes around
+// `Connection.prototype.command` — so no live database is needed. Asserting a
+// nested `db` span proves the subscriber, the emitted attributes, AND the
+// context-strategy wiring all work.
+Deno.test('mongodb instrumentation: orchestrion:mongodb:command channel produces a nested db span', async () => {
+ resetGlobals();
+ const sink = transactionSink();
+ init({
+ dsn: 'https://username@domain/123',
+ tracesSampleRate: 1,
+ beforeSendTransaction: sink.beforeSendTransaction,
+ });
+
+ const channel = tracingChannel('orchestrion:mongodb:command');
+
+ // `arguments[0]` is the namespace, `arguments[1]` the command doc (its first
+ // key is the operation); `self.address` is the connection's host:port.
+ const ctx = {
+ self: { address: '127.0.0.1:27017' },
+ arguments: [
+ { db: 'mydb', collection: 'users' },
+ { find: 'users', filter: { name: 'test' } },
+ ],
+ };
+
+ startSpan({ name: 'parent', op: 'test' }, () => {
+ channel.start.runStores(ctx, () => {
+ channel.end.publish(ctx);
+ });
+ channel.asyncStart.runStores(ctx, () => {
+ channel.asyncEnd.publish(ctx);
+ });
+ });
+
+ const parent = await withTimeout(
+ sink.waitFor(t => t.transaction === 'parent'),
+ 5000,
+ "'parent' transaction",
+ );
+
+ const mongoSpan = parent.spans?.find(s => s.op === 'db');
+ assertExists(mongoSpan, `expected a db child span, got ops: ${parent.spans?.map(s => s.op).join(', ')}`);
+ assertEquals(mongoSpan!.description, 'mongodb.find');
+ assertEquals(mongoSpan!.data?.['db.system'], 'mongodb');
+ assertEquals(mongoSpan!.data?.['db.name'], 'mydb');
+ assertEquals(mongoSpan!.data?.['db.mongodb.collection'], 'users');
+ assertEquals(mongoSpan!.data?.['db.operation'], 'find');
+ assertEquals(mongoSpan!.data?.['net.peer.name'], '127.0.0.1');
+ assertEquals(mongoSpan!.data?.['net.peer.port'], 27017);
+ assertEquals(mongoSpan!.data?.['sentry.origin'], 'auto.db.orchestrion.mongo');
+});
diff --git a/dev-packages/deno-integration-tests/suites/orchestrion-mongoose/test.ts b/dev-packages/deno-integration-tests/suites/orchestrion-mongoose/test.ts
new file mode 100644
index 000000000000..2ca17958006a
--- /dev/null
+++ b/dev-packages/deno-integration-tests/suites/orchestrion-mongoose/test.ts
@@ -0,0 +1,119 @@
+//
+
+import { tracingChannel } from 'node:diagnostics_channel';
+import type { TransactionEvent } from '@sentry/core';
+import type { DenoClient } from '@sentry/deno';
+import { getCurrentScope, getGlobalScope, getIsolationScope, init, startSpan } from '@sentry/deno';
+import { assert } from 'https://deno.land/std@0.212.0/assert/assert.ts';
+import { assertExists } from 'https://deno.land/std@0.212.0/assert/assert_exists.ts';
+import { assertEquals } from 'https://deno.land/std@0.212.0/assert/assert_equals.ts';
+
+function resetGlobals(): void {
+ getCurrentScope().clear();
+ getCurrentScope().setClient(undefined);
+ getIsolationScope().clear();
+ getGlobalScope().clear();
+}
+
+/** See deno-redis.test.ts — same sink shape, deduped for clarity. */
+function transactionSink(): {
+ beforeSendTransaction: (event: TransactionEvent) => null;
+ waitFor: (predicate: (event: TransactionEvent) => boolean) => Promise;
+} {
+ const transactions: TransactionEvent[] = [];
+ const waiters: { predicate: (e: TransactionEvent) => boolean; resolve: (e: TransactionEvent) => void }[] = [];
+ return {
+ beforeSendTransaction(event) {
+ transactions.push(event);
+ for (let i = waiters.length - 1; i >= 0; i--) {
+ const w = waiters[i]!;
+ if (w.predicate(event)) {
+ waiters.splice(i, 1);
+ w.resolve(event);
+ }
+ }
+ return null;
+ },
+ waitFor(predicate) {
+ const already = transactions.find(predicate);
+ if (already) return Promise.resolve(already);
+ return new Promise(resolve => {
+ waiters.push({ predicate, resolve });
+ });
+ },
+ };
+}
+
+function withTimeout(p: Promise, ms: number, what: string): Promise {
+ let timer: ReturnType | undefined;
+ const timeout = new Promise((_, reject) => {
+ timer = setTimeout(() => reject(new Error(`Timed out waiting for ${what} after ${ms}ms`)), ms);
+ });
+ return Promise.race([p, timeout]).finally(() => {
+ if (timer !== undefined) clearTimeout(timer);
+ });
+}
+
+Deno.test('mongoose instrumentation: included in default integrations (Deno 2.8.0+)', () => {
+ resetGlobals();
+ const client = init({ dsn: 'https://username@domain/123' }) as DenoClient;
+ const names = client.getOptions().integrations.map(i => i.name);
+ assert(names.includes('Mongoose'), `Mongoose should be in defaults, got ${names.join(', ')}`);
+});
+
+// Exercises the SDK path end-to-end: `init()` installs the AsyncLocalStorage
+// context strategy and wires the default `mongooseChannelIntegration` (which
+// subscribes to the channel), and we drive the `orchestrion:mongoose:model_save`
+// channel manually — the same events the orchestrion transform publishes around
+// `Model.prototype.save` — so no live database is needed. Asserting a nested
+// `db` span proves the subscriber, the emitted attributes, AND the
+// context-strategy wiring all work.
+Deno.test('mongoose instrumentation: orchestrion:mongoose:model_save channel produces a nested db span', async () => {
+ resetGlobals();
+ const sink = transactionSink();
+ init({
+ dsn: 'https://username@domain/123',
+ tracesSampleRate: 1,
+ beforeSendTransaction: sink.beforeSendTransaction,
+ });
+
+ const channel = tracingChannel('orchestrion:mongoose:model_save');
+
+ // `self` is the mongoose document; its `constructor` carries the collection
+ // (name + connection info) and the model name.
+ const ctx = {
+ self: {
+ constructor: {
+ collection: { name: 'blogposts', conn: { name: 'mydb', user: 'root', host: '127.0.0.1', port: 27017 } },
+ modelName: 'BlogPost',
+ },
+ },
+ };
+
+ startSpan({ name: 'parent', op: 'test' }, () => {
+ channel.start.runStores(ctx, () => {
+ channel.end.publish(ctx);
+ });
+ channel.asyncStart.runStores(ctx, () => {
+ channel.asyncEnd.publish(ctx);
+ });
+ });
+
+ const parent = await withTimeout(
+ sink.waitFor(t => t.transaction === 'parent'),
+ 5000,
+ "'parent' transaction",
+ );
+
+ const mongooseSpan = parent.spans?.find(s => s.op === 'db');
+ assertExists(mongooseSpan, `expected a db child span, got ops: ${parent.spans?.map(s => s.op).join(', ')}`);
+ assertEquals(mongooseSpan!.description, 'mongoose.BlogPost.save');
+ assertEquals(mongooseSpan!.data?.['db.system'], 'mongoose');
+ assertEquals(mongooseSpan!.data?.['db.name'], 'mydb');
+ assertEquals(mongooseSpan!.data?.['db.mongodb.collection'], 'blogposts');
+ assertEquals(mongooseSpan!.data?.['db.operation'], 'save');
+ assertEquals(mongooseSpan!.data?.['db.user'], 'root');
+ assertEquals(mongooseSpan!.data?.['net.peer.name'], '127.0.0.1');
+ assertEquals(mongooseSpan!.data?.['net.peer.port'], 27017);
+ assertEquals(mongooseSpan!.data?.['sentry.origin'], 'auto.db.orchestrion.mongoose');
+});
diff --git a/dev-packages/deno-integration-tests/suites/orchestrion-mysql/test.ts b/dev-packages/deno-integration-tests/suites/orchestrion-mysql/test.ts
index 6e68395b1681..a7c92d675f44 100644
--- a/dev-packages/deno-integration-tests/suites/orchestrion-mysql/test.ts
+++ b/dev-packages/deno-integration-tests/suites/orchestrion-mysql/test.ts
@@ -54,11 +54,11 @@ function withTimeout(p: Promise, ms: number, what: string): Promise {
});
}
-Deno.test('denoMysqlIntegration: included in default integrations (Deno 2.8.0+)', () => {
+Deno.test('mysql instrumentation: included in default integrations (Deno 2.8.0+)', () => {
resetGlobals();
const client = init({ dsn: 'https://username@domain/123' }) as DenoClient;
const names = client.getOptions().integrations.map(i => i.name);
- assert(names.includes('DenoMysql'), `DenoMysql should be in defaults, got ${names.join(', ')}`);
+ assert(names.includes('Mysql'), `Mysql should be in defaults, got ${names.join(', ')}`);
});
// The orchestrion runtime hook (`@sentry/deno/import`) only works as a FIRST
@@ -95,13 +95,7 @@ Deno.test('@sentry/deno/import: transforms mysql so it publishes the orchestrion
assert(line.includes('"runtime":["mysql"]'), `expected runtime marker, got: ${line}`);
});
-// Exercises the SDK path end-to-end: `init()` wires `denoMysqlIntegration`
-// (which installs the AsyncLocalStorage context strategy and subscribes to the
-// channel), and we drive the `orchestrion:mysql:query` channel manually — the
-// same events the orchestrion transform publishes around `connection.query()` —
-// so no live database is needed. Asserting a nested `db` span proves the
-// subscriber, the emitted attributes, AND the context-strategy wiring all work.
-Deno.test('denoMysqlIntegration: orchestrion:mysql:query channel produces a nested db span', async () => {
+Deno.test('mysql instrumentation: orchestrion:mysql:query channel produces a nested db span', async () => {
resetGlobals();
const sink = transactionSink();
init({
diff --git a/dev-packages/deno-integration-tests/suites/orchestrion-postgres/test.ts b/dev-packages/deno-integration-tests/suites/orchestrion-postgres/test.ts
index 8584b222c7d7..4d08d5e2505b 100644
--- a/dev-packages/deno-integration-tests/suites/orchestrion-postgres/test.ts
+++ b/dev-packages/deno-integration-tests/suites/orchestrion-postgres/test.ts
@@ -54,11 +54,11 @@ function withTimeout(p: Promise, ms: number, what: string): Promise {
});
}
-Deno.test('denoPostgresIntegration: included in default integrations (Deno 2.8.0+)', () => {
+Deno.test('pg instrumentation: included in default integrations (Deno 2.8.0+)', () => {
resetGlobals();
const client = init({ dsn: 'https://username@domain/123' }) as DenoClient;
const names = client.getOptions().integrations.map(i => i.name);
- assert(names.includes('DenoPostgres'), `DenoPostgres should be in defaults, got ${names.join(', ')}`);
+ assert(names.includes('Postgres'), `Postgres should be in defaults, got ${names.join(', ')}`);
});
// The orchestrion runtime hook (`@sentry/deno/import`) only works as a FIRST
@@ -95,13 +95,7 @@ Deno.test('@sentry/deno/import: transforms pg so it publishes the orchestrion ch
assert(line.includes('"runtime":["pg","pg-pool"]'), `expected runtime marker, got: ${line}`);
});
-// Exercises the SDK path end-to-end: `init()` wires `denoPostgresIntegration`
-// (which installs the AsyncLocalStorage context strategy and subscribes to the
-// channel), and we drive the `orchestrion:pg:query` channel manually — the
-// same events the orchestrion transform publishes around `client.query()` —
-// so no live database is needed. Asserting a nested `db` span proves the
-// subscriber, the emitted attributes, AND the context-strategy wiring all work.
-Deno.test('denoPostgresIntegration: orchestrion:pg:query channel produces a nested db span', async () => {
+Deno.test('pg instrumentation: orchestrion:pg:query channel produces a nested db span', async () => {
resetGlobals();
const sink = transactionSink();
init({
diff --git a/packages/deno/src/client.ts b/packages/deno/src/client.ts
index 36886c8d5a5e..f403b9ba6f0d 100644
--- a/packages/deno/src/client.ts
+++ b/packages/deno/src/client.ts
@@ -1,5 +1,6 @@
import type { ServerRuntimeClientOptions } from '@sentry/core';
import { _INTERNAL_flushLogsBuffer, SDK_VERSION, ServerRuntimeClient } from '@sentry/core';
+import { setAsyncLocalStorageAsyncContextStrategy } from './async';
import type { DenoClientOptions } from './types';
function getHostName(): string | undefined {
@@ -67,6 +68,16 @@ export class DenoClient extends ServerRuntimeClient {
}
}
+ /** @inheritDoc */
+ public init(): void {
+ // The channel-based default integrations propagate scope across async
+ // boundaries via Deno's AsyncLocalStorage context strategy. Install it here,
+ // the setup path both `Sentry.init()` and a directly-constructed client run
+ // through, so it is in place before the integrations subscribe.
+ setAsyncLocalStorageAsyncContextStrategy();
+ super.init();
+ }
+
/** @inheritDoc */
// @ts-expect-error - PromiseLike is a subset of Promise
public async close(timeout?: number | undefined): PromiseLike {
diff --git a/packages/deno/src/index.ts b/packages/deno/src/index.ts
index 58611921021d..ab41f4179bad 100644
--- a/packages/deno/src/index.ts
+++ b/packages/deno/src/index.ts
@@ -111,14 +111,32 @@ export { denoHttpIntegration } from './integrations/http';
export type { DenoHttpIntegrationOptions } from './integrations/http';
export { denoRedisIntegration } from './integrations/redis';
export type { DenoRedisIntegrationOptions } from './integrations/redis';
+// The orchestrion channel integrations, re-exported from `@sentry/server-utils`.
+// The first six are in the default set; `dataloader` and `knex` are opt-in (add
+// them to `integrations` to enable), matching Node.
+export {
+ amqplibChannelIntegration,
+ dataloaderChannelIntegration,
+ knexChannelIntegration,
+ koaChannelIntegration,
+ mongodbChannelIntegration,
+ mongooseChannelIntegration,
+ mysqlChannelIntegration,
+ postgresChannelIntegration,
+} from '@sentry/server-utils/orchestrion';
+// Deprecated aliases kept for back-compat. Each forwards to the shared
+// integration above, so its name is the shared name (e.g. `Mysql`), not the old
+// `Deno*` name. See each alias's `@deprecated` note.
+/* eslint-disable typescript/no-deprecated */
export { denoMysqlIntegration } from './integrations/mysql';
export { denoPostgresIntegration } from './integrations/postgres';
export { denoAmqplibIntegration } from './integrations/amqplib';
-export { denoDataloaderIntegration } from './integrations/dataloader';
-export { denoKnexIntegration } from './integrations/knex';
export { denoKoaIntegration } from './integrations/koa';
export { denoMongoIntegration } from './integrations/mongo';
export { denoMongooseIntegration } from './integrations/mongoose';
+export { denoDataloaderIntegration } from './integrations/dataloader';
+export { denoKnexIntegration } from './integrations/knex';
+/* eslint-enable typescript/no-deprecated */
export { denoContextIntegration } from './integrations/context';
export { globalHandlersIntegration } from './integrations/globalhandlers';
export { normalizePathsIntegration } from './integrations/normalizepaths';
diff --git a/packages/deno/src/integrations/amqplib.ts b/packages/deno/src/integrations/amqplib.ts
index 2ff88fd7e939..42a04828f9a3 100644
--- a/packages/deno/src/integrations/amqplib.ts
+++ b/packages/deno/src/integrations/amqplib.ts
@@ -1,34 +1,10 @@
import { amqplibChannelIntegration } from '@sentry/server-utils/orchestrion';
-import type { Integration, IntegrationFn } from '@sentry/core';
-import { defineIntegration, extendIntegration } from '@sentry/core';
-import { setAsyncLocalStorageAsyncContextStrategy } from '../async';
-
-const INTEGRATION_NAME = 'DenoAmqplib' as const;
/**
- * Create spans for `amqplib` publish/consume operations under Deno.
- *
- * `amqplib` channels are injected by the orchestrion runtime hook at load time.
- * The `@sentry/deno/import` loader must be active for this integration to
- * record anything.
+ * Create spans for `amqplib` publish/consume operations under Deno. Included in
+ * the default integrations.
*
- * The channel-subscription logic is shared with the other server runtimes in
- * `@sentry/server-utils`. This just installs Deno's `AsyncLocalStorage` context
- * strategy (so spans nest under the active span and survive amqplib's internal
- * callback dispatch) before delegating.
+ * @deprecated Use `amqplibChannelIntegration` instead. This alias will be
+ * removed in a future major.
*/
-const _denoAmqplibIntegration = (() => {
- const inner = amqplibChannelIntegration();
-
- return extendIntegration(inner, {
- name: INTEGRATION_NAME,
- setupOnce() {
- setAsyncLocalStorageAsyncContextStrategy();
- },
- });
-}) satisfies IntegrationFn;
-
-export const denoAmqplibIntegration = defineIntegration(_denoAmqplibIntegration) as () => Integration & {
- name: 'DenoAmqplib';
- setupOnce: () => void;
-};
+export const denoAmqplibIntegration = amqplibChannelIntegration;
diff --git a/packages/deno/src/integrations/dataloader.ts b/packages/deno/src/integrations/dataloader.ts
index 37aedf926417..192b38e0cc43 100644
--- a/packages/deno/src/integrations/dataloader.ts
+++ b/packages/deno/src/integrations/dataloader.ts
@@ -1,34 +1,10 @@
import { dataloaderChannelIntegration } from '@sentry/server-utils/orchestrion';
-import type { Integration, IntegrationFn } from '@sentry/core';
-import { defineIntegration, extendIntegration } from '@sentry/core';
-import { setAsyncLocalStorageAsyncContextStrategy } from '../async';
-
-const INTEGRATION_NAME = 'DenoDataloader' as const;
/**
- * Create spans for `dataloader` load/batch operations under Deno.
- *
- * `dataloader` channels are injected by the orchestrion runtime hook at load time.
- * The `@sentry/deno/import` loader must be active for this integration to
- * record anything.
+ * Create spans for `dataloader` load/batch operations under Deno. Not a default;
+ * add it to `integrations` to enable.
*
- * The channel-subscription logic is shared with the other server runtimes in
- * `@sentry/server-utils`. This just installs Deno's `AsyncLocalStorage` context
- * strategy (so spans nest under the active span and survive dataloader's deferred
- * batch dispatch) before delegating.
+ * @deprecated Use `dataloaderChannelIntegration` instead. This alias will be
+ * removed in a future major.
*/
-const _denoDataloaderIntegration = (() => {
- const inner = dataloaderChannelIntegration();
-
- return extendIntegration(inner, {
- name: INTEGRATION_NAME,
- setupOnce() {
- setAsyncLocalStorageAsyncContextStrategy();
- },
- });
-}) satisfies IntegrationFn;
-
-export const denoDataloaderIntegration = defineIntegration(_denoDataloaderIntegration) as () => Integration & {
- name: 'DenoDataloader';
- setupOnce: () => void;
-};
+export const denoDataloaderIntegration = dataloaderChannelIntegration;
diff --git a/packages/deno/src/integrations/deno-serve.ts b/packages/deno/src/integrations/deno-serve.ts
index 1bde0dad15d8..18af58b8d60d 100644
--- a/packages/deno/src/integrations/deno-serve.ts
+++ b/packages/deno/src/integrations/deno-serve.ts
@@ -1,6 +1,5 @@
import type { IntegrationFn } from '@sentry/core';
import { debug, defineIntegration } from '@sentry/core';
-import { setAsyncLocalStorageAsyncContextStrategy } from '../async';
import type { RequestHandlerWrapperOptions } from '../wrap-deno-request-handler';
import { wrapDenoRequestHandler } from '../wrap-deno-request-handler';
@@ -63,8 +62,6 @@ const _denoServeIntegration = (() => {
return {
name: INTEGRATION_NAME,
setupOnce() {
- setAsyncLocalStorageAsyncContextStrategy();
-
const originalServe = Deno.serve;
const wrappedServe = instrumentedDenoServe(originalServe);
diff --git a/packages/deno/src/integrations/http.ts b/packages/deno/src/integrations/http.ts
index a256a8f25bf6..6e11f7abaa99 100644
--- a/packages/deno/src/integrations/http.ts
+++ b/packages/deno/src/integrations/http.ts
@@ -11,7 +11,6 @@ import {
HTTP_ON_CLIENT_REQUEST,
HTTP_ON_SERVER_REQUEST,
} from '@sentry/core';
-import { setAsyncLocalStorageAsyncContextStrategy } from '../async';
import {
DENO_VERSION,
HTTP_CLIENT_DIAGNOSTICS_CHANNEL_SUPPORTED,
@@ -111,11 +110,6 @@ const _denoHttpIntegration = ((options: DenoHttpIntegrationOptions = {}) => {
return;
}
- // Wire up Deno's AsyncLocalStorage-backed ACS so the server subscription's
- // `withIsolationScope(clone, ...)` actually activates the cloned scope.
- // Without this, request isolation and span creation degrade silently.
- setAsyncLocalStorageAsyncContextStrategy();
-
if (HTTP_SERVER_DIAGNOSTICS_CHANNEL_SUPPORTED) {
const { [HTTP_ON_SERVER_REQUEST]: onHttpServerRequest } = getHttpServerSubscriptions({
// `spans` falls through to the client's tracing config when unset.
diff --git a/packages/deno/src/integrations/knex.ts b/packages/deno/src/integrations/knex.ts
index 84924ed1c2dc..4467a8b8b1c2 100644
--- a/packages/deno/src/integrations/knex.ts
+++ b/packages/deno/src/integrations/knex.ts
@@ -1,34 +1,10 @@
import { knexChannelIntegration } from '@sentry/server-utils/orchestrion';
-import type { Integration, IntegrationFn } from '@sentry/core';
-import { defineIntegration, extendIntegration } from '@sentry/core';
-import { setAsyncLocalStorageAsyncContextStrategy } from '../async';
-
-const INTEGRATION_NAME = 'DenoKnex' as const;
/**
- * Create spans for `knex` queries under Deno.
- *
- * `knex` channels are injected by the orchestrion runtime hook at load time.
- * The `@sentry/deno/import` loader must be active for this integration to
- * record anything.
+ * Create spans for `knex` queries under Deno. Not a default; add it to
+ * `integrations` to enable.
*
- * The channel-subscription logic is shared with the other server runtimes in
- * `@sentry/server-utils`. This just installs Deno's
- * `AsyncLocalStorage` context strategy (so spans nest under the active
- * span and survive knex's internal callback dispatch) before delegating.
+ * @deprecated Use `knexChannelIntegration` instead. This alias will be removed
+ * in a future major.
*/
-const _denoKnexIntegration = (() => {
- const inner = knexChannelIntegration();
-
- return extendIntegration(inner, {
- name: INTEGRATION_NAME,
- setupOnce() {
- setAsyncLocalStorageAsyncContextStrategy();
- },
- });
-}) satisfies IntegrationFn;
-
-export const denoKnexIntegration = defineIntegration(_denoKnexIntegration) as () => Integration & {
- name: 'DenoKnex';
- setupOnce: () => void;
-};
+export const denoKnexIntegration = knexChannelIntegration;
diff --git a/packages/deno/src/integrations/koa.ts b/packages/deno/src/integrations/koa.ts
index 7f41fd166c43..0ad40fe71909 100644
--- a/packages/deno/src/integrations/koa.ts
+++ b/packages/deno/src/integrations/koa.ts
@@ -1,31 +1,10 @@
import { koaChannelIntegration } from '@sentry/server-utils/orchestrion';
-import type { KoaChannelIntegrationOptions } from '@sentry/server-utils/orchestrion';
-import type { Integration, IntegrationFn } from '@sentry/core';
-import { defineIntegration, extendIntegration } from '@sentry/core';
-import { setAsyncLocalStorageAsyncContextStrategy } from '../async';
-
-const INTEGRATION_NAME = 'DenoKoa' as const;
/**
- * Create spans for `koa` middleware/router layers under Deno. Requires the
- * `@sentry/deno/import` loader. Delegates to the shared subscriber in
- * `@sentry/server-utils`, adding Deno's `AsyncLocalStorage` context strategy so
- * spans nest under the active HTTP server span.
+ * Create spans for `koa` middleware/router layers under Deno. Included in the
+ * default integrations.
+ *
+ * @deprecated Use `koaChannelIntegration` instead. This alias will be removed
+ * in a future major.
*/
-const _denoKoaIntegration = ((options: KoaChannelIntegrationOptions = {}) => {
- const inner = koaChannelIntegration(options);
-
- return extendIntegration(inner, {
- name: INTEGRATION_NAME,
- setupOnce() {
- setAsyncLocalStorageAsyncContextStrategy();
- },
- });
-}) satisfies IntegrationFn;
-
-export const denoKoaIntegration = defineIntegration(_denoKoaIntegration) as (
- options?: KoaChannelIntegrationOptions,
-) => Integration & {
- name: 'DenoKoa';
- setupOnce: () => void;
-};
+export const denoKoaIntegration = koaChannelIntegration;
diff --git a/packages/deno/src/integrations/mongo.ts b/packages/deno/src/integrations/mongo.ts
index 15d6d1e9e028..28146a4d70c2 100644
--- a/packages/deno/src/integrations/mongo.ts
+++ b/packages/deno/src/integrations/mongo.ts
@@ -1,34 +1,10 @@
import { mongodbChannelIntegration } from '@sentry/server-utils/orchestrion';
-import type { Integration, IntegrationFn } from '@sentry/core';
-import { defineIntegration, extendIntegration } from '@sentry/core';
-import { setAsyncLocalStorageAsyncContextStrategy } from '../async';
-
-const INTEGRATION_NAME = 'DenoMongo' as const;
/**
- * Create spans for `mongodb` queries under Deno.
- *
- * `mongodb` channels are injected by the orchestrion runtime hook at load time.
- * The `@sentry/deno/import` loader must be active for this integration to
- * record anything.
+ * Create spans for `mongodb` queries under Deno. Included in the default
+ * integrations.
*
- * The channel-subscription logic is shared with the other server runtimes in
- * `@sentry/server-utils`. This just installs Deno's
- * `AsyncLocalStorage` context strategy (so spans nest under the active
- * span and survive mongodb's internal callback dispatch) before delegating.
+ * @deprecated Use `mongodbChannelIntegration` instead. This alias will be
+ * removed in a future major.
*/
-const _denoMongoIntegration = (() => {
- const inner = mongodbChannelIntegration();
-
- return extendIntegration(inner, {
- name: INTEGRATION_NAME,
- setupOnce() {
- setAsyncLocalStorageAsyncContextStrategy();
- },
- });
-}) satisfies IntegrationFn;
-
-export const denoMongoIntegration = defineIntegration(_denoMongoIntegration) as () => Integration & {
- name: 'DenoMongo';
- setupOnce: () => void;
-};
+export const denoMongoIntegration = mongodbChannelIntegration;
diff --git a/packages/deno/src/integrations/mongoose.ts b/packages/deno/src/integrations/mongoose.ts
index 86e079cd37e6..67df26fe70ed 100644
--- a/packages/deno/src/integrations/mongoose.ts
+++ b/packages/deno/src/integrations/mongoose.ts
@@ -1,34 +1,10 @@
import { mongooseChannelIntegration } from '@sentry/server-utils/orchestrion';
-import type { Integration, IntegrationFn } from '@sentry/core';
-import { defineIntegration, extendIntegration } from '@sentry/core';
-import { setAsyncLocalStorageAsyncContextStrategy } from '../async';
-
-const INTEGRATION_NAME = 'DenoMongoose' as const;
/**
- * Create spans for `mongoose` queries under Deno.
- *
- * `mongoose` channels are injected by the orchestrion runtime hook at load
- * time. The `@sentry/deno/import` loader must be active for this integration
- * to record anything.
+ * Create spans for `mongoose` queries under Deno. Included in the default
+ * integrations.
*
- * The channel-subscription logic is shared with the other server runtimes in
- * `@sentry/server-utils`. This just installs Deno's `AsyncLocalStorage`
- * context strategy (so spans nest under the active span and survive mongoose's
- * internal callback dispatch) before delegating.
+ * @deprecated Use `mongooseChannelIntegration` instead. This alias will be
+ * removed in a future major.
*/
-const _denoMongooseIntegration = (() => {
- const inner = mongooseChannelIntegration();
-
- return extendIntegration(inner, {
- name: INTEGRATION_NAME,
- setupOnce() {
- setAsyncLocalStorageAsyncContextStrategy();
- },
- });
-}) satisfies IntegrationFn;
-
-export const denoMongooseIntegration = defineIntegration(_denoMongooseIntegration) as () => Integration & {
- name: 'DenoMongoose';
- setupOnce: () => void;
-};
+export const denoMongooseIntegration = mongooseChannelIntegration;
diff --git a/packages/deno/src/integrations/mysql.ts b/packages/deno/src/integrations/mysql.ts
index 717f26b1369f..4b98805d868a 100644
--- a/packages/deno/src/integrations/mysql.ts
+++ b/packages/deno/src/integrations/mysql.ts
@@ -1,34 +1,10 @@
import { mysqlChannelIntegration } from '@sentry/server-utils/orchestrion';
-import type { Integration, IntegrationFn } from '@sentry/core';
-import { defineIntegration, extendIntegration } from '@sentry/core';
-import { setAsyncLocalStorageAsyncContextStrategy } from '../async';
-
-const INTEGRATION_NAME = 'DenoMysql' as const;
/**
- * Create spans for `mysql` queries under Deno.
- *
- * `mysql` channels are injected by the orchestrion runtime hook at load time.
- * The `@sentry/deno/import` loader must be active for this integration to
- * record anything.
+ * Create spans for `mysql` queries under Deno. Included in the default
+ * integrations.
*
- * The channel-subscription logic is shared with the other server runtimes in
- * `@sentry/server-utils`. This just installs Deno's
- * `AsyncLocalStorage` context strategy (so spans nest under the active
- * span and survive mysql's internal callback dispatch) before delegating.
+ * @deprecated Use `mysqlChannelIntegration` instead. This alias will be removed
+ * in a future major.
*/
-const _denoMysqlIntegration = (() => {
- const inner = mysqlChannelIntegration();
-
- return extendIntegration(inner, {
- name: INTEGRATION_NAME,
- setupOnce() {
- setAsyncLocalStorageAsyncContextStrategy();
- },
- });
-}) satisfies IntegrationFn;
-
-export const denoMysqlIntegration = defineIntegration(_denoMysqlIntegration) as () => Integration & {
- name: 'DenoMysql';
- setupOnce: () => void;
-};
+export const denoMysqlIntegration = mysqlChannelIntegration;
diff --git a/packages/deno/src/integrations/postgres.ts b/packages/deno/src/integrations/postgres.ts
index b0ccc9b30c95..e1233a441359 100644
--- a/packages/deno/src/integrations/postgres.ts
+++ b/packages/deno/src/integrations/postgres.ts
@@ -1,36 +1,10 @@
import { postgresChannelIntegration } from '@sentry/server-utils/orchestrion';
-import type { IntegrationFn } from '@sentry/core';
-import { defineIntegration, extendIntegration } from '@sentry/core';
-import { setAsyncLocalStorageAsyncContextStrategy } from '../async';
-
-const INTEGRATION_NAME = 'DenoPostgres' as const;
-
-interface DenoPostgresIntegrationOptions {
- /** Whether to skip creating spans for `pg`/`pg-pool` connections. Defaults to `false`. */
- ignoreConnectSpans?: boolean;
-}
/**
- * Create spans for `pg` (node-postgres) queries under Deno.
- *
- * `pg` channels are injected by the orchestrion runtime hook at load time.
- * The `@sentry/deno/import` loader must be active for this integration to
- * record anything.
+ * Create spans for `pg` (node-postgres) queries under Deno. Included in the
+ * default integrations.
*
- * The channel-subscription logic is shared with the other server runtimes in
- * `@sentry/server-utils`. This just installs Deno's
- * `AsyncLocalStorage` context strategy (so spans nest under the active
- * span and survive pg's internal callback dispatch) before delegating.
+ * @deprecated Use `postgresChannelIntegration` instead. This alias will be
+ * removed in a future major.
*/
-const _denoPostgresIntegration = ((options?: DenoPostgresIntegrationOptions) => {
- const inner = postgresChannelIntegration(options);
-
- return extendIntegration(inner, {
- name: INTEGRATION_NAME,
- setupOnce() {
- setAsyncLocalStorageAsyncContextStrategy();
- },
- });
-}) satisfies IntegrationFn;
-
-export const denoPostgresIntegration = defineIntegration(_denoPostgresIntegration);
+export const denoPostgresIntegration = postgresChannelIntegration;
diff --git a/packages/deno/src/integrations/redis.ts b/packages/deno/src/integrations/redis.ts
index 98479eb857cc..713be849562f 100644
--- a/packages/deno/src/integrations/redis.ts
+++ b/packages/deno/src/integrations/redis.ts
@@ -2,7 +2,6 @@ import type { RedisDiagnosticChannelResponseHook } from '@sentry/server-utils';
import { redisIntegration as redisChannelIntegration } from '@sentry/server-utils';
import type { Integration, IntegrationFn } from '@sentry/core';
import { defineIntegration, extendIntegration } from '@sentry/core';
-import { setAsyncLocalStorageAsyncContextStrategy } from '../async';
const INTEGRATION_NAME = 'DenoRedis' as const;
@@ -15,16 +14,12 @@ export interface DenoRedisIntegrationOptions {
}
const _denoRedisIntegration = ((options: DenoRedisIntegrationOptions = {}) => {
- // The diagnostics_channel subscription lives in server-utils so it is shared across runtimes; we
- // extend it here to install Deno's AsyncLocalStorage async-context strategy, which the channel
- // binding reads via `getTracingChannelBinding`. `extendIntegration` runs the base `setupOnce`
- // first, but its subscribe is deferred a tick when no binding exists yet, so the strategy set
- // synchronously below is in place by the time the deferred subscribe runs.
+ // The diagnostics_channel subscription lives in server-utils so it is shared
+ // across runtimes. The AsyncLocalStorage async-context strategy the channel
+ // binding depends on is installed once in `init()`, so this wrapper only
+ // renames the shared integration.
return extendIntegration(redisChannelIntegration({ responseHook: options.responseHook }), {
name: INTEGRATION_NAME,
- setupOnce() {
- setAsyncLocalStorageAsyncContextStrategy();
- },
});
}) satisfies IntegrationFn;
diff --git a/packages/deno/src/sdk.ts b/packages/deno/src/sdk.ts
index d9a75982b667..342f45797393 100644
--- a/packages/deno/src/sdk.ts
+++ b/packages/deno/src/sdk.ts
@@ -11,6 +11,14 @@ import {
requestDataIntegration,
stackParserFromStackParserOptions,
} from '@sentry/core';
+import {
+ amqplibChannelIntegration,
+ koaChannelIntegration,
+ mongodbChannelIntegration,
+ mongooseChannelIntegration,
+ mysqlChannelIntegration,
+ postgresChannelIntegration,
+} from '@sentry/server-utils/orchestrion';
import { DenoClient } from './client';
import { breadcrumbsIntegration } from './integrations/breadcrumbs';
import { denoContextIntegration } from './integrations/context';
@@ -23,12 +31,6 @@ import {
} from './denoVersion';
import { denoServeIntegration } from './integrations/deno-serve';
import { denoHttpIntegration } from './integrations/http';
-import { denoAmqplibIntegration } from './integrations/amqplib';
-import { denoKoaIntegration } from './integrations/koa';
-import { denoMongoIntegration } from './integrations/mongo';
-import { denoMongooseIntegration } from './integrations/mongoose';
-import { denoMysqlIntegration } from './integrations/mysql';
-import { denoPostgresIntegration } from './integrations/postgres';
import { denoRedisIntegration } from './integrations/redis';
import { globalHandlersIntegration } from './integrations/globalhandlers';
import { normalizePathsIntegration } from './integrations/normalizepaths';
@@ -60,18 +62,21 @@ export function getDefaultIntegrations(_options: Options): Integration[] {
: []),
// node:diagnostics_channel.tracingChannel exists on Deno 1.44.3+.
...(TRACING_CHANNEL_SUPPORTED ? [denoRedisIntegration()] : []),
- // orchestrion-based instrumentations.
- // It's possible that the orchestrion channels will be injected AFTER
- // (or in parallel to) loading the SDK, so we only gate on whether the
- // feature is possible. If they're never loaded, it'll just be a no-op.
+ // orchestrion-based instrumentations. We add a deliberate list here rather
+ // than every channel integration: each one needs a Deno test proving it
+ // records spans.
+ //
+ // The orchestrion channels may be injected after (or while) the SDK loads,
+ // so we gate only on whether the feature is possible. If they never load,
+ // this is a no-op.
...(MODULE_REGISTER_HOOKS_SUPPORTED
? [
- denoAmqplibIntegration(),
- denoKoaIntegration(),
- denoMongoIntegration(),
- denoMongooseIntegration(),
- denoMysqlIntegration(),
- denoPostgresIntegration(),
+ amqplibChannelIntegration(),
+ koaChannelIntegration(),
+ mongodbChannelIntegration(),
+ mongooseChannelIntegration(),
+ mysqlChannelIntegration(),
+ postgresChannelIntegration(),
]
: []),
contextLinesIntegration(),
diff --git a/packages/deno/test/__snapshots__/mod.test.ts.snap b/packages/deno/test/__snapshots__/mod.test.ts.snap
index 7da66392eb30..c7e4b9616035 100644
--- a/packages/deno/test/__snapshots__/mod.test.ts.snap
+++ b/packages/deno/test/__snapshots__/mod.test.ts.snap
@@ -115,12 +115,12 @@ snapshot[`captureException 1`] = `
"DenoServe",
"DenoHttp",
"DenoRedis",
- "DenoAmqplib",
- "DenoKoa",
- "DenoMongo",
- "DenoMongoose",
- "DenoMysql",
- "DenoPostgres",
+ "Amqplib",
+ "Koa",
+ "Mongo",
+ "Mongoose",
+ "Mysql",
+ "Postgres",
"ContextLines",
"NormalizePaths",
"GlobalHandlers",
@@ -196,12 +196,12 @@ snapshot[`captureMessage 1`] = `
"DenoServe",
"DenoHttp",
"DenoRedis",
- "DenoAmqplib",
- "DenoKoa",
- "DenoMongo",
- "DenoMongoose",
- "DenoMysql",
- "DenoPostgres",
+ "Amqplib",
+ "Koa",
+ "Mongo",
+ "Mongoose",
+ "Mysql",
+ "Postgres",
"ContextLines",
"NormalizePaths",
"GlobalHandlers",
@@ -284,12 +284,12 @@ snapshot[`captureMessage twice 1`] = `
"DenoServe",
"DenoHttp",
"DenoRedis",
- "DenoAmqplib",
- "DenoKoa",
- "DenoMongo",
- "DenoMongoose",
- "DenoMysql",
- "DenoPostgres",
+ "Amqplib",
+ "Koa",
+ "Mongo",
+ "Mongoose",
+ "Mysql",
+ "Postgres",
"ContextLines",
"NormalizePaths",
"GlobalHandlers",
@@ -379,12 +379,12 @@ snapshot[`captureMessage twice 2`] = `
"DenoServe",
"DenoHttp",
"DenoRedis",
- "DenoAmqplib",
- "DenoKoa",
- "DenoMongo",
- "DenoMongoose",
- "DenoMysql",
- "DenoPostgres",
+ "Amqplib",
+ "Koa",
+ "Mongo",
+ "Mongoose",
+ "Mysql",
+ "Postgres",
"ContextLines",
"NormalizePaths",
"GlobalHandlers",