Skip to content
Draft
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
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
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,
integrations: [Sentry.knexIntegration()],
traceLifecycle: 'stream',
});
Original file line number Diff line number Diff line change
Expand Up @@ -66,5 +66,41 @@ describeWithDockerCompose('knex auto instrumentation', { workingDirectory: [__di
await createRunner().expect({ transaction: EXPECTED_TRANSACTION }).start().completed();
});
});

createEsmAndCjsTests(__dirname, 'scenario.mjs', 'instrument-span-streaming.mjs', (createRunner, test) => {
test('should name spans after the query summary with span streaming', { timeout: 60_000 }, async () => {
await createRunner()
.expect({
span: container => {
// The `mysql2` driver spans underneath the knex spans come from a different integration and
// are named after the full statement, so they are filtered out here.
const knexSpans = container.items.filter(item => item.attributes['sentry.origin']?.value === ORIGIN);

expect(
knexSpans.map(span => ({
name: span.name,
summary: span.attributes['db.query.summary']?.value,
text: span.attributes['db.query.text']?.value,
})),
).toEqual([
{
name: 'create table `User`',
summary: 'create table `User`',
text: 'create table `User` (`id` int unsigned not null auto_increment primary key, `createdAt` timestamp(3) not null default CURRENT_TIMESTAMP(3), `email` text not null, `name` text not null)',
},
{
name: 'insert `User`',
summary: 'insert `User`',
text: 'insert into `User` (`email`, `name`) values (?, ?)',
},
{ name: 'select `User`', summary: 'select `User`', text: 'select * from `User`' },
{ name: 'drop table `User`', summary: 'drop table `User`', text: 'drop table `User`' },
]);
},
})
.start()
.completed();
});
});
});
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
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,
integrations: [Sentry.knexIntegration()],
traceLifecycle: 'stream',
});
Original file line number Diff line number Diff line change
Expand Up @@ -80,5 +80,46 @@ describe('knex auto instrumentation', () => {
await createRunner().expect({ transaction: EXPECTED_TRANSACTION }).start().completed();
});
});

