diff --git a/packages/node/src/sdk/client.ts b/packages/node/src/sdk/client.ts index b4730ac0f07c..f8a775c15600 100644 --- a/packages/node/src/sdk/client.ts +++ b/packages/node/src/sdk/client.ts @@ -1,6 +1,7 @@ import * as os from 'node:os'; import type { Tracer } from '@opentelemetry/api'; import { trace } from '@opentelemetry/api'; +import { registerInstrumentations } from '@opentelemetry/instrumentation'; import type { BasicTracerProvider } from '@opentelemetry/sdk-trace-base'; import type { Scope, ServerRuntimeClientOptions } from '@sentry/core'; import { SDK_VERSION, ServerRuntimeClient, applySdkMetadata, logger } from '@sentry/core'; @@ -27,6 +28,12 @@ export class NodeClient extends ServerRuntimeClient { serverName: options.serverName || global.process.env.SENTRY_NAME || os.hostname(), }; + if (options.openTelemetryInstrumentations) { + registerInstrumentations({ + instrumentations: options.openTelemetryInstrumentations, + }); + } + applySdkMetadata(clientOptions, 'node'); logger.log( diff --git a/packages/node/src/types.ts b/packages/node/src/types.ts index 7235e2057c34..15df14d59847 100644 --- a/packages/node/src/types.ts +++ b/packages/node/src/types.ts @@ -1,4 +1,5 @@ import type { Span as WriteableSpan } from '@opentelemetry/api'; +import type { Instrumentation } from '@opentelemetry/instrumentation'; import type { ReadableSpan } from '@opentelemetry/sdk-trace-base'; import type { ClientOptions, Options, SamplingContext, Scope, Span, TracePropagationTargets } from '@sentry/types'; @@ -90,6 +91,13 @@ export interface BaseNodeOptions { */ skipOpenTelemetrySetup?: boolean; + /** + * Provide an array of OpenTelemetry Instrumentations that should be registered. + * + * Use this option if you want to register OpenTelemetry instrumentation that the Sentry SDK does not yet have support for. + */ + openTelemetryInstrumentations?: Instrumentation[]; + /** * The max. duration in seconds that the SDK will wait for parent spans to be finished before discarding a span. * The SDK will automatically clean up spans that have no finished parent after this duration. @@ -156,7 +164,7 @@ export interface CurrentScopes { * The base `Span` type is basically a `WriteableSpan`. * There are places where we basically want to allow passing _any_ span, * so in these cases we type this as `AbstractSpan` which could be either a regular `Span` or a `ReadableSpan`. - * You'll have to make sur to check revelant fields before accessing them. + * You'll have to make sur to check relevant fields before accessing them. * * Note that technically, the `Span` exported from `@opentelemetry/sdk-trace-base` matches this, * but we cannot be 100% sure that we are actually getting such a span, so this type is more defensive. diff --git a/packages/node/test/sdk/client.test.ts b/packages/node/test/sdk/client.test.ts index 1aa4cf14fdff..8f85c375592f 100644 --- a/packages/node/test/sdk/client.test.ts +++ b/packages/node/test/sdk/client.test.ts @@ -1,5 +1,6 @@ import * as os from 'os'; import { ProxyTracer } from '@opentelemetry/api'; +import * as opentelemetryInstrumentationPackage from '@opentelemetry/instrumentation'; import { SDK_VERSION, SessionFlusher, @@ -495,6 +496,21 @@ describe('NodeClient', () => { expect(sendEnvelopeSpy).toHaveBeenCalledTimes(0); }); }); + + it('registers instrumentations provided with `openTelemetryInstrumentations`', () => { + const registerInstrumentationsSpy = jest + .spyOn(opentelemetryInstrumentationPackage, 'registerInstrumentations') + .mockImplementationOnce(() => () => undefined); + const instrumentationsArray = ['foobar'] as unknown as opentelemetryInstrumentationPackage.Instrumentation[]; + + new NodeClient(getDefaultNodeClientOptions({ openTelemetryInstrumentations: instrumentationsArray })); + + expect(registerInstrumentationsSpy).toHaveBeenCalledWith( + expect.objectContaining({ + instrumentations: instrumentationsArray, + }), + ); + }); }); describe('flush/close', () => {