diff --git a/packages/node-experimental/src/index.ts b/packages/node-experimental/src/index.ts index bd87436e36eb..2473b3d9aa64 100644 --- a/packages/node-experimental/src/index.ts +++ b/packages/node-experimental/src/index.ts @@ -1,6 +1,3 @@ -import { Integrations as CoreIntegrations } from '@sentry/core'; - -import * as NodeExperimentalIntegrations from './integrations'; export { expressIntegration } from './integrations/express'; export { fastifyIntegration } from './integrations/fastify'; export { graphqlIntegration } from './integrations/graphql'; @@ -14,13 +11,6 @@ export { nativeNodeFetchIntegration } from './integrations/node-fetch'; export { postgresIntegration } from './integrations/postgres'; export { prismaIntegration } from './integrations/prisma'; -/** @deprecated Import the integration function directly, e.g. `inboundFiltersIntegration()` instead of `new Integrations.InboundFilter(). */ -export const Integrations = { - // eslint-disable-next-line deprecation/deprecation - ...CoreIntegrations, - ...NodeExperimentalIntegrations, -}; - export { init, getDefaultIntegrations } from './sdk/init'; export { getAutoPerformanceIntegrations } from './integrations/getAutoPerformanceIntegrations'; export * as Handlers from './sdk/handlers'; diff --git a/packages/node-experimental/src/integrations/http.ts b/packages/node-experimental/src/integrations/http.ts index 319f2da4f60f..ca4f69f6fe86 100644 --- a/packages/node-experimental/src/integrations/http.ts +++ b/packages/node-experimental/src/integrations/http.ts @@ -4,19 +4,11 @@ import { SpanKind } from '@opentelemetry/api'; import { registerInstrumentations } from '@opentelemetry/instrumentation'; import { HttpInstrumentation } from '@opentelemetry/instrumentation-http'; -import { - addBreadcrumb, - defineIntegration, - getIsolationScope, - hasTracingEnabled, - isSentryRequestUrl, -} from '@sentry/core'; +import { addBreadcrumb, defineIntegration, getIsolationScope, isSentryRequestUrl } from '@sentry/core'; import { _INTERNAL, getClient, getSpanKind, setSpanMetadata } from '@sentry/opentelemetry'; -import type { EventProcessor, Hub, Integration, IntegrationFn } from '@sentry/types'; -import { stringMatchesSomePattern } from '@sentry/utils'; +import type { IntegrationFn } from '@sentry/types'; import { setIsolationScope } from '../sdk/scope'; -import type { NodeExperimentalClient } from '../types'; import { addOriginToSpan } from '../utils/addOriginToSpan'; import { getRequestUrl } from '../utils/getRequestUrl'; @@ -111,148 +103,6 @@ const _httpIntegration = ((options: HttpOptions = {}) => { export const httpIntegration = defineIntegration(_httpIntegration); -interface OldHttpOptions { - /** - * Whether breadcrumbs should be recorded for requests - * Defaults to true - */ - breadcrumbs?: boolean; - - /** - * Whether tracing spans should be created for requests - * Defaults to false - */ - spans?: boolean; - - /** - * Do not capture spans or breadcrumbs for outgoing HTTP requests to URLs matching the given patterns. - */ - ignoreOutgoingRequests?: (string | RegExp)[]; -} - -/** - * Http instrumentation based on @opentelemetry/instrumentation-http. - * This instrumentation does two things: - * * Create breadcrumbs for outgoing requests - * * Create spans for outgoing requests - * - * Note that this integration is also needed for the Express integration to work! - * - * @deprecated Use `httpIntegration()` instead. - */ -export class Http implements Integration { - /** - * @inheritDoc - */ - public static id: string = 'Http'; - - /** - * @inheritDoc - */ - public name: string; - - /** - * If spans for HTTP requests should be captured. - */ - public shouldCreateSpansForRequests: boolean; - - private _unload?: () => void; - private readonly _breadcrumbs: boolean; - // If this is undefined, use default behavior based on client settings - private readonly _spans: boolean | undefined; - private _ignoreOutgoingRequests: (string | RegExp)[]; - - /** - * @inheritDoc - */ - public constructor(options: OldHttpOptions = {}) { - // eslint-disable-next-line deprecation/deprecation - this.name = Http.id; - this._breadcrumbs = typeof options.breadcrumbs === 'undefined' ? true : options.breadcrumbs; - this._spans = typeof options.spans === 'undefined' ? undefined : options.spans; - - this._ignoreOutgoingRequests = options.ignoreOutgoingRequests || []; - - // Properly set in setupOnce based on client settings - this.shouldCreateSpansForRequests = false; - } - - /** - * @inheritDoc - */ - public setupOnce(_addGlobalEventProcessor: (callback: EventProcessor) => void, _getCurrentHub: () => Hub): void { - // No need to instrument if we don't want to track anything - if (!this._breadcrumbs && this._spans === false) { - return; - } - - const client = getClient(); - const clientOptions = client?.getOptions(); - - // This is used in the sampler function - this.shouldCreateSpansForRequests = - typeof this._spans === 'boolean' ? this._spans : hasTracingEnabled(clientOptions); - - // Register instrumentations we care about - this._unload = registerInstrumentations({ - instrumentations: [ - new HttpInstrumentation({ - ignoreOutgoingRequestHook: request => { - const url = getRequestUrl(request); - - if (!url) { - return false; - } - - if (isSentryRequestUrl(url, getClient())) { - return true; - } - - if (this._ignoreOutgoingRequests.length && stringMatchesSomePattern(url, this._ignoreOutgoingRequests)) { - return true; - } - - return false; - }, - - ignoreIncomingRequestHook: request => { - const method = request.method?.toUpperCase(); - // We do not capture OPTIONS/HEAD requests as transactions - if (method === 'OPTIONS' || method === 'HEAD') { - return true; - } - - return false; - }, - - requireParentforOutgoingSpans: true, - requireParentforIncomingSpans: false, - requestHook: (span, req) => { - _updateSpan(span, req); - - // Update the isolation scope, isolate this request - if (getSpanKind(span) === SpanKind.SERVER) { - setIsolationScope(getIsolationScope().clone()); - } - }, - responseHook: (span, res) => { - if (this._breadcrumbs) { - _addRequestBreadcrumb(span, res); - } - }, - }), - ], - }); - } - - /** - * Unregister this integration. - */ - public unregister(): void { - this._unload?.(); - } -} - /** Update the span with data we need. */ function _updateSpan(span: Span, request: ClientRequest | IncomingMessage): void { addOriginToSpan(span, 'auto.http.otel.http'); diff --git a/packages/node-experimental/src/integrations/index.ts b/packages/node-experimental/src/integrations/index.ts deleted file mode 100644 index d9c238f68141..000000000000 --- a/packages/node-experimental/src/integrations/index.ts +++ /dev/null @@ -1,20 +0,0 @@ -import { Integrations as NodeIntegrations } from '@sentry/node'; - -const { Console, OnUncaughtException, OnUnhandledRejection, Modules, ContextLines, Context, RequestData } = - NodeIntegrations; - -export { Console, OnUncaughtException, OnUnhandledRejection, Modules, ContextLines, Context, RequestData }; - -/* eslint-disable deprecation/deprecation */ -export { Express } from './express'; -export { Http } from './http'; -export { NodeFetch } from './node-fetch'; -export { Fastify } from './fastify'; -export { GraphQL } from './graphql'; -export { Mongo } from './mongo'; -export { Mongoose } from './mongoose'; -export { Mysql } from './mysql'; -export { Mysql2 } from './mysql2'; -export { Nest } from './nest'; -export { Postgres } from './postgres'; -export { Prisma } from './prisma'; diff --git a/packages/node-experimental/src/integrations/node-fetch.ts b/packages/node-experimental/src/integrations/node-fetch.ts index 35bf982d286e..94eca67c29ba 100644 --- a/packages/node-experimental/src/integrations/node-fetch.ts +++ b/packages/node-experimental/src/integrations/node-fetch.ts @@ -2,14 +2,12 @@ import type { Span } from '@opentelemetry/api'; import { SpanKind } from '@opentelemetry/api'; import type { Instrumentation } from '@opentelemetry/instrumentation'; import { registerInstrumentations } from '@opentelemetry/instrumentation'; -import { addBreadcrumb, defineIntegration, hasTracingEnabled } from '@sentry/core'; -import { _INTERNAL, getClient, getSpanKind } from '@sentry/opentelemetry'; -import type { Integration, IntegrationFn } from '@sentry/types'; +import { addBreadcrumb, defineIntegration } from '@sentry/core'; +import { _INTERNAL, getSpanKind } from '@sentry/opentelemetry'; +import type { IntegrationFn } from '@sentry/types'; import { parseSemver } from '@sentry/utils'; -import type { NodeExperimentalClient } from '../types'; import { addOriginToSpan } from '../utils/addOriginToSpan'; -import { NodePerformanceIntegration } from './NodePerformanceIntegration'; const NODE_VERSION: ReturnType = parseSemver(process.versions.node); @@ -77,111 +75,6 @@ const _nativeNodeFetchIntegration = ((options: NodeFetchOptions = {}) => { export const nativeNodeFetchIntegration = defineIntegration(_nativeNodeFetchIntegration); -interface OldNodeFetchOptions { - /** - * Whether breadcrumbs should be recorded for requests - * Defaults to true - */ - breadcrumbs?: boolean; - - /** - * Whether tracing spans should be created for requests - * Defaults to false - */ - spans?: boolean; -} - -/** - * Fetch instrumentation based on opentelemetry-instrumentation-fetch. - * This instrumentation does two things: - * * Create breadcrumbs for outgoing requests - * * Create spans for outgoing requests - * - * @deprecated Use `nativeNodeFetchIntegration()` instead. - */ -export class NodeFetch extends NodePerformanceIntegration implements Integration { - /** - * @inheritDoc - */ - public static id: string = 'NodeFetch'; - - /** - * @inheritDoc - */ - public name: string; - - /** - * If spans for HTTP requests should be captured. - */ - public shouldCreateSpansForRequests: boolean; - - private readonly _breadcrumbs: boolean; - // If this is undefined, use default behavior based on client settings - private readonly _spans: boolean | undefined; - - /** - * @inheritDoc - */ - public constructor(options: OldNodeFetchOptions = {}) { - super(options); - - // eslint-disable-next-line deprecation/deprecation - this.name = NodeFetch.id; - this._breadcrumbs = typeof options.breadcrumbs === 'undefined' ? true : options.breadcrumbs; - this._spans = typeof options.spans === 'undefined' ? undefined : options.spans; - - // Properly set in setupOnce based on client settings - this.shouldCreateSpansForRequests = false; - } - - /** @inheritDoc */ - public setupInstrumentation(): void | Instrumentation[] { - // Only add NodeFetch if Node >= 16, as previous versions do not support it - if (!NODE_VERSION.major || NODE_VERSION.major < 16) { - return; - } - - try { - // eslint-disable-next-line @typescript-eslint/no-var-requires - const { FetchInstrumentation } = require('opentelemetry-instrumentation-fetch-node'); - return [ - new FetchInstrumentation({ - onRequest: ({ span }: { span: Span }) => { - _updateSpan(span); - - if (this._breadcrumbs) { - _addRequestBreadcrumb(span); - } - }, - }), - ]; - } catch (error) { - // Could not load instrumentation - } - } - - /** - * @inheritDoc - */ - public setupOnce(): void { - super.setupOnce(); - - const client = getClient(); - const clientOptions = client?.getOptions(); - - // This is used in the sampler function - this.shouldCreateSpansForRequests = - typeof this._spans === 'boolean' ? this._spans : hasTracingEnabled(clientOptions); - } - - /** - * Unregister this integration. - */ - public unregister(): void { - this._unload?.(); - } -} - /** Update the span with data we need. */ function _updateSpan(span: Span): void { addOriginToSpan(span, 'auto.http.otel.node_fetch'); diff --git a/packages/node-experimental/src/sdk/init.ts b/packages/node-experimental/src/sdk/init.ts index e7fc35a3f4b4..11c98b72c89b 100644 --- a/packages/node-experimental/src/sdk/init.ts +++ b/packages/node-experimental/src/sdk/init.ts @@ -7,7 +7,6 @@ import { startSession, } from '@sentry/core'; import { - defaultIntegrations as defaultNodeIntegrations, defaultStackParser, getDefaultIntegrations as getDefaultNodeIntegrations, getSentryRelease, @@ -34,14 +33,6 @@ import { initOtel } from './initOtel'; const ignoredDefaultIntegrations = ['Http', 'Undici']; -/** @deprecated Use `getDefaultIntegrations(options)` instead. */ -export const defaultIntegrations: Integration[] = [ - // eslint-disable-next-line deprecation/deprecation - ...defaultNodeIntegrations.filter(i => !ignoredDefaultIntegrations.includes(i.name)), - httpIntegration(), - nativeNodeFetchIntegration(), -]; - /** Get the default integrations for the Node Experimental SDK. */ export function getDefaultIntegrations(options: Options): Integration[] { return [ diff --git a/packages/node-experimental/src/sdk/spanProcessor.ts b/packages/node-experimental/src/sdk/spanProcessor.ts index 4cc782d804f1..c7254ac23808 100644 --- a/packages/node-experimental/src/sdk/spanProcessor.ts +++ b/packages/node-experimental/src/sdk/spanProcessor.ts @@ -1,41 +1,6 @@ -import { SpanKind } from '@opentelemetry/api'; -import type { Span } from '@opentelemetry/sdk-trace-base'; -import { SemanticAttributes } from '@opentelemetry/semantic-conventions'; -import { SentrySpanProcessor, getClient } from '@sentry/opentelemetry'; - -import type { Http } from '../integrations/http'; -import type { NodeFetch } from '../integrations/node-fetch'; -import type { NodeExperimentalClient } from '../types'; +import { SentrySpanProcessor } from '@sentry/opentelemetry'; /** - * Implement custom code to avoid sending spans in certain cases. + * Nothing custom here anymore! We can remove this eventually... */ -export class NodeExperimentalSentrySpanProcessor extends SentrySpanProcessor { - /** @inheritDoc */ - protected _shouldSendSpanToSentry(span: Span): boolean { - const client = getClient(); - // eslint-disable-next-line deprecation/deprecation - const httpIntegration = client ? client.getIntegrationByName('Http') : undefined; - // eslint-disable-next-line deprecation/deprecation - const fetchIntegration = client ? client.getIntegrationByName('NodeFetch') : undefined; - - // If we encounter a client or server span with url & method, we assume this comes from the http instrumentation - // In this case, if `shouldCreateSpansForRequests` is false, we want to _record_ the span but not _sample_ it, - // So we can generate a breadcrumb for it but no span will be sent - // TODO v8: Remove this - if ( - (span.kind === SpanKind.CLIENT || span.kind === SpanKind.SERVER) && - span.attributes[SemanticAttributes.HTTP_URL] && - span.attributes[SemanticAttributes.HTTP_METHOD] - ) { - const shouldCreateSpansForRequests = - span.attributes['http.client'] === 'fetch' - ? fetchIntegration?.shouldCreateSpansForRequests - : httpIntegration?.shouldCreateSpansForRequests; - - return shouldCreateSpansForRequests !== false; - } - - return true; - } -} +export class NodeExperimentalSentrySpanProcessor extends SentrySpanProcessor {} diff --git a/packages/node-experimental/test/integration/transactions.test.ts b/packages/node-experimental/test/integration/transactions.test.ts index 7de51c7811e6..72dd4966bc41 100644 --- a/packages/node-experimental/test/integration/transactions.test.ts +++ b/packages/node-experimental/test/integration/transactions.test.ts @@ -1,14 +1,11 @@ -import { SpanKind, TraceFlags, context, trace } from '@opentelemetry/api'; +import { TraceFlags, context, trace } from '@opentelemetry/api'; import type { SpanProcessor } from '@opentelemetry/sdk-trace-base'; -import { SemanticAttributes } from '@opentelemetry/semantic-conventions'; import { spanToJSON } from '@sentry/core'; import { SentrySpanProcessor, getClient, setPropagationContextOnContext } from '@sentry/opentelemetry'; -import type { Integration, PropagationContext, TransactionEvent } from '@sentry/types'; +import type { PropagationContext, TransactionEvent } from '@sentry/types'; import { logger } from '@sentry/utils'; import * as Sentry from '../../src'; -import { startSpan } from '../../src'; -import type { Http, NodeFetch } from '../../src/integrations'; import type { NodeExperimentalClient } from '../../src/types'; import { cleanupOtel, getProvider, mockSdkInit } from '../helpers/mockSdkInit'; @@ -679,135 +676,4 @@ describe('Integration | Transactions', () => { ]), ); }); - - it('does not create spans for http requests if disabled in http integration', async () => { - const beforeSendTransaction = jest.fn(() => null); - - mockSdkInit({ enableTracing: true, beforeSendTransaction }); - - jest.useFakeTimers(); - - const client = getClient() as NodeExperimentalClient; - - jest.spyOn(client, 'getIntegrationByName').mockImplementation(name => { - if (name === 'Http') { - return { - shouldCreateSpansForRequests: false, - // eslint-disable-next-line deprecation/deprecation - } as Http; - } - - return {} as Integration; - }); - - client.tracer.startActiveSpan( - 'test op', - { - kind: SpanKind.CLIENT, - attributes: { - [SemanticAttributes.HTTP_METHOD]: 'GET', - [SemanticAttributes.HTTP_URL]: 'https://example.com', - }, - }, - span => { - startSpan({ name: 'inner 1' }, () => { - startSpan({ name: 'inner 2' }, () => {}); - }); - - span.end(); - }, - ); - - void client.flush(); - jest.advanceTimersByTime(5_000); - - expect(beforeSendTransaction).toHaveBeenCalledTimes(0); - - // Now try a non-HTTP span - client.tracer.startActiveSpan( - 'test op 2', - { - kind: SpanKind.CLIENT, - attributes: {}, - }, - span => { - startSpan({ name: 'inner 1' }, () => { - startSpan({ name: 'inner 2' }, () => {}); - }); - - span.end(); - }, - ); - - void client.flush(); - jest.advanceTimersByTime(5_000); - - expect(beforeSendTransaction).toHaveBeenCalledTimes(1); - }); - - it('does not create spans for fetch requests if disabled in fetch integration', async () => { - const beforeSendTransaction = jest.fn(() => null); - - mockSdkInit({ enableTracing: true, beforeSendTransaction }); - - jest.useFakeTimers(); - - const client = getClient() as NodeExperimentalClient; - - jest.spyOn(client, 'getIntegrationByName').mockImplementation(name => { - if (name === 'NodeFetch') { - return { - shouldCreateSpansForRequests: false, - // eslint-disable-next-line deprecation/deprecation - } as NodeFetch; - } - - return {} as Integration; - }); - - client.tracer.startActiveSpan( - 'test op', - { - kind: SpanKind.CLIENT, - attributes: { - [SemanticAttributes.HTTP_METHOD]: 'GET', - [SemanticAttributes.HTTP_URL]: 'https://example.com', - 'http.client': 'fetch', - }, - }, - span => { - startSpan({ name: 'inner 1' }, () => { - startSpan({ name: 'inner 2' }, () => {}); - }); - - span.end(); - }, - ); - - void client.flush(); - jest.advanceTimersByTime(5_000); - - expect(beforeSendTransaction).toHaveBeenCalledTimes(0); - - // Now try a non-HTTP span - client.tracer.startActiveSpan( - 'test op 2', - { - kind: SpanKind.CLIENT, - attributes: {}, - }, - span => { - startSpan({ name: 'inner 1' }, () => { - startSpan({ name: 'inner 2' }, () => {}); - }); - - span.end(); - }, - ); - - void client.flush(); - jest.advanceTimersByTime(5_000); - - expect(beforeSendTransaction).toHaveBeenCalledTimes(1); - }); });