UNPKG

@sentry/nextjs

Version:
121 lines (118 loc) • 6.23 kB
import { HTTP_REQUEST_METHOD, HTTP_METHOD, SENTRY_ORIGIN, SENTRY_SEGMENT_NAME_SOURCE, HTTP_ROUTE, SENTRY_OP } from '@sentry/conventions/attributes'; import { MIDDLEWARE } from '@sentry/conventions/op'; import { getRootSpan, spanToJSON, hasSpanStreamingEnabled, HTTP_SPAN_NAME_FALLBACK, getIsolationScope } from '@sentry/core'; import { enhanceMiddlewareRootSpan } from '../common/enhanceMiddlewareRootSpan.js'; import { ATTR_NEXT_SPAN_TYPE, ATTR_NEXT_ROUTE, ATTR_NEXT_SPAN_NAME } from '../common/nextSpanAttributes.js'; import { TRANSACTION_ATTR_SHOULD_DROP_TRANSACTION } from '../common/span-attributes-with-logic-attached.js'; import { addHeadersAsAttributes } from '../common/utils/addHeadersAsAttributes.js'; import { backfillHttpServerStatus } from '../common/utils/backfillHttpServerStatus.js'; import { dropMiddlewareTunnelRequests } from '../common/utils/dropMiddlewareTunnelRequests.js'; import { createLiveRootSpanAdapter } from '../common/utils/liveRootSpanAdapter.js'; import { maybeForkIsolationScopeForRootSpan } from '../common/utils/forkIsolationScopeForRootSpan.js'; import { setUrlProcessingMetadata } from '../common/utils/setUrlProcessingMetadata.js'; import { maybeEnhanceServerComponentSpanName } from '../common/utils/tracingUtils.js'; import { enhanceHandleRequestRootSpan } from './enhanceHandleRequestRootSpan.js'; import { maybeCompleteCronCheckIn, maybeStartCronCheckIn } from './vercelCronsMonitoring.js'; import { maybeCleanupQueueSpan, maybeEnrichQueueProducerSpan, maybeEnrichQueueConsumerSpan } from './vercelQueuesMonitoring.js'; const NEXTJS_ERROR_PAGE_ROUTES = ["/500", "/_error"]; const NEXTJS_SERVER_IGNORE_SPANS = [ // Static assets (matches `_next/static` anywhere in the name to handle custom basePath) /^GET (\/.*)?\/_next\/static\//, // Dev source-map fetch endpoints /\/__nextjs_original-stack-frame/, // Pages router /404 /^\/404$/, // App router /404 and /_not-found segments (any HTTP method) /^(GET|HEAD|POST|PUT|DELETE|CONNECT|OPTIONS|TRACE|PATCH) \/(404|_not-found)$/, // Root transactions named "NextServer.getRequestHandler" containing useless tracing /^NextServer\.getRequestHandler$/, // Spans flagged via TRANSACTION_ATTR_SHOULD_DROP_TRANSACTION // (set in `dropMiddlewareTunnelRequests` during `spanStart`) { attributes: { [TRANSACTION_ATTR_SHOULD_DROP_TRANSACTION]: true } } ]; function addNextjsServerSpanHooks(client) { client.on("spanStart", (span) => handleOnSpanStart(span, client)); client.on("spanEnd", (span) => { if (span !== getRootSpan(span)) { return; } const mutableRootSpan = createLiveRootSpanAdapter(span); enhanceHandleRequestRootSpan(mutableRootSpan); enhanceMiddlewareRootSpan(mutableRootSpan); backfillHttpServerStatus(span); }); client.on("spanEnd", maybeCompleteCronCheckIn); client.on("spanEnd", maybeCleanupQueueSpan); client.on("preprocessEvent", (event) => { setUrlProcessingMetadata(event); }); } function maybeHoistRouteToRootSpan(spanAttributes, rootSpan, rootSpanAttributes) { if (typeof spanAttributes?.[ATTR_NEXT_ROUTE] !== "string") { return; } const route = spanAttributes[ATTR_NEXT_ROUTE].replace(/\/route$/, ""); const isErrorPageBehindMiddleware = NEXTJS_ERROR_PAGE_ROUTES.includes(route) && rootSpanAttributes?.[SENTRY_SEGMENT_NAME_SOURCE] === "route"; const method = rootSpanAttributes?.[HTTP_REQUEST_METHOD] || rootSpanAttributes?.[HTTP_METHOD]; if (!method || rootSpanAttributes?.[HTTP_ROUTE] || isErrorPageBehindMiddleware) { return; } const name = typeof method === "string" ? `${method} ${route}` : route; rootSpan.updateName(name); rootSpan.setAttributes({ [HTTP_ROUTE]: route, // Preserving the original attribute despite internally not depending on it [ATTR_NEXT_ROUTE]: route, [SENTRY_SEGMENT_NAME_SOURCE]: "route" }); getIsolationScope().setTransactionName(name); maybeStartCronCheckIn(rootSpan, route); maybeEnrichQueueConsumerSpan(rootSpan); } function handleMiddlewareSpanStart(span, spanAttributes, rootSpan, rootSpanAttributes) { const middlewareName = spanAttributes?.[ATTR_NEXT_SPAN_NAME]; if (rootSpanAttributes?.[ATTR_NEXT_SPAN_TYPE] === void 0) { span.setAttribute(SENTRY_OP, MIDDLEWARE); if (typeof middlewareName === "string" && !rootSpanAttributes?.[HTTP_ROUTE]) { rootSpan.updateName(middlewareName); rootSpan.setAttribute(SENTRY_SEGMENT_NAME_SOURCE, "route"); } } else if (typeof middlewareName === "string") { rootSpan.updateName(middlewareName); rootSpan.setAttributes({ [HTTP_ROUTE]: middlewareName, [ATTR_NEXT_SPAN_NAME]: middlewareName, [SENTRY_SEGMENT_NAME_SOURCE]: "route" }); } span.setAttribute(SENTRY_ORIGIN, "auto"); } function handleOnSpanStart(span, client) { const spanAttributes = spanToJSON(span).attributes; const rootSpan = getRootSpan(span); const rootSpanAttributes = spanToJSON(rootSpan).attributes; const isRootSpan = span === rootSpan; dropMiddlewareTunnelRequests(span, spanAttributes); if (isRootSpan && spanAttributes?.[ATTR_NEXT_SPAN_TYPE] === "BaseServer.handleRequest" && hasSpanStreamingEnabled(client)) { const method = spanAttributes[HTTP_REQUEST_METHOD] ?? spanAttributes[HTTP_METHOD]; createLiveRootSpanAdapter(span).setName( (typeof method === "string" ? method.toUpperCase() : "") || HTTP_SPAN_NAME_FALLBACK ); } maybeHoistRouteToRootSpan(spanAttributes, rootSpan, rootSpanAttributes); if (spanAttributes?.[ATTR_NEXT_SPAN_TYPE] === "Middleware.execute") { handleMiddlewareSpanStart(span, spanAttributes, rootSpan, rootSpanAttributes); } if (spanAttributes?.[ATTR_NEXT_SPAN_TYPE] !== void 0) { span.setAttribute(SENTRY_ORIGIN, "auto"); } if (isRootSpan) { const headers = getIsolationScope().getScopeData().sdkProcessingMetadata?.normalizedRequest?.headers; addHeadersAsAttributes(headers, rootSpan); } maybeForkIsolationScopeForRootSpan(span, spanAttributes); maybeEnhanceServerComponentSpanName(span, spanAttributes, rootSpanAttributes, client); maybeEnrichQueueProducerSpan(span); } export { NEXTJS_SERVER_IGNORE_SPANS, addNextjsServerSpanHooks, handleOnSpanStart }; //# sourceMappingURL=serverSpanHooks.js.map