From 66aff2ee85eaab59aa74a902e07b322e83179b15 Mon Sep 17 00:00:00 2001 From: Nicolas Hrubec Date: Tue, 9 Jun 2026 15:39:41 +0200 Subject: [PATCH 1/9] ref(node): Streamline connect Co-Authored-By: Claude Opus 4.8 (1M context) --- .../connect/vendored/enums/AttributeNames.ts | 1 - .../connect/vendored/instrumentation.ts | 37 +++----- .../connect/vendored/internal-types.ts | 1 - .../tracing/connect/vendored/utils.ts | 4 +- .../test/integrations/tracing/connect.test.ts | 87 +++++++++++++++++++ 5 files changed, 103 insertions(+), 27 deletions(-) create mode 100644 packages/node/test/integrations/tracing/connect.test.ts diff --git a/packages/node/src/integrations/tracing/connect/vendored/enums/AttributeNames.ts b/packages/node/src/integrations/tracing/connect/vendored/enums/AttributeNames.ts index 49a7d014d714..7ad986df3608 100644 --- a/packages/node/src/integrations/tracing/connect/vendored/enums/AttributeNames.ts +++ b/packages/node/src/integrations/tracing/connect/vendored/enums/AttributeNames.ts @@ -17,7 +17,6 @@ * - Vendored from: https://github.com/open-telemetry/opentelemetry-js-contrib/tree/15ef7506553f631ea4181391e0c5725a56f0d082/packages/instrumentation-connect * - Upstream version: @opentelemetry/instrumentation-connect@0.61.0 */ -/* eslint-disable */ export enum AttributeNames { CONNECT_TYPE = 'connect.type', diff --git a/packages/node/src/integrations/tracing/connect/vendored/instrumentation.ts b/packages/node/src/integrations/tracing/connect/vendored/instrumentation.ts index 911b6753c92e..9e0ec4f89986 100644 --- a/packages/node/src/integrations/tracing/connect/vendored/instrumentation.ts +++ b/packages/node/src/integrations/tracing/connect/vendored/instrumentation.ts @@ -18,26 +18,21 @@ * - Upstream version: @opentelemetry/instrumentation-connect@0.61.0 * - Minor TypeScript strictness adjustments for this repository's compiler settings */ -/* eslint-disable */ -import { context, Span, SpanOptions } from '@opentelemetry/api'; +import type { Span, SpanOptions } from '@opentelemetry/api'; import type { ServerResponse } from 'http'; import { AttributeNames, ConnectNames, ConnectTypes } from './enums/AttributeNames'; -import { HandleFunction, NextFunction, Server, PatchedRequest, Use, UseArgs, UseArgs2 } from './internal-types'; +import type { HandleFunction, NextFunction, PatchedRequest, Server, Use, UseArgs, UseArgs2 } from './internal-types'; import { SDK_VERSION } from '@sentry/core'; import { setHttpServerSpanRouteAttribute } from '../../../../utils/setHttpServerSpanRouteAttribute'; -import { - InstrumentationBase, - InstrumentationConfig, - InstrumentationNodeModuleDefinition, - isWrapped, -} from '@opentelemetry/instrumentation'; +import type { InstrumentationConfig } from '@opentelemetry/instrumentation'; +import { InstrumentationBase, InstrumentationNodeModuleDefinition, isWrapped } from '@opentelemetry/instrumentation'; import { ATTR_HTTP_ROUTE } from '@opentelemetry/semantic-conventions'; import { replaceCurrentStackRoute, addNewStackLayer, generateRoute } from './utils'; const PACKAGE_NAME = '@sentry/instrumentation-connect'; -export const ANONYMOUS_NAME = 'anonymous'; +const ANONYMOUS_NAME = 'anonymous'; /** Connect instrumentation for OpenTelemetry */ export class ConnectInstrumentation extends InstrumentationBase { @@ -54,15 +49,18 @@ export class ConnectInstrumentation extends InstrumentationBase { } private _patchApp(patchedApp: Server) { + // eslint-disable-next-line @typescript-eslint/unbound-method -- only checking the wrapped flag, not calling it if (!isWrapped(patchedApp.use)) { this._wrap(patchedApp, 'use', this._patchUse.bind(this)); } + // eslint-disable-next-line @typescript-eslint/unbound-method -- only checking the wrapped flag, not calling it if (!isWrapped(patchedApp.handle)) { this._wrap(patchedApp, 'handle', this._patchHandle.bind(this)); } } private _patchConstructor(original: () => Server): () => Server { + // eslint-disable-next-line @typescript-eslint/no-this-alias const instrumentation = this; return function (this: Server, ...args: any[]) { const app = original.apply(this, args) as Server; @@ -105,12 +103,13 @@ export class ConnectInstrumentation extends InstrumentationBase { } public _patchMiddleware(routeName: string, middleWare: HandleFunction): HandleFunction { + // eslint-disable-next-line @typescript-eslint/no-this-alias const instrumentation = this; const isErrorMiddleware = middleWare.length === 4; function patchedMiddleware(this: Use): void { if (!instrumentation.isEnabled()) { - return (middleWare as any).apply(this, arguments); + return Reflect.apply(middleWare, this, arguments); } const [reqArgIdx, resArgIdx, nextArgIdx] = isErrorMiddleware ? [1, 2, 3] : [0, 1, 2]; const req = arguments[reqArgIdx] as PatchedRequest; @@ -123,23 +122,13 @@ export class ConnectInstrumentation extends InstrumentationBase { setHttpServerSpanRouteAttribute(generateRoute(req)); } - let spanName = ''; - if (routeName) { - spanName = `request handler - ${routeName}`; - } else { - spanName = `middleware - ${middleWare.name || ANONYMOUS_NAME}`; - } const span = instrumentation._startSpan(routeName, middleWare); - instrumentation._diag.debug('start span', spanName); let spanFinished = false; function finishSpan() { if (!spanFinished) { spanFinished = true; - instrumentation._diag.debug(`finishing span ${(span as any).name}`); span.end(); - } else { - instrumentation._diag.debug(`span ${(span as any).name} - already finished`); } res.removeListener('close', finishSpan); } @@ -147,7 +136,7 @@ export class ConnectInstrumentation extends InstrumentationBase { res.addListener('close', finishSpan); arguments[nextArgIdx] = instrumentation._patchNext(next, finishSpan); - return (middleWare as any).apply(this, arguments); + return Reflect.apply(middleWare, this, arguments); } Object.defineProperty(patchedMiddleware, 'length', { @@ -160,6 +149,7 @@ export class ConnectInstrumentation extends InstrumentationBase { } public _patchUse(original: Server['use']): Use { + // eslint-disable-next-line @typescript-eslint/no-this-alias const instrumentation = this; return function (this: Server, ...args: UseArgs): Server { const middleWare = args[args.length - 1] as HandleFunction; @@ -172,6 +162,7 @@ export class ConnectInstrumentation extends InstrumentationBase { } public _patchHandle(original: Server['handle']): Server['handle'] { + // eslint-disable-next-line @typescript-eslint/no-this-alias const instrumentation = this; return function (this: Server): ReturnType { const [reqIdx, outIdx] = [0, 2]; @@ -183,7 +174,7 @@ export class ConnectInstrumentation extends InstrumentationBase { arguments[outIdx] = instrumentation._patchOut(out as NextFunction, completeStack); } - return (original as any).apply(this, arguments); + return Reflect.apply(original, this, arguments); }; } diff --git a/packages/node/src/integrations/tracing/connect/vendored/internal-types.ts b/packages/node/src/integrations/tracing/connect/vendored/internal-types.ts index 981e1d9aa0e4..7e7aa40ff37a 100644 --- a/packages/node/src/integrations/tracing/connect/vendored/internal-types.ts +++ b/packages/node/src/integrations/tracing/connect/vendored/internal-types.ts @@ -18,7 +18,6 @@ * - Upstream version: @opentelemetry/instrumentation-connect@0.61.0 * - Some types vendored from @types/connect */ -/* eslint-disable */ import type * as http from 'http'; diff --git a/packages/node/src/integrations/tracing/connect/vendored/utils.ts b/packages/node/src/integrations/tracing/connect/vendored/utils.ts index e4c7575178f9..e4bb26025388 100644 --- a/packages/node/src/integrations/tracing/connect/vendored/utils.ts +++ b/packages/node/src/integrations/tracing/connect/vendored/utils.ts @@ -17,10 +17,10 @@ * - Vendored from: https://github.com/open-telemetry/opentelemetry-js-contrib/tree/15ef7506553f631ea4181391e0c5725a56f0d082/packages/instrumentation-connect * - Upstream version: @opentelemetry/instrumentation-connect@0.61.0 */ -/* eslint-disable */ import { diag } from '@opentelemetry/api'; -import { _LAYERS_STORE_PROPERTY, PatchedRequest } from './internal-types'; +import type { PatchedRequest } from './internal-types'; +import { _LAYERS_STORE_PROPERTY } from './internal-types'; export const addNewStackLayer = (request: PatchedRequest) => { if (Array.isArray(request[_LAYERS_STORE_PROPERTY]) === false) { diff --git a/packages/node/test/integrations/tracing/connect.test.ts b/packages/node/test/integrations/tracing/connect.test.ts new file mode 100644 index 000000000000..2f718e77a4b5 --- /dev/null +++ b/packages/node/test/integrations/tracing/connect.test.ts @@ -0,0 +1,87 @@ +/* + * 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/'); + }); + }); +}); From 12aad861c81a3b9092a4ddb64d738af7ffdf9844 Mon Sep 17 00:00:00 2001 From: Nicolas Hrubec Date: Tue, 9 Jun 2026 15:58:06 +0200 Subject: [PATCH 2/9] test(node): Add connect instrumentation unit tests; fix no-this-alias Move the ported utils tests into a connect/ folder and add instrumentation.test.ts (fake-driven span-creation cases). Replace the no-this-alias suppressions with bound-method captures; only the two unbound-method false-positives remain disabled. Co-Authored-By: Claude Opus 4.8 (1M context) --- .../connect/vendored/instrumentation.ts | 26 +++--- .../tracing/connect/instrumentation.test.ts | 93 +++++++++++++++++++ .../utils.test.ts} | 6 +- 3 files changed, 108 insertions(+), 17 deletions(-) create mode 100644 packages/node/test/integrations/tracing/connect/instrumentation.test.ts rename packages/node/test/integrations/tracing/{connect.test.ts => connect/utils.test.ts} (91%) diff --git a/packages/node/src/integrations/tracing/connect/vendored/instrumentation.ts b/packages/node/src/integrations/tracing/connect/vendored/instrumentation.ts index 9e0ec4f89986..f14c0830f16e 100644 --- a/packages/node/src/integrations/tracing/connect/vendored/instrumentation.ts +++ b/packages/node/src/integrations/tracing/connect/vendored/instrumentation.ts @@ -60,11 +60,10 @@ export class ConnectInstrumentation extends InstrumentationBase { } private _patchConstructor(original: () => Server): () => Server { - // eslint-disable-next-line @typescript-eslint/no-this-alias - const instrumentation = this; + const patchApp = this._patchApp.bind(this); return function (this: Server, ...args: any[]) { const app = original.apply(this, args) as Server; - instrumentation._patchApp(app); + patchApp(app); return app; }; } @@ -103,12 +102,13 @@ export class ConnectInstrumentation extends InstrumentationBase { } public _patchMiddleware(routeName: string, middleWare: HandleFunction): HandleFunction { - // eslint-disable-next-line @typescript-eslint/no-this-alias - const instrumentation = this; + 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 (!instrumentation.isEnabled()) { + if (!isEnabled()) { return Reflect.apply(middleWare, this, arguments); } const [reqArgIdx, resArgIdx, nextArgIdx] = isErrorMiddleware ? [1, 2, 3] : [0, 1, 2]; @@ -122,7 +122,7 @@ export class ConnectInstrumentation extends InstrumentationBase { setHttpServerSpanRouteAttribute(generateRoute(req)); } - const span = instrumentation._startSpan(routeName, middleWare); + const span = startSpan(routeName, middleWare); let spanFinished = false; function finishSpan() { @@ -134,7 +134,7 @@ export class ConnectInstrumentation extends InstrumentationBase { } res.addListener('close', finishSpan); - arguments[nextArgIdx] = instrumentation._patchNext(next, finishSpan); + arguments[nextArgIdx] = patchNext(next, finishSpan); return Reflect.apply(middleWare, this, arguments); } @@ -149,21 +149,19 @@ export class ConnectInstrumentation extends InstrumentationBase { } public _patchUse(original: Server['use']): Use { - // eslint-disable-next-line @typescript-eslint/no-this-alias - const instrumentation = this; + 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] = instrumentation._patchMiddleware(routeName, middleWare); + args[args.length - 1] = patchMiddleware(routeName, middleWare); return original.apply(this, args as UseArgs2); }; } public _patchHandle(original: Server['handle']): Server['handle'] { - // eslint-disable-next-line @typescript-eslint/no-this-alias - const instrumentation = this; + const patchOut = this._patchOut.bind(this); return function (this: Server): ReturnType { const [reqIdx, outIdx] = [0, 2]; const req = arguments[reqIdx] as PatchedRequest; @@ -171,7 +169,7 @@ export class ConnectInstrumentation extends InstrumentationBase { const completeStack = addNewStackLayer(req); if (typeof out === 'function') { - arguments[outIdx] = instrumentation._patchOut(out as NextFunction, completeStack); + arguments[outIdx] = patchOut(out as NextFunction, completeStack); } return Reflect.apply(original, this, arguments); diff --git a/packages/node/test/integrations/tracing/connect/instrumentation.test.ts b/packages/node/test/integrations/tracing/connect/instrumentation.test.ts new file mode 100644 index 000000000000..7d32093be949 --- /dev/null +++ b/packages/node/test/integrations/tracing/connect/instrumentation.test.ts @@ -0,0 +1,93 @@ +/* + * 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 { EventEmitter } from 'node:events'; +import type { ServerResponse } from 'node:http'; +import { ATTR_HTTP_ROUTE } from '@opentelemetry/semantic-conventions'; +import { BasicTracerProvider, InMemorySpanExporter, SimpleSpanProcessor } from '@opentelemetry/sdk-trace-base'; +import { afterEach, beforeEach, describe, expect, it } from 'vitest'; +import { ConnectInstrumentation } from '../../../../src/integrations/tracing/connect/vendored/instrumentation'; +import type { + NextHandleFunction, + PatchedRequest, +} from '../../../../src/integrations/tracing/connect/vendored/internal-types'; +import { addNewStackLayer } from '../../../../src/integrations/tracing/connect/vendored/utils'; + +describe('ConnectInstrumentation', () => { + const memoryExporter = new InMemorySpanExporter(); + const provider = new BasicTracerProvider({ spanProcessors: [new SimpleSpanProcessor(memoryExporter)] }); + + let instrumentation: ConnectInstrumentation; + + beforeEach(() => { + instrumentation = new ConnectInstrumentation(); + instrumentation.setTracerProvider(provider); + memoryExporter.reset(); + }); + + afterEach(() => { + instrumentation.disable(); + }); + + // Drive `_patchMiddleware` directly with a fake request/response/next (no real `connect` dependency), + // mirroring how connect invokes a middleware. Calling `next()` ends the span. + function runMiddleware(routeName: string, middleware: NextHandleFunction): void { + const patched = instrumentation._patchMiddleware(routeName, middleware) as NextHandleFunction; + const req = {} as PatchedRequest; + addNewStackLayer(req); + const res = new EventEmitter() as unknown as ServerResponse; + patched(req, res, () => {}); + } + + it('should not generate any spans when disabled', () => { + instrumentation.disable(); + + runMiddleware('', (_req, _res, next) => next()); + + expect(memoryExporter.getFinishedSpans()).toHaveLength(0); + }); + + it('should generate span for anonymous middleware', () => { + runMiddleware('', (_req, _res, next) => next()); + + const spans = memoryExporter.getFinishedSpans(); + expect(spans).toHaveLength(1); + expect(spans[0]!.name).toBe('middleware - anonymous'); + expect(spans[0]!.attributes).toEqual({ + 'connect.type': 'middleware', + 'connect.name': 'anonymous', + [ATTR_HTTP_ROUTE]: '/', + }); + }); + + it('should generate span for named middleware', () => { + runMiddleware('', function middleware1(_req, _res, next) { + next(); + }); + + const spans = memoryExporter.getFinishedSpans(); + expect(spans).toHaveLength(1); + expect(spans[0]!.name).toBe('middleware - middleware1'); + expect(spans[0]!.attributes).toEqual({ + 'connect.type': 'middleware', + 'connect.name': 'middleware1', + [ATTR_HTTP_ROUTE]: '/', + }); + }); + + it('should generate span for route', () => { + runMiddleware('/foo', (_req, _res, next) => next()); + + const spans = memoryExporter.getFinishedSpans(); + expect(spans).toHaveLength(1); + expect(spans[0]!.name).toBe('request handler - /foo'); + expect(spans[0]!.attributes).toEqual({ + 'connect.type': 'request_handler', + 'connect.name': '/foo', + [ATTR_HTTP_ROUTE]: '/foo', + }); + }); +}); diff --git a/packages/node/test/integrations/tracing/connect.test.ts b/packages/node/test/integrations/tracing/connect/utils.test.ts similarity index 91% rename from packages/node/test/integrations/tracing/connect.test.ts rename to packages/node/test/integrations/tracing/connect/utils.test.ts index 2f718e77a4b5..380595a801fa 100644 --- a/packages/node/test/integrations/tracing/connect.test.ts +++ b/packages/node/test/integrations/tracing/connect/utils.test.ts @@ -5,13 +5,13 @@ */ 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 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'; +} from '../../../../src/integrations/tracing/connect/vendored/utils'; describe('utils', () => { describe('addNewStackLayer', () => { From fa9668bbe9dea70c83c277a065a189064ab07d63 Mon Sep 17 00:00:00 2001 From: Nicolas Hrubec Date: Wed, 10 Jun 2026 15:25:03 +0200 Subject: [PATCH 3/9] test(node): Move connect unit tests to integration coverage Remove the fake-driven instrumentation test; the integration suite now covers named + anonymous middleware spans with the real package. Keep the pure-function utils test, relocated to connect.test.ts. Co-Authored-By: Claude Opus 4.8 (1M context) --- .../suites/tracing/connect/scenario.mjs | 7 ++ .../suites/tracing/connect/test.ts | 26 ++++++ .../utils.test.ts => connect.test.ts} | 6 +- .../tracing/connect/instrumentation.test.ts | 93 ------------------- 4 files changed, 36 insertions(+), 96 deletions(-) rename packages/node/test/integrations/tracing/{connect/utils.test.ts => connect.test.ts} (91%) delete mode 100644 packages/node/test/integrations/tracing/connect/instrumentation.test.ts diff --git a/dev-packages/node-integration-tests/suites/tracing/connect/scenario.mjs b/dev-packages/node-integration-tests/suites/tracing/connect/scenario.mjs index 39054ba009c5..4ecabbebd0b1 100644 --- a/dev-packages/node-integration-tests/suites/tracing/connect/scenario.mjs +++ b/dev-packages/node-integration-tests/suites/tracing/connect/scenario.mjs @@ -8,6 +8,13 @@ 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(); diff --git a/dev-packages/node-integration-tests/suites/tracing/connect/test.ts b/dev-packages/node-integration-tests/suites/tracing/connect/test.ts index 0c37c58f8a4c..10259b5fe55a 100644 --- a/dev-packages/node-integration-tests/suites/tracing/connect/test.ts +++ b/dev-packages/node-integration-tests/suites/tracing/connect/test.ts @@ -22,6 +22,32 @@ describe('connect auto-instrumentation', () => { 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', + }), ]), }; diff --git a/packages/node/test/integrations/tracing/connect/utils.test.ts b/packages/node/test/integrations/tracing/connect.test.ts similarity index 91% rename from packages/node/test/integrations/tracing/connect/utils.test.ts rename to packages/node/test/integrations/tracing/connect.test.ts index 380595a801fa..2f718e77a4b5 100644 --- a/packages/node/test/integrations/tracing/connect/utils.test.ts +++ b/packages/node/test/integrations/tracing/connect.test.ts @@ -5,13 +5,13 @@ */ 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 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'; +} from '../../../src/integrations/tracing/connect/vendored/utils'; describe('utils', () => { describe('addNewStackLayer', () => { diff --git a/packages/node/test/integrations/tracing/connect/instrumentation.test.ts b/packages/node/test/integrations/tracing/connect/instrumentation.test.ts deleted file mode 100644 index 7d32093be949..000000000000 --- a/packages/node/test/integrations/tracing/connect/instrumentation.test.ts +++ /dev/null @@ -1,93 +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 { EventEmitter } from 'node:events'; -import type { ServerResponse } from 'node:http'; -import { ATTR_HTTP_ROUTE } from '@opentelemetry/semantic-conventions'; -import { BasicTracerProvider, InMemorySpanExporter, SimpleSpanProcessor } from '@opentelemetry/sdk-trace-base'; -import { afterEach, beforeEach, describe, expect, it } from 'vitest'; -import { ConnectInstrumentation } from '../../../../src/integrations/tracing/connect/vendored/instrumentation'; -import type { - NextHandleFunction, - PatchedRequest, -} from '../../../../src/integrations/tracing/connect/vendored/internal-types'; -import { addNewStackLayer } from '../../../../src/integrations/tracing/connect/vendored/utils'; - -describe('ConnectInstrumentation', () => { - const memoryExporter = new InMemorySpanExporter(); - const provider = new BasicTracerProvider({ spanProcessors: [new SimpleSpanProcessor(memoryExporter)] }); - - let instrumentation: ConnectInstrumentation; - - beforeEach(() => { - instrumentation = new ConnectInstrumentation(); - instrumentation.setTracerProvider(provider); - memoryExporter.reset(); - }); - - afterEach(() => { - instrumentation.disable(); - }); - - // Drive `_patchMiddleware` directly with a fake request/response/next (no real `connect` dependency), - // mirroring how connect invokes a middleware. Calling `next()` ends the span. - function runMiddleware(routeName: string, middleware: NextHandleFunction): void { - const patched = instrumentation._patchMiddleware(routeName, middleware) as NextHandleFunction; - const req = {} as PatchedRequest; - addNewStackLayer(req); - const res = new EventEmitter() as unknown as ServerResponse; - patched(req, res, () => {}); - } - - it('should not generate any spans when disabled', () => { - instrumentation.disable(); - - runMiddleware('', (_req, _res, next) => next()); - - expect(memoryExporter.getFinishedSpans()).toHaveLength(0); - }); - - it('should generate span for anonymous middleware', () => { - runMiddleware('', (_req, _res, next) => next()); - - const spans = memoryExporter.getFinishedSpans(); - expect(spans).toHaveLength(1); - expect(spans[0]!.name).toBe('middleware - anonymous'); - expect(spans[0]!.attributes).toEqual({ - 'connect.type': 'middleware', - 'connect.name': 'anonymous', - [ATTR_HTTP_ROUTE]: '/', - }); - }); - - it('should generate span for named middleware', () => { - runMiddleware('', function middleware1(_req, _res, next) { - next(); - }); - - const spans = memoryExporter.getFinishedSpans(); - expect(spans).toHaveLength(1); - expect(spans[0]!.name).toBe('middleware - middleware1'); - expect(spans[0]!.attributes).toEqual({ - 'connect.type': 'middleware', - 'connect.name': 'middleware1', - [ATTR_HTTP_ROUTE]: '/', - }); - }); - - it('should generate span for route', () => { - runMiddleware('/foo', (_req, _res, next) => next()); - - const spans = memoryExporter.getFinishedSpans(); - expect(spans).toHaveLength(1); - expect(spans[0]!.name).toBe('request handler - /foo'); - expect(spans[0]!.attributes).toEqual({ - 'connect.type': 'request_handler', - 'connect.name': '/foo', - [ATTR_HTTP_ROUTE]: '/foo', - }); - }); -}); From 050d892f2983b5d638e9ba4eac94bdfc95a3c5d6 Mon Sep 17 00:00:00 2001 From: Nicolas Hrubec Date: Wed, 10 Jun 2026 15:36:18 +0200 Subject: [PATCH 4/9] ref(node): Drop no-explicit-any exemption for vendored connect Type the remaining `any`s as `unknown` (forwarding via `Reflect.apply`) and remove the connect entry from the oxlint exemption list. Co-Authored-By: Claude Opus 4.8 (1M context) --- .oxlintrc.base.json | 1 - .../tracing/connect/vendored/instrumentation.ts | 12 ++++++------ .../tracing/connect/vendored/internal-types.ts | 4 ++-- 3 files changed, 8 insertions(+), 9 deletions(-) diff --git a/.oxlintrc.base.json b/.oxlintrc.base.json index 6f530396796d..a049629accf6 100644 --- a/.oxlintrc.base.json +++ b/.oxlintrc.base.json @@ -148,7 +148,6 @@ "**/integrations/tracing/knex/vendored/**/*.ts", "**/integrations/tracing/mongo/vendored/**/*.ts", "**/integrations/tracing/koa/vendored/**/*.ts", - "**/integrations/tracing/connect/vendored/**/*.ts", "**/integrations/tracing/mysql2/vendored/**/*.ts", "**/integration/aws/vendored/**/*.ts", "**/nestjs/src/integrations/vendored/**/*.ts", diff --git a/packages/node/src/integrations/tracing/connect/vendored/instrumentation.ts b/packages/node/src/integrations/tracing/connect/vendored/instrumentation.ts index f14c0830f16e..4949df131839 100644 --- a/packages/node/src/integrations/tracing/connect/vendored/instrumentation.ts +++ b/packages/node/src/integrations/tracing/connect/vendored/instrumentation.ts @@ -49,11 +49,11 @@ export class ConnectInstrumentation extends InstrumentationBase { } private _patchApp(patchedApp: Server) { - // eslint-disable-next-line @typescript-eslint/unbound-method -- only checking the wrapped flag, not calling it + // eslint-disable-next-line @typescript-eslint/unbound-method if (!isWrapped(patchedApp.use)) { this._wrap(patchedApp, 'use', this._patchUse.bind(this)); } - // eslint-disable-next-line @typescript-eslint/unbound-method -- only checking the wrapped flag, not calling it + // eslint-disable-next-line @typescript-eslint/unbound-method if (!isWrapped(patchedApp.handle)) { this._wrap(patchedApp, 'handle', this._patchHandle.bind(this)); } @@ -61,15 +61,15 @@ export class ConnectInstrumentation extends InstrumentationBase { private _patchConstructor(original: () => Server): () => Server { const patchApp = this._patchApp.bind(this); - return function (this: Server, ...args: any[]) { - const app = original.apply(this, args) as Server; + return function (this: Server, ...args: unknown[]) { + const app = Reflect.apply(original, this, args) as Server; patchApp(app); return app; }; } public _patchNext(next: NextFunction, finishSpan: () => void): NextFunction { - return function nextFunction(this: NextFunction, err?: any): void { + return function nextFunction(this: NextFunction, err?: unknown): void { const result = next.apply(this, [err]); finishSpan(); return result; @@ -177,7 +177,7 @@ export class ConnectInstrumentation extends InstrumentationBase { } public _patchOut(out: NextFunction, completeStack: () => void): NextFunction { - return function nextFunction(this: NextFunction, ...args: any[]): void { + 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 index 7e7aa40ff37a..0087074459d9 100644 --- a/packages/node/src/integrations/tracing/connect/vendored/internal-types.ts +++ b/packages/node/src/integrations/tracing/connect/vendored/internal-types.ts @@ -25,12 +25,12 @@ export type IncomingMessage = http.IncomingMessage & { originalUrl?: http.IncomingMessage['url'] | undefined; }; -export type NextFunction = (err?: any) => void; +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: any, + err: unknown, req: IncomingMessage, res: http.ServerResponse, next: NextFunction, From c9e669bf8e27279f20e3a2186176ef3e16c1b715 Mon Sep 17 00:00:00 2001 From: Nicolas Hrubec Date: Wed, 10 Jun 2026 15:45:00 +0200 Subject: [PATCH 5/9] test(node): Assert connect error-middleware span Cover the `isErrorMiddleware` branch by asserting the `connectErrorMiddleware` span in the errored-transaction test. Co-Authored-By: Claude Opus 4.8 (1M context) --- .../suites/tracing/connect/test.ts | 19 ++++++++++++++++++- 1 file changed, 18 insertions(+), 1 deletion(-) diff --git a/dev-packages/node-integration-tests/suites/tracing/connect/test.ts b/dev-packages/node-integration-tests/suites/tracing/connect/test.ts index 10259b5fe55a..97d9d3375e2a 100644 --- a/dev-packages/node-integration-tests/suites/tracing/connect/test.ts +++ b/dev-packages/node-integration-tests/suites/tracing/connect/test.ts @@ -82,7 +82,24 @@ describe('connect auto-instrumentation', () => { test('should report errored transactions.', async () => { const runner = createTestRunner() .ignore('event') - .expect({ transaction: { transaction: 'GET /error' } }) + .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', + }), + ]), + }, + }) .start(); runner.makeRequest('get', '/error'); await runner.completed(); From 9a83a8dae894c05930ee8e9f76d49f4c86476569 Mon Sep 17 00:00:00 2001 From: Nicolas Hrubec Date: Mon, 15 Jun 2026 11:58:12 +0200 Subject: [PATCH 6/9] ref(node): Use Sentry span APIs in vendored connect instrumentation Replace the OTel tracer/context span lifecycle with `startInactiveSpan` from `@sentry/core`, and mark middleware spans as errored (throw or `next(err)`). Swap the remaining `diag.warn` for the Sentry debug logger. Co-Authored-By: Claude Opus 4.8 (1M context) --- .../suites/tracing/connect/test.ts | 6 +++++ .../connect/vendored/instrumentation.ts | 26 ++++++++++++------- .../tracing/connect/vendored/utils.ts | 5 ++-- 3 files changed, 26 insertions(+), 11 deletions(-) diff --git a/dev-packages/node-integration-tests/suites/tracing/connect/test.ts b/dev-packages/node-integration-tests/suites/tracing/connect/test.ts index 97d9d3375e2a..fa50ca028ab8 100644 --- a/dev-packages/node-integration-tests/suites/tracing/connect/test.ts +++ b/dev-packages/node-integration-tests/suites/tracing/connect/test.ts @@ -97,6 +97,12 @@ describe('connect auto-instrumentation', () => { origin: 'auto.http.otel.connect', op: 'middleware.connect', }), + + expect.objectContaining({ + description: '/error', + op: 'request_handler.connect', + status: 'internal_error', + }), ]), }, }) diff --git a/packages/node/src/integrations/tracing/connect/vendored/instrumentation.ts b/packages/node/src/integrations/tracing/connect/vendored/instrumentation.ts index 4949df131839..728dcdf20b46 100644 --- a/packages/node/src/integrations/tracing/connect/vendored/instrumentation.ts +++ b/packages/node/src/integrations/tracing/connect/vendored/instrumentation.ts @@ -19,11 +19,11 @@ * - Minor TypeScript strictness adjustments for this repository's compiler settings */ -import type { Span, SpanOptions } from '@opentelemetry/api'; import type { ServerResponse } from 'http'; import { AttributeNames, ConnectNames, ConnectTypes } from './enums/AttributeNames'; import type { HandleFunction, NextFunction, PatchedRequest, Server, Use, UseArgs, UseArgs2 } from './internal-types'; -import { SDK_VERSION } from '@sentry/core'; +import type { Span } from '@sentry/core'; +import { SDK_VERSION, 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'; @@ -68,8 +68,11 @@ export class ConnectInstrumentation extends InstrumentationBase { }; } - public _patchNext(next: NextFunction, finishSpan: () => void): NextFunction { + public _patchNext(next: NextFunction, span: Span, finishSpan: () => void): NextFunction { return function nextFunction(this: NextFunction, err?: unknown): void { + if (err) { + span.setStatus({ code: SPAN_STATUS_ERROR, message: 'internal_error' }); + } const result = next.apply(this, [err]); finishSpan(); return result; @@ -90,15 +93,14 @@ export class ConnectInstrumentation extends InstrumentationBase { connectName = middleWare.name || ANONYMOUS_NAME; } const spanName = `${connectTypeName} - ${connectName}`; - const options: SpanOptions = { + return startInactiveSpan({ + name: spanName, attributes: { [ATTR_HTTP_ROUTE]: routeName.length > 0 ? routeName : '/', [AttributeNames.CONNECT_TYPE]: connectType, [AttributeNames.CONNECT_NAME]: connectName, }, - }; - - return this.tracer.startSpan(spanName, options); + }); } public _patchMiddleware(routeName: string, middleWare: HandleFunction): HandleFunction { @@ -134,9 +136,15 @@ export class ConnectInstrumentation extends InstrumentationBase { } res.addListener('close', finishSpan); - arguments[nextArgIdx] = patchNext(next, finishSpan); + arguments[nextArgIdx] = patchNext(next, span, finishSpan); - return Reflect.apply(middleWare, this, arguments); + 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', { diff --git a/packages/node/src/integrations/tracing/connect/vendored/utils.ts b/packages/node/src/integrations/tracing/connect/vendored/utils.ts index e4bb26025388..74393ea17275 100644 --- a/packages/node/src/integrations/tracing/connect/vendored/utils.ts +++ b/packages/node/src/integrations/tracing/connect/vendored/utils.ts @@ -18,9 +18,10 @@ * - Upstream version: @opentelemetry/instrumentation-connect@0.61.0 */ -import { diag } from '@opentelemetry/api'; +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) { @@ -37,7 +38,7 @@ export const addNewStackLayer = (request: PatchedRequest) => { if (stackLength === request[_LAYERS_STORE_PROPERTY].length) { request[_LAYERS_STORE_PROPERTY].pop(); } else { - diag.warn('Connect: Trying to pop the stack multiple time'); + DEBUG_BUILD && debug.warn('Connect: Trying to pop the stack multiple time'); } }; }; From be87fa521511fb24000483051687e8ac5d18bf1b Mon Sep 17 00:00:00 2001 From: Nicolas Hrubec Date: Mon, 15 Jun 2026 13:18:48 +0200 Subject: [PATCH 7/9] ref(node): Set connect span op/origin/name at creation Move the Sentry op/origin/name off the `addConnectSpanAttributes` spanStart hook into the `startInactiveSpan` call, so they're set unconditionally (the hook only ran when `setupConnectErrorHandler` was used). Drop the now-unused `ConnectNames` enum and the hook. Co-Authored-By: Claude Opus 4.8 (1M context) --- .../src/integrations/tracing/connect/index.ts | 45 +------------------ .../connect/vendored/enums/AttributeNames.ts | 5 --- .../connect/vendored/instrumentation.ts | 23 +++------- 3 files changed, 9 insertions(+), 64 deletions(-) diff --git a/packages/node/src/integrations/tracing/connect/index.ts b/packages/node/src/integrations/tracing/connect/index.ts index b8f5322b087d..0c1ad31bcdfc 100644 --- a/packages/node/src/integrations/tracing/connect/index.ts +++ b/packages/node/src/integrations/tracing/connect/index.ts @@ -1,13 +1,6 @@ import { ConnectInstrumentation } from './vendored/instrumentation'; -import type { IntegrationFn, Span } from '@sentry/core'; -import { - captureException, - defineIntegration, - getClient, - SEMANTIC_ATTRIBUTE_SENTRY_OP, - SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN, - spanToJSON, -} from '@sentry/core'; +import type { IntegrationFn } from '@sentry/core'; +import { captureException, defineIntegration } from '@sentry/core'; import { ensureIsWrapped, generateInstrumentOnce } from '@sentry/node-core'; type ConnectApp = { @@ -78,39 +71,5 @@ function connectErrorMiddleware(err: any, req: any, res: any, next: any): void { */ export const setupConnectErrorHandler = (app: ConnectApp): void => { app.use(connectErrorMiddleware); - - // Sadly, ConnectInstrumentation has no requestHook, so we need to add the attributes here - // We register this hook in this method, because if we register it in the integration `setup`, - // it would always run even for users that are not even using connect - const client = getClient(); - if (client) { - client.on('spanStart', span => { - addConnectSpanAttributes(span); - }); - } - ensureIsWrapped(app.use, 'connect'); }; - -function addConnectSpanAttributes(span: Span): void { - const attributes = spanToJSON(span).data; - - // this is one of: middleware, request_handler - const type = attributes['connect.type']; - - // If this is already set, or we have no connect span, no need to process again... - if (attributes[SEMANTIC_ATTRIBUTE_SENTRY_OP] || !type) { - return; - } - - span.setAttributes({ - [SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: 'auto.http.otel.connect', - [SEMANTIC_ATTRIBUTE_SENTRY_OP]: `${type}.connect`, - }); - - // Also update the name, we don't need the "middleware - " prefix - const name = attributes['connect.name']; - if (typeof name === 'string') { - span.updateName(name); - } -} diff --git a/packages/node/src/integrations/tracing/connect/vendored/enums/AttributeNames.ts b/packages/node/src/integrations/tracing/connect/vendored/enums/AttributeNames.ts index 7ad986df3608..81aebb26d227 100644 --- a/packages/node/src/integrations/tracing/connect/vendored/enums/AttributeNames.ts +++ b/packages/node/src/integrations/tracing/connect/vendored/enums/AttributeNames.ts @@ -27,8 +27,3 @@ export enum ConnectTypes { MIDDLEWARE = 'middleware', REQUEST_HANDLER = 'request_handler', } - -export enum ConnectNames { - 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 index 728dcdf20b46..16d3cf4b7808 100644 --- a/packages/node/src/integrations/tracing/connect/vendored/instrumentation.ts +++ b/packages/node/src/integrations/tracing/connect/vendored/instrumentation.ts @@ -20,10 +20,10 @@ */ import type { ServerResponse } from 'http'; -import { AttributeNames, ConnectNames, ConnectTypes } from './enums/AttributeNames'; +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 { SDK_VERSION, SPAN_STATUS_ERROR, startInactiveSpan } from '@sentry/core'; +import { 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'; @@ -80,22 +80,13 @@ export class ConnectInstrumentation extends InstrumentationBase { } public _startSpan(routeName: string, middleWare: HandleFunction): Span { - let connectType: ConnectTypes; - let connectName: string; - let connectTypeName: string; - if (routeName) { - connectType = ConnectTypes.REQUEST_HANDLER; - connectTypeName = ConnectNames.REQUEST_HANDLER; - connectName = routeName; - } else { - connectType = ConnectTypes.MIDDLEWARE; - connectTypeName = ConnectNames.MIDDLEWARE; - connectName = middleWare.name || ANONYMOUS_NAME; - } - const spanName = `${connectTypeName} - ${connectName}`; + const connectType = routeName ? ConnectTypes.REQUEST_HANDLER : ConnectTypes.MIDDLEWARE; + const connectName = routeName || middleWare.name || ANONYMOUS_NAME; return startInactiveSpan({ - name: spanName, + name: connectName, + op: `${connectType}.connect`, attributes: { + [SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: 'auto.http.otel.connect', [ATTR_HTTP_ROUTE]: routeName.length > 0 ? routeName : '/', [AttributeNames.CONNECT_TYPE]: connectType, [AttributeNames.CONNECT_NAME]: connectName, From 732f16c33ed18e2bf3a605371b9c947d0d70b91e Mon Sep 17 00:00:00 2001 From: Nicolas Hrubec Date: Mon, 15 Jun 2026 13:31:00 +0200 Subject: [PATCH 8/9] fix(node): Only mark connect span errored for actual errors Guard the `next(err)` path with `isError` so routing signals like `next('route')` don't mark the span as errored. Co-Authored-By: Claude Opus 4.8 (1M context) --- .../tracing/connect/vendored/instrumentation.ts | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/packages/node/src/integrations/tracing/connect/vendored/instrumentation.ts b/packages/node/src/integrations/tracing/connect/vendored/instrumentation.ts index 16d3cf4b7808..e7ff4f0c4cdf 100644 --- a/packages/node/src/integrations/tracing/connect/vendored/instrumentation.ts +++ b/packages/node/src/integrations/tracing/connect/vendored/instrumentation.ts @@ -23,7 +23,13 @@ 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 { SDK_VERSION, SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN, SPAN_STATUS_ERROR, startInactiveSpan } 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'; @@ -70,7 +76,7 @@ export class ConnectInstrumentation extends InstrumentationBase { public _patchNext(next: NextFunction, span: Span, finishSpan: () => void): NextFunction { return function nextFunction(this: NextFunction, err?: unknown): void { - if (err) { + if (isError(err)) { span.setStatus({ code: SPAN_STATUS_ERROR, message: 'internal_error' }); } const result = next.apply(this, [err]); From abc93f2b5ae4dbbc24b7417f761e576abe515d1e Mon Sep 17 00:00:00 2001 From: Nicolas Hrubec Date: Mon, 15 Jun 2026 13:36:30 +0200 Subject: [PATCH 9/9] ref(node): Use oxlint-disable for connect unbound-method suppressions Co-Authored-By: Claude Opus 4.8 (1M context) --- .../integrations/tracing/connect/vendored/instrumentation.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/node/src/integrations/tracing/connect/vendored/instrumentation.ts b/packages/node/src/integrations/tracing/connect/vendored/instrumentation.ts index e7ff4f0c4cdf..63dfb41e3d9d 100644 --- a/packages/node/src/integrations/tracing/connect/vendored/instrumentation.ts +++ b/packages/node/src/integrations/tracing/connect/vendored/instrumentation.ts @@ -55,11 +55,11 @@ export class ConnectInstrumentation extends InstrumentationBase { } private _patchApp(patchedApp: Server) { - // eslint-disable-next-line @typescript-eslint/unbound-method + // oxlint-disable-next-line typescript/unbound-method if (!isWrapped(patchedApp.use)) { this._wrap(patchedApp, 'use', this._patchUse.bind(this)); } - // eslint-disable-next-line @typescript-eslint/unbound-method + // oxlint-disable-next-line typescript/unbound-method if (!isWrapped(patchedApp.handle)) { this._wrap(patchedApp, 'handle', this._patchHandle.bind(this)); }