index.js 9.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307
  1. var {
  2. _optionalChain
  3. } = require('@sentry/utils');
  4. Object.defineProperty(exports, '__esModule', { value: true });
  5. const core = require('@sentry/core');
  6. const utils = require('@sentry/utils');
  7. const nodeVersion = require('../../nodeVersion.js');
  8. exports.ChannelName = void 0;(function (ChannelName) {
  9. // https://github.com/nodejs/undici/blob/e6fc80f809d1217814c044f52ed40ef13f21e43c/docs/api/DiagnosticsChannel.md#undicirequestcreate
  10. const RequestCreate = 'undici:request:create'; ChannelName["RequestCreate"] = RequestCreate;
  11. const RequestEnd = 'undici:request:headers'; ChannelName["RequestEnd"] = RequestEnd;
  12. const RequestError = 'undici:request:error'; ChannelName["RequestError"] = RequestError;
  13. })(exports.ChannelName || (exports.ChannelName = {}));
  14. // Please note that you cannot use `console.log` to debug the callbacks registered to the `diagnostics_channel` API.
  15. // To debug, you can use `writeFileSync` to write to a file:
  16. // https://nodejs.org/api/async_hooks.html#printing-in-asynchook-callbacks
  17. //
  18. // import { writeFileSync } from 'fs';
  19. // import { format } from 'util';
  20. //
  21. // function debug(...args: any): void {
  22. // // Use a function like this one when debugging inside an AsyncHook callback
  23. // // @ts-expect-error any
  24. // writeFileSync('log.out', `${format(...args)}\n`, { flag: 'a' });
  25. // }
  26. const _nativeNodeFetchintegration = ((options) => {
  27. // eslint-disable-next-line deprecation/deprecation
  28. return new Undici(options) ;
  29. }) ;
  30. const nativeNodeFetchintegration = core.defineIntegration(_nativeNodeFetchintegration);
  31. /**
  32. * Instruments outgoing HTTP requests made with the `undici` package via
  33. * Node's `diagnostics_channel` API.
  34. *
  35. * Supports Undici 4.7.0 or higher.
  36. *
  37. * Requires Node 16.17.0 or higher.
  38. *
  39. * @deprecated Use `nativeNodeFetchintegration()` instead.
  40. */
  41. class Undici {
  42. /**
  43. * @inheritDoc
  44. */
  45. static __initStatic() {this.id = 'Undici';}
  46. /**
  47. * @inheritDoc
  48. */
  49. // eslint-disable-next-line deprecation/deprecation
  50. __init() {this.name = Undici.id;}
  51. __init2() {this._createSpanUrlMap = new utils.LRUMap(100);}
  52. __init3() {this._headersUrlMap = new utils.LRUMap(100);}
  53. constructor(_options = {}) {Undici.prototype.__init.call(this);Undici.prototype.__init2.call(this);Undici.prototype.__init3.call(this);Undici.prototype.__init4.call(this);Undici.prototype.__init5.call(this);Undici.prototype.__init6.call(this);
  54. this._options = {
  55. breadcrumbs: _options.breadcrumbs === undefined ? true : _options.breadcrumbs,
  56. tracing: _options.tracing,
  57. shouldCreateSpanForRequest: _options.shouldCreateSpanForRequest,
  58. };
  59. }
  60. /**
  61. * @inheritDoc
  62. */
  63. setupOnce(_addGlobalEventProcessor) {
  64. // Requires Node 16+ to use the diagnostics_channel API.
  65. if (nodeVersion.NODE_VERSION.major < 16) {
  66. return;
  67. }
  68. let ds;
  69. try {
  70. // eslint-disable-next-line @typescript-eslint/no-var-requires
  71. ds = require('diagnostics_channel') ;
  72. } catch (e) {
  73. // no-op
  74. }
  75. if (!ds || !ds.subscribe) {
  76. return;
  77. }
  78. // https://github.com/nodejs/undici/blob/e6fc80f809d1217814c044f52ed40ef13f21e43c/docs/api/DiagnosticsChannel.md
  79. ds.subscribe(exports.ChannelName.RequestCreate, this._onRequestCreate);
  80. ds.subscribe(exports.ChannelName.RequestEnd, this._onRequestEnd);
  81. ds.subscribe(exports.ChannelName.RequestError, this._onRequestError);
  82. }
  83. /** Helper that wraps shouldCreateSpanForRequest option */
  84. _shouldCreateSpan(url) {
  85. if (this._options.tracing === false || (this._options.tracing === undefined && !core.hasTracingEnabled())) {
  86. return false;
  87. }
  88. if (this._options.shouldCreateSpanForRequest === undefined) {
  89. return true;
  90. }
  91. const cachedDecision = this._createSpanUrlMap.get(url);
  92. if (cachedDecision !== undefined) {
  93. return cachedDecision;
  94. }
  95. const decision = this._options.shouldCreateSpanForRequest(url);
  96. this._createSpanUrlMap.set(url, decision);
  97. return decision;
  98. }
  99. __init4() {this._onRequestCreate = (message) => {
  100. // eslint-disable-next-line deprecation/deprecation
  101. if (!_optionalChain([core.getClient, 'call', _10 => _10(), 'optionalAccess', _11 => _11.getIntegration, 'call', _12 => _12(Undici)])) {
  102. return;
  103. }
  104. const { request } = message ;
  105. const stringUrl = request.origin ? request.origin.toString() + request.path : request.path;
  106. const client = core.getClient();
  107. if (!client) {
  108. return;
  109. }
  110. if (core.isSentryRequestUrl(stringUrl, client) || request.__sentry_span__ !== undefined) {
  111. return;
  112. }
  113. const clientOptions = client.getOptions();
  114. const scope = core.getCurrentScope();
  115. const isolationScope = core.getIsolationScope();
  116. const parentSpan = core.getActiveSpan();
  117. const span = this._shouldCreateSpan(stringUrl) ? createRequestSpan(parentSpan, request, stringUrl) : undefined;
  118. if (span) {
  119. request.__sentry_span__ = span;
  120. }
  121. const shouldAttachTraceData = (url) => {
  122. if (clientOptions.tracePropagationTargets === undefined) {
  123. return true;
  124. }
  125. const cachedDecision = this._headersUrlMap.get(url);
  126. if (cachedDecision !== undefined) {
  127. return cachedDecision;
  128. }
  129. const decision = utils.stringMatchesSomePattern(url, clientOptions.tracePropagationTargets);
  130. this._headersUrlMap.set(url, decision);
  131. return decision;
  132. };
  133. if (shouldAttachTraceData(stringUrl)) {
  134. const { traceId, spanId, sampled, dsc } = {
  135. ...isolationScope.getPropagationContext(),
  136. ...scope.getPropagationContext(),
  137. };
  138. const sentryTraceHeader = span ? core.spanToTraceHeader(span) : utils.generateSentryTraceHeader(traceId, spanId, sampled);
  139. const sentryBaggageHeader = utils.dynamicSamplingContextToSentryBaggageHeader(
  140. dsc ||
  141. (span
  142. ? core.getDynamicSamplingContextFromSpan(span)
  143. : core.getDynamicSamplingContextFromClient(traceId, client, scope)),
  144. );
  145. setHeadersOnRequest(request, sentryTraceHeader, sentryBaggageHeader);
  146. }
  147. };}
  148. __init5() {this._onRequestEnd = (message) => {
  149. // eslint-disable-next-line deprecation/deprecation
  150. if (!_optionalChain([core.getClient, 'call', _13 => _13(), 'optionalAccess', _14 => _14.getIntegration, 'call', _15 => _15(Undici)])) {
  151. return;
  152. }
  153. const { request, response } = message ;
  154. const stringUrl = request.origin ? request.origin.toString() + request.path : request.path;
  155. if (core.isSentryRequestUrl(stringUrl, core.getClient())) {
  156. return;
  157. }
  158. const span = request.__sentry_span__;
  159. if (span) {
  160. core.setHttpStatus(span, response.statusCode);
  161. span.end();
  162. }
  163. if (this._options.breadcrumbs) {
  164. core.addBreadcrumb(
  165. {
  166. category: 'http',
  167. data: {
  168. method: request.method,
  169. status_code: response.statusCode,
  170. url: stringUrl,
  171. },
  172. type: 'http',
  173. },
  174. {
  175. event: 'response',
  176. request,
  177. response,
  178. },
  179. );
  180. }
  181. };}
  182. __init6() {this._onRequestError = (message) => {
  183. // eslint-disable-next-line deprecation/deprecation
  184. if (!_optionalChain([core.getClient, 'call', _16 => _16(), 'optionalAccess', _17 => _17.getIntegration, 'call', _18 => _18(Undici)])) {
  185. return;
  186. }
  187. const { request } = message ;
  188. const stringUrl = request.origin ? request.origin.toString() + request.path : request.path;
  189. if (core.isSentryRequestUrl(stringUrl, core.getClient())) {
  190. return;
  191. }
  192. const span = request.__sentry_span__;
  193. if (span) {
  194. span.setStatus('internal_error');
  195. span.end();
  196. }
  197. if (this._options.breadcrumbs) {
  198. core.addBreadcrumb(
  199. {
  200. category: 'http',
  201. data: {
  202. method: request.method,
  203. url: stringUrl,
  204. },
  205. level: 'error',
  206. type: 'http',
  207. },
  208. {
  209. event: 'error',
  210. request,
  211. },
  212. );
  213. }
  214. };}
  215. }Undici.__initStatic();
  216. function setHeadersOnRequest(
  217. request,
  218. sentryTrace,
  219. sentryBaggageHeader,
  220. ) {
  221. const headerLines = request.headers.split('\r\n');
  222. const hasSentryHeaders = headerLines.some(headerLine => headerLine.startsWith('sentry-trace:'));
  223. if (hasSentryHeaders) {
  224. return;
  225. }
  226. request.addHeader('sentry-trace', sentryTrace);
  227. if (sentryBaggageHeader) {
  228. request.addHeader('baggage', sentryBaggageHeader);
  229. }
  230. }
  231. function createRequestSpan(
  232. activeSpan,
  233. request,
  234. stringUrl,
  235. ) {
  236. const url = utils.parseUrl(stringUrl);
  237. const method = request.method || 'GET';
  238. const data = {
  239. 'http.method': method,
  240. };
  241. if (url.search) {
  242. data['http.query'] = url.search;
  243. }
  244. if (url.hash) {
  245. data['http.fragment'] = url.hash;
  246. }
  247. // eslint-disable-next-line deprecation/deprecation
  248. return _optionalChain([activeSpan, 'optionalAccess', _19 => _19.startChild, 'call', _20 => _20({
  249. op: 'http.client',
  250. origin: 'auto.http.node.undici',
  251. description: `${method} ${utils.getSanitizedUrlString(url)}`,
  252. data,
  253. })]);
  254. }
  255. exports.Undici = Undici;
  256. exports.nativeNodeFetchintegration = nativeNodeFetchintegration;
  257. //# sourceMappingURL=index.js.map