Skip to content

Conversation

@onurtemizkan
Copy link
Collaborator

@onurtemizkan onurtemizkan commented Oct 17, 2025

Resolves: #17889

This PR rewrites the postgresjs instrumentation with a new architecture:

  • Added ESM support via replaceExports

  • Moved to main export wrapping instead of internal module patching

    • Previously, we were patching connection.js and query.js internal modules
    • New approach: We are wrapping the main postgres module export to intercept sql instance creation
  • Connection context is now stored directly on sql instances using CONNECTION_CONTEXT_SYMBOL

  • Query.prototype fallback (CJS only)

    • Patches Query.prototype.handle as a fallback for pre-existing sql instances
    • Uses QUERY_FROM_INSTRUMENTED_SQL marker to prevent duplicate spans

Also,

  • Improved SQL sanitization
  • port attribute is now stored as a number per OTEL semantic conventions
  • Added fallback regex extraction for operation name when command isn't available

@github-actions
Copy link
Contributor

github-actions bot commented Oct 17, 2025

size-limit report 📦

Path Size % Change Change
@sentry/browser 24.81 kB - -
@sentry/browser - with treeshaking flags 23.3 kB - -
@sentry/browser (incl. Tracing) 41.55 kB - -
@sentry/browser (incl. Tracing, Profiling) 46.14 kB - -
@sentry/browser (incl. Tracing, Replay) 79.97 kB - -
@sentry/browser (incl. Tracing, Replay) - with treeshaking flags 69.7 kB - -
@sentry/browser (incl. Tracing, Replay with Canvas) 84.64 kB - -
@sentry/browser (incl. Tracing, Replay, Feedback) 96.89 kB - -
@sentry/browser (incl. Feedback) 41.52 kB - -
@sentry/browser (incl. sendFeedback) 29.49 kB - -
@sentry/browser (incl. FeedbackAsync) 34.48 kB - -
@sentry/react 26.52 kB - -
@sentry/react (incl. Tracing) 43.75 kB - -
@sentry/vue 29.27 kB - -
@sentry/vue (incl. Tracing) 43.36 kB - -
@sentry/svelte 24.82 kB - -
CDN Bundle 27.24 kB - -
CDN Bundle (incl. Tracing) 42.23 kB - -
CDN Bundle (incl. Tracing, Replay) 78.75 kB - -
CDN Bundle (incl. Tracing, Replay, Feedback) 84.21 kB - -
CDN Bundle - uncompressed 80.04 kB - -
CDN Bundle (incl. Tracing) - uncompressed 125.39 kB - -
CDN Bundle (incl. Tracing, Replay) - uncompressed 241.42 kB - -
CDN Bundle (incl. Tracing, Replay, Feedback) - uncompressed 254.18 kB - -
@sentry/nextjs (client) 45.97 kB - -
@sentry/sveltekit (client) 41.92 kB - -
@sentry/node-core 51.5 kB +0.01% +1 B 🔺
@sentry/node 160.98 kB +0.66% +1.04 kB 🔺
@sentry/node - without tracing 92.91 kB - -
@sentry/aws-serverless 108.44 kB - -

View base workflow run

@github-actions
Copy link
Contributor

github-actions bot commented Oct 17, 2025

node-overhead report 🧳

Note: This is a synthetic benchmark with a minimal express app and does not necessarily reflect the real-world performance impact in an application.

Scenario Requests/s % of Baseline Prev. Requests/s Change %
GET Baseline 8,779 - 8,915 -2%
GET With Sentry 1,777 20% 1,709 +4%
GET With Sentry (error only) 6,103 70% 6,117 -0%
POST Baseline 1,207 - 1,215 -1%
POST With Sentry 607 50% 593 +2%
POST With Sentry (error only) 1,062 88% 1,060 +0%
MYSQL Baseline 3,384 - 3,317 +2%
MYSQL With Sentry 494 15% 426 +16%
MYSQL With Sentry (error only) 2,725 81% 2,654 +3%

View base workflow run

