diff --git a/packages/node/src/integrations/tracing/graphql.ts b/packages/node/src/integrations/tracing/graphql.ts index 914653ac745c..f4d817145cf2 100644 --- a/packages/node/src/integrations/tracing/graphql.ts +++ b/packages/node/src/integrations/tracing/graphql.ts @@ -40,12 +40,7 @@ const INTEGRATION_NAME = 'Graphql'; export const instrumentGraphql = generateInstrumentOnce( INTEGRATION_NAME, (_options: GraphqlOptions = {}) => { - const options = { - ignoreResolveSpans: true, - ignoreTrivialResolveSpans: true, - useOperationNameForRootSpan: true, - ..._options, - }; + const options = getOptionsWithDefaults(_options); return new GraphQLInstrumentation({ ...options, @@ -89,7 +84,10 @@ const _graphqlIntegration = ((options: GraphqlOptions = {}) => { return { name: INTEGRATION_NAME, setupOnce() { - instrumentGraphql(options); + // We set defaults here, too, because otherwise we'd update the instrumentation config + // to the config without defaults, as `generateInstrumentOnce` automatically calls `setConfig(options)` + // when being called the second time + instrumentGraphql(getOptionsWithDefaults(options)); }, }; }) satisfies IntegrationFn; @@ -100,3 +98,12 @@ const _graphqlIntegration = ((options: GraphqlOptions = {}) => { * Capture tracing data for GraphQL. */ export const graphqlIntegration = defineIntegration(_graphqlIntegration); + +function getOptionsWithDefaults(options?: GraphqlOptions): GraphqlOptions { + return { + ignoreResolveSpans: true, + ignoreTrivialResolveSpans: true, + useOperationNameForRootSpan: true, + ...options, + }; +} diff --git a/packages/node/src/otel/instrument.ts b/packages/node/src/otel/instrument.ts index 5db7960e4019..6db677a3956c 100644 --- a/packages/node/src/otel/instrument.ts +++ b/packages/node/src/otel/instrument.ts @@ -1,7 +1,8 @@ import type { Instrumentation } from '@opentelemetry/instrumentation'; import { addOpenTelemetryInstrumentation } from '@sentry/opentelemetry'; -const INSTRUMENTED: Record = {}; +/** Exported only for tests. */ +export const INSTRUMENTED: Record = {}; /** * Instrument an OpenTelemetry instrumentation once. diff --git a/packages/node/test/integrations/tracing/graphql.test.ts b/packages/node/test/integrations/tracing/graphql.test.ts new file mode 100644 index 000000000000..779b8dace830 --- /dev/null +++ b/packages/node/test/integrations/tracing/graphql.test.ts @@ -0,0 +1,46 @@ +import { GraphQLInstrumentation } from '@opentelemetry/instrumentation-graphql'; +import { graphqlIntegration, instrumentGraphql } from '../../../src/integrations/tracing/graphql'; +import { INSTRUMENTED } from '../../../src/otel/instrument'; + +jest.mock('@opentelemetry/instrumentation-graphql'); + +describe('GraphQL', () => { + beforeEach(() => { + jest.clearAllMocks(); + delete INSTRUMENTED.Graphql; + + (GraphQLInstrumentation as unknown as jest.SpyInstance).mockImplementation(() => { + return { + setTracerProvider: () => undefined, + setMeterProvider: () => undefined, + getConfig: () => ({}), + setConfig: () => ({}), + enable: () => undefined, + }; + }); + }); + + it('defaults are correct for instrumentGraphql', () => { + instrumentGraphql({ ignoreTrivialResolveSpans: false }); + + expect(GraphQLInstrumentation).toHaveBeenCalledTimes(1); + expect(GraphQLInstrumentation).toHaveBeenCalledWith({ + ignoreResolveSpans: true, + ignoreTrivialResolveSpans: false, + useOperationNameForRootSpan: true, + responseHook: expect.any(Function), + }); + }); + + it('defaults are correct for _graphqlIntegration', () => { + graphqlIntegration({ ignoreTrivialResolveSpans: false }).setupOnce!(); + + expect(GraphQLInstrumentation).toHaveBeenCalledTimes(1); + expect(GraphQLInstrumentation).toHaveBeenCalledWith({ + ignoreResolveSpans: true, + ignoreTrivialResolveSpans: false, + useOperationNameForRootSpan: true, + responseHook: expect.any(Function), + }); + }); +});