import { context } from '@opentelemetry/api'; import { ATTR_HTTP_REQUEST_METHOD, SEMATTRS_HTTP_METHOD, ATTR_HTTP_ROUTE } from '@opentelemetry/semantic-conventions'; import { spanToJSON, getRootSpan, SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN, getIsolationScope, getCapturedScopesOnSpan, getCurrentScope, setCapturedScopesOnSpan } from '@sentry/core'; import { getScopesFromContext } from '@sentry/opentelemetry'; import { ATTR_NEXT_ROUTE, ATTR_NEXT_SPAN_TYPE, ATTR_NEXT_SPAN_NAME } from '../common/nextSpanAttributes.js'; import { addHeadersAsAttributes } from '../common/utils/addHeadersAsAttributes.js'; import { dropMiddlewareTunnelRequests } from '../common/utils/dropMiddlewareTunnelRequests.js'; import { maybeEnhanceServerComponentSpanName } from '../common/utils/tracingUtils.js'; import { maybeStartCronCheckIn } from './vercelCronsMonitoring.js'; /** * Handles the on span start event for Next.js spans. * This function is used to enhance the span with additional information such as the route, the method, the headers, etc. * It is called for every span that is started by Next.js. * @param span The span that is starting. */ function handleOnSpanStart(span) { const spanAttributes = spanToJSON(span).data; const rootSpan = getRootSpan(span); const rootSpanAttributes = spanToJSON(rootSpan).data; const isRootSpan = span === rootSpan; dropMiddlewareTunnelRequests(span, spanAttributes); // What we do in this glorious piece of code, is hoist any information about parameterized routes from spans emitted // by Next.js via the `next.route` attribute, up to the transaction by setting the http.route attribute. if (typeof spanAttributes?.[ATTR_NEXT_ROUTE] === 'string') { // Only hoist the http.route attribute if the transaction doesn't already have it if ( // eslint-disable-next-line deprecation/deprecation (rootSpanAttributes?.[ATTR_HTTP_REQUEST_METHOD] || rootSpanAttributes?.[SEMATTRS_HTTP_METHOD]) && !rootSpanAttributes?.[ATTR_HTTP_ROUTE] ) { const route = spanAttributes[ATTR_NEXT_ROUTE].replace(/\/route$/, ''); rootSpan.updateName(route); rootSpan.setAttribute(ATTR_HTTP_ROUTE, route); // Preserving the original attribute despite internally not depending on it rootSpan.setAttribute(ATTR_NEXT_ROUTE, route); // Check if this is a Vercel cron request and start a check-in maybeStartCronCheckIn(rootSpan, route); } } if (spanAttributes?.[ATTR_NEXT_SPAN_TYPE] === 'Middleware.execute') { const middlewareName = spanAttributes[ATTR_NEXT_SPAN_NAME]; if (typeof middlewareName === 'string') { rootSpan.updateName(middlewareName); rootSpan.setAttribute(ATTR_HTTP_ROUTE, middlewareName); rootSpan.setAttribute(ATTR_NEXT_SPAN_NAME, middlewareName); } span.setAttribute(SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN, 'auto'); } // We want to skip span data inference for any spans generated by Next.js. Reason being that Next.js emits spans // with patterns (e.g. http.server spans) that will produce confusing data. if (spanAttributes?.[ATTR_NEXT_SPAN_TYPE] !== undefined) { span.setAttribute(SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN, 'auto'); } if (isRootSpan) { const headers = getIsolationScope().getScopeData().sdkProcessingMetadata?.normalizedRequest?.headers; addHeadersAsAttributes(headers, rootSpan); } // We want to fork the isolation scope for incoming requests if (spanAttributes?.[ATTR_NEXT_SPAN_TYPE] === 'BaseServer.handleRequest' && isRootSpan) { const scopes = getCapturedScopesOnSpan(span); const isolationScope = (scopes.isolationScope || getIsolationScope()).clone(); const scope = scopes.scope || getCurrentScope(); const currentScopesPointer = getScopesFromContext(context.active()); if (currentScopesPointer) { currentScopesPointer.isolationScope = isolationScope; } setCapturedScopesOnSpan(span, scope, isolationScope); } maybeEnhanceServerComponentSpanName(span, spanAttributes, rootSpanAttributes); } export { handleOnSpanStart }; //# sourceMappingURL=handleOnSpanStart.js.map