Skip to content

Commit 4adaf1c

Browse files
jtomaszewskiclaude
andcommitted
feat(instrumentation-pg): add skipConnectSpans option
Add a configuration option to skip creating `pg.connect` and `pg-pool.connect` spans while preserving query spans and pool metrics. This helps reduce trace noise in high-throughput scenarios where connection time is not of interest. - Add `skipConnectSpans?: boolean` to PgInstrumentationConfig - Skip span creation for both client and pool connect operations - Still set up event listeners for pool metrics when skipping spans - Add tests for new option - Update README with new option documentation 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <[email protected]>
1 parent 53a6c94 commit 4adaf1c

File tree

4 files changed

+83
-4
lines changed

4 files changed

+83
-4
lines changed

packages/instrumentation-pg/README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,7 @@ PostgreSQL instrumentation has few options available to choose from. You can set
5050
| `responseHook` | `PgInstrumentationExecutionResponseHook` (function) | Function for adding custom span attributes from db response |
5151
| `requireParentSpan` | `boolean` | If true, requires a parent span to create new spans (default false) |
5252
| `addSqlCommenterCommentToQueries` | `boolean` | If true, adds [sqlcommenter](https://github.com/open-telemetry/opentelemetry-sqlcommenter) specification compliant comment to queries with tracing context (default false). _NOTE: A comment will not be added to queries that already contain `--` or `/* ... */` in them, even if these are not actually part of comments_ |
53+
| `skipConnectSpans` | `boolean` | If true, `pg.connect` and `pg-pool.connect` spans will not be created. Query spans and pool metrics are still recorded (default false) |
5354

5455
## Semantic Conventions
5556

packages/instrumentation-pg/src/instrumentation.ts

Lines changed: 13 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -249,7 +249,9 @@ export class PgInstrumentation extends InstrumentationBase<PgInstrumentationConf
249249
const plugin = this;
250250
return (original: PgClientConnect) => {
251251
return function connect(this: pgTypes.Client, callback?: Function) {
252-
if (utils.shouldSkipInstrumentation(plugin.getConfig())) {
252+
const config = plugin.getConfig();
253+
254+
if (utils.shouldSkipInstrumentation(config) || config.skipConnectSpans) {
253255
return original.call(this, callback);
254256
}
255257

@@ -574,7 +576,16 @@ export class PgInstrumentation extends InstrumentationBase<PgInstrumentationConf
574576
const plugin = this;
575577
return (originalConnect: typeof pgPoolTypes.prototype.connect) => {
576578
return function connect(this: PgPoolExtended, callback?: PgPoolCallback) {
577-
if (utils.shouldSkipInstrumentation(plugin.getConfig())) {
579+
const config = plugin.getConfig();
580+
581+
if (utils.shouldSkipInstrumentation(config)) {
582+
return originalConnect.call(this, callback as any);
583+
}
584+
585+
// Still set up event listeners for metrics even when skipping spans
586+
plugin._setPoolConnectEventListeners(this);
587+
588+
if (config.skipConnectSpans) {
578589
return originalConnect.call(this, callback as any);
579590
}
580591

@@ -587,8 +598,6 @@ export class PgInstrumentation extends InstrumentationBase<PgInstrumentationConf
587598
),
588599
});
589600

590-
plugin._setPoolConnectEventListeners(this);
591-
592601
if (callback) {
593602
const parentSpan = trace.getSpan(context.active());
594603
callback = utils.patchCallbackPGPool(

packages/instrumentation-pg/src/types.ts

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -79,4 +79,12 @@ export interface PgInstrumentationConfig extends InstrumentationConfig {
7979
* the tracing context, following the {@link https://github.com/open-telemetry/opentelemetry-sqlcommenter sqlcommenter} format
8080
*/
8181
addSqlCommenterCommentToQueries?: boolean;
82+
83+
/**
84+
* If true, `pg.connect` and `pg-pool.connect` spans will not be created.
85+
* Query spans and pool metrics are still recorded.
86+
*
87+
* @default false
88+
*/
89+
skipConnectSpans?: boolean;
8290
}

packages/instrumentation-pg/test/pg-pool.test.ts

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -327,6 +327,67 @@ describe('pg-pool', () => {
327327
const spans = memoryExporter.getFinishedSpans();
328328
assert.strictEqual(spans.length, 0);
329329
});
330+
331+
it('should not create connect spans when skipConnectSpans=true', async () => {
332+
const newPool = new pgPool(CONFIG);
333+
create({
334+
skipConnectSpans: true,
335+
});
336+
const span = provider.getTracer('test-pg-pool').startSpan('test span');
337+
await context.with(trace.setSpan(context.active(), span), async () => {
338+
const client = await newPool.connect();
339+
try {
340+
await client.query('SELECT NOW()');
341+
} finally {
342+
client.release();
343+
}
344+
});
345+
await newPool.end();
346+
347+
const spans = memoryExporter.getFinishedSpans();
348+
const poolConnectSpans = spans.filter(s => s.name === 'pg-pool.connect');
349+
const clientConnectSpans = spans.filter(s => s.name === 'pg.connect');
350+
const querySpans = spans.filter(s => s.name.startsWith('pg.query'));
351+
352+
assert.strictEqual(
353+
poolConnectSpans.length,
354+
0,
355+
'Expected no pg-pool.connect spans'
356+
);
357+
assert.strictEqual(
358+
clientConnectSpans.length,
359+
0,
360+
'Expected no pg.connect spans'
361+
);
362+
assert.ok(querySpans.length > 0, 'Expected query spans to be created');
363+
});
364+
365+
it('should still record pool metrics when skipConnectSpans=true', async () => {
366+
const metricReader = testUtils.initMeterProvider(instrumentation);
367+
const newPool = new pgPool(CONFIG);
368+
create({
369+
skipConnectSpans: true,
370+
});
371+
372+
const client = await newPool.connect();
373+
try {
374+
await client.query('SELECT NOW()');
375+
} finally {
376+
client.release();
377+
}
378+
379+
const { resourceMetrics, errors } = await metricReader.collect();
380+
assert.deepEqual(errors, [], 'expected no errors during metric collection');
381+
382+
const metrics = resourceMetrics.scopeMetrics[0].metrics;
383+
assert.strictEqual(
384+
metrics[1].descriptor.name,
385+
METRIC_DB_CLIENT_CONNECTION_COUNT,
386+
'Expected connection count metric'
387+
);
388+
389+
await newPool.end();
390+
});
330391
});
331392

332393
describe('#pool.query()', () => {

0 commit comments

Comments
 (0)