feat(core,cloudflare,deno): Add instrumentPostgresJsSql instrumentation#19566
feat(core,cloudflare,deno): Add instrumentPostgresJsSql instrumentation#19566andreiborza merged 2 commits intodevelopfrom
Conversation
size-limit report 📦
|
There was a problem hiding this comment.
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
runNamedropped 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.
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([
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.
|
f399513 to
6f8db7d
Compare
There was a problem hiding this comment.
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 { |
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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.

This PR extracts parts from
@sentry/node'sPostgresJsInstrumentationto@sentry/coreand exposes it as a callableinstrumentPostgresJsSqlhelper that can be used in SDKs that aren't based on OpenTelemetry.It is currently exposed in
@sentry/cloudflareand@sentry/denoas these two SDKs are not based on OpenTelemetry under the hood.@sentry/nodeimports and reuses as much as possible from it.Closes #19567 (added automatically)