From abd1f1071defac668939434e47d0b5922ad682e7 Mon Sep 17 00:00:00 2001 From: Martin Sonnberger Date: Tue, 21 Jul 2026 15:22:32 +0200 Subject: [PATCH 1/2] feat(node): Remove connect instrumentation Removes the `connectIntegration` / `setupConnectErrorHandler` API and the vendored `@opentelemetry/instrumentation-connect` files from `@sentry/node`, along with its wiring, tests, and the re-exports in every downstream server SDK. We have seen no connect spans throughout the year, so the instrumentation is effectively unused and not worth maintaining. Ref JS-2922 Co-Authored-By: Claude Opus 4.8 (1M context) --- .../node-connect/package.json | 28 --- .../node-connect/playwright.config.mjs | 7 - .../test-applications/node-connect/src/app.ts | 59 ------ .../node-connect/start-event-proxy.mjs | 6 - .../node-connect/tests/errors.test.ts | 35 ---- .../node-connect/tests/transactions.test.ts | 102 ---------- .../node-connect/tsconfig.json | 11 -- .../node-integration-tests/package.json | 1 - .../suites/tracing/connect/instrument.mjs | 9 - .../suites/tracing/connect/scenario.mjs | 36 ---- .../suites/tracing/connect/test.ts | 116 ------------ packages/astro/src/index.server.ts | 2 - packages/aws-serverless/src/index.ts | 2 - packages/bun/src/index.ts | 2 - packages/elysia/src/index.ts | 2 - packages/google-cloud-serverless/src/index.ts | 2 - packages/node/src/index.ts | 1 - .../src/integrations/tracing/connect/index.ts | 75 -------- .../connect/vendored/enums/AttributeNames.ts | 18 -- .../connect/vendored/instrumentation.ts | 179 ------------------ .../connect/vendored/internal-types.ts | 53 ------ .../tracing/connect/vendored/utils.ts | 46 ----- .../node/src/integrations/tracing/index.ts | 3 - .../test/integrations/tracing/connect.test.ts | 87 --------- packages/remix/src/server/index.ts | 2 - packages/solidstart/src/server/index.ts | 2 - packages/sveltekit/src/server/index.ts | 2 - 27 files changed, 888 deletions(-) delete mode 100644 dev-packages/e2e-tests/test-applications/node-connect/package.json delete mode 100644 dev-packages/e2e-tests/test-applications/node-connect/playwright.config.mjs delete mode 100644 dev-packages/e2e-tests/test-applications/node-connect/src/app.ts delete mode 100644 dev-packages/e2e-tests/test-applications/node-connect/start-event-proxy.mjs delete mode 100644 dev-packages/e2e-tests/test-applications/node-connect/tests/errors.test.ts delete mode 100644 dev-packages/e2e-tests/test-applications/node-connect/tests/transactions.test.ts delete mode 100644 dev-packages/e2e-tests/test-applications/node-connect/tsconfig.json delete mode 100644 dev-packages/node-integration-tests/suites/tracing/connect/instrument.mjs delete mode 100644 dev-packages/node-integration-tests/suites/tracing/connect/scenario.mjs delete mode 100644 dev-packages/node-integration-tests/suites/tracing/connect/test.ts delete mode 100644 packages/node/src/integrations/tracing/connect/index.ts delete mode 100644 packages/node/src/integrations/tracing/connect/vendored/enums/AttributeNames.ts delete mode 100644 packages/node/src/integrations/tracing/connect/vendored/instrumentation.ts delete mode 100644 packages/node/src/integrations/tracing/connect/vendored/internal-types.ts delete mode 100644 packages/node/src/integrations/tracing/connect/vendored/utils.ts delete mode 100644 packages/node/test/integrations/tracing/connect.test.ts diff --git a/dev-packages/e2e-tests/test-applications/node-connect/package.json b/dev-packages/e2e-tests/test-applications/node-connect/package.json deleted file mode 100644 index 729cfbe6c095..000000000000 --- a/dev-packages/e2e-tests/test-applications/node-connect/package.json +++ /dev/null @@ -1,28 +0,0 @@ -{ - "name": "node-connect", - "version": "1.0.0", - "private": true, - "scripts": { - "start": "ts-node src/app.ts", - "test": "playwright test", - "clean": "npx rimraf node_modules pnpm-lock.yaml", - "typecheck": "tsc", - "test:build": "pnpm install && pnpm run typecheck", - "test:assert": "pnpm test" - }, - "dependencies": { - "@sentry/node": "file:../../packed/sentry-node-packed.tgz", - "@types/node": "^18.19.1", - "@types/connect": "3.4.38", - "connect": "3.7.0", - "typescript": "~5.0.0", - "ts-node": "10.9.1" - }, - "devDependencies": { - "@playwright/test": "~1.56.0", - "@sentry-internal/test-utils": "link:../../../test-utils" - }, - "volta": { - "extends": "../../package.json" - } -} diff --git a/dev-packages/e2e-tests/test-applications/node-connect/playwright.config.mjs b/dev-packages/e2e-tests/test-applications/node-connect/playwright.config.mjs deleted file mode 100644 index 31f2b913b58b..000000000000 --- a/dev-packages/e2e-tests/test-applications/node-connect/playwright.config.mjs +++ /dev/null @@ -1,7 +0,0 @@ -import { getPlaywrightConfig } from '@sentry-internal/test-utils'; - -const config = getPlaywrightConfig({ - startCommand: `pnpm start`, -}); - -export default config; diff --git a/dev-packages/e2e-tests/test-applications/node-connect/src/app.ts b/dev-packages/e2e-tests/test-applications/node-connect/src/app.ts deleted file mode 100644 index 375554845d6f..000000000000 --- a/dev-packages/e2e-tests/test-applications/node-connect/src/app.ts +++ /dev/null @@ -1,59 +0,0 @@ -import type * as S from '@sentry/node'; -const Sentry = require('@sentry/node') as typeof S; - -Sentry.init({ - environment: 'qa', // dynamic sampling bias to keep transactions - dsn: process.env.E2E_TEST_DSN, - integrations: [], - tracesSampleRate: 1, - tunnel: 'http://localhost:3031/', // proxy server - tracePropagationTargets: ['http://localhost:3030', '/external-allowed'], -}); - -import type * as H from 'http'; -import type C from 'connect'; - -const connect = require('connect') as typeof C; -const http = require('http') as typeof H; - -const app = connect(); -const port = 3030; - -app.use('/test-success', (req, res, next) => { - res.end( - JSON.stringify({ - version: 'v1', - }), - ); -}); - -app.use('/test-error', async (req, res, next) => { - const exceptionId = Sentry.captureException(new Error('Sentry Test Error')); - - await Sentry.flush(); - - res.end(JSON.stringify({ exceptionId })); - next(); -}); - -app.use('/test-exception', () => { - throw new Error('This is an exception'); -}); - -app.use('/test-transaction', (req, res, next) => { - Sentry.startSpan({ name: 'test-span' }, () => {}); - - res.end( - JSON.stringify({ - version: 'v1', - }), - ); - - next(); -}); - -Sentry.setupConnectErrorHandler(app); - -const server = http.createServer(app); - -server.listen(port); diff --git a/dev-packages/e2e-tests/test-applications/node-connect/start-event-proxy.mjs b/dev-packages/e2e-tests/test-applications/node-connect/start-event-proxy.mjs deleted file mode 100644 index 9ae7c07950d4..000000000000 --- a/dev-packages/e2e-tests/test-applications/node-connect/start-event-proxy.mjs +++ /dev/null @@ -1,6 +0,0 @@ -import { startEventProxyServer } from '@sentry-internal/test-utils'; - -startEventProxyServer({ - port: 3031, - proxyServerName: 'node-connect', -}); diff --git a/dev-packages/e2e-tests/test-applications/node-connect/tests/errors.test.ts b/dev-packages/e2e-tests/test-applications/node-connect/tests/errors.test.ts deleted file mode 100644 index c8d7b3123482..000000000000 --- a/dev-packages/e2e-tests/test-applications/node-connect/tests/errors.test.ts +++ /dev/null @@ -1,35 +0,0 @@ -import { expect, test } from '@playwright/test'; -import { waitForError } from '@sentry-internal/test-utils'; - -test('Sends correct error event', async ({ baseURL }) => { - const errorEventPromise = waitForError('node-connect', event => { - return !event.type && event.exception?.values?.[0]?.value === 'This is an exception'; - }); - - await fetch(`${baseURL}/test-exception`); - - const errorEvent = await errorEventPromise; - - expect(errorEvent.exception?.values).toHaveLength(1); - const exception = errorEvent.exception?.values?.[0]; - expect(exception?.value).toBe('This is an exception'); - - expect(exception?.mechanism).toEqual({ - type: 'auto.middleware.connect', - handled: false, - }); - - expect(errorEvent.request).toEqual({ - method: 'GET', - cookies: {}, - headers: expect.any(Object), - url: 'http://localhost:3030/test-exception', - }); - - expect(errorEvent.transaction).toEqual('GET /test-exception'); - - expect(errorEvent.contexts?.trace).toEqual({ - trace_id: expect.stringMatching(/[a-f0-9]{32}/), - span_id: expect.stringMatching(/[a-f0-9]{16}/), - }); -}); diff --git a/dev-packages/e2e-tests/test-applications/node-connect/tests/transactions.test.ts b/dev-packages/e2e-tests/test-applications/node-connect/tests/transactions.test.ts deleted file mode 100644 index f6991ed7a75a..000000000000 --- a/dev-packages/e2e-tests/test-applications/node-connect/tests/transactions.test.ts +++ /dev/null @@ -1,102 +0,0 @@ -import { expect, test } from '@playwright/test'; -import { waitForTransaction } from '@sentry-internal/test-utils'; - -test('Sends an API route transaction', async ({ baseURL }) => { - const pageloadTransactionEventPromise = waitForTransaction('node-connect', transactionEvent => { - return ( - transactionEvent?.contexts?.trace?.op === 'http.server' && - transactionEvent?.transaction === 'GET /test-transaction' - ); - }); - - await fetch(`${baseURL}/test-transaction`); - - const transactionEvent = await pageloadTransactionEventPromise; - const transactionEventId = transactionEvent.event_id; - - expect(transactionEvent.contexts?.trace).toEqual({ - data: { - 'sentry.source': 'route', - 'sentry.origin': 'auto.http.otel.http', - 'sentry.op': 'http.server', - 'sentry.sample_rate': 1, - url: 'http://localhost:3030/test-transaction', - 'otel.kind': 'SERVER', - 'http.response.status_code': 200, - 'http.url': 'http://localhost:3030/test-transaction', - 'http.host': 'localhost:3030', - 'net.host.name': 'localhost', - 'http.method': 'GET', - 'http.scheme': 'http', - 'http.target': '/test-transaction', - 'http.user_agent': 'node', - 'http.flavor': '1.1', - 'net.transport': 'ip_tcp', - 'net.host.ip': expect.any(String), - 'net.host.port': expect.any(Number), - 'net.peer.ip': expect.any(String), - 'net.peer.port': expect.any(Number), - 'http.status_code': 200, - 'http.status_text': 'OK', - 'http.route': '/test-transaction', - 'http.request.header.accept': '*/*', - 'http.request.header.accept_encoding': 'gzip, deflate', - 'http.request.header.accept_language': '*', - 'http.request.header.connection': 'keep-alive', - 'http.request.header.host': expect.any(String), - 'http.request.header.sec_fetch_mode': 'cors', - 'http.request.header.user_agent': 'node', - }, - op: 'http.server', - span_id: expect.stringMatching(/[a-f0-9]{16}/), - status: 'ok', - trace_id: expect.stringMatching(/[a-f0-9]{32}/), - origin: 'auto.http.otel.http', - }); - - const manualSpanExpectation = { - data: { - 'sentry.origin': 'manual', - }, - description: 'test-span', - parent_span_id: expect.stringMatching(/[a-f0-9]{16}/), - span_id: expect.stringMatching(/[a-f0-9]{16}/), - start_timestamp: expect.any(Number), - status: 'ok', - timestamp: expect.any(Number), - trace_id: expect.stringMatching(/[a-f0-9]{32}/), - origin: 'manual', - }; - - const connectSpanExpectation = { - data: { - 'sentry.origin': 'auto.http.otel.connect', - 'sentry.op': 'request_handler.connect', - 'http.route': '/test-transaction', - 'connect.type': 'request_handler', - 'connect.name': '/test-transaction', - }, - op: 'request_handler.connect', - description: '/test-transaction', - parent_span_id: expect.stringMatching(/[a-f0-9]{16}/), - span_id: expect.stringMatching(/[a-f0-9]{16}/), - start_timestamp: expect.any(Number), - status: 'ok', - timestamp: expect.any(Number), - trace_id: expect.stringMatching(/[a-f0-9]{32}/), - origin: 'auto.http.otel.connect', - }; - - expect(transactionEvent).toEqual( - expect.objectContaining({ - // The SentryTracerProvider serializes native child spans in start/tree order, so the - // Connect handler span appears before the manual span created inside it. - spans: [connectSpanExpectation, manualSpanExpectation], - transaction: 'GET /test-transaction', - type: 'transaction', - transaction_info: { - source: 'route', - }, - }), - ); -}); diff --git a/dev-packages/e2e-tests/test-applications/node-connect/tsconfig.json b/dev-packages/e2e-tests/test-applications/node-connect/tsconfig.json deleted file mode 100644 index b7391228c421..000000000000 --- a/dev-packages/e2e-tests/test-applications/node-connect/tsconfig.json +++ /dev/null @@ -1,11 +0,0 @@ -{ - "compilerOptions": { - "types": ["node"], - "esModuleInterop": true, - "lib": ["dom", "dom.iterable", "esnext"], - "strict": true, - "noEmit": true, - "skipLibCheck": true - }, - "include": ["src/*.ts"] -} diff --git a/dev-packages/node-integration-tests/package.json b/dev-packages/node-integration-tests/package.json index a7a089786d65..8cfdf5b9f388 100644 --- a/dev-packages/node-integration-tests/package.json +++ b/dev-packages/node-integration-tests/package.json @@ -61,7 +61,6 @@ "ai": "^4.3.16", "amqplib": "^0.10.9", "body-parser": "^2.3.0", - "connect": "^3.7.0", "consola": "^3.2.3", "cors": "^2.8.5", "cron": "^3.1.6", diff --git a/dev-packages/node-integration-tests/suites/tracing/connect/instrument.mjs b/dev-packages/node-integration-tests/suites/tracing/connect/instrument.mjs deleted file mode 100644 index 46a27dd03b74..000000000000 --- a/dev-packages/node-integration-tests/suites/tracing/connect/instrument.mjs +++ /dev/null @@ -1,9 +0,0 @@ -import * as Sentry from '@sentry/node'; -import { loggingTransport } from '@sentry-internal/node-integration-tests'; - -Sentry.init({ - dsn: 'https://public@dsn.ingest.sentry.io/1337', - release: '1.0', - tracesSampleRate: 1.0, - transport: loggingTransport, -}); diff --git a/dev-packages/node-integration-tests/suites/tracing/connect/scenario.mjs b/dev-packages/node-integration-tests/suites/tracing/connect/scenario.mjs deleted file mode 100644 index 4ecabbebd0b1..000000000000 --- a/dev-packages/node-integration-tests/suites/tracing/connect/scenario.mjs +++ /dev/null @@ -1,36 +0,0 @@ -import * as Sentry from '@sentry/node'; -import { sendPortToRunner } from '@sentry-internal/node-integration-tests'; -import connect from 'connect'; -import http from 'http'; - -const port = 5986; - -const run = async () => { - const app = connect(); - - // Path-less middleware produces `middleware`-type spans (named and anonymous). - app.use(function middleware1(req, res, next) { - next(); - }); - - app.use((req, res, next) => next()); - - app.use('/', function (req, res, next) { - res.end('Hello World'); - next(); - }); - - app.use('/error', function () { - throw new Error('Sentry Test Error'); - }); - - Sentry.setupConnectErrorHandler(app); - - const server = http.createServer(app); - - server.listen(port); - - sendPortToRunner(port); -}; - -run(); diff --git a/dev-packages/node-integration-tests/suites/tracing/connect/test.ts b/dev-packages/node-integration-tests/suites/tracing/connect/test.ts deleted file mode 100644 index fa50ca028ab8..000000000000 --- a/dev-packages/node-integration-tests/suites/tracing/connect/test.ts +++ /dev/null @@ -1,116 +0,0 @@ -import { afterAll, describe, expect } from 'vitest'; -import { cleanupChildProcesses, createEsmAndCjsTests } from '../../../utils/runner'; - -describe('connect auto-instrumentation', () => { - afterAll(async () => { - cleanupChildProcesses(); - }); - - const EXPECTED_TRANSACTION = { - transaction: 'GET /', - spans: expect.arrayContaining([ - expect.objectContaining({ - data: expect.objectContaining({ - 'connect.name': '/', - 'connect.type': 'request_handler', - 'http.route': '/', - 'sentry.origin': 'auto.http.otel.connect', - 'sentry.op': 'request_handler.connect', - }), - description: '/', - origin: 'auto.http.otel.connect', - op: 'request_handler.connect', - status: 'ok', - }), - - expect.objectContaining({ - data: expect.objectContaining({ - 'connect.name': 'middleware1', - 'connect.type': 'middleware', - 'sentry.origin': 'auto.http.otel.connect', - 'sentry.op': 'middleware.connect', - }), - description: 'middleware1', - origin: 'auto.http.otel.connect', - op: 'middleware.connect', - status: 'ok', - }), - - expect.objectContaining({ - data: expect.objectContaining({ - 'connect.name': 'anonymous', - 'connect.type': 'middleware', - 'sentry.origin': 'auto.http.otel.connect', - 'sentry.op': 'middleware.connect', - }), - description: 'anonymous', - origin: 'auto.http.otel.connect', - op: 'middleware.connect', - status: 'ok', - }), - ]), - }; - - const EXPECTED_EVENT = { - exception: { - values: [ - { - type: 'Error', - value: 'Sentry Test Error', - }, - ], - }, - }; - - createEsmAndCjsTests( - __dirname, - 'scenario.mjs', - 'instrument.mjs', - (createTestRunner, test) => { - test('should auto-instrument `connect` package.', async () => { - const runner = createTestRunner().expect({ transaction: EXPECTED_TRANSACTION }).start(); - runner.makeRequest('get', '/'); - await runner.completed(); - }); - - test('should capture errors in `connect` middleware.', async () => { - const runner = createTestRunner().ignore('transaction').expect({ event: EXPECTED_EVENT }).start(); - runner.makeRequest('get', '/error'); - await runner.completed(); - }); - - test('should report errored transactions.', async () => { - const runner = createTestRunner() - .ignore('event') - .expect({ - transaction: { - transaction: 'GET /error', - spans: expect.arrayContaining([ - expect.objectContaining({ - data: expect.objectContaining({ - 'connect.name': 'connectErrorMiddleware', - 'connect.type': 'middleware', - 'sentry.origin': 'auto.http.otel.connect', - 'sentry.op': 'middleware.connect', - }), - description: 'connectErrorMiddleware', - origin: 'auto.http.otel.connect', - op: 'middleware.connect', - }), - - expect.objectContaining({ - description: '/error', - op: 'request_handler.connect', - status: 'internal_error', - }), - ]), - }, - }) - .start(); - runner.makeRequest('get', '/error'); - await runner.completed(); - }); - }, - { failsOnEsm: true }, - ); -}); diff --git a/packages/astro/src/index.server.ts b/packages/astro/src/index.server.ts index 789097ac44cb..7be3132e0fd4 100644 --- a/packages/astro/src/index.server.ts +++ b/packages/astro/src/index.server.ts @@ -26,7 +26,6 @@ export { captureMessage, captureSession, close, - connectIntegration, consoleIntegration, contextLinesIntegration, continueTrace, @@ -127,7 +126,6 @@ export { setTags, setAttribute, setAttributes, - setupConnectErrorHandler, setupExpressErrorHandler, setupHapiErrorHandler, // eslint-disable-next-line typescript/no-deprecated diff --git a/packages/aws-serverless/src/index.ts b/packages/aws-serverless/src/index.ts index 2078044d64a8..5b7b9ebafeb8 100644 --- a/packages/aws-serverless/src/index.ts +++ b/packages/aws-serverless/src/index.ts @@ -100,8 +100,6 @@ export { setupExpressErrorHandler, koaIntegration, setupKoaErrorHandler, - connectIntegration, - setupConnectErrorHandler, fastifyIntegration, firebaseIntegration, fsIntegration, diff --git a/packages/bun/src/index.ts b/packages/bun/src/index.ts index ae98ae2aecad..3e174941751c 100644 --- a/packages/bun/src/index.ts +++ b/packages/bun/src/index.ts @@ -125,8 +125,6 @@ export { firebaseIntegration, koaIntegration, setupKoaErrorHandler, - connectIntegration, - setupConnectErrorHandler, genericPoolIntegration, graphqlIntegration, knexIntegration, diff --git a/packages/elysia/src/index.ts b/packages/elysia/src/index.ts index abbdc9ec513d..63a80ae7e81a 100644 --- a/packages/elysia/src/index.ts +++ b/packages/elysia/src/index.ts @@ -103,8 +103,6 @@ export { firebaseIntegration, koaIntegration, setupKoaErrorHandler, - connectIntegration, - setupConnectErrorHandler, genericPoolIntegration, graphqlIntegration, knexIntegration, diff --git a/packages/google-cloud-serverless/src/index.ts b/packages/google-cloud-serverless/src/index.ts index 4ed627ad007e..e37ed3c5b559 100644 --- a/packages/google-cloud-serverless/src/index.ts +++ b/packages/google-cloud-serverless/src/index.ts @@ -101,8 +101,6 @@ export { setupExpressErrorHandler, koaIntegration, setupKoaErrorHandler, - connectIntegration, - setupConnectErrorHandler, fastifyIntegration, firebaseIntegration, genericPoolIntegration, diff --git a/packages/node/src/index.ts b/packages/node/src/index.ts index 1ad92fd21b8e..70a52d1c5b27 100644 --- a/packages/node/src/index.ts +++ b/packages/node/src/index.ts @@ -18,7 +18,6 @@ export { hapiIntegration, setupHapiErrorHandler } from './integrations/tracing/h // eslint-disable-next-line typescript/no-deprecated export { honoIntegration, setupHonoErrorHandler } from './integrations/tracing/hono'; export { koaIntegration, setupKoaErrorHandler } from './integrations/tracing/koa'; -export { connectIntegration, setupConnectErrorHandler } from './integrations/tracing/connect'; export { knexIntegration } from './integrations/tracing/knex'; export { tediousIntegration } from './integrations/tracing/tedious'; export { genericPoolIntegration } from './integrations/tracing/genericPool'; diff --git a/packages/node/src/integrations/tracing/connect/index.ts b/packages/node/src/integrations/tracing/connect/index.ts deleted file mode 100644 index 60e21c77b5eb..000000000000 --- a/packages/node/src/integrations/tracing/connect/index.ts +++ /dev/null @@ -1,75 +0,0 @@ -import { ConnectInstrumentation } from './vendored/instrumentation'; -import type { IntegrationFn } from '@sentry/core'; -import { captureException, defineIntegration } from '@sentry/core'; -import { ensureIsWrapped, generateInstrumentOnce } from '@sentry/node-core'; - -type ConnectApp = { - // eslint-disable-next-line @typescript-eslint/no-explicit-any - use: (middleware: any) => void; -}; - -const INTEGRATION_NAME = 'Connect' as const; - -export const instrumentConnect = generateInstrumentOnce(INTEGRATION_NAME, () => new ConnectInstrumentation()); - -const _connectIntegration = (() => { - return { - name: INTEGRATION_NAME, - setupOnce() { - instrumentConnect(); - }, - }; -}) satisfies IntegrationFn; - -/** - * Adds Sentry tracing instrumentation for [Connect](https://github.com/senchalabs/connect/). - * - * If you also want to capture errors, you need to call `setupConnectErrorHandler(app)` after you initialize your connect app. - * - * For more information, see the [connect documentation](https://docs.sentry.io/platforms/javascript/guides/connect/). - * - * @example - * ```javascript - * const Sentry = require('@sentry/node'); - * - * Sentry.init({ - * integrations: [Sentry.connectIntegration()], - * }) - * ``` - */ -export const connectIntegration = defineIntegration(_connectIntegration); - -// eslint-disable-next-line @typescript-eslint/no-explicit-any -function connectErrorMiddleware(err: any, req: any, res: any, next: any): void { - captureException(err, { - mechanism: { - handled: false, - type: 'auto.middleware.connect', - }, - }); - next(err); -} - -/** - * Add a Connect middleware to capture errors to Sentry. - * - * @param app The Connect app to attach the error handler to - * - * @example - * ```javascript - * const Sentry = require('@sentry/node'); - * const connect = require("connect"); - * - * const app = connect(); - * - * Sentry.setupConnectErrorHandler(app); - * - * // Add you connect routes here - * - * app.listen(3000); - * ``` - */ -export const setupConnectErrorHandler = (app: ConnectApp): void => { - app.use(connectErrorMiddleware); - ensureIsWrapped(app.use, 'connect'); -}; diff --git a/packages/node/src/integrations/tracing/connect/vendored/enums/AttributeNames.ts b/packages/node/src/integrations/tracing/connect/vendored/enums/AttributeNames.ts deleted file mode 100644 index 2cb1ae07504e..000000000000 --- a/packages/node/src/integrations/tracing/connect/vendored/enums/AttributeNames.ts +++ /dev/null @@ -1,18 +0,0 @@ -/* - * Copyright The OpenTelemetry Authors - * SPDX-License-Identifier: Apache-2.0 - * - * NOTICE from the Sentry authors: - * - Vendored from: https://github.com/open-telemetry/opentelemetry-js-contrib/tree/15ef7506553f631ea4181391e0c5725a56f0d082/packages/instrumentation-connect - * - Upstream version: @opentelemetry/instrumentation-connect@0.61.0 - */ - -export enum AttributeNames { - CONNECT_TYPE = 'connect.type', - CONNECT_NAME = 'connect.name', -} - -export enum ConnectTypes { - MIDDLEWARE = 'middleware', - REQUEST_HANDLER = 'request_handler', -} diff --git a/packages/node/src/integrations/tracing/connect/vendored/instrumentation.ts b/packages/node/src/integrations/tracing/connect/vendored/instrumentation.ts deleted file mode 100644 index 8328c03ebac8..000000000000 --- a/packages/node/src/integrations/tracing/connect/vendored/instrumentation.ts +++ /dev/null @@ -1,179 +0,0 @@ -/* - * Copyright The OpenTelemetry Authors - * SPDX-License-Identifier: Apache-2.0 - * - * NOTICE from the Sentry authors: - * - Vendored from: https://github.com/open-telemetry/opentelemetry-js-contrib/tree/15ef7506553f631ea4181391e0c5725a56f0d082/packages/instrumentation-connect - * - Upstream version: @opentelemetry/instrumentation-connect@0.61.0 - * - Minor TypeScript strictness adjustments for this repository's compiler settings - */ - -import type { ServerResponse } from 'http'; -import { AttributeNames, ConnectTypes } from './enums/AttributeNames'; -import type { HandleFunction, NextFunction, PatchedRequest, Server, Use, UseArgs, UseArgs2 } from './internal-types'; -import type { Span } from '@sentry/core'; -import { - isError, - SDK_VERSION, - SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN, - SPAN_STATUS_ERROR, - startInactiveSpan, -} from '@sentry/core'; -import { setHttpServerSpanRouteAttribute } from '../../../../utils/setHttpServerSpanRouteAttribute'; -import type { InstrumentationConfig } from '@opentelemetry/instrumentation'; -import { InstrumentationBase, InstrumentationNodeModuleDefinition, isWrapped } from '@opentelemetry/instrumentation'; -import { HTTP_ROUTE } from '@sentry/conventions/attributes'; -import { replaceCurrentStackRoute, addNewStackLayer, generateRoute } from './utils'; - -const PACKAGE_NAME = '@sentry/instrumentation-connect'; - -const ANONYMOUS_NAME = 'anonymous'; - -/** Connect instrumentation for OpenTelemetry */ -export class ConnectInstrumentation extends InstrumentationBase { - constructor(config: InstrumentationConfig = {}) { - super(PACKAGE_NAME, SDK_VERSION, config); - } - - init() { - return [ - new InstrumentationNodeModuleDefinition('connect', ['>=3.0.0 <4'], moduleExports => { - return this._patchConstructor(moduleExports); - }), - ]; - } - - private _patchApp(patchedApp: Server) { - // oxlint-disable-next-line typescript/unbound-method - if (!isWrapped(patchedApp.use)) { - this._wrap(patchedApp, 'use', this._patchUse.bind(this)); - } - // oxlint-disable-next-line typescript/unbound-method - if (!isWrapped(patchedApp.handle)) { - this._wrap(patchedApp, 'handle', this._patchHandle.bind(this)); - } - } - - private _patchConstructor(original: () => Server): () => Server { - const patchApp = this._patchApp.bind(this); - return function (this: Server, ...args: unknown[]) { - const app = Reflect.apply(original, this, args) as Server; - patchApp(app); - return app; - }; - } - - public _patchNext(next: NextFunction, span: Span, finishSpan: () => void): NextFunction { - return function nextFunction(this: NextFunction, err?: unknown): void { - if (isError(err)) { - span.setStatus({ code: SPAN_STATUS_ERROR, message: 'internal_error' }); - } - const result = next.apply(this, [err]); - finishSpan(); - return result; - }; - } - - public _startSpan(routeName: string, middleWare: HandleFunction): Span { - const connectType = routeName ? ConnectTypes.REQUEST_HANDLER : ConnectTypes.MIDDLEWARE; - const connectName = routeName || middleWare.name || ANONYMOUS_NAME; - return startInactiveSpan({ - name: connectName, - op: `${connectType}.connect`, - attributes: { - [SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: 'auto.http.otel.connect', - [HTTP_ROUTE]: routeName.length > 0 ? routeName : '/', - [AttributeNames.CONNECT_TYPE]: connectType, - [AttributeNames.CONNECT_NAME]: connectName, - }, - }); - } - - public _patchMiddleware(routeName: string, middleWare: HandleFunction): HandleFunction { - const isEnabled = this.isEnabled.bind(this); - const startSpan: (routeName: string, middleWare: HandleFunction) => Span = this._startSpan.bind(this); - const patchNext = this._patchNext.bind(this); - const isErrorMiddleware = middleWare.length === 4; - - function patchedMiddleware(this: Use): void { - if (!isEnabled()) { - return Reflect.apply(middleWare, this, arguments); - } - const [reqArgIdx, resArgIdx, nextArgIdx] = isErrorMiddleware ? [1, 2, 3] : [0, 1, 2]; - const req = arguments[reqArgIdx] as PatchedRequest; - const res = arguments[resArgIdx] as ServerResponse; - const next = arguments[nextArgIdx] as NextFunction; - - replaceCurrentStackRoute(req, routeName); - - if (routeName) { - setHttpServerSpanRouteAttribute(generateRoute(req)); - } - - const span = startSpan(routeName, middleWare); - let spanFinished = false; - - function finishSpan() { - if (!spanFinished) { - spanFinished = true; - span.end(); - } - res.removeListener('close', finishSpan); - } - - res.addListener('close', finishSpan); - arguments[nextArgIdx] = patchNext(next, span, finishSpan); - - try { - return Reflect.apply(middleWare, this, arguments); - } catch (e) { - span.setStatus({ code: SPAN_STATUS_ERROR, message: 'internal_error' }); - finishSpan(); - throw e; - } - } - - Object.defineProperty(patchedMiddleware, 'length', { - value: middleWare.length, - writable: false, - configurable: true, - }); - - return patchedMiddleware; - } - - public _patchUse(original: Server['use']): Use { - const patchMiddleware = this._patchMiddleware.bind(this); - return function (this: Server, ...args: UseArgs): Server { - const middleWare = args[args.length - 1] as HandleFunction; - const routeName = (args[args.length - 2] || '') as string; - - args[args.length - 1] = patchMiddleware(routeName, middleWare); - - return original.apply(this, args as UseArgs2); - }; - } - - public _patchHandle(original: Server['handle']): Server['handle'] { - const patchOut = this._patchOut.bind(this); - return function (this: Server): ReturnType { - const [reqIdx, outIdx] = [0, 2]; - const req = arguments[reqIdx] as PatchedRequest; - const out = arguments[outIdx]; - const completeStack = addNewStackLayer(req); - - if (typeof out === 'function') { - arguments[outIdx] = patchOut(out as NextFunction, completeStack); - } - - return Reflect.apply(original, this, arguments); - }; - } - - public _patchOut(out: NextFunction, completeStack: () => void): NextFunction { - return function nextFunction(this: NextFunction, ...args: unknown[]): void { - completeStack(); - return Reflect.apply(out, this, args); - }; - } -} diff --git a/packages/node/src/integrations/tracing/connect/vendored/internal-types.ts b/packages/node/src/integrations/tracing/connect/vendored/internal-types.ts deleted file mode 100644 index 59e6569f06de..000000000000 --- a/packages/node/src/integrations/tracing/connect/vendored/internal-types.ts +++ /dev/null @@ -1,53 +0,0 @@ -/* - * Copyright The OpenTelemetry Authors - * SPDX-License-Identifier: Apache-2.0 - * - * NOTICE from the Sentry authors: - * - Vendored from: https://github.com/open-telemetry/opentelemetry-js-contrib/tree/15ef7506553f631ea4181391e0c5725a56f0d082/packages/instrumentation-connect - * - Upstream version: @opentelemetry/instrumentation-connect@0.61.0 - * - Some types vendored from @types/connect - */ - -import type * as http from 'http'; - -export type IncomingMessage = http.IncomingMessage & { - originalUrl?: http.IncomingMessage['url'] | undefined; -}; - -export type NextFunction = (err?: unknown) => void; - -export type SimpleHandleFunction = (req: IncomingMessage, res: http.ServerResponse) => void; -export type NextHandleFunction = (req: IncomingMessage, res: http.ServerResponse, next: NextFunction) => void; -export type ErrorHandleFunction = ( - err: unknown, - req: IncomingMessage, - res: http.ServerResponse, - next: NextFunction, -) => void; -export type HandleFunction = SimpleHandleFunction | NextHandleFunction | ErrorHandleFunction; - -export interface Server extends NodeJS.EventEmitter { - (req: http.IncomingMessage, res: http.ServerResponse, next?: Function): void; - - route: string; - stack: Array<{ route: string; handle: HandleFunction | http.Server }>; - - use(fn: NextHandleFunction): Server; - use(fn: HandleFunction): Server; - use(route: string, fn: NextHandleFunction): Server; - use(route: string, fn: HandleFunction): Server; - - handle(req: http.IncomingMessage, res: http.ServerResponse, next: Function): void; -} - -export const _LAYERS_STORE_PROPERTY: unique symbol = Symbol( - 'opentelemetry.instrumentation-connect.request-route-stack', -); - -export type UseArgs1 = [HandleFunction]; -export type UseArgs2 = [string, HandleFunction]; -export type UseArgs = UseArgs1 | UseArgs2; -export type Use = (...args: UseArgs) => Server; -export type PatchedRequest = { - [_LAYERS_STORE_PROPERTY]: string[]; -} & IncomingMessage; diff --git a/packages/node/src/integrations/tracing/connect/vendored/utils.ts b/packages/node/src/integrations/tracing/connect/vendored/utils.ts deleted file mode 100644 index 4f201a95f33e..000000000000 --- a/packages/node/src/integrations/tracing/connect/vendored/utils.ts +++ /dev/null @@ -1,46 +0,0 @@ -/* - * Copyright The OpenTelemetry Authors - * SPDX-License-Identifier: Apache-2.0 - * - * NOTICE from the Sentry authors: - * - Vendored from: https://github.com/open-telemetry/opentelemetry-js-contrib/tree/15ef7506553f631ea4181391e0c5725a56f0d082/packages/instrumentation-connect - * - Upstream version: @opentelemetry/instrumentation-connect@0.61.0 - */ - -import { debug } from '@sentry/core'; -import type { PatchedRequest } from './internal-types'; -import { _LAYERS_STORE_PROPERTY } from './internal-types'; -import { DEBUG_BUILD } from '../../../../debug-build'; - -export const addNewStackLayer = (request: PatchedRequest) => { - if (Array.isArray(request[_LAYERS_STORE_PROPERTY]) === false) { - Object.defineProperty(request, _LAYERS_STORE_PROPERTY, { - enumerable: false, - value: [], - }); - } - request[_LAYERS_STORE_PROPERTY].push('/'); - - const stackLength = request[_LAYERS_STORE_PROPERTY].length; - - return () => { - if (stackLength === request[_LAYERS_STORE_PROPERTY].length) { - request[_LAYERS_STORE_PROPERTY].pop(); - } else { - DEBUG_BUILD && debug.warn('Connect: Trying to pop the stack multiple time'); - } - }; -}; - -export const replaceCurrentStackRoute = (request: PatchedRequest, newRoute?: string) => { - if (newRoute) { - request[_LAYERS_STORE_PROPERTY].splice(-1, 1, newRoute); - } -}; - -// generate route from existing stack on request object. -// splash between stack layer will be deduped -// ["/first/", "/second", "/third/"] => /first/second/third/ -export const generateRoute = (request: PatchedRequest) => { - return request[_LAYERS_STORE_PROPERTY].reduce((acc, sub) => acc.replace(/\/+$/, '') + sub); -}; diff --git a/packages/node/src/integrations/tracing/index.ts b/packages/node/src/integrations/tracing/index.ts index 32857f2049ee..0706f89dffdd 100644 --- a/packages/node/src/integrations/tracing/index.ts +++ b/packages/node/src/integrations/tracing/index.ts @@ -3,7 +3,6 @@ import { prismaIntegration } from '@sentry/server-utils'; import { instrumentSentryHttp } from '../http'; import { amqplibIntegration, instrumentAmqplib } from './amqplib'; import { anthropicAIIntegration, instrumentAnthropicAi } from './anthropic-ai'; -import { connectIntegration, instrumentConnect } from './connect'; import { expressIntegration, instrumentExpress } from './express'; import { fastifyIntegration, instrumentFastifyV3 } from './fastify'; import { firebaseIntegration, instrumentFirebase } from './firebase'; @@ -47,7 +46,6 @@ export function getAutoPerformanceIntegrations(): Integration[] { prismaIntegration(), hapiIntegration(), koaIntegration(), - connectIntegration(), tediousIntegration(), genericPoolIntegration(), kafkaIntegration(), @@ -74,7 +72,6 @@ export function getOpenTelemetryInstrumentationToPreload(): (((options?: any) => return [ instrumentSentryHttp, instrumentExpress, - instrumentConnect, instrumentFastifyV3, instrumentHapi, instrumentHono, diff --git a/packages/node/test/integrations/tracing/connect.test.ts b/packages/node/test/integrations/tracing/connect.test.ts deleted file mode 100644 index 2f718e77a4b5..000000000000 --- a/packages/node/test/integrations/tracing/connect.test.ts +++ /dev/null @@ -1,87 +0,0 @@ -/* - * Tests ported from @opentelemetry/instrumentation-connect@0.61.0 - * Original source: https://github.com/open-telemetry/opentelemetry-js-contrib/tree/main/packages/instrumentation-connect - * Licensed under the Apache License, Version 2.0 - */ - -import { describe, expect, it } from 'vitest'; -import type { PatchedRequest } from '../../../src/integrations/tracing/connect/vendored/internal-types'; -import { _LAYERS_STORE_PROPERTY } from '../../../src/integrations/tracing/connect/vendored/internal-types'; -import { - addNewStackLayer, - generateRoute, - replaceCurrentStackRoute, -} from '../../../src/integrations/tracing/connect/vendored/utils'; - -describe('utils', () => { - describe('addNewStackLayer', () => { - it('should inject new array to symbol property if not exist', () => { - const fakeRequest = {} as PatchedRequest; - - addNewStackLayer(fakeRequest); - - expect(fakeRequest[_LAYERS_STORE_PROPERTY].length).toBe(1); - }); - - it('should append new stack item if private symbol already exists', () => { - const stack = ['/first']; - const fakeRequest = { - [_LAYERS_STORE_PROPERTY]: stack, - } as PatchedRequest; - - addNewStackLayer(fakeRequest); - - expect(fakeRequest[_LAYERS_STORE_PROPERTY]).toBe(stack); - expect(fakeRequest[_LAYERS_STORE_PROPERTY].length).toBe(2); - }); - - it('should return pop method to remove newly add stack', () => { - const fakeRequest = {} as PatchedRequest; - - const pop = addNewStackLayer(fakeRequest); - - expect(pop).toBeDefined(); - - pop(); - - expect(fakeRequest[_LAYERS_STORE_PROPERTY].length).toBe(0); - }); - - it('should prevent pop the same stack item multiple time', () => { - const fakeRequest = {} as PatchedRequest; - - addNewStackLayer(fakeRequest); // add first stack item - const pop = addNewStackLayer(fakeRequest); // add second stack item - - pop(); - pop(); - - expect(fakeRequest[_LAYERS_STORE_PROPERTY].length).toBe(1); - }); - }); - - describe('replaceCurrentStackRoute', () => { - it('should replace the last stack item with new value', () => { - const fakeRequest = { - [_LAYERS_STORE_PROPERTY]: ['/first', '/second'], - } as PatchedRequest; - - replaceCurrentStackRoute(fakeRequest, '/new_route'); - - expect(fakeRequest[_LAYERS_STORE_PROPERTY].length).toBe(2); - expect(fakeRequest[_LAYERS_STORE_PROPERTY][1]).toBe('/new_route'); - }); - }); - - describe('generateRoute', () => { - it('should combine the stack and striped any slash between layer', () => { - const fakeRequest = { - [_LAYERS_STORE_PROPERTY]: ['/first/', '/second', '/third/'], - } as PatchedRequest; - - const route = generateRoute(fakeRequest); - - expect(route).toBe('/first/second/third/'); - }); - }); -}); diff --git a/packages/remix/src/server/index.ts b/packages/remix/src/server/index.ts index 3ce2aa4a2caf..eaca7585db42 100644 --- a/packages/remix/src/server/index.ts +++ b/packages/remix/src/server/index.ts @@ -20,7 +20,6 @@ export { captureMessage, captureSession, close, - connectIntegration, consoleIntegration, contextLinesIntegration, continueTrace, @@ -99,7 +98,6 @@ export { setTags, setAttribute, setAttributes, - setupConnectErrorHandler, setupExpressErrorHandler, setupHapiErrorHandler, setupKoaErrorHandler, diff --git a/packages/solidstart/src/server/index.ts b/packages/solidstart/src/server/index.ts index fc90450c2ee2..fd5fbb2e0e39 100644 --- a/packages/solidstart/src/server/index.ts +++ b/packages/solidstart/src/server/index.ts @@ -23,7 +23,6 @@ export { captureMessage, captureSession, close, - connectIntegration, consoleIntegration, contextLinesIntegration, continueTrace, @@ -103,7 +102,6 @@ export { setTags, setAttribute, setAttributes, - setupConnectErrorHandler, setupExpressErrorHandler, setupHapiErrorHandler, setupKoaErrorHandler, diff --git a/packages/sveltekit/src/server/index.ts b/packages/sveltekit/src/server/index.ts index bfc3714a621b..2c712e9f7a4a 100644 --- a/packages/sveltekit/src/server/index.ts +++ b/packages/sveltekit/src/server/index.ts @@ -21,7 +21,6 @@ export { captureMessage, captureSession, close, - connectIntegration, consoleIntegration, contextLinesIntegration, continueTrace, @@ -100,7 +99,6 @@ export { setTags, setAttribute, setAttributes, - setupConnectErrorHandler, setupExpressErrorHandler, setupHapiErrorHandler, setupKoaErrorHandler, From a0a41519971b5d5ef4f85863f2e87b2f752a82ce Mon Sep 17 00:00:00 2001 From: Martin Sonnberger Date: Tue, 21 Jul 2026 15:30:19 +0200 Subject: [PATCH 2/2] lockfile --- yarn.lock | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/yarn.lock b/yarn.lock index 26aa37ea78bc..c11c22586a4f 100644 --- a/yarn.lock +++ b/yarn.lock @@ -13306,7 +13306,7 @@ connect-history-api-fallback@^2.0.0: resolved "https://registry.yarnpkg.com/connect-history-api-fallback/-/connect-history-api-fallback-2.0.0.tgz#647264845251a0daf25b97ce87834cace0f5f1c8" integrity sha512-U73+6lQFmfiNPrYbXqr6kZ1i1wiRqXnp2nhMsINseWXO8lDau0LGEffJ8kQi4EjLZympVgRdvqjAgiZ1tgzDDA== -connect@^3.6.6, connect@^3.7.0: +connect@^3.6.6: version "3.7.0" resolved "https://registry.yarnpkg.com/connect/-/connect-3.7.0.tgz#5d49348910caa5e07a01800b030d0c35f20484f8" integrity sha512-ZqRXc+tZukToSNmh5C2iWMSoV3X1YUcPbqEM4DkEG5tNQXrQUZCNVGGv3IuicnkMtPfGf3Xtp8WCXs295iQ1pQ==