Skip to content

feat(tracing): Move common tracing code to core #7339

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 17 commits into from
Mar 6, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions packages/core/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ export type { ClientClass } from './sdk';
export type { Carrier, Layer } from './hub';
export type { OfflineStore, OfflineTransportOptions } from './transports/offline';

export * from './tracing';
export {
addBreadcrumb,
captureException,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,17 +1,9 @@
import type { Hub } from '@sentry/core';
import { getMainCarrier, hasTracingEnabled } from '@sentry/core';
import type {
ClientOptions,
CustomSamplingContext,
Integration,
IntegrationClass,
Options,
SamplingContext,
TransactionContext,
} from '@sentry/types';
import { dynamicRequire, isNaN, isNodeEnv, loadModule, logger } from '@sentry/utils';
import type { ClientOptions, CustomSamplingContext, Options, SamplingContext, TransactionContext } from '@sentry/types';
import { isNaN, logger } from '@sentry/utils';

import { registerErrorInstrumentation } from './errors';
import type { Hub } from '../hub';
import { getMainCarrier } from '../hub';
import { hasTracingEnabled } from '../utils/hasTracingEnabled';
import { IdleTransaction } from './idletransaction';
import { Transaction } from './transaction';

Expand Down Expand Up @@ -225,9 +217,9 @@ export function startIdleTransaction(
}

/**
* @private
* Adds tracing extensions to the global hub.
*/
export function _addTracingExtensions(): void {
export function addTracingExtensions(): void {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

l: Can we mark this as deprecated out of caution. I think we wanna move to dependency injection rather than functions with side-effects.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Will do this in a follow up PR since we should make the deprecations the final step after we've moved everything over.

const carrier = getMainCarrier();
if (!carrier.__SENTRY__) {
return;
Expand All @@ -240,70 +232,3 @@ export function _addTracingExtensions(): void {
carrier.__SENTRY__.extensions.traceHeaders = traceHeaders;
}
}

/**
* @private
*/
function _autoloadDatabaseIntegrations(): void {
const carrier = getMainCarrier();
if (!carrier.__SENTRY__) {
return;
}

const packageToIntegrationMapping: Record<string, () => Integration> = {
mongodb() {
const integration = dynamicRequire(module, './integrations/node/mongo') as {
Mongo: IntegrationClass<Integration>;
};
return new integration.Mongo();
},
mongoose() {
const integration = dynamicRequire(module, './integrations/node/mongo') as {
Mongo: IntegrationClass<Integration>;
};
return new integration.Mongo({ mongoose: true });
},
mysql() {
const integration = dynamicRequire(module, './integrations/node/mysql') as {
Mysql: IntegrationClass<Integration>;
};
return new integration.Mysql();
},
pg() {
const integration = dynamicRequire(module, './integrations/node/postgres') as {
Postgres: IntegrationClass<Integration>;
};
return new integration.Postgres();
},
};

const mappedPackages = Object.keys(packageToIntegrationMapping)
.filter(moduleName => !!loadModule(moduleName))
.map(pkg => {
try {
return packageToIntegrationMapping[pkg]();
} catch (e) {
return undefined;
}
})
.filter(p => p) as Integration[];

if (mappedPackages.length > 0) {
carrier.__SENTRY__.integrations = [...(carrier.__SENTRY__.integrations || []), ...mappedPackages];
}
}

/**
* This patches the global object and injects the Tracing extensions methods
*/
export function addExtensionMethods(): void {
_addTracingExtensions();

// Detect and automatically load specified integrations.
if (isNodeEnv()) {
_autoloadDatabaseIntegrations();
}

// If an error happens globally, we should make sure transaction status is set to error.
registerErrorInstrumentation();
}
Original file line number Diff line number Diff line change
@@ -1,15 +1,17 @@
/* eslint-disable max-lines */
import type { Hub } from '@sentry/core';
import type { TransactionContext } from '@sentry/types';
import { logger, timestampWithMs } from '@sentry/utils';

import type { Hub } from '../hub';
import type { Span } from './span';
import { SpanRecorder } from './span';
import { Transaction } from './transaction';

export const DEFAULT_IDLE_TIMEOUT = 1000;
export const DEFAULT_FINAL_TIMEOUT = 30000;
export const DEFAULT_HEARTBEAT_INTERVAL = 5000;
export const TRACING_DEFAULTS = {
idleTimeout: 1000,
finalTimeout: 30000,
heartbeatInterval: 5000,
};

/**
* @inheritDoc
Expand Down Expand Up @@ -81,12 +83,12 @@ export class IdleTransaction extends Transaction {
* The time to wait in ms until the idle transaction will be finished. This timer is started each time
* there are no active spans on this transaction.
*/
private readonly _idleTimeout: number = DEFAULT_IDLE_TIMEOUT,
private readonly _idleTimeout: number = TRACING_DEFAULTS.idleTimeout,
/**
* The final value in ms that a transaction cannot exceed
*/
private readonly _finalTimeout: number = DEFAULT_FINAL_TIMEOUT,
private readonly _heartbeatInterval: number = DEFAULT_HEARTBEAT_INTERVAL,
private readonly _finalTimeout: number = TRACING_DEFAULTS.finalTimeout,
private readonly _heartbeatInterval: number = TRACING_DEFAULTS.heartbeatInterval,
// Whether or not the transaction should put itself on the scope when it starts and pop itself off when it ends
private readonly _onScope: boolean = false,
) {
Expand Down
8 changes: 8 additions & 0 deletions packages/core/src/tracing/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
export { startIdleTransaction, addTracingExtensions } from './hubextensions';
export { IdleTransaction, TRACING_DEFAULTS } from './idletransaction';
export { Span, spanStatusfromHttpCode } from './span';
export { Transaction } from './transaction';
export { extractTraceparentData, getActiveTransaction, stripUrlQueryAndFragment, TRACEPARENT_REGEXP } from './utils';
// eslint-disable-next-line deprecation/deprecation
export { SpanStatus } from './spanstatus';
export type { SpanStatusType } from './span';
File renamed without changes.
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
import type { Hub } from '@sentry/core';
import { DEFAULT_ENVIRONMENT, getCurrentHub } from '@sentry/core';
import type {
Context,
Contexts,
Expand All @@ -13,6 +11,9 @@ import type {
} from '@sentry/types';
import { dropUndefinedKeys, logger } from '@sentry/utils';

import { DEFAULT_ENVIRONMENT } from '../constants';
import type { Hub } from '../hub';
Comment on lines +14 to +15
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

l: Do we want to point these to the actual definitions instead of to index?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Changed the imports to use ../hub - does this still apply?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nope all good

import { getCurrentHub } from '../hub';
import { Span as SpanClass, SpanRecorder } from './span';

/** JSDoc */
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import type { Hub } from '@sentry/core';
import { getCurrentHub, hasTracingEnabled as _hasTracingEnabled } from '@sentry/core';
import type { Options, Transaction } from '@sentry/types';
import type { Transaction } from '@sentry/types';

import type { Hub } from '../hub';
import { getCurrentHub } from '../hub';
Comment on lines +3 to +4
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same as above.


/**
* The `extractTraceparentData` function and `TRACEPARENT_REGEXP` constant used
Expand All @@ -16,40 +17,12 @@ import type { Options, Transaction } from '@sentry/types';
*/
export { TRACEPARENT_REGEXP, extractTraceparentData } from '@sentry/utils';

/**
* Determines if tracing is currently enabled.
*
* Tracing is enabled when at least one of `tracesSampleRate` and `tracesSampler` is defined in the SDK config.
* @deprecated This export has moved to `@sentry/core`. This export will be removed from `@sentry/tracing` in v8.
*/
export function hasTracingEnabled(
maybeOptions?: Pick<Options, 'tracesSampleRate' | 'tracesSampler' | 'enableTracing'> | undefined,
): boolean {
return _hasTracingEnabled(maybeOptions);
}

/** Grabs active transaction off scope, if any */
export function getActiveTransaction<T extends Transaction>(maybeHub?: Hub): T | undefined {
const hub = maybeHub || getCurrentHub();
const scope = hub.getScope();
return scope && (scope.getTransaction() as T | undefined);
}

/**
* Converts from milliseconds to seconds
* @param time time in ms
*/
export function msToSec(time: number): number {
return time / 1000;
}

/**
* Converts from seconds to milliseconds
* @param time time in seconds
*/
export function secToMs(time: number): number {
return time * 1000;
}

// so it can be used in manual instrumentation without necessitating a hard dependency on @sentry/utils
export { stripUrlQueryAndFragment } from '@sentry/utils';
2 changes: 2 additions & 0 deletions packages/nextjs/src/index.types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,8 @@ export declare const ErrorBoundary: typeof clientSdk.ErrorBoundary;
export declare const showReportDialog: typeof clientSdk.showReportDialog;
export declare const withErrorBoundary: typeof clientSdk.withErrorBoundary;

export declare const Span: typeof edgeSdk.Span;

/**
* @deprecated Use `wrapApiHandlerWithSentry` instead
*/
Expand Down
5 changes: 2 additions & 3 deletions packages/tracing/src/browser/backgroundtab.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,7 @@
import type { IdleTransaction, SpanStatusType } from '@sentry/core';
import { getActiveTransaction } from '@sentry/core';
import { logger } from '@sentry/utils';

import type { IdleTransaction } from '../idletransaction';
import type { SpanStatusType } from '../span';
import { getActiveTransaction } from '../utils';
import { WINDOW } from './types';

/**
Expand Down
11 changes: 3 additions & 8 deletions packages/tracing/src/browser/browsertracing.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,9 @@
/* eslint-disable max-lines */
import type { Hub } from '@sentry/core';
import type { Hub, IdleTransaction } from '@sentry/core';
import { extractTraceparentData, startIdleTransaction, TRACING_DEFAULTS } from '@sentry/core';
import type { EventProcessor, Integration, Transaction, TransactionContext, TransactionSource } from '@sentry/types';
import { baggageHeaderToDynamicSamplingContext, getDomElement, logger } from '@sentry/utils';

import { startIdleTransaction } from '../hubextensions';
import type { IdleTransaction } from '../idletransaction';
import { DEFAULT_FINAL_TIMEOUT, DEFAULT_HEARTBEAT_INTERVAL, DEFAULT_IDLE_TIMEOUT } from '../idletransaction';
import { extractTraceparentData } from '../utils';
import { registerBackgroundTabDetection } from './backgroundtab';
import { addPerformanceEntries, startTrackingLongTasks, startTrackingWebVitals } from './metrics';
import type { RequestInstrumentationOptions } from './request';
Expand Down Expand Up @@ -131,9 +128,7 @@ export interface BrowserTracingOptions extends RequestInstrumentationOptions {
}

const DEFAULT_BROWSER_TRACING_OPTIONS: BrowserTracingOptions = {
idleTimeout: DEFAULT_IDLE_TIMEOUT,
finalTimeout: DEFAULT_FINAL_TIMEOUT,
heartbeatInterval: DEFAULT_HEARTBEAT_INTERVAL,
...TRACING_DEFAULTS,
markBackgroundTransactions: true,
routingInstrumentation: instrumentRoutingWithDefaults,
startTransactionOnLocationChange: true,
Expand Down
13 changes: 10 additions & 3 deletions packages/tracing/src/browser/metrics/index.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,9 @@
/* eslint-disable max-lines */
import type { IdleTransaction, Transaction } from '@sentry/core';
import { getActiveTransaction } from '@sentry/core';
import type { Measurements } from '@sentry/types';
import { browserPerformanceTimeOrigin, htmlTreeAsString, logger } from '@sentry/utils';

import type { IdleTransaction } from '../../idletransaction';
import type { Transaction } from '../../transaction';
import { getActiveTransaction, msToSec } from '../../utils';
import { WINDOW } from '../types';
import { onCLS } from '../web-vitals/getCLS';
import { onFID } from '../web-vitals/getFID';
Expand All @@ -14,6 +13,14 @@ import { observe } from '../web-vitals/lib/observe';
import type { NavigatorDeviceMemory, NavigatorNetworkInformation } from '../web-vitals/types';
import { _startChild, isMeasurementValue } from './utils';

/**
* Converts from milliseconds to seconds
* @param time time in ms
*/
function msToSec(time: number): number {
return time / 1000;
}

function getBrowserPerformanceAPI(): Performance | undefined {
return WINDOW && WINDOW.addEventListener && WINDOW.performance;
}
Expand Down
3 changes: 1 addition & 2 deletions packages/tracing/src/browser/metrics/utils.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
import type { Transaction } from '@sentry/core';
import type { Span, SpanContext } from '@sentry/types';

import type { Transaction } from '../../transaction';

/**
* Checks if a given value is a valid measurement value.
*/
Expand Down
5 changes: 2 additions & 3 deletions packages/tracing/src/errors.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,7 @@
import type { SpanStatusType } from '@sentry/core';
import { getActiveTransaction } from '@sentry/core';
import { addInstrumentationHandler, logger } from '@sentry/utils';

import type { SpanStatusType } from './span';
import { getActiveTransaction } from './utils';

/**
* Configures global error listeners
*/
Expand Down
72 changes: 72 additions & 0 deletions packages/tracing/src/extensions.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
import { addTracingExtensions, getMainCarrier } from '@sentry/core';
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

the content of this file will be moved entirely to Node in the future, right?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes it will! For now it should be completely tree-shaken out.

import type { Integration, IntegrationClass } from '@sentry/types';
import { dynamicRequire, isNodeEnv, loadModule } from '@sentry/utils';

import { registerErrorInstrumentation } from './errors';

/**
* @private
*/
function _autoloadDatabaseIntegrations(): void {
const carrier = getMainCarrier();
if (!carrier.__SENTRY__) {
return;
}

const packageToIntegrationMapping: Record<string, () => Integration> = {
mongodb() {
const integration = dynamicRequire(module, './integrations/node/mongo') as {
Mongo: IntegrationClass<Integration>;
};
return new integration.Mongo();
},
mongoose() {
const integration = dynamicRequire(module, './integrations/node/mongo') as {
Mongo: IntegrationClass<Integration>;
};
return new integration.Mongo({ mongoose: true });
},
mysql() {
const integration = dynamicRequire(module, './integrations/node/mysql') as {
Mysql: IntegrationClass<Integration>;
};
return new integration.Mysql();
},
pg() {
const integration = dynamicRequire(module, './integrations/node/postgres') as {
Postgres: IntegrationClass<Integration>;
};
return new integration.Postgres();
},
};

const mappedPackages = Object.keys(packageToIntegrationMapping)
.filter(moduleName => !!loadModule(moduleName))
.map(pkg => {
try {
return packageToIntegrationMapping[pkg]();
} catch (e) {
return undefined;
}
})
.filter(p => p) as Integration[];

if (mappedPackages.length > 0) {
carrier.__SENTRY__.integrations = [...(carrier.__SENTRY__.integrations || []), ...mappedPackages];
}
}

/**
* This patches the global object and injects the Tracing extensions methods
*/
export function addExtensionMethods(): void {
addTracingExtensions();

// Detect and automatically load specified integrations.
if (isNodeEnv()) {
_autoloadDatabaseIntegrations();
}

// If an error happens globally, we should make sure transaction status is set to error.
registerErrorInstrumentation();
}
Loading