@sentry/nextjs
Version:
Official Sentry SDK for Next.js
121 lines (118 loc) • 6.23 kB
JavaScript
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