123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339 |
- import { TRACING_DEFAULTS, addTracingExtensions, startIdleTransaction, SEMANTIC_ATTRIBUTE_SENTRY_SOURCE, getActiveTransaction } from '@sentry/core';
- import { logger, propagationContextFromHeaders, getDomElement } from '@sentry/utils';
- import { DEBUG_BUILD } from '../common/debug-build.js';
- import { registerBackgroundTabDetection } from './backgroundtab.js';
- import { startTrackingWebVitals, startTrackingLongTasks, startTrackingInteractions, addPerformanceEntries } from './metrics/index.js';
- import { defaultRequestInstrumentationOptions, instrumentOutgoingRequests } from './request.js';
- import { instrumentRoutingWithDefaults } from './router.js';
- import { WINDOW } from './types.js';
- const BROWSER_TRACING_INTEGRATION_ID = 'BrowserTracing';
- /** Options for Browser Tracing integration */
- const DEFAULT_BROWSER_TRACING_OPTIONS = {
- ...TRACING_DEFAULTS,
- markBackgroundTransactions: true,
- routingInstrumentation: instrumentRoutingWithDefaults,
- startTransactionOnLocationChange: true,
- startTransactionOnPageLoad: true,
- enableLongTask: true,
- _experiments: {},
- ...defaultRequestInstrumentationOptions,
- };
- /**
- * The Browser Tracing integration automatically instruments browser pageload/navigation
- * actions as transactions, and captures requests, metrics and errors as spans.
- *
- * The integration can be configured with a variety of options, and can be extended to use
- * any routing library. This integration uses {@see IdleTransaction} to create transactions.
- *
- * @deprecated Use `browserTracingIntegration()` instead.
- */
- class BrowserTracing {
- // This class currently doesn't have a static `id` field like the other integration classes, because it prevented
- // @sentry/tracing from being treeshaken. Tree shakers do not like static fields, because they behave like side effects.
- // TODO: Come up with a better plan, than using static fields on integration classes, and use that plan on all
- // integrations.
- /** Browser Tracing integration options */
- /**
- * @inheritDoc
- */
- constructor(_options) {
- this.name = BROWSER_TRACING_INTEGRATION_ID;
- this._hasSetTracePropagationTargets = false;
- addTracingExtensions();
- if (DEBUG_BUILD) {
- this._hasSetTracePropagationTargets = !!(
- _options &&
- // eslint-disable-next-line deprecation/deprecation
- (_options.tracePropagationTargets || _options.tracingOrigins)
- );
- }
- this.options = {
- ...DEFAULT_BROWSER_TRACING_OPTIONS,
- ..._options,
- };
- // Special case: enableLongTask can be set in _experiments
- // TODO (v8): Remove this in v8
- if (this.options._experiments.enableLongTask !== undefined) {
- this.options.enableLongTask = this.options._experiments.enableLongTask;
- }
- // TODO (v8): remove this block after tracingOrigins is removed
- // Set tracePropagationTargets to tracingOrigins if specified by the user
- // In case both are specified, tracePropagationTargets takes precedence
- // eslint-disable-next-line deprecation/deprecation
- if (_options && !_options.tracePropagationTargets && _options.tracingOrigins) {
- // eslint-disable-next-line deprecation/deprecation
- this.options.tracePropagationTargets = _options.tracingOrigins;
- }
- this._collectWebVitals = startTrackingWebVitals();
- if (this.options.enableLongTask) {
- startTrackingLongTasks();
- }
- if (this.options._experiments.enableInteractions) {
- startTrackingInteractions();
- }
- }
- /**
- * @inheritDoc
- */
- setupOnce(_, getCurrentHub) {
- this._getCurrentHub = getCurrentHub;
- const hub = getCurrentHub();
- // eslint-disable-next-line deprecation/deprecation
- const client = hub.getClient();
- const clientOptions = client && client.getOptions();
- const {
- routingInstrumentation: instrumentRouting,
- startTransactionOnLocationChange,
- startTransactionOnPageLoad,
- markBackgroundTransactions,
- traceFetch,
- traceXHR,
- shouldCreateSpanForRequest,
- enableHTTPTimings,
- _experiments,
- } = this.options;
- const clientOptionsTracePropagationTargets = clientOptions && clientOptions.tracePropagationTargets;
- // There are three ways to configure tracePropagationTargets:
- // 1. via top level client option `tracePropagationTargets`
- // 2. via BrowserTracing option `tracePropagationTargets`
- // 3. via BrowserTracing option `tracingOrigins` (deprecated)
- //
- // To avoid confusion, favour top level client option `tracePropagationTargets`, and fallback to
- // BrowserTracing option `tracePropagationTargets` and then `tracingOrigins` (deprecated).
- // This is done as it minimizes bundle size (we don't have to have undefined checks).
- //
- // If both 1 and either one of 2 or 3 are set (from above), we log out a warning.
- // eslint-disable-next-line deprecation/deprecation
- const tracePropagationTargets = clientOptionsTracePropagationTargets || this.options.tracePropagationTargets;
- if (DEBUG_BUILD && this._hasSetTracePropagationTargets && clientOptionsTracePropagationTargets) {
- logger.warn(
- '[Tracing] The `tracePropagationTargets` option was set in the BrowserTracing integration and top level `Sentry.init`. The top level `Sentry.init` value is being used.',
- );
- }
- instrumentRouting(
- (context) => {
- const transaction = this._createRouteTransaction(context);
- this.options._experiments.onStartRouteTransaction &&
- this.options._experiments.onStartRouteTransaction(transaction, context, getCurrentHub);
- return transaction;
- },
- startTransactionOnPageLoad,
- startTransactionOnLocationChange,
- );
- if (markBackgroundTransactions) {
- registerBackgroundTabDetection();
- }
- if (_experiments.enableInteractions) {
- this._registerInteractionListener();
- }
- instrumentOutgoingRequests({
- traceFetch,
- traceXHR,
- tracePropagationTargets,
- shouldCreateSpanForRequest,
- enableHTTPTimings,
- });
- }
- /** Create routing idle transaction. */
- _createRouteTransaction(context) {
- if (!this._getCurrentHub) {
- DEBUG_BUILD &&
- logger.warn(`[Tracing] Did not create ${context.op} transaction because _getCurrentHub is invalid.`);
- return undefined;
- }
- const hub = this._getCurrentHub();
- const { beforeNavigate, idleTimeout, finalTimeout, heartbeatInterval } = this.options;
- const isPageloadTransaction = context.op === 'pageload';
- let expandedContext;
- if (isPageloadTransaction) {
- const sentryTrace = isPageloadTransaction ? getMetaContent('sentry-trace') : '';
- const baggage = isPageloadTransaction ? getMetaContent('baggage') : undefined;
- const { traceId, dsc, parentSpanId, sampled } = propagationContextFromHeaders(sentryTrace, baggage);
- expandedContext = {
- traceId,
- parentSpanId,
- parentSampled: sampled,
- ...context,
- metadata: {
- // eslint-disable-next-line deprecation/deprecation
- ...context.metadata,
- dynamicSamplingContext: dsc,
- },
- trimEnd: true,
- };
- } else {
- expandedContext = {
- trimEnd: true,
- ...context,
- };
- }
- const modifiedContext = typeof beforeNavigate === 'function' ? beforeNavigate(expandedContext) : expandedContext;
- // For backwards compatibility reasons, beforeNavigate can return undefined to "drop" the transaction (prevent it
- // from being sent to Sentry).
- const finalContext = modifiedContext === undefined ? { ...expandedContext, sampled: false } : modifiedContext;
- // If `beforeNavigate` set a custom name, record that fact
- // eslint-disable-next-line deprecation/deprecation
- finalContext.metadata =
- finalContext.name !== expandedContext.name
- ? // eslint-disable-next-line deprecation/deprecation
- { ...finalContext.metadata, source: 'custom' }
- : // eslint-disable-next-line deprecation/deprecation
- finalContext.metadata;
- this._latestRouteName = finalContext.name;
- this._latestRouteSource = getSource(finalContext);
- // eslint-disable-next-line deprecation/deprecation
- if (finalContext.sampled === false) {
- DEBUG_BUILD && logger.log(`[Tracing] Will not send ${finalContext.op} transaction because of beforeNavigate.`);
- }
- DEBUG_BUILD && logger.log(`[Tracing] Starting ${finalContext.op} transaction on scope`);
- const { location } = WINDOW;
- const idleTransaction = startIdleTransaction(
- hub,
- finalContext,
- idleTimeout,
- finalTimeout,
- true,
- { location }, // for use in the tracesSampler
- heartbeatInterval,
- isPageloadTransaction, // should wait for finish signal if it's a pageload transaction
- );
- if (isPageloadTransaction) {
- WINDOW.document.addEventListener('readystatechange', () => {
- if (['interactive', 'complete'].includes(WINDOW.document.readyState)) {
- idleTransaction.sendAutoFinishSignal();
- }
- });
- if (['interactive', 'complete'].includes(WINDOW.document.readyState)) {
- idleTransaction.sendAutoFinishSignal();
- }
- }
- idleTransaction.registerBeforeFinishCallback(transaction => {
- this._collectWebVitals();
- addPerformanceEntries(transaction);
- });
- return idleTransaction ;
- }
- /** Start listener for interaction transactions */
- _registerInteractionListener() {
- let inflightInteractionTransaction;
- const registerInteractionTransaction = () => {
- const { idleTimeout, finalTimeout, heartbeatInterval } = this.options;
- const op = 'ui.action.click';
- // eslint-disable-next-line deprecation/deprecation
- const currentTransaction = getActiveTransaction();
- if (currentTransaction && currentTransaction.op && ['navigation', 'pageload'].includes(currentTransaction.op)) {
- DEBUG_BUILD &&
- logger.warn(
- `[Tracing] Did not create ${op} transaction because a pageload or navigation transaction is in progress.`,
- );
- return undefined;
- }
- if (inflightInteractionTransaction) {
- inflightInteractionTransaction.setFinishReason('interactionInterrupted');
- inflightInteractionTransaction.end();
- inflightInteractionTransaction = undefined;
- }
- if (!this._getCurrentHub) {
- DEBUG_BUILD && logger.warn(`[Tracing] Did not create ${op} transaction because _getCurrentHub is invalid.`);
- return undefined;
- }
- if (!this._latestRouteName) {
- DEBUG_BUILD && logger.warn(`[Tracing] Did not create ${op} transaction because _latestRouteName is missing.`);
- return undefined;
- }
- const hub = this._getCurrentHub();
- const { location } = WINDOW;
- const context = {
- name: this._latestRouteName,
- op,
- trimEnd: true,
- data: {
- [SEMANTIC_ATTRIBUTE_SENTRY_SOURCE]: this._latestRouteSource || 'url',
- },
- };
- inflightInteractionTransaction = startIdleTransaction(
- hub,
- context,
- idleTimeout,
- finalTimeout,
- true,
- { location }, // for use in the tracesSampler
- heartbeatInterval,
- );
- };
- ['click'].forEach(type => {
- addEventListener(type, registerInteractionTransaction, { once: false, capture: true });
- });
- }
- }
- /** Returns the value of a meta tag */
- function getMetaContent(metaName) {
- // Can't specify generic to `getDomElement` because tracing can be used
- // in a variety of environments, have to disable `no-unsafe-member-access`
- // as a result.
- const metaTag = getDomElement(`meta[name=${metaName}]`);
- // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access
- return metaTag ? metaTag.getAttribute('content') : undefined;
- }
- function getSource(context) {
- const sourceFromAttributes = context.attributes && context.attributes[SEMANTIC_ATTRIBUTE_SENTRY_SOURCE];
- // eslint-disable-next-line deprecation/deprecation
- const sourceFromData = context.data && context.data[SEMANTIC_ATTRIBUTE_SENTRY_SOURCE];
- // eslint-disable-next-line deprecation/deprecation
- const sourceFromMetadata = context.metadata && context.metadata.source;
- return sourceFromAttributes || sourceFromData || sourceFromMetadata;
- }
- export { BROWSER_TRACING_INTEGRATION_ID, BrowserTracing, getMetaContent };
- //# sourceMappingURL=browsertracing.js.map
|