Skip to content

feat(core,cloudflare,deno): Add instrumentPostgresJsSql instrumentation#19566

Merged
andreiborza merged 2 commits intodevelopfrom
ab/postgresjs-core-instrumentation
Feb 27, 2026
Merged

feat(core,cloudflare,deno): Add instrumentPostgresJsSql instrumentation#19566
andreiborza merged 2 commits intodevelopfrom
ab/postgresjs-core-instrumentation

Conversation

@andreiborza
Copy link
Member

@andreiborza andreiborza commented Feb 27, 2026

This PR extracts parts from @sentry/node's PostgresJsInstrumentation to @sentry/core and exposes it as a callable instrumentPostgresJsSql helper that can be used in SDKs that aren't based on OpenTelemetry.

It is currently exposed in @sentry/cloudflare and @sentry/deno as these two SDKs are not based on OpenTelemetry under the hood.

@sentry/node imports and reuses as much as possible from it.

Closes #19567 (added automatically)

@andreiborza andreiborza changed the title Ab/postgresjs core instrumentation feat(core,cloudflare,deno): Add instrumentPostgresJsSql instrumentation Feb 27, 2026
@andreiborza andreiborza requested a review from logaretm February 27, 2026 14:01
@github-actions
Copy link
Contributor

github-actions bot commented Feb 27, 2026

size-limit report 📦

Path Size % Change Change
@sentry/browser 25.62 kB - -
@sentry/browser - with treeshaking flags 24.12 kB - -
@sentry/browser (incl. Tracing) 42.42 kB - -
@sentry/browser (incl. Tracing, Profiling) 47.09 kB - -
@sentry/browser (incl. Tracing, Replay) 81.24 kB - -
@sentry/browser (incl. Tracing, Replay) - with treeshaking flags 70.86 kB - -
@sentry/browser (incl. Tracing, Replay with Canvas) 85.94 kB - -
@sentry/browser (incl. Tracing, Replay, Feedback) 98.2 kB - -
@sentry/browser (incl. Feedback) 42.43 kB - -
@sentry/browser (incl. sendFeedback) 30.29 kB - -
@sentry/browser (incl. FeedbackAsync) 35.34 kB - -
@sentry/browser (incl. Metrics) 26.79 kB - -
@sentry/browser (incl. Logs) 26.93 kB - -
@sentry/browser (incl. Metrics & Logs) 27.61 kB - -
@sentry/react 27.37 kB - -
@sentry/react (incl. Tracing) 44.76 kB - -
@sentry/vue 30.07 kB - -
@sentry/vue (incl. Tracing) 44.29 kB - -
@sentry/svelte 25.64 kB - -
CDN Bundle 28.16 kB - -
CDN Bundle (incl. Tracing) 43.25 kB - -
CDN Bundle (incl. Logs, Metrics) 29 kB - -
CDN Bundle (incl. Tracing, Logs, Metrics) 44.09 kB - -
CDN Bundle (incl. Replay, Logs, Metrics) 68.08 kB - -
CDN Bundle (incl. Tracing, Replay) 80.13 kB - -
CDN Bundle (incl. Tracing, Replay, Logs, Metrics) 80.99 kB - -
CDN Bundle (incl. Tracing, Replay, Feedback) 85.64 kB - -
CDN Bundle (incl. Tracing, Replay, Feedback, Logs, Metrics) 86.53 kB - -
CDN Bundle - uncompressed 82.34 kB - -
CDN Bundle (incl. Tracing) - uncompressed 128.06 kB - -
CDN Bundle (incl. Logs, Metrics) - uncompressed 85.18 kB - -
CDN Bundle (incl. Tracing, Logs, Metrics) - uncompressed 130.89 kB - -
CDN Bundle (incl. Replay, Logs, Metrics) - uncompressed 208.84 kB - -
CDN Bundle (incl. Tracing, Replay) - uncompressed 244.94 kB - -
CDN Bundle (incl. Tracing, Replay, Logs, Metrics) - uncompressed 247.76 kB - -
CDN Bundle (incl. Tracing, Replay, Feedback) - uncompressed 257.85 kB - -
CDN Bundle (incl. Tracing, Replay, Feedback, Logs, Metrics) - uncompressed 260.66 kB - -
@sentry/nextjs (client) 47.17 kB - -
@sentry/sveltekit (client) 42.88 kB - -
@sentry/node-core 52.18 kB +0.02% +9 B 🔺
@sentry/node 174.6 kB +0.07% +110 B 🔺
@sentry/node - without tracing 97.33 kB +0.02% +10 B 🔺
@sentry/aws-serverless 113.13 kB +0.01% +7 B 🔺