createEsmAndCjsTests(__dirname, 'scenario.mjs', 'instrument-span-streaming.mjs', (createRunner, test) => {
test('should name spans after the query summary with span streaming', { timeout: 60_000 }, async () => {
await createRunner()
.expect({
span: container => {
// The `pg` driver spans underneath the knex spans come from a different integration and
// are named after the full statement, so they are filtered out here.
const knexSpans = container.items.filter(item => item.attributes['sentry.origin']?.value === ORIGIN);

expect(
knexSpans.map(span => ({
name: span.name,
summary: span.attributes['db.query.summary']?.value,
text: span.attributes['db.query.text']?.value,
})),
).toEqual([
{
name: 'create table "User"',
summary: 'create table "User"',
text: 'create table "User" ("id" serial primary key, "createdAt" timestamptz(3) not null default CURRENT_TIMESTAMP(3), "email" text not null, "name" text not null)',
},
{
name: 'insert "User"',
summary: 'insert "User"',
text: 'insert into "User" ("email", "name") values (?, ?)',
},
{ name: 'select "User"', summary: 'select "User"', text: 'select * from "User"' },
{
name: 'select "DoesNotExist"',
summary: 'select "DoesNotExist"',
text: 'select * from "DoesNotExist"',
},
{ name: 'drop table "User"', summary: 'drop table "User"', text: 'drop table "User"' },
]);
},
})
.start()
.completed();
});
});
});
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
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,
traceLifecycle: 'stream',
});
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,7 @@ function expectPrismaV5Spans(transaction: TransactionEvent): void {
expect.objectContaining({
data: {
'db.statement': expect.stringContaining('INSERT INTO'),
'db.query.summary': 'INSERT "public"."User"',
'db.system': 'postgresql',
'sentry.kind': 'client',
'sentry.op': 'db',
Expand All @@ -84,6 +85,7 @@ function expectPrismaV5Spans(transaction: TransactionEvent): void {
expect.objectContaining({
data: {
'db.statement': expect.stringContaining('SELECT'),
'db.query.summary': 'SELECT "public"',
'db.system': 'postgresql',
'sentry.kind': 'client',
'sentry.op': 'db',
Expand All @@ -96,6 +98,7 @@ function expectPrismaV5Spans(transaction: TransactionEvent): void {
expect.objectContaining({
data: {
'db.statement': expect.stringContaining('DELETE'),
'db.query.summary': 'DELETE "public"."User"',
'db.system': 'postgresql',
'sentry.kind': 'client',
'sentry.op': 'db',
Expand Down Expand Up @@ -128,5 +131,51 @@ describeWithDockerCompose('Prisma ORM v5', { workingDirectory: [__dirname] }, ()
copyPaths: ['prisma'],
},
);

createEsmAndCjsTests(
__dirname,
'scenario.mjs',
'instrument-span-streaming.mjs',
(createRunner, test) => {
test(
'should name db query spans after the query summary with span streaming',
{ timeout: 75_000 },
async () => {
await createRunner()
.expect({
span: container => {
// v5 reports the SQL on the deprecated `db.statement` rather than `db.query.text`.
const querySpans = container.items.filter(item => item.attributes['db.statement']);

expect(
querySpans.map(span => ({
name: span.name,
summary: span.attributes['db.query.summary']?.value,
})),
).toEqual([
{ name: 'INSERT "public"."User"', summary: 'INSERT "public"."User"' },
{ name: 'SELECT "public"', summary: 'SELECT "public"' },
{ name: 'BEGIN', summary: 'BEGIN' },
{ name: 'INSERT "public"."User"', summary: 'INSERT "public"."User"' },
{ name: 'SELECT "public"', summary: 'SELECT "public"' },
{ name: 'COMMIT', summary: 'COMMIT' },
{ name: 'DELETE "public"."User"', summary: 'DELETE "public"."User"' },
]);

// The raw engine span name must never leak through.
expect(container.items.map(span => span.name)).not.toContain('prisma:engine:db_query');
},
})
.start()
.completed();
},
);
},
{
additionalDependencies: ADDITIONAL_DEPENDENCIES,
afterSetupCommand: AFTER_SETUP_COMMAND,
copyPaths: ['prisma'],
},
);
});
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
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,
traceLifecycle: 'stream',
});
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,7 @@ describeWithDockerCompose('Prisma ORM v6 Tests', { workingDirectory: [__dirname]
'sentry.op': 'db',
'db.query.text':
'SELECT "public"."User"."id", "public"."User"."createdAt", "public"."User"."email", "public"."User"."name" FROM "public"."User" WHERE 1=1 OFFSET $1',
'db.query.summary': 'SELECT "public"',
'db.system': 'postgresql',
'sentry.kind': 'client',
},
Expand All @@ -99,6 +100,7 @@ describeWithDockerCompose('Prisma ORM v6 Tests', { workingDirectory: [__dirname]
data: {
'sentry.op': 'db',
'db.query.text': 'DELETE FROM "public"."User" WHERE "public"."User"."email"::text LIKE $1',
'db.query.summary': 'DELETE "public"."User"',
'db.system': 'postgresql',
'sentry.kind': 'client',
},
Expand All @@ -119,4 +121,45 @@ describeWithDockerCompose('Prisma ORM v6 Tests', { workingDirectory: [__dirname]
copyPaths: ['prisma'],
},
);

createEsmAndCjsTests(
__dirname,
'scenario.mjs',
'instrument-span-streaming.mjs',
(createRunner, test) => {
test('should name db query spans after the query summary with span streaming', { timeout: 75_000 }, async () => {
await createRunner()
.expect({
span: container => {
const querySpans = container.items.filter(item => item.attributes['db.query.text']);

// `SELECT "public"` is what the core query-summary helper derives from a schema-qualified,
// quoted table (it stops at the first quoted identifier).
expect(
querySpans.map(span => ({
name: span.name,
summary: span.attributes['db.query.summary']?.value,
})),
).toEqual([
{ name: 'INSERT "public"."User"', summary: 'INSERT "public"."User"' },
{ name: 'SELECT "public"', summary: 'SELECT "public"' },
{ name: 'DELETE "public"."User"', summary: 'DELETE "public"."User"' },
]);

// Neither the raw engine span name nor the full statement may end up as a span name.
expect(container.items.map(span => span.name)).not.toContain('prisma:engine:db_query');
querySpans.forEach(span => {
expect(span.name).not.toBe(span.attributes['db.query.text']?.value);
});
},
})
.start()
.completed();
});
},
{
afterSetupCommand: 'prisma generate --schema prisma/schema.prisma',
copyPaths: ['prisma'],
},
);
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
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,
traceLifecycle: 'stream',
});
Original file line number Diff line number Diff line change
Expand Up @@ -91,5 +91,63 @@ describe('Prisma ORM v7 Tests', () => {
copyPaths: ['prisma', 'prisma.config.ts'],
},
);

createEsmAndCjsTests(
__dirname,
'scenario.mjs',
'instrument-span-streaming.mjs',
(createRunner, test) => {
test(
'should name db query spans after the query summary with span streaming',
{ timeout: 75_000 },
async () => {
await createRunner()
.expect({
span: container => {
// v7 runs the queries through the `pg` adapter, whose own spans are named after the full
// statement by a different integration, so they are filtered out here.
const querySpans = container.items.filter(
item =>
item.attributes['sentry.origin']?.value === 'auto.db.otel.prisma' &&
item.attributes['db.query.text'],
);

// `SELECT "public"` is what the core query-summary helper derives from a schema-qualified,
// quoted table (it stops at the first quoted identifier).
expect(
querySpans.map(span => ({
name: span.name,
summary: span.attributes['db.query.summary']?.value,
})),
).toEqual([
{ name: 'INSERT "public"."User"', summary: 'INSERT "public"."User"' },
{ name: 'SELECT "public"', summary: 'SELECT "public"' },
{ name: 'DELETE "public"."User"', summary: 'DELETE "public"."User"' },
]);

// Neither the raw client span name nor the full statement may end up as a span name.
expect(container.items.map(span => span.name)).not.toContain('prisma:client:db_query');
querySpans.forEach(span => {
expect(span.name).not.toBe(span.attributes['db.query.text']?.value);
});
},
})
.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',
},
afterSetupCommand: 'prisma generate --schema prisma/schema.prisma && tsc -p prisma/tsconfig.json',
copyPaths: ['prisma', 'prisma.config.ts'],
},
);
});
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
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,
traceLifecycle: 'stream',
});
Loading
Loading