UNPKG

@sentry/remix

Version:
195 lines (191 loc) • 7.37 kB
Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' }); const diagnosticsChannel = require('node:diagnostics_channel'); const core = require('@sentry/core'); const serverUtils = require('@sentry/server-utils'); const attributes = require('@sentry/conventions/attributes'); const op = require('@sentry/conventions/op'); const config = require('@sentry/server-utils/orchestrion/config'); const formData = require('../../utils/formData.js'); const ORIGIN = "auto.http.remix"; const NOOP = () => { }; const MATCH_ROUTE_ID = "match.route.id"; const MATCH_PARAMS = "match.params"; function getRequestAttributes(request) { if (!core.isObjectLike(request)) { return {}; } const { method, url } = request; const attributes$1 = {}; if (typeof method === "string") { attributes$1[attributes.HTTP_REQUEST_METHOD] = method; } if (typeof url === "string") { const urlObject = core.parseStringToURLObject(url); attributes$1[attributes.URL_FULL] = core.filterCollectedUrl( urlObject && !core.isURLObjectRelative(urlObject) ? urlObject.href : void 0 ); attributes$1[attributes.URL_PATH] = urlObject?.pathname; } return attributes$1; } function getMatchAttributes(params) { const attributes$1 = {}; if (params.routeId) { attributes$1[MATCH_ROUTE_ID] = params.routeId; attributes$1[attributes.ROUTER_NAVIGATION_ROUTE_ID] = params.routeId; } for (const [name, value] of Object.entries(params.params ?? {})) { attributes$1[`${MATCH_PARAMS}.${name}`] = value || "(undefined)"; } return attributes$1; } function setResponseStatus(span, result) { if (!core.isObjectLike(result)) { return; } const status = result.status; if (typeof status === "number") { span.setAttribute(attributes.HTTP_RESPONSE_STATUS_CODE, status); const spanStatus = core.getSpanStatusFromHttpCode(status); span.setStatus(spanStatus); } } function enrichActiveSpanWithRoute(result) { const span = core.getActiveSpan(); if (!span) { return; } const matches = Array.isArray(result) ? result : []; const route = matches[matches.length - 1]?.route; if (route?.path) { span.setAttribute(attributes.HTTP_ROUTE, route.path); const method = core.spanToJSON(span).attributes[attributes.HTTP_REQUEST_METHOD]; span.updateName(typeof method === "string" ? `${method} ${route.path}` : route.path); span.setAttribute(attributes.SENTRY_SEGMENT_NAME_SOURCE, "route"); } if (route?.id) { span.setAttribute(MATCH_ROUTE_ID, route.id); } } function subscribeRequestHandler() { serverUtils.bindTracingChannelToSpan( diagnosticsChannel.tracingChannel(config.remixChannels.REMIX_REQUEST_HANDLER), (data) => { const requestAttributes = getRequestAttributes(data.arguments[0]); const method = requestAttributes[attributes.HTTP_REQUEST_METHOD]; const path = requestAttributes[attributes.URL_PATH]; const hasUrlName = typeof method === "string" && typeof path === "string"; const client = core.getClient(); const isStreamed = !!client && core.hasSpanStreamingEnabled(client); return core.startInactiveSpan({ name: isStreamed ? typeof method === "string" ? method : core.HTTP_SPAN_NAME_FALLBACK : hasUrlName ? `${method} ${path}` : "remix.request", attributes: { [attributes.SENTRY_KIND]: "server", [attributes.SENTRY_ORIGIN]: ORIGIN, [attributes.SENTRY_OP]: op.HTTP_SERVER, ...hasUrlName && { [attributes.SENTRY_SEGMENT_NAME_SOURCE]: "url" }, [attributes.CODE_FUNCTION_NAME]: "requestHandler", ...requestAttributes } }); }, { beforeSpanEnd: (span, data) => setResponseStatus(span, data.result) } ); } function subscribeMatchServerRoutes() { diagnosticsChannel.tracingChannel(config.remixChannels.REMIX_MATCH_SERVER_ROUTES).subscribe({ start: NOOP, end(data) { enrichActiveSpanWithRoute(data.result); }, asyncStart: NOOP, asyncEnd: NOOP, error: NOOP }); } function subscribeCallRouteLoader() { serverUtils.bindTracingChannelToSpan( diagnosticsChannel.tracingChannel(config.remixChannels.REMIX_CALL_ROUTE_LOADER), (data) => { const params = data.arguments[0] ?? {}; const client = core.getClient(); const description = `LOADER ${params.routeId}`; return core.startInactiveSpan({ // With span streaming, a `function` span is named after the function it wraps. The route id // stays on `match.route.id`. name: client && core.hasSpanStreamingEnabled(client) ? "loader" : description, attributes: { [attributes.SENTRY_ORIGIN]: ORIGIN, [attributes.SENTRY_OP]: op.FUNCTION, [attributes.SENTRY_DESCRIPTION]: description, [attributes.CODE_FUNCTION_NAME]: "loader", ...getRequestAttributes(params.request), ...getMatchAttributes(params) } }); }, { requiresParentSpan: true, beforeSpanEnd: (span, data) => setResponseStatus(span, data.result) } ); } function subscribeCallRouteAction(formDataCapture) { serverUtils.bindTracingChannelToSpan( diagnosticsChannel.tracingChannel(config.remixChannels.REMIX_CALL_ROUTE_ACTION), (data) => { const params = data.arguments[0] ?? {}; if (formDataCapture && params.request) { const formData = params.request.clone().formData(); formData.catch(() => void 0); data._sentryFormData = formData; } const client = core.getClient(); const description = `ACTION ${params.routeId}`; return core.startInactiveSpan({ // With span streaming, a `function` span is named after the function it wraps. The route id // stays on `match.route.id`. name: client && core.hasSpanStreamingEnabled(client) ? "action" : description, attributes: { [attributes.SENTRY_ORIGIN]: ORIGIN, [attributes.SENTRY_OP]: op.FUNCTION, [attributes.SENTRY_DESCRIPTION]: description, [attributes.CODE_FUNCTION_NAME]: "action", ...getRequestAttributes(params.request), ...getMatchAttributes(params) } }); }, { requiresParentSpan: true, beforeSpanEnd: (span, data) => setResponseStatus(span, data.result), // Hold the span end until the (already in-flight) form-data read resolves, then apply the // attributes and end (which sets the response status via `beforeSpanEnd`). On error, or when // capture isn't configured, let the helper end the span normally. deferSpanEnd: ({ span, data, end }) => { const formData$1 = data._sentryFormData; if (!formDataCapture || !formData$1 || "error" in data) { return false; } formData$1.then((resolved) => formData.applyFormDataAttributes(span, resolved, formDataCapture)).catch(() => void 0).finally(() => end()); return true; } } ); } function instrumentRemix(formDataCapture) { if (!diagnosticsChannel.tracingChannel) { return; } core.waitForTracingChannelBinding(() => { subscribeRequestHandler(); subscribeMatchServerRoutes(); subscribeCallRouteLoader(); subscribeCallRouteAction(formDataCapture); }); } exports.instrumentRemix = instrumentRemix; //# sourceMappingURL=tracing-channel.js.map