Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
65 changes: 21 additions & 44 deletions packages/nextjs/src/common/wrapMiddlewareWithSentry.ts
Original file line number Diff line number Diff line change
@@ -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.
Expand All @@ -33,6 +32,7 @@ export function wrapMiddlewareWithSentry<H extends EdgeRouteHandler>(
? (globalThis as Record<string, unknown>)._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
Expand All @@ -53,66 +53,43 @@ export function wrapMiddlewareWithSentry<H extends EdgeRouteHandler>(
}
}
}

// 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());
},
);
});
Expand Down
Loading