Skip to content

feat(server-utils): Instrument graphql v17 via native tracing channels#21804

Open
logaretm wants to merge 4 commits into
developfrom
awad/graphql-tracing-channels
Open

feat(server-utils): Instrument graphql v17 via native tracing channels#21804
logaretm wants to merge 4 commits into
developfrom
awad/graphql-tracing-channels

Conversation

@logaretm

@logaretm logaretm commented Jun 25, 2026

Copy link
Copy Markdown
Member

GraphQL v17 uses its native diagnostics_channel for tracing instead of monkey-patching. It publishes lifecycle events on:

  • graphql:parse
  • graphql:validate
  • graphql:execute
  • graphql:subscribe

This PR adds new graphqlChannelIntegration in server-utils that subscribes to these events and builds spans with graphql semantic conventions. The graphqlIntegration node extends this integration and keeps the vendored OTel patcher for graphql < 17. The patcher is gated >=14 <17.

A couple of things worth calling out:

  • The graphql.document value is the original query text with inline literal arguments redacted ("foo""*", 42*) so raw values can't leak, and variable values are never attached.
  • Per-field graphql:resolve spans are off by default because they're very high volume, users can opt in via ignoreResolveSpans just like previous versions.

@github-actions

github-actions Bot commented Jun 25, 2026

Copy link
Copy Markdown
Contributor

size-limit report 📦

Path Size % Change Change
@sentry/browser 27.48 kB +0.01% +1 B 🔺
@sentry/browser - with treeshaking flags 25.91 kB - -
@sentry/browser (incl. Tracing) 45.97 kB +0.01% +1 B 🔺
@sentry/browser (incl. Tracing + Span Streaming) 47.72 kB +0.01% +1 B 🔺
@sentry/browser (incl. Tracing, Profiling) 50.76 kB - -
@sentry/browser (incl. Tracing, Replay) 85.22 kB - -
@sentry/browser (incl. Tracing, Replay) - with treeshaking flags 74.81 kB - -
@sentry/browser (incl. Tracing, Replay with Canvas) 89.91 kB - -
@sentry/browser (incl. Tracing, Replay, Feedback) 102.57 kB - -
@sentry/browser (incl. Feedback) 44.66 kB - -
@sentry/browser (incl. sendFeedback) 32.26 kB +0.01% +1 B 🔺
@sentry/browser (incl. FeedbackAsync) 37.4 kB - -
@sentry/browser (incl. Metrics) 28.54 kB - -
@sentry/browser (incl. Logs) 28.78 kB - -
@sentry/browser (incl. Metrics & Logs) 29.47 kB - -
@sentry/react 29.27 kB - -
@sentry/react (incl. Tracing) 48.28 kB +0.01% +1 B 🔺
@sentry/vue 32.63 kB - -
@sentry/vue (incl. Tracing) 47.84 kB - -
@sentry/svelte 27.5 kB - -
CDN Bundle 29.89 kB - -
CDN Bundle (incl. Tracing) 47.89 kB - -
CDN Bundle (incl. Logs, Metrics) 31.44 kB - -
CDN Bundle (incl. Tracing, Logs, Metrics) 49.24 kB +0.01% +1 B 🔺
CDN Bundle (incl. Replay, Logs, Metrics) 70.78 kB - -
CDN Bundle (incl. Tracing, Replay) 85.4 kB - -
CDN Bundle (incl. Tracing, Replay, Logs, Metrics) 86.68 kB - -
CDN Bundle (incl. Tracing, Replay, Feedback) 91.19 kB - -
CDN Bundle (incl. Tracing, Replay, Feedback, Logs, Metrics) 92.45 kB - -
CDN Bundle - uncompressed 88.94 kB - -
CDN Bundle (incl. Tracing) - uncompressed 145.03 kB - -
CDN Bundle (incl. Logs, Metrics) - uncompressed 93.65 kB - -
CDN Bundle (incl. Tracing, Logs, Metrics) - uncompressed 149 kB - -
CDN Bundle (incl. Replay, Logs, Metrics) - uncompressed 218.62 kB - -
CDN Bundle (incl. Tracing, Replay) - uncompressed 264.05 kB - -
CDN Bundle (incl. Tracing, Replay, Logs, Metrics) - uncompressed 268 kB - -
CDN Bundle (incl. Tracing, Replay, Feedback) - uncompressed 277.75 kB - -
CDN Bundle (incl. Tracing, Replay, Feedback, Logs, Metrics) - uncompressed 281.69 kB - -
@sentry/nextjs (client) 50.67 kB - -
@sentry/sveltekit (client) 46.37 kB - -
@sentry/core/server 77.54 kB - -
@sentry/core/browser 63.88 kB - -
@sentry/node-core 61.51 kB - -
@sentry/node 124.3 kB +0.67% +821 B 🔺
@sentry/node/import (ESM hook with diagnostics-channel injection) 69.95 kB - -
@sentry/node/light 50.4 kB -0.01% -1 B 🔽
@sentry/node - without tracing 73.61 kB - -
@sentry/aws-serverless 84.4 kB +0.01% +1 B 🔺
@sentry/cloudflare (withSentry) - minified 180.31 kB - -
@sentry/cloudflare (withSentry) 446.24 kB - -

