Skip to content

Commit 3706a2c

Browse files
authored
Better OTEL default resources and Hive Tracing improvements (#1721)
1 parent b449b05 commit 3706a2c

File tree

6 files changed

+80
-57
lines changed

6 files changed

+80
-57
lines changed

.changeset/proud-tips-invent.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
'@graphql-hive/plugin-opentelemetry': patch
3+
---
4+
5+
Have a default endpoint for Hive Tracing

.changeset/shaggy-seals-return.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
'@graphql-hive/plugin-opentelemetry': patch
3+
---
4+
5+
Improved default resource service name and version to use Hive Gateway

e2e/cloudflare-workers/cloudflare-workers.e2e.ts

Lines changed: 19 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import os from 'os';
22
import { createTenv, getAvailablePort, type Container } from '@internal/e2e';
3-
import { getLocalhost, isDebug } from '@internal/testing';
3+
import { getLocalhost } from '@internal/testing';
44
import { fetch } from '@whatwg-node/fetch';
55
import { ExecutionResult } from 'graphql';
66
import { beforeAll, describe, expect, it } from 'vitest';
@@ -35,7 +35,13 @@ describe.skipIf(gatewayRunner !== 'node' || process.version.startsWith('v1'))(
3535
additionalContainerPorts: [16686],
3636
healthcheck: ['CMD-SHELL', 'wget --spider http://0.0.0.0:14269'],
3737
});
38-
jaegerHostname = await getLocalhost(jaeger.port);
38+
try {
39+
jaegerHostname = await getLocalhost(jaeger.port);
40+
} catch {
41+
throw new Error(
42+
`Jaeger unavailable\n${jaeger.getStd('both') || 'no output'}`,
43+
);
44+
}
3945
});
4046

