@sentry/nextjs
Version:
Official Sentry SDK for Next.js
94 lines (91 loc) • 4.31 kB
JavaScript
import { debug, withIsolationScope, getActiveSpan, continueTrace, httpRequestToRequestData, parseStringToURLObject, startSpanManual, isURLObjectRelative, SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN, SEMANTIC_ATTRIBUTE_SENTRY_SOURCE, setHttpStatus, objectify, captureException, isString } from '@sentry/core';
import { waitUntil, flushSafelyWithTimeout } from '../utils/responseEnd.js';
import { dropNextjsRootContext, escapeNextjsTracing } from '../utils/tracingUtils.js';
import { HTTP_ROUTE, URL_PATH, URL_FULL } from '@sentry/conventions/attributes';
function wrapApiHandlerWithSentry(apiHandler, parameterizedRoute) {
return new Proxy(apiHandler, {
apply: (wrappingTarget, thisArg, args) => {
dropNextjsRootContext();
return escapeNextjsTracing(() => {
const [req, res] = args;
if (!req) {
debug.log(
`Wrapped API handler on route "${parameterizedRoute}" was not passed a request object. Will not instrument.`
);
return wrappingTarget.apply(thisArg, args);
} else if (!res) {
debug.log(
`Wrapped API handler on route "${parameterizedRoute}" was not passed a response object. Will not instrument.`
);
return wrappingTarget.apply(thisArg, args);
}
if (req.__withSentry_applied__) {
return wrappingTarget.apply(thisArg, args);
}
req.__withSentry_applied__ = true;
return withIsolationScope((isolationScope) => {
const continueTraceIfNoActiveSpan = getActiveSpan() ? (_opts, callback) => callback() : continueTrace;
return continueTraceIfNoActiveSpan(
{
sentryTrace: req.headers && isString(req.headers["sentry-trace"]) ? req.headers["sentry-trace"] : void 0,
baggage: req.headers?.baggage
},
() => {
const reqMethod = `${(req.method || "GET").toUpperCase()} `;
const normalizedRequest = httpRequestToRequestData(req);
isolationScope.setSDKProcessingMetadata({ normalizedRequest });
isolationScope.setTransactionName(`${reqMethod}${parameterizedRoute}`);
const requestUrl = normalizedRequest.url || req.url;
const urlObject = requestUrl ? parseStringToURLObject(requestUrl) : void 0;
return startSpanManual(
{
name: `${reqMethod}${parameterizedRoute}`,
op: "http.server",
forceTransaction: true,
attributes: {
[SEMANTIC_ATTRIBUTE_SENTRY_SOURCE]: "route",
[SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: "auto.http.nextjs",
[URL_FULL]: urlObject && !isURLObjectRelative(urlObject) ? urlObject.href : void 0,
[URL_PATH]: urlObject?.pathname,
[HTTP_ROUTE]: parameterizedRoute
}
},
async (span) => {
res.end = new Proxy(res.end, {
apply(target, thisArg2, argArray) {
setHttpStatus(span, res.statusCode);
span.end();
waitUntil(flushSafelyWithTimeout());
return target.apply(thisArg2, argArray);
}
});
try {
return await wrappingTarget.apply(thisArg, args);
} catch (e) {
const objectifiedErr = objectify(e);
captureException(objectifiedErr, {
mechanism: {
type: "auto.http.nextjs.api_handler",
handled: false,
data: {
wrapped_handler: wrappingTarget.name,
function: "withSentry"
}
}
});
setHttpStatus(span, 500);
span.end();
await flushSafelyWithTimeout();
throw objectifiedErr;
}
}
);
}
);
});
});
}
});
}
export { wrapApiHandlerWithSentry };
//# sourceMappingURL=wrapApiHandlerWithSentry.js.map