View base workflow run

Copy link

@cursor cursor bot left a comment

Choose a reason for hiding this comment

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

Cursor Bugbot has reviewed your changes and found 2 potential issues.

Autofix Details

Bugbot Autofix prepared fixes for both issues found in the latest run.

  • ✅ Fixed: Langchain runName dropped from chain naming logic
    • Added missing parameters including runName to handleChainStart method and restored the priority chain naming logic (runName || chain.name || 'unknown_chain').
  • ✅ Fixed: Duplicated SQL utility functions across core and node
    • Removed duplicate _reconstructQuery and _sanitizeSqlQuery methods from node integration and updated code to use exported functions from @sentry/core.

Create PR

Or push these changes by commenting:

@cursor push 8963d48a41
Preview (8963d48a41)
diff --git a/packages/core/src/index.ts b/packages/core/src/index.ts
--- a/packages/core/src/index.ts
+++ b/packages/core/src/index.ts
@@ -120,7 +120,7 @@
 export { extraErrorDataIntegration } from './integrations/extraerrordata';
 export { rewriteFramesIntegration } from './integrations/rewriteframes';
 export { supabaseIntegration, instrumentSupabaseClient } from './integrations/supabase';
-export { instrumentPostgresJsSql } from './integrations/postgresjs';
+export { instrumentPostgresJsSql, _reconstructQuery, _sanitizeSqlQuery } from './integrations/postgresjs';
 export { zodErrorsIntegration } from './integrations/zoderrors';
 export { thirdPartyErrorFilterIntegration } from './integrations/third-party-errors-filter';
 export { consoleIntegration } from './integrations/console';

diff --git a/packages/core/src/tracing/langchain/index.ts b/packages/core/src/tracing/langchain/index.ts
--- a/packages/core/src/tracing/langchain/index.ts
+++ b/packages/core/src/tracing/langchain/index.ts
@@ -184,8 +184,17 @@
     },
 
     // Chain Start Handler
