@@ -54,6 +54,7 @@ import {
54
54
createGraphQLSpan ,
55
55
createGraphQLValidateSpan ,
56
56
createHttpSpan ,
57
+ createSchemaLoadingSpan ,
57
58
startSubgraphExecuteFetchSpan as createSubgraphExecuteFetchSpan ,
58
59
createUpstreamHttpFetchSpan ,
59
60
recordCacheError ,
@@ -67,11 +68,15 @@ import {
67
68
setGraphQLValidateAttributes ,
68
69
setParamsAttributes ,
69
70
setResponseAttributes ,
71
+ setSchemaAttributes ,
70
72
setUpstreamFetchAttributes ,
71
73
setUpstreamFetchResponseAttributes ,
72
74
} from './spans' ;
73
75
import { getEnvVar , tryContextManagerSetup } from './utils' ;
74
76
77
+ const initializationTime =
78
+ 'performance' in globalThis ? performance . now ( ) : undefined ;
79
+
75
80
type BooleanOrPredicate < TInput = never > =
76
81
| boolean
77
82
| ( ( input : TInput ) => boolean ) ;
@@ -177,13 +182,6 @@ export type OpenTelemetryGatewayPluginOptions =
177
182
* You may specify a boolean value to enable/disable all spans, or a function to dynamically enable/disable spans based on the input.
178
183
*/
179
184
spans ?: {
180
- /**
181
- * Enable/disable Spans of internal introspection queries in proxy mode (default: true).
182
- */
183
- introspection ?: BooleanOrPredicate < {
184
- executionRequest : ExecutionRequest ;
185
- subgraphName : string ;
186
- } > ;
187
185
/**
188
186
* Enable/disable HTTP request spans (default: true).
189
187
*
@@ -231,6 +229,16 @@ export type OpenTelemetryGatewayPluginOptions =
231
229
* Enable/Disable cache related span events (default: true).
232
230
*/
233
231
cache ?: BooleanOrPredicate < { key : string ; action : 'read' | 'write' } > ;
232
+ /**
233
+ * Enable/disable schema loading spans (default: true if context manager available).
234
+ *
235
+ * Note: This span requires an Async compatible context manager
236
+ */
237
+ schema ?: boolean ;
238
+ /**
239
+ * Enable/disable initialization span (default: true).
240
+ */
241
+ initialization ?: boolean ;
234
242
} ;
235
243
} ;
236
244
@@ -283,16 +291,25 @@ export function useOpenTelemetry(
283
291
let provider : WebTracerProvider ;
284
292
285
293
const yogaVersion = createDeferred < string > ( ) ;
294
+ let initSpan : Context | null ;
286
295
287
296
function isParentEnabled ( state : State ) : boolean {
288
297
const parentState = getMostSpecificState ( state ) ;
289
298
return ! parentState || ! ! parentState . otel ;
290
299
}
291
300
292
301
function getContext ( state ?: State ) : Context {
293
- return useContextManager
294
- ? context . active ( )
295
- : ( getMostSpecificState ( state ) ?. otel ?. current ?? ROOT_CONTEXT ) ;
302
+ const specificState = getMostSpecificState ( state ) ?. otel ;
303
+
304
+ if ( initSpan && ! specificState ) {
305
+ return initSpan ;
306
+ }
307
+
308
+ if ( useContextManager ) {
309
+ return context . active ( ) ;
310
+ }
311
+
312
+ return specificState ?. current ?? ROOT_CONTEXT ;
296
313
}
297
314
298
315
const yogaLogger = createDeferred < YogaLogger > ( ) ;
@@ -370,12 +387,27 @@ export function useOpenTelemetry(
370
387
preparation$ = init ( ) . then ( ( contextManager ) => {
371
388
useContextManager = contextManager ;
372
389
tracer = options . tracer || trace . getTracer ( 'gateway' ) ;
390
+ initSpan = trace . setSpan (
391
+ context . active ( ) ,
392
+ tracer . startSpan ( 'gateway.initialization' , {
393
+ startTime : initializationTime ,
394
+ } ) ,
395
+ ) ;
373
396
preparation$ = fakePromise ( ) ;
374
397
return pluginLogger . then ( ( logger ) => {
375
398
pluginLogger = fakePromise ( logger ) ;
376
399
logger . debug (
377
400
`context manager is ${ useContextManager ? 'enabled' : 'disabled' } ` ,
378
401
) ;
402
+ if ( ! useContextManager ) {
403
+ if ( options . spans ?. schema ) {
404
+ logger . warn (
405
+ 'Schema loading spans are disabled because no context manager is available' ,
406
+ ) ;
407
+ }
408
+ options . spans = options . spans ?? { } ;
409
+ options . spans . schema = false ;
410
+ }
379
411
diag . setLogger (
380
412
{
381
413
error : ( message , ...args ) =>
@@ -453,23 +485,25 @@ export function useOpenTelemetry(
453
485
return wrapped ( ) ;
454
486
}
455
487
456
- const ctx = getContext ( parentState ) ;
457
- forOperation . otel = new OtelContextStack (
458
- createGraphQLSpan ( { tracer, ctx } ) ,
459
- ) ;
460
-
461
- if ( useContextManager ) {
462
- wrapped = context . bind ( forOperation . otel . current , wrapped ) ;
463
- }
464
-
465
488
return unfakePromise (
466
- fakePromise ( )
467
- . then ( wrapped )
468
- . catch ( ( err ) => {
469
- registerException ( forOperation . otel ?. current , err ) ;
470
- throw err ;
471
- } )
472
- . finally ( ( ) => trace . getSpan ( forOperation . otel ! . current ) ?. end ( ) ) ,
489
+ preparation$ . then ( ( ) => {
490
+ const ctx = getContext ( parentState ) ;
491
+ forOperation . otel = new OtelContextStack (
492
+ createGraphQLSpan ( { tracer, ctx } ) ,
493
+ ) ;
494
+
495
+ if ( useContextManager ) {
496
+ wrapped = context . bind ( forOperation . otel . current , wrapped ) ;
497
+ }
498
+
499
+ return fakePromise ( )
500
+ . then ( wrapped )
501
+ . catch ( ( err ) => {
502
+ registerException ( forOperation . otel ?. current , err ) ;
503
+ throw err ;
504
+ } )
505
+ . finally ( ( ) => trace . getSpan ( forOperation . otel ! . current ) ?. end ( ) ) ;
506
+ } ) ,
473
507
) ;
474
508
} ,
475
509
@@ -610,7 +644,7 @@ export function useOpenTelemetry(
610
644
parentState . forOperation ?. skipExecuteSpan ||
611
645
! shouldTrace (
612
646
isIntrospection
613
- ? options . spans ?. introspection
647
+ ? options . spans ?. schema
614
648
: options . spans ?. subgraphExecute ,
615
649
{
616
650
subgraphName,
@@ -625,7 +659,7 @@ export function useOpenTelemetry(
625
659
// (such as Introspection requests in proxy mode), we don't want to use the active context,
626
660
// we want the span to be in it's own trace.
627
661
const parentContext = isIntrospection
628
- ? ROOT_CONTEXT
662
+ ? context . active ( )
629
663
: getContext ( parentState ) ;
630
664
631
665
forSubgraphExecution . otel = new OtelContextStack (
@@ -671,29 +705,51 @@ export function useOpenTelemetry(
671
705
return wrapped ( ) ;
672
706
}
673
707
674
- const { forSubgraphExecution } = state ;
675
- const ctx = createUpstreamHttpFetchSpan ( {
676
- ctx : getContext ( state ) ,
677
- tracer,
678
- } ) ;
679
-
680
- forSubgraphExecution ?. otel ! . push ( ctx ) ;
708
+ return unfakePromise (
709
+ preparation$ . then ( ( ) => {
710
+ const { forSubgraphExecution } = state ;
711
+ const ctx = createUpstreamHttpFetchSpan ( {
712
+ ctx : getContext ( state ) ,
713
+ tracer,
714
+ } ) ;
715
+
716
+ forSubgraphExecution ?. otel ! . push ( ctx ) ;
717
+
718
+ if ( useContextManager ) {
719
+ wrapped = context . bind ( ctx , wrapped ) ;
720
+ }
721
+
722
+ return fakePromise ( )
723
+ . then ( wrapped )
724
+ . catch ( ( err ) => {
725
+ registerException ( ctx , err ) ;
726
+ throw err ;
727
+ } )
728
+ . finally ( ( ) => {
729
+ trace . getSpan ( ctx ) ?. end ( ) ;
730
+ forSubgraphExecution ?. otel ! . pop ( ) ;
731
+ } ) ;
732
+ } ) ,
733
+ ) ;
734
+ } ,
681
735
682
- if ( useContextManager ) {
683
- wrapped = context . bind ( ctx , wrapped ) ;
736
+ schema ( _ , wrapped ) {
737
+ if ( ! shouldTrace ( options . spans ?. schema , null ) ) {
738
+ return wrapped ( ) ;
684
739
}
685
740
686
741
return unfakePromise (
687
- fakePromise ( )
688
- . then ( wrapped )
689
- . catch ( ( err ) => {
690
- registerException ( ctx , err ) ;
691
- throw err ;
692
- } )
693
- . finally ( ( ) => {
694
- trace . getSpan ( ctx ) ?. end ( ) ;
695
- forSubgraphExecution ?. otel ! . pop ( ) ;
696
- } ) ,
742
+ preparation$ . then ( ( ) => {
743
+ const ctx = createSchemaLoadingSpan ( { tracer } ) ;
744
+ return fakePromise ( )
745
+ . then ( ( ) => context . with ( ctx , wrapped ) )
746
+ . catch ( ( err ) => {
747
+ trace . getSpan ( ctx ) ?. recordException ( err ) ;
748
+ } )
749
+ . finally ( ( ) => {
750
+ trace . getSpan ( ctx ) ?. end ( ) ;
751
+ } ) ;
752
+ } ) ,
697
753
) ;
698
754
} ,
699
755
} ,
@@ -863,6 +919,16 @@ export function useOpenTelemetry(
863
919
setUpstreamFetchResponseAttributes ( { ctx, response } ) ;
864
920
} ;
865
921
} ,
922
+
923
+ onSchemaChange ( payload ) {
924
+ setSchemaAttributes ( payload ) ;
925
+
926
+ if ( initSpan ) {
927
+ trace . getSpan ( initSpan ) ?. end ( ) ;
928
+ initSpan = null ;
929
+ }
930
+ } ,
931
+
866
932
async onDispose ( ) {
867
933
if ( options . initializeNodeSDK ) {
868
934
await provider ?. forceFlush ?.( ) ;
0 commit comments