Skip to content

Commit e430249

Browse files
committed
feat(node): Add postgresjs instrumentation
1 parent 4bbe610 commit e430249

File tree

7 files changed

+538
-1
lines changed

7 files changed

+538
-1
lines changed

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

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,7 @@
6161
"node-cron": "^3.0.3",
6262
"node-schedule": "^2.1.1",
6363
"pg": "8.16.0",
64+
"postgres": "^3.4.7",
6465
"proxy": "^2.1.1",
6566
"redis-4": "npm:redis@^4.6.14",
6667
"reflect-metadata": "0.2.1",
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-postgresjs
8+
ports:
9+
- '5444:5432'
10+
environment:
11+
POSTGRES_USER: test
12+
POSTGRES_PASSWORD: test
13+
POSTGRES_DB: test_db
Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
const { loggingTransport } = require('@sentry-internal/node-integration-tests');
2+
const Sentry = require('@sentry/node');
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+
});
10+
11+
// Stop the process from exiting before the transaction is sent
12+
setInterval(() => {}, 1000);
13+
14+
const postgres = require('postgres');
15+
16+
const sql = postgres({ port: 5444, user: 'test', password: 'test', database: 'test_db' });
17+
18+
async function run() {
19+
await Sentry.startSpan(
20+
{
21+
name: 'Test Transaction',
22+
op: 'transaction',
23+
},
24+
async () => {
25+
try {
26+
await sql`
27+
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"));
28+
`;
29+
30+
await sql`
31+
INSERT INTO "User" ("email", "name") VALUES ('Foo', 'bar@baz.com');
32+
`;
33+
34+
await sql`
35+
UPDATE "User" SET "name" = 'Foo' WHERE "email" = 'bar@baz.com';
36+
`;
37+
38+
await sql`
39+
SELECT * FROM "User" WHERE "email" = 'bar@baz.com';
40+
`;
41+
42+
await sql`SELECT * from generate_series(1,1000) as x `.cursor(10, async rows => {
43+
await Promise.all(rows);
44+
});
45+
46+
await sql`
47+
DROP TABLE "User";
48+
`;
49+
50+
await sql`
51+
SELECT * FROM "User" WHERE "email" = 'bar@baz.com';
52+
`;
53+
} finally {
54+
await sql.end();
55+
}
56+
},
57+
);
58+
}
59+
60+
// eslint-disable-next-line @typescript-eslint/no-floating-promises
61+
run();
Lines changed: 198 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,198 @@
1+
import { describe, expect, test } from 'vitest';
2+
import { createRunner } from '../../../utils/runner';
3+
4+
describe('postgresjs auto instrumentation', () => {
5+
test('should auto-instrument `postgres` package', { timeout: 60_000 }, async () => {
6+
const EXPECTED_TRANSACTION = {
7+
transaction: 'Test Transaction',
8+
spans: expect.arrayContaining([
9+
expect.objectContaining({
10+
data: expect.objectContaining({
11+
'db.namespace': 'test_db',
12+
'db.operation.name': 'CREATE TABLE',
13+
'db.query.text':
14+
'CREATE TABLE "User" ("id" SERIAL NOT NULL,"createdAt" TIMESTAMP(?) NOT NULL DEFAULT CURRENT_TIMESTAMP,"email" TEXT NOT NULL,"name" TEXT,CONSTRAINT "User_pkey" PRIMARY KEY ("id"))',
15+
'db.system.name': 'postgres',
16+
'sentry.op': 'db',
17+
'sentry.origin': 'manual',
18+
'server.address': 'localhost',
19+
'server.port': 5444,
20+
}),
21+
description: 'CREATE TABLE db:test_db',
22+
op: 'db',
23+
status: 'ok',
24+
origin: 'manual',
25+
parent_span_id: expect.any(String),
26+
span_id: expect.any(String),
27+
start_timestamp: expect.any(Number),
28+
timestamp: expect.any(Number),
29+
trace_id: expect.any(String),
30+
}),
31+
expect.objectContaining({
32+
data: expect.objectContaining({
33+
'db.namespace': 'test_db',
34+
'db.operation.name': 'SELECT',
35+
'db.query.text':
36+
"select b.oid, b.typarray from pg_catalog.pg_type a left join pg_catalog.pg_type b on b.oid = a.typelem where a.typcategory = 'A' group by b.oid, b.typarray order by b.oid",
37+
'db.system.name': 'postgres',
38+
'sentry.op': 'db',
39+
'sentry.origin': 'manual',
40+
'server.address': 'localhost',
41+
'server.port': 5444,
42+
}),
43+
description: 'SELECT db:test_db',
44+
op: 'db',
45+
status: 'ok',
46+
origin: 'manual',
47+
parent_span_id: expect.any(String),
48+
span_id: expect.any(String),
49+
start_timestamp: expect.any(Number),
50+
timestamp: expect.any(Number),
51+
trace_id: expect.any(String),
52+
}),
53+
expect.objectContaining({
54+
data: expect.objectContaining({
55+
'db.namespace': 'test_db',
56+
'db.operation.name': 'INSERT',
57+
'db.query.text': 'INSERT INTO "User" ("email", "name") VALUES (\'Foo\', \'bar@baz.com\')',
58+
'db.system.name': 'postgres',
59+
'sentry.origin': 'manual',
60+
'sentry.op': 'db',
61+
'server.address': 'localhost',
62+
'server.port': 5444,
63+
}),
64+
description: 'INSERT db:test_db',
65+
op: 'db',
66+
status: 'ok',
67+
origin: 'manual',
68+
parent_span_id: expect.any(String),
69+
span_id: expect.any(String),
70+
start_timestamp: expect.any(Number),
71+
timestamp: expect.any(Number),
72+
trace_id: expect.any(String),
73+
}),
74+
expect.objectContaining({
75+
data: expect.objectContaining({
76+
'db.namespace': 'test_db',
77+
'db.operation.name': 'UPDATE',
78+
'db.query.text': 'UPDATE "User" SET "name" = \'Foo\' WHERE "email" = \'bar@baz.com\'',
79+
'db.system.name': 'postgres',
80+
'sentry.op': 'db',
81+
'sentry.origin': 'manual',
82+
'server.address': 'localhost',
83+
'server.port': 5444,
84+
}),
85+
description: 'UPDATE db:test_db',
86+
op: 'db',
87+
status: 'ok',
88+
origin: 'manual',
89+
parent_span_id: expect.any(String),
90+
span_id: expect.any(String),
91+
start_timestamp: expect.any(Number),
92+
timestamp: expect.any(Number),
93+
trace_id: expect.any(String),
94+
}),
95+
expect.objectContaining({
96+
data: expect.objectContaining({
97+
'db.namespace': 'test_db',
98+
'db.operation.name': 'SELECT',
99+
'db.query.text': 'SELECT * FROM "User" WHERE "email" = \'bar@baz.com\'',
100+
'db.system.name': 'postgres',
101+
'sentry.op': 'db',
102+
'sentry.origin': 'manual',
103+
'server.address': 'localhost',
104+
'server.port': 5444,
105+
}),
106+
description: 'SELECT db:test_db',
107+
op: 'db',
108+
status: 'ok',
109+
origin: 'manual',
110+
parent_span_id: expect.any(String),
111+
span_id: expect.any(String),
112+
start_timestamp: expect.any(Number),
113+
timestamp: expect.any(Number),
114+
trace_id: expect.any(String),
115+
}),
116+
expect.objectContaining({
117+
data: expect.objectContaining({
118+
'db.namespace': 'test_db',
119+
'db.operation.name': 'SELECT',
120+
'db.query.text': 'SELECT * from generate_series(?,?) as x',
121+
'db.system.name': 'postgres',
122+
'sentry.op': 'db',
123+
'sentry.origin': 'manual',
124+
'server.address': 'localhost',
125+
'server.port': 5444,
126+
}),
127+
description: 'SELECT db:test_db',
128+
op: 'db',
129+
status: 'ok',
130+
origin: 'manual',
131+
parent_span_id: expect.any(String),
132+
span_id: expect.any(String),
133+
start_timestamp: expect.any(Number),
134+
timestamp: expect.any(Number),
135+
trace_id: expect.any(String),
136+
}),
137+
expect.objectContaining({
138+
data: expect.objectContaining({
139+
'db.namespace': 'test_db',
140+
'db.operation.name': 'DROP TABLE',
141+
'db.query.text': 'DROP TABLE "User"',
142+
'db.system.name': 'postgres',
143+
'sentry.op': 'db',
144+
'sentry.origin': 'manual',
145+
'server.address': 'localhost',
146+
'server.port': 5444,
147+
}),
148+
description: 'DROP TABLE db:test_db',
149+
op: 'db',
150+
status: 'ok',
151+
origin: 'manual',
152+
parent_span_id: expect.any(String),
153+
span_id: expect.any(String),
154+
start_timestamp: expect.any(Number),
155+
timestamp: expect.any(Number),
156+
trace_id: expect.any(String),
157+
}),
158+
expect.objectContaining({
159+
data: expect.objectContaining({
160+
'db.namespace': 'test_db',
161+
'db.query.text': 'SELECT * FROM "User" WHERE "email" = \'bar@baz.com\'',
162+
'db.system.name': 'postgres',
163+
'sentry.op': 'db',
164+
'sentry.origin': 'manual',
165+
'server.address': 'localhost',
166+
'server.port': 5444,
167+
}),
168+
description: 'SELECT db:test_db',
169+
op: 'db',
170+
status: 'ok',
171+
origin: 'manual',
172+
parent_span_id: expect.any(String),
173+
span_id: expect.any(String),
174+
start_timestamp: expect.any(Number),
175+
timestamp: expect.any(Number),
176+
trace_id: expect.any(String),
177+
}),
178+
]),
179+
};
180+
181+
const EXPECTED_ERROR_EVENT = {
182+
event_id: expect.any(String),
183+
contexts: {
184+
trace: {
185+
trace_id: expect.any(String),
186+
span_id: expect.any(String),
187+
status: 'unknown_error',
188+
},
189+
},
190+
};
191+
192+
await createRunner(__dirname, 'scenario.js')
193+
.withDockerCompose({ workingDirectory: [__dirname], readyMatches: ['port 5432'] })
194+
.expect({ transaction: EXPECTED_TRANSACTION, event: EXPECTED_ERROR_EVENT })
195+
.start()
196+
.completed();
197+
});
198+
});

packages/node/src/integrations/tracing/index.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ import { instrumentMongoose, mongooseIntegration } from './mongoose';
1515
import { instrumentMysql, mysqlIntegration } from './mysql';
1616
import { instrumentMysql2, mysql2Integration } from './mysql2';
1717
import { instrumentPostgres, postgresIntegration } from './postgres';
18+
import { instrumentPostgresJs, postgresJsIntegration } from './postgresjs';
1819
import { prismaIntegration } from './prisma';
1920
import { instrumentRedis, redisIntegration } from './redis';
2021
import { instrumentTedious, tediousIntegration } from './tedious';
@@ -44,6 +45,7 @@ export function getAutoPerformanceIntegrations(): Integration[] {
4445
amqplibIntegration(),
4546
lruMemoizerIntegration(),
4647
vercelAIIntegration(),
48+
postgresJsIntegration(),
4749
];
4850
}
4951

@@ -75,5 +77,6 @@ export function getOpenTelemetryInstrumentationToPreload(): (((options?: any) =>
7577
instrumentGenericPool,
7678
instrumentAmqplib,
7779
instrumentVercelAi,
80+
instrumentPostgresJs,
7881
];
7982
}

0 commit comments

Comments
 (0)