-    handleChainStart(chain: { name?: string }, inputs: Record<string, unknown>, runId: string, _parentRunId?: string) {
-      const chainName = chain.name || 'unknown_chain';
+    handleChainStart(
+      chain: { name?: string },
+      inputs: Record<string, unknown>,
+      runId: string,
+      _parentRunId?: string,
+      _tags?: string[],
+      _metadata?: Record<string, unknown>,
+      _runType?: string,
+      runName?: string,
+    ) {
+      const chainName = runName || chain.name || 'unknown_chain';
       const attributes: Record<string, SpanAttributeValue> = {
         [SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: 'auto.ai.langchain',
         'langchain.chain.name': chainName,

diff --git a/packages/node/src/integrations/tracing/postgresjs.ts b/packages/node/src/integrations/tracing/postgresjs.ts
--- a/packages/node/src/integrations/tracing/postgresjs.ts
+++ b/packages/node/src/integrations/tracing/postgresjs.ts
@@ -17,6 +17,8 @@
 } from '@opentelemetry/semantic-conventions';
 import type { IntegrationFn, Span } from '@sentry/core';
 import {
+  _reconstructQuery,
+  _sanitizeSqlQuery,
   debug,
   defineIntegration,
   instrumentPostgresJsSql,
@@ -198,65 +200,6 @@
   }
 
   /**
-   * Reconstructs the full SQL query from template strings with PostgreSQL placeholders.
-   *
-   * For sql`SELECT * FROM users WHERE id = ${123} AND name = ${'foo'}`:
-   *   strings = ["SELECT * FROM users WHERE id = ", " AND name = ", ""]
-   *   returns: "SELECT * FROM users WHERE id = $1 AND name = $2"
-   */
-  private _reconstructQuery(strings: string[] | undefined): string | undefined {
-    if (!strings?.length) {
-      return undefined;
-    }
-    if (strings.length === 1) {
-      return strings[0] || undefined;
-    }
-    // Join template parts with PostgreSQL placeholders ($1, $2, etc.)
-    return strings.reduce((acc, str, i) => (i === 0 ? str : `${acc}$${i}${str}`), '');
-  }
-
-  /**
-   * Sanitize SQL query as per the OTEL semantic conventions
-   * https://opentelemetry.io/docs/specs/semconv/database/database-spans/#sanitization-of-dbquerytext
-   *
-   * PostgreSQL $n placeholders are preserved per OTEL spec - they're parameterized queries,
-   * not sensitive literals. Only actual values (strings, numbers, booleans) are sanitized.
-   */
-  private _sanitizeSqlQuery(sqlQuery: string | undefined): string {
-    if (!sqlQuery) {
-      return 'Unknown SQL Query';
-    }
-
-    return (
-      sqlQuery
-        // Remove comments first (they may contain newlines and extra spaces)
-        .replace(/--.*$/gm, '') // Single line comments (multiline mode)
-        .replace(/\/\*[\s\S]*?\*\//g, '') // Multi-line comments
-        .replace(/;\s*$/, '') // Remove trailing semicolons
-        // Collapse whitespace to a single space (after removing comments)
-        .replace(/\s+/g, ' ')
-        .trim() // Remove extra spaces and trim
-        // Sanitize hex/binary literals before string literals
-        .replace(/\bX'[0-9A-Fa-f]*'/gi, '?') // Hex string literals
-        .replace(/\bB'[01]*'/gi, '?') // Binary string literals
-        // Sanitize string literals (handles escaped quotes)
-        .replace(/'(?:[^']|'')*'/g, '?')
-        // Sanitize hex numbers
-        .replace(/\b0x[0-9A-Fa-f]+/gi, '?')
-        // Sanitize boolean literals
-        .replace(/\b(?:TRUE|FALSE)\b/gi, '?')
-        // Sanitize numeric literals (preserve $n placeholders via negative lookbehind)
-        .replace(/-?\b\d+\.?\d*[eE][+-]?\d+\b/g, '?') // Scientific notation
-        .replace(/-?\b\d+\.\d+\b/g, '?') // Decimals
-        .replace(/-?\.\d+\b/g, '?') // Decimals starting with dot
-        .replace(/(?<!\$)-?\b\d+\b/g, '?') // Integers (NOT $n placeholders)
-        // Collapse IN clauses for cardinality (both ? and $n variants)
-        .replace(/\bIN\b\s*\(\s*\?(?:\s*,\s*\?)*\s*\)/gi, 'IN (?)')
-        .replace(/\bIN\b\s*\(\s*\$\d+(?:\s*,\s*\$\d+)*\s*\)/gi, 'IN ($?)')
-    );
-  }
-
-  /**
    * Fallback patch for Query.prototype.handle to instrument queries from pre-existing sql instances.
    * This catches queries from sql instances created BEFORE Sentry was initialized (CJS only).
    *
@@ -294,8 +237,8 @@
         return originalHandle.apply(this, args);
       }
 
-      const fullQuery = self._reconstructQuery(this.strings);
-      const sanitizedSqlQuery = self._sanitizeSqlQuery(fullQuery);
+      const fullQuery = _reconstructQuery(this.strings);
+      const sanitizedSqlQuery = _sanitizeSqlQuery(fullQuery);
 
       return startSpanManual(
         {

diff --git a/packages/node/test/integrations/tracing/postgresjs.test.ts b/packages/node/test/integrations/tracing/postgresjs.test.ts
--- a/packages/node/test/integrations/tracing/postgresjs.test.ts
+++ b/packages/node/test/integrations/tracing/postgresjs.test.ts
@@ -1,3 +1,4 @@
+import { _reconstructQuery, _sanitizeSqlQuery } from '@sentry/core';
 import { describe, expect, it } from 'vitest';
 import { PostgresJsInstrumentation } from '../../../src/integrations/tracing/postgresjs';
 
@@ -5,10 +6,7 @@
   const instrumentation = new PostgresJsInstrumentation({ requireParentSpan: true });
 
   describe('_reconstructQuery', () => {
-    const reconstruct = (strings: string[] | undefined) =>
-      (
-        instrumentation as unknown as { _reconstructQuery: (s: string[] | undefined) => string | undefined }
-      )._reconstructQuery(strings);
+    const reconstruct = (strings: string[] | undefined) => _reconstructQuery(strings);
 
     describe('empty input handling', () => {
       it.each([
@@ -69,10 +67,7 @@
     });
 
     describe('integration with _sanitizeSqlQuery', () => {
-      const sanitize = (query: string | undefined) =>
-        (instrumentation as unknown as { _sanitizeSqlQuery: (q: string | undefined) => string })._sanitizeSqlQuery(
-          query,
-        );
+      const sanitize = (query: string | undefined) => _sanitizeSqlQuery(query);
 
       it('preserves $n placeholders per OTEL spec', () => {
         const strings = ['SELECT * FROM users WHERE id = ', ' AND name = ', ''];
@@ -96,8 +91,7 @@
   });
 
   describe('_sanitizeSqlQuery', () => {
-    const sanitize = (query: string | undefined) =>
-      (instrumentation as unknown as { _sanitizeSqlQuery: (q: string | undefined) => string })._sanitizeSqlQuery(query);
+    const sanitize = (query: string | undefined) => _sanitizeSqlQuery(query);
 
     describe('passthrough (no literals)', () => {
       it.each([
This Bugbot Autofix run was free. To enable autofix for future PRs, go to the Cursor dashboard.

@github-actions
Copy link
Contributor

github-actions bot commented Feb 27, 2026

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,912 - 8,895 +0%
GET With Sentry 1,647 18% 1,704 -3%
GET With Sentry (error only) 6,026 68% 6,025 +0%
POST Baseline 1,177 - 1,203 -2%
POST With Sentry 584 50% 596 -2%
POST With Sentry (error only) 1,034 88% 1,050 -2%
MYSQL Baseline 3,185 - 3,240 -2%
MYSQL With Sentry 358 11% 486 -26%
MYSQL With Sentry (error only) 2,616 82% 2,651 -1%

View base workflow run

@andreiborza andreiborza force-pushed the ab/postgresjs-core-instrumentation branch from f399513 to 6f8db7d Compare February 27, 2026 14:40
Copy link

@cursor cursor bot left a comment

Choose a reason for hiding this comment

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

Cursor Bugbot has reviewed your changes and found 2 potential issues.

Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.

*
* @internal Exported for testing only
*/
export function _sanitizeSqlQuery(sqlQuery: string | undefined): string {
Copy link
Member

Choose a reason for hiding this comment

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

Just making a note of this, this might be very useful exposed as a util for other DB instrumentations like db0. We can do it later once we need it.

Copy link
Member Author

Choose a reason for hiding this comment

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

Right, yea I mean we could already use it directly in the node SDK too but I didn't want to bloat core's api surface for these.

If we find multiple usages we can always think of exposing these as utils, good point.

Copy link
Member

@logaretm logaretm left a comment

Choose a reason for hiding this comment

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

Nice one

@andreiborza andreiborza merged commit 88078a0 into develop Feb 27, 2026
435 of 439 checks passed
@andreiborza andreiborza deleted the ab/postgresjs-core-instrumentation branch February 27, 2026 16:00
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.

feat(core,cloudflare,deno): Add instrumentPostgresJsSql instrumentation #19566

2 participants