@@ -130,7 +130,6 @@ export interface GraphqlDiagnosticChannelsOptions {
130130export type GraphqlTracingChannelFactory = < T extends object > ( name : string ) => TracingChannel < T , T > ;
131131
132132let subscribed = false ;
133- let activeUnbinds : Array < ( ) => void > = [ ] ;
134133
135134/**
136135 * Subscribe Sentry span handlers to graphql's diagnostics-channel events
@@ -159,15 +158,13 @@ export function subscribeGraphqlDiagnosticChannels(
159158 const ignoreTrivialResolveSpans = options . ignoreTrivialResolveSpans !== false ;
160159
161160 try {
162- activeUnbinds . push (
163- setupParseChannel ( tracingChannel ) ,
164- setupValidateChannel ( tracingChannel ) ,
165- setupOperationChannel ( tracingChannel , GRAPHQL_DC_CHANNEL_EXECUTE , SPAN_NAME_EXECUTE ) ,
166- setupOperationChannel ( tracingChannel , GRAPHQL_DC_CHANNEL_SUBSCRIBE , SPAN_NAME_SUBSCRIBE ) ,
167- ) ;
161+ setupParseChannel ( tracingChannel ) ;
162+ setupValidateChannel ( tracingChannel ) ;
163+ setupOperationChannel ( tracingChannel , GRAPHQL_DC_CHANNEL_EXECUTE , SPAN_NAME_EXECUTE ) ;
164+ setupOperationChannel ( tracingChannel , GRAPHQL_DC_CHANNEL_SUBSCRIBE , SPAN_NAME_SUBSCRIBE ) ;
168165
169166 if ( ! ignoreResolveSpans ) {
170- activeUnbinds . push ( setupResolveChannel ( tracingChannel , ignoreTrivialResolveSpans ) ) ;
167+ setupResolveChannel ( tracingChannel , ignoreTrivialResolveSpans ) ;
171168 }
172169 } catch {
173170 // The factory relies on `node:diagnostics_channel`, which isn't always
@@ -176,26 +173,24 @@ export function subscribeGraphqlDiagnosticChannels(
176173 }
177174}
178175
179- function setupParseChannel ( tracingChannel : GraphqlTracingChannelFactory ) : ( ) => void {
180- return bindTracingChannelToSpan (
181- tracingChannel < GraphqlParseData > ( GRAPHQL_DC_CHANNEL_PARSE ) ,
182- ( ) =>
183- startInactiveSpan ( {
184- name : SPAN_NAME_PARSE ,
185- attributes : {
186- [ SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN ] : ORIGIN ,
187- [ SEMANTIC_ATTRIBUTE_SENTRY_OP ] : WEB_SERVER_GRAPHQL_SPAN_OP ,
188- } ,
189- } ) ,
190- { captureError : false } ,
191- ) . unbind ;
176+ function setupParseChannel ( tracingChannel : GraphqlTracingChannelFactory ) : void {
177+ bindTracingChannelToSpan ( tracingChannel < GraphqlParseData > ( GRAPHQL_DC_CHANNEL_PARSE ) , ( ) =>
178+ startInactiveSpan ( {
179+ name : SPAN_NAME_PARSE ,
180+ attributes : {
181+ [ SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN ] : ORIGIN ,
182+ [ SEMANTIC_ATTRIBUTE_SENTRY_OP ] : WEB_SERVER_GRAPHQL_SPAN_OP ,
183+ } ,
184+ } ) ,
185+ ) ;
192186}
193187
194- function setupValidateChannel ( tracingChannel : GraphqlTracingChannelFactory ) : ( ) => void {
195- return bindTracingChannelToSpan (
188+ function setupValidateChannel ( tracingChannel : GraphqlTracingChannelFactory ) : void {
189+ bindTracingChannelToSpan (
196190 tracingChannel < GraphqlValidateData > ( GRAPHQL_DC_CHANNEL_VALIDATE ) ,
197191 data => {
198192 const document = redactGraphqlDocument ( data . document ) ;
193+
199194 return startInactiveSpan ( {
200195 name : SPAN_NAME_VALIDATE ,
201196 attributes : {
@@ -212,20 +207,20 @@ function setupValidateChannel(tracingChannel: GraphqlTracingChannelFactory): ()
212207 span . setStatus ( { code : SPAN_STATUS_ERROR , message : 'invalid_argument' } ) ;
213208 }
214209 } ,
215- captureError : false ,
216210 } ,
217- ) . unbind ;
211+ ) ;
218212}
219213
220214function setupOperationChannel (
221215 tracingChannel : GraphqlTracingChannelFactory ,
222216 channelName : string ,
223217 fallbackName : string ,
224- ) : ( ) => void {
225- return bindTracingChannelToSpan (
218+ ) : void {
219+ bindTracingChannelToSpan (
226220 tracingChannel < GraphqlOperationData > ( channelName ) ,
227221 data => {
228222 const document = redactGraphqlDocument ( data . document ) ;
223+
229224 return startInactiveSpan ( {
230225 name : getOperationSpanName ( data , fallbackName ) ,
231226 attributes : {
@@ -244,40 +239,30 @@ function setupOperationChannel(
244239 span . setStatus ( { code : SPAN_STATUS_ERROR , message : 'internal_error' } ) ;
245240 }
246241 } ,
247- // Execution errors are surfaced to the caller in the result; only annotate the span so we
248- // don't emit a duplicate error event for every failed operation.
249- captureError : false ,
250242 } ,
251- ) . unbind ;
243+ ) ;
252244}
253245
254- function setupResolveChannel (
255- tracingChannel : GraphqlTracingChannelFactory ,
256- ignoreTrivialResolveSpans : boolean ,
257- ) : ( ) => void {
258- return bindTracingChannelToSpan (
259- tracingChannel < GraphqlResolveData > ( GRAPHQL_DC_CHANNEL_RESOLVE ) ,
260- data => {
261- // Returning `undefined` opts this field out: no span is created and the active context is left
262- // untouched, so the field still resolves under its parent span.
263- if ( ignoreTrivialResolveSpans && data . isDefaultResolver ) {
264- return undefined ;
265- }
266- return startInactiveSpan ( {
267- name : `${ SPAN_NAME_RESOLVE } ${ data . fieldPath } ` ,
268- attributes : {
269- [ SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN ] : ORIGIN ,
270- [ SEMANTIC_ATTRIBUTE_SENTRY_OP ] : WEB_SERVER_GRAPHQL_SPAN_OP ,
271- [ GRAPHQL_FIELD_NAME ] : data . fieldName ,
272- [ GRAPHQL_FIELD_PATH ] : data . fieldPath ,
273- [ GRAPHQL_FIELD_TYPE ] : data . fieldType ,
274- [ GRAPHQL_PARENT_NAME ] : data . parentType ,
275- } ,
276- } ) ;
277- } ,
278- // Resolver errors also surface in the enclosing execution result; only annotate the span.
279- { captureError : false } ,
280- ) . unbind ;
246+ function setupResolveChannel ( tracingChannel : GraphqlTracingChannelFactory , ignoreTrivialResolveSpans : boolean ) : void {
247+ bindTracingChannelToSpan ( tracingChannel < GraphqlResolveData > ( GRAPHQL_DC_CHANNEL_RESOLVE ) , data => {
248+ // Returning `undefined` opts this field out: no span is created and the active context is left
249+ // untouched, so the field still resolves under its parent span.
250+ if ( ignoreTrivialResolveSpans && data . isDefaultResolver ) {
251+ return undefined ;
252+ }
253+
254+ return startInactiveSpan ( {
255+ name : `${ SPAN_NAME_RESOLVE } ${ data . fieldPath } ` ,
256+ attributes : {
257+ [ SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN ] : ORIGIN ,
258+ [ SEMANTIC_ATTRIBUTE_SENTRY_OP ] : WEB_SERVER_GRAPHQL_SPAN_OP ,
259+ [ GRAPHQL_FIELD_NAME ] : data . fieldName ,
260+ [ GRAPHQL_FIELD_PATH ] : data . fieldPath ,
261+ [ GRAPHQL_FIELD_TYPE ] : data . fieldType ,
262+ [ GRAPHQL_PARENT_NAME ] : data . parentType ,
263+ } ,
264+ } ) ;
265+ } ) ;
281266}
282267
283268/**
@@ -292,14 +277,17 @@ function getOperationSpanName(data: GraphqlOperationData, fallbackName: string):
292277 if ( operationType ) {
293278 return operationType ;
294279 }
280+
295281 return fallbackName ;
296282}
297283
298284function hasResultErrors ( result : unknown ) : boolean {
299285 if ( result && typeof result === 'object' && 'errors' in result ) {
300286 const errors = ( result as { errors ?: unknown } ) . errors ;
287+
301288 return Array . isArray ( errors ) && errors . length > 0 ;
302289 }
290+
303291 return false ;
304292}
305293
@@ -332,15 +320,9 @@ function redactGraphqlDocument(document: GraphqlDocumentNode | undefined): strin
332320 const replacement = kind === 'String' || kind === 'BlockString' ? '"*"' : '*' ;
333321 out = out . slice ( 0 , start ) + replacement + out . slice ( end ) ;
334322 }
323+
335324 return out ;
336325 } catch {
337326 return undefined ;
338327 }
339328}
340-
341- /** Test-only: detach all channel bindings and reset module-local subscribe state. */
342- export function _resetGraphqlDiagnosticChannelsForTesting ( ) : void {
343- activeUnbinds . forEach ( unbind => unbind ( ) ) ;
344- activeUnbinds = [ ] ;
345- subscribed = false ;
346- }
0 commit comments