Skip to content

Commit 627a488

Browse files
committed
fix
1 parent b05b272 commit 627a488

File tree

6 files changed

+307
-82
lines changed

6 files changed

+307
-82
lines changed

e2e/opentelemetry/gateway.config.ts

Lines changed: 30 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,9 @@
11
import { defineConfig, GatewayPlugin } from '@graphql-hive/gateway';
22
import { trace } from '@graphql-hive/gateway/opentelemetry/api';
3-
import { openTelemetrySetup } from '@graphql-hive/gateway/opentelemetry/setup';
3+
import {
4+
HiveTracingSpanProcessor,
5+
openTelemetrySetup,
6+
} from '@graphql-hive/gateway/opentelemetry/setup';
47
import type { MeshFetchRequestInit } from '@graphql-mesh/types';
58
import {
69
getNodeAutoInstrumentations,
@@ -29,6 +32,7 @@ const useOnFetchTracer = (): GatewayPlugin => {
2932
};
3033
};
3134

35+
//*
3236
if (process.env['DISABLE_OPENTELEMETRY_SETUP'] !== '1') {
3337
const { OTLPTraceExporter } =
3438
process.env['OTLP_EXPORTER_TYPE'] === 'http'
@@ -74,6 +78,31 @@ if (process.env['DISABLE_OPENTELEMETRY_SETUP'] !== '1') {
7478
});
7579
}
7680
}
81+
/*/
82+
83+
const resource = resources.resourceFromAttributes({
84+
'custom.resource': 'custom value',
85+
});
86+
const { OTLPTraceExporter } =
87+
process.env['OTLP_EXPORTER_TYPE'] === 'http'
88+
? await import(`@opentelemetry/exporter-trace-otlp-http`)
89+
: await import(`@opentelemetry/exporter-trace-otlp-grpc`);
90+
91+
const exporter = new OTLPTraceExporter({
92+
url: process.env['OTLP_EXPORTER_URL'],
93+
});
94+
openTelemetrySetup({
95+
contextManager: new AsyncLocalStorageContextManager(),
96+
resource,
97+
traces: {
98+
processors: [
99+
new HiveTracingSpanProcessor({
100+
processor: new tracing.SimpleSpanProcessor(exporter),
101+
}),
102+
],
103+
},
104+
});
105+
//*/
77106

