Skip to content

Commit 9d229ad

Browse files
committed
feat(node): Add Prisma v7 support
Upgrades `@prisma/instrumentation` from `6.19.0` to `7.2.0`. The instrumentation should be backwards compatible with v5 and v6 (we have integration tests for v5 and v6). I also added integration tests for v7. Closes: #18876
1 parent 2f0d9dc commit 9d229ad

File tree

11 files changed

+259
-23
lines changed

11 files changed

+259
-23
lines changed

dev-packages/node-integration-tests/package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@
3535
"@nestjs/common": "^11",
3636
"@nestjs/core": "^11",
3737
"@nestjs/platform-express": "^11",
38+
"@prisma/adapter-pg": "7.2.0",
3839
"@prisma/client": "6.15.0",
3940
"@sentry/aws-serverless": "10.35.0",
4041
"@sentry/core": "10.35.0",
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
version: '3.9'
2+
3+
services:
4+
db:
5+
image: postgres:13
6+
restart: always
7+
container_name: integration-tests-prisma-v7
8+
ports:
9+
- '5435:5432'
10+
environment:
11+
POSTGRES_USER: prisma
12+
POSTGRES_PASSWORD: prisma
13+
POSTGRES_DB: tests
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
import * as Sentry from '@sentry/node';
2+
import { loggingTransport } from '@sentry-internal/node-integration-tests';
3+
4+
Sentry.init({
5+
dsn: 'https://public@dsn.ingest.sentry.io/1337',
6+
release: '1.0',
7+
tracesSampleRate: 1.0,
8+
transport: loggingTransport,
9+
});
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
import { defineConfig } from 'prisma/config';
2+
3+
export default defineConfig({
4+
earlyAccess: true,
5+
schema: './prisma/schema.prisma',
6+
migrate: {
7+
migrations: './prisma/migrations',
8+
},
9+
datasource: {
10+
url: 'postgresql://prisma:prisma@localhost:5435/tests',
11+
},
12+
});
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
# Please do not edit this file manually
2+
# It should be added in your version-control system (i.e. Git)
3+
provider = "postgresql"
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
-- CreateTable
2+
CREATE TABLE "User" (
3+
"id" SERIAL NOT NULL,
4+
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
5+
"email" TEXT NOT NULL,
6+
"name" TEXT,
7+
8+
CONSTRAINT "User_pkey" PRIMARY KEY ("id")
9+
);
10+
11+
-- CreateIndex
12+
CREATE UNIQUE INDEX "User_email_key" ON "User"("email");
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
datasource db {
2+
provider = "postgresql"
3+
}
4+
5+
generator client {
6+
provider = "prisma-client-js"
7+
}
8+
9+
model User {
10+
id Int @id @default(autoincrement())
11+
createdAt DateTime @default(now())
12+
email String @unique
13+
name String?
14+
}
Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
import { PrismaPg } from '@prisma/adapter-pg';
2+
import { PrismaClient } from '@prisma/client';
3+
import * as Sentry from '@sentry/node';
4+
import { randomBytes } from 'crypto';
5+
6+
// Stop the process from exiting before the transaction is sent
7+
setInterval(() => {}, 1000);
8+
9+
const connectionString = 'postgresql://prisma:prisma@localhost:5435/tests';
10+
11+
async function run() {
12+
await Sentry.startSpan(
13+
{
14+
name: 'Test Transaction',
15+
op: 'transaction',
16+
},
17+
async span => {
18+
const adapter = new PrismaPg({ connectionString });
19+
const client = new PrismaClient({ adapter });
20+
21+
await client.user.create({
22+
data: {
23+
name: 'Tilda',
24+
email: `tilda_${randomBytes(4).toString('hex')}@sentry.io`,
25+
},
26+
});
27+
28+
await client.user.findMany();
29+
30+
await client.user.deleteMany({
31+
where: {
32+
email: {
33+
contains: 'sentry.io',
34+
},
35+
},
36+
});
37+
38+
setTimeout(async () => {
39+
span.end();
40+
await client.$disconnect();
41+
}, 500);
42+
},
43+
);
44+
}
45+
46+
run();
Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
import { afterAll, describe, expect } from 'vitest';
2+
import { cleanupChildProcesses, createEsmAndCjsTests } from '../../../utils/runner';
3+
4+
afterAll(() => {
5+
cleanupChildProcesses();
6+
});
7+
8+
describe('Prisma ORM v7 Tests', () => {
9+
createEsmAndCjsTests(
10+
__dirname,
11+
'scenario.mjs',
12+
'instrument.mjs',
13+
(createRunner, test, _mode, cwd) => {
14+
test('should instrument PostgreSQL queries from Prisma ORM', { timeout: 75_000 }, async () => {
15+
await createRunner()
16+
.withDockerCompose({
17+
workingDirectory: [cwd],
18+
readyMatches: ['port 5432'],
19+
setupCommand: `yarn prisma generate --schema ${cwd}/prisma/schema.prisma && yarn prisma migrate dev -n sentry-test --schema ${cwd}/prisma/schema.prisma`,
20+
})
21+
.expect({
22+
transaction: transaction => {
23+
expect(transaction.transaction).toBe('Test Transaction');
24+
25+
const spans = transaction.spans || [];
26+
expect(spans.length).toBeGreaterThanOrEqual(5);
27+
28+
// Verify Prisma spans have the correct origin
29+
const prismaSpans = spans.filter(
30+
span => span.data && span.data['sentry.origin'] === 'auto.db.otel.prisma',
31+
);
32+
expect(prismaSpans.length).toBeGreaterThanOrEqual(5);
33+
34+
// Check for key Prisma span descriptions
35+
const spanDescriptions = prismaSpans.map(span => span.description);
36+
expect(spanDescriptions).toContain('prisma:client:operation');
37+
expect(spanDescriptions).toContain('prisma:client:serialize');
38+
expect(spanDescriptions).toContain('prisma:client:connect');
39+
expect(spanDescriptions).toContain('prisma:client:db_query');
40+
41+
// Verify the create operation has correct metadata
42+
const createSpan = prismaSpans.find(
43+
span =>
44+
span.description === 'prisma:client:operation' &&
45+
span.data?.['method'] === 'create' &&
46+
span.data?.['model'] === 'User',
47+
);
48+
expect(createSpan).toBeDefined();
49+
50+
// Verify db_query span has system info and correct op (v7 uses db.system.name)
51+
const dbQuerySpan = prismaSpans.find(span => span.description === 'prisma:client:db_query');
52+
expect(dbQuerySpan?.data?.['db.system.name']).toBe('postgresql');
53+
expect(dbQuerySpan?.data?.['sentry.op']).toBe('db');
54+
expect(dbQuerySpan?.op).toBe('db');
55+
},
56+
})
57+
.start()
58+
.completed();
59+
});
60+
},
61+
{
62+
additionalDependencies: {
63+
'@prisma/adapter-pg': '7.2.0',
64+
'@prisma/client': '7.2.0',
65+
pg: '^8.11.0',
66+
prisma: '7.2.0',
67+
},
68+
copyPaths: ['prisma', 'prisma.config.ts', 'docker-compose.yml'],
69+
},
70+
);
71+
});

packages/node/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -94,7 +94,7 @@
9494
"@opentelemetry/resources": "^2.4.0",
9595
"@opentelemetry/sdk-trace-base": "^2.4.0",
9696
"@opentelemetry/semantic-conventions": "^1.37.0",
97-
"@prisma/instrumentation": "6.19.0",
97+
"@prisma/instrumentation": "7.2.0",
9898
"@sentry/core": "10.35.0",
9999
"@sentry/node-core": "10.35.0",
100100
"@sentry/opentelemetry": "10.35.0",

0 commit comments

Comments
 (0)