UNPKG

@sentry/nextjs

Version:
107 lines (100 loc) • 4.94 kB
import { _nullishCoalesce, _optionalChain } from '@sentry/utils'; import { addTracingExtensions, runWithAsyncContext, continueTrace, getCurrentScope, startSpanManual, SEMANTIC_ATTRIBUTE_SENTRY_SOURCE, handleCallbackErrors, captureException, getClient } from '@sentry/core'; import { winterCGHeadersToDict } from '@sentry/utils'; import { isNotFoundNavigationError, isRedirectNavigationError } from './nextNavigationErrorUtils.js'; import { commonObjectToPropagationContext } from './utils/commonObjectTracing.js'; /** * Wraps a generation function (e.g. generateMetadata) with Sentry error and performance instrumentation. */ // eslint-disable-next-line @typescript-eslint/no-explicit-any function wrapGenerationFunctionWithSentry( generationFunction, context, ) { addTracingExtensions(); const { requestAsyncStorage, componentRoute, componentType, generationFunctionIdentifier } = context; return new Proxy(generationFunction, { apply: (originalFunction, thisArg, args) => { let headers = undefined; // We try-catch here just in case anything goes wrong with the async storage here goes wrong since it is Next.js internal API try { headers = _optionalChain([requestAsyncStorage, 'optionalAccess', _ => _.getStore, 'call', _2 => _2(), 'optionalAccess', _3 => _3.headers]); } catch (e) { /** empty */ } let data = undefined; if (_optionalChain([getClient, 'call', _4 => _4(), 'optionalAccess', _5 => _5.getOptions, 'call', _6 => _6(), 'access', _7 => _7.sendDefaultPii])) { const props = args[0]; const params = props && typeof props === 'object' && 'params' in props ? props.params : undefined; const searchParams = props && typeof props === 'object' && 'searchParams' in props ? props.searchParams : undefined; data = { params, searchParams }; } return runWithAsyncContext(() => { const transactionContext = continueTrace({ baggage: _optionalChain([headers, 'optionalAccess', _8 => _8.get, 'call', _9 => _9('baggage')]), sentryTrace: _nullishCoalesce(_optionalChain([headers, 'optionalAccess', _10 => _10.get, 'call', _11 => _11('sentry-trace')]), () => ( undefined)), }); // If there is no incoming trace, we are setting the transaction context to one that is shared between all other // transactions for this request. We do this based on the `headers` object, which is the same for all components. const propagationContext = getCurrentScope().getPropagationContext(); if (!transactionContext.traceId && !transactionContext.parentSpanId) { const { traceId: commonTraceId, spanId: commonSpanId } = commonObjectToPropagationContext( headers, propagationContext, ); transactionContext.traceId = commonTraceId; transactionContext.parentSpanId = commonSpanId; } return startSpanManual( { op: 'function.nextjs', name: `${componentType}.${generationFunctionIdentifier} (${componentRoute})`, origin: 'auto.function.nextjs', ...transactionContext, data, attributes: { [SEMANTIC_ATTRIBUTE_SENTRY_SOURCE]: 'url', }, metadata: { // eslint-disable-next-line deprecation/deprecation ...transactionContext.metadata, request: { headers: headers ? winterCGHeadersToDict(headers) : undefined, }, }, }, span => { return handleCallbackErrors( () => originalFunction.apply(thisArg, args), err => { if (isNotFoundNavigationError(err)) { // We don't want to report "not-found"s _optionalChain([span, 'optionalAccess', _12 => _12.setStatus, 'call', _13 => _13('not_found')]); } else if (isRedirectNavigationError(err)) { // We don't want to report redirects _optionalChain([span, 'optionalAccess', _14 => _14.setStatus, 'call', _15 => _15('ok')]); } else { _optionalChain([span, 'optionalAccess', _16 => _16.setStatus, 'call', _17 => _17('internal_error')]); captureException(err, { mechanism: { handled: false, data: { function: 'wrapGenerationFunctionWithSentry', }, }, }); } }, () => { _optionalChain([span, 'optionalAccess', _18 => _18.end, 'call', _19 => _19()]); }, ); }, ); }); }, }); } export { wrapGenerationFunctionWithSentry }; //# sourceMappingURL=wrapGenerationFunctionWithSentry.js.map