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
Original file line number Diff line number Diff line change
Expand Up @@ -117,6 +117,11 @@ function expectedDbSpan({
type: 'string',
value: statement,
};
// The name of a db query span is its `db.query.summary` attribute
attributes['db.query.summary'] = {
type: 'string',
value: name,
};
attributes['sentry.origin'] = {
type: 'string',
value: origin,
Expand Down Expand Up @@ -170,12 +175,12 @@ describeWithDockerCompose('postgres auto instrumentation (streamed)', { workingD

expect(dbSpans).toEqual([
expectedDbSpan({ name: 'pg.connect' }),
expectedDbSpan({ name: CREATE_USER_TABLE_STATEMENT, statement: CREATE_USER_TABLE_STATEMENT }),
expectedDbSpan({ name: 'CREATE TABLE "User"', statement: CREATE_USER_TABLE_STATEMENT }),
expectedDbSpan({
name: 'INSERT INTO "User" ("email", "name") VALUES ($1, $2)',
name: 'INSERT "User"',
statement: 'INSERT INTO "User" ("email", "name") VALUES ($1, $2)',
}),
expectedDbSpan({ name: 'SELECT * FROM "User"', statement: 'SELECT * FROM "User"' }),
expectedDbSpan({ name: 'SELECT "User"', statement: 'SELECT * FROM "User"' }),
expectedDbSpan({ name: 'DROP TABLE "User"', statement: 'DROP TABLE "User"' }),
]);
},
Expand All @@ -201,13 +206,13 @@ describeWithDockerCompose('postgres auto instrumentation (streamed)', { workingD
// `ignoreConnectSpans`.
const origin = 'auto.db.postgres';
expect(dbSpans).toEqual([
expectedDbSpan({ name: CREATE_USER_TABLE_STATEMENT, statement: CREATE_USER_TABLE_STATEMENT, origin }),
expectedDbSpan({ name: 'CREATE TABLE "User"', statement: CREATE_USER_TABLE_STATEMENT, origin }),
expectedDbSpan({
name: 'INSERT INTO "User" ("email", "name") VALUES ($1, $2)',
name: 'INSERT "User"',
statement: 'INSERT INTO "User" ("email", "name") VALUES ($1, $2)',
origin,
}),
expectedDbSpan({ name: 'SELECT * FROM "User"', statement: 'SELECT * FROM "User"', origin }),
expectedDbSpan({ name: 'SELECT "User"', statement: 'SELECT * FROM "User"', origin }),
expectedDbSpan({ name: 'DROP TABLE "User"', statement: 'DROP TABLE "User"', origin }),
]);
},
Expand Down Expand Up @@ -240,17 +245,17 @@ describeWithDockerCompose('postgres auto instrumentation (streamed)', { workingD
expect(dbSpans).toEqual([
expectedDbSpan({ name: 'pg.connect', host: '127.0.0.1' }),
expectedDbSpan({
name: CREATE_NATIVE_USER_TABLE_STATEMENT,
name: 'CREATE TABLE "NativeUser"',
statement: CREATE_NATIVE_USER_TABLE_STATEMENT,
host: '127.0.0.1',
}),
expectedDbSpan({
name: 'INSERT INTO "NativeUser" ("email", "name") VALUES ($1, $2)',
name: 'INSERT "NativeUser"',
statement: 'INSERT INTO "NativeUser" ("email", "name") VALUES ($1, $2)',
host: '127.0.0.1',
}),
expectedDbSpan({
name: 'SELECT * FROM "NativeUser"',
name: 'SELECT "NativeUser"',
statement: 'SELECT * FROM "NativeUser"',
host: '127.0.0.1',
}),
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
version: '3.9'

services:
db:
image: postgres:13
restart: always
ports:
- '5446:5432'
environment:
POSTGRES_USER: test
POSTGRES_PASSWORD: test
POSTGRES_DB: test_db
healthcheck:
test: ['CMD-SHELL', 'pg_isready -U test -d test_db']
interval: 2s
timeout: 3s
retries: 30
start_period: 5s
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import * as Sentry from '@sentry/node';
import { loggingTransport } from '@sentry-internal/node-integration-tests';

const requestHook = (span, sanitizedSqlQuery, connectionContext) => {
// Add custom attributes to demonstrate requestHook functionality.
// Streamed spans carry no `extra`, so the connection context is asserted via span attributes
// rather than `Sentry.setExtra` (as the static-lifecycle suite does).
span.setAttributes({
'custom.requestHook': 'called',
'custom.requestHook.query': sanitizedSqlQuery,
'custom.requestHook.database': connectionContext?.ATTR_DB_NAMESPACE,
'custom.requestHook.host': connectionContext?.ATTR_SERVER_ADDRESS,
'custom.requestHook.port': connectionContext?.ATTR_SERVER_PORT,
});
};

// `postgresJsIntegration()` is the diagnostics-channel implementation by default; it forwards the
// `requestHook` to the channel subscriber.
Sentry.init({
dsn: 'https://public@dsn.ingest.sentry.io/1337',
release: '1.0',
tracesSampleRate: 1.0,
transport: loggingTransport,
integrations: [Sentry.postgresJsIntegration({ requestHook })],
traceLifecycle: 'stream',
});
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,39 @@
import * as Sentry from '@sentry/node';
import { uuid4 } from '@sentry/core/server';
import postgres from 'postgres';
import { waitForConnection } from '@sentry-internal/node-integration-tests';

const sql = postgres({ port: 5446, user: 'test', password: 'test', database: 'test_db' });

async function run() {
await Sentry.startSpan(
{
name: 'Test Transaction',
op: 'transaction',
},
async () => {
try {
await waitForConnection(() => sql`SELECT 1`);
await sql`
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"));
`;

const email = `${uuid4()}@domain.com`;
await sql`
INSERT INTO "User" ("email", "name") VALUES (${email}, 'tim');
`;

await sql`
SELECT * FROM "User" WHERE "email" = ${email};
`;
} finally {
await sql`
DROP TABLE "User";
`;
await sql.end();
}
},
);
}

run();
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
import * as Sentry from '@sentry/node';
import { uuid4 } from '@sentry/core/server';
import postgres from 'postgres';
import { waitForConnection } from '@sentry-internal/node-integration-tests';

// Test with plain object options
const sql = postgres({ port: 5446, user: 'test', password: 'test', database: 'test_db' });

async function run() {
await Sentry.startSpan(
{
name: 'Test Transaction',
op: 'transaction',
},
async () => {
try {
await waitForConnection(() => sql`SELECT 1`);
// Test sql.unsafe() - this was not being instrumented before the fix
await sql.unsafe('CREATE TABLE "User" ("id" SERIAL NOT NULL, "email" TEXT NOT NULL, PRIMARY KEY ("id"))');

const email = `${uuid4()}@domain.com`;
await sql.unsafe('INSERT INTO "User" ("email") VALUES ($1)', [email]);

await sql.unsafe('SELECT * FROM "User" WHERE "email" = $1', [email]);

await sql.unsafe('DROP TABLE "User"');

// This will be captured as an error as the table no longer exists
await sql.unsafe('SELECT * FROM "User"');
} finally {
await sql.end();
}
},
);
}

run();
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
import * as Sentry from '@sentry/node';
import { uuid4 } from '@sentry/core/server';
import postgres from 'postgres';
import { waitForConnection } from '@sentry-internal/node-integration-tests';

// Test URL-based initialization - this is the common pattern that was causing the regression
const sql = postgres('postgres://test:test@localhost:5446/test_db');

async function run() {
await Sentry.startSpan(
{
name: 'Test Transaction',
op: 'transaction',
},
async () => {
try {
await waitForConnection(() => sql`SELECT 1`);
await sql`
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"));
`;

const email = `${uuid4()}@domain.com`;
await sql`
INSERT INTO "User" ("email", "name") VALUES (${email}, 'tim');
`;

await sql`
UPDATE "User" SET "name" = 'Foo' WHERE "email" = ${email};
`;

await sql`
SELECT * FROM "User" WHERE "email" = ${email};
`;

// Test parameterized queries
await sql`
SELECT * FROM "User" WHERE "email" = ${email} AND "name" = ${'Foo'};
`;

// Test DELETE operation
await sql`
DELETE FROM "User" WHERE "email" = ${email};
`;

// Test INSERT with RETURNING
await sql`
INSERT INTO "User" ("email", "name") VALUES (${email}, 'Test User') RETURNING *;
`;

// Test cursor-based queries
await sql`SELECT * from generate_series(1,1000) as x `.cursor(10, async rows => {
await Promise.all(rows);
});

// Test multiple rows at once
await sql`
SELECT * FROM "User" LIMIT 10;
`;

await sql`
DROP TABLE "User";
`;

// This will be captured as an error as the table no longer exists
await sql`
SELECT * FROM "User" WHERE "email" = ${email};
`;
} finally {
await sql.end();
}
},
);
}

run();
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
import * as Sentry from '@sentry/node';
import { uuid4 } from '@sentry/core/server';
import postgres from 'postgres';
import { waitForConnection } from '@sentry-internal/node-integration-tests';

const sql = postgres({ port: 5446, user: 'test', password: 'test', database: 'test_db' });

async function run() {
await Sentry.startSpan(
{
name: 'Test Transaction',
op: 'transaction',
},
async () => {
try {
await waitForConnection(() => sql`SELECT 1`);
await sql`
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"));
`;

const email = `${uuid4()}@domain.com`;
await sql`
INSERT INTO "User" ("email", "name") VALUES (${email}, 'tim');
`;

await sql`
UPDATE "User" SET "name" = 'Foo' WHERE "email" = ${email};
`;

await sql`
SELECT * FROM "User" WHERE "email" = ${email};
`;

// Test parameterized queries
await sql`
SELECT * FROM "User" WHERE "email" = ${email} AND "name" = ${'Foo'};
`;

// Test DELETE operation
await sql`
DELETE FROM "User" WHERE "email" = ${email};
`;

// Test INSERT with RETURNING
await sql`
INSERT INTO "User" ("email", "name") VALUES (${email}, 'Test User') RETURNING *;
`;

// Test cursor-based queries
await sql`SELECT * from generate_series(1,1000) as x `.cursor(10, async rows => {
await Promise.all(rows);
});

// Test multiple rows at once
await sql`
SELECT * FROM "User" LIMIT 10;
`;

await sql`
DROP TABLE "User";
`;

// This will be captured as an error as the table no longer exists
await sql`
SELECT * FROM "User" WHERE "email" = ${email};
`;
} finally {
await sql.end();
}
},
);
}

run();
Loading
Loading