From 75493c41efaa719921e1b5386f519634d91754aa Mon Sep 17 00:00:00 2001 From: Valentin Cocaud Date: Fri, 20 Jun 2025 22:30:07 +0200 Subject: [PATCH 01/10] docs(opentelemetry): Update documentation for Hive Gateway v2 --- .../content/gateway/monitoring-tracing.mdx | 870 ++++++++++++------ 1 file changed, 577 insertions(+), 293 deletions(-) diff --git a/packages/web/docs/src/content/gateway/monitoring-tracing.mdx b/packages/web/docs/src/content/gateway/monitoring-tracing.mdx index 107b0777eb..9b4b0f9f52 100644 --- a/packages/web/docs/src/content/gateway/monitoring-tracing.mdx +++ b/packages/web/docs/src/content/gateway/monitoring-tracing.mdx @@ -123,111 +123,242 @@ The following are available to use with this plugin: - Upstream HTTP calls: tracks the outgoing HTTP requests made by the GraphQL execution. - Context propagation: propagates the trace context between the incoming HTTP request and the outgoing HTTP requests. +- Custom Span and attributes: Add your own business spans and attributes from your own plugin. +- Logs and Traces correlation: Rely on stanadard OTEL shared context to correlate logs and traces ![image](https://github.com/user-attachments/assets/74918ade-8d7c-44ee-89b2-e10a13ffc4ad) -### Usage Example +### OpenTelemetry Setup - +For the OpenTelemetry tracing feature to work, OpenTelemetry must firt be setup. - +We recommend to place your OpenTelemetry setup in a `telemetry.ts` file that will be your first +import in your `gateway.config.ts` file. This allow instrumentations to be registered (if any) +before any other packages are imported. -```ts filename="gateway.config.ts" -import { createStdoutExporter, defineConfig } from '@graphql-hive/gateway' +For ease of configuration, we provide a `opentelemetrySetup` function from +'@graphql-hive/plugin-opentelemetry/setup' module, with sensible default and straightforward API +compatible with all runtimes. -export const gatewayConfig = defineConfig({ - openTelemetry: { - exporters: [ - // A simple output to the console. - // You can add more exporters here, please see documentation below for more examples. - createStdoutExporter() - ], - serviceName: 'my-custom-service-name', // Optional, the name of your service - tracer: myCustomTracer, // Optional, a custom tracer to use - inheritContext: true, // Optional, whether to inherit the context from the incoming request - propagateContext: true, // Optional, whether to propagate the context to the outgoing requests - // Optional config to customize the spans. By default all spans are enabled. - spans: { - http: true, // Whether to track the HTTP request/response - graphqlParse: true, // Whether to track the GraphQL parse phase - graphqlValidate: true, // Whether to track the GraphQL validate phase - graphqlExecute: true, // Whether to track the GraphQL execute phase - subgraphExecute: true, // Whether to track the subgraph execution phase - upstreamFetch: true // Whether to track the upstream HTTP requests - } +But this utility is not mandatory, you can use any setup relevant to your specific use case and +infrastrcture. + +The most commonnly used otel packages are available when using Hive Gateway with CLI. Please switch +to programatic usage if you need more packages. + +Please refer to [`opentelemetry-js` documentation](https://opentelemetry.io/docs/languages/js/) for +more details about OpenTelemetry setup and API. + +#### Basic usage + +Hive Gateway opentelemetrySetup() (recommended),
OpenTelemetry NodeSDK
]}> + + + +This configuration API still rely on offical `@opentelemetry/api` package, which means you can use +any official or standard compliant packages with it. + +```ts filename="telemetry.ts" +import { opentelemetrySetup } from '@graphql-hive/plugin-opentelemetry/setup' +import { AsyncLocalStorageContextManager } from '@opentelemetry/context-async-hooks' +import { OTLPTraceExporter } from '@opentelemetry/exporter-trace-otlp-http' + +opentelemetrySetup({ + // Mandatory: It depends on the available API in your runtime. + // We recommend AsyncLocalStorage based manager when possible. + // `@opentelemetry/context-zone` is also available for other runtimes. + // Pass `flase` to disable context manager usage. + contextManager: new AsyncLocalStorageContextManager(), + + traces: { + // Define your exporter, most of the time the OTLP HTTP one. Traces are batched by default. + exporter: new OTLPTraceExporter({ url: process.env['OTLP_URL'] }), + + // You can easily enable a console exporter for quick debug + console: process.env['DEBUG_TRACES'] === '1' } }) ``` - +
-```sh npm2yarn -npm i @graphql-mesh/plugin-opentelemetry + + Official OpenTelemetry Node SDK is only working when Hive Gateway is used via the CLI or + programatically with a Node runtime. + + +OpenTelemetry provides an official SDK for Node (`@opentelemetry/sdk-node`). This SDK offers a +standard API compatible with OTEL SDK specification. + +It ships with a lot of features, most of them being configurable via environment variables. + +The most commonnly used otel packages are available when using Hive Gateway with CLI, which means +you can follow official `@opentelemetry/sdk-node` documentation for your setup. Please switch to +programatic usage if you need more packages. + +```ts filename="telemetry.ts" +import { OTLPTraceExporter } from '@opentelemetry/exporter-trace-otlp-http' +import { NodeSDK, resources, tracing } from '@opentelemetry/sdk-node' + +new NodeSDK({ + // All configuration is optional. OTEL rely on env variables or sensible default value. + + // Defines the exporter, HTTP OTLP most of the time. Traces are batched by default + traceExporter: new OTLPTraceExporter({ url: process.env['OTLP_URL'] }), + + // Optional, enables automatic instrumentation, adding traces like network spans. + instrumentations: getNodeAutoInstrumentations(), + + // Optional, enables automatic ressource attributes detection + resourceDetectors: getResourceDetectors() +}).start() ``` -```ts filename="index.ts" -import { createGatewayRuntime } from '@graphql-hive/gateway-runtime' -import { createStdoutExporter, useOpenTelemetry } from '@graphql-mesh/plugin-opentelemetry' + -export const gateway = createGatewayRuntime({ - plugins: ctx => [ - useOpenTelemetry({ - ...ctx, - exporters: [createStdoutExporter()], - serviceName: 'my-custom-service-name', // Optional, the name of your service - tracer: myCustomTracer, // Optional, a custom tracer to use - inheritContext: true, // Optional, whether to inherit the context from the incoming request - propagateContext: true, // Optional, whether to propagate the context to the outgoing requests - // Optional config to customize the spans. By default all spans are enabled. - spans: { - http: true, // Whether to track the HTTP request/response - graphqlParse: true, // Whether to track the GraphQL parse phase - graphqlValidate: true, // Whether to track the GraphQL validate phase - graphqlExecute: true, // Whether to track the GraphQL execute phase - subgraphExecute: true, // Whether to track the subgraph execution phase - upstreamFetch: true // Whether to track the upstream HTTP requests - } - }) - ] +
+ +#### Service name and version + +You can provide a service name, either by using standard `OTEL_SERVICE_NAME` and +`OTEL_SERVICE_VERSION` or by providing them programatically via setup options + +Hive Gateway opentelemetrySetup() (recommended),
OpenTelemetry NodeSDK
]}> + + + +```ts filename="telemetry.ts" +import { opentelemetrySetup } from '@graphql-hive/plugin-opentelemetry/setup' + +opentelemetrySetup({ + resource: { + serviceName: 'my-service', + serviceVersion: '1.0.0' + } }) ``` + + +```ts filename="telemetry.ts" +import { NodeSDK, resources } from '@opentelemetry/sdk-node' + +new NodeSDK({ + resource: resources.resourceFromAttributes({ + 'service.name': 'my-service', + 'service.version': '1.0.0' + }) +}).start() +``` + + +
-### Exporters +#### Custom resource attributes -You may use one of the following exporters to send the traces to a backend, or create an configure -custom exporters and processors. +Resource attributes can be defined by providing a `Resource` instance to the setup `resource` +option. -To use a custom exporter that is not listen below, please refer to -[Customer Exporters in OpenTelemetry documentation](https://opentelemetry.io/docs/languages/js/exporters/#custom-exporters). +Hive Gateway opentelemetrySetup() (recommended),
OpenTelemetry NodeSDK
]}> -In addition, you can fully customize the plugin's Tracer with any kind of OpenTelemetry -[tracer](https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/trace/api.md#tracer), -and integrate it to any tracing/metric platform that supports this standard. + - +This resource will be merged with the resource created from env variables, which means +`service.name` and `service.version` are not mandatory if already provided through environment +variables. + +```ts filename="telemetry.ts" +import { opentelemetrySetup } from '@graphql-hive/plugin-opentelemetry/setup' +import { resourceFromAttributes } from '@opentelemetry/resources' + +opentelemetrySetup({ + resource: resourceFromAttributes({ + 'custom.attribute': 'my custom value' + }) +}) +``` -{/* Stdout */} + -A simple exporter that writes the spans to the `stdout` of the process. +```ts filename="telemetry.ts" +import { NodeSDK, resources } from '@opentelemetry/sdk-node' - +new NodeSDK({ + resource: resources.resourceFromAttributes({ + 'service.name': 'my-service', + 'service.version': '1.0.0' + }) +}).start() +``` + + + +
+ +#### Exporters, Span Processors and Tracer Provider + +Exporters are responsible of storing the traces recorded by OpenTelemetry. There is a large existing +range of exporters, Hive Gateway is compatible with any exporter using `@opentelemetry/api` standard +OpenTelemetry implementation. + +Span Processors are responsible of processing recorded spans before they are stored. They genreally +take an exporter in paramater, which is used to store processed spans. + +Tracer Provider is responsible of creating Tracers that will be used to record spans. + +You can setup OpenTelemetry by providing either: + +- a Trace Exporter. A Span processor and a Tracer Provider will be created for you, with sensible + poduction defaults like trace batching. +- a list of Span Processors. This gives you more control, and allows to define more than one + exporter. The Tracer Provider will be created for you. +- a Tracer Provider. This is the manual setup mode where nothing is created automatically. The + Tracer Provider will just be registered. + +Hive Gateway opentelemetrySetup() (recommended),
OpenTelemetry NodeSDK
]}> -```ts filename="gateway.config.ts" -import { createStdoutExporter, defineConfig } from '@graphql-hive/gateway' -export const gatewayConfig = defineConfig({ - openTelemetry: { - exporters: [createStdoutExporter()] - } +```ts filename="telemetry.ts" +import { opentelemetrySetup } from '@graphql-hive/plugin-opentelemetry/setup' +import { AsyncLocalStorageContextManager } from '@opentelemetry/context-async-hooks' + +opentelemetrySetup({ + contextManager: new AsyncLocalStorageContextManager(), + traces: { + // Define your exporter, most of the time the OTLP HTTP one. Traces are batched by default. + exporter: ..., + + // To ease debug, you can also add a non-batched console exporter easily with `console` option + console: true, + }, +}) + +// or + +opentelemetrySetup({ + contextManager: new AsyncLocalStorageContextManager(), + traces: { + // Define your span processors. + processors: [...], + }, +}) + +// or + +opentelemetrySetup({ + contextManager: new AsyncLocalStorageContextManager(), + traces: { + // Define your span processors. + tracerProvider: ..., + }, }) ``` @@ -235,54 +366,104 @@ export const gatewayConfig = defineConfig({ - - Beware that OpenTelemetry JavaScript SDK writes spans using `console.dir`. Meaning, - serverless/on-the-edge environments that don't support `console.dir` (like [Cloudflare - Workers](https://developers.cloudflare.com/workers/runtime-apis/console/)) wont show any logs. - +```ts filename="telemetry.ts" +import { NodeSDK } from '@opentelemetry/sdk-node' -```ts filename="index.ts" -import { createGatewayRuntime } from '@graphql-hive/gateway-runtime' -import { createStdoutExporter, useOpenTelemetry } from '@graphql-mesh/plugin-opentelemetry' +new NodeSDK({ + // Define your exporter, most of the time the OTLP HTTP one. Traces are batched by default. + traceExporter: ..., +}).start() -export const gateway = createGatewayRuntime({ - plugins: ctx => [ - useOpenTelemetry({ - ...ctx, - exporters: [createStdoutExporter()] - }) - ] -}) +// or + +new NodeSDK({ + // Define your processors + spanProcessors: [...], +}).start() +``` + +OpenTelemetry's `NodeSDK` doesn't allow to manually porvide a Tracer Provider. You have to register +it seperatly. + +```ts filename="telemetry.ts" +import { trace } from '@opentelemetry/api' +import { NodeSDK } from '@opentelemetry/sdk-node' + +// Manually set the Tracer Provider, NodeSDK will detect that it is already registered +trace.setGlobalTracerProvider(...) + +new NodeSDK({ + //... +}).start() ```
+Hive Gateway CLI embeds every official OpenTelemetry exporters. Please switch manual deployement or +programtice usage to install a non-official exporter. + + + + + +A simple exporter that writes the spans to the `stdout` of the process. It is mostly used for +debugging purpose. + +[See official documentation for more details](https://open-telemetry.github.io/opentelemetry-js/classes/_opentelemetry_sdk-trace-base.ConsoleSpanExporter.html). + +Hive Gateway opentelemetrySetup() (recommended),
OpenTelemetry NodeSDK
]}> + + + +```ts filename="telemetry.ts" +import { opentelemetrySetup } from '@graphql-hive/plugin-opentelemetry/setup' + +opentelemetrySetup({ + contextManager: new AsyncLocalStorageContextManager(), + traces: { + console: true + } +}) +``` + -{/* OTLP (HTTP) */} + + +```ts filename="telemetry.ts" +import { NodeSDK, tracing } from '@opentelemetry/sdk-node' + +new NodeSDK({ + // Use `spanProcessors` instead of `traceExporter` to avoid the default batching configuration + spanProcessors: [new tracing.SimpleSpanProcessor(new tracing.ConsoleSpanExporter())] +}).start() +``` + + + +
+ +
An exporter that writes the spans to an OTLP-supported backend using HTTP. - +Hive Gateway opentelemetrySetup() (recommended),
OpenTelemetry NodeSDK
]}> -```ts filename="gateway.config.ts" -import { createOtlpHttpExporter, defineConfig } from '@graphql-hive/gateway' -export const gatewayConfig = defineConfig({ - openTelemetry: { - exporters: [ - createOtlpHttpExporter({ - url: 'http://:4318' - // ... - // additional options to pass to @opentelemetry/exporter-trace-otlp-http - // https://www.npmjs.com/package/@opentelemetry/exporter-trace-otlp-http - }) - ] +```ts filename="telemetry.ts" +import { opentelemetrySetup } from '@graphql-hive/plugin-opentelemetry/setup' +import { AsyncLocalStorageContextManager } from '@opentelemetry/context-async-hooks' +import { OTLPTraceExporter } from '@opentelemetry/exporter-trace-otlp-http' + +opentelemetrySetup({ + contextManager: new AsyncLocalStorageContextManager(), + traces: { + exporter: new OTLPTraceExporter({ url: 'http://:4318' }) } }) ``` @@ -291,25 +472,13 @@ export const gatewayConfig = defineConfig({ -```ts filename="index.ts" -import { createGatewayRuntime } from '@graphql-hive/gateway-runtime' -import { createOtlpHttpExporter, useOpenTelemetry } from '@graphql-mesh/plugin-opentelemetry' +```ts filename="telemetry.ts" +import { OTLPTraceExporter } from '@opentelemetry/exporter-trace-otlp-http' +import { NodeSDK } from '@opentelemetry/sdk-node' -export const gateway = createGatewayRuntime({ - plugins: ctx => [ - useOpenTelemetry({ - ...ctx, - exporters: [ - createOtlpHttpExporter({ - url: 'http://:4318' - // ... - // additional options to pass to @opentelemetry/exporter-trace-otlp-http - // https://www.npmjs.com/package/@opentelemetry/exporter-trace-otlp-http - }) - ] - }) - ] -}) +new NodeSDK({ + traceExporter: new OTLPTraceExporter({ url: 'http://:4318' }) +}).start() ``` @@ -318,28 +487,23 @@ export const gateway = createGatewayRuntime({ -{/* OTLP (gRPC) */} - An exporter that writes the spans to an OTLP-supported backend using gRPC. - +Hive Gateway opentelemetrySetup() (recommended),
OpenTelemetry NodeSDK
]}> -```ts filename="gateway.config.ts" -import { createOtlpGrpcExporter, defineConfig } from '@graphql-hive/gateway' -export const gatewayConfig = defineConfig({ - openTelemetry: { - exporters: [ - createOtlpGrpcExporter({ - url: 'http://:4317' - // ... - // additional options to pass to @opentelemetry/exporter-trace-otlp-grpc - // https://www.npmjs.com/package/@opentelemetry/exporter-trace-otlp-grpc - }) - ] +```ts filename="telemetry.ts" +import { opentelemetrySetup } from '@graphql-hive/plugin-opentelemetry/setup' +import { AsyncLocalStorageContextManager } from '@opentelemetry/context-async-hooks' +import { OTLPTraceExporter } from '@opentelemetry/exporter-trace-otlp-grpc' + +opentelemetrySetup({ + contextManager: new AsyncLocalStorageContextManager(), + traces: { + exporter: new OTLPTraceExporter({ url: 'http://:4317' }) } }) ``` @@ -348,52 +512,45 @@ export const gatewayConfig = defineConfig({ -```ts filename="index.ts" -import { createGatewayRuntime } from '@graphql-hive/gateway-runtime' -import { createOtlpGrpcExporter, useOpenTelemetry } from '@graphql-mesh/plugin-opentelemetry' +```ts filename="telemetry.ts" +import { OTLPTraceExporter } from '@opentelemetry/exporter-trace-otlp-grpc' +import { NodeSDK } from '@opentelemetry/sdk-node' -export const gateway = createGatewayRuntime({ - plugins: ctx => [ - useOpenTelemetry({ - ...ctx, - exporters: [ - createOtlpGrpcExporter({ - url: 'http://:4317' - // ... - // additional options to pass to @opentelemetry/exporter-trace-otlp-grpc - // https://www.npmjs.com/package/@opentelemetry/exporter-trace-otlp-grpc - }) - ] - }) - ] -}) +new NodeSDK({ + traceExporter: new OTLPTraceExporter({ url: 'http://:4317' }) +}).start() ```
-
-{/* Jaeger */} +
[Jaeger](https://www.jaegertracing.io/) supports [OTLP over HTTP/gRPC](#otlp-over-http), so you can -use it by pointing the `createOtlpHttpExporter`/`createOtlpGrpcExporter` to the Jaeger endpoint: +use it by pointing the +`@opentelemetry/exporter-trace-otlp-http`/`@opentelemetry/exporter-trace-otlp-grpc` to the Jaeger +endpoint. - +Your Jaeger instance needs to have OTLP ingestion enabeld, so verify that you have the +`COLLECTOR_OTLP_ENABLED=true` environment variable set, and that ports `4317` and `4318` are +acessible. + +Hive Gateway opentelemetrySetup() (recommended),
OpenTelemetry NodeSDK
]}> -```ts filename="gateway.config.ts" -import { createOtlpHttpExporter, defineConfig } from '@graphql-hive/gateway' -export const gatewayConfig = defineConfig({ - openTelemetry: { - exporters: [ - createOtlpHttpExporter({ - url: 'http://:4318' - }) - ] +```ts filename="telemetry.ts" +import { opentelemetrySetup } from '@graphql-hive/plugin-opentelemetry/setup' +import { AsyncLocalStorageContextManager } from '@opentelemetry/context-async-hooks' +import { OTLPTraceExporter } from '@opentelemetry/exporter-trace-otlp-http' + +opentelemetrySetup({ + contextManager: new AsyncLocalStorageContextManager(), + traces: { + exporter: new OTLPTraceExporter({ url: 'http://:4318' }) } }) ``` @@ -402,68 +559,104 @@ export const gatewayConfig = defineConfig({ -```ts filename="index.ts" -import { createGatewayRuntime } from '@graphql-hive/gateway-runtime' -import { createOtlpHttpExporter, useOpenTelemetry } from '@graphql-mesh/plugin-opentelemetry' +```ts filename="telemetry.ts" +import { OTLPTraceExporter } from '@opentelemetry/exporter-trace-otlp-http' +import { NodeSDK } from '@opentelemetry/sdk-node' -export const gateway = createGatewayRuntime({ - plugins: ctx => [ - useOpenTelemetry({ - ...ctx, - exporters: [ - createOtlpHttpExporter({ - url: 'http://:4318' - }) - ] - }) - ] -}) +new NodeSDK({ + traceExporter: new OTLPTraceExporter({ url: 'http://:4318' }) +}).start() ```
- - Your Jaeger instance needs to have OTLP ingestion enabeld, so verify that you have the - `COLLECTOR_OTLP_ENABLED=true` environment variable set, and that ports `4317` and `4318` are - acessible. - +
+ + + +[NewRelic](https://newrelic.com/) supports [OTLP over HTTP/gRPC](#otlp-over-http), so you can use it +by configuring the +`@opentelemetry/exporter-trace-otlp-http`/`@opentelemetry/exporter-trace-otlp-grpc` to the NewRelic +endpoint: + +Please refer to the +[NewRelic OTLP documentation](https://docs.newrelic.com/docs/opentelemetry/best-practices/opentelemetry-otlp/#configure-endpoint-port-protocol) +for complete documentation and to find the apropriate endpoint. -To test this integration, you can run a local Jaeger instance using Docker: +Hive Gateway opentelemetrySetup() (recommended),
OpenTelemetry NodeSDK
]}> + + +```ts filename="telemetry.ts" +import { opentelemetrySetup } from '@graphql-hive/plugin-opentelemetry/setup' +import { AsyncLocalStorageContextManager } from '@opentelemetry/context-async-hooks' +import { OTLPTraceExporter } from '@opentelemetry/exporter-trace-otlp-http' + +opentelemetrySetup({ + contextManager: new AsyncLocalStorageContextManager(), + traces: { + exporter: new OTLPTraceExporter({ + url: 'https://otlp.nr-data.net', // For US users, or https://otlp.eu01.nr-data.net for EU users + headers: { 'api-key': '' }, + compression: 'gzip' // Compression is recommended by NewRelic + }), + batching: { + // Depending on your traces size and network quality, you will probably need to tweak batching + // configuration. A batch should not be larger than 1Mo. + } + } +}) ``` -docker run -d --name jaeger \ - -e COLLECTOR_OTLP_ENABLED=true \ - -p 5778:5778 \ - -p 16686:16686 \ - -p 4317:4317 \ - -p 4318:4318 \ - jaegertracing/all-in-one:latest + + + + + +```ts filename="telemetry.ts" +import { OTLPTraceExporter } from '@opentelemetry/exporter-trace-otlp-http' +import { NodeSDK } from '@opentelemetry/sdk-node' + +new NodeSDK({ + traceExporter: new OTLPTraceExporter({ + url: 'https://otlp.nr-data.net', // For US users, or https://otlp.eu01.nr-data.net for EU users + headers: { 'api-key': '' }, + compression: 'gzip' // Compression is recommended by NewRelic + }) +}).start() ``` -{/* NewRelic */} +
+ +
-[NewRelic](https://newrelic.com/) supports [OTLP over HTTP/gRPC](#otlp-over-http), so you can use it -by configuring the `createOtlpHttpExporter`/`createOtlpGrpcExporter` to the NewRelic endpoint: +[DataDog Agent](https://docs.datadoghq.com/agent/) supports [OTLP over HTTP/gRPC](#otlp-over-http), +so you can use it by pointing the `@opentelemetry/exporter-trace-otlp-http` to the DataDog Agent +endpoint - +You can also use the official DataDog Tracer Provider by using manual Hive Gateway deployement and +installing the dependency. + +Hive Gateway opentelemetrySetup() (recommended),
OpenTelemetry NodeSDK
, 'DataDog Tracer Provider']}> -```ts filename="gateway.config.ts" -import { createOtlpHttpExporter, defineConfig } from '@graphql-hive/gateway' -export const gatewayConfig = defineConfig({ - openTelemetry: { - exporters: [ - createOtlpHttpExporter({ - url: 'http://:4318' - }) - ] +```ts filename="telemetry.ts" +import { opentelemetrySetup } from '@graphql-hive/plugin-opentelemetry/setup' +import { AsyncLocalStorageContextManager } from '@opentelemetry/context-async-hooks' +import { OTLPTraceExporter } from '@opentelemetry/exporter-trace-otlp-http' + +opentelemetrySetup({ + contextManager: new AsyncLocalStorageContextManager(), + traces: { + exporter: new OTLPTraceExporter({ + url: 'http://:4318' + }) } }) ``` @@ -472,55 +665,64 @@ export const gatewayConfig = defineConfig({ -```ts filename="index.ts" -import { createGatewayRuntime } from '@graphql-hive/gateway-runtime' -import { createOtlpHttpExporter, useOpenTelemetry } from '@graphql-mesh/plugin-opentelemetry' +```ts filename="telemetry.ts" +import { OTLPTraceExporter } from '@opentelemetry/exporter-trace-otlp-http' +import { NodeSDK } from '@opentelemetry/sdk-node' -export const gateway = createGatewayRuntime({ - plugins: ctx => [ - useOpenTelemetry({ - ...ctx, - exporters: [ - createOtlpHttpExporter({ - url: 'http://:4318' - }) - ] - }) - ] -}) +new NodeSDK({ + traceExporter: new OTLPTraceExporter({ + url: 'http://:4318' + }) +}).start() ``` -
+ - - For additional information and NewRelic ingestion endpoints, see [**New Relic OTLP - endpoint**](https://docs.newrelic.com/docs/opentelemetry/best-practices/opentelemetry-otlp/). - +```ts filename="telemetry.ts" +import ddTrace from 'dd-trace' +import { opentelemetrySetup } from '@graphql-hive/plugin-opentelemetry/setup' +import { AsyncLocalStorageContextManager } from '@opentelemetry/context-async-hooks' + +const { TracerProvider } = ddTrace.init({ + // Your configuration +}) + +opentelemetrySetup({ + contextManager: new AsyncLocalStorageContextManager(), + traces: { + tracerProvider: new TracerProvider() + } +}) +``` -{/* Datadog */} +
+ +
-[DataDog Agent](https://docs.datadoghq.com/agent/) supports [OTLP over HTTP/gRPC](#otlp-over-http), -so you can use it by pointing the `createOtlpHttpExporter` to the DataDog Agent endpoint: +[Zipkin](https://zipkin.io/) is using a custom protocol to send the spans, so you can use the Zipkin +exporter to send the spans to a Zipkin backend. - +Hive Gateway opentelemetrySetup() (recommended),
OpenTelemetry NodeSDK
]}> -```ts filename="gateway.config.ts" -import { createOtlpHttpExporter, defineConfig } from '@graphql-hive/gateway' -export const gatewayConfig = defineConfig({ - openTelemetry: { - exporters: [ - createOtlpHttpExporter({ - url: 'http://:4318' - }) - ] +```ts filename="telemetry.ts" +import { opentelemetrySetup } from '@graphql-hive/plugin-opentelemetry/setup' +import { AsyncLocalStorageContextManager } from '@opentelemetry/context-async-hooks' +import { ZipkinExporter } from '@opentelemetry/exporter-zipkin' + +opentelemetrySetup({ + contextManager: new AsyncLocalStorageContextManager(), + traces: { + exporter: new ZipkinExporter({ + url: '' + }) } }) ``` @@ -529,59 +731,77 @@ export const gatewayConfig = defineConfig({ -```ts filename="index.ts" -import { createGatewayRuntime } from '@graphql-hive/gateway-runtime' -import { createOtlpHttpExporter, useOpenTelemetry } from '@graphql-mesh/plugin-opentelemetry' +```ts filename="telemetry.ts" +import { ZipkinExporter } from '@opentelemetry/exporter-zipkin' +import { NodeSDK } from '@opentelemetry/sdk-node' -export const gateway = createGatewayRuntime({ - plugins: ctx => [ - useOpenTelemetry({ - ...ctx, - exporters: [ - createOtlpHttpExporter({ - url: 'http://:4318' - }) - ] - }) - ] -}) +new NodeSDK({ + traceExporter: new ZipkinExporter({ + url: '' + }) +}).start() ``` -
+ - - For additional information, see [**OpenTelemetry in - Datadog**](https://docs.datadoghq.com/opentelemetry/interoperability/otlp_ingest_in_the_agent/?tab=host#enabling-otlp-ingestion-on-the-datadog-agent). - +```ts filename="telemetry.ts" +import ddTrace from 'dd-trace' +import { opentelemetrySetup } from '@graphql-hive/plugin-opentelemetry/setup' +import { AsyncLocalStorageContextManager } from '@opentelemetry/context-async-hooks' + +const { TracerProvider } = ddTrace.init({ + // Your configuration +}) + +opentelemetrySetup({ + contextManager: new AsyncLocalStorageContextManager(), + traces: { + tracerProvider: new TracerProvider() + } +}) +``` -{/* Zipkin */} +
- + -[Zipkin](https://zipkin.io/) is using a custom protocol to send the spans, so you can use the Zipkin -exporter to send the spans to a Zipkin backend: +
- +### Configuration + + +Hive Gateway rely on `@opentelemetry/api` package, which means you can use official OpenTelemetry +API to configure it. By using `NodeSDK`, you will have access to the fully featured SDK with +defaults and automatic configuration based environment variables. + +For a more portable approach, please refer to `Programatic Usage with any runtime` + ```ts filename="gateway.config.ts" -import { createZipkinExporter, defineConfig } from '@graphql-hive/gateway' +import { defineConfig } from '@graphql-hive/gateway' export const gatewayConfig = defineConfig({ openTelemetry: { - exporters: [ - createZipkinExporter({ - url: 'http://:9411/api/v2/spans' - // ... - // additional options to pass to @opentelemetry/exporter-zipkin - // https://www.npmjs.com/package/@opentelemetry/exporter-zipkin - }) - ] + inheritContext: true, // Optional, whether to inherit the context from the incoming request + propagateContext: true, // Optional, whether to propagate the context to the outgoing requests + // Optional config to customize the spans. By default all spans are enabled. + traces: { + tracer: myCustomTracer, // Optional, a custom tracer to use + spans: { + http: true, // Whether to track the HTTP request/response + graphqlParse: true, // Whether to track the GraphQL parse phase + graphqlValidate: true, // Whether to track the GraphQL validate phase + graphqlExecute: true, // Whether to track the GraphQL execute phase + subgraphExecute: true, // Whether to track the subgraph execution phase + upstreamFetch: true // Whether to track the upstream HTTP requests + } + } } }) ``` @@ -590,22 +810,48 @@ export const gatewayConfig = defineConfig({ +```sh npm2yarn +npm i @graphql-mesh/plugin-opentelemetry +``` + ```ts filename="index.ts" import { createGatewayRuntime } from '@graphql-hive/gateway-runtime' -import { createZipkinExporter, useOpenTelemetry } from '@graphql-mesh/plugin-opentelemetry' +import { useOpenTelemetry } from '@graphql-mesh/plugin-opentelemetry' +import { NodeSDK, resources, tracing } from '@opentelemetry/sdk-node' + +new NodeSDK({ + // A simple output to the console. + // You can add more exporters here, please see documentation below for more examples. + traceExporter: new tracing.ConsoleSpanExporter(), + // Optional, enables automatic instrumentation to get low level Node tracing, like network spans. + instrumentations: getNodeAutoInstrumentations(), + // Optional, a base resource with the name of your service. + resource: resources.resourceFromAttributes({ + serviceName: 'my-custom-service-name' + }), + // Optional, enables automatic ressource discovery which will add all resources attribute possible + resourceDetectors: getResourceDetectors() +}).start() export const gateway = createGatewayRuntime({ plugins: ctx => [ useOpenTelemetry({ ...ctx, - exporters: [ - createZipkinExporter({ - url: 'http://:9411/api/v2/spans' - // ... - // additional options to pass to @opentelemetry/exporter-zipkin - // https://www.npmjs.com/package/@opentelemetry/exporter-zipkin - }) - ] + inheritContext: true, // Optional, whether to inherit the context from the incoming request + propagateContext: true, // Optional, whether to propagate the context to the outgoing requests + // Enables OTEL tracing. It is possible to use `true` to enable tracing with default config + traces: { + tracer: myCustomTracer, // Optional, a custom tracer to use + // Optional config to customize the spans. By default all spans are enabled. + spans: { + http: true, // Whether to track the HTTP request/response + graphqlParse: true, // Whether to track the GraphQL parse phase + graphqlValidate: true, // Whether to track the GraphQL validate phase + graphqlExecute: true, // Whether to track the GraphQL execute phase + subgraphExecute: true, // Whether to track the subgraph execution phase + upstreamFetch: true // Whether to track the upstream HTTP requests + } + } }) ] }) @@ -615,14 +861,19 @@ export const gateway = createGatewayRuntime({ - +#### Batching - +By default, if you provide only a Trace Exporter, it will be wrapped into a `BatchSpanProcessor` to +batch spans together and reduce the number of request to you backeng. + +This is an important feature for a real world production environment, and you can configure its +behaviour to exactly suites your infrastructure limits. + +By default, the batch processor will send the spans every 5 seconds or when the buffer is full. -### Batching +Hive Gateway opentelemetrySetup() (recommended),
OpenTelemetry NodeSDK
]}> -All built-in processors allow you to configure batching options by an additional argument to the -factory function. + The following configuration are allowed: @@ -635,17 +886,50 @@ The following configuration are allowed: - `false` - disables batching and use [`SimpleSpanProcessor`](https://opentelemetry.io/docs/specs/otel/trace/sdk/#simple-processor) -By default, the batch processor will send the spans every 5 seconds or when the buffer is full. +```ts filename="telemetry.ts" +import { opentelemetrySetup } from '@graphql-hive/plugin-opentelemetry/setup' + +opentelemetrySetup({ + traces: { + exporter: ..., + batching: { + exportTimeoutMillis: 30_000, // Default to 30_000ms + maxExportBatchSize: 512, // Default to 512 spans + maxQueueSize: 2048, // Default to 2048 spans + scheduledDelayMillis: 5_000, // Default to 5_000ms + } + }, +}) +``` -```json -{ - "scheduledDelayMillis": 5000, - "maxQueueSize": 2048, - "exportTimeoutMillis": 30000, - "maxExportBatchSize": 512 -} + + + + +```ts filename="telemetry.ts" +import { NodeSDK, tracing } from '@opentelemetry/sdk-node' + +const exporter = ... + +new NodeSDK({ + spanProcessors: [ + new tracing.BatchSpanProcessor( + exporter, + { + exportTimeoutMillis: 30_000, // Default to 30_000ms + maxExportBatchSize: 512, // Default to 512 spans + maxQueueSize: 2048, // Default to 2048 spans + scheduledDelayMillis: 5_000, // Default to 5_000ms + }, + ), + ], +}).start() ``` + + +
+ You can learn more about the batching options in the [Picking the right span processor](https://opentelemetry.io/docs/languages/js/instrumentation/#picking-the-right-span-processor) page. From b734e98fd3169e30efa3005d2a12f92c6781507f Mon Sep 17 00:00:00 2001 From: Valentin Cocaud Date: Wed, 25 Jun 2025 21:18:40 +0200 Subject: [PATCH 02/10] exporter settings and span list --- .../content/gateway/monitoring-tracing.mdx | 1066 ++++++++++++----- 1 file changed, 749 insertions(+), 317 deletions(-) diff --git a/packages/web/docs/src/content/gateway/monitoring-tracing.mdx b/packages/web/docs/src/content/gateway/monitoring-tracing.mdx index 9b4b0f9f52..7830fc89bf 100644 --- a/packages/web/docs/src/content/gateway/monitoring-tracing.mdx +++ b/packages/web/docs/src/content/gateway/monitoring-tracing.mdx @@ -130,14 +130,14 @@ The following are available to use with this plugin: ### OpenTelemetry Setup -For the OpenTelemetry tracing feature to work, OpenTelemetry must firt be setup. +For the OpenTelemetry tracing feature to work, OpenTelemetry JS API must be setup. We recommend to place your OpenTelemetry setup in a `telemetry.ts` file that will be your first import in your `gateway.config.ts` file. This allow instrumentations to be registered (if any) before any other packages are imported. For ease of configuration, we provide a `opentelemetrySetup` function from -'@graphql-hive/plugin-opentelemetry/setup' module, with sensible default and straightforward API +`@graphql-hive/plugin-opentelemetry/setup` module, with sensible default and straightforward API compatible with all runtimes. But this utility is not mandatory, you can use any setup relevant to your specific use case and @@ -302,7 +302,7 @@ new NodeSDK({ -#### Exporters, Span Processors and Tracer Provider +#### Trace Exporter, Span Processors and Tracer Provider Exporters are responsible of storing the traces recorded by OpenTelemetry. There is a large existing range of exporters, Hive Gateway is compatible with any exporter using `@opentelemetry/api` standard @@ -642,10 +642,38 @@ endpoint You can also use the official DataDog Tracer Provider by using manual Hive Gateway deployement and installing the dependency. -Hive Gateway opentelemetrySetup() (recommended),
OpenTelemetry NodeSDK
, 'DataDog Tracer Provider']}> +Hive Gateway opentelemetrySetup() (recommended),
OpenTelemetry NodeSDK
]}> +The official DataDog's `TracerProvider` is the recommended approach, because it enable and sets up +the correlation with DataDog APM spans. + +```ts filename="telemetry.ts" +import ddTrace from 'dd-trace' +import { opentelemetrySetup } from '@graphql-hive/plugin-opentelemetry/setup' +import { AsyncLocalStorageContextManager } from '@opentelemetry/context-async-hooks' + +const { TracerProvider } = ddTrace.init({ + // Your configuration +}) + +opentelemetrySetup({ + contextManager: null, // Dont' register a context manager, DataDog Agent registers its own. + traces: { + tracerProvider: new TracerProvider() + } +}) +``` + + + + + +It is possible to not use DataDog Agent if you want to only use DataDog as a tracing backend. + +DataDog is compatible with standard OTLP over HTTP export format. + ```ts filename="telemetry.ts" import { opentelemetrySetup } from '@graphql-hive/plugin-opentelemetry/setup' import { AsyncLocalStorageContextManager } from '@opentelemetry/context-async-hooks' @@ -665,6 +693,10 @@ opentelemetrySetup({ +It is possible to not use DataDog Agent if you want to only use DataDog as a tracing backend. + +DataDog is compatible with standard OTLP over HTTP export format. + ```ts filename="telemetry.ts" import { OTLPTraceExporter } from '@opentelemetry/exporter-trace-otlp-http' import { NodeSDK } from '@opentelemetry/sdk-node' @@ -678,27 +710,6 @@ new NodeSDK({ - - -```ts filename="telemetry.ts" -import ddTrace from 'dd-trace' -import { opentelemetrySetup } from '@graphql-hive/plugin-opentelemetry/setup' -import { AsyncLocalStorageContextManager } from '@opentelemetry/context-async-hooks' - -const { TracerProvider } = ddTrace.init({ - // Your configuration -}) - -opentelemetrySetup({ - contextManager: new AsyncLocalStorageContextManager(), - traces: { - tracerProvider: new TracerProvider() - } -}) -``` - - -
@@ -771,38 +782,42 @@ opentelemetrySetup({
-### Configuration +#### Context Propagation - +By default, Hive Gateway will +[propagate the trace context](https://opentelemetry.io/docs/concepts/context-propagation/) between +the incoming HTTP request and the outgoing HTTP requests using standard Baggage and Trace Context +propagators. - +You can configure the list of propagators that will be used. All official propagators are bundled +with Hive Gateway CLI. To use other non-official propagators, please switch to manual deployement. -Hive Gateway rely on `@opentelemetry/api` package, which means you can use official OpenTelemetry -API to configure it. By using `NodeSDK`, you will have access to the fully featured SDK with -defaults and automatic configuration based environment variables. +You will also have to pick a Context Manager. It will be responsible to keep track of the current +OpenTelemetry Context at any point of program. We recommend using the official +`AsyncLocalStorageContextManager` from `@opentelemetry/context-async-hooks` when `AsyncLocalStorage` +API is available. In other cases, you can either try `@opentelemetry/context-zone`, or pass `null` +to not use any context manager. -For a more portable approach, please refer to `Programatic Usage with any runtime` +If no Context Manager compatible with async is registered, automatic parenting of custom spans will +not work. You will have to retreive the current OpenTelemetry context from the GraphQL context, or +from the `getOtelContext` method of the plugin instance. -```ts filename="gateway.config.ts" -import { defineConfig } from '@graphql-hive/gateway' +Hive Gateway opentelemetrySetup() (recommended),
OpenTelemetry NodeSDK
]}> -export const gatewayConfig = defineConfig({ - openTelemetry: { - inheritContext: true, // Optional, whether to inherit the context from the incoming request - propagateContext: true, // Optional, whether to propagate the context to the outgoing requests - // Optional config to customize the spans. By default all spans are enabled. - traces: { - tracer: myCustomTracer, // Optional, a custom tracer to use - spans: { - http: true, // Whether to track the HTTP request/response - graphqlParse: true, // Whether to track the GraphQL parse phase - graphqlValidate: true, // Whether to track the GraphQL validate phase - graphqlExecute: true, // Whether to track the GraphQL execute phase - subgraphExecute: true, // Whether to track the subgraph execution phase - upstreamFetch: true // Whether to track the upstream HTTP requests - } - } - } + + +```ts filename="telemetry.ts" +import { opentelemetrySetup } from '@graphql-hive/plugin-opentelemetry/setup' +import { AsyncLocalStorageContextManager } from '@opentelemetry/context-async-hooks' +import { OTLPTraceExporter } from '@opentelemetry/exporter-trace-otlp-grpc' +import { B3Propagator } from '@opentelemetry/propagator-b3' + +opentelemetrySetup({ + contextManager: new AsyncLocalStorageContextManager(), + traces: { + exporter: new OTLPTraceExporter({ url: 'http://:4317' }) + }, + propagators: [new B3Propagator()] }) ``` @@ -810,61 +825,25 @@ export const gatewayConfig = defineConfig({ -```sh npm2yarn -npm i @graphql-mesh/plugin-opentelemetry -``` - -```ts filename="index.ts" -import { createGatewayRuntime } from '@graphql-hive/gateway-runtime' -import { useOpenTelemetry } from '@graphql-mesh/plugin-opentelemetry' -import { NodeSDK, resources, tracing } from '@opentelemetry/sdk-node' +```ts filename="telemetry.ts" +import { ZipkinExporter } from '@opentelemetry/exporter-zipkin' +import { B3Propagator } from '@opentelemetry/propagator-b3' +import { NodeSDK } from '@opentelemetry/sdk-node' new NodeSDK({ - // A simple output to the console. - // You can add more exporters here, please see documentation below for more examples. - traceExporter: new tracing.ConsoleSpanExporter(), - // Optional, enables automatic instrumentation to get low level Node tracing, like network spans. - instrumentations: getNodeAutoInstrumentations(), - // Optional, a base resource with the name of your service. - resource: resources.resourceFromAttributes({ - serviceName: 'my-custom-service-name' - }), - // Optional, enables automatic ressource discovery which will add all resources attribute possible - resourceDetectors: getResourceDetectors() + traceExporter: new OTLPTraceExporter({ url: 'http://:4317' }), + textMapPropagator: new B3Propagator() }).start() - -export const gateway = createGatewayRuntime({ - plugins: ctx => [ - useOpenTelemetry({ - ...ctx, - inheritContext: true, // Optional, whether to inherit the context from the incoming request - propagateContext: true, // Optional, whether to propagate the context to the outgoing requests - // Enables OTEL tracing. It is possible to use `true` to enable tracing with default config - traces: { - tracer: myCustomTracer, // Optional, a custom tracer to use - // Optional config to customize the spans. By default all spans are enabled. - spans: { - http: true, // Whether to track the HTTP request/response - graphqlParse: true, // Whether to track the GraphQL parse phase - graphqlValidate: true, // Whether to track the GraphQL validate phase - graphqlExecute: true, // Whether to track the GraphQL execute phase - subgraphExecute: true, // Whether to track the subgraph execution phase - upstreamFetch: true // Whether to track the upstream HTTP requests - } - } - }) - ] -}) ```
-#### Batching +#### Span Batching By default, if you provide only a Trace Exporter, it will be wrapped into a `BatchSpanProcessor` to -batch spans together and reduce the number of request to you backeng. +batch spans together and reduce the number of request to you backend. This is an important feature for a real world production environment, and you can configure its behaviour to exactly suites your infrastructure limits. @@ -909,46 +888,407 @@ opentelemetrySetup({ ```ts filename="telemetry.ts" import { NodeSDK, tracing } from '@opentelemetry/sdk-node' -const exporter = ... +const exporter = ... + +new NodeSDK({ + spanProcessors: [ + new tracing.BatchSpanProcessor( + exporter, + { + exportTimeoutMillis: 30_000, // Default to 30_000ms + maxExportBatchSize: 512, // Default to 512 spans + maxQueueSize: 2048, // Default to 2048 spans + scheduledDelayMillis: 5_000, // Default to 5_000ms + }, + ), + ], +}).start() +``` + +
+ +
+ +You can learn more about the batching options in the +[Picking the right span processor](https://opentelemetry.io/docs/languages/js/instrumentation/#picking-the-right-span-processor) +page. + +#### Sampling + +When your gateway have a lot of traffic, tracing every requests can become a very expensive +approach. + +A mitigation for this problem is to trace only some requests, using a strategy to choose which +request to trace or not. + +The most common strategy is to combine both a parent first (a span is picked if parent is picked) +and a ratio based on trace id (each trace, one by request, have a chance to be picked, with a given +rate). + +By default, all requests are traced. You can either provide you own Sampler, or provide a sampling +rate which will be used to setup a Parent + TraceID Ratio strategy. + +Hive Gateway opentelemetrySetup() (recommended),
OpenTelemetry NodeSDK
]}> + + + +```ts filename="telemetry.ts" +import { opentelemetrySetup } from '@graphql-hive/plugin-opentelemetry/setup' +import { JaegerRemoteSampler } from '@opentelemetry/sampler-jaeger-remote' +import { AlwaysOnSampler } from '@opentelemetry/sdk-trace-base' + +opentelemetrySetup({ + // Use Parent + TraceID Ratio strategy + samplingRate: 0.1, + + // Or use a custom Sampler + sampler: new JaegerRemoteSampler({ + endpoint: 'http://your-jaeger-agent:14268/api/sampling', + serviceName: 'your-service-name', + initialSampler: new AlwaysOnSampler(), + poolingInterval: 60000 // 60 seconds + }) +}) +``` + + + + + +```ts filename="telemetry.ts" +import { JaegerRemoteSampler } from '@opentelemetry/sampler-jaeger-remote' +import { NodeSDK, tracing } from '@opentelemetry/sdk-node' + +new NodeSDK({ + // Use Parent + TraceID Ratio strategy + sampler: new ParentBasedSampler({ + root: new TraceIdRatioBasedSampler(0.1) + }), + + // Or use a custom Sampler + sampler: new JaegerRemoteSampler({ + endpoint: 'http://your-jaeger-agent:14268/api/sampling', + serviceName: 'your-service-name', + initialSampler: new tracing.AlwaysOnSampler(), + poolingInterval: 60000 // 60 seconds + }) +}).start() +``` + + + +
+ +#### Limits + +To ensure that you don't overwhelm your tracing injestion infrastructure, you can set limits for +both cardinality and amount of data the OpenTelemetry SDK will be allowed to generate. + +Hive Gateway opentelemetrySetup() (recommended),
OpenTelemetry NodeSDK
]}> + + + +```ts filename="telemetry.ts" +import { opentelemetrySetup } from '@graphql-hive/plugin-opentelemetry/setup' + +opentelemetrySetup({ + generalLimits: {}, + traces: { + spanLimits: {} + } +}) +``` + + + + + +```ts filename="telemetry.ts" +import { JaegerRemoteSampler } from '@opentelemetry/sampler-jaeger-remote' +import { NodeSDK, tracing } from '@opentelemetry/sdk-node' + +new NodeSDK({ + generalLimits: {}, + spanLimits: {} +}).start() +``` + + + +
+ +### Configuration + +Once you have an OpenTelemetry setup file, you must import it from you `gateway.config.ts` file. It +must be the very first import so that any other package relying on OpenTelemetry have access to the +correct configuration. + +You can then enable OpenTelemetry Tracing support in the Gateway configuration. + + + + + +```ts filename="gateway.config.ts" +import './telemetry.ts' +import { defineConfig } from '@graphql-hive/gateway' + +export const gatewayConfig = defineConfig({ + openTelemetry: { + traces: true + } +}) +``` + + + + + +```sh npm2yarn +npm i @graphql-mesh/plugin-opentelemetry +``` + +```ts filename="index.ts" +import './telemetry.ts' +import { createGatewayRuntime } from '@graphql-hive/gateway-runtime' +import { useOpenTelemetry } from '@graphql-mesh/plugin-opentelemetry' + +export const gateway = createGatewayRuntime({ + plugins: ctx => [ + useOpenTelemetry({ + ...ctx, + traces: true + }) + ] +}) +``` + + + + + +#### OpenTelemetry Context + +To correlate all observability events (like tracing, metrics, logs...), OpenTelemetry have a global +and standard Context API. + +This context also allows to keep the link between related spans (for parenting or linking of spans). + +You can configure the behaviour of the plugin with this context. + + + + + +```ts filename="gateway.config.ts" +import './telemetry.ts' +import { defineConfig } from '@graphql-hive/gateway' + +export const gatewayConfig = defineConfig({ + openTelemetry: { + useContextManager: true, // If false, the parenting of spans will not rely on OTEL Context + inheritContext: true, // If false, the root span will not be based on OTEL Context, it will always be a root span + propagateContext: true // If false, the context will not be propagated to subgraphs + } +}) +``` + + + + + +```sh npm2yarn +npm i @graphql-mesh/plugin-opentelemetry +``` + +```ts filename="index.ts" +import './telemetry.ts' +import { createGatewayRuntime } from '@graphql-hive/gateway-runtime' +import { useOpenTelemetry } from '@graphql-mesh/plugin-opentelemetry' + +export const gateway = createGatewayRuntime({ + plugins: ctx => [ + useOpenTelemetry({ + ...ctx, + useContextManager: true, // If false, the parenting of spans will not rely on OTEL Context + inheritContext: true, // If false, the root span will not be based on OTEL Context, it will always be a root span + propagateContext: true // If false, the context will not be propagated to subgraphs + }) + ] +}) +``` + + + + + +#### OpenTelemetry Diagnostics + +If you encounter an issue with you OpenTelemetry setup, you can enable the Diagnostics API. This +will enable logging of OpenTelemetry SDK based on `OTEL_LOG_LEVEL` env variable. + +By default, Hive Gateway configure the Diagnostics API to output logs using Hive Gateway's logger. +You can disable this using `configureDiagLogger` option. + + + + + +```ts filename="gateway.config.ts" +import './telemetry.ts' +import { defineConfig } from '@graphql-hive/gateway' + +export const gatewayConfig = defineConfig({ + openTelemetry: { + ...ctx, + // Use the default DiagLogger, which outputs logs directly to stdout + configureDiagLogger: false + } +}) +``` + + + + + +```ts filename="gateway.config.ts" +import './telemetry.ts' +import { createGatewayRuntime } from '@graphql-hive/gateway-runtime' +import { useOpenTelemetry } from '@graphql-mesh/plugin-opentelemetry' + +export const gateway = createGatewayRuntime({ + plugins: ctx => [ + useOpenTelemetry({ + ...ctx, + // Use the default DiagLogger, which outputs logs directly to stdout + configureDiagLogger: false + }) + ] +}) +``` + + + + + +#### Graceful shutdown + +Since spans are batched by default, it is possible to miss some traces if the batching processor is +not properly flused when the process exits. + +To avoid this kind of data loss, Hive Gateway is calling `forceFlush` method on the registered +Tracer Provider by default. You can customize which method to call or entirely disable this +behaviour by using the `flushOnDispose` option. + +Hive Gateway opentelemetrySetup() (recommended),
OpenTelemetry NodeSDK
]}> + + + +```ts filename="gateway.config.ts" +import './telemetry.ts' +import { defineConfig } from '@graphql-hive/gateway' + +export const gatewayConfig = defineConfig({ + openTelemetry: { + ...ctx, + // Disable the auto-flush on shutdown + flushOnDispose: false, + // or call a custom method + flushOnDispose: 'flush' + } +}) +``` + + + + + +```ts filename="gateway.config.ts" +import './telemetry.ts' +import { createGatewayRuntime } from '@graphql-hive/gateway-runtime' +import { useOpenTelemetry } from '@graphql-mesh/plugin-opentelemetry' + +export const gateway = createGatewayRuntime({ + plugins: ctx => [ + useOpenTelemetry({ + ...ctx, + // Disable the auto-flush on shutdown + flushOnDispose: false, + // or call a custom method + flushOnDispose: 'flush' + }) + ] +}) +``` + + + +
+ +### Reported Spans + +The plugin exports the following OpenTelemetry Spans: + +#### Background Spans + +
+ +Gateway Initialization + +By default, the plugin will create a span from the start of the gateway process to the first schema +load. + +All spans hapenning during this time will be parented under this initialisation span, including the +schema loading span. + +You may disable this by setting `traces.spans.initialization` to `false`: + +```ts +const openTelemetryConfig = { + traces: { + spans: { + initialization: false + } + } +} +``` + +
-new NodeSDK({ - spanProcessors: [ - new tracing.BatchSpanProcessor( - exporter, - { - exportTimeoutMillis: 30_000, // Default to 30_000ms - maxExportBatchSize: 512, // Default to 512 spans - maxQueueSize: 2048, // Default to 2048 spans - scheduledDelayMillis: 5_000, // Default to 5_000ms - }, - ), - ], -}).start() -``` +
- +Schema Loading - +By default, the plugin will create a span covering each loading of a schema. It can be useful when +polling or file watch is enabled to identify when the schema changes. -You can learn more about the batching options in the -[Picking the right span processor](https://opentelemetry.io/docs/languages/js/instrumentation/#picking-the-right-span-processor) -page. +You may disable this by setting `traces.spans.schema` to `false`: -### Reported Spans +```ts +const openTelemetryConfig = { + traces: { + spans: { + schema: false + } + } +} +``` + +
-The plugin exports OpenTelemetry spans for the following operations: +#### Request Spans
-HTTP Server +HTTP Request This span is created for each incoming HTTP request, and acts as a root span for the entire request. Disabling this span will also disable the other hooks and spans. -By default, the plugin will a root span for the HTTP layer as a span (`METHOD /path`) with the -following attributes for the HTTP request: +By default, the plugin will create a root span for the HTTP layer as a span (` /path`, eg. +`POST /graphql`) with the following attributes: - `http.method`: The HTTP method - `http.url`: The HTTP URL @@ -970,42 +1310,91 @@ And the following attributes for the HTTP response: [`Exception`](https://opentelemetry.io/docs/specs/otel/trace/exceptions/). -You may disable this by setting `spans.http` to `false`: +You may disable this by setting `traces.spans.http` to `false`: ```ts const openTelemetryConfig = { - exporters: [ - /* ... */ - ], - spans: { - /* ... */ - http: false + traces: { + spans: { + http: false + } } } ``` -Or, you may filter the spans by setting the `spans` configuration to a function: +Or, you may filter the spans by setting the `traces.spans.http` configuration to a function: ```ts filename="gateway.config.ts" const openTelemetryConfig = { - exporters: [ - /* ... */ - ], - spans: { - /* ... */ - http: payload => { - // Filter the spans based on the payload - return true + traces: { + spans: { + http: ({ request }) => { + // Filter the spans based on the request + return true + } } } } ``` +
+ +
+ +GraphQL Operation + + + This span is created for each GraphQL operation found in incoming HTTP requests, and acts as a + parent span for the entire graphql operation. Disabling this span will also disable the other + hooks and spans related to the execution of operation. + + +By default, the plugin will create a span for the GraphQL layer as a span +(`graphql.operation ` or `graphql.operation` for unexecutable operations) with the +following attributes: + +- `graphql.operation.type`: The type of operation (`query`, `mutation` or `subscription`). +- `grapqhl.operation.name`: The name of the operation to execute, `Anonymous` for operations without + name. +- `graphql.document`: The operation document as a GraphQL string + - The `payload` object is the same as the one passed to the [`onRequest` - hook](https://github.com/ardatan/whatwg-node/blob/master/packages/server/src/plugins/types.ts#L16-L25). + An error in the parse phase will be reported as an [error + span](https://opentelemetry.io/docs/specs/semconv/exceptions/exceptions-spans/), including the + error message and as an OpenTelemetry + [`Exception`](https://opentelemetry.io/docs/specs/otel/trace/exceptions/). +You may disable this by setting `traces.spans.graphql` to `false`: + +```ts +const openTelemetryConfig = { + traces: { + traces: { + spans: { + graphql: false + } + } + } +} +``` + +Or, you may filter the spans by setting the `traces.spans.graphql` configuration to a function which +takes the GraphQL context as parameter: + +```ts filename="gateway.config.ts" +const openTelemetryConfig = { + traces: { + spans: { + graphql: ({ context }) => { + // Filter the span based on the GraphQL context + return true + } + } + } +} +``` +
@@ -1018,6 +1407,10 @@ following attributes: - `graphql.document`: The GraphQL query string - `graphql.operation.name`: The operation name +If a parsing error is reported, the following attribute will also be present: + +- `graphql.error.count`: `1` if a parse error occured + An error in the parse phase will be reported as an [error span](https://opentelemetry.io/docs/specs/semconv/exceptions/exceptions-spans/), including the @@ -1025,42 +1418,33 @@ following attributes: [`Exception`](https://opentelemetry.io/docs/specs/otel/trace/exceptions/). -You may disable this by setting `spans.graphqlParse` to `false`: +You may disable this by setting `traces.spans.graphqlParse` to `false`: ```ts filename="gateway.config.ts" const openTelemetryConfig = { - exporters: [ - /* ... */ - ], - spans: { - /* ... */ - graphqlParse: false + traces: { + spans: { + graphqlParse: false + } } } ``` -Or, you may filter the spans by setting the `spans` configuration to a function: +Or, you may filter the spans by setting the `traces.spans.graphqlParse` configuration to a function: ```ts filename="gateway.config.ts" const openTelemetryConfig = { - exporters: [ - /* ... */ - ], - spans: { - /* ... */ - graphqlParse: payload => { - // Filter the spans based on the payload - return true + traces: { + spans: { + graphqlParse: ({ context }) => { + // Filter the spans based on the GraphQL context + return true + } } } } ``` - - The `payload` object is the same as the one passed to the [`onParse` - hook](https://the-guild.dev/graphql/envelop/v4/plugins/lifecycle#before). - -
@@ -1073,6 +1457,10 @@ following attributes: - `graphql.document`: The GraphQL query string - `graphql.operation.name`: The operation name +If a validation error is reported, the following attribute will also be present: + +- `graphql.error.count`: The number of validation errors + An error in the validate phase will be reported as an [error span](https://opentelemetry.io/docs/specs/semconv/exceptions/exceptions-spans/), including the @@ -1080,42 +1468,78 @@ following attributes: [`Exception`](https://opentelemetry.io/docs/specs/otel/trace/exceptions/). -You may disable this by setting `spans.graphqlValidate` to `false`: +You may disable this by setting `traces.spans.graphqlValidate` to `false`: ```ts filename="gateway.config.ts" const openTelemetryConfig = { - exporters: [ - /* ... */ - ], - spans: { - /* ... */ - graphqlValidate: false + traces: { + spans: { + graphqlValidate: false + } } } ``` -Or, you may filter the spans by setting the `spans` configuration to a function: +Or, you may filter the spans by setting the `traces.spans.graphqlValidate` configuration to a +function: ```ts filename="gateway.config.ts" const openTelemetryConfig = { - exporters: [ - /* ... */ - ], - spans: { - /* ... */ - graphqlValidate: payload => { - // Filter the spans based on the payload - return true + traces: { + spans: { + graphqlValidate: ({ context }) => { + // Filter the spans based on the GraphQL context + return true + } } } } ``` +
+ +
+ +Graphql Context Building + +By default, the plugin will report the validation phase as a span (`graphql.context`). This span +doesn't have any attribute. + - The `payload` object is the same as the one passed to the [`onValidate` - hook](https://the-guild.dev/graphql/envelop/v4/plugins/lifecycle#before-1). + An error in the context building phase will be reported as an [error + span](https://opentelemetry.io/docs/specs/semconv/exceptions/exceptions-spans/), including the + error message and as an OpenTelemetry + [`Exception`](https://opentelemetry.io/docs/specs/otel/trace/exceptions/). +You may disable this by setting `traces.spans.graphqlContextBuilding` to `false`: + +```ts filename="gateway.config.ts" +const openTelemetryConfig = { + traces: { + spans: { + graphqlContextBuilding: false + } + } +} +``` + +Or, you may filter the spans by setting the `traces.spans.graphqlContextBuilding` configuration to a +function: + +```ts filename="gateway.config.ts" +const openTelemetryConfig = { + traces: { + spans: { + graphqlContextBuilding: ({ context }) => { + // Filter the spans based on the GraphQL context + return true + } + } + } +} +``` +
@@ -1126,9 +1550,13 @@ By default, the plugin will report the execution phase as a span (`graphql.execu following attributes: - `graphql.document`: The GraphQL query string -- `graphql.operation.name`: The operation name +- `graphql.operation.name`: The operation name (`Anonymous` for operations without name) - `graphql.operation.type`: The operation type (`query`/`mutation`/`subscription`) +If an execution error is reported, the following attribute will also be present: + +- `graphql.error.count`: The number of errors in the execution result + An error in the execute phase will be reported as an [error span](https://opentelemetry.io/docs/specs/semconv/exceptions/exceptions-spans/), including the @@ -1136,105 +1564,76 @@ following attributes: [`Exception`](https://opentelemetry.io/docs/specs/otel/trace/exceptions/). -You may disable this by setting `spans.graphqlExecute` to `false`: +You may disable this by setting `traces.spans.graphqlExecute` to `false`: ```ts filename="gateway.config.ts" const openTelemetryConfig = { - exporters: [ - /* ... */ - ], - spans: { - /* ... */ - graphqlExecute: false + traces: { + spans: { + graphqlExecute: false + } } } ``` -Or, you may filter the spans by setting the `spans` configuration to a function: +Or, you may filter the spans by setting the `traces.spans.graphqlExecute` configuration to a +function: ```ts filename="gateway.config.ts" const openTelemetryConfig = { - exporters: [ - /* ... */ - ], - spans: { - /* ... */ - graphqlExecute: payload => { - // Filter the spans based on the payload - return true + traces: { + spans: { + graphqlExecute: ({ context }) => { + // Filter the spans based on the GraphQL context + return true + } } } } ``` - - The `payload` object is the same as the one passed to the [`onExecute` - hook](https://the-guild.dev/graphql/envelop/v4/plugins/lifecycle#before-3). - -
Subgraph Execute -By default, the plugin will report the subgraph execution phase as a span (`subgraph.execute`) with -the following attributes: +By default, the plugin will report the subgraph execution phase as a client span +(`subgraph.execute`) with the following attributes: - `graphql.document`: The GraphQL query string executed to the upstream - `graphql.operation.name`: The operation name - `graphql.operation.type`: The operation type (`query`/`mutation`/`subscription`) - `gateway.upstream.subgraph.name`: The name of the upstream subgraph -In addition, the span will include the following attributes for the HTTP requests; - -- `http.method`: The HTTP method -- `http.url`: The HTTP URL -- `http.route`: The HTTP status code -- `http.scheme`: The HTTP scheme -- `net.host.name`: The hostname -- `http.host`: The HTTP host - -And the following attributes for the HTTP response: - -- `http.status_code`: The HTTP status code - -You may disable this by setting `spans.subgraphExecute` to `false`: +You may disable this by setting `traces.spans.subgraphExecute` to `false`: ```ts filename="gateway.config.ts" const openTelemetryConfig = { - exporters: [ - /* ... */ - ], - spans: { - /* ... */ - subgraphExecute: false + traces: { + spans: { + subgraphExecute: false + } } } ``` -Or, you may filter the spans by setting the `spans.subgraphExecute` configuration to a function: +Or, you may filter the spans by setting the `traces.spans.subgraphExecute` configuration to a +function: ```ts filename="gateway.config.ts" const openTelemetryConfig = { - exporters: [ - /* ... */ - ], - spans: { - /* ... */ - subgraphExecute: payload => { - // Filter the spans based on the payload - return true + traces: { + spans: { + subgraphExecute: ({ executionRequest, subgraphName }) => { + // Filter the spans based on the target SubGraph name and the Execution Request + return true + } } } } ``` - - The `payload` object is the same as the one passed to the [`onSubgraphHook` - hook](/docs/gateway/other-features/custom-plugins#onsubgraphexecute). - -
@@ -1252,104 +1651,146 @@ The following attributes are included in the span: - `http.scheme`: The HTTP scheme - `net.host.name`: The hostname - `http.host`: The HTTP host +- `http.request.resend_count`: Number of retry attempt. Only present starting from the first retry. And the following attributes for the HTTP response: - `http.status_code`: The HTTP status code -You may disable this by setting `spans.upstreamFetch` to `false`: + + An error in the fetch phase (including responses with a non-ok status code) will be reported as an + [error span](https://opentelemetry.io/docs/specs/semconv/exceptions/exceptions-spans/), including + the error message and as an OpenTelemetry + [`Exception`](https://opentelemetry.io/docs/specs/otel/trace/exceptions/). + + +You may disable this by setting `traces.spans.upstreamFetch` to `false`: ```ts filename="gateway.config.ts" const openTelemetryConfig = { - exporters: [ - /* ... */ - ], - spans: { - /* ... */ - upstreamFetch: false + traces: { + spans: { + upstreamFetch: false + } } } ``` -Or, you may filter the spans by setting the `spans.upstreamFetch` configuration to a function: +Or, you may filter the spans by setting the `traces.spans.upstreamFetch` configuration to a +function: ```ts filename="gateway.config.ts" const openTelemetryConfig = { - exporters: [ - /* ... */ - ], - spans: { - /* ... */ - upstreamFetch: payload => { - // Filter the spans based on the payload - return true + traces: { + spans: { + upstreamFetch: ({ executionRequest }) => { + // Filter the spans based on the Execution Request + return true + } } } } ``` - - The `payload` object is the same as the one passed to the [`onFetch` - hook](/docs/gateway/other-features/custom-plugins#onfetch). - -
-### Context Propagation +### Reported Events -By default, the plugin will -[propagate the trace context](https://opentelemetry.io/docs/concepts/context-propagation/) between -the incoming HTTP request and the outgoing HTTP requests. +The plugin exports the folowing OpenTelemetry Events. -You may disable this by setting `inheritContext` or `propagateContext` to `false`: +Events are attached to the current span, meaning that they will be attached to your custom spans if +you use them. It also means that events can be orphans if you didn't properly setup an asyn +compatible Context Manager - +
- +Cache Read and Write + +By default, the plugin will report any cache read or write as an event. The possible event names +are: + +- `gateway.cache.miss`: A cache read happened, but the key didn't match any entity +- `gateway.cache.hit`: A cache read happened, and the key did match an entity +- `gateway.cache.write`: A new entity have been added to the cache store + +All those events have the following attributes: + +- `gateway.cache.key`: The key of the cache entry +- `gateway.cache.ttl`: The ttl of the cache entry + +You may disable this by setting `traces.events.cache` to `false`: ```ts filename="gateway.config.ts" -import { defineConfig } from '@graphql-hive/gateway' +const openTelemetryConfig = { + traces: { + events: { + cache: false + } + } +} +``` -export const gatewayConfig = defineConfig({ - openTelemetry: { - exporters: [ - /* ... */ - ], - // Controls the propagation of the trace context between the incoming HTTP request and Hive Gateway - inheritContext: false, - // Controls the propagation of the trace context between Hive Gateway and the upstream HTTP requests - propagateContext: false +Or, you may filter the spans by setting the `traces.spans.upstreamFetch` configuration to a +function: + +```ts filename="gateway.config.ts" +const openTelemetryConfig = { + traces: { + events: { + cache: ({ key, action }) => { + // Filter the event based on action ('read' or 'write') and the entity key + return true + } + } } -}) +} ``` - +
- +
-```ts filename="gateway.config.ts" -import { createGatewayRuntime } from '@graphql-hive/gateway-runtime' -import { useOpenTelemetry } from '@graphql-mesh/plugin-opentelemetry' +Cache Error -export const gateway = createGatewayRuntime({ - plugins: ctx => [ - useOpenTelemetry({ - ...ctx, - exporters: [ - /* ... */ - ], - // Controls the propagation of the trace context between the incoming HTTP request and Hive Gateway - inheritContext: false, - // Controls the propagation of the trace context between Hive Gateway and the upstream HTTP requests - propagateContext: false - }) - ] -}) +By default, the plugin will report any cache error as an event (`gateway.cache.error`). This events +have the following attributes: + +- `gateway.cache.key`: The key of the cache entry +- `gateway.cache.ttl`: The ttl of the cache entry +- `gateway.cache.action`: The type of action (`read` or `write`) +- `exception.type`: The type of error (the `code` if it exists, the message otherwise) +- `exception.message`: The message of the error +- `exception.stacktrace`: The error stacktrace as a string + +You may disable this by setting `traces.events.cache` to `false`: + +```ts filename="gateway.config.ts" +const openTelemetryConfig = { + traces: { + events: { + cache: false + } + } +} ``` - +Or, you may filter the spans by setting the `traces.spans.upstreamFetch` configuration to a +function: - +```ts filename="gateway.config.ts" +const openTelemetryConfig = { + traces: { + events: { + cache: ({ key, action }) => { + // Filter the event based on action ('read' or 'write') and the entity key + return true + } + } + } +} +``` + +
### Troubleshooting @@ -1365,12 +1806,14 @@ In addition, you can use the stdout exporter to log the traces to the console: -```ts filename="gateway.config.ts" -import { createStdoutExporter, defineConfig } from '@graphql-hive/gateway' +```ts filename="telemetry.ts" +import { opentelemetrySetup } from '@graphql-hive/plugin-opentelemetry/setup' +import { AsyncLocalStorageContextManager } from '@opentelemetry/context-async-hooks' -export const gatewayConfig = defineConfig({ - openTelemetry: { - exporters: [createStdoutExporter()] +opentelemetrySetup({ + contextManager: new AsyncLocalStorageContextManager + traces: { + console: true } }) ``` @@ -1379,24 +1822,13 @@ export const gatewayConfig = defineConfig({ - - Beware that OpenTelemetry JavaScript SDK writes spans using `console.dir`. Meaning, - serverless/on-the-edge environments that don't support `console.dir` (like [Cloudflare - Workers](https://developers.cloudflare.com/workers/runtime-apis/console/)) wont show any logs. - - -```ts filename="gateway.config.ts" -import { createGatewayRuntime } from '@graphql-hive/gateway-runtime' -import { createStdoutExporter, useOpenTelemetry } from '@graphql-mesh/plugin-opentelemetry' +```ts filename="telemetry.ts" +import { NodeSDK, tracing } from '@opentelemetry/sdk-node' -export const gateway = createGatewayRuntime({ - plugins: ctx => [ - useOpenTelemetry({ - ...ctx, - exporters: [createStdoutExporter()] - }) - ] -}) +new NodeSDK({ + // Use `spanProcessors` instead of `traceExporter` to avoid the default batching configuration + spanProcessors: [new tracing.SimpleSpanProcessor(new tracing.ConsoleSpanExporter())] +}).start() ``` From 87462a25cf21389ac6a6079d62238585e6ddf6f4 Mon Sep 17 00:00:00 2001 From: Valentin Cocaud Date: Wed, 25 Jun 2025 21:35:54 +0200 Subject: [PATCH 03/10] add precision about schema loading span --- packages/web/docs/src/content/gateway/monitoring-tracing.mdx | 3 +++ 1 file changed, 3 insertions(+) diff --git a/packages/web/docs/src/content/gateway/monitoring-tracing.mdx b/packages/web/docs/src/content/gateway/monitoring-tracing.mdx index 7830fc89bf..392383cbec 100644 --- a/packages/web/docs/src/content/gateway/monitoring-tracing.mdx +++ b/packages/web/docs/src/content/gateway/monitoring-tracing.mdx @@ -1262,6 +1262,9 @@ const openTelemetryConfig = { By default, the plugin will create a span covering each loading of a schema. It can be useful when polling or file watch is enabled to identify when the schema changes. +Schema loading in Hive Gateway can be lazy, which means it can be triggered as part of the handling +of a request. If it happens, the schema loading span will be added as a link to the current span. + You may disable this by setting `traces.spans.schema` to `false`: ```ts From 9873583eee3c1f1125609674bd3448ef9bb0337a Mon Sep 17 00:00:00 2001 From: Valentin Cocaud Date: Thu, 26 Jun 2025 10:06:59 +0200 Subject: [PATCH 04/10] add tracer configuration --- .../content/gateway/monitoring-tracing.mdx | 50 ++++++++++++++++++- 1 file changed, 48 insertions(+), 2 deletions(-) diff --git a/packages/web/docs/src/content/gateway/monitoring-tracing.mdx b/packages/web/docs/src/content/gateway/monitoring-tracing.mdx index 392383cbec..21b2bf20f4 100644 --- a/packages/web/docs/src/content/gateway/monitoring-tracing.mdx +++ b/packages/web/docs/src/content/gateway/monitoring-tracing.mdx @@ -1140,7 +1140,6 @@ import { defineConfig } from '@graphql-hive/gateway' export const gatewayConfig = defineConfig({ openTelemetry: { - ...ctx, // Use the default DiagLogger, which outputs logs directly to stdout configureDiagLogger: false } @@ -1190,7 +1189,6 @@ import { defineConfig } from '@graphql-hive/gateway' export const gatewayConfig = defineConfig({ openTelemetry: { - ...ctx, // Disable the auto-flush on shutdown flushOnDispose: false, // or call a custom method @@ -1225,6 +1223,54 @@ export const gateway = createGatewayRuntime({
+#### Tracer + +By default, Hive Gateway will create a tracer named `gateway`. You can provide your own tracer if +needed. + +Hive Gateway opentelemetrySetup() (recommended),
OpenTelemetry NodeSDK
]}> + + + +```ts filename="gateway.config.ts" +import './telemetry.ts' +import { defineConfig } from '@graphql-hive/gateway' +import { trace } from '@opentelemetry/api' + +export const gatewayConfig = defineConfig({ + openTelemetry: { + traces: { + tracer: trace.getTracer('my-custom-tracer') + } + } +}) +``` + + + + + +```ts filename="gateway.config.ts" +import './telemetry.ts' +import { createGatewayRuntime } from '@graphql-hive/gateway-runtime' +import { useOpenTelemetry } from '@graphql-mesh/plugin-opentelemetry' + +export const gateway = createGatewayRuntime({ + plugins: ctx => [ + useOpenTelemetry({ + ...ctx, + traces: { + tracer: trace.getTracer('my-custom-tracer') + } + }) + ] +}) +``` + + + +
+ ### Reported Spans The plugin exports the following OpenTelemetry Spans: From 0f310c71f08a8952d210608998ceafb797385e25 Mon Sep 17 00:00:00 2001 From: Valentin Cocaud Date: Thu, 26 Jun 2025 11:58:51 +0200 Subject: [PATCH 05/10] add explanations for custom spans --- .../content/gateway/monitoring-tracing.mdx | 602 +++++++++++++----- 1 file changed, 444 insertions(+), 158 deletions(-) diff --git a/packages/web/docs/src/content/gateway/monitoring-tracing.mdx b/packages/web/docs/src/content/gateway/monitoring-tracing.mdx index 21b2bf20f4..74befb5b37 100644 --- a/packages/web/docs/src/content/gateway/monitoring-tracing.mdx +++ b/packages/web/docs/src/content/gateway/monitoring-tracing.mdx @@ -1076,52 +1076,14 @@ This context also allows to keep the link between related spans (for parenting o You can configure the behaviour of the plugin with this context. - - - - ```ts filename="gateway.config.ts" -import './telemetry.ts' -import { defineConfig } from '@graphql-hive/gateway' - -export const gatewayConfig = defineConfig({ - openTelemetry: { - useContextManager: true, // If false, the parenting of spans will not rely on OTEL Context - inheritContext: true, // If false, the root span will not be based on OTEL Context, it will always be a root span - propagateContext: true // If false, the context will not be propagated to subgraphs - } +const opentelemetryConfig = { + useContextManager: true, // If false, the parenting of spans will not rely on OTEL Context + inheritContext: true, // If false, the root span will not be based on OTEL Context, it will always be a root span + propagateContext: true // If false, the context will not be propagated to subgraphs }) ``` - - - - -```sh npm2yarn -npm i @graphql-mesh/plugin-opentelemetry -``` - -```ts filename="index.ts" -import './telemetry.ts' -import { createGatewayRuntime } from '@graphql-hive/gateway-runtime' -import { useOpenTelemetry } from '@graphql-mesh/plugin-opentelemetry' - -export const gateway = createGatewayRuntime({ - plugins: ctx => [ - useOpenTelemetry({ - ...ctx, - useContextManager: true, // If false, the parenting of spans will not rely on OTEL Context - inheritContext: true, // If false, the root span will not be based on OTEL Context, it will always be a root span - propagateContext: true // If false, the context will not be propagated to subgraphs - }) - ] -}) -``` - - - - - #### OpenTelemetry Diagnostics If you encounter an issue with you OpenTelemetry setup, you can enable the Diagnostics API. This @@ -1130,46 +1092,14 @@ will enable logging of OpenTelemetry SDK based on `OTEL_LOG_LEVEL` env variable. By default, Hive Gateway configure the Diagnostics API to output logs using Hive Gateway's logger. You can disable this using `configureDiagLogger` option. - - - - -```ts filename="gateway.config.ts" -import './telemetry.ts' -import { defineConfig } from '@graphql-hive/gateway' - -export const gatewayConfig = defineConfig({ - openTelemetry: { - // Use the default DiagLogger, which outputs logs directly to stdout - configureDiagLogger: false - } -}) -``` - - - - ```ts filename="gateway.config.ts" -import './telemetry.ts' -import { createGatewayRuntime } from '@graphql-hive/gateway-runtime' -import { useOpenTelemetry } from '@graphql-mesh/plugin-opentelemetry' - -export const gateway = createGatewayRuntime({ - plugins: ctx => [ - useOpenTelemetry({ - ...ctx, - // Use the default DiagLogger, which outputs logs directly to stdout - configureDiagLogger: false - }) - ] -}) +const opentelemetryConfig = { + // Use the default DiagLogger, which outputs logs directly to stdout + configureDiagLogger: false +} ``` - - - - #### Graceful shutdown Since spans are batched by default, it is possible to miss some traces if the batching processor is @@ -1179,98 +1109,28 @@ To avoid this kind of data loss, Hive Gateway is calling `forceFlush` method on Tracer Provider by default. You can customize which method to call or entirely disable this behaviour by using the `flushOnDispose` option. -Hive Gateway opentelemetrySetup() (recommended),
OpenTelemetry NodeSDK
]}> - - - ```ts filename="gateway.config.ts" -import './telemetry.ts' -import { defineConfig } from '@graphql-hive/gateway' - -export const gatewayConfig = defineConfig({ - openTelemetry: { - // Disable the auto-flush on shutdown - flushOnDispose: false, - // or call a custom method - flushOnDispose: 'flush' - } -}) -``` - - - - - -```ts filename="gateway.config.ts" -import './telemetry.ts' -import { createGatewayRuntime } from '@graphql-hive/gateway-runtime' -import { useOpenTelemetry } from '@graphql-mesh/plugin-opentelemetry' - -export const gateway = createGatewayRuntime({ - plugins: ctx => [ - useOpenTelemetry({ - ...ctx, - // Disable the auto-flush on shutdown - flushOnDispose: false, - // or call a custom method - flushOnDispose: 'flush' - }) - ] -}) +const opentelemetryConfig = { + // Disable the auto-flush on shutdown + flushOnDispose: false, + // or call a custom method + flushOnDispose: 'flush' +} ``` - - -
- #### Tracer By default, Hive Gateway will create a tracer named `gateway`. You can provide your own tracer if needed. -Hive Gateway opentelemetrySetup() (recommended),
OpenTelemetry NodeSDK
]}> - - - ```ts filename="gateway.config.ts" -import './telemetry.ts' -import { defineConfig } from '@graphql-hive/gateway' -import { trace } from '@opentelemetry/api' - -export const gatewayConfig = defineConfig({ - openTelemetry: { - traces: { - tracer: trace.getTracer('my-custom-tracer') - } +const opentelemetryConfig = { + traces: { + tracer: trace.getTracer('my-custom-tracer') } -}) -``` - - - - - -```ts filename="gateway.config.ts" -import './telemetry.ts' -import { createGatewayRuntime } from '@graphql-hive/gateway-runtime' -import { useOpenTelemetry } from '@graphql-mesh/plugin-opentelemetry' - -export const gateway = createGatewayRuntime({ - plugins: ctx => [ - useOpenTelemetry({ - ...ctx, - traces: { - tracer: trace.getTracer('my-custom-tracer') - } - }) - ] -}) +} ``` - - -
- ### Reported Spans The plugin exports the following OpenTelemetry Spans: @@ -1841,6 +1701,432 @@ const openTelemetryConfig = { +### Custom spans + +Hive Gateway relys on official OpenTelemetry API, which means it is compatible with +`@opentelemetry/api`. + +You can use any tool relying on it too, or directly use it to create your own custom spans. + +To parent spans correctly, an async compatible Context Manager is highly recommended, but we also +provide an alternative if your runtime doesn't implement `AsyncLocalStorage` or you want to avoid +the related performance cost. + + + + + +If you are using an async compatible context manager, you can simply use the standard +`@opentelemetry/api` methods, as shown in +[OpenTelemetry documentation](https://opentelemetry.io/docs/languages/js/instrumentation/#create-spans). + + + + + +The Gateway's tracer is available in the graphql context. If you don't have access to the context, +you can either create your own tracer, or manually instanciate the opentelemetry plugin (see +Programatic Usage). + +```ts filename="gateway.config.ts" +import { defineConfig } from '@graphql-hive/gateway' +import { opentelemetrySetup } from '@graphql-mesh/plugin-opentelemetry/setup' +import { AsyncLocalStorageContextManager } from '@opentelemetry/context-async-hooks' +import { useGenericAuth } from '@envelop/generic-auth' + +opentelemetrySetup({ + contextManager: new AsyncLocalStorageContextManager(), + traces: { console: true }, +}) + +export const gatewayConfig = defineConfig({ + openTelemetry: { + traces: true, + }, + plugins: () => [ + useGenericAuth({ + resolveUserFn: ({ context }) => + // `startActiveSpan` will rely on the current context to parent the new span correctly + // You can also use your own tracer instead of Hive Gateway's one. + context.opentelemetry.tracer.startActiveSpan('users.fetch', (span) => { + const user = await fetchUser(extractUserIdFromContext(context)) + span.end(); + return user + }) + }), + ], +}) +``` + + + + + +The Gateway's tracer is available in the graphql context (`context.opentelemetry.tracer`). If you +don't have access to the graphql context, you can either create your own tracer, or use `getTracer` +plugin method (as shown in this example). + +```ts filename="index.ts" +import './telemetry.ts' +import { createGatewayRuntime } from '@graphql-hive/gateway-runtime' +import { useOpenTelemetry } from '@graphql-mesh/plugin-opentelemetry' +import { AsyncLocalStorageContextManager } from '@opentelemetry/context-async-hooks' +import { useGenericAuth } from '@envelop/generic-auth' + +opentelemetrySetup({ + contextManager: new AsyncLocalStorageContextManager(), + traces: { console: true }, +}) + +export const gateway = createGatewayRuntime({ + plugins: ctx => { + const otelPlugin = useOpenTelemetry({ + ...ctx, + traces: true + }) + + return [ + otelPlugin, + useGenericAuth({ + resolveUserFn: ({ context }) => + // `startActiveSpan` will rely on the current context to parent the new span correctly + // You can also use your own tracer instead of Hive Gateway's one. + otelPlugin.getTracer().startActiveSpan('users.fetch', (span) => { + const user = await fetchUser(extractUserIdFromContext(context)) + span.end(); + return user + }) + }), + ], + } +}) +``` + + + + + + + + + +If you can't or don't want to use the Context Manager, Hive Gateway provides a cross plateform +context tracking mechanism. + +To parent spans correctly, you will have to manually provide the current OTEL context. You can +retreive the current OTEL context by either using the `context.opentelemetry.activeContext` +function, or the plugin's method `getOtelContext()`. + + + + + +If you don't have access to the graphql context, manually instanciate the opentelemetry plugin (see +Programatic Usage). + +```ts filename="gateway.config.ts" +import { defineConfig } from '@graphql-hive/gateway' +import { opentelemetrySetup } from '@graphql-mesh/plugin-opentelemetry/setup' +import { AsyncLocalStorageContextManager } from '@opentelemetry/context-async-hooks' +import { useGenericAuth } from '@envelop/generic-auth' + +opentelemetrySetup({ + contextManager: null, // Don't register any context manager + traces: { console: true }, +}) + +export const gatewayConfig = defineConfig({ + openTelemetry: { + useContextManager: false, // Make sure to disable context manager usage + traces: true, + }, + plugins: () => [ + useGenericAuth({ + resolveUserFn: ({ context }) => { + const ctx = context.opentelemetry.activeContext(); + + // Explicitly pass the parent context as the third argument. + return context.opentelemetry.tracer.startActiveSpan('users.fetch', {}, ctx, (span) => { + const user = await fetchUser(extractUserIdFromContext(context)) + span.end(); + return user + }) + } + }), + ], +}) +``` + + + + + +The Gateway's tracer is available in the graphql context (`context.opentelemetry.tracer`). If you +don't have access to the graphql context, you can either create your own tracer, or use `getTracer` +plugin method (as shown in this example). + +```ts filename="index.ts" +import './telemetry.ts' +import { createGatewayRuntime } from '@graphql-hive/gateway-runtime' +import { useOpenTelemetry } from '@graphql-mesh/plugin-opentelemetry' +import { AsyncLocalStorageContextManager } from '@opentelemetry/context-async-hooks' +import { useGenericAuth } from '@envelop/generic-auth' + +opentelemetrySetup({ + contextManager: null, // Don't register any context manager + traces: { console: true }, +}) + +export const gateway = createGatewayRuntime({ + plugins: ctx => { + const otelPlugin = useOpenTelemetry({ + ...ctx, + useContextManager: false, // Make sure to disable context manager usage + traces: true + }) + + return [ + otelPlugin, + useGenericAuth({ + resolveUserFn: ({ context }) => { + // Retreive current OTEL Context by passing an object containing the graphql `context`, + // the http `request` and/or the `executionRequest`. The appropriate context will be + // found based on which of those properties are provided. + const ctx = otelPlugin.getOtelContext({ context }); + + // Explicitly pass the parent context as the third argument. + return otelPlugin.getTracer().startActiveSpan('users.fetch', {}, ctx, (span) => { + const user = await fetchUser(extractUserIdFromContext(context)) + span.end(); + return user + }) + } + }), + ], + } +}) +``` + + + + + + + + + +### Custom Span Attributes, Events and Links + +You can add custom attribute to Hive Gateway's spans by using the standard `@opentelemetry/api` +package. You can use the same package to record custom +[Events](https://opentelemetry.io/docs/languages/js/instrumentation/#span-events) or +[Links](https://opentelemetry.io/docs/languages/js/instrumentation/#span-links). + +This can be done by getting access to the current span. + +If you have an async compatible Context Manager setup, you can use the standard OpenTelemetry API to +retreive the current span as shown in +[OpenTelemetry documentation](https://opentelemetry.io/docs/languages/js/instrumentation/#get-the-current-span). + +Otherwise, Hive Gateway provide it's own cross-runtime Context tracking mechanism. In this case, you +can use +[`trace.getSpan` standard function](https://opentelemetry.io/docs/languages/js/instrumentation/#get-a-span-from-context) +to get access to the current span. + + + + + + +If you are using an async compatible context manager, you can simply use the standard +`@opentelemetry/api` methods, as shown in +[OpenTelemetry documentation](https://opentelemetry.io/docs/languages/js/instrumentation/#create-spans). + + + + + +```ts filename="gateway.config.ts" +import { defineConfig } from '@graphql-hive/gateway' +import { opentelemetrySetup } from '@graphql-mesh/plugin-opentelemetry/setup' +import { trace } from '@opentelemetry/api' +import { AsyncLocalStorageContextManager } from '@opentelemetry/context-async-hooks' +import { useGenericAuth } from '@envelop/generic-auth' + +opentelemetrySetup({ + contextManager: new AsyncLocalStorageContextManager(), + traces: { console: true }, +}) + +export const gatewayConfig = defineConfig({ + openTelemetry: { + traces: true, + }, + plugins: () => [ + useGenericAuth({ + resolveUserFn: ({ context }) => { + const span = trace.getActiveSpan(); + const user = await fetchUser(extractUserIdFromContext(context)) + span.setAttribute('user.id', user.id); + return user + } + }), + ], +}) +``` + + + + + +```ts filename="index.ts" +import './telemetry.ts' +import { createGatewayRuntime } from '@graphql-hive/gateway-runtime' +import { useOpenTelemetry } from '@graphql-mesh/plugin-opentelemetry' +import { AsyncLocalStorageContextManager } from '@opentelemetry/context-async-hooks' +import { useGenericAuth } from '@envelop/generic-auth' + +opentelemetrySetup({ + contextManager: new AsyncLocalStorageContextManager(), + traces: { console: true }, +}) + +export const gateway = createGatewayRuntime({ + plugins: ctx => { + const otelPlugin = useOpenTelemetry({ + ...ctx, + traces: true + }) + + return [ + otelPlugin, + useGenericAuth({ + resolveUserFn: ({ context }) => { + const span = trace.getActiveSpan(); + const user = await fetchUser(extractUserIdFromContext(context)) + span.setAttribute('user.id', user.id); + return user + } + }), + ], + } +}) +``` + + + + + + + + + +If you can't or don't want to use the Context Manager, Hive Gateway provides a cross plateform +context tracking mechanism. + +To parent spans correctly, you will have to manually provide the current OTEL context. You can +retreive the current OTEL context by either using the `context.opentelemetry.activeContext` +function, or the plugin's method `getOtelContext()`. + + + + + +If you don't have access to the graphql context, manually instanciate the opentelemetry plugin (see +Programatic Usage). + +```ts filename="gateway.config.ts" +import { defineConfig } from '@graphql-hive/gateway' +import { opentelemetrySetup } from '@graphql-mesh/plugin-opentelemetry/setup' +import { trace } from '@opentelemetry/api' +import { AsyncLocalStorageContextManager } from '@opentelemetry/context-async-hooks' +import { useGenericAuth } from '@envelop/generic-auth' + +opentelemetrySetup({ + contextManager: null, // Don't register any context manager + traces: { console: true }, +}) + +export const gatewayConfig = defineConfig({ + openTelemetry: { + useContextManager: false, // Make sure to disable context manager usage + traces: true, + }, + plugins: () => [ + useGenericAuth({ + resolveUserFn: ({ context }) => { + const ctx = context.opentelemetry.activeContext(); + const span = trace.getSpan(ctx) + + const user = await fetchUser(extractUserIdFromContext(context)) + span.setAttribute('user.id', user.id); + + return user + } + }), + ], +}) +``` + + + + + +The Gateway's tracer is available in the graphql context (`context.opentelemetry.tracer`). If you +don't have access to the graphql context, you can either create your own tracer, or use `getTracer` +plugin method (as shown in this example). + +```ts filename="index.ts" +import './telemetry.ts' +import { createGatewayRuntime } from '@graphql-hive/gateway-runtime' +import { useOpenTelemetry } from '@graphql-mesh/plugin-opentelemetry' +import { AsyncLocalStorageContextManager } from '@opentelemetry/context-async-hooks' +import { useGenericAuth } from '@envelop/generic-auth' + +opentelemetrySetup({ + contextManager: null, // Don't register any context manager + traces: { console: true }, +}) + +const opentelemetryPlugin = + +export const gateway = createGatewayRuntime({ + plugins: ctx => { + const otelPlugin = useOpenTelemetry({ + ...ctx, + useContextManager: false, // Make sure to disable context manager usage + traces: true + }) + + return [ + otelPlugin, + useGenericAuth({ + resolveUserFn: ({ context }) => { + // Retreive current OTEL Context by passing an object containing the graphql `context`, + // the http `request` and/or the `executionRequest`. The appropriate context will be + // found based on which of those properties are provided. + const ctx = otelPlugin.getOtelContext({ context }); + const span = trace.getSpan(ctx) + + const user = await fetchUser(extractUserIdFromContext(context)) + span.setAttribute('user.id', user.id); + + return user + } + }), + ], + } +}) +``` + + + + + + + + + ### Troubleshooting The default behavor of the plugin is to log errors and warnings to the console. From 3189a0f5247671d224855ff155621da9c314474d Mon Sep 17 00:00:00 2001 From: Valentin Cocaud Date: Thu, 26 Jun 2025 14:25:58 +0200 Subject: [PATCH 06/10] prettier --- packages/web/docs/src/content/gateway/monitoring-tracing.mdx | 2 -- 1 file changed, 2 deletions(-) diff --git a/packages/web/docs/src/content/gateway/monitoring-tracing.mdx b/packages/web/docs/src/content/gateway/monitoring-tracing.mdx index 74befb5b37..4f7f0ed6e3 100644 --- a/packages/web/docs/src/content/gateway/monitoring-tracing.mdx +++ b/packages/web/docs/src/content/gateway/monitoring-tracing.mdx @@ -1092,7 +1092,6 @@ will enable logging of OpenTelemetry SDK based on `OTEL_LOG_LEVEL` env variable. By default, Hive Gateway configure the Diagnostics API to output logs using Hive Gateway's logger. You can disable this using `configureDiagLogger` option. - ```ts filename="gateway.config.ts" const opentelemetryConfig = { // Use the default DiagLogger, which outputs logs directly to stdout @@ -1933,7 +1932,6 @@ can use [`trace.getSpan` standard function](https://opentelemetry.io/docs/languages/js/instrumentation/#get-a-span-from-context) to get access to the current span. - From 440896926da97c5e6237b1f05d364e205f1549e1 Mon Sep 17 00:00:00 2001 From: Valentin Cocaud Date: Mon, 30 Jun 2025 10:38:24 +0200 Subject: [PATCH 07/10] add migration guide --- .../migration-guides/gateway-v1-v2.mdx | 279 ++++++++++++++++++ 1 file changed, 279 insertions(+) diff --git a/packages/web/docs/src/content/migration-guides/gateway-v1-v2.mdx b/packages/web/docs/src/content/migration-guides/gateway-v1-v2.mdx index 5ec50e886b..2dfe274f79 100644 --- a/packages/web/docs/src/content/migration-guides/gateway-v1-v2.mdx +++ b/packages/web/docs/src/content/migration-guides/gateway-v1-v2.mdx @@ -13,6 +13,7 @@ v2 includes several breaking changes and improvements over v1. The most signific - [Remove Mocking Plugin from built-ins](#remove-mocking-plugin-from-built-ins) - [Disabled Automatic Forking](#disabled-automatic-forking) - [New Hive Logger for next-level observability and debugging](#hive-logger) +- [New OpenTelemetry integration for better traces and custom spans](#opentelemetry) ## Drop Support for Node v18 @@ -538,3 +539,281 @@ export const gatewayConfig = defineConfig({ + }) }) ``` + +## Opentelemetry + +OpenTelemetry integration have been re-worked to offer better traces, custom attributes and spans, +and overall compatiblity with standard OTEL API. + +For this features to be possible, we had to break the configuration API. + +You can read more about the new capabilities of the OpenTelemetry Tracing integration in the +[Hive Monitoring / Tracing documentation](/docs/gateway/monitoring-tracing) + +### OpenTelemetry SDK Setup + +The OpenTelemetry SDK setup used to be automatically done by the plugin it self, it is no longer the +case. You have the choice to either setup it yourself using official `@opentelemetry/*` pacakges +(like official Node SDK `@opentelemetry/sdk-node`), or to use our cross-plateform setup helper +(recommended). + +Extracting OTEL setup out of the plugin allows to you to decide on the version of `opentelemetry-js` +SDK you want to use. + +Most of OTEL related settings have been moved to `opentelemetrySetup` options. Please refere to +[OpenTelemetry Setup documentation](/docs/gateway/monitoring-tracing#opentelemetry-setup) for more +informations. + +#### Example with HTTP OTLP Exporter + +Hive Gateway opentelemetrySetup() (recommended),
OpenTelemetry NodeSDK
]}> + + + +1. Install OpenTelemetry SDK + +```sh npm2yarn +npm i @opentelemetry/api @opentelemetry/context-async-hooks @opentelemetry/exporter-trace-otlp-http +``` + +2. Create a new `telemetry.ts` file which will contain your open telemetry setup + +```ts filename="telemetry.ts" +import { opentelemetrySetup } from '@graphql-hive/plugin-opentelemetry/setup' +import { AsyncLocalStorageContextManager } from '@opentelemetry/context-async-hooks' +import { OTLPTraceExporter } from '@opentelemetry/exporter-trace-otlp-http' + +opentelemetrySetup({ + resource: { + serviceName: 'gateway' + }, + contextManager: new AsyncLocalStorageContextManager(), + traces: { + exporter: new OTLPTraceExporter({ url: 'http://:4318' }), + batching: { + //... batching options + } + } +}) +``` + +3. Update `gateway.config.ts` to import your `telemetry.ts` file (very first import), and adapt the + plugin configuration + + + + + +```diff filname="config.gateway.ts" ++ import './telemetry.ts' +- import { createOtlpHttpExporter, defineConfig } from '@graphql-hive/gateway' ++ import { defineConfig } from '@graphql-hive/gateway' + +export const gatewayConfig = defineConfig({ + openTelemetry: { +- serviceName: 'gateway', +- exporters: [ +- createOtlpHttpExporter({ +- url: 'http://:4318' +- }, { +- //... batching options +- }) +- ] ++ traces: true, + } +}) +``` + + + + + +```diff filname="config.gateway.ts" ++ import './telemetry.ts' +import { createGatewayRuntime } from '@graphql-hive/gateway-runtime' +- import { useOpenTelemetry, createOtlHttpExporter } from '@graphql-mesh/plugin-opentelemetry' ++ import { useOpenTelemetry } from '@graphql-mesh/plugin-opentelemetry' + +export const gateway = createGatewayRuntime({ + plugins: ctx => [ + useOpenTelemetry({ + ...ctx, +- serviceName: 'gateway', +- exporters: [ +- createOtlpHttpExporter({ +- url: 'http://:4318' +- }, { +- //... batching options +- }) +- ] ++ traces: true, + }) + ] +}) +``` + + + + + + + + + +1. Install OpenTelemetry SDK + +```sh npm2yarn +npm i @opentelemetry/sdk-node @opentelemetry/exporter-trace-otlp-http +``` + +2. Create a new `telemetry.ts` file which will contain your open telemetry setup + +```ts filename="telemetry.ts" +import { OTLPTraceExporter } from '@opentelemetry/exporter-trace-otlp-http' +import { NodeSDK, tracing } from '@opentelemetry/sdk-node' + +new NodeSDK({ + traceExporter: new OTLPTraceExporter({ url: 'http://:4318' }), + // or with batching options + spanProcessors: [new tracing.BatchSpanProcessor( + new OTLPTraceExporter({ url: 'http://:4318' }), + { + //... batching options + } + )] +}).start() +``` + +3. Update `gateway.config.ts` to import your `telemetry.ts` file (very first import), and adapt the + plugin configuration + + + + + +```diff filname="config.gateway.ts" ++ import './telemetry.ts' +- import { createOtlpHttpExporter, defineConfig } from '@graphql-hive/gateway' ++ import { defineConfig } from '@graphql-hive/gateway' + +export const gatewayConfig = defineConfig({ + openTelemetry: { +- serviceName: 'gateway', +- exporters: [ +- createOtlpHttpExporter({ +- url: 'http://:4318' +- }, { +- //... batching options +- }) +- ] ++ traces: true, + } +}) +``` + + + + + +```diff filname="config.gateway.ts" ++ import './telemetry.ts' +import { createGatewayRuntime } from '@graphql-hive/gateway-runtime' +- import { useOpenTelemetry, createOtlHttpExporter } from '@graphql-mesh/plugin-opentelemetry' ++ import { useOpenTelemetry } from '@graphql-mesh/plugin-opentelemetry' + +export const gateway = createGatewayRuntime({ + plugins: ctx => [ + useOpenTelemetry({ + ...ctx, +- serviceName: 'gateway', +- exporters: [ +- createOtlpHttpExporter({ +- url: 'http://:4318' +- }, { +- //... batching options +- }) +- ] ++ traces: true, + }) + ] +}) +``` + + + + + + + +
+ +### Tracing related configuration + +All tracing related options has been moved to a `traces` option. + +```diff filename="gateway.config" +import { denfineConfig } from '@grahpl-hive/gateway' + +export const gatewayConfig = defineConfig({ + openTelemetry: { ++ traces: { + tracer: ..., + spans: { + ... + } ++ } + } +}) +``` + +### Spans filter functions payload + +The payload given as a parameter of the span filtering functions have been restrained. + +Due to internal changes, the informations available at span filtering time have been reduced to only +include (depending on the span) the GraphQL `context`, the HTTP `request` and the Upsream +`executionRequest`. + +Please refere to [Request Spans documentation](/docs/gateway/monitoring-tracing#request-spans) for +details of what is availbe for each span filter. + +### Span parenting + +Spans are now parented correctly. This can have impact on trace queries used in your dashboard. + +Please review your queries to not filter against `null` parent span id. + +### New Graphql Operation Span + +A new span encapsulating each GraphQL operation have been added. + +It is a subspan of the HTTP request span, and encapsulate all the actual GraphQL processing. +There can be multiple GraphQL operation spans for one HTTP request span if you have enabled graphql operation batching over http. + +### OpenTelemetry Context + +The OpenTelemetry Context is now modified by Hive Gateway. The Context is set with the current phase +span. This means that if you were creating custom spans in your plugin without explicitly providing +a parent context, your spans will be considered sub-spans of Hive Gateway's current span. + +To maintain your span as a root span, add an explicit parent context a creation time: + +```diff +import { + trace, ++ ROOT_CONTEXT +} from '@opentelemetry/api' + +export const myPlugin = () => ({ + onExecute() { + trace.startActiveSpan( + 'my-custom-span', + { foo: 'bar' }, ++ ROOT_CONTEXT + () => { + // do something + } + ) + } +}) +``` From 7f9f704547645d47f9b9e92c2ccd30648d63141c Mon Sep 17 00:00:00 2001 From: Valentin Cocaud Date: Mon, 30 Jun 2025 15:06:48 +0200 Subject: [PATCH 08/10] prettier --- .../src/content/migration-guides/gateway-v1-v2.mdx | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/packages/web/docs/src/content/migration-guides/gateway-v1-v2.mdx b/packages/web/docs/src/content/migration-guides/gateway-v1-v2.mdx index 2dfe274f79..80d8f8de7f 100644 --- a/packages/web/docs/src/content/migration-guides/gateway-v1-v2.mdx +++ b/packages/web/docs/src/content/migration-guides/gateway-v1-v2.mdx @@ -675,12 +675,11 @@ import { NodeSDK, tracing } from '@opentelemetry/sdk-node' new NodeSDK({ traceExporter: new OTLPTraceExporter({ url: 'http://:4318' }), // or with batching options - spanProcessors: [new tracing.BatchSpanProcessor( - new OTLPTraceExporter({ url: 'http://:4318' }), - { + spanProcessors: [ + new tracing.BatchSpanProcessor(new OTLPTraceExporter({ url: 'http://:4318' }), { //... batching options - } - )] + }) + ] }).start() ``` @@ -787,8 +786,9 @@ Please review your queries to not filter against `null` parent span id. A new span encapsulating each GraphQL operation have been added. -It is a subspan of the HTTP request span, and encapsulate all the actual GraphQL processing. -There can be multiple GraphQL operation spans for one HTTP request span if you have enabled graphql operation batching over http. +It is a subspan of the HTTP request span, and encapsulate all the actual GraphQL processing. There +can be multiple GraphQL operation spans for one HTTP request span if you have enabled graphql +operation batching over http. ### OpenTelemetry Context From fd1790395013fef387cb3e8876723011a99ac7c4 Mon Sep 17 00:00:00 2001 From: Valentin Cocaud Date: Tue, 1 Jul 2025 15:10:11 +0200 Subject: [PATCH 09/10] fix casing of opentelemetry --- .../src/content/migration-guides/gateway-v1-v2.mdx | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/packages/web/docs/src/content/migration-guides/gateway-v1-v2.mdx b/packages/web/docs/src/content/migration-guides/gateway-v1-v2.mdx index 80d8f8de7f..ae7f248d26 100644 --- a/packages/web/docs/src/content/migration-guides/gateway-v1-v2.mdx +++ b/packages/web/docs/src/content/migration-guides/gateway-v1-v2.mdx @@ -540,7 +540,7 @@ export const gatewayConfig = defineConfig({ }) ``` -## Opentelemetry +## OpenTelemetry OpenTelemetry integration have been re-worked to offer better traces, custom attributes and spans, and overall compatiblity with standard OTEL API. @@ -560,13 +560,13 @@ case. You have the choice to either setup it yourself using official `@opentelem Extracting OTEL setup out of the plugin allows to you to decide on the version of `opentelemetry-js` SDK you want to use. -Most of OTEL related settings have been moved to `opentelemetrySetup` options. Please refere to +Most of OTEL related settings have been moved to `openTelemetrySetup` options. Please refere to [OpenTelemetry Setup documentation](/docs/gateway/monitoring-tracing#opentelemetry-setup) for more informations. #### Example with HTTP OTLP Exporter -Hive Gateway opentelemetrySetup() (recommended),
OpenTelemetry NodeSDK
]}> +Hive Gateway openTelemetrySetup() (recommended),
OpenTelemetry NodeSDK
]}> @@ -579,11 +579,11 @@ npm i @opentelemetry/api @opentelemetry/context-async-hooks @opentelemetry/expor 2. Create a new `telemetry.ts` file which will contain your open telemetry setup ```ts filename="telemetry.ts" -import { opentelemetrySetup } from '@graphql-hive/plugin-opentelemetry/setup' +import { openTelemetrySetup } from '@graphql-hive/plugin-opentelemetry/setup' import { AsyncLocalStorageContextManager } from '@opentelemetry/context-async-hooks' import { OTLPTraceExporter } from '@opentelemetry/exporter-trace-otlp-http' -opentelemetrySetup({ +openTelemetrySetup({ resource: { serviceName: 'gateway' }, From 0fef3e6e9483e495a71f83bc4eb7479ea1ee3ba9 Mon Sep 17 00:00:00 2001 From: Valentin Cocaud Date: Tue, 1 Jul 2025 15:20:13 +0200 Subject: [PATCH 10/10] fix opentelemetry casing --- .../content/gateway/monitoring-tracing.mdx | 167 +++++++++--------- 1 file changed, 84 insertions(+), 83 deletions(-) diff --git a/packages/web/docs/src/content/gateway/monitoring-tracing.mdx b/packages/web/docs/src/content/gateway/monitoring-tracing.mdx index 4f7f0ed6e3..aac1dca024 100644 --- a/packages/web/docs/src/content/gateway/monitoring-tracing.mdx +++ b/packages/web/docs/src/content/gateway/monitoring-tracing.mdx @@ -136,7 +136,7 @@ We recommend to place your OpenTelemetry setup in a `telemetry.ts` file that wil import in your `gateway.config.ts` file. This allow instrumentations to be registered (if any) before any other packages are imported. -For ease of configuration, we provide a `opentelemetrySetup` function from +For ease of configuration, we provide a `openTelemetrySetup` function from `@graphql-hive/plugin-opentelemetry/setup` module, with sensible default and straightforward API compatible with all runtimes. @@ -151,7 +151,7 @@ more details about OpenTelemetry setup and API. #### Basic usage -Hive Gateway opentelemetrySetup() (recommended),
OpenTelemetry NodeSDK
]}> +Hive Gateway openTelemetrySetup() (recommended),
OpenTelemetry NodeSDK
]}> @@ -159,11 +159,11 @@ This configuration API still rely on offical `@opentelemetry/api` package, which any official or standard compliant packages with it. ```ts filename="telemetry.ts" -import { opentelemetrySetup } from '@graphql-hive/plugin-opentelemetry/setup' +import { openTelemetrySetup } from '@graphql-hive/plugin-opentelemetry/setup' import { AsyncLocalStorageContextManager } from '@opentelemetry/context-async-hooks' import { OTLPTraceExporter } from '@opentelemetry/exporter-trace-otlp-http' -opentelemetrySetup({ +openTelemetrySetup({ // Mandatory: It depends on the available API in your runtime. // We recommend AsyncLocalStorage based manager when possible. // `@opentelemetry/context-zone` is also available for other runtimes. @@ -225,14 +225,14 @@ new NodeSDK({ You can provide a service name, either by using standard `OTEL_SERVICE_NAME` and `OTEL_SERVICE_VERSION` or by providing them programatically via setup options -Hive Gateway opentelemetrySetup() (recommended),
OpenTelemetry NodeSDK
]}> +Hive Gateway openTelemetrySetup() (recommended),
OpenTelemetry NodeSDK
]}> ```ts filename="telemetry.ts" -import { opentelemetrySetup } from '@graphql-hive/plugin-opentelemetry/setup' +import { openTelemetrySetup } from '@graphql-hive/plugin-opentelemetry/setup' -opentelemetrySetup({ +openTelemetrySetup({ resource: { serviceName: 'my-service', serviceVersion: '1.0.0' @@ -264,7 +264,7 @@ new NodeSDK({ Resource attributes can be defined by providing a `Resource` instance to the setup `resource` option. -Hive Gateway opentelemetrySetup() (recommended),
OpenTelemetry NodeSDK
]}> +Hive Gateway openTelemetrySetup() (recommended),
OpenTelemetry NodeSDK
]}> @@ -273,10 +273,10 @@ This resource will be merged with the resource created from env variables, which variables. ```ts filename="telemetry.ts" -import { opentelemetrySetup } from '@graphql-hive/plugin-opentelemetry/setup' +import { openTelemetrySetup } from '@graphql-hive/plugin-opentelemetry/setup' import { resourceFromAttributes } from '@opentelemetry/resources' -opentelemetrySetup({ +openTelemetrySetup({ resource: resourceFromAttributes({ 'custom.attribute': 'my custom value' }) @@ -322,15 +322,15 @@ You can setup OpenTelemetry by providing either: - a Tracer Provider. This is the manual setup mode where nothing is created automatically. The Tracer Provider will just be registered. -Hive Gateway opentelemetrySetup() (recommended),
OpenTelemetry NodeSDK
]}> +Hive Gateway openTelemetrySetup() (recommended),
OpenTelemetry NodeSDK
]}> ```ts filename="telemetry.ts" -import { opentelemetrySetup } from '@graphql-hive/plugin-opentelemetry/setup' +import { openTelemetrySetup } from '@graphql-hive/plugin-opentelemetry/setup' import { AsyncLocalStorageContextManager } from '@opentelemetry/context-async-hooks' -opentelemetrySetup({ +openTelemetrySetup({ contextManager: new AsyncLocalStorageContextManager(), traces: { // Define your exporter, most of the time the OTLP HTTP one. Traces are batched by default. @@ -343,7 +343,7 @@ opentelemetrySetup({ // or -opentelemetrySetup({ +openTelemetrySetup({ contextManager: new AsyncLocalStorageContextManager(), traces: { // Define your span processors. @@ -353,7 +353,7 @@ opentelemetrySetup({ // or -opentelemetrySetup({ +openTelemetrySetup({ contextManager: new AsyncLocalStorageContextManager(), traces: { // Define your span processors. @@ -413,14 +413,14 @@ debugging purpose. [See official documentation for more details](https://open-telemetry.github.io/opentelemetry-js/classes/_opentelemetry_sdk-trace-base.ConsoleSpanExporter.html). -Hive Gateway opentelemetrySetup() (recommended),
OpenTelemetry NodeSDK
]}> +Hive Gateway openTelemetrySetup() (recommended),
OpenTelemetry NodeSDK
]}> ```ts filename="telemetry.ts" -import { opentelemetrySetup } from '@graphql-hive/plugin-opentelemetry/setup' +import { openTelemetrySetup } from '@graphql-hive/plugin-opentelemetry/setup' -opentelemetrySetup({ +openTelemetrySetup({ contextManager: new AsyncLocalStorageContextManager(), traces: { console: true @@ -451,16 +451,16 @@ new NodeSDK({ An exporter that writes the spans to an OTLP-supported backend using HTTP. -Hive Gateway opentelemetrySetup() (recommended),
OpenTelemetry NodeSDK
]}> +Hive Gateway openTelemetrySetup() (recommended),
OpenTelemetry NodeSDK
]}> ```ts filename="telemetry.ts" -import { opentelemetrySetup } from '@graphql-hive/plugin-opentelemetry/setup' +import { openTelemetrySetup } from '@graphql-hive/plugin-opentelemetry/setup' import { AsyncLocalStorageContextManager } from '@opentelemetry/context-async-hooks' import { OTLPTraceExporter } from '@opentelemetry/exporter-trace-otlp-http' -opentelemetrySetup({ +openTelemetrySetup({ contextManager: new AsyncLocalStorageContextManager(), traces: { exporter: new OTLPTraceExporter({ url: 'http://:4318' }) @@ -491,16 +491,16 @@ new NodeSDK({ An exporter that writes the spans to an OTLP-supported backend using gRPC. -Hive Gateway opentelemetrySetup() (recommended),
OpenTelemetry NodeSDK
]}> +Hive Gateway openTelemetrySetup() (recommended),
OpenTelemetry NodeSDK
]}> ```ts filename="telemetry.ts" -import { opentelemetrySetup } from '@graphql-hive/plugin-opentelemetry/setup' +import { openTelemetrySetup } from '@graphql-hive/plugin-opentelemetry/setup' import { AsyncLocalStorageContextManager } from '@opentelemetry/context-async-hooks' import { OTLPTraceExporter } from '@opentelemetry/exporter-trace-otlp-grpc' -opentelemetrySetup({ +openTelemetrySetup({ contextManager: new AsyncLocalStorageContextManager(), traces: { exporter: new OTLPTraceExporter({ url: 'http://:4317' }) @@ -538,16 +538,16 @@ Your Jaeger instance needs to have OTLP ingestion enabeld, so verify that you ha `COLLECTOR_OTLP_ENABLED=true` environment variable set, and that ports `4317` and `4318` are acessible. -Hive Gateway opentelemetrySetup() (recommended),
OpenTelemetry NodeSDK
]}> +Hive Gateway openTelemetrySetup() (recommended),
OpenTelemetry NodeSDK
]}> ```ts filename="telemetry.ts" -import { opentelemetrySetup } from '@graphql-hive/plugin-opentelemetry/setup' +import { openTelemetrySetup } from '@graphql-hive/plugin-opentelemetry/setup' import { AsyncLocalStorageContextManager } from '@opentelemetry/context-async-hooks' import { OTLPTraceExporter } from '@opentelemetry/exporter-trace-otlp-http' -opentelemetrySetup({ +openTelemetrySetup({ contextManager: new AsyncLocalStorageContextManager(), traces: { exporter: new OTLPTraceExporter({ url: 'http://:4318' }) @@ -585,16 +585,16 @@ Please refer to the [NewRelic OTLP documentation](https://docs.newrelic.com/docs/opentelemetry/best-practices/opentelemetry-otlp/#configure-endpoint-port-protocol) for complete documentation and to find the apropriate endpoint. -Hive Gateway opentelemetrySetup() (recommended),
OpenTelemetry NodeSDK
]}> +Hive Gateway openTelemetrySetup() (recommended),
OpenTelemetry NodeSDK
]}> ```ts filename="telemetry.ts" -import { opentelemetrySetup } from '@graphql-hive/plugin-opentelemetry/setup' +import { openTelemetrySetup } from '@graphql-hive/plugin-opentelemetry/setup' import { AsyncLocalStorageContextManager } from '@opentelemetry/context-async-hooks' import { OTLPTraceExporter } from '@opentelemetry/exporter-trace-otlp-http' -opentelemetrySetup({ +openTelemetrySetup({ contextManager: new AsyncLocalStorageContextManager(), traces: { exporter: new OTLPTraceExporter({ @@ -642,7 +642,7 @@ endpoint You can also use the official DataDog Tracer Provider by using manual Hive Gateway deployement and installing the dependency. -Hive Gateway opentelemetrySetup() (recommended),
OpenTelemetry NodeSDK
]}> +Hive Gateway openTelemetrySetup() (recommended),
OpenTelemetry NodeSDK
]}> @@ -651,14 +651,14 @@ the correlation with DataDog APM spans. ```ts filename="telemetry.ts" import ddTrace from 'dd-trace' -import { opentelemetrySetup } from '@graphql-hive/plugin-opentelemetry/setup' +import { openTelemetrySetup } from '@graphql-hive/plugin-opentelemetry/setup' import { AsyncLocalStorageContextManager } from '@opentelemetry/context-async-hooks' const { TracerProvider } = ddTrace.init({ // Your configuration }) -opentelemetrySetup({ +openTelemetrySetup({ contextManager: null, // Dont' register a context manager, DataDog Agent registers its own. traces: { tracerProvider: new TracerProvider() @@ -675,11 +675,11 @@ It is possible to not use DataDog Agent if you want to only use DataDog as a tra DataDog is compatible with standard OTLP over HTTP export format. ```ts filename="telemetry.ts" -import { opentelemetrySetup } from '@graphql-hive/plugin-opentelemetry/setup' +import { openTelemetrySetup } from '@graphql-hive/plugin-opentelemetry/setup' import { AsyncLocalStorageContextManager } from '@opentelemetry/context-async-hooks' import { OTLPTraceExporter } from '@opentelemetry/exporter-trace-otlp-http' -opentelemetrySetup({ +openTelemetrySetup({ contextManager: new AsyncLocalStorageContextManager(), traces: { exporter: new OTLPTraceExporter({ @@ -719,16 +719,16 @@ new NodeSDK({ [Zipkin](https://zipkin.io/) is using a custom protocol to send the spans, so you can use the Zipkin exporter to send the spans to a Zipkin backend. -Hive Gateway opentelemetrySetup() (recommended),
OpenTelemetry NodeSDK
]}> +Hive Gateway openTelemetrySetup() (recommended),
OpenTelemetry NodeSDK
]}> ```ts filename="telemetry.ts" -import { opentelemetrySetup } from '@graphql-hive/plugin-opentelemetry/setup' +import { openTelemetrySetup } from '@graphql-hive/plugin-opentelemetry/setup' import { AsyncLocalStorageContextManager } from '@opentelemetry/context-async-hooks' import { ZipkinExporter } from '@opentelemetry/exporter-zipkin' -opentelemetrySetup({ +openTelemetrySetup({ contextManager: new AsyncLocalStorageContextManager(), traces: { exporter: new ZipkinExporter({ @@ -759,14 +759,14 @@ new NodeSDK({ ```ts filename="telemetry.ts" import ddTrace from 'dd-trace' -import { opentelemetrySetup } from '@graphql-hive/plugin-opentelemetry/setup' +import { openTelemetrySetup } from '@graphql-hive/plugin-opentelemetry/setup' import { AsyncLocalStorageContextManager } from '@opentelemetry/context-async-hooks' const { TracerProvider } = ddTrace.init({ // Your configuration }) -opentelemetrySetup({ +openTelemetrySetup({ contextManager: new AsyncLocalStorageContextManager(), traces: { tracerProvider: new TracerProvider() @@ -802,17 +802,17 @@ If no Context Manager compatible with async is registered, automatic parenting o not work. You will have to retreive the current OpenTelemetry context from the GraphQL context, or from the `getOtelContext` method of the plugin instance. -Hive Gateway opentelemetrySetup() (recommended),
OpenTelemetry NodeSDK
]}> +Hive Gateway openTelemetrySetup() (recommended),
OpenTelemetry NodeSDK
]}> ```ts filename="telemetry.ts" -import { opentelemetrySetup } from '@graphql-hive/plugin-opentelemetry/setup' +import { openTelemetrySetup } from '@graphql-hive/plugin-opentelemetry/setup' import { AsyncLocalStorageContextManager } from '@opentelemetry/context-async-hooks' import { OTLPTraceExporter } from '@opentelemetry/exporter-trace-otlp-grpc' import { B3Propagator } from '@opentelemetry/propagator-b3' -opentelemetrySetup({ +openTelemetrySetup({ contextManager: new AsyncLocalStorageContextManager(), traces: { exporter: new OTLPTraceExporter({ url: 'http://:4317' }) @@ -850,7 +850,7 @@ behaviour to exactly suites your infrastructure limits. By default, the batch processor will send the spans every 5 seconds or when the buffer is full. -Hive Gateway opentelemetrySetup() (recommended),
OpenTelemetry NodeSDK
]}> +Hive Gateway openTelemetrySetup() (recommended),
OpenTelemetry NodeSDK
]}> @@ -866,9 +866,9 @@ The following configuration are allowed: [`SimpleSpanProcessor`](https://opentelemetry.io/docs/specs/otel/trace/sdk/#simple-processor) ```ts filename="telemetry.ts" -import { opentelemetrySetup } from '@graphql-hive/plugin-opentelemetry/setup' +import { openTelemetrySetup } from '@graphql-hive/plugin-opentelemetry/setup' -opentelemetrySetup({ +openTelemetrySetup({ traces: { exporter: ..., batching: { @@ -928,16 +928,16 @@ rate). By default, all requests are traced. You can either provide you own Sampler, or provide a sampling rate which will be used to setup a Parent + TraceID Ratio strategy. -Hive Gateway opentelemetrySetup() (recommended),
OpenTelemetry NodeSDK
]}> +Hive Gateway openTelemetrySetup() (recommended),
OpenTelemetry NodeSDK
]}> ```ts filename="telemetry.ts" -import { opentelemetrySetup } from '@graphql-hive/plugin-opentelemetry/setup' +import { openTelemetrySetup } from '@graphql-hive/plugin-opentelemetry/setup' import { JaegerRemoteSampler } from '@opentelemetry/sampler-jaeger-remote' import { AlwaysOnSampler } from '@opentelemetry/sdk-trace-base' -opentelemetrySetup({ +openTelemetrySetup({ // Use Parent + TraceID Ratio strategy samplingRate: 0.1, @@ -984,14 +984,14 @@ new NodeSDK({ To ensure that you don't overwhelm your tracing injestion infrastructure, you can set limits for both cardinality and amount of data the OpenTelemetry SDK will be allowed to generate. -Hive Gateway opentelemetrySetup() (recommended),
OpenTelemetry NodeSDK
]}> +Hive Gateway openTelemetrySetup() (recommended),
OpenTelemetry NodeSDK
]}> ```ts filename="telemetry.ts" -import { opentelemetrySetup } from '@graphql-hive/plugin-opentelemetry/setup' +import { openTelemetrySetup } from '@graphql-hive/plugin-opentelemetry/setup' -opentelemetrySetup({ +openTelemetrySetup({ generalLimits: {}, traces: { spanLimits: {} @@ -1077,7 +1077,7 @@ This context also allows to keep the link between related spans (for parenting o You can configure the behaviour of the plugin with this context. ```ts filename="gateway.config.ts" -const opentelemetryConfig = { +const openTelemetryConfig = { useContextManager: true, // If false, the parenting of spans will not rely on OTEL Context inheritContext: true, // If false, the root span will not be based on OTEL Context, it will always be a root span propagateContext: true // If false, the context will not be propagated to subgraphs @@ -1093,7 +1093,7 @@ By default, Hive Gateway configure the Diagnostics API to output logs using Hive You can disable this using `configureDiagLogger` option. ```ts filename="gateway.config.ts" -const opentelemetryConfig = { +const openTelemetryConfig = { // Use the default DiagLogger, which outputs logs directly to stdout configureDiagLogger: false } @@ -1109,7 +1109,7 @@ Tracer Provider by default. You can customize which method to call or entirely d behaviour by using the `flushOnDispose` option. ```ts filename="gateway.config.ts" -const opentelemetryConfig = { +const openTelemetryConfig = { // Disable the auto-flush on shutdown flushOnDispose: false, // or call a custom method @@ -1123,7 +1123,7 @@ By default, Hive Gateway will create a tracer named `gateway`. You can provide y needed. ```ts filename="gateway.config.ts" -const opentelemetryConfig = { +const openTelemetryConfig = { traces: { tracer: trace.getTracer('my-custom-tracer') } @@ -1729,11 +1729,11 @@ Programatic Usage). ```ts filename="gateway.config.ts" import { defineConfig } from '@graphql-hive/gateway' -import { opentelemetrySetup } from '@graphql-mesh/plugin-opentelemetry/setup' +import { openTelemetrySetup } from '@graphql-mesh/plugin-opentelemetry/setup' import { AsyncLocalStorageContextManager } from '@opentelemetry/context-async-hooks' import { useGenericAuth } from '@envelop/generic-auth' -opentelemetrySetup({ +openTelemetrySetup({ contextManager: new AsyncLocalStorageContextManager(), traces: { console: true }, }) @@ -1747,7 +1747,7 @@ export const gatewayConfig = defineConfig({ resolveUserFn: ({ context }) => // `startActiveSpan` will rely on the current context to parent the new span correctly // You can also use your own tracer instead of Hive Gateway's one. - context.opentelemetry.tracer.startActiveSpan('users.fetch', (span) => { + context.openTelemetry.tracer.startActiveSpan('users.fetch', (span) => { const user = await fetchUser(extractUserIdFromContext(context)) span.end(); return user @@ -1761,7 +1761,7 @@ export const gatewayConfig = defineConfig({ -The Gateway's tracer is available in the graphql context (`context.opentelemetry.tracer`). If you +The Gateway's tracer is available in the graphql context (`context.openTelemetry.tracer`). If you don't have access to the graphql context, you can either create your own tracer, or use `getTracer` plugin method (as shown in this example). @@ -1769,10 +1769,11 @@ plugin method (as shown in this example). import './telemetry.ts' import { createGatewayRuntime } from '@graphql-hive/gateway-runtime' import { useOpenTelemetry } from '@graphql-mesh/plugin-opentelemetry' +import { openTelemetrySetup } from '@graphql-mesh/plugin-opentelemetry/setup' import { AsyncLocalStorageContextManager } from '@opentelemetry/context-async-hooks' import { useGenericAuth } from '@envelop/generic-auth' -opentelemetrySetup({ +openTelemetrySetup({ contextManager: new AsyncLocalStorageContextManager(), traces: { console: true }, }) @@ -1813,23 +1814,23 @@ If you can't or don't want to use the Context Manager, Hive Gateway provides a c context tracking mechanism. To parent spans correctly, you will have to manually provide the current OTEL context. You can -retreive the current OTEL context by either using the `context.opentelemetry.activeContext` +retreive the current OTEL context by either using the `context.openTelemetry.activeContext` function, or the plugin's method `getOtelContext()`. -If you don't have access to the graphql context, manually instanciate the opentelemetry plugin (see +If you don't have access to the graphql context, manually instanciate the OpenTelemetry plugin (see Programatic Usage). ```ts filename="gateway.config.ts" import { defineConfig } from '@graphql-hive/gateway' -import { opentelemetrySetup } from '@graphql-mesh/plugin-opentelemetry/setup' +import { openTelemetrySetup } from '@graphql-mesh/plugin-opentelemetry/setup' import { AsyncLocalStorageContextManager } from '@opentelemetry/context-async-hooks' import { useGenericAuth } from '@envelop/generic-auth' -opentelemetrySetup({ +openTelemetrySetup({ contextManager: null, // Don't register any context manager traces: { console: true }, }) @@ -1842,10 +1843,10 @@ export const gatewayConfig = defineConfig({ plugins: () => [ useGenericAuth({ resolveUserFn: ({ context }) => { - const ctx = context.opentelemetry.activeContext(); + const ctx = context.openTelemetry.activeContext(); // Explicitly pass the parent context as the third argument. - return context.opentelemetry.tracer.startActiveSpan('users.fetch', {}, ctx, (span) => { + return context.openTelemetry.tracer.startActiveSpan('users.fetch', {}, ctx, (span) => { const user = await fetchUser(extractUserIdFromContext(context)) span.end(); return user @@ -1860,7 +1861,7 @@ export const gatewayConfig = defineConfig({ -The Gateway's tracer is available in the graphql context (`context.opentelemetry.tracer`). If you +The Gateway's tracer is available in the graphql context (`context.openTelemetry.tracer`). If you don't have access to the graphql context, you can either create your own tracer, or use `getTracer` plugin method (as shown in this example). @@ -1868,10 +1869,11 @@ plugin method (as shown in this example). import './telemetry.ts' import { createGatewayRuntime } from '@graphql-hive/gateway-runtime' import { useOpenTelemetry } from '@graphql-mesh/plugin-opentelemetry' +import { openTelemetrySetup } from '@graphql-mesh/plugin-opentelemetry/setup' import { AsyncLocalStorageContextManager } from '@opentelemetry/context-async-hooks' import { useGenericAuth } from '@envelop/generic-auth' -opentelemetrySetup({ +openTelemetrySetup({ contextManager: null, // Don't register any context manager traces: { console: true }, }) @@ -1946,12 +1948,12 @@ If you are using an async compatible context manager, you can simply use the sta ```ts filename="gateway.config.ts" import { defineConfig } from '@graphql-hive/gateway' -import { opentelemetrySetup } from '@graphql-mesh/plugin-opentelemetry/setup' +import { openTelemetrySetup } from '@graphql-mesh/plugin-opentelemetry/setup' import { trace } from '@opentelemetry/api' import { AsyncLocalStorageContextManager } from '@opentelemetry/context-async-hooks' import { useGenericAuth } from '@envelop/generic-auth' -opentelemetrySetup({ +openTelemetrySetup({ contextManager: new AsyncLocalStorageContextManager(), traces: { console: true }, }) @@ -1981,10 +1983,11 @@ export const gatewayConfig = defineConfig({ import './telemetry.ts' import { createGatewayRuntime } from '@graphql-hive/gateway-runtime' import { useOpenTelemetry } from '@graphql-mesh/plugin-opentelemetry' +import { openTelemetrySetup } from '@graphql-mesh/plugin-opentelemetry/setup' import { AsyncLocalStorageContextManager } from '@opentelemetry/context-async-hooks' import { useGenericAuth } from '@envelop/generic-auth' -opentelemetrySetup({ +openTelemetrySetup({ contextManager: new AsyncLocalStorageContextManager(), traces: { console: true }, }) @@ -2023,24 +2026,24 @@ If you can't or don't want to use the Context Manager, Hive Gateway provides a c context tracking mechanism. To parent spans correctly, you will have to manually provide the current OTEL context. You can -retreive the current OTEL context by either using the `context.opentelemetry.activeContext` +retreive the current OTEL context by either using the `context.openTelemetry.activeContext` function, or the plugin's method `getOtelContext()`. -If you don't have access to the graphql context, manually instanciate the opentelemetry plugin (see +If you don't have access to the graphql context, manually instanciate the OpenTelemetry plugin (see Programatic Usage). ```ts filename="gateway.config.ts" import { defineConfig } from '@graphql-hive/gateway' -import { opentelemetrySetup } from '@graphql-mesh/plugin-opentelemetry/setup' +import { openTelemetrySetup } from '@graphql-mesh/plugin-opentelemetry/setup' import { trace } from '@opentelemetry/api' import { AsyncLocalStorageContextManager } from '@opentelemetry/context-async-hooks' import { useGenericAuth } from '@envelop/generic-auth' -opentelemetrySetup({ +openTelemetrySetup({ contextManager: null, // Don't register any context manager traces: { console: true }, }) @@ -2053,7 +2056,7 @@ export const gatewayConfig = defineConfig({ plugins: () => [ useGenericAuth({ resolveUserFn: ({ context }) => { - const ctx = context.opentelemetry.activeContext(); + const ctx = context.openTelemetry.activeContext(); const span = trace.getSpan(ctx) const user = await fetchUser(extractUserIdFromContext(context)) @@ -2070,7 +2073,7 @@ export const gatewayConfig = defineConfig({ -The Gateway's tracer is available in the graphql context (`context.opentelemetry.tracer`). If you +The Gateway's tracer is available in the graphql context (`context.openTelemetry.tracer`). If you don't have access to the graphql context, you can either create your own tracer, or use `getTracer` plugin method (as shown in this example). @@ -2081,13 +2084,11 @@ import { useOpenTelemetry } from '@graphql-mesh/plugin-opentelemetry' import { AsyncLocalStorageContextManager } from '@opentelemetry/context-async-hooks' import { useGenericAuth } from '@envelop/generic-auth' -opentelemetrySetup({ +openTelemetrySetup({ contextManager: null, // Don't register any context manager traces: { console: true }, }) -const opentelemetryPlugin = - export const gateway = createGatewayRuntime({ plugins: ctx => { const otelPlugin = useOpenTelemetry({ @@ -2140,10 +2141,10 @@ In addition, you can use the stdout exporter to log the traces to the console: ```ts filename="telemetry.ts" -import { opentelemetrySetup } from '@graphql-hive/plugin-opentelemetry/setup' +import { openTelemetrySetup } from '@graphql-hive/plugin-opentelemetry/setup' import { AsyncLocalStorageContextManager } from '@opentelemetry/context-async-hooks' -opentelemetrySetup({ +openTelemetrySetup({ contextManager: new AsyncLocalStorageContextManager traces: { console: true