Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .size-limit.js
Original file line number Diff line number Diff line change
Expand Up @@ -287,7 +287,7 @@ module.exports = [
import: createImport('init'),
ignore: [...builtinModules, ...nodePrefixedBuiltinModules],
gzip: true,
limit: '164 KB',
limit: '166 KB',
},
{
name: '@sentry/node - without tracing',
Expand Down
1 change: 1 addition & 0 deletions dev-packages/node-integration-tests/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@
"@nestjs/common": "^11",
"@nestjs/core": "^11",
"@nestjs/platform-express": "^11",
"@prisma/adapter-pg": "7.2.0",
"@prisma/client": "6.15.0",
"@sentry/aws-serverless": "10.35.0",
"@sentry/core": "10.35.0",
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
version: '3.9'

services:
db:
image: postgres:13
restart: always
container_name: integration-tests-prisma-v7
ports:
- '5435:5432'
environment:
POSTGRES_USER: prisma
POSTGRES_PASSWORD: prisma
POSTGRES_DB: tests
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import * as Sentry from '@sentry/node';
import { loggingTransport } from '@sentry-internal/node-integration-tests';

Sentry.init({
dsn: 'https://public@dsn.ingest.sentry.io/1337',
release: '1.0',
tracesSampleRate: 1.0,
transport: loggingTransport,
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import { defineConfig } from 'prisma/config';

export default defineConfig({
schema: './prisma/schema.prisma',
migrations: './prisma/migrations',
datasource: {
url: 'postgresql://prisma:prisma@localhost:5435/tests',
},
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
# Please do not edit this file manually
# It should be added in your version-control system (i.e. Git)
provider = "postgresql"
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
-- CreateTable
CREATE TABLE "User" (
"id" SERIAL NOT NULL,
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
"email" TEXT NOT NULL,
"name" TEXT,

CONSTRAINT "User_pkey" PRIMARY KEY ("id")
);

-- CreateIndex
CREATE UNIQUE INDEX "User_email_key" ON "User"("email");
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
datasource db {
provider = "postgresql"
}

generator client {
provider = "prisma-client"
output = "./generated/prisma"
}

model User {
id Int @id @default(autoincrement())
createdAt DateTime @default(now())
email String @unique
name String?
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
{
"compilerOptions": {
"module": "ESNext",
"moduleResolution": "bundler",
"target": "ES2023",
"declaration": false,
"rewriteRelativeImportExtensions": true,
"skipLibCheck": true
},
"include": ["./generated/prisma/**/*.ts"]
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
import { PrismaPg } from '@prisma/adapter-pg';
import * as Sentry from '@sentry/node';
import { randomBytes } from 'crypto';
import { PrismaClient } from './prisma/generated/prisma/client.js';

// Stop the process from exiting before the transaction is sent
setInterval(() => {}, 1000);

const connectionString = 'postgresql://prisma:prisma@localhost:5435/tests';

async function run() {
await Sentry.startSpan(
{
name: 'Test Transaction',
op: 'transaction',
},
async span => {
const adapter = new PrismaPg({ connectionString });
const client = new PrismaClient({ adapter });

await client.user.create({
data: {
name: 'Tilda',
email: `tilda_${randomBytes(4).toString('hex')}@sentry.io`,
},
});

await client.user.findMany();

await client.user.deleteMany({
where: {
email: {
contains: 'sentry.io',
},
},
});

setTimeout(async () => {
span.end();
await client.$disconnect();
}, 500);
},
);
Copy link

Choose a reason for hiding this comment

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

Wrong API used: span auto-ends before setTimeout fires

Low Severity

The v7 scenario uses Sentry.startSpan() with an async callback that includes a setTimeout calling span.end(). However, startSpan automatically ends the span when the async callback returns (after the database operations complete), which happens before the setTimeout fires. This means span.end() is called on an already-ended span. The v5 test correctly uses Sentry.startSpanManual() for this same pattern, which does not auto-end the span. While the test likely still passes because all database operations complete before the callback returns, this is inconsistent with the existing v5 test pattern and could cause confusion.

Fix in Cursor Fix in Web

Copy link
Member Author

Choose a reason for hiding this comment

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

This is consistent with our v6 tests.

}

run();
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
import { afterAll, expect } from 'vitest';
import { conditionalTest } from '../../../utils';
import { cleanupChildProcesses, createEsmAndCjsTests } from '../../../utils/runner';

afterAll(() => {
cleanupChildProcesses();
});

// Prisma 7 requires Node.js 20.19+
conditionalTest({ min: 20 })('Prisma ORM v7 Tests', () => {
createEsmAndCjsTests(
__dirname,
'scenario.mjs',
'instrument.mjs',
(createRunner, test, _mode, cwd) => {
test('should instrument PostgreSQL queries from Prisma ORM', { timeout: 75_000 }, async () => {
await createRunner()
.withDockerCompose({
workingDirectory: [cwd],
readyMatches: ['port 5432'],
setupCommand: `prisma generate --schema ${cwd}/prisma/schema.prisma && tsc -p ${cwd}/prisma/tsconfig.json && prisma migrate dev -n sentry-test --schema ${cwd}/prisma/schema.prisma`,
Copy link
Member Author

Choose a reason for hiding this comment

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

This previously used the workspace prisma, which was v6. I changed the runner to modify PATH so that the closest node_modules bin path is preferred so that runners that specify additional dependencies have those take precedence.

})
.expect({
transaction: transaction => {
expect(transaction.transaction).toBe('Test Transaction');

const spans = transaction.spans || [];
expect(spans.length).toBeGreaterThanOrEqual(5);

// Verify Prisma spans have the correct origin
const prismaSpans = spans.filter(
span => span.data && span.data['sentry.origin'] === 'auto.db.otel.prisma',
);
expect(prismaSpans.length).toBeGreaterThanOrEqual(5);

// Check for key Prisma span descriptions
const spanDescriptions = prismaSpans.map(span => span.description);
expect(spanDescriptions).toContain('prisma:client:operation');
expect(spanDescriptions).toContain('prisma:client:serialize');
expect(spanDescriptions).toContain('prisma:client:connect');
expect(spanDescriptions).toContain('prisma:client:db_query');

// Verify the create operation has correct metadata
const createSpan = prismaSpans.find(
span =>
span.description === 'prisma:client:operation' &&
span.data?.['method'] === 'create' &&
span.data?.['model'] === 'User',
);
expect(createSpan).toBeDefined();

// Verify db_query span has system info and correct op (v7 uses db.system.name)
const dbQuerySpan = prismaSpans.find(span => span.description === 'prisma:client:db_query');
expect(dbQuerySpan?.data?.['db.system.name']).toBe('postgresql');
expect(dbQuerySpan?.data?.['sentry.op']).toBe('db');
expect(dbQuerySpan?.op).toBe('db');
},
})
.start()
.completed();
});
},
{
additionalDependencies: {
'@prisma/adapter-pg': '7.2.0',
'@prisma/client': '7.2.0',
pg: '^8.11.0',
prisma: '7.2.0',
typescript: '^5.9.0',
},
copyPaths: ['prisma', 'prisma.config.ts', 'docker-compose.yml'],
},
);
});
4 changes: 3 additions & 1 deletion dev-packages/node-integration-tests/utils/runner.ts
Original file line number Diff line number Diff line change
Expand Up @@ -110,7 +110,9 @@ async function runDockerCompose(options: DockerOptions): Promise<VoidFunction> {
clearTimeout(timeout);
if (options.setupCommand) {
try {
execSync(options.setupCommand, { cwd, stdio: 'inherit' });
// Prepend local node_modules/.bin to PATH so additionalDependencies binaries take precedence
const env = { ...process.env, PATH: `${cwd}/node_modules/.bin:${process.env.PATH}` };
execSync(options.setupCommand, { cwd, stdio: 'inherit', env });
} catch (e) {
log('Error running docker setup command', e);
}
Expand Down
2 changes: 1 addition & 1 deletion packages/node/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -94,7 +94,7 @@
"@opentelemetry/resources": "^2.4.0",
"@opentelemetry/sdk-trace-base": "^2.4.0",
"@opentelemetry/semantic-conventions": "^1.37.0",
"@prisma/instrumentation": "6.19.0",
"@prisma/instrumentation": "7.2.0",
"@sentry/core": "10.35.0",
"@sentry/node-core": "10.35.0",
"@sentry/opentelemetry": "10.35.0",
Expand Down
Loading
Loading