Skip to content

Commit add5c8f

Browse files
isaacsJPeer264
authored andcommitted
feat: pg orchestrion instrumentation
Add orchestrion instrumentation for Node, Deno, and Bun, covering the `pg` module. This basically copies exactly what the `mysql` integration does, but for postgres. fix: #20764 fix: JS-2415
1 parent c9b9900 commit add5c8f

1 file changed

Lines changed: 190 additions & 0 deletions

File tree

  • dev-packages/node-integration-tests/suites/tracing/postgres

‎dev-packages/node-integration-tests/suites/tracing/postgres/test.ts‎

Lines changed: 190 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -611,4 +611,194 @@ describe('postgres auto instrumentation', () => {
611611
);
612612
});
613613
});
614+
615+
// Orchestrion (diagnostics-channel) variant: the same scenarios opted into
616+
// `experimentalUseDiagnosticsChannelInjection()`. Produces the same spans as
617+
// the OTel path, except the query origin reports the mechanism
618+
// (`auto.db.orchestrion.postgres`); connect/pool-connect spans stay 'manual'
619+
// (mirroring OTel — those spans never set an origin).
620+
describe('orchestrion (diagnostics-channel)', () => {
621+
const ORIGIN = 'auto.db.orchestrion.postgres';
622+
623+
describe('default', () => {
624+
const EXPECTED_TRANSACTION = {
625+
transaction: 'Test Transaction',
626+
spans: expect.arrayContaining([
627+
expect.objectContaining({
628+
data: expect.objectContaining({
629+
'db.system': 'postgresql',
630+
'db.name': 'tests',
631+
'sentry.origin': 'manual',
632+
'sentry.op': 'db',
633+
}),
634+
description: 'pg.connect',
635+
op: 'db',
636+
status: 'ok',
637+
}),
638+
expect.objectContaining({
639+
data: expect.objectContaining({
640+
'db.system': 'postgresql',
641+
'db.name': 'tests',
642+
'db.statement': 'INSERT INTO "User" ("email", "name") VALUES ($1, $2)',
643+
'sentry.origin': ORIGIN,
644+
'sentry.op': 'db',
645+
}),
646+
description: 'INSERT INTO "User" ("email", "name") VALUES ($1, $2)',
647+
op: 'db',
648+
status: 'ok',
649+
origin: ORIGIN,
650+
}),
651+
expect.objectContaining({
652+
data: expect.objectContaining({
653+
'db.system': 'postgresql',
654+
'db.name': 'tests',
655+
'db.statement': 'SELECT * FROM "User" WHERE "email" = $1',
656+
'db.postgresql.plan': 'select-user-by-email',
657+
'sentry.origin': ORIGIN,
658+
'sentry.op': 'db',
659+
}),
660+
description: 'SELECT * FROM "User" WHERE "email" = $1',
661+
op: 'db',
662+
status: 'ok',
663+
origin: ORIGIN,
664+
}),
665+
expect.objectContaining({
666+
data: expect.objectContaining({
667+
'db.system': 'postgresql',
668+
'db.statement': 'SELECT * FROM "does_not_exist_table"',
669+
'sentry.origin': ORIGIN,
670+
'sentry.op': 'db',
671+
}),
672+
description: 'SELECT * FROM "does_not_exist_table"',
673+
op: 'db',
674+
status: 'internal_error',
675+
origin: ORIGIN,
676+
}),
677+
]),
678+
};
679+
680+
createEsmAndCjsTests(__dirname, 'scenario.mjs', 'instrument-orchestrion.mjs', (createTestRunner, test) => {
681+
test('auto-instruments `pg` via diagnostics channels', { timeout: 90_000 }, async () => {
682+
await createTestRunner()
683+
.withDockerCompose({ workingDirectory: [__dirname] })
684+
.expect({ transaction: EXPECTED_TRANSACTION })
685+
.start()
686+
.completed();
687+
});
688+
});
689+
});
690+
691+
describe('pool', () => {
692+
const EXPECTED_TRANSACTION = {
693+
transaction: 'Test Transaction',
694+
spans: expect.arrayContaining([
695+
expect.objectContaining({
696+
data: expect.objectContaining({
697+
'db.system': 'postgresql',
698+
'db.name': 'tests',
699+
'db.connection_string': 'postgresql://localhost:5494/tests',
700+
'sentry.op': 'db',
701+
}),
702+
description: 'pg-pool.connect',
703+
op: 'db',
704+
status: 'ok',
705+
origin: 'manual',
706+
}),
707+
expect.objectContaining({
708+
data: expect.objectContaining({
709+
'db.system': 'postgresql',
710+
'db.name': 'tests',
711+
'db.statement': 'SELECT 1 AS foo',
712+
'sentry.origin': ORIGIN,
713+
'sentry.op': 'db',
714+
}),
715+
description: 'SELECT 1 AS foo',
716+
op: 'db',
717+
status: 'ok',
718+
origin: ORIGIN,
719+
}),
720+
]),
721+
};
722+
723+
createEsmAndCjsTests(__dirname, 'scenario-pool.mjs', 'instrument-orchestrion.mjs', (createTestRunner, test) => {
724+
test('auto-instruments `pg.Pool` and handles callback-style queries', { timeout: 90_000 }, async () => {
725+
await createTestRunner()
726+
.withDockerCompose({ workingDirectory: [__dirname] })
727+
.expect({ transaction: EXPECTED_TRANSACTION })
728+
.start()
729+
.completed();
730+
});
731+
});
732+
});
733+
734+
describe('connect error', () => {
735+
const EXPECTED_TRANSACTION = {
736+
transaction: 'Test Transaction',
737+
spans: expect.arrayContaining([
738+
expect.objectContaining({
739+
data: expect.objectContaining({ 'db.system': 'postgresql', 'db.name': 'tests', 'sentry.op': 'db' }),
740+
description: 'pg.connect',
741+
op: 'db',
742+
status: 'internal_error',
743+
origin: 'manual',
744+
}),
745+
]),
746+
};
747+
748+
createEsmAndCjsTests(
749+
__dirname,
750+
'scenario-connect-error.mjs',
751+
'instrument-orchestrion.mjs',
752+
(createTestRunner, test) => {
753+
test('records an errored connect span when the connection fails', { timeout: 90_000 }, async () => {
754+
await createTestRunner().expect({ transaction: EXPECTED_TRANSACTION }).start().completed();
755+
});
756+
},
757+
);
758+
});
759+
760+
describe('requireParentSpan', () => {
761+
createEsmAndCjsTests(
762+
__dirname,
763+
'scenario-no-parent.mjs',
764+
'instrument-orchestrion.mjs',
765+
(createTestRunner, test) => {
766+
test(
767+
'does not instrument queries or connects without an active parent span',
768+
{ timeout: 90_000 },
769+
async () => {
770+
await createTestRunner()
771+
.withDockerCompose({ workingDirectory: [__dirname] })
772+
.expect({
773+
transaction: txn => {
774+
const descriptions = txn.spans?.map(span => span.description) ?? [];
775+
expect(descriptions).not.toContain('SELECT 1 AS unparented');
776+
expect(descriptions.find(name => name?.includes('connect'))).toBeUndefined();
777+
expect(txn).toMatchObject({
778+
transaction: 'Test Transaction',
779+
spans: expect.arrayContaining([
780+
expect.objectContaining({
781+
data: expect.objectContaining({
782+
'db.system': 'postgresql',
783+
'db.statement': 'SELECT 2 AS parented',
784+
'sentry.origin': ORIGIN,
785+
'sentry.op': 'db',
786+
}),
787+
description: 'SELECT 2 AS parented',
788+
op: 'db',
789+
status: 'ok',
790+
origin: ORIGIN,
791+
}),
792+
]),
793+
});
794+
},
795+
})
796+
.start()
797+
.completed();
798+
},
799+
);
800+
},
801+
);
802+
});
803+
});
614804
});

0 commit comments

Comments
 (0)