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