4147
type JaegerTracesApiResponse = {
@@ -83,7 +89,7 @@ describe.skipIf(gatewayRunner !== 'node' || process.version.startsWith('v1'))(
8389
OTEL_SERVICE_NAME: string;
8490
}) {
8591
const port = await getAvailablePort();
86-
await spawn([
92+
const [proc] = await spawn([
8793
'yarn',
8894
'wrangler',
8995
'dev',
@@ -95,9 +101,17 @@ describe.skipIf(gatewayRunner !== 'node' || process.version.startsWith('v1'))(
95101
'OTEL_SERVICE_NAME:' + env.OTEL_SERVICE_NAME,
96102
'--var',
97103
'OTEL_LOG_LEVEL:debug',
98-
...(isDebug() ? ['--var', 'DEBUG:1'] : []),
104+
'--var',
105+
'DEBUG:1',
99106
]);
100-
const hostname = await getLocalhost(port);
107+
let hostname: string;
108+
try {
109+
hostname = await getLocalhost(port, undefined, 30_000);
110+
} catch {
111+
throw new Error(
112+
`Wrangler unavailable\n${proc.getStd('both') || 'no output'}`,
113+
);
114+
}
101115
return {
102116
url: `${hostname}:${port}`,
103117
async execute({

internal/testing/src/getLocalhost.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,9 @@ export const hostnames = ['0.0.0.0', '127.0.0.1', 'localhost'];
66
export async function getLocalhost(
77
port: number,
88
protocol = 'http',
9+
timeout = 5000,
910
): Promise<string> {
10-
const timeoutSignal = AbortSignal.timeout(5000);
11+
const timeoutSignal = AbortSignal.timeout(timeout);
1112
while (!timeoutSignal.aborted) {
1213
for (const hostname of hostnames) {
1314
const url = `${protocol}://${hostname}:${port}`;

packages/plugins/opentelemetry/src/setup.ts

Lines changed: 46 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -125,7 +125,7 @@ type BaseOptions = {
125125
* The Resource that will be used to create the Trace Provider.
126126
* Can be either a Resource instance, or an simple object with service name and version
127127
*/
128-
resource?: Resource | { serviceName: string; serviceVersion: string };
128+
resource?: Resource | { serviceName: string; serviceVersion?: string };
129129
/**
130130
* The Context Manager to be used to track OTEL Context.
131131
* If possible, use `AsyncLocalStorageContextManager` from `@opentelemetry/context-async-hooks`.
@@ -244,21 +244,7 @@ export function openTelemetrySetup(options: OpentelemetrySetupOptions) {
244244
logAttributes['console'] = true;
245245
}
246246

247-
const resource = createResource(
248-
resourceFromAttributes({
249-
[ATTR_SERVICE_NAME]:
250-
getEnvStr('OTEL_SERVICE_NAME') ||
251-
'@graphql-hive/plugin-opentelemetry',
252-
[ATTR_SERVICE_VERSION]:
253-
getEnvStr('OTEL_SERVICE_VERSION') ||
254-
globalThis.__OTEL_PLUGIN_VERSION__ ||
255-
'unknown',
256-
['hive.gateway.version']: globalThis.__VERSION__,
257-
['hive.otel.version']:
258-
globalThis.__OTEL_PLUGIN_VERSION__ || 'unknown',
259-
}),
260-
options.resource,
261-
);
247+
const resource = createResource(options);
262248

263249
logAttributes['resource'] = resource.attributes;
264250
logAttributes['sampling'] = options.sampler
@@ -311,6 +297,7 @@ export type HiveTracingOptions = { target?: string } & (
311297
accessToken?: string;
312298
batching?: BufferConfig;
313299
processor?: never;
300+
/** @default 'https://api.graphql-hive.com/otel/v1/traces' */
314301
endpoint?: string;
315302
}
316303
| {
@@ -338,40 +325,48 @@ export function hiveTracingSetup(options: HiveTracingSetupOptions) {
338325
target: options.target,
339326
};
340327

341-
if (!options.processor) {
328+
let processorOptions: HiveTracingSpanProcessorOptions;
329+
if (options.processor) {
330+
processorOptions = { processor: options.processor };
331+
} else {
342332
options.accessToken ??=
343333
getEnvStr('HIVE_TRACING_ACCESS_TOKEN') ?? getEnvStr('HIVE_ACCESS_TOKEN');
344-
345334
if (!options.accessToken) {
346335
throw new Error(
347-
'You must specify the Hive Registry `accessToken`. Either provide `accessToken` option or `HIVE_ACCESS_TOKEN`/`HIVE_TRACE_ACCESS_TOKEN` environment variable.',
336+
'You must specify the Hive Registry access token. Either provide the "accessToken" option or "HIVE_ACCESS_TOKEN"/"HIVE_TRACE_ACCESS_TOKEN" environment variable.',
337+
);
338+
}
339+
340+
options.endpoint ??=
341+
getEnvStr('HIVE_TRACING_ENDPOINT') ??
342+
'https://api.graphql-hive.com/otel/v1/traces';
343+
if (!options.endpoint) {
344+
throw new Error(
345+
'You must specify the Hive Tracing endpoint. Either provide the "endpoint" option or the "HIVE_TRACING_ENDPOINT" environment variable.',
348346
);
349347
}
350348

349+
processorOptions = {
350+
target: options.target,
351+
accessToken: options.accessToken,
352+
endpoint: options.endpoint,
353+
batching: options.batching,
354+
};
355+
351356
logAttributes['endpoint'] = options.endpoint;
352357
logAttributes['batching'] = options.batching;
353358
}
354359

355360
openTelemetrySetup({
356361
...options,
357362
log,
358-
resource: createResource(
363+
resource: createResource(options).merge(
359364
resourceFromAttributes({
360365
'hive.target_id': options.target,
361-
[ATTR_SERVICE_NAME]: getEnvStr('OTEL_SERVICE_NAME') || 'hive-gateway',
362-
[ATTR_SERVICE_VERSION]:
363-
getEnvStr('OTEL_SERVICE_VERSION') ||
364-
globalThis.__OTEL_PLUGIN_VERSION__ ||
365-
'unknown',
366366
}),
367-
options.resource,
368367
),
369368
traces: {
370-
processors: [
371-
new HiveTracingSpanProcessor(
372-
options as HiveTracingSpanProcessorOptions,
373-
),
374-
],
369+
processors: [new HiveTracingSpanProcessor(processorOptions)],
375370
spanLimits: options.spanLimits,
376371
console: options.console,
377372
},
@@ -400,24 +395,27 @@ function resolveBatchingConfig(
400395
}
401396
}
402397

403-
function createResource(
404-
baseResource: Resource,
405-
userResource?: OpentelemetrySetupOptions['resource'],
406-
): Resource {
407-
if (!userResource) {
408-
return baseResource;
409-
}
410-
411-
if ('serviceName' in userResource) {
412-
return baseResource.merge(
413-
resourceFromAttributes({
414-
[ATTR_SERVICE_NAME]: userResource.serviceName,
415-
[ATTR_SERVICE_VERSION]: userResource.serviceVersion,
416-
}),
417-
);
398+
function createResource(opts: Pick<OpentelemetrySetupOptions, 'resource'>) {
399+
const resourceObj =
400+
opts.resource && 'serviceName' in opts.resource ? opts.resource : null;
401+
402+
let resource = resourceFromAttributes({
403+
[ATTR_SERVICE_NAME]:
404+
resourceObj?.serviceName ||
405+
getEnvStr('OTEL_SERVICE_NAME') ||
406+
'hive-gateway',
407+
[ATTR_SERVICE_VERSION]:
408+
resourceObj?.serviceVersion ||
409+
getEnvStr('OTEL_SERVICE_VERSION') ||
410+
globalThis.__VERSION__ ||
411+
'unknown',
412+
['hive.otel.version']: globalThis.__OTEL_PLUGIN_VERSION__ || 'unknown',
413+
});
414+
if (opts.resource && 'attributes' in opts.resource) {
415+
// opts.resource is a Resource
416+
resource = resource.merge(opts.resource);
418417
}
419-
420-
return baseResource.merge(userResource);
418+
return resource;
421419
}
422420

423421
/**

packages/plugins/opentelemetry/tests/useOpenTelemetry.spec.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -121,7 +121,7 @@ describe('useOpenTelemetry', () => {
121121

122122
const resource = getResource();
123123
expect(resource?.attributes).toMatchObject({
124-
'service.name': '@graphql-hive/plugin-opentelemetry',
124+
'service.name': 'hive-gateway',
125125
});
126126
});
127127

@@ -398,7 +398,7 @@ describe('useOpenTelemetry', () => {
398398
expect(exporter).toBeInstanceOf(OTLPTraceExporter);
399399
// @ts-expect-error Access of private field
400400
expect(exporter._delegate._transport._transport._parameters.url).toBe(
401-
'http://localhost:4318/v1/traces',
401+
'https://api.graphql-hive.com/otel/v1/traces',
402402
);
403403
});
404404
});
@@ -562,7 +562,7 @@ describe('useOpenTelemetry', () => {
562562
let attempts = 0;
563563
await using gateway = await buildTestGatewayForCtx({
564564
gatewayOptions: {
565-
logging: true,
565+
logging: false,
566566
upstreamRetry: {
567567
maxRetries: 2,
568568
retryDelay: 1,

0 commit comments

Comments
 (0)