@onurtemizkan onurtemizkan force-pushed the onur/postgres-js-esm-support branch 2 times, most recently from f635159 to 96641f9 Compare October 17, 2025 10:06
@onurtemizkan onurtemizkan marked this pull request as ready for review November 21, 2025 14:28
@onurtemizkan onurtemizkan requested a review from Copilot November 21, 2025 14:31
Copy link

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR adds ESM (ECMAScript Module) support for postgres.js instrumentation by refactoring the patching approach from file-based module instrumentation to module export wrapping. The instrumentation now wraps the main postgres export function to intercept SQL instance creation and instrument query methods dynamically.

Key changes:

  • Refactored instrumentation to wrap the main postgres export instead of patching internal files
  • Added comprehensive ESM test coverage alongside existing CJS tests
  • Enhanced SQL query sanitization to handle PostgreSQL placeholders ($1, $2, etc.)

Reviewed changes

Copilot reviewed 14 out of 14 changed files in this pull request and generated 3 comments.

Show a summary per file
File Description
packages/node/src/integrations/tracing/postgresjs.ts Complete rewrite of instrumentation using module export wrapping; adds ESM support and improves query/connection handling
dev-packages/node-integration-tests/suites/tracing/postgresjs/test.ts Added ESM tests, requestHook tests, URL initialization tests, and unsafe query tests
dev-packages/node-integration-tests/suites/tracing/postgresjs/scenario.mjs New ESM scenario file for testing ESM imports
dev-packages/node-integration-tests/suites/tracing/postgresjs/scenario.cjs New CJS scenario file with consolidated initialization
dev-packages/node-integration-tests/suites/tracing/postgresjs/scenario.js Enhanced with additional query test cases
dev-packages/node-integration-tests/suites/tracing/postgresjs/scenario-url.mjs New ESM test scenario for URL-based postgres initialization
dev-packages/node-integration-tests/suites/tracing/postgresjs/scenario-url.cjs New CJS test scenario for URL-based postgres initialization
dev-packages/node-integration-tests/suites/tracing/postgresjs/scenario-unsafe.cjs New test scenario for sql.unsafe() method
dev-packages/node-integration-tests/suites/tracing/postgresjs/scenario-requestHook.mjs New ESM test scenario for requestHook functionality
dev-packages/node-integration-tests/suites/tracing/postgresjs/scenario-requestHook.js New CJS test scenario for requestHook functionality
dev-packages/node-integration-tests/suites/tracing/postgresjs/instrument.mjs New ESM instrumentation file for tests
dev-packages/node-integration-tests/suites/tracing/postgresjs/instrument.cjs New CJS instrumentation file for tests
dev-packages/node-integration-tests/suites/tracing/postgresjs/instrument-requestHook.mjs New ESM instrumentation with requestHook configuration
dev-packages/node-integration-tests/suites/tracing/postgresjs/instrument-requestHook.cjs New CJS instrumentation with requestHook configuration
Comments suppressed due to low confidence (1)

dev-packages/node-integration-tests/suites/tracing/postgresjs/scenario.js:83

  • The floating promise should be handled with proper error handling. Consider using run().catch(console.error) or wrapping in an async IIFE to ensure errors are not silently swallowed.
