From 33b6d7eb06a0973c39e8ee19bc9663ac25e63262 Mon Sep 17 00:00:00 2001 From: Abdelrahman Awad Date: Tue, 21 Jul 2026 11:12:01 -0400 Subject: [PATCH] feat(nextjs): Remove tracing from middleware wrappers Drops the manually-created span from the middleware wrapper. On Next.js 14+ the native OTel middleware span (normalized by enhanceMiddlewareRootSpan) already produces the middleware transaction, so the wrapper only forks an isolation scope, binds it to the native root span, captures errors, and flushes. Keeps withIsolationScope + setCapturedScopesOnSpan so per-request isolation still holds and global-scope data does not leak into the middleware transaction. --- .../src/common/wrapMiddlewareWithSentry.ts | 65 ++++++------------- 1 file changed, 21 insertions(+), 44 deletions(-) diff --git a/packages/nextjs/src/common/wrapMiddlewareWithSentry.ts b/packages/nextjs/src/common/wrapMiddlewareWithSentry.ts index 4f8054015e10..a49995d1e5f2 100644 --- a/packages/nextjs/src/common/wrapMiddlewareWithSentry.ts +++ b/packages/nextjs/src/common/wrapMiddlewareWithSentry.ts @@ -1,24 +1,23 @@ -import type { TransactionSource } from '@sentry/core'; import { captureException, getActiveSpan, getCurrentScope, getRootSpan, handleCallbackErrors, - SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN, - SEMANTIC_ATTRIBUTE_SENTRY_SOURCE, setCapturedScopesOnSpan, - startSpan, winterCGRequestToRequestData, withIsolationScope, } from '@sentry/core'; import { flushSafelyWithTimeout, waitUntil } from '../common/utils/responseEnd'; import { isPathnameUnderSentryTunnelRoute } from '../common/utils/tunnelPathnameMatch'; import type { EdgeRouteHandler } from '../edge/types'; -import { SENTRY_KIND } from '@sentry/conventions/attributes'; /** - * Wraps Next.js middleware with Sentry error and performance instrumentation. + * Wraps Next.js middleware with Sentry error instrumentation. + * + * The middleware transaction itself is created by Next.js' native OpenTelemetry instrumentation + * (the `Middleware.execute` span, normalized by `enhanceMiddlewareRootSpan`), so this wrapper no + * longer starts its own span. It only forks an isolation scope, captures errors, and flushes. * * @param middleware The middleware handler. * @returns a wrapped middleware handler. @@ -33,6 +32,7 @@ export function wrapMiddlewareWithSentry( ? (globalThis as Record)._sentryRewritesTunnelPath : undefined; + // TODO: This can never work with Turbopack, need to remove it for consistency between builds. if (tunnelRoute && typeof tunnelRoute === 'string') { const req: unknown = args[0]; // Check if the current request matches the tunnel route @@ -53,66 +53,43 @@ export function wrapMiddlewareWithSentry( } } } + // TODO: We still should add central isolation scope creation for when our build-time instrumentation does not work anymore with turbopack. return withIsolationScope(isolationScope => { const req: unknown = args[0]; const currentScope = getCurrentScope(); - let spanName: string; - let spanSource: TransactionSource; - if (req instanceof Request) { isolationScope.setSDKProcessingMetadata({ normalizedRequest: winterCGRequestToRequestData(req), }); - spanName = `middleware ${req.method}`; - spanSource = 'url'; + currentScope.setTransactionName(`middleware ${req.method}`); } else { - spanName = 'middleware'; - spanSource = 'component'; + currentScope.setTransactionName('middleware'); } - currentScope.setTransactionName(spanName); - const activeSpan = getActiveSpan(); - if (activeSpan) { - // If there is an active span, it likely means that the automatic Next.js OTEL instrumentation worked and we can - // rely on that for parameterization. - spanName = 'middleware'; - spanSource = 'component'; - + // If there is an active span, the native Next.js OTEL instrumentation created the middleware root span. + // Bind our forked scopes to it so the transaction picks up the isolation scope instead of the global one. const rootSpan = getRootSpan(activeSpan); if (rootSpan) { setCapturedScopesOnSpan(rootSpan, currentScope, isolationScope); } } - return startSpan( - { - name: spanName, - op: 'http.server.middleware', - attributes: { - [SENTRY_KIND]: 'server', - [SEMANTIC_ATTRIBUTE_SENTRY_SOURCE]: spanSource, - [SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: 'auto.function.nextjs.wrap_middleware', - }, + return handleCallbackErrors( + () => wrappingTarget.apply(thisArg, args), + error => { + captureException(error, { + mechanism: { + type: 'auto.function.nextjs.wrap_middleware', + handled: false, + }, + }); }, () => { - return handleCallbackErrors( - () => wrappingTarget.apply(thisArg, args), - error => { - captureException(error, { - mechanism: { - type: 'auto.function.nextjs.wrap_middleware', - handled: false, - }, - }); - }, - () => { - waitUntil(flushSafelyWithTimeout()); - }, - ); + waitUntil(flushSafelyWithTimeout()); }, ); });