78107
export const gatewayConfig = defineConfig({
79108
openTelemetry: {

e2e/opentelemetry/opentelemetry.e2e.ts

Lines changed: 113 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -248,6 +248,119 @@ describe('OpenTelemetry', () => {
248248
});
249249
});
250250

251+
it.only('should report telemetry metrics correctly to jaeger', async () => {
252+
const serviceName = crypto.randomUUID();
253+
const { execute } = await gateway({
254+
supergraph,
255+
env: {
256+
OTLP_EXPORTER_TYPE,
257+
OTLP_EXPORTER_URL: urls[OTLP_EXPORTER_TYPE],
258+
OTEL_SERVICE_NAME: serviceName,
259+
OTEL_SERVICE_VERSION: '1.0.0',
260+
},
261+
});
262+
263+
/*
264+
await expect(execute({ query: exampleSetup.query })).resolves.toEqual(
265+
exampleSetup.result,
266+
);
267+
/*/
268+
await expect(
269+
execute({ query: 'query test { unknown }' }),
270+
).resolves.toEqual({
271+
errors: [
272+
{
273+
message: 'Cannot query field "unknown" on type "Query".',
274+
extensions: {
275+
code: 'GRAPHQL_VALIDATION_FAILED',
276+
},
277+
locations: [
278+
{
279+
column: 14,
280+
line: 1,
281+
},
282+
],
283+
},
284+
],
285+
});
286+
//*/
287+
await expectJaegerTraces(serviceName, (traces) => {
288+
const relevantTraces = traces.data.filter((trace) =>
289+
trace.spans.some((span) =>
290+
span.operationName.startsWith('graphql.operation'),
291+
),
292+
);
293+
expect(relevantTraces.length).toBe(1);
294+
const relevantTrace = relevantTraces[0];
295+
expect(relevantTrace).toBeDefined();
296+
297+
// const resource = relevantTrace!.processes['p1'];
298+
// expect(resource).toBeDefined();
299+
300+
// const tags = resource!.tags.map(({ key, value }) => ({ key, value }));
301+
// // const tagKeys = resource!.tags.map(({ key }) => key);
302+
// expect(resource!.serviceName).toBe(serviceName);
303+
// [
304+
// ['custom.resource', 'custom value'],
305+
// ['otel.library.name', 'gateway'],
306+
// ].forEach(([key, value]) => {
307+
// return expect(tags).toContainEqual({ key, value });
308+
// });
309+
310+
// if (
311+
// gatewayRunner === 'node' ||
312+
// gatewayRunner === 'docker' ||
313+
// gatewayRunner === 'bin'
314+
// ) {
315+
// const expectedTags = [
316+
// 'process.owner',
317+
// 'host.arch',
318+
// 'os.type',
319+
// 'service.instance.id',
320+
// ];
321+
// if (gatewayRunner.includes('docker')) {
322+
// expectedTags.push('container.id');
323+
// }
324+
// expectedTags.forEach((key) => {
325+
// return expect(tags).toContainEqual(
326+
// expect.objectContaining({ key }),
327+
// );
328+
// });
329+
// }
330+
331+
// const spanTree = buildSpanTree(relevantTrace!.spans, 'POST /graphql');
332+
// expect(spanTree).toBeDefined();
333+
334+
// expect(spanTree!.children).toHaveLength(1);
335+
336+
const operationSpan = buildSpanTree(
337+
relevantTrace!.spans,
338+
'graphql.operation',
339+
)!;
340+
const expectedOperationChildren = [
341+
'graphql.parse',
342+
'graphql.validate',
343+
];
344+
// expect(operationSpan!.children).toHaveLength(
345+
// expectedOperationChildren.length,
346+
// );
347+
for (const operationName of expectedOperationChildren) {
348+
expect(operationSpan?.children).toContainEqual(
349+
expect.objectContaining({
350+
span: expect.objectContaining({ operationName }),
351+
}),
352+
);
353+
}
354+
355+
console.log(operationSpan.span.tags);
356+
expect(
357+
operationSpan.span.tags.find(
358+
({ key }) => key === 'graphql.operation.name',
359+
),
360+
).toMatchObject({ value: 'TestQuery' });
361+
});
362+
});
363+
251364
it('should report telemetry metrics correctly to jaeger using cli options', async () => {
252365
const serviceName = crypto.randomUUID();
253366
const { execute } = await gateway({

packages/plugins/opentelemetry/src/plugin.ts

Lines changed: 23 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import {
88
import { getHeadersObj } from '@graphql-mesh/utils';
99
import { ExecutionRequest, fakePromise } from '@graphql-tools/utils';
1010
import { unfakePromise } from '@whatwg-node/promise-helpers';
11+
import { DocumentNode } from 'graphql';
1112
import {
1213
context,
1314
hive,
@@ -40,7 +41,7 @@ import {
4041
recordCacheError,
4142
recordCacheEvent,
4243
registerException,
43-
setExecutionAttributesOnOperationSpan,
44+
setDocumentAttributesOnOperationSpan,
4445
setExecutionResultAttributes,
4546
setGraphQLExecutionAttributes,
4647
setGraphQLExecutionResultAttributes,
@@ -483,12 +484,7 @@ export function useOpenTelemetry(
483484

484485
const { forOperation } = state;
485486
forOperation.otel!.push(
486-
createGraphQLValidateSpan({
487-
ctx: getContext(state),
488-
tracer,
489-
query: gqlCtx.params.query?.trim(),
490-
operationName: gqlCtx.params.operationName,
491-
}),
487+
createGraphQLValidateSpan({ ctx: getContext(state), tracer }),
492488
);
493489

494490
if (useContextManager) {
@@ -800,10 +796,17 @@ export function useOpenTelemetry(
800796
query: gqlCtx.params.query?.trim(),
801797
result,
802798
});
799+
if (!(result instanceof Error)) {
800+
setDocumentAttributesOnOperationSpan({
801+
ctx: state.forOperation.otel!.root,
802+
document: result,
803+
operationName: gqlCtx.params.operationName,
804+
});
805+
}
803806
};
804807
},
805808

806-
onValidate({ state, context: gqlCtx }) {
809+
onValidate({ state, context: gqlCtx, params }) {
807810
if (
808811
!isParentEnabled(state) ||
809812
!shouldTrace(traces.spans?.graphqlValidate, { context: gqlCtx })
@@ -812,7 +815,12 @@ export function useOpenTelemetry(
812815
}
813816

814817
return ({ result }) => {
815-
setGraphQLValidateAttributes({ ctx: getContext(state), result });
818+
setGraphQLValidateAttributes({
819+
ctx: getContext(state),
820+
result,
821+
document: params.documentAST,
822+
operationName: gqlCtx.params.operationName,
823+
});
816824
};
817825
},
818826

@@ -821,18 +829,17 @@ export function useOpenTelemetry(
821829
return;
822830
}
823831

824-
setExecutionAttributesOnOperationSpan({
825-
ctx: state.forOperation.otel!.root,
826-
args,
827-
hashOperationFn: options.hashOperation,
828-
});
829-
830832
if (state.forOperation.skipExecuteSpan) {
831833
return;
832834
}
833835

834836
const ctx = getContext(state);
835-
setGraphQLExecutionAttributes({ ctx, args });
837+
setGraphQLExecutionAttributes({
838+
ctx,
839+
operationCtx: state.forOperation.otel!.root,
840+
args,
841+
hashOperationFn: options.hashOperation,
842+
});
836843

837844
state.forOperation.subgraphNames = [];
838845

0 commit comments

Comments
 (0)