// eslint-disable-next-line @typescript-eslint/no-floating-promises
run();

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines 363 to 367
let spanName = sanitizedSqlQuery?.trim() || '';
if (!spanName) {
// Fallback: try to extract operation from the sanitized query
const operationMatch = sanitizedSqlQuery?.match(SQL_OPERATION_REGEX);
spanName = operationMatch?.[1] ? `db.${operationMatch[1].toLowerCase()}` : 'db.query';
Copy link

Copilot AI Nov 21, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Logic error: spanName is set to the trimmed sanitizedSqlQuery on line 363, which means the condition !spanName on line 364 will only be true if sanitizedSqlQuery is empty or undefined. However, line 366 attempts to match the same sanitizedSqlQuery against SQL_OPERATION_REGEX. This code path is unreachable when sanitizedSqlQuery has content. The fallback logic appears to be intended for cases where sanitizedSqlQuery exists but is not suitable as a span name, but the current implementation doesn't achieve this.

Suggested change
let spanName = sanitizedSqlQuery?.trim() || '';
if (!spanName) {
// Fallback: try to extract operation from the sanitized query
const operationMatch = sanitizedSqlQuery?.match(SQL_OPERATION_REGEX);
spanName = operationMatch?.[1] ? `db.${operationMatch[1].toLowerCase()}` : 'db.query';
let spanName: string;
const operationMatch = sanitizedSqlQuery?.match(SQL_OPERATION_REGEX);
if (operationMatch?.[1]) {
spanName = `db.${operationMatch[1].toLowerCase()}`;
} else if (sanitizedSqlQuery?.trim()) {
spanName = sanitizedSqlQuery.trim();
} else {
spanName = 'db.query';

Copilot uses AI. Check for mistakes.
InstrumentationBase,
InstrumentationNodeModuleDefinition,
InstrumentationNodeModuleFile,
safeExecuteInTheMiddle,
Copy link

Copilot AI Nov 21, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The import of safeExecuteInTheMiddle should be removed from '@opentelemetry/instrumentation' and imported from '@sentry/core' instead, as indicated by the usage pattern at line 394. However, checking the actual usage shows it is used correctly. If this import is not used elsewhere in the file, it should be removed.

Copilot uses AI. Check for mistakes.
@onurtemizkan onurtemizkan force-pushed the onur/postgres-js-esm-support branch from d9a0dfa to 35bf27d Compare November 26, 2025 14:56
@onurtemizkan onurtemizkan force-pushed the onur/postgres-js-esm-support branch from 35bf27d to 5143c10 Compare November 27, 2025 10:54
spanName = sanitizedSqlQuery.trim();
} else {
spanName = 'db.query';
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Bug: Span description uses operation prefix instead of full query

The span name logic sets spanName to db.${operation} (e.g., db.create, db.select) for recognized SQL operations, but this value becomes the span's description when serialized (as confirmed in sentrySpan.ts line 226: description: this._name). The tests in this same PR explicitly expect the span description to be the full sanitized SQL query (e.g., 'CREATE TABLE "User" (...)'), not the abbreviated db.create format. This mismatch means the detailed test expectations will fail.

Fix in Cursor Fix in Web

@onurtemizkan onurtemizkan force-pushed the onur/postgres-js-esm-support branch from 5143c10 to 898802b Compare November 27, 2025 17:16
Comment on lines 482 to 510
sqlQuery
.replace(/\s+/g, ' ')
.trim() // Remove extra spaces including newlines and trim
.substring(0, 1024) // Truncate to 1024 characters
.replace(/--.*?(\r?\n|$)/g, '') // Single line comments
// Remove comments first (they may contain newlines and extra spaces)
.replace(/--.*$/gm, '') // Single line comments (multiline mode)
.replace(/\/\*[\s\S]*?\*\//g, '') // Multi-line comments

This comment was marked as outdated.

@onurtemizkan onurtemizkan force-pushed the onur/postgres-js-esm-support branch from b0cf889 to 29ff73f Compare November 28, 2025 14:58
@onurtemizkan onurtemizkan requested a review from Lms24 November 28, 2025 15:29
@onurtemizkan onurtemizkan force-pushed the onur/postgres-js-esm-support branch from 299da8a to 11a366d Compare December 2, 2025 11:42
@aldy505
Copy link

aldy505 commented Dec 11, 2025

...any updates?

Copy link
Member

@Lms24 Lms24 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for making the changes. This looks good to me for now, though I'd like us to improve parameterization in a follow-up PR (see slack convo).

Copy link
Member

@Lms24 Lms24 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

thanks for making the changes!

@onurtemizkan
Copy link
Collaborator Author

@Lms24 - Implemented complete sanitization. 4ae3e66

}),
};

await createRunner(__dirname, 'scenario-requestHook.js')
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Bug: CJS requestHook test references non-existent scenario file

The CJS requestHook test at line 536 references scenario-requestHook.js, but this file is not created in the PR. The diff shows scenario-requestHook.mjs being added for ESM testing, but no corresponding CJS file (scenario-requestHook.js or scenario-requestHook.cjs) exists. This inconsistency with the other scenario files (which have both CJS and ESM versions) means the test will fail at runtime due to a missing file.

Fix in Cursor Fix in Web

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Sentry.postgresJsIntegration() not creating any DB spans

4 participants