UNPKG

@sentry/remix

Version:
219 lines (173 loc) 6.17 kB
import { _optionalChain } from '@sentry/utils/esm/buildPolyfills'; import { captureException, getCurrentHub } from '@sentry/node'; import { getActiveTransaction } from '@sentry/tracing'; import { addExceptionMechanism, stripUrlQueryAndFragment, fill, loadModule, logger } from '@sentry/utils'; // Types vendored from @remix-run/server-runtime@1.6.0: // https://github.com/remix-run/remix/blob/f3691d51027b93caa3fd2cdfe146d7b62a6eb8f2/packages/remix-server-runtime/server.ts // Taken from Remix Implementation // https://github.com/remix-run/remix/blob/7688da5c75190a2e29496c78721456d6e12e3abe/packages/remix-server-runtime/responses.ts#L54-L62 function isResponse(value) { return ( value != null && typeof value.status === 'number' && typeof value.statusText === 'string' && typeof value.headers === 'object' && typeof value.body !== 'undefined' ); } // Taken from Remix Implementation // https://github.com/remix-run/remix/blob/7688da5c75190a2e29496c78721456d6e12e3abe/packages/remix-server-runtime/data.ts#L131-L145 function extractData(response) { var contentType = response.headers.get('Content-Type'); if (contentType && /\bapplication\/json\b/.test(contentType)) { return response.json(); } return response.text(); } function captureRemixServerException(err, name) { // Skip capturing if the thrown error is an OK Response // https://remix.run/docs/en/v1/api/conventions#throwing-responses-in-loaders if (isResponse(err) && err.status < 400) { return; } captureException(isResponse(err) ? extractData(err) : err, scope => { scope.addEventProcessor(event => { addExceptionMechanism(event, { type: 'instrument', handled: true, data: { function: name, }, }); return event; }); return scope; }); } function makeWrappedDocumentRequestFunction( origDocumentRequestFunction, ) { return async function ( request, responseStatusCode, responseHeaders, context, ) { let res; var activeTransaction = getActiveTransaction(); var currentScope = getCurrentHub().getScope(); if (!activeTransaction || !currentScope) { return origDocumentRequestFunction.call(this, request, responseStatusCode, responseHeaders, context); } try { var span = activeTransaction.startChild({ op: 'remix.server.documentRequest', description: activeTransaction.name, tags: { method: request.method, url: request.url, }, }); res = await origDocumentRequestFunction.call(this, request, responseStatusCode, responseHeaders, context); span.finish(); } catch (err) { captureRemixServerException(err, 'documentRequest'); throw err; } return res; }; } function makeWrappedDataFunction(origFn, name) { return async function ( args) { let res; var activeTransaction = getActiveTransaction(); var currentScope = getCurrentHub().getScope(); if (!activeTransaction || !currentScope) { return origFn.call(this, args); } try { var span = activeTransaction.startChild({ op: `remix.server.${name}`, description: activeTransaction.name, tags: { name, }, }); if (span) { // Assign data function to hub to be able to see `db` transactions (if any) as children. currentScope.setSpan(span); } res = await origFn.call(this, args); currentScope.setSpan(activeTransaction); span.finish(); } catch (err) { captureRemixServerException(err, name); throw err; } return res; }; } function makeWrappedAction(origAction) { return makeWrappedDataFunction(origAction, 'action'); } function makeWrappedLoader(origAction) { return makeWrappedDataFunction(origAction, 'loader'); } function wrapRequestHandler(origRequestHandler) { return async function ( request, loadContext) { var hub = getCurrentHub(); var currentScope = hub.getScope(); var transaction = hub.startTransaction({ name: stripUrlQueryAndFragment(request.url), op: 'http.server', tags: { method: request.method, }, metadata: { source: 'url', }, }); if (transaction) { _optionalChain([currentScope, 'optionalAccess', _ => _.setSpan, 'call', _2 => _2(transaction)]); } var res = (await origRequestHandler.call(this, request, loadContext)) ; _optionalChain([transaction, 'optionalAccess', _3 => _3.setHttpStatus, 'call', _4 => _4(res.status)]); _optionalChain([transaction, 'optionalAccess', _5 => _5.finish, 'call', _6 => _6()]); return res; }; } function makeWrappedCreateRequestHandler( origCreateRequestHandler, ) { return function ( build, mode) { var routes = {}; var wrappedEntry = { ...build.entry, module: { ...build.entry.module } }; fill(wrappedEntry.module, 'default', makeWrappedDocumentRequestFunction); for (const [id, route] of Object.entries(build.routes)) { var wrappedRoute = { ...route, module: { ...route.module } }; if (wrappedRoute.module.action) { fill(wrappedRoute.module, 'action', makeWrappedAction); } if (wrappedRoute.module.loader) { fill(wrappedRoute.module, 'loader', makeWrappedLoader); } routes[id] = wrappedRoute; } var requestHandler = origCreateRequestHandler.call(this, { ...build, routes, entry: wrappedEntry }, mode); return wrapRequestHandler(requestHandler); }; } /** * Monkey-patch Remix's `createRequestHandler` from `@remix-run/server-runtime` * which Remix Adapters (https://remix.run/docs/en/v1/api/remix) use underneath. */ function instrumentServer() { var pkg = loadModule('@remix-run/server-runtime'); if (!pkg) { (typeof __SENTRY_DEBUG__ === 'undefined' || __SENTRY_DEBUG__) && logger.warn('Remix SDK was unable to require `@remix-run/server-runtime` package.'); return; } fill(pkg, 'createRequestHandler', makeWrappedCreateRequestHandler); } export { instrumentServer }; //# sourceMappingURL=instrumentServer.js.map