@graphql-hive/plugin-opentelemetry 1.0.0-alpha-2cea6e8a62aea3e45963d47c35cb6db588d78c54

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,1037 @@
1
+ import { isRetryExecutionRequest as isRetryExecutionRequest$1, getRetryInfo as getRetryInfo$1, Logger } from '@graphql-hive/gateway-runtime';
2
+ import { getHeadersObj } from '@graphql-mesh/utils';
3
+ import { getOperationASTFromDocument, isAsyncIterable, fakePromise } from '@graphql-tools/utils';
4
+ import { setGlobalErrorHandler } from '@opentelemetry/core';
5
+ import { unfakePromise } from '@whatwg-node/promise-helpers';
6
+ import { hive } from './api.js';
7
+ import { trace, SpanStatusCode, context, ROOT_CONTEXT, SpanKind, DiagLogLevel, diag, propagation } from '@opentelemetry/api';
8
+ import { hashOperation } from '@graphql-hive/core';
9
+ import { defaultPrintFn } from '@graphql-mesh/transport-common';
10
+ import { SEMATTRS_EXCEPTION_STACKTRACE, SEMATTRS_EXCEPTION_MESSAGE, SEMATTRS_EXCEPTION_TYPE, SEMATTRS_HTTP_METHOD, SEMATTRS_HTTP_URL, SEMATTRS_NET_HOST_NAME, SEMATTRS_HTTP_HOST, SEMATTRS_HTTP_ROUTE, SEMATTRS_HTTP_SCHEME, SEMATTRS_HTTP_STATUS_CODE, SEMATTRS_HTTP_USER_AGENT, SEMATTRS_HTTP_CLIENT_IP } from '@opentelemetry/semantic-conventions';
11
+ import { printSchema, TypeInfo } from 'graphql';
12
+
13
+ class OtelContextStack {
14
+ #root;
15
+ #current;
16
+ constructor(root) {
17
+ this.#root = { ctx: root };
18
+ this.#current = this.#root;
19
+ }
20
+ get current() {
21
+ return this.#current.ctx;
22
+ }
23
+ get root() {
24
+ return this.#root.ctx;
25
+ }
26
+ push = (ctx) => {
27
+ this.#current = { ctx, previous: this.#current };
28
+ };
29
+ pop = () => {
30
+ this.#current = this.#current.previous ?? this.#root;
31
+ };
32
+ toString() {
33
+ let node = this.#current;
34
+ const names = [];
35
+ while (node != void 0) {
36
+ names.push(trace.getSpan(node.ctx).name);
37
+ node = node.previous;
38
+ }
39
+ return names.join(" -> ");
40
+ }
41
+ }
42
+
43
+ function withState(pluginFactory) {
44
+ const states = {};
45
+ function getProp(scope, key) {
46
+ return {
47
+ get() {
48
+ if (!states[scope]) states[scope] = /* @__PURE__ */ new WeakMap();
49
+ let value = states[scope].get(key);
50
+ if (!value) states[scope].set(key, value = {});
51
+ return value;
52
+ },
53
+ enumerable: true
54
+ };
55
+ }
56
+ function getState(payload) {
57
+ let { executionRequest, context, request } = payload;
58
+ const state = {};
59
+ const defineState = (scope, key) => Object.defineProperty(state, scope, getProp(scope, key));
60
+ if (executionRequest) {
61
+ defineState("forSubgraphExecution", executionRequest);
62
+ if (executionRequest.context?.params) context = executionRequest.context;
63
+ }
64
+ if (context) {
65
+ defineState("forOperation", context);
66
+ if (context.request) request = context.request;
67
+ }
68
+ if (request) {
69
+ defineState("forRequest", request);
70
+ }
71
+ return state;
72
+ }
73
+ function addStateGetters(src) {
74
+ const result = {};
75
+ const properties = Object.entries(Object.getOwnPropertyDescriptors(src));
76
+ for (const [hookName, descriptor] of properties) {
77
+ const hook = descriptor.value;
78
+ if (typeof hook !== "function") {
79
+ descriptor.get &&= () => src[hookName];
80
+ descriptor.set &&= (value) => {
81
+ src[hookName] = value;
82
+ };
83
+ Object.defineProperty(result, hookName, descriptor);
84
+ } else {
85
+ result[hookName] = {
86
+ [hook.name](payload, ...args) {
87
+ return hook(
88
+ {
89
+ ...payload,
90
+ get state() {
91
+ return getState(payload);
92
+ }
93
+ },
94
+ ...args
95
+ );
96
+ }
97
+ }[hook.name];
98
+ }
99
+ }
100
+ return result;
101
+ }
102
+ const plugin = pluginFactory(getState);
103
+ const pluginWithState = addStateGetters(plugin);
104
+ pluginWithState.instrumentation = addStateGetters(plugin.instrumentation);
105
+ return pluginWithState;
106
+ }
107
+ function getMostSpecificState(state = {}) {
108
+ const { forOperation, forRequest, forSubgraphExecution } = state;
109
+ return forSubgraphExecution ?? forOperation ?? forRequest;
110
+ }
111
+
112
+ const RETRY_SYMBOL = Symbol.for("@hive-gateway/runtime/upstreamRetry");
113
+ function isRetryExecutionRequest(executionRequest) {
114
+ return !!executionRequest?.[RETRY_SYMBOL];
115
+ }
116
+ function getRetryInfo(executionRequest) {
117
+ return executionRequest[RETRY_SYMBOL];
118
+ }
119
+
120
+ const SEMATTRS_GRAPHQL_DOCUMENT = "graphql.document";
121
+ const SEMATTRS_GRAPHQL_OPERATION_TYPE = "graphql.operation.type";
122
+ const SEMATTRS_GRAPHQL_OPERATION_NAME = "graphql.operation.name";
123
+ const SEMATTRS_HIVE_GRAPHQL_OPERATION_HASH = "hive.graphql.operation.hash";
124
+ const SEMATTRS_HIVE_GRAPHQL_ERROR_COUNT = "hive.graphql.error.count";
125
+ const SEMATTRS_HIVE_GRAPHQL_ERROR_CODES = "hive.graphql.error.codes";
126
+ const SEMATTRS_HIVE_GATEWAY_UPSTREAM_SUBGRAPH_NAME = "hive.gateway.upstream.subgraph.name";
127
+ const SEMATTRS_HIVE_GATEWAY_OPERATION_SUBGRAPH_NAMES = "hive.gateway.operation.subgraph.names";
128
+
129
+ function createHttpSpan(input) {
130
+ const { url, request, tracer } = input;
131
+ const span = tracer.startSpan(
132
+ `${request.method || "GET"} ${url.pathname}`,
133
+ {
134
+ attributes: {
135
+ [SEMATTRS_HTTP_METHOD]: request.method || "GET",
136
+ [SEMATTRS_HTTP_URL]: request.url,
137
+ [SEMATTRS_HTTP_ROUTE]: url.pathname,
138
+ [SEMATTRS_HTTP_SCHEME]: url.protocol,
139
+ [SEMATTRS_NET_HOST_NAME]: url.hostname || url.host || request.headers.get("host") || "localhost",
140
+ [SEMATTRS_HTTP_HOST]: url.host || request.headers.get("host") || void 0,
141
+ [SEMATTRS_HTTP_CLIENT_IP]: request.headers.get("x-forwarded-for")?.split(",")[0],
142
+ [SEMATTRS_HTTP_USER_AGENT]: request.headers.get("user-agent") || void 0,
143
+ "hive.client.name": request.headers.get("x-graphql-client-name") || void 0,
144
+ "hive.client.version": request.headers.get("x-graphql-client-version") || void 0
145
+ },
146
+ kind: SpanKind.SERVER
147
+ },
148
+ input.ctx
149
+ );
150
+ return {
151
+ ctx: trace.setSpan(input.ctx, span)
152
+ };
153
+ }
154
+ function setResponseAttributes(ctx, response) {
155
+ const span = trace.getSpan(ctx);
156
+ if (span) {
157
+ span.setAttribute(SEMATTRS_HTTP_STATUS_CODE, response.status);
158
+ span.setAttribute(
159
+ "gateway.cache.response_cache",
160
+ response.status === 304 && response.headers.get("ETag") ? "hit" : "miss"
161
+ );
162
+ span.setStatus({
163
+ code: response.ok ? SpanStatusCode.OK : SpanStatusCode.ERROR,
164
+ message: response.ok ? void 0 : response.statusText
165
+ });
166
+ }
167
+ }
168
+ function createGraphQLSpan(input) {
169
+ const span = input.tracer.startSpan(
170
+ `graphql.operation`,
171
+ { kind: SpanKind.INTERNAL },
172
+ input.ctx
173
+ );
174
+ return trace.setSpan(input.ctx, span);
175
+ }
176
+ function setParamsAttributes(input) {
177
+ const { ctx, params } = input;
178
+ const span = trace.getSpan(ctx);
179
+ if (!span) {
180
+ return;
181
+ }
182
+ span.setAttribute(SEMATTRS_GRAPHQL_DOCUMENT, params.query ?? "<undefined>");
183
+ if (params.operationName) {
184
+ span.setAttribute(SEMATTRS_GRAPHQL_OPERATION_NAME, params.operationName);
185
+ }
186
+ }
187
+ const typeInfos = /* @__PURE__ */ new WeakMap();
188
+ const defaultOperationHashingFn = (input) => {
189
+ if (!typeInfos.has(input.schema)) {
190
+ typeInfos.set(input.schema, new TypeInfo(input.schema));
191
+ }
192
+ const typeInfo = typeInfos.get(input.schema);
193
+ return hashOperation({
194
+ documentNode: input.document,
195
+ operationName: input.operationName ?? null,
196
+ schema: input.schema,
197
+ variables: null,
198
+ // Unstable feature, not using it for now
199
+ typeInfo
200
+ });
201
+ };
202
+ function setExecutionAttributesOnOperationSpan(input) {
203
+ const { hashOperationFn = defaultOperationHashingFn, args, ctx } = input;
204
+ const span = trace.getSpan(ctx);
205
+ if (span) {
206
+ const operation = getOperationASTFromDocument(
207
+ args.document,
208
+ args.operationName || void 0
209
+ );
210
+ span.setAttribute(SEMATTRS_GRAPHQL_OPERATION_TYPE, operation.operation);
211
+ const document = defaultPrintFn(args.document);
212
+ span.setAttribute(SEMATTRS_GRAPHQL_DOCUMENT, document);
213
+ const hash = hashOperationFn?.({ ...args });
214
+ if (hash) {
215
+ span.setAttribute(SEMATTRS_HIVE_GRAPHQL_OPERATION_HASH, hash);
216
+ }
217
+ const operationName = operation.name?.value;
218
+ if (operationName) {
219
+ span.setAttribute(SEMATTRS_GRAPHQL_OPERATION_NAME, operationName);
220
+ span.updateName(`graphql.operation ${operationName}`);
221
+ }
222
+ }
223
+ }
224
+ function createGraphqlContextBuildingSpan(input) {
225
+ const span = input.tracer.startSpan(
226
+ "graphql.context",
227
+ { kind: SpanKind.INTERNAL },
228
+ input.ctx
229
+ );
230
+ return trace.setSpan(input.ctx, span);
231
+ }
232
+ function createGraphQLParseSpan(input) {
233
+ const span = input.tracer.startSpan(
234
+ "graphql.parse",
235
+ {
236
+ kind: SpanKind.INTERNAL
237
+ },
238
+ input.ctx
239
+ );
240
+ return trace.setSpan(input.ctx, span);
241
+ }
242
+ function setGraphQLParseAttributes(input) {
243
+ const span = trace.getSpan(input.ctx);
244
+ if (!span) {
245
+ return;
246
+ }
247
+ if (input.query) {
248
+ span.setAttribute(SEMATTRS_GRAPHQL_DOCUMENT, input.query);
249
+ }
250
+ if (input.operationName) {
251
+ span.setAttribute(SEMATTRS_GRAPHQL_OPERATION_NAME, input.operationName);
252
+ }
253
+ if (input.result instanceof Error) {
254
+ span.setAttribute(SEMATTRS_HIVE_GRAPHQL_ERROR_COUNT, 1);
255
+ }
256
+ }
257
+ function createGraphQLValidateSpan(input) {
258
+ const span = input.tracer.startSpan(
259
+ "graphql.validate",
260
+ {
261
+ attributes: {
262
+ [SEMATTRS_GRAPHQL_DOCUMENT]: input.query,
263
+ [SEMATTRS_GRAPHQL_OPERATION_NAME]: input.operationName
264
+ },
265
+ kind: SpanKind.INTERNAL
266
+ },
267
+ input.ctx
268
+ );
269
+ return trace.setSpan(input.ctx, span);
270
+ }
271
+ function setGraphQLValidateAttributes(input) {
272
+ const { result, ctx } = input;
273
+ const span = trace.getSpan(ctx);
274
+ if (!span) {
275
+ return;
276
+ }
277
+ if (result instanceof Error) {
278
+ span.setStatus({
279
+ code: SpanStatusCode.ERROR,
280
+ message: result.message
281
+ });
282
+ } else if (Array.isArray(result) && result.length > 0) {
283
+ span.setAttribute(SEMATTRS_HIVE_GRAPHQL_ERROR_COUNT, result.length);
284
+ span.setStatus({
285
+ code: SpanStatusCode.ERROR,
286
+ message: result.map((e) => e.message).join(", ")
287
+ });
288
+ for (const error in result) {
289
+ span.recordException(error);
290
+ }
291
+ }
292
+ }
293
+ function createGraphQLExecuteSpan(input) {
294
+ const span = input.tracer.startSpan(
295
+ "graphql.execute",
296
+ { kind: SpanKind.INTERNAL },
297
+ input.ctx
298
+ );
299
+ return trace.setSpan(input.ctx, span);
300
+ }
301
+ function setGraphQLExecutionAttributes(input) {
302
+ const { ctx, args } = input;
303
+ const span = trace.getSpan(ctx);
304
+ if (!span) {
305
+ return;
306
+ }
307
+ const operation = getOperationASTFromDocument(
308
+ args.document,
309
+ args.operationName || void 0
310
+ );
311
+ span.setAttribute(SEMATTRS_GRAPHQL_OPERATION_TYPE, operation.operation);
312
+ const operationName = operation.name?.value;
313
+ if (operationName) {
314
+ span.setAttribute(SEMATTRS_GRAPHQL_OPERATION_NAME, operationName);
315
+ }
316
+ const document = defaultPrintFn(input.args.document);
317
+ span.setAttribute(SEMATTRS_GRAPHQL_DOCUMENT, document);
318
+ }
319
+ function setGraphQLExecutionResultAttributes(input) {
320
+ const { ctx, result } = input;
321
+ const span = trace.getSpan(ctx);
322
+ if (!span) {
323
+ return;
324
+ }
325
+ if (input.subgraphNames?.length) {
326
+ span.setAttribute(
327
+ SEMATTRS_HIVE_GATEWAY_OPERATION_SUBGRAPH_NAMES,
328
+ input.subgraphNames
329
+ );
330
+ }
331
+ if (!isAsyncIterable(result) && // FIXME: Handle async iterable too
332
+ result.errors && result.errors.length > 0) {
333
+ span.setAttribute(SEMATTRS_HIVE_GRAPHQL_ERROR_COUNT, result.errors.length);
334
+ span.setStatus({
335
+ code: SpanStatusCode.ERROR,
336
+ message: result.errors.map((e) => e.message).join(", ")
337
+ });
338
+ const codes = [];
339
+ for (const error of result.errors) {
340
+ span.recordException(error);
341
+ if (error.extensions["code"]) {
342
+ codes.push(`${error.extensions["code"]}`);
343
+ }
344
+ }
345
+ if (codes.length > 0) {
346
+ span.setAttribute(SEMATTRS_HIVE_GRAPHQL_ERROR_CODES, codes);
347
+ }
348
+ }
349
+ }
350
+ function createSubgraphExecuteSpan(input) {
351
+ const operation = getOperationASTFromDocument(
352
+ input.executionRequest.document,
353
+ input.executionRequest.operationName
354
+ );
355
+ const span = input.tracer.startSpan(
356
+ `subgraph.execute (${input.subgraphName})`,
357
+ {
358
+ attributes: {
359
+ [SEMATTRS_GRAPHQL_OPERATION_NAME]: operation.name?.value,
360
+ [SEMATTRS_GRAPHQL_DOCUMENT]: defaultPrintFn(
361
+ input.executionRequest.document
362
+ ),
363
+ [SEMATTRS_GRAPHQL_OPERATION_TYPE]: operation.operation,
364
+ [SEMATTRS_HIVE_GATEWAY_UPSTREAM_SUBGRAPH_NAME]: input.subgraphName
365
+ },
366
+ kind: SpanKind.CLIENT
367
+ },
368
+ input.ctx
369
+ );
370
+ return trace.setSpan(input.ctx, span);
371
+ }
372
+ function createUpstreamHttpFetchSpan(input) {
373
+ const span = input.tracer.startSpan(
374
+ "http.fetch",
375
+ {
376
+ attributes: {},
377
+ kind: SpanKind.CLIENT
378
+ },
379
+ input.ctx
380
+ );
381
+ return trace.setSpan(input.ctx, span);
382
+ }
383
+ function setUpstreamFetchAttributes(input) {
384
+ const { ctx, url, options: fetchOptions } = input;
385
+ const span = trace.getSpan(ctx);
386
+ if (!span) {
387
+ return;
388
+ }
389
+ const urlObj = new URL(input.url);
390
+ span.setAttribute(SEMATTRS_HTTP_METHOD, fetchOptions.method ?? "GET");
391
+ span.setAttribute(SEMATTRS_HTTP_URL, url);
392
+ span.setAttribute(SEMATTRS_NET_HOST_NAME, urlObj.hostname);
393
+ span.setAttribute(SEMATTRS_HTTP_HOST, urlObj.host);
394
+ span.setAttribute(SEMATTRS_HTTP_ROUTE, urlObj.pathname);
395
+ span.setAttribute(SEMATTRS_HTTP_SCHEME, urlObj.protocol);
396
+ if (input.executionRequest && isRetryExecutionRequest(input.executionRequest)) {
397
+ const { attempt } = getRetryInfo(input.executionRequest);
398
+ if (attempt > 0) {
399
+ span.setAttribute("http.request.resend_count", attempt);
400
+ }
401
+ }
402
+ }
403
+ function setUpstreamFetchResponseAttributes(input) {
404
+ const { ctx, response } = input;
405
+ const span = trace.getSpan(ctx);
406
+ if (!span) {
407
+ return;
408
+ }
409
+ span.setAttribute(SEMATTRS_HTTP_STATUS_CODE, response.status);
410
+ span.setStatus({
411
+ code: response.ok ? SpanStatusCode.OK : SpanStatusCode.ERROR,
412
+ message: response.ok ? void 0 : response.statusText
413
+ });
414
+ }
415
+ function recordCacheEvent(event, payload) {
416
+ trace.getActiveSpan()?.addEvent("gateway.cache." + event, {
417
+ "gateway.cache.key": payload.key,
418
+ "gateway.cache.ttl": payload.ttl
419
+ });
420
+ }
421
+ function recordCacheError(action, error, payload) {
422
+ trace.getActiveSpan()?.addEvent("gateway.cache.error", {
423
+ "gateway.cache.key": payload.key,
424
+ "gateway.cache.ttl": payload.ttl,
425
+ "gateway.cache.action": action,
426
+ [SEMATTRS_EXCEPTION_TYPE]: "code" in error ? error.code : error.message,
427
+ [SEMATTRS_EXCEPTION_MESSAGE]: error.message,
428
+ [SEMATTRS_EXCEPTION_STACKTRACE]: error.stack
429
+ });
430
+ }
431
+ const responseCacheSymbol = Symbol.for("servedFromResponseCache");
432
+ function setExecutionResultAttributes(input) {
433
+ const span = trace.getSpan(input.ctx);
434
+ if (input.result && span) {
435
+ span.setAttribute(
436
+ "gateway.cache.response_cache",
437
+ input.result[responseCacheSymbol] ? "hit" : "miss"
438
+ );
439
+ }
440
+ }
441
+ function createSchemaLoadingSpan(inputs) {
442
+ const span = inputs.tracer.startSpan(
443
+ "gateway.schema",
444
+ { attributes: { "gateway.schema.changed": false } },
445
+ inputs.ctx
446
+ );
447
+ const currentContext = context.active();
448
+ if (currentContext !== inputs.ctx) {
449
+ const currentSpan = trace.getActiveSpan();
450
+ currentSpan?.addLink({ context: span.spanContext() });
451
+ }
452
+ return trace.setSpan(ROOT_CONTEXT, span);
453
+ }
454
+ function setSchemaAttributes(inputs) {
455
+ const span = trace.getActiveSpan();
456
+ if (!span) {
457
+ return;
458
+ }
459
+ span.setAttribute("gateway.schema.changed", true);
460
+ span.setAttribute("graphql.schema", printSchema(inputs.schema));
461
+ }
462
+ function registerException(ctx, error) {
463
+ const span = ctx && trace.getSpan(ctx);
464
+ if (!span) {
465
+ return;
466
+ }
467
+ const message = error?.message?.toString() ?? error?.toString();
468
+ span.setStatus({ code: SpanStatusCode.ERROR, message });
469
+ span.recordException(error);
470
+ }
471
+
472
+ function getEnvStr(key, opts = {}) {
473
+ const globalThat = opts.globalThis ?? globalThis;
474
+ let variable = globalThat.process?.env?.[key] || // @ts-expect-error can exist in wrangler and maybe other runtimes
475
+ globalThat.env?.[key] || // @ts-expect-error can exist in deno
476
+ globalThat.Deno?.env?.get(key) || // @ts-expect-error could be
477
+ globalThat[key];
478
+ if (variable != null) {
479
+ variable += "";
480
+ } else {
481
+ variable = void 0;
482
+ }
483
+ return variable?.trim();
484
+ }
485
+ function getEnvBool(key, opts = {}) {
486
+ return strToBool(getEnvStr(key, opts));
487
+ }
488
+ function strToBool(str) {
489
+ return ["1", "t", "true", "y", "yes", "on", "enabled"].includes(
490
+ (str || "").toLowerCase()
491
+ );
492
+ }
493
+
494
+ function isContextManagerCompatibleWithAsync() {
495
+ const symbol = Symbol();
496
+ const root = context.active();
497
+ return context.with(root.setValue(symbol, true), () => {
498
+ return new Promise((resolve) => {
499
+ setTimeout(() => {
500
+ resolve(context.active().getValue(symbol) || false);
501
+ });
502
+ });
503
+ });
504
+ }
505
+ const logLevelMap = {
506
+ ALL: DiagLogLevel.ALL,
507
+ VERBOSE: DiagLogLevel.VERBOSE,
508
+ DEBUG: DiagLogLevel.DEBUG,
509
+ INFO: DiagLogLevel.INFO,
510
+ WARN: DiagLogLevel.WARN,
511
+ ERROR: DiagLogLevel.ERROR,
512
+ NONE: DiagLogLevel.NONE
513
+ };
514
+ function diagLogLevelFromEnv() {
515
+ const value = getEnvStr("OTEL_LOG_LEVEL");
516
+ if (value == null) {
517
+ return void 0;
518
+ }
519
+ const resolvedLogLevel = logLevelMap[value.toUpperCase()];
520
+ if (resolvedLogLevel == null) {
521
+ diag.warn(
522
+ `Unknown log level "${value}", expected one of ${Object.keys(logLevelMap)}, using default`
523
+ );
524
+ return DiagLogLevel.INFO;
525
+ }
526
+ return resolvedLogLevel;
527
+ }
528
+
529
+ const initializationTime = "performance" in globalThis ? performance.now() : void 0;
530
+ const otelCtxForRequestId = /* @__PURE__ */ new Map();
531
+ const HeadersTextMapGetter = {
532
+ keys(carrier) {
533
+ return [...carrier.keys()];
534
+ },
535
+ get(carrier, key) {
536
+ return carrier.get(key) || void 0;
537
+ }
538
+ };
539
+ function useOpenTelemetry(options) {
540
+ const inheritContext = options.inheritContext ?? true;
541
+ const propagateContext = options.propagateContext ?? true;
542
+ let useContextManager;
543
+ const traces = typeof options.traces === "object" ? options.traces : {};
544
+ let tracer;
545
+ let initSpan;
546
+ let pluginLogger = options.log && options.log.child("[OpenTelemetry] ");
547
+ function isParentEnabled(state) {
548
+ const parentState = getMostSpecificState(state);
549
+ return !parentState || !!parentState.otel;
550
+ }
551
+ function getContext(state) {
552
+ const specificState = getMostSpecificState(state)?.otel;
553
+ if (initSpan && !specificState) {
554
+ return initSpan;
555
+ }
556
+ if (useContextManager) {
557
+ return context.active();
558
+ }
559
+ return specificState?.current ?? ROOT_CONTEXT;
560
+ }
561
+ let preparation$ = init();
562
+ preparation$.then(() => {
563
+ preparation$ = fakePromise();
564
+ });
565
+ async function init() {
566
+ if (options.useContextManager !== false && !await isContextManagerCompatibleWithAsync()) {
567
+ useContextManager = false;
568
+ if (options.useContextManager === true) {
569
+ throw new Error(
570
+ "[OTEL] Context Manager usage is enabled, but the registered one is not compatible with async calls. Please use another context manager, such as `AsyncLocalStorageContextManager`."
571
+ );
572
+ }
573
+ } else {
574
+ useContextManager = options.useContextManager ?? true;
575
+ }
576
+ tracer = traces.tracer || trace.getTracer("gateway");
577
+ initSpan = trace.setSpan(
578
+ context.active(),
579
+ tracer.startSpan("gateway.initialization", {
580
+ startTime: initializationTime
581
+ })
582
+ );
583
+ if (!useContextManager) {
584
+ if (traces.spans?.schema) {
585
+ pluginLogger?.warn(
586
+ "Schema loading spans are disabled because no context manager is available"
587
+ );
588
+ }
589
+ traces.spans = traces.spans ?? {};
590
+ traces.spans.schema = false;
591
+ }
592
+ }
593
+ const plugin = withState((getState) => ({
594
+ get tracer() {
595
+ return tracer;
596
+ },
597
+ getActiveContext: ({ state }) => getContext(state),
598
+ getHttpContext: (request) => {
599
+ return getState({ request }).forRequest.otel?.root;
600
+ },
601
+ getOperationContext: (context2) => {
602
+ return getState({ context: context2 }).forOperation.otel?.root;
603
+ },
604
+ getExecutionRequestContext: (executionRequest) => {
605
+ return getState({ executionRequest }).forSubgraphExecution.otel?.root;
606
+ },
607
+ instrumentation: {
608
+ request({ state: { forRequest }, request }, wrapped) {
609
+ if (!shouldTrace(traces.spans?.http, { request })) {
610
+ return wrapped();
611
+ }
612
+ const url = getURL(request);
613
+ return unfakePromise(
614
+ preparation$.then(() => {
615
+ const ctx = inheritContext ? propagation.extract(
616
+ context.active(),
617
+ request.headers,
618
+ HeadersTextMapGetter
619
+ ) : context.active();
620
+ forRequest.otel = new OtelContextStack(
621
+ createHttpSpan({ ctx, request, tracer, url }).ctx
622
+ );
623
+ if (useContextManager) {
624
+ wrapped = context.bind(forRequest.otel.current, wrapped);
625
+ }
626
+ return wrapped();
627
+ }).catch((error) => {
628
+ registerException(forRequest.otel?.current, error);
629
+ throw error;
630
+ }).finally(() => {
631
+ const ctx = forRequest.otel?.root;
632
+ ctx && trace.getSpan(ctx)?.end();
633
+ })
634
+ );
635
+ },
636
+ operation({ context: gqlCtx, state: { forOperation, ...parentState } }, wrapped) {
637
+ if (!isParentEnabled(parentState) || !shouldTrace(traces.spans?.graphql, { context: gqlCtx })) {
638
+ return wrapped();
639
+ }
640
+ return unfakePromise(
641
+ preparation$.then(() => {
642
+ const ctx = getContext(parentState);
643
+ forOperation.otel = new OtelContextStack(
644
+ createGraphQLSpan({ tracer, ctx })
645
+ );
646
+ if (useContextManager) {
647
+ wrapped = context.bind(forOperation.otel.current, wrapped);
648
+ }
649
+ return fakePromise().then(wrapped).catch((err) => {
650
+ registerException(forOperation.otel?.current, err);
651
+ throw err;
652
+ }).finally(() => trace.getSpan(forOperation.otel.current)?.end());
653
+ })
654
+ );
655
+ },
656
+ context({ state, context: gqlCtx }, wrapped) {
657
+ if (!isParentEnabled(state) || !shouldTrace(traces.spans?.graphqlContextBuilding, {
658
+ context: gqlCtx
659
+ })) {
660
+ return wrapped();
661
+ }
662
+ const { forOperation } = state;
663
+ const ctx = getContext(state);
664
+ forOperation.otel.push(
665
+ createGraphqlContextBuildingSpan({ ctx, tracer })
666
+ );
667
+ if (useContextManager) {
668
+ wrapped = context.bind(forOperation.otel.current, wrapped);
669
+ }
670
+ try {
671
+ wrapped();
672
+ } catch (err) {
673
+ registerException(forOperation.otel?.current, err);
674
+ throw err;
675
+ } finally {
676
+ trace.getSpan(forOperation.otel.current)?.end();
677
+ forOperation.otel.pop();
678
+ }
679
+ },
680
+ parse({ state, context: gqlCtx }, wrapped) {
681
+ if (!isParentEnabled(state) || !shouldTrace(traces.spans?.graphqlParse, { context: gqlCtx })) {
682
+ return wrapped();
683
+ }
684
+ const ctx = getContext(state);
685
+ const { forOperation } = state;
686
+ forOperation.otel.push(createGraphQLParseSpan({ ctx, tracer }));
687
+ if (useContextManager) {
688
+ wrapped = context.bind(forOperation.otel.current, wrapped);
689
+ }
690
+ try {
691
+ wrapped();
692
+ } catch (err) {
693
+ registerException(forOperation.otel.current, err);
694
+ throw err;
695
+ } finally {
696
+ trace.getSpan(forOperation.otel.current)?.end();
697
+ forOperation.otel.pop();
698
+ }
699
+ },
700
+ validate({ state, context: gqlCtx }, wrapped) {
701
+ if (!isParentEnabled(state) || !shouldTrace(traces.spans?.graphqlValidate, { context: gqlCtx })) {
702
+ return wrapped();
703
+ }
704
+ const { forOperation } = state;
705
+ forOperation.otel.push(
706
+ createGraphQLValidateSpan({
707
+ ctx: getContext(state),
708
+ tracer,
709
+ query: gqlCtx.params.query?.trim(),
710
+ operationName: gqlCtx.params.operationName
711
+ })
712
+ );
713
+ if (useContextManager) {
714
+ wrapped = context.bind(forOperation.otel.current, wrapped);
715
+ }
716
+ try {
717
+ wrapped();
718
+ } catch (err) {
719
+ registerException(forOperation.otel?.current, err);
720
+ throw err;
721
+ } finally {
722
+ trace.getSpan(forOperation.otel.current)?.end();
723
+ forOperation.otel.pop();
724
+ }
725
+ },
726
+ execute({ state, context: gqlCtx }, wrapped) {
727
+ if (!isParentEnabled(state) || !shouldTrace(traces.spans?.graphqlExecute, { context: gqlCtx })) {
728
+ state.forOperation.skipExecuteSpan = true;
729
+ return wrapped();
730
+ }
731
+ const ctx = getContext(state);
732
+ const { forOperation } = state;
733
+ forOperation.otel?.push(createGraphQLExecuteSpan({ ctx, tracer }));
734
+ if (useContextManager) {
735
+ wrapped = context.bind(forOperation.otel.current, wrapped);
736
+ }
737
+ return unfakePromise(
738
+ fakePromise().then(wrapped).catch((err) => {
739
+ registerException(forOperation.otel.current, err);
740
+ throw err;
741
+ }).finally(() => {
742
+ trace.getSpan(forOperation.otel.current)?.end();
743
+ forOperation.otel.pop();
744
+ })
745
+ );
746
+ },
747
+ subgraphExecute({
748
+ state: { forSubgraphExecution, ...parentState },
749
+ executionRequest,
750
+ subgraphName
751
+ }, wrapped) {
752
+ const isIntrospection = !executionRequest.context.params;
753
+ if (!isParentEnabled(parentState) || parentState.forOperation?.skipExecuteSpan || !shouldTrace(
754
+ isIntrospection ? traces.spans?.schema : traces.spans?.subgraphExecute,
755
+ {
756
+ subgraphName,
757
+ executionRequest
758
+ }
759
+ )) {
760
+ return wrapped();
761
+ }
762
+ const parentContext = isIntrospection ? context.active() : getContext(parentState);
763
+ forSubgraphExecution.otel = new OtelContextStack(
764
+ createSubgraphExecuteSpan({
765
+ ctx: parentContext,
766
+ tracer,
767
+ executionRequest,
768
+ subgraphName
769
+ })
770
+ );
771
+ if (useContextManager) {
772
+ wrapped = context.bind(forSubgraphExecution.otel.current, wrapped);
773
+ }
774
+ return unfakePromise(
775
+ fakePromise().then(wrapped).catch((err) => {
776
+ registerException(forSubgraphExecution.otel.current, err);
777
+ throw err;
778
+ }).finally(() => {
779
+ trace.getSpan(forSubgraphExecution.otel.current)?.end();
780
+ forSubgraphExecution.otel.pop();
781
+ })
782
+ );
783
+ },
784
+ fetch({ state, executionRequest }, wrapped) {
785
+ if (isRetryExecutionRequest$1(executionRequest)) {
786
+ state = getState(getRetryInfo$1(executionRequest));
787
+ }
788
+ if (!isParentEnabled(state) || !shouldTrace(traces.spans?.upstreamFetch, { executionRequest })) {
789
+ return wrapped();
790
+ }
791
+ return unfakePromise(
792
+ preparation$.then(() => {
793
+ const { forSubgraphExecution } = state;
794
+ const ctx = createUpstreamHttpFetchSpan({
795
+ ctx: getContext(state),
796
+ tracer
797
+ });
798
+ forSubgraphExecution?.otel.push(ctx);
799
+ if (useContextManager) {
800
+ wrapped = context.bind(ctx, wrapped);
801
+ }
802
+ return fakePromise().then(wrapped).catch((err) => {
803
+ registerException(ctx, err);
804
+ throw err;
805
+ }).finally(() => {
806
+ trace.getSpan(ctx)?.end();
807
+ forSubgraphExecution?.otel.pop();
808
+ });
809
+ })
810
+ );
811
+ },
812
+ schema(_, wrapped) {
813
+ if (!shouldTrace(traces.spans?.schema, null)) {
814
+ return wrapped();
815
+ }
816
+ return unfakePromise(
817
+ preparation$.then(() => {
818
+ const ctx = createSchemaLoadingSpan({
819
+ ctx: initSpan ?? ROOT_CONTEXT,
820
+ tracer
821
+ });
822
+ return fakePromise().then(() => context.with(ctx, wrapped)).catch((err) => {
823
+ trace.getSpan(ctx)?.recordException(err);
824
+ }).finally(() => {
825
+ trace.getSpan(ctx)?.end();
826
+ });
827
+ })
828
+ );
829
+ }
830
+ },
831
+ onYogaInit({ yoga }) {
832
+ pluginLogger ??= new Logger({
833
+ writers: [
834
+ {
835
+ write(level, attrs, msg) {
836
+ level = level === "trace" ? "debug" : level;
837
+ yoga.logger[level](msg, attrs);
838
+ }
839
+ }
840
+ ]
841
+ }).child("[OpenTelemetry] ");
842
+ if (options.configureDiagLogger !== false) {
843
+ const logLevel = diagLogLevelFromEnv();
844
+ if (logLevel) {
845
+ const diagLog = pluginLogger.child("[diag] ");
846
+ diagLog.verbose = diagLog.trace;
847
+ diag.setLogger(diagLog, logLevel);
848
+ setGlobalErrorHandler((err) => diagLog.error(err));
849
+ }
850
+ }
851
+ pluginLogger.debug(
852
+ `context manager is ${useContextManager ? "enabled" : "disabled"}`
853
+ );
854
+ },
855
+ onRequest({ state, serverContext }) {
856
+ if (!useContextManager) {
857
+ const requestId = (
858
+ // TODO: serverContext.log will not be available in Yoga, this will be fixed when Hive Logger is integrated into Yoga
859
+ serverContext.log?.attrs?.[
860
+ // @ts-expect-error even if the attrs is an array this will work
861
+ "requestId"
862
+ ]
863
+ );
864
+ if (typeof requestId === "string") {
865
+ otelCtxForRequestId.set(requestId, getContext(state));
866
+ }
867
+ }
868
+ },
869
+ onEnveloped({ state, extendContext }) {
870
+ extendContext({
871
+ openTelemetry: {
872
+ tracer,
873
+ getHttpContext: (request) => {
874
+ const { forRequest } = request ? getState({ request }) : state;
875
+ return forRequest.otel?.root;
876
+ },
877
+ getOperationContext: (context2) => {
878
+ const { forOperation } = context2 ? getState({ context: context2 }) : state;
879
+ return forOperation.otel?.root;
880
+ },
881
+ getExecutionRequestContext: (executionRequest) => {
882
+ return getState({ executionRequest }).forSubgraphExecution.otel?.root;
883
+ },
884
+ getActiveContext: (contextMatcher) => getContext(contextMatcher ? getState(contextMatcher) : state)
885
+ }
886
+ });
887
+ },
888
+ onCacheGet: (payload) => shouldTrace(traces.events?.cache, { key: payload.key, action: "read" }) ? {
889
+ onCacheMiss: () => recordCacheEvent("miss", payload),
890
+ onCacheHit: () => recordCacheEvent("hit", payload),
891
+ onCacheGetError: ({ error }) => recordCacheError("read", error, payload)
892
+ } : void 0,
893
+ onCacheSet: (payload) => shouldTrace(traces.events?.cache, { key: payload.key, action: "write" }) ? {
894
+ onCacheSetDone: () => recordCacheEvent("write", payload),
895
+ onCacheSetError: ({ error }) => recordCacheError("write", error, payload)
896
+ } : void 0,
897
+ onResponse({ response, state, serverContext }) {
898
+ state.forRequest.otel && setResponseAttributes(state.forRequest.otel.root, response);
899
+ if (!useContextManager) {
900
+ const requestId = (
901
+ // TODO: serverContext.log will not be available in Yoga, this will be fixed when Hive Logger is integrated into Yoga
902
+ serverContext.log?.attrs?.[
903
+ // @ts-expect-error even if the attrs is an array this will work
904
+ "requestId"
905
+ ]
906
+ );
907
+ if (typeof requestId === "string") {
908
+ otelCtxForRequestId.delete(requestId);
909
+ }
910
+ }
911
+ },
912
+ onParams({ state, context: gqlCtx, params }) {
913
+ if (!isParentEnabled(state) || !shouldTrace(traces.spans?.graphql, { context: gqlCtx })) {
914
+ return;
915
+ }
916
+ const ctx = getContext(state);
917
+ setParamsAttributes({ ctx, params });
918
+ },
919
+ onExecutionResult({ result, context: gqlCtx, state }) {
920
+ if (!isParentEnabled(state) || !shouldTrace(traces.spans?.graphql, { context: gqlCtx })) {
921
+ return;
922
+ }
923
+ setExecutionResultAttributes({ ctx: getContext(state), result });
924
+ },
925
+ onParse({ state, context: gqlCtx }) {
926
+ if (!isParentEnabled(state) || !shouldTrace(traces.spans?.graphqlParse, { context: gqlCtx })) {
927
+ return;
928
+ }
929
+ return ({ result }) => {
930
+ setGraphQLParseAttributes({
931
+ ctx: getContext(state),
932
+ operationName: gqlCtx.params.operationName,
933
+ query: gqlCtx.params.query?.trim(),
934
+ result
935
+ });
936
+ };
937
+ },
938
+ onValidate({ state, context: gqlCtx }) {
939
+ if (!isParentEnabled(state) || !shouldTrace(traces.spans?.graphqlValidate, { context: gqlCtx })) {
940
+ return;
941
+ }
942
+ return ({ result }) => {
943
+ setGraphQLValidateAttributes({ ctx: getContext(state), result });
944
+ };
945
+ },
946
+ onExecute({ state, args }) {
947
+ if (!isParentEnabled(state)) {
948
+ return;
949
+ }
950
+ setExecutionAttributesOnOperationSpan({
951
+ ctx: state.forOperation.otel.root,
952
+ args,
953
+ hashOperationFn: options.hashOperation
954
+ });
955
+ if (state.forOperation.skipExecuteSpan) {
956
+ return;
957
+ }
958
+ const ctx = getContext(state);
959
+ setGraphQLExecutionAttributes({ ctx, args });
960
+ state.forOperation.subgraphNames = [];
961
+ return {
962
+ onExecuteDone({ result }) {
963
+ setGraphQLExecutionResultAttributes({
964
+ ctx,
965
+ result,
966
+ subgraphNames: state.forOperation.subgraphNames
967
+ });
968
+ }
969
+ };
970
+ },
971
+ onSubgraphExecute({ subgraphName, state }) {
972
+ state.forOperation?.subgraphNames?.push(subgraphName);
973
+ },
974
+ onFetch(payload) {
975
+ const { url, setFetchFn, fetchFn, executionRequest } = payload;
976
+ let { state } = payload;
977
+ if (executionRequest && isRetryExecutionRequest$1(executionRequest)) {
978
+ state = getState(getRetryInfo$1(executionRequest));
979
+ }
980
+ if (propagateContext) {
981
+ setFetchFn((url2, options2, ...args) => {
982
+ const reqHeaders = getHeadersObj(options2?.headers || {});
983
+ propagation.inject(getContext(state), reqHeaders);
984
+ return fetchFn(url2, { ...options2, headers: reqHeaders }, ...args);
985
+ });
986
+ }
987
+ if (!isParentEnabled(state) || !shouldTrace(traces.spans?.upstreamFetch, { executionRequest })) {
988
+ return;
989
+ }
990
+ const ctx = getContext(state);
991
+ setUpstreamFetchAttributes({
992
+ ctx,
993
+ url,
994
+ options: payload.options,
995
+ executionRequest
996
+ });
997
+ return ({ response }) => {
998
+ setUpstreamFetchResponseAttributes({ ctx, response });
999
+ };
1000
+ },
1001
+ onSchemaChange(payload) {
1002
+ setSchemaAttributes(payload);
1003
+ if (initSpan) {
1004
+ trace.getSpan(initSpan)?.end();
1005
+ initSpan = null;
1006
+ }
1007
+ },
1008
+ onDispose() {
1009
+ if (options.flushOnDispose !== false) {
1010
+ const flushMethod = options.flushOnDispose ?? "forceFlush";
1011
+ const provider = trace.getTracerProvider();
1012
+ if (flushMethod in provider && typeof provider[flushMethod] === "function") {
1013
+ return provider[flushMethod]();
1014
+ }
1015
+ }
1016
+ }
1017
+ }));
1018
+ hive.setPluginUtils(plugin);
1019
+ return plugin;
1020
+ }
1021
+ function shouldTrace(value, args) {
1022
+ if (value == null) {
1023
+ return true;
1024
+ }
1025
+ if (typeof value === "function") {
1026
+ return value(args);
1027
+ }
1028
+ return value;
1029
+ }
1030
+ function getURL(request) {
1031
+ if ("parsedUrl" in request) {
1032
+ return request.parsedUrl;
1033
+ }
1034
+ return new URL(request.url, "http://localhost");
1035
+ }
1036
+
1037
+ export { SEMATTRS_GRAPHQL_DOCUMENT as S, SEMATTRS_GRAPHQL_OPERATION_TYPE as a, SEMATTRS_GRAPHQL_OPERATION_NAME as b, SEMATTRS_HIVE_GRAPHQL_OPERATION_HASH as c, SEMATTRS_HIVE_GRAPHQL_ERROR_COUNT as d, SEMATTRS_HIVE_GRAPHQL_ERROR_CODES as e, SEMATTRS_HIVE_GATEWAY_UPSTREAM_SUBGRAPH_NAME as f, SEMATTRS_HIVE_GATEWAY_OPERATION_SUBGRAPH_NAMES as g, getEnvBool as h, getEnvStr as i, otelCtxForRequestId as o, useOpenTelemetry as u };