rittenhop-dev/versions/5.94.2/node_modules/@sentry/node/esm/integrations/http.js

382 lines
13 KiB
JavaScript
Raw Normal View History

2024-09-23 19:40:12 -04:00
import { _optionalChain } from '@sentry/utils';
import { defineIntegration, getClient, isSentryRequestUrl, getCurrentScope, getIsolationScope, getActiveSpan, spanToTraceHeader, getDynamicSamplingContextFromSpan, getDynamicSamplingContextFromClient, setHttpStatus, spanToJSON, hasTracingEnabled, getCurrentHub, addBreadcrumb } from '@sentry/core';
import { dropUndefinedKeys, logger, fill, LRUMap, generateSentryTraceHeader, dynamicSamplingContextToSentryBaggageHeader, stringMatchesSomePattern } from '@sentry/utils';
import { DEBUG_BUILD } from '../debug-build.js';
import { NODE_VERSION } from '../nodeVersion.js';
import { normalizeRequestArgs, extractRawUrl, extractUrl, cleanSpanDescription } from './utils/http.js';
const _httpIntegration = ((options = {}) => {
const { breadcrumbs, tracing, shouldCreateSpanForRequest } = options;
const convertedOptions = {
breadcrumbs,
tracing:
tracing === false
? false
: dropUndefinedKeys({
// If tracing is forced to `true`, we don't want to set `enableIfHasTracingEnabled`
enableIfHasTracingEnabled: tracing === true ? undefined : true,
shouldCreateSpanForRequest,
}),
};
// eslint-disable-next-line deprecation/deprecation
return new Http(convertedOptions) ;
}) ;
/**
* The http module integration instruments Node's internal http module. It creates breadcrumbs, spans for outgoing
* http requests, and attaches trace data when tracing is enabled via its `tracing` option.
*
* By default, this will always create breadcrumbs, and will create spans if tracing is enabled.
*/
const httpIntegration = defineIntegration(_httpIntegration);
/**
* The http module integration instruments Node's internal http module. It creates breadcrumbs, transactions for outgoing
* http requests and attaches trace data when tracing is enabled via its `tracing` option.
*
* @deprecated Use `httpIntegration()` instead.
*/
class Http {
/**
* @inheritDoc
*/
static __initStatic() {this.id = 'Http';}
/**
* @inheritDoc
*/
// eslint-disable-next-line deprecation/deprecation
__init() {this.name = Http.id;}
/**
* @inheritDoc
*/
constructor(options = {}) {Http.prototype.__init.call(this);
this._breadcrumbs = typeof options.breadcrumbs === 'undefined' ? true : options.breadcrumbs;
this._tracing = !options.tracing ? undefined : options.tracing === true ? {} : options.tracing;
}
/**
* @inheritDoc
*/
setupOnce(
_addGlobalEventProcessor,
// eslint-disable-next-line deprecation/deprecation
setupOnceGetCurrentHub,
) {
// eslint-disable-next-line deprecation/deprecation
const clientOptions = _optionalChain([setupOnceGetCurrentHub, 'call', _ => _(), 'access', _2 => _2.getClient, 'call', _3 => _3(), 'optionalAccess', _4 => _4.getOptions, 'call', _5 => _5()]);
// If `tracing` is not explicitly set, we default this based on whether or not tracing is enabled.
// But for compatibility, we only do that if `enableIfHasTracingEnabled` is set.
const shouldCreateSpans = _shouldCreateSpans(this._tracing, clientOptions);
// No need to instrument if we don't want to track anything
if (!this._breadcrumbs && !shouldCreateSpans) {
return;
}
// Do not auto-instrument for other instrumenter
if (clientOptions && clientOptions.instrumenter !== 'sentry') {
DEBUG_BUILD && logger.log('HTTP Integration is skipped because of instrumenter configuration.');
return;
}
const shouldCreateSpanForRequest = _getShouldCreateSpanForRequest(shouldCreateSpans, this._tracing, clientOptions);
// eslint-disable-next-line deprecation/deprecation
const tracePropagationTargets = _optionalChain([clientOptions, 'optionalAccess', _6 => _6.tracePropagationTargets]) || _optionalChain([this, 'access', _7 => _7._tracing, 'optionalAccess', _8 => _8.tracePropagationTargets]);
// eslint-disable-next-line @typescript-eslint/no-var-requires
const httpModule = require('http');
const wrappedHttpHandlerMaker = _createWrappedRequestMethodFactory(
httpModule,
this._breadcrumbs,
shouldCreateSpanForRequest,
tracePropagationTargets,
);
fill(httpModule, 'get', wrappedHttpHandlerMaker);
fill(httpModule, 'request', wrappedHttpHandlerMaker);
// NOTE: Prior to Node 9, `https` used internals of `http` module, thus we don't patch it.
// If we do, we'd get double breadcrumbs and double spans for `https` calls.
// It has been changed in Node 9, so for all versions equal and above, we patch `https` separately.
if (NODE_VERSION.major > 8) {
// eslint-disable-next-line @typescript-eslint/no-var-requires
const httpsModule = require('https');
const wrappedHttpsHandlerMaker = _createWrappedRequestMethodFactory(
httpsModule,
this._breadcrumbs,
shouldCreateSpanForRequest,
tracePropagationTargets,
);
fill(httpsModule, 'get', wrappedHttpsHandlerMaker);
fill(httpsModule, 'request', wrappedHttpsHandlerMaker);
}
}
}Http.__initStatic();
// for ease of reading below
/**
* Function which creates a function which creates wrapped versions of internal `request` and `get` calls within `http`
* and `https` modules. (NB: Not a typo - this is a creator^2!)
*
* @param breadcrumbsEnabled Whether or not to record outgoing requests as breadcrumbs
* @param tracingEnabled Whether or not to record outgoing requests as tracing spans
*
* @returns A function which accepts the exiting handler and returns a wrapped handler
*/
function _createWrappedRequestMethodFactory(
httpModule,
breadcrumbsEnabled,
shouldCreateSpanForRequest,
tracePropagationTargets,
) {
// We're caching results so we don't have to recompute regexp every time we create a request.
const createSpanUrlMap = new LRUMap(100);
const headersUrlMap = new LRUMap(100);
const shouldCreateSpan = (url) => {
if (shouldCreateSpanForRequest === undefined) {
return true;
}
const cachedDecision = createSpanUrlMap.get(url);
if (cachedDecision !== undefined) {
return cachedDecision;
}
const decision = shouldCreateSpanForRequest(url);
createSpanUrlMap.set(url, decision);
return decision;
};
const shouldAttachTraceData = (url) => {
if (tracePropagationTargets === undefined) {
return true;
}
const cachedDecision = headersUrlMap.get(url);
if (cachedDecision !== undefined) {
return cachedDecision;
}
const decision = stringMatchesSomePattern(url, tracePropagationTargets);
headersUrlMap.set(url, decision);
return decision;
};
/**
* Captures Breadcrumb based on provided request/response pair
*/
function addRequestBreadcrumb(
event,
requestSpanData,
req,
res,
) {
// eslint-disable-next-line deprecation/deprecation
if (!getCurrentHub().getIntegration(Http)) {
return;
}
addBreadcrumb(
{
category: 'http',
data: {
status_code: res && res.statusCode,
...requestSpanData,
},
type: 'http',
},
{
event,
request: req,
response: res,
},
);
}
return function wrappedRequestMethodFactory(originalRequestMethod) {
return function wrappedMethod( ...args) {
const requestArgs = normalizeRequestArgs(httpModule, args);
const requestOptions = requestArgs[0];
// eslint-disable-next-line deprecation/deprecation
const rawRequestUrl = extractRawUrl(requestOptions);
const requestUrl = extractUrl(requestOptions);
const client = getClient();
// we don't want to record requests to Sentry as either breadcrumbs or spans, so just use the original method
if (isSentryRequestUrl(requestUrl, client)) {
return originalRequestMethod.apply(httpModule, requestArgs);
}
const scope = getCurrentScope();
const isolationScope = getIsolationScope();
const parentSpan = getActiveSpan();
const data = getRequestSpanData(requestUrl, requestOptions);
const requestSpan = shouldCreateSpan(rawRequestUrl)
? // eslint-disable-next-line deprecation/deprecation
_optionalChain([parentSpan, 'optionalAccess', _9 => _9.startChild, 'call', _10 => _10({
op: 'http.client',
origin: 'auto.http.node.http',
description: `${data['http.method']} ${data.url}`,
data,
})])
: undefined;
if (client && shouldAttachTraceData(rawRequestUrl)) {
const { traceId, spanId, sampled, dsc } = {
...isolationScope.getPropagationContext(),
...scope.getPropagationContext(),
};
const sentryTraceHeader = requestSpan
? spanToTraceHeader(requestSpan)
: generateSentryTraceHeader(traceId, spanId, sampled);
const sentryBaggageHeader = dynamicSamplingContextToSentryBaggageHeader(
dsc ||
(requestSpan
? getDynamicSamplingContextFromSpan(requestSpan)
: getDynamicSamplingContextFromClient(traceId, client, scope)),
);
addHeadersToRequestOptions(requestOptions, requestUrl, sentryTraceHeader, sentryBaggageHeader);
} else {
DEBUG_BUILD &&
logger.log(
`[Tracing] Not adding sentry-trace header to outgoing request (${requestUrl}) due to mismatching tracePropagationTargets option.`,
);
}
// eslint-disable-next-line @typescript-eslint/no-unsafe-member-access
return originalRequestMethod
.apply(httpModule, requestArgs)
.once('response', function ( res) {
// eslint-disable-next-line @typescript-eslint/no-this-alias
const req = this;
if (breadcrumbsEnabled) {
addRequestBreadcrumb('response', data, req, res);
}
if (requestSpan) {
if (res.statusCode) {
setHttpStatus(requestSpan, res.statusCode);
}
requestSpan.updateName(
cleanSpanDescription(spanToJSON(requestSpan).description || '', requestOptions, req) || '',
);
requestSpan.end();
}
})
.once('error', function () {
// eslint-disable-next-line @typescript-eslint/no-this-alias
const req = this;
if (breadcrumbsEnabled) {
addRequestBreadcrumb('error', data, req);
}
if (requestSpan) {
setHttpStatus(requestSpan, 500);
requestSpan.updateName(
cleanSpanDescription(spanToJSON(requestSpan).description || '', requestOptions, req) || '',
);
requestSpan.end();
}
});
};
};
}
function addHeadersToRequestOptions(
requestOptions,
requestUrl,
sentryTraceHeader,
sentryBaggageHeader,
) {
// Don't overwrite sentry-trace and baggage header if it's already set.
const headers = requestOptions.headers || {};
if (headers['sentry-trace']) {
return;
}
DEBUG_BUILD &&
logger.log(`[Tracing] Adding sentry-trace header ${sentryTraceHeader} to outgoing request to "${requestUrl}": `);
requestOptions.headers = {
...requestOptions.headers,
'sentry-trace': sentryTraceHeader,
// Setting a header to `undefined` will crash in node so we only set the baggage header when it's defined
...(sentryBaggageHeader &&
sentryBaggageHeader.length > 0 && { baggage: normalizeBaggageHeader(requestOptions, sentryBaggageHeader) }),
};
}
function getRequestSpanData(requestUrl, requestOptions) {
const method = requestOptions.method || 'GET';
const data = {
url: requestUrl,
'http.method': method,
};
if (requestOptions.hash) {
// strip leading "#"
data['http.fragment'] = requestOptions.hash.substring(1);
}
if (requestOptions.search) {
// strip leading "?"
data['http.query'] = requestOptions.search.substring(1);
}
return data;
}
function normalizeBaggageHeader(
requestOptions,
sentryBaggageHeader,
) {
if (!requestOptions.headers || !requestOptions.headers.baggage) {
return sentryBaggageHeader;
} else if (!sentryBaggageHeader) {
return requestOptions.headers.baggage ;
} else if (Array.isArray(requestOptions.headers.baggage)) {
return [...requestOptions.headers.baggage, sentryBaggageHeader];
}
// Type-cast explanation:
// Technically this the following could be of type `(number | string)[]` but for the sake of simplicity
// we say this is undefined behaviour, since it would not be baggage spec conform if the user did this.
return [requestOptions.headers.baggage, sentryBaggageHeader] ;
}
/** Exported for tests only. */
function _shouldCreateSpans(
tracingOptions,
clientOptions,
) {
return tracingOptions === undefined
? false
: tracingOptions.enableIfHasTracingEnabled
? hasTracingEnabled(clientOptions)
: true;
}
/** Exported for tests only. */
function _getShouldCreateSpanForRequest(
shouldCreateSpans,
tracingOptions,
clientOptions,
) {
const handler = shouldCreateSpans
? // eslint-disable-next-line deprecation/deprecation
_optionalChain([tracingOptions, 'optionalAccess', _11 => _11.shouldCreateSpanForRequest]) || _optionalChain([clientOptions, 'optionalAccess', _12 => _12.shouldCreateSpanForRequest])
: () => false;
return handler;
}
export { Http, _getShouldCreateSpanForRequest, _shouldCreateSpans, httpIntegration };
//# sourceMappingURL=http.js.map