UNPKG

@sentry/browser

Version:
226 lines (222 loc) 8.63 kB
Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' }); const browser = require('@sentry/core/browser'); const browserUtils = require('@sentry-internal/browser-utils'); const utils = require('./utils.js'); const defaultRequestInstrumentationOptions = { traceFetch: true, traceXHR: true, enableHTTPTimings: true, trackFetchStreamPerformance: false }; function instrumentOutgoingRequests(client, _options) { const { traceFetch, traceXHR, shouldCreateSpanForRequest, enableHTTPTimings, tracePropagationTargets, onRequestSpanStart, onRequestSpanEnd } = { ...defaultRequestInstrumentationOptions, ..._options }; const shouldCreateSpan = typeof shouldCreateSpanForRequest === "function" ? shouldCreateSpanForRequest : (_) => true; const shouldAttachHeadersWithTargets = (url) => shouldAttachHeaders(url, tracePropagationTargets); const spans = {}; const propagateTraceparent = client.getOptions().propagateTraceparent; if (traceFetch) { browser.addFetchInstrumentationHandler((handlerData) => { const createdSpan = browser.instrumentFetchRequest(handlerData, shouldCreateSpan, shouldAttachHeadersWithTargets, spans, { propagateTraceparent, onRequestSpanEnd }); if (createdSpan) { const fullUrl = utils.getFullURL(handlerData.fetchData.url); const host = fullUrl ? browser.parseUrl(fullUrl).host : void 0; createdSpan.setAttributes({ "http.url": fullUrl ? browser.stripDataUrlContent(fullUrl) : void 0, "server.address": host }); if (enableHTTPTimings) { addHTTPTimings(createdSpan, client); } onRequestSpanStart?.(createdSpan, { headers: handlerData.headers }); } }); } if (traceXHR) { browserUtils.addXhrInstrumentationHandler((handlerData) => { const createdSpan = xhrCallback( handlerData, shouldCreateSpan, shouldAttachHeadersWithTargets, spans, propagateTraceparent, onRequestSpanEnd ); if (createdSpan) { if (enableHTTPTimings) { addHTTPTimings(createdSpan, client); } onRequestSpanStart?.(createdSpan, { headers: utils.createHeadersSafely(handlerData.xhr.__sentry_xhr_v3__?.request_headers) }); } }); } } const HTTP_TIMING_WAIT_MS = 300; function addHTTPTimings(span, client) { const { url } = browser.spanToJSON(span).data; if (!url || typeof url !== "string") { return; } let onEntryFound = () => void setTimeout(unsubscribePerformanceObsever); if (browser.hasSpanStreamingEnabled(client)) { const originalEnd = span.end.bind(span); span.end = (endTimestamp) => { const capturedEndTimestamp = endTimestamp ?? browser.timestampInSeconds(); let isEnded = false; const endSpanAndCleanup = () => { if (isEnded) { return; } isEnded = true; setTimeout(unsubscribePerformanceObsever); originalEnd(capturedEndTimestamp); clearTimeout(fallbackTimeout); }; onEntryFound = endSpanAndCleanup; const fallbackTimeout = setTimeout(endSpanAndCleanup, HTTP_TIMING_WAIT_MS); }; } const unsubscribePerformanceObsever = browserUtils.addPerformanceInstrumentationHandler("resource", ({ entries }) => { entries.forEach((entry) => { if (utils.isPerformanceResourceTiming(entry) && entry.name.endsWith(url)) { span.setAttributes(browserUtils.resourceTimingToSpanAttributes(entry)); onEntryFound(); } }); }); } function shouldAttachHeaders(targetUrl, tracePropagationTargets) { const href = browser.getLocationHref(); if (!href) { const isRelativeSameOriginRequest = !!targetUrl.match(/^\/(?!\/)/); if (!tracePropagationTargets) { return isRelativeSameOriginRequest; } else { return browser.stringMatchesSomePattern(targetUrl, tracePropagationTargets); } } else { let resolvedUrl; let currentOrigin; try { resolvedUrl = new URL(targetUrl, href); currentOrigin = new URL(href).origin; } catch { return false; } const isSameOriginRequest = resolvedUrl.origin === currentOrigin; if (!tracePropagationTargets) { return isSameOriginRequest; } else { return browser.stringMatchesSomePattern(resolvedUrl.toString(), tracePropagationTargets) || isSameOriginRequest && browser.stringMatchesSomePattern(resolvedUrl.pathname, tracePropagationTargets); } } } function xhrCallback(handlerData, shouldCreateSpan, shouldAttachHeaders2, spans, propagateTraceparent, onRequestSpanEnd) { const xhr = handlerData.xhr; const sentryXhrData = xhr?.[browserUtils.SENTRY_XHR_DATA_KEY]; if (!xhr || xhr.__sentry_own_request__ || !sentryXhrData) { return void 0; } const { url, method } = sentryXhrData; const shouldCreateSpanResult = browser.hasSpansEnabled() && shouldCreateSpan(url); if (handlerData.endTimestamp) { const spanId = xhr.__sentry_xhr_span_id__; if (!spanId) return; const span2 = spans[spanId]; if (span2) { if (shouldCreateSpanResult && sentryXhrData.status_code !== void 0) { browser.setHttpStatus(span2, sentryXhrData.status_code); span2.end(); onRequestSpanEnd?.(span2, { headers: utils.createHeadersSafely(browserUtils.parseXhrResponseHeaders(xhr)), error: handlerData.error }); } delete spans[spanId]; } return void 0; } const fullUrl = utils.getFullURL(url); const parsedUrl = fullUrl ? browser.parseUrl(fullUrl) : browser.parseUrl(url); const urlForSpanName = browser.stripDataUrlContent(browser.stripUrlQueryAndFragment(url)); const client = browser.getClient(); const hasParent = !!browser.getActiveSpan(); const shouldEmitSpan = hasParent || !!client && browser.hasSpanStreamingEnabled(client); const span = shouldCreateSpanResult && shouldEmitSpan ? browser.startInactiveSpan({ name: `${method} ${urlForSpanName}`, attributes: { url: browser.stripDataUrlContent(url), type: "xhr", "http.method": method, "http.url": fullUrl ? browser.stripDataUrlContent(fullUrl) : void 0, "server.address": parsedUrl?.host, [browser.SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: "auto.http.browser", [browser.SEMANTIC_ATTRIBUTE_SENTRY_OP]: "http.client", ...parsedUrl?.search && { "http.query": parsedUrl?.search }, ...parsedUrl?.hash && { "http.fragment": parsedUrl?.hash } } }) : new browser.SentryNonRecordingSpan(); if (shouldCreateSpanResult && !shouldEmitSpan) { client?.recordDroppedEvent("no_parent_span", "span"); } xhr.__sentry_xhr_span_id__ = span.spanContext().spanId; spans[xhr.__sentry_xhr_span_id__] = span; if (shouldAttachHeaders2(url)) { addTracingHeadersToXhrRequest( xhr, // If performance is disabled (TWP) or there's no active root span (pageload/navigation/interaction), // we do not want to use the span as base for the trace headers, // which means that the headers will be generated from the scope and the sampling decision is deferred browser.hasSpansEnabled() && shouldEmitSpan ? span : void 0, propagateTraceparent ); } if (client) { client.emit("beforeOutgoingRequestSpan", span, handlerData); } return span; } function addTracingHeadersToXhrRequest(xhr, span, propagateTraceparent) { const { "sentry-trace": sentryTrace, baggage, traceparent } = browser.getTraceData({ span, propagateTraceparent }); if (sentryTrace) { setHeaderOnXhr(xhr, sentryTrace, baggage, traceparent); } } function setHeaderOnXhr(xhr, sentryTraceHeader, sentryBaggageHeader, traceparentHeader) { const originalHeaders = xhr.__sentry_xhr_v3__?.request_headers; if (originalHeaders?.["sentry-trace"] || !xhr.setRequestHeader) { return; } try { xhr.setRequestHeader("sentry-trace", sentryTraceHeader); if (traceparentHeader && !originalHeaders?.["traceparent"]) { xhr.setRequestHeader("traceparent", traceparentHeader); } if (sentryBaggageHeader) { const originalBaggageHeader = originalHeaders?.["baggage"]; if (!originalBaggageHeader || !utils.baggageHeaderHasSentryValues(originalBaggageHeader)) { xhr.setRequestHeader("baggage", sentryBaggageHeader); } } } catch { } } exports.defaultRequestInstrumentationOptions = defaultRequestInstrumentationOptions; exports.instrumentOutgoingRequests = instrumentOutgoingRequests; exports.shouldAttachHeaders = shouldAttachHeaders; //# sourceMappingURL=request.js.map