View base workflow run

@logaretm logaretm changed the title feat(server-utils): Instrument graphql v17 via native diagnostics_channel tracing channels feat(server-utils): Instrument graphql v17 via native tracing channels Jun 25, 2026
@mydea mydea force-pushed the fn/better-integration-extend branch 2 times, most recently from 2407b9e to 31520be Compare June 26, 2026 08:48
Base automatically changed from fn/better-integration-extend to develop June 26, 2026 09:38
@logaretm logaretm force-pushed the awad/graphql-tracing-channels branch from c907efa to 3da22d3 Compare June 26, 2026 11:48
@logaretm logaretm marked this pull request as ready for review June 26, 2026 12:55
@logaretm logaretm requested a review from a team as a code owner June 26, 2026 12:55
@logaretm logaretm requested review from JPeer264, andreiborza, isaacs and mydea and removed request for a team June 26, 2026 12:55
span.setStatus({ code: SPAN_STATUS_ERROR, message: 'internal_error' });
}
},
},

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Root span rename missing v17

Medium Severity

On GraphQL 17, tracing uses the diagnostics-channel path only, but useOperationNameForRootSpan (default true on graphqlIntegration) is never applied there. The vendored OTel instrumentation that renames the enclosing HTTP root span is gated to GraphQL < 17, so upgrading GraphQL while keeping the same SDK option leaves server transaction names unchanged.

Additional Locations (1)
Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit abebece. Configure here.

logaretm added 4 commits June 26, 2026 09:01
…nnel tracing channels

Adds a graphqlChannelIntegration in server-utils that subscribes to graphql >= 17's
native node:diagnostics_channel tracing channels (graphql:parse / :validate / :execute
/ :subscribe) and builds spans with the graphql semantic conventions. The node
graphqlIntegration extends it via extendIntegration so the vendored OTel patcher still
handles graphql < 17. Per-field graphql:resolve spans are omitted (high volume; the OTel
path also defaults them off).
…pans option

Wires the per-field `graphql:resolve` channel into the graphql diagnostics-channel
subscriber, gated behind the existing `ignoreResolveSpans` option (default true, so off
by default). When enabled, `ignoreTrivialResolveSpans` (default true) additionally skips
graphql's default property resolver. Both options now flow through to the channel path,
matching the vendored OTel patcher's behavior on graphql < 17.
…keeping

Split option-matrix tests into process-isolated files instead of resetting
and re-subscribing the process-global channels.
…tion

Runtimes that use the server-utils integration directly expose it to users,
so 'channel' in the name leaks an implementation detail. Node imports it under
an internal alias since it keeps its own composed graphqlIntegration.
@logaretm logaretm force-pushed the awad/graphql-tracing-channels branch from abebece to 3f660c0 Compare June 26, 2026 13:01

@cursor cursor Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Cursor Bugbot has reviewed your changes and found 1 potential issue.

There are 2 total unresolved issues (including 1 from previous review).

Fix All in Cursor

❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.

Reviewed by Cursor Bugbot for commit 3f660c0. Configure here.

// The factory relies on `node:diagnostics_channel`, which isn't always
// available. Fail closed; the SDK simply won't emit graphql spans here.
DEBUG_BUILD && debug.log('GraphQL node:diagnostics_channel subscription failed.');
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Failed subscribe blocks forever

Low Severity

subscribed is set to true before channel setup finishes. If the surrounding try throws partway through or in the catch path, later calls to subscribeGraphqlDiagnosticChannels return immediately and GraphQL tracing never comes online for that process.

Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit 3f660c0. Configure here.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant