fetch.js 5.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174
  1. Object.defineProperty(exports, '__esModule', { value: true });
  2. const core = require('@sentry/core');
  3. const utils = require('@sentry/utils');
  4. /**
  5. * Create and track fetch request spans for usage in combination with `addInstrumentationHandler`.
  6. *
  7. * @returns Span if a span was created, otherwise void.
  8. */
  9. function instrumentFetchRequest(
  10. handlerData,
  11. shouldCreateSpan,
  12. shouldAttachHeaders,
  13. spans,
  14. spanOrigin = 'auto.http.browser',
  15. ) {
  16. if (!core.hasTracingEnabled() || !handlerData.fetchData) {
  17. return undefined;
  18. }
  19. const shouldCreateSpanResult = shouldCreateSpan(handlerData.fetchData.url);
  20. if (handlerData.endTimestamp && shouldCreateSpanResult) {
  21. const spanId = handlerData.fetchData.__span;
  22. if (!spanId) return;
  23. const span = spans[spanId];
  24. if (span) {
  25. if (handlerData.response) {
  26. core.setHttpStatus(span, handlerData.response.status);
  27. const contentLength =
  28. handlerData.response && handlerData.response.headers && handlerData.response.headers.get('content-length');
  29. if (contentLength) {
  30. const contentLengthNum = parseInt(contentLength);
  31. if (contentLengthNum > 0) {
  32. span.setAttribute('http.response_content_length', contentLengthNum);
  33. }
  34. }
  35. } else if (handlerData.error) {
  36. span.setStatus('internal_error');
  37. }
  38. span.end();
  39. // eslint-disable-next-line @typescript-eslint/no-dynamic-delete
  40. delete spans[spanId];
  41. }
  42. return undefined;
  43. }
  44. const scope = core.getCurrentScope();
  45. const client = core.getClient();
  46. const { method, url } = handlerData.fetchData;
  47. const span = shouldCreateSpanResult
  48. ? core.startInactiveSpan({
  49. name: `${method} ${url}`,
  50. onlyIfParent: true,
  51. attributes: {
  52. url,
  53. type: 'fetch',
  54. 'http.method': method,
  55. [core.SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: spanOrigin,
  56. },
  57. op: 'http.client',
  58. })
  59. : undefined;
  60. if (span) {
  61. handlerData.fetchData.__span = span.spanContext().spanId;
  62. spans[span.spanContext().spanId] = span;
  63. }
  64. if (shouldAttachHeaders(handlerData.fetchData.url) && client) {
  65. const request = handlerData.args[0];
  66. // In case the user hasn't set the second argument of a fetch call we default it to `{}`.
  67. handlerData.args[1] = handlerData.args[1] || {};
  68. // eslint-disable-next-line @typescript-eslint/no-explicit-any
  69. const options = handlerData.args[1];
  70. // eslint-disable-next-line @typescript-eslint/no-explicit-any, @typescript-eslint/no-unsafe-member-access
  71. options.headers = addTracingHeadersToFetchRequest(request, client, scope, options, span);
  72. }
  73. return span;
  74. }
  75. /**
  76. * Adds sentry-trace and baggage headers to the various forms of fetch headers
  77. */
  78. function addTracingHeadersToFetchRequest(
  79. request, // unknown is actually type Request but we can't export DOM types from this package,
  80. client,
  81. scope,
  82. options
  83. ,
  84. requestSpan,
  85. ) {
  86. // eslint-disable-next-line deprecation/deprecation
  87. const span = requestSpan || scope.getSpan();
  88. const isolationScope = core.getIsolationScope();
  89. const { traceId, spanId, sampled, dsc } = {
  90. ...isolationScope.getPropagationContext(),
  91. ...scope.getPropagationContext(),
  92. };
  93. const sentryTraceHeader = span ? core.spanToTraceHeader(span) : utils.generateSentryTraceHeader(traceId, spanId, sampled);
  94. const sentryBaggageHeader = utils.dynamicSamplingContextToSentryBaggageHeader(
  95. dsc ||
  96. (span ? core.getDynamicSamplingContextFromSpan(span) : core.getDynamicSamplingContextFromClient(traceId, client, scope)),
  97. );
  98. const headers =
  99. options.headers ||
  100. (typeof Request !== 'undefined' && utils.isInstanceOf(request, Request) ? (request ).headers : undefined);
  101. if (!headers) {
  102. return { 'sentry-trace': sentryTraceHeader, baggage: sentryBaggageHeader };
  103. } else if (typeof Headers !== 'undefined' && utils.isInstanceOf(headers, Headers)) {
  104. const newHeaders = new Headers(headers );
  105. newHeaders.append('sentry-trace', sentryTraceHeader);
  106. if (sentryBaggageHeader) {
  107. // If the same header is appended multiple times the browser will merge the values into a single request header.
  108. // Its therefore safe to simply push a "baggage" entry, even though there might already be another baggage header.
  109. newHeaders.append(utils.BAGGAGE_HEADER_NAME, sentryBaggageHeader);
  110. }
  111. return newHeaders ;
  112. } else if (Array.isArray(headers)) {
  113. const newHeaders = [...headers, ['sentry-trace', sentryTraceHeader]];
  114. if (sentryBaggageHeader) {
  115. // If there are multiple entries with the same key, the browser will merge the values into a single request header.
  116. // Its therefore safe to simply push a "baggage" entry, even though there might already be another baggage header.
  117. newHeaders.push([utils.BAGGAGE_HEADER_NAME, sentryBaggageHeader]);
  118. }
  119. return newHeaders ;
  120. } else {
  121. const existingBaggageHeader = 'baggage' in headers ? headers.baggage : undefined;
  122. const newBaggageHeaders = [];
  123. if (Array.isArray(existingBaggageHeader)) {
  124. newBaggageHeaders.push(...existingBaggageHeader);
  125. } else if (existingBaggageHeader) {
  126. newBaggageHeaders.push(existingBaggageHeader);
  127. }
  128. if (sentryBaggageHeader) {
  129. newBaggageHeaders.push(sentryBaggageHeader);
  130. }
  131. return {
  132. ...(headers ),
  133. 'sentry-trace': sentryTraceHeader,
  134. baggage: newBaggageHeaders.length > 0 ? newBaggageHeaders.join(',') : undefined,
  135. };
  136. }
  137. }
  138. exports.addTracingHeadersToFetchRequest = addTracingHeadersToFetchRequest;
  139. exports.instrumentFetchRequest = instrumentFetchRequest;
  140. //# sourceMappingURL=fetch.js.map