UNPKG

@sentry/node

Version:
289 lines (242 loc) 8.44 kB
var { _optionalChain } = require('@sentry/utils/cjs/buildPolyfills'); Object.defineProperty(exports, '__esModule', { value: true }); const core = require('@sentry/core'); const utils = require('@sentry/utils'); const lru_map = require('lru_map'); const nodeVersion = require('../../nodeVersion.js'); const http = require('../utils/http.js'); exports.ChannelName = void 0;(function (ChannelName) { // https://github.com/nodejs/undici/blob/e6fc80f809d1217814c044f52ed40ef13f21e43c/docs/api/DiagnosticsChannel.md#undicirequestcreate const RequestCreate = 'undici:request:create'; ChannelName["RequestCreate"] = RequestCreate; const RequestEnd = 'undici:request:headers'; ChannelName["RequestEnd"] = RequestEnd; const RequestError = 'undici:request:error'; ChannelName["RequestError"] = RequestError; })(exports.ChannelName || (exports.ChannelName = {})); // Please note that you cannot use `console.log` to debug the callbacks registered to the `diagnostics_channel` API. // To debug, you can use `writeFileSync` to write to a file: // https://nodejs.org/api/async_hooks.html#printing-in-asynchook-callbacks // // import { writeFileSync } from 'fs'; // import { format } from 'util'; // // function debug(...args: any): void { // // Use a function like this one when debugging inside an AsyncHook callback // // @ts-expect-error any // writeFileSync('log.out', `${format(...args)}\n`, { flag: 'a' }); // } /** * Instruments outgoing HTTP requests made with the `undici` package via * Node's `diagnostics_channel` API. * * Supports Undici 4.7.0 or higher. * * Requires Node 16.17.0 or higher. */ class Undici { /** * @inheritDoc */ static __initStatic() {this.id = 'Undici';} /** * @inheritDoc */ __init() {this.name = Undici.id;} __init2() {this._createSpanUrlMap = new lru_map.LRUMap(100);} __init3() {this._headersUrlMap = new lru_map.LRUMap(100);} 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); this._options = { breadcrumbs: _options.breadcrumbs === undefined ? true : _options.breadcrumbs, shouldCreateSpanForRequest: _options.shouldCreateSpanForRequest, }; } /** * @inheritDoc */ setupOnce(_addGlobalEventProcessor) { // Requires Node 16+ to use the diagnostics_channel API. if (nodeVersion.NODE_VERSION.major && nodeVersion.NODE_VERSION.major < 16) { return; } let ds; try { // eslint-disable-next-line @typescript-eslint/no-var-requires ds = utils.dynamicRequire(module, 'diagnostics_channel') ; } catch (e) { // no-op } if (!ds || !ds.subscribe) { return; } // https://github.com/nodejs/undici/blob/e6fc80f809d1217814c044f52ed40ef13f21e43c/docs/api/DiagnosticsChannel.md ds.subscribe(exports.ChannelName.RequestCreate, this._onRequestCreate); ds.subscribe(exports.ChannelName.RequestEnd, this._onRequestEnd); ds.subscribe(exports.ChannelName.RequestError, this._onRequestError); } /** Helper that wraps shouldCreateSpanForRequest option */ _shouldCreateSpan(url) { if (this._options.shouldCreateSpanForRequest === undefined) { return true; } const cachedDecision = this._createSpanUrlMap.get(url); if (cachedDecision !== undefined) { return cachedDecision; } const decision = this._options.shouldCreateSpanForRequest(url); this._createSpanUrlMap.set(url, decision); return decision; } __init4() {this._onRequestCreate = (message) => { const hub = core.getCurrentHub(); if (!hub.getIntegration(Undici)) { return; } const { request } = message ; const stringUrl = request.origin ? request.origin.toString() + request.path : request.path; if (http.isSentryRequest(stringUrl) || request.__sentry_span__ !== undefined) { return; } const client = hub.getClient(); if (!client) { return; } const clientOptions = client.getOptions(); const scope = hub.getScope(); const parentSpan = scope.getSpan(); const span = this._shouldCreateSpan(stringUrl) ? createRequestSpan(parentSpan, request, stringUrl) : undefined; if (span) { request.__sentry_span__ = span; } const shouldAttachTraceData = (url) => { if (clientOptions.tracePropagationTargets === undefined) { return true; } const cachedDecision = this._headersUrlMap.get(url); if (cachedDecision !== undefined) { return cachedDecision; } const decision = utils.stringMatchesSomePattern(url, clientOptions.tracePropagationTargets); this._headersUrlMap.set(url, decision); return decision; }; if (shouldAttachTraceData(stringUrl)) { if (span) { const dynamicSamplingContext = _optionalChain([span, 'optionalAccess', _4 => _4.transaction, 'optionalAccess', _5 => _5.getDynamicSamplingContext, 'call', _6 => _6()]); const sentryBaggageHeader = utils.dynamicSamplingContextToSentryBaggageHeader(dynamicSamplingContext); setHeadersOnRequest(request, span.toTraceparent(), sentryBaggageHeader); } else { const { traceId, sampled, dsc } = scope.getPropagationContext(); const sentryTrace = utils.generateSentryTraceHeader(traceId, undefined, sampled); const dynamicSamplingContext = dsc || core.getDynamicSamplingContextFromClient(traceId, client, scope); const sentryBaggageHeader = utils.dynamicSamplingContextToSentryBaggageHeader(dynamicSamplingContext); setHeadersOnRequest(request, sentryTrace, sentryBaggageHeader); } } };} __init5() {this._onRequestEnd = (message) => { const hub = core.getCurrentHub(); if (!hub.getIntegration(Undici)) { return; } const { request, response } = message ; const stringUrl = request.origin ? request.origin.toString() + request.path : request.path; if (http.isSentryRequest(stringUrl)) { return; } const span = request.__sentry_span__; if (span) { span.setHttpStatus(response.statusCode); span.finish(); } if (this._options.breadcrumbs) { hub.addBreadcrumb( { category: 'http', data: { method: request.method, status_code: response.statusCode, url: stringUrl, }, type: 'http', }, { event: 'response', request, response, }, ); } };} __init6() {this._onRequestError = (message) => { const hub = core.getCurrentHub(); if (!hub.getIntegration(Undici)) { return; } const { request } = message ; const stringUrl = request.origin ? request.origin.toString() + request.path : request.path; if (http.isSentryRequest(stringUrl)) { return; } const span = request.__sentry_span__; if (span) { span.setStatus('internal_error'); span.finish(); } if (this._options.breadcrumbs) { hub.addBreadcrumb( { category: 'http', data: { method: request.method, url: stringUrl, }, level: 'error', type: 'http', }, { event: 'error', request, }, ); } };} }Undici.__initStatic(); function setHeadersOnRequest( request, sentryTrace, sentryBaggageHeader, ) { if (request.__sentry_has_headers__) { return; } request.addHeader('sentry-trace', sentryTrace); if (sentryBaggageHeader) { request.addHeader('baggage', sentryBaggageHeader); } request.__sentry_has_headers__ = true; } function createRequestSpan( activeSpan, request, stringUrl, ) { const url = utils.parseUrl(stringUrl); const method = request.method || 'GET'; const data = { 'http.method': method, }; if (url.search) { data['http.query'] = url.search; } if (url.hash) { data['http.fragment'] = url.hash; } return _optionalChain([activeSpan, 'optionalAccess', _7 => _7.startChild, 'call', _8 => _8({ op: 'http.client', origin: 'auto.http.node.undici', description: `${method} ${utils.getSanitizedUrlString(url)}`, data, })]); } exports.Undici = Undici; //# sourceMappingURL=index.js.map