diff --git a/packages/cloudflare/src/index.ts b/packages/cloudflare/src/index.ts index d114590262bf..a3a07a905470 100644 --- a/packages/cloudflare/src/index.ts +++ b/packages/cloudflare/src/index.ts @@ -106,7 +106,8 @@ export { withStreamedSpan, spanStreamingIntegration, } from '@sentry/core'; -export { instrumentPostgresJsSql, trpcMiddleware, wrapMcpServerWithSentry } from '@sentry/core/server'; +export { trpcMiddleware, wrapMcpServerWithSentry } from '@sentry/core/server'; +export { instrumentPostgresJsSql } from '@sentry/server-utils'; export { withSentry } from './withSentry'; export { defineCloudflareOptions } from './defineCloudflareOptions'; diff --git a/packages/cloudflare/src/instrumentations/instrumentSqlStorage.ts b/packages/cloudflare/src/instrumentations/instrumentSqlStorage.ts index 092074c371f6..e7ae5e5d173f 100644 --- a/packages/cloudflare/src/instrumentations/instrumentSqlStorage.ts +++ b/packages/cloudflare/src/instrumentations/instrumentSqlStorage.ts @@ -2,7 +2,7 @@ import type { SqlStorage } from '@cloudflare/workers-types'; import { SENTRY_OP } from '@sentry/conventions/attributes'; import { DB_QUERY } from '@sentry/conventions/op'; import { getClient, SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN, startSpan } from '@sentry/core'; -import { _INTERNAL_getSqlQuerySummary, _INTERNAL_sanitizeSqlQuery } from '@sentry/core/server'; +import { getSqlQuerySummary, sanitizeSqlQuery } from '@sentry/server-utils'; import type { CloudflareClientOptions } from '../client'; import { targetsCloudflareInternalTable } from '../utils/internalSqlQuery'; @@ -24,8 +24,8 @@ export function instrumentSqlStorage(sql: SqlStorage): SqlStorage { return function (this: unknown, ...args: unknown[]) { const [query, ...bindings] = args as [string, ...unknown[]]; - const sanitizedQuery = _INTERNAL_sanitizeSqlQuery(query); - const querySummary = _INTERNAL_getSqlQuerySummary(sanitizedQuery); + const sanitizedQuery = sanitizeSqlQuery(query); + const querySummary = getSqlQuerySummary(sanitizedQuery); // oxlint-disable-next-line typescript/no-unnecessary-type-assertion -- rule false positive: the cast reaches the Cloudflare-only `durableObjectSqlSpanAllowlist`; tsc errors without it const allowlist = (getClient()?.getOptions() as CloudflareClientOptions | undefined) diff --git a/packages/cloudflare/src/instrumentations/worker/instrumentD1.ts b/packages/cloudflare/src/instrumentations/worker/instrumentD1.ts index 322ffa4c4e4b..3827a3eafdc0 100644 --- a/packages/cloudflare/src/instrumentations/worker/instrumentD1.ts +++ b/packages/cloudflare/src/instrumentations/worker/instrumentD1.ts @@ -11,7 +11,7 @@ import { SPAN_STATUS_ERROR, startSpan, } from '@sentry/core'; -import { _INTERNAL_getSqlQuerySummary, _INTERNAL_sanitizeSqlQuery } from '@sentry/core/server'; +import { getSqlQuerySummary, sanitizeSqlQuery } from '@sentry/server-utils'; import { ensureInstrumented } from '../../instrument'; // Patching is based on internal Cloudflare D1 API @@ -131,7 +131,7 @@ function createD1Breadcrumb(query: string, type: D1QueryType, d1Result?: D1Respo } function createStartSpanOptions(query: string, type: D1QueryType): StartSpanOptions { - const querySummary = query ? _INTERNAL_getSqlQuerySummary(_INTERNAL_sanitizeSqlQuery(query)) : undefined; + const querySummary = query ? getSqlQuerySummary(sanitizeSqlQuery(query)) : undefined; const client = getClient(); const name = client && hasSpanStreamingEnabled(client) ? querySummary || 'cloudflare-d1' : query; diff --git a/packages/core/src/server.ts b/packages/core/src/server.ts index 0accf0ad69ce..7bc3aca4abb6 100644 --- a/packages/core/src/server.ts +++ b/packages/core/src/server.ts @@ -23,20 +23,6 @@ export type { ExpressErrorMiddleware, } from './integrations/express/types'; /* oxlint-enable typescript/no-deprecated */ -export { - instrumentPostgresJsSql, - _reconstructQuery as _INTERNAL_reconstructPostgresQuery, - _buildConnectionContext as _INTERNAL_buildPostgresConnectionContext, - _getConnectionAttributes as _INTERNAL_getConnectionAttributes, - _getOperationName as _INTERNAL_getPostgresOperationName, -} from './integrations/postgresjs'; -export type { PostgresConnectionContext } from './integrations/postgresjs'; -export { - getSqlQuerySummary as _INTERNAL_getSqlQuerySummary, - sanitizeSqlQuery as _INTERNAL_sanitizeSqlQuery, -} from './utils/sql'; -export type { SqlDialect } from './utils/sql'; - export { patchHttpModuleClient } from './integrations/http/client-patch'; export { getHttpClientSubscriptions } from './integrations/http/client-subscriptions'; export { getHttpServerSubscriptions, isStaticAssetRequest } from './integrations/http/server-subscription'; diff --git a/packages/deno/src/index.ts b/packages/deno/src/index.ts index aa3fa1babba8..fa1cb2df6b9f 100644 --- a/packages/deno/src/index.ts +++ b/packages/deno/src/index.ts @@ -101,7 +101,8 @@ export { consoleLoggingIntegration, spanStreamingIntegration, } from '@sentry/core'; -export { instrumentPostgresJsSql, wrapMcpServerWithSentry } from '@sentry/core/server'; +export { wrapMcpServerWithSentry } from '@sentry/core/server'; +export { instrumentPostgresJsSql } from '@sentry/server-utils'; export { DenoClient } from './client'; diff --git a/packages/server-utils/src/exports.ts b/packages/server-utils/src/exports.ts index 9baca0c266f6..bf6a2e17d89e 100644 --- a/packages/server-utils/src/exports.ts +++ b/packages/server-utils/src/exports.ts @@ -3,3 +3,7 @@ export { setHttpServerSpanRouteAttribute } from './utils/setHttpServerSpanRouteA export { setAsyncLocalStorageAsyncContextStrategy } from './async-context'; export { otlpIntegration, getOtlpTracesEndpoint } from './otlp'; export * from './ai'; +export { getSqlQuerySummary, sanitizeSqlQuery } from './utils/sql'; +export type { SqlDialect } from './utils/sql'; +export { instrumentPostgresJsSql } from './integrations/postgresjs'; +export type { PostgresConnectionContext } from './integrations/postgresjs'; diff --git a/packages/server-utils/src/integrations/mysql.ts b/packages/server-utils/src/integrations/mysql.ts index 3876df576a0a..30ccfdcb8e60 100644 --- a/packages/server-utils/src/integrations/mysql.ts +++ b/packages/server-utils/src/integrations/mysql.ts @@ -22,7 +22,7 @@ import { SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN, startInactiveSpan, } from '@sentry/core'; -import { _INTERNAL_getSqlQuerySummary, _INTERNAL_sanitizeSqlQuery } from '@sentry/core/server'; +import { getSqlQuerySummary, sanitizeSqlQuery } from '../utils/sql'; import { CHANNELS } from '../orchestrion/channels'; import { bindTracingChannelToSpan } from '../tracing-channel'; import { mysqlModuleNames } from '../orchestrion/config/mysql'; @@ -88,7 +88,7 @@ function instrumentMysql(): void { // handler with the caller's context lost. `deferSpanEnd` replays this scope onto the emitter. data._sentryCallerScope = getCurrentScope(); - const querySummary = sql ? _INTERNAL_getSqlQuerySummary(_INTERNAL_sanitizeSqlQuery(sql, 'mysql')) : undefined; + const querySummary = sql ? getSqlQuerySummary(sanitizeSqlQuery(sql, 'mysql')) : undefined; const client = getClient(); const name = diff --git a/packages/server-utils/src/integrations/mysql2/index.ts b/packages/server-utils/src/integrations/mysql2/index.ts index e00783fc27bb..684c0886b45e 100644 --- a/packages/server-utils/src/integrations/mysql2/index.ts +++ b/packages/server-utils/src/integrations/mysql2/index.ts @@ -9,7 +9,7 @@ import { startInactiveSpan, waitForTracingChannelBinding, } from '@sentry/core'; -import { _INTERNAL_getSqlQuerySummary, _INTERNAL_sanitizeSqlQuery } from '@sentry/core/server'; +import { getSqlQuerySummary, sanitizeSqlQuery } from '../../utils/sql'; import { subscribeMysql2DiagnosticChannels } from './mysql2-dc-subscriber'; import type { ChannelName } from '../../orchestrion/channels'; import { CHANNELS } from '../../orchestrion/channels'; @@ -84,9 +84,7 @@ function subscribeQueryChannel(channelName: ChannelName): void { data => { const statement = getQueryText(data.arguments); const connectionAttributes = getConnectionAttributes(data.self?.config); - const querySummary = statement - ? _INTERNAL_getSqlQuerySummary(_INTERNAL_sanitizeSqlQuery(statement, 'mysql')) - : undefined; + const querySummary = statement ? getSqlQuerySummary(sanitizeSqlQuery(statement, 'mysql')) : undefined; const client = getClient(); const name = diff --git a/packages/server-utils/src/integrations/mysql2/mysql2-dc-subscriber.ts b/packages/server-utils/src/integrations/mysql2/mysql2-dc-subscriber.ts index b1a31e31746b..9920ac7850a5 100644 --- a/packages/server-utils/src/integrations/mysql2/mysql2-dc-subscriber.ts +++ b/packages/server-utils/src/integrations/mysql2/mysql2-dc-subscriber.ts @@ -12,7 +12,7 @@ import { import { DB } from '@sentry/conventions/op'; import { getClient, hasSpanStreamingEnabled, SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN, startInactiveSpan } from '@sentry/core'; import { bindTracingChannelToSpan } from '../../tracing-channel'; -import { _INTERNAL_getSqlQuerySummary, _INTERNAL_sanitizeSqlQuery } from '@sentry/core/server'; +import { getSqlQuerySummary, sanitizeSqlQuery } from '../../utils/sql'; // Channel names published by mysql2 >= 3.20.0 (see mysql2 `lib/tracing.js`). // Hardcoded so the subscriber does not have to import mysql2 — the channels @@ -97,9 +97,9 @@ function setupQueryChannel(tracingChannel: MySQL2TracingChannelFactory, channelN // mysql2 does not sanitize its channel payload, so the statement may carry // raw user values (on the `query` channel they are inlined). Strip every // literal before it leaves the process; `values` is never attached. - const queryText = data.query ? _INTERNAL_sanitizeSqlQuery(data.query, 'mysql') : undefined; + const queryText = data.query ? sanitizeSqlQuery(data.query, 'mysql') : undefined; const operation = queryText?.match(SQL_OPERATION_RE)?.[1]?.toUpperCase(); - const querySummary = _INTERNAL_getSqlQuerySummary(queryText); + const querySummary = getSqlQuerySummary(queryText); const client = getClient(); const name = diff --git a/packages/server-utils/src/integrations/postgres-js.ts b/packages/server-utils/src/integrations/postgres-js.ts index f0eb4ee9da1f..24cf8e6c656a 100644 --- a/packages/server-utils/src/integrations/postgres-js.ts +++ b/packages/server-utils/src/integrations/postgres-js.ts @@ -10,7 +10,6 @@ import { } from '@sentry/conventions/attributes'; import { DB } from '@sentry/conventions/op'; import type { IntegrationFn, Span } from '@sentry/core'; -import type { PostgresConnectionContext } from '@sentry/core/server'; import { debug, defineIntegration, @@ -20,15 +19,15 @@ import { SPAN_STATUS_ERROR, startInactiveSpan, } from '@sentry/core'; -import { - _INTERNAL_buildPostgresConnectionContext, - _INTERNAL_getConnectionAttributes, - _INTERNAL_getPostgresOperationName, - _INTERNAL_getSqlQuerySummary, - _INTERNAL_reconstructPostgresQuery, - _INTERNAL_sanitizeSqlQuery, -} from '@sentry/core/server'; import { DEBUG_BUILD } from '../debug-build'; +import { getSqlQuerySummary, sanitizeSqlQuery } from '../utils/sql'; +import { + _buildConnectionContext, + _getConnectionAttributes, + _getOperationName, + _reconstructQuery, + type PostgresConnectionContext, +} from './postgresjs'; import { CHANNELS } from '../orchestrion/channels'; import { bindTracingChannelToSpan } from '../tracing-channel'; import { postgresJsModuleNames } from '../orchestrion/config/postgres'; @@ -125,7 +124,7 @@ function recordConnectionFromChannel(message: PostgresJsQueryContext): void { if (!connection || typeof connection !== 'object' || !options) { return; } - const context = _INTERNAL_buildPostgresConnectionContext(options); + const context = _buildConnectionContext(options); connectionContexts.set(connection, context); registerEndpoint(context); } @@ -137,7 +136,7 @@ function setConnectionAttributes(span: Span, query: PostgresQuery, context: Post } queryRecord[CONNECTION_ATTRS_SET] = true; if (context) { - span.setAttributes(_INTERNAL_getConnectionAttributes(context)); + span.setAttributes(_getConnectionAttributes(context)); } } @@ -189,7 +188,7 @@ function wrapQuerySettlement(data: PostgresJsQueryContext, span: Span, sanitized try { const command = (resolveArgs[0] as { command?: string } | undefined)?.command; // Re-set the operation name with the server-reported command, which is more reliable than the query text. - span.setAttribute(DB_OPERATION_NAME, _INTERNAL_getPostgresOperationName(sanitizedSqlQuery, command)); + span.setAttribute(DB_OPERATION_NAME, _getOperationName(sanitizedSqlQuery, command)); span.end(); } catch (e) { DEBUG_BUILD && debug.error('[instrumentation:postgresjs] error ending span in resolve:', e); @@ -278,10 +277,10 @@ function instrumentPostgresJs(options: PostgresJsIntegrationOptions): void { return undefined; } - const fullQuery = _INTERNAL_reconstructPostgresQuery(query.strings); - const sanitizedSqlQuery = _INTERNAL_sanitizeSqlQuery(fullQuery); + const fullQuery = _reconstructQuery(query.strings); + const sanitizedSqlQuery = sanitizeSqlQuery(fullQuery); - const querySummary = _INTERNAL_getSqlQuerySummary(sanitizedSqlQuery); + const querySummary = getSqlQuerySummary(sanitizedSqlQuery); const client = getClient(); @@ -304,7 +303,7 @@ function instrumentPostgresJs(options: PostgresJsIntegrationOptions): void { [DB_SYSTEM_NAME]: DB_SYSTEM_NAME_POSTGRES, [DB_QUERY_TEXT]: sanitizedSqlQuery, [DB_QUERY_SUMMARY]: querySummary, - [DB_OPERATION_NAME]: _INTERNAL_getPostgresOperationName(sanitizedSqlQuery), + [DB_OPERATION_NAME]: _getOperationName(sanitizedSqlQuery), }, }); diff --git a/packages/server-utils/src/integrations/postgres.ts b/packages/server-utils/src/integrations/postgres.ts index 49ee295249a1..d2b54e0ef089 100644 --- a/packages/server-utils/src/integrations/postgres.ts +++ b/packages/server-utils/src/integrations/postgres.ts @@ -22,7 +22,7 @@ import { SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN, startInactiveSpan, } from '@sentry/core'; -import { _INTERNAL_getSqlQuerySummary, _INTERNAL_sanitizeSqlQuery } from '@sentry/core/server'; +import { getSqlQuerySummary, sanitizeSqlQuery } from '../utils/sql'; import { CHANNELS } from '../orchestrion/channels'; import { bindTracingChannelToSpan } from '../tracing-channel'; import { pgModuleNames } from '../orchestrion/config/pg'; @@ -182,9 +182,7 @@ function querySpanOptions(ctx: PgChannelContext): { name: string; attributes: Sp const client = getClient(); // The statement is sanitized before it is summarized, so that a string literal containing // `from`/`join` can't leak a value into the summary. - const querySummary = queryConfig?.text - ? _INTERNAL_getSqlQuerySummary(_INTERNAL_sanitizeSqlQuery(queryConfig.text)) - : undefined; + const querySummary = queryConfig?.text ? getSqlQuerySummary(sanitizeSqlQuery(queryConfig.text)) : undefined; const name = client && hasSpanStreamingEnabled(client) diff --git a/packages/core/src/integrations/postgresjs.ts b/packages/server-utils/src/integrations/postgresjs.ts similarity index 97% rename from packages/core/src/integrations/postgresjs.ts rename to packages/server-utils/src/integrations/postgresjs.ts index 49bb7ff38cc5..8588ed1a56ea 100644 --- a/packages/core/src/integrations/postgresjs.ts +++ b/packages/server-utils/src/integrations/postgresjs.ts @@ -3,16 +3,19 @@ // without depending on OpenTelemetry module hooking. /* eslint-disable max-lines */ -import { getClient } from '../currentScopes'; +import { + debug, + getActiveSpan, + getClient, + hasSpanStreamingEnabled, + isObjectLike, + type Span, + type SpanAttributes, + SPAN_STATUS_ERROR, + startSpanManual, +} from '@sentry/core'; import { DEBUG_BUILD } from '../debug-build'; -import { SPAN_STATUS_ERROR } from '../tracing'; -import { hasSpanStreamingEnabled } from '../tracing/spans/hasSpanStreamingEnabled'; -import { startSpanManual } from '../tracing/trace'; -import type { Span, SpanAttributes } from '../types/span'; import { getSqlQuerySummary, sanitizeSqlQuery } from '../utils/sql'; -import { debug } from '../utils/debug-logger'; -import { isObjectLike } from '../utils/is'; -import { getActiveSpan } from '../utils/spanUtils'; import { DB_NAMESPACE, DB_OPERATION_NAME, diff --git a/packages/core/src/utils/sql.ts b/packages/server-utils/src/utils/sql.ts similarity index 100% rename from packages/core/src/utils/sql.ts rename to packages/server-utils/src/utils/sql.ts diff --git a/packages/core/test/lib/integrations/postgresjs.test.ts b/packages/server-utils/test/integrations/postgresjs.test.ts similarity index 97% rename from packages/core/test/lib/integrations/postgresjs.test.ts rename to packages/server-utils/test/integrations/postgresjs.test.ts index dc6c4508279e..3d2283f2cc28 100644 --- a/packages/core/test/lib/integrations/postgresjs.test.ts +++ b/packages/server-utils/test/integrations/postgresjs.test.ts @@ -1,8 +1,7 @@ +import * as SentryCore from '@sentry/core'; import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'; -import { _reconstructQuery, instrumentPostgresJsSql } from '../../../src/integrations/postgresjs'; -import { sanitizeSqlQuery } from '../../../src/utils/sql'; -import * as tracing from '../../../src/tracing/trace'; -import * as spanUtils from '../../../src/utils/spanUtils'; +import { _reconstructQuery, instrumentPostgresJsSql } from '../../src/integrations/postgresjs'; +import { sanitizeSqlQuery } from '../../src/utils/sql'; describe('PostgresJs portable instrumentation', () => { describe('_reconstructQuery', () => { @@ -196,7 +195,7 @@ describe('PostgresJs portable instrumentation', () => { describe('span creation', () => { beforeEach(() => { // By default, mock getActiveSpan to return undefined (no parent) - vi.spyOn(spanUtils, 'getActiveSpan').mockReturnValue(undefined); + vi.spyOn(SentryCore, 'getActiveSpan').mockReturnValue(undefined); }); afterEach(() => { @@ -245,7 +244,7 @@ describe('PostgresJs portable instrumentation', () => { it('only creates one span even when handle() is called multiple times', async () => { const mockSpan = { setAttribute: vi.fn(), setAttributes: vi.fn(), end: vi.fn() }; const startSpanManualSpy = vi - .spyOn(tracing, 'startSpanManual') + .spyOn(SentryCore, 'startSpanManual') .mockImplementation((_opts, callback) => callback(mockSpan as any, () => {})); const originalHandle = vi.fn().mockResolvedValue([]); diff --git a/packages/core/test/lib/utils/sql.ported.test.ts b/packages/server-utils/test/utils/sql.ported.test.ts similarity index 99% rename from packages/core/test/lib/utils/sql.ported.test.ts rename to packages/server-utils/test/utils/sql.ported.test.ts index 91f5a193546a..1feca0093ca4 100644 --- a/packages/core/test/lib/utils/sql.ported.test.ts +++ b/packages/server-utils/test/utils/sql.ported.test.ts @@ -12,7 +12,7 @@ */ import { describe, expect, it } from 'vitest'; -import { getSqlQuerySummary } from '../../../src/utils/sql'; +import { getSqlQuerySummary } from '../../src/utils/sql'; describe('getSqlQuerySummary', () => { describe('joins', () => { diff --git a/packages/core/test/lib/utils/sql.test.ts b/packages/server-utils/test/utils/sql.test.ts similarity index 99% rename from packages/core/test/lib/utils/sql.test.ts rename to packages/server-utils/test/utils/sql.test.ts index b41463645870..117f399dfe42 100644 --- a/packages/core/test/lib/utils/sql.test.ts +++ b/packages/server-utils/test/utils/sql.test.ts @@ -1,5 +1,5 @@ import { describe, expect, it } from 'vitest'; -import { getSqlQuerySummary, sanitizeSqlQuery } from '../../../src/utils/sql'; +import { getSqlQuerySummary, sanitizeSqlQuery } from '../../src/utils/sql'; describe('getSqlQuerySummary', () => { it.each([undefined, ''])('returns undefined for %j', input => {