Skip to content
Draft
Original file line number Diff line number Diff line change
Expand Up @@ -33,11 +33,16 @@ export default defineNuxtConfig({
nitro: {
// Nuxt's server is built by Nitro (Rollup), not Vite — so the orchestrion
// code transform has to run as a Nitro Rollup plugin to reach `server/api/*`
// routes. Force-bundle ONLY the instrumented deps (`mysql`) via
// `externals.inline`; externalized deps are `require()`d from `node_modules`
// at runtime and never pass through the transform.
// routes. Force-bundle the instrumented deps via `externals.inline`;
// externalized deps are `require()`d from `node_modules` at runtime and never
// pass through the transform.
//
// `standard-as-callback` is ioredis' CJS `export default` helper used by
// `connect()`. Left external, Rollup's interop resolves its `.default` to a
// non-function in the bundle; inlining it alongside ioredis links the
// interop consistently.
externals: {
inline: INSTRUMENTED_MODULE_NAMES,
inline: [...INSTRUMENTED_MODULE_NAMES, 'standard-as-callback'],
},
rollupConfig: {
plugins: [
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,12 +16,13 @@
"test:build-canary": "pnpm add nuxt@npm:nuxt-nightly@latest && pnpm add nitropack@npm:nitropack-nightly@latest && pnpm install --force && pnpm build",
"test:assert": "pnpm test:prod"
},
"//": "Need to use ioredis 5.10.1 because that's the last version before they support tracing channels",
"//": "ioredis is pinned to 5.10.1 and redis (node-redis) to 5.5.6 because those are the last versions before each supports its own diagnostics_channel, so orchestrion owns them",
"dependencies": {
"@sentry/nuxt": "file:../../packed/sentry-nuxt-packed.tgz",
"@sentry/server-utils": "file:../../packed/sentry-server-utils-packed.tgz",
"ioredis": "5.10.1",
"mysql": "^2.18.1",
"redis": "5.5.6",
"nuxt": "^4.4.8",
"vue": "^3.5.38",
"vue-router": "^5.1.0"
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import { defineEventHandler } from '#imports';
import { createClient } from 'redis';

export default defineEventHandler(async () => {
const client = createClient({
// Don't keep retrying forever if Redis goes away (e.g. on test teardown)
socket: { reconnectStrategy: false },
});

await client.connect();

try {
await client.set('test-key', 'test-value');
return await client.get('test-key');
} finally {
await client.destroy();
}
});
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ import { expect, test } from '@playwright/test';
import { waitForTransaction } from '@sentry-internal/test-utils';

test('Instruments ioredis automatically', async ({ baseURL }) => {
// This test works as well without orchestrion
const transactionEventPromise = waitForTransaction('nuxt-4-orchestrion', transactionEvent => {
return (
transactionEvent.contexts?.trace?.op === 'http.server' && transactionEvent.transaction === 'GET /api/db-ioredis'
Expand All @@ -21,7 +20,7 @@ test('Instruments ioredis automatically', async ({ baseURL }) => {
expect(spans).toContainEqual(
expect.objectContaining({
op: 'db',
origin: 'auto.db.otel.redis',
origin: 'auto.db.orchestrion.redis',
description: 'set test-key [1 other arguments]',
status: 'ok',
data: expect.objectContaining({
Expand All @@ -33,7 +32,7 @@ test('Instruments ioredis automatically', async ({ baseURL }) => {
expect(spans).toContainEqual(
expect.objectContaining({
op: 'db',
origin: 'auto.db.otel.redis',
origin: 'auto.db.orchestrion.redis',
description: 'get test-key',
status: 'ok',
data: expect.objectContaining({
Expand All @@ -44,6 +43,46 @@ test('Instruments ioredis automatically', async ({ baseURL }) => {
);
});

test('Instruments node-redis automatically', async ({ baseURL }) => {
const transactionEventPromise = waitForTransaction('nuxt-4-orchestrion', transactionEvent => {
return (
transactionEvent.contexts?.trace?.op === 'http.server' && transactionEvent.transaction === 'GET /api/db-redis'
);
});

await fetch(`${baseURL}/api/db-redis`);

const transactionEvent = await transactionEventPromise;

expect(transactionEvent.contexts?.trace?.op).toEqual('http.server');
expect(transactionEvent.transaction).toEqual('GET /api/db-redis');

const spans = transactionEvent.spans || [];

expect(spans).toContainEqual(
expect.objectContaining({
origin: 'auto.db.orchestrion.redis',
description: 'redis-SET',
status: 'ok',
data: expect.objectContaining({
'db.system': 'redis',
'db.statement': 'SET test-key [1 other arguments]',
}),
}),
);
expect(spans).toContainEqual(
expect.objectContaining({
origin: 'auto.db.orchestrion.redis',
description: 'redis-GET',
status: 'ok',
data: expect.objectContaining({
'db.system': 'redis',
'db.statement': 'GET test-key',
}),
}),
);
});

test('Instruments mysql automatically', async ({ baseURL }) => {
const transactionEventPromise = waitForTransaction('nuxt-4-orchestrion', transactionEvent => {
return (
Expand Down
Loading
Loading