1
1
import {
2
2
Attributes ,
3
+ GatewayConfigContext ,
3
4
getRetryInfo ,
4
5
isRetryExecutionRequest ,
5
6
Logger ,
@@ -214,10 +215,31 @@ const HeadersTextMapGetter: TextMapGetter<Headers> = {
214
215
} ,
215
216
} ;
216
217
218
+ export type ContextMatcher = {
219
+ request ?: Request ;
220
+ context ?: any ;
221
+ executionRequest ?: ExecutionRequest ;
222
+ } ;
223
+
224
+ export type OpenTelemetryPluginUtils = {
225
+ tracer ?: Tracer ;
226
+ getActiveContext : ( payload : ContextMatcher ) => Context ;
227
+ getHttpContext : ( request : Request ) => Context | undefined ;
228
+ getOperationContext : ( context : any ) => Context | undefined ;
229
+ getExecutionRequestContext : (
230
+ ExecutionRequest : ExecutionRequest ,
231
+ ) => Context | undefined ;
232
+ } ;
233
+
217
234
export type OpenTelemetryContextExtension = {
218
235
openTelemetry : {
219
236
tracer : Tracer ;
220
- activeContext : ( ) => Context ;
237
+ getActiveContext : ( payload ?: ContextMatcher ) => Context ;
238
+ getHttpContext : ( request ?: Request ) => Context | undefined ;
239
+ getOperationContext : ( context ?: any ) => Context | undefined ;
240
+ getExecutionRequestContext : (
241
+ ExecutionRequest : ExecutionRequest ,
242
+ ) => Context | undefined ;
221
243
} ;
222
244
} ;
223
245
@@ -229,30 +251,26 @@ type State = Partial<
229
251
HttpState < OtelState > & GraphQLState < OtelState > & GatewayState < OtelState >
230
252
> ;
231
253
232
- export type OpenTelemetryPlugin =
233
- GatewayPlugin < OpenTelemetryContextExtension > & {
234
- getOtelContext : ( payload : {
235
- request ?: Request ;
236
- context ?: any ;
237
- executionRequest ?: ExecutionRequest ;
238
- } ) => Context ;
239
- getTracer ( ) : Tracer ;
240
- } ;
254
+ export type OpenTelemetryPlugin = GatewayPlugin < OpenTelemetryContextExtension > &
255
+ OpenTelemetryPluginUtils ;
241
256
242
257
export function useOpenTelemetry (
243
- options : OpenTelemetryGatewayPluginOptions & {
244
- log : Logger ;
245
- } ,
258
+ options : OpenTelemetryGatewayPluginOptions &
259
+ // We ask for a Partial context to still allow the usage as a Yoga plugin
260
+ Partial < GatewayConfigContext > ,
246
261
) : OpenTelemetryPlugin {
247
262
const inheritContext = options . inheritContext ?? true ;
248
263
const propagateContext = options . propagateContext ?? true ;
249
264
let useContextManager : boolean ;
250
265
const traces = typeof options . traces === 'object' ? options . traces : { } ;
251
266
252
267
let tracer : Tracer ;
253
- let pluginLogger : Logger ;
254
268
let initSpan : Context | null ;
255
269
270
+ // TODO: Make it const once Yoga has the Hive Logger
271
+ let pluginLogger : Logger | undefined =
272
+ options . log && options . log . child ( '[OpenTelemetry] ' ) ;
273
+
256
274
function isParentEnabled ( state : State ) : boolean {
257
275
const parentState = getMostSpecificState ( state ) ;
258
276
return ! parentState || ! ! parentState . otel ;
@@ -304,7 +322,7 @@ export function useOpenTelemetry(
304
322
305
323
if ( ! useContextManager ) {
306
324
if ( traces . spans ?. schema ) {
307
- pluginLogger . warn (
325
+ pluginLogger ? .warn (
308
326
'Schema loading spans are disabled because no context manager is available' ,
309
327
) ;
310
328
}
@@ -314,14 +332,25 @@ export function useOpenTelemetry(
314
332
}
315
333
}
316
334
317
- return withState <
335
+ const plugin = withState <
318
336
OpenTelemetryPlugin ,
319
337
OtelState ,
320
338
OtelState & { skipExecuteSpan ?: true ; subgraphNames : string [ ] } ,
321
339
OtelState
322
340
> ( ( getState ) => ( {
323
- getTracer : ( ) => tracer ,
324
- getOtelContext : ( { state } ) => getContext ( state ) ,
341
+ get tracer ( ) {
342
+ return tracer ;
343
+ } ,
344
+ getActiveContext : ( { state } ) => getContext ( state ) ,
345
+ getHttpContext : ( request ) => {
346
+ return getState ( { request } ) . forRequest . otel ?. root ;
347
+ } ,
348
+ getOperationContext : ( context ) => {
349
+ return getState ( { context } ) . forOperation . otel ?. root ;
350
+ } ,
351
+ getExecutionRequestContext : ( executionRequest ) => {
352
+ return getState ( { executionRequest } ) . forSubgraphExecution . otel ?. root ;
353
+ } ,
325
354
instrumentation : {
326
355
request ( { state : { forRequest } , request } , wrapped ) {
327
356
if ( ! shouldTrace ( traces . spans ?. http , { request } ) ) {
@@ -648,21 +677,17 @@ export function useOpenTelemetry(
648
677
} ,
649
678
650
679
onYogaInit ( { yoga } ) {
651
- const log =
652
- options . log ??
653
- //TODO remove this when Yoga will also use the new Logger API
654
- new Logger ( {
655
- writers : [
656
- {
657
- write ( level , attrs , msg ) {
658
- level = level === 'trace' ? 'debug' : level ;
659
- yoga . logger [ level ] ( msg , attrs ) ;
660
- } ,
680
+ //TODO remove this when Yoga will also use the new Logger API
681
+ pluginLogger ??= new Logger ( {
682
+ writers : [
683
+ {
684
+ write ( level , attrs , msg ) {
685
+ level = level === 'trace' ? 'debug' : level ;
686
+ yoga . logger [ level ] ( msg , attrs ) ;
661
687
} ,
662
- ] ,
663
- } ) ;
664
-
665
- pluginLogger = log . child ( '[OpenTelemetry] ' ) ;
688
+ } ,
689
+ ] ,
690
+ } ) . child ( '[OpenTelemetry] ' ) ;
666
691
667
692
if ( options . configureDiagLogger !== false ) {
668
693
const logLevel = diagLogLevelFromEnv ( ) ; // We enable the diag only if it is explicitly enabled, as NodeSDK does
@@ -685,7 +710,9 @@ export function useOpenTelemetry(
685
710
try {
686
711
const requestId = requestIdByRequest . get ( request ) ;
687
712
if ( requestId ) {
688
- loggerForRequest ( options . log . child ( { requestId } ) , request ) ;
713
+ if ( options . log ) {
714
+ loggerForRequest ( options . log . child ( { requestId } ) , request ) ;
715
+ }
689
716
690
717
// When running in a runtime without a context manager, we have to keep track of the
691
718
// span correlated to a log manually. For now, we just link all logs for a request to
@@ -695,7 +722,7 @@ export function useOpenTelemetry(
695
722
}
696
723
}
697
724
} catch ( error ) {
698
- pluginLogger . error (
725
+ pluginLogger ! . error (
699
726
{ error } ,
700
727
'Error while setting up logger for request' ,
701
728
) ;
@@ -706,7 +733,20 @@ export function useOpenTelemetry(
706
733
extendContext ( {
707
734
openTelemetry : {
708
735
tracer,
709
- activeContext : ( ) => getContext ( state ) ,
736
+ getHttpContext : ( request ) => {
737
+ const { forRequest } = request ? getState ( { request } ) : state ;
738
+ return forRequest . otel ?. root ;
739
+ } ,
740
+ getOperationContext : ( context ) => {
741
+ const { forOperation } = context ? getState ( { context } ) : state ;
742
+ return forOperation . otel ?. root ;
743
+ } ,
744
+ getExecutionRequestContext : ( executionRequest ) => {
745
+ return getState ( { executionRequest } ) . forSubgraphExecution . otel
746
+ ?. root ;
747
+ } ,
748
+ getActiveContext : ( contextMatcher ?: Parameters < typeof getState > [ 0 ] ) =>
749
+ getContext ( contextMatcher ? getState ( contextMatcher ) : state ) ,
710
750
} ,
711
751
} ) ;
712
752
} ,
@@ -903,6 +943,16 @@ export function useOpenTelemetry(
903
943
}
904
944
} ,
905
945
} ) ) ;
946
+
947
+ if ( options . openTelemetry ) {
948
+ if ( options . openTelemetry . register ) {
949
+ options . openTelemetry ?. register ?.( plugin ) ;
950
+ } else {
951
+ options . log ?. warn ( 'An OpenTelemetry plugin is already registered' ) ;
952
+ }
953
+ }
954
+
955
+ return plugin ;
906
956
}
907
957
908
958
function shouldTrace < Args > (
0 commit comments