313 lines
9.2 KiB
JavaScript
313 lines
9.2 KiB
JavaScript
var {
|
|
_optionalChain
|
|
} = require('@sentry/utils');
|
|
|
|
Object.defineProperty(exports, '__esModule', { value: true });
|
|
|
|
const core = require('@sentry/core');
|
|
const utils = require('@sentry/utils');
|
|
const nodeVersion = require('../../nodeVersion.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' });
|
|
// }
|
|
|
|
const _nativeNodeFetchintegration = ((options) => {
|
|
// eslint-disable-next-line deprecation/deprecation
|
|
return new Undici(options) ;
|
|
}) ;
|
|
|
|
const nativeNodeFetchintegration = core.defineIntegration(_nativeNodeFetchintegration);
|
|
|
|
/**
|
|
* 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.
|
|
*
|
|
* @deprecated Use `nativeNodeFetchintegration()` instead.
|
|
*/
|
|
class Undici {
|
|
/**
|
|
* @inheritDoc
|
|
*/
|
|
static __initStatic() {this.id = 'Undici';}
|
|
|
|
/**
|
|
* @inheritDoc
|
|
*/
|
|
// eslint-disable-next-line deprecation/deprecation
|
|
__init() {this.name = Undici.id;}
|
|
|
|
__init2() {this._createSpanUrlMap = new utils.LRUMap(100);}
|
|
__init3() {this._headersUrlMap = new utils.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,
|
|
tracing: _options.tracing,
|
|
shouldCreateSpanForRequest: _options.shouldCreateSpanForRequest,
|
|
};
|
|
}
|
|
|
|
/**
|
|
* @inheritDoc
|
|
*/
|
|
setupOnce(_addGlobalEventProcessor) {
|
|
// Requires Node 16+ to use the diagnostics_channel API.
|
|
if (nodeVersion.NODE_VERSION.major < 16) {
|
|
return;
|
|
}
|
|
|
|
let ds;
|
|
try {
|
|
// eslint-disable-next-line @typescript-eslint/no-var-requires
|
|
ds = require('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.tracing === false || (this._options.tracing === undefined && !core.hasTracingEnabled())) {
|
|
return false;
|
|
}
|
|
|
|
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) => {
|
|
// eslint-disable-next-line deprecation/deprecation
|
|
if (!_optionalChain([core.getClient, 'call', _10 => _10(), 'optionalAccess', _11 => _11.getIntegration, 'call', _12 => _12(Undici)])) {
|
|
return;
|
|
}
|
|
|
|
const { request } = message ;
|
|
|
|
const stringUrl = request.origin ? request.origin.toString() + request.path : request.path;
|
|
|
|
const client = core.getClient();
|
|
if (!client) {
|
|
return;
|
|
}
|
|
|
|
if (core.isSentryRequestUrl(stringUrl, client) || request.__sentry_span__ !== undefined) {
|
|
return;
|
|
}
|
|
|
|
const clientOptions = client.getOptions();
|
|
const scope = core.getCurrentScope();
|
|
const isolationScope = core.getIsolationScope();
|
|
const parentSpan = core.getActiveSpan();
|
|
|
|
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)) {
|
|
const { traceId, spanId, sampled, dsc } = {
|
|
...isolationScope.getPropagationContext(),
|
|
...scope.getPropagationContext(),
|
|
};
|
|
|
|
const sentryTraceHeader = span ? core.spanToTraceHeader(span) : utils.generateSentryTraceHeader(traceId, spanId, sampled);
|
|
|
|
const sentryBaggageHeader = utils.dynamicSamplingContextToSentryBaggageHeader(
|
|
dsc ||
|
|
(span
|
|
? core.getDynamicSamplingContextFromSpan(span)
|
|
: core.getDynamicSamplingContextFromClient(traceId, client, scope)),
|
|
);
|
|
|
|
setHeadersOnRequest(request, sentryTraceHeader, sentryBaggageHeader);
|
|
}
|
|
};}
|
|
|
|
__init5() {this._onRequestEnd = (message) => {
|
|
// eslint-disable-next-line deprecation/deprecation
|
|
if (!_optionalChain([core.getClient, 'call', _13 => _13(), 'optionalAccess', _14 => _14.getIntegration, 'call', _15 => _15(Undici)])) {
|
|
return;
|
|
}
|
|
|
|
const { request, response } = message ;
|
|
|
|
const stringUrl = request.origin ? request.origin.toString() + request.path : request.path;
|
|
|
|
if (core.isSentryRequestUrl(stringUrl, core.getClient())) {
|
|
return;
|
|
}
|
|
|
|
const span = request.__sentry_span__;
|
|
if (span) {
|
|
core.setHttpStatus(span, response.statusCode);
|
|
span.end();
|
|
}
|
|
|
|
if (this._options.breadcrumbs) {
|
|
core.addBreadcrumb(
|
|
{
|
|
category: 'http',
|
|
data: {
|
|
method: request.method,
|
|
status_code: response.statusCode,
|
|
url: stringUrl,
|
|
},
|
|
type: 'http',
|
|
},
|
|
{
|
|
event: 'response',
|
|
request,
|
|
response,
|
|
},
|
|
);
|
|
}
|
|
};}
|
|
|
|
__init6() {this._onRequestError = (message) => {
|
|
// eslint-disable-next-line deprecation/deprecation
|
|
if (!_optionalChain([core.getClient, 'call', _16 => _16(), 'optionalAccess', _17 => _17.getIntegration, 'call', _18 => _18(Undici)])) {
|
|
return;
|
|
}
|
|
|
|
const { request } = message ;
|
|
|
|
const stringUrl = request.origin ? request.origin.toString() + request.path : request.path;
|
|
|
|
if (core.isSentryRequestUrl(stringUrl, core.getClient())) {
|
|
return;
|
|
}
|
|
|
|
const span = request.__sentry_span__;
|
|
if (span) {
|
|
span.setStatus('internal_error');
|
|
span.end();
|
|
}
|
|
|
|
if (this._options.breadcrumbs) {
|
|
core.addBreadcrumb(
|
|
{
|
|
category: 'http',
|
|
data: {
|
|
method: request.method,
|
|
url: stringUrl,
|
|
},
|
|
level: 'error',
|
|
type: 'http',
|
|
},
|
|
{
|
|
event: 'error',
|
|
request,
|
|
},
|
|
);
|
|
}
|
|
};}
|
|
}Undici.__initStatic();
|
|
|
|
function setHeadersOnRequest(
|
|
request,
|
|
sentryTrace,
|
|
sentryBaggageHeader,
|
|
) {
|
|
let hasSentryHeaders;
|
|
if (Array.isArray(request.headers)) {
|
|
hasSentryHeaders = request.headers.some(headerLine => headerLine === 'sentry-trace');
|
|
} else {
|
|
const headerLines = request.headers.split('\r\n');
|
|
hasSentryHeaders = headerLines.some(headerLine => headerLine.startsWith('sentry-trace:'));
|
|
}
|
|
|
|
if (hasSentryHeaders) {
|
|
return;
|
|
}
|
|
|
|
request.addHeader('sentry-trace', sentryTrace);
|
|
if (sentryBaggageHeader) {
|
|
request.addHeader('baggage', sentryBaggageHeader);
|
|
}
|
|
}
|
|
|
|
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;
|
|
}
|
|
// eslint-disable-next-line deprecation/deprecation
|
|
return _optionalChain([activeSpan, 'optionalAccess', _19 => _19.startChild, 'call', _20 => _20({
|
|
op: 'http.client',
|
|
origin: 'auto.http.node.undici',
|
|
description: `${method} ${utils.getSanitizedUrlString(url)}`,
|
|
data,
|
|
})]);
|
|
}
|
|
|
|
exports.Undici = Undici;
|
|
exports.nativeNodeFetchintegration = nativeNodeFetchintegration;
|
|
//# sourceMappingURL=